@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
@@ -1,3 +1,594 @@
1
+ // src/visualizations/specs.breakdown.ts
2
+ import { defineVisualization } from "@contractspec/lib.contracts-spec/visualizations";
3
+ var QUERY_REF = { key: "analytics.query.execute", version: "1.0.0" };
4
+ var META = {
5
+ version: "1.0.0",
6
+ domain: "analytics",
7
+ stability: "experimental",
8
+ owners: ["@example.analytics-dashboard"],
9
+ tags: ["analytics", "dashboard", "visualization"]
10
+ };
11
+ var ChannelMixVisualization = defineVisualization({
12
+ meta: {
13
+ ...META,
14
+ key: "analytics.visualization.channel-mix",
15
+ title: "Channel Mix",
16
+ description: "Session distribution across acquisition channels.",
17
+ goal: "Explain which channels currently drive the largest share of traffic.",
18
+ context: "Marketing attribution dashboard."
19
+ },
20
+ source: { primary: QUERY_REF, resultPath: "data" },
21
+ visualization: {
22
+ kind: "pie",
23
+ nameDimension: "channel",
24
+ valueMeasure: "sessions",
25
+ dimensions: [
26
+ {
27
+ key: "channel",
28
+ label: "Channel",
29
+ dataPath: "channel",
30
+ type: "category"
31
+ }
32
+ ],
33
+ measures: [{ key: "sessions", label: "Sessions", dataPath: "sessions" }],
34
+ table: { caption: "Sessions by acquisition channel." }
35
+ }
36
+ });
37
+ var EngagementHeatmapVisualization = defineVisualization({
38
+ meta: {
39
+ ...META,
40
+ key: "analytics.visualization.engagement-heatmap",
41
+ title: "Engagement Heatmap",
42
+ description: "Average engagement score by weekday and time band.",
43
+ goal: "Reveal the highest-engagement time windows for product activity.",
44
+ context: "Usage analytics dashboard."
45
+ },
46
+ source: { primary: QUERY_REF, resultPath: "data" },
47
+ visualization: {
48
+ kind: "heatmap",
49
+ xDimension: "timeBand",
50
+ yDimension: "weekday",
51
+ valueMeasure: "engagementScore",
52
+ dimensions: [
53
+ {
54
+ key: "timeBand",
55
+ label: "Time Band",
56
+ dataPath: "timeBand",
57
+ type: "category"
58
+ },
59
+ {
60
+ key: "weekday",
61
+ label: "Weekday",
62
+ dataPath: "weekday",
63
+ type: "category"
64
+ }
65
+ ],
66
+ measures: [
67
+ {
68
+ key: "engagementScore",
69
+ label: "Engagement",
70
+ dataPath: "engagementScore"
71
+ }
72
+ ],
73
+ table: { caption: "Engagement score by weekday and time band." }
74
+ }
75
+ });
76
+ var ConversionFunnelVisualization = defineVisualization({
77
+ meta: {
78
+ ...META,
79
+ key: "analytics.visualization.conversion-funnel",
80
+ title: "Conversion Funnel",
81
+ description: "Progression through the main acquisition funnel.",
82
+ goal: "Show where the product is losing the largest share of prospects.",
83
+ context: "Growth dashboard."
84
+ },
85
+ source: { primary: QUERY_REF, resultPath: "data" },
86
+ visualization: {
87
+ kind: "funnel",
88
+ nameDimension: "stage",
89
+ valueMeasure: "users",
90
+ sort: "descending",
91
+ dimensions: [
92
+ { key: "stage", label: "Stage", dataPath: "stage", type: "category" }
93
+ ],
94
+ measures: [{ key: "users", label: "Users", dataPath: "users" }],
95
+ table: { caption: "Users per conversion stage." }
96
+ }
97
+ });
98
+ var AccountCoverageGeoVisualization = defineVisualization({
99
+ meta: {
100
+ ...META,
101
+ key: "analytics.visualization.account-coverage-geo",
102
+ title: "Account Coverage",
103
+ description: "High-value accounts plotted on a slippy-map surface.",
104
+ goal: "Locate where active commercial concentration is strongest.",
105
+ context: "Territory coverage dashboard."
106
+ },
107
+ source: { primary: QUERY_REF, resultPath: "data" },
108
+ visualization: {
109
+ kind: "geo",
110
+ mode: "slippy-map",
111
+ variant: "scatter",
112
+ nameDimension: "city",
113
+ latitudeDimension: "latitude",
114
+ longitudeDimension: "longitude",
115
+ valueMeasure: "accounts",
116
+ dimensions: [
117
+ { key: "city", label: "City", dataPath: "city", type: "category" },
118
+ {
119
+ key: "latitude",
120
+ label: "Latitude",
121
+ dataPath: "latitude",
122
+ type: "latitude"
123
+ },
124
+ {
125
+ key: "longitude",
126
+ label: "Longitude",
127
+ dataPath: "longitude",
128
+ type: "longitude"
129
+ }
130
+ ],
131
+ measures: [{ key: "accounts", label: "Accounts", dataPath: "accounts" }],
132
+ table: { caption: "Account concentration by city." }
133
+ }
134
+ });
135
+
136
+ // src/visualizations/specs.performance.ts
137
+ import { defineVisualization as defineVisualization2 } from "@contractspec/lib.contracts-spec/visualizations";
138
+ var QUERY_REF2 = { key: "analytics.query.execute", version: "1.0.0" };
139
+ var META2 = {
140
+ version: "1.0.0",
141
+ domain: "analytics",
142
+ stability: "experimental",
143
+ owners: ["@example.analytics-dashboard"],
144
+ tags: ["analytics", "dashboard", "visualization"]
145
+ };
146
+ var RevenueMetricVisualization = defineVisualization2({
147
+ meta: {
148
+ ...META2,
149
+ key: "analytics.visualization.revenue-metric",
150
+ title: "Revenue Snapshot",
151
+ description: "Current recurring revenue with prior-period comparison.",
152
+ goal: "Highlight the headline commercial metric for the dashboard.",
153
+ context: "Executive revenue overview."
154
+ },
155
+ source: { primary: QUERY_REF2, resultPath: "data" },
156
+ visualization: {
157
+ kind: "metric",
158
+ measure: "totalRevenue",
159
+ comparisonMeasure: "priorRevenue",
160
+ dimensions: [
161
+ { key: "period", label: "Period", dataPath: "period", type: "time" }
162
+ ],
163
+ measures: [
164
+ {
165
+ key: "totalRevenue",
166
+ label: "Revenue",
167
+ dataPath: "totalRevenue",
168
+ format: "currency"
169
+ },
170
+ {
171
+ key: "priorRevenue",
172
+ label: "Prior Revenue",
173
+ dataPath: "priorRevenue",
174
+ format: "currency"
175
+ }
176
+ ],
177
+ sparkline: { dimension: "period", measure: "totalRevenue" },
178
+ table: { caption: "Revenue trend and prior period values." }
179
+ }
180
+ });
181
+ var RevenueTrendVisualization = defineVisualization2({
182
+ meta: {
183
+ ...META2,
184
+ key: "analytics.visualization.revenue-trend",
185
+ title: "Revenue Trend",
186
+ description: "Monthly revenue progression for the current quarter.",
187
+ goal: "Track whether revenue growth is accelerating or stalling.",
188
+ context: "Quarterly commercial dashboard."
189
+ },
190
+ source: { primary: QUERY_REF2, resultPath: "data" },
191
+ visualization: {
192
+ kind: "cartesian",
193
+ variant: "line",
194
+ xDimension: "date",
195
+ yMeasures: ["revenue"],
196
+ dimensions: [
197
+ { key: "date", label: "Month", dataPath: "date", type: "time" }
198
+ ],
199
+ measures: [
200
+ {
201
+ key: "revenue",
202
+ label: "Revenue",
203
+ dataPath: "revenue",
204
+ format: "currency",
205
+ color: "#0f766e"
206
+ }
207
+ ],
208
+ thresholds: [
209
+ { key: "target", value: 140000, label: "Target", color: "#dc2626" }
210
+ ],
211
+ table: { caption: "Monthly revenue values." }
212
+ }
213
+ });
214
+ var RegionalRevenueVisualization = defineVisualization2({
215
+ meta: {
216
+ ...META2,
217
+ key: "analytics.visualization.regional-revenue",
218
+ title: "Regional Revenue",
219
+ description: "Revenue split by sales territory.",
220
+ goal: "Compare the strongest and weakest performing territories.",
221
+ context: "Territory planning and commercial comparison."
222
+ },
223
+ source: { primary: QUERY_REF2, resultPath: "data" },
224
+ visualization: {
225
+ kind: "cartesian",
226
+ variant: "bar",
227
+ xDimension: "region",
228
+ yMeasures: ["revenue"],
229
+ dimensions: [
230
+ { key: "region", label: "Region", dataPath: "region", type: "category" }
231
+ ],
232
+ measures: [
233
+ {
234
+ key: "revenue",
235
+ label: "Revenue",
236
+ dataPath: "revenue",
237
+ format: "currency",
238
+ color: "#1d4ed8"
239
+ }
240
+ ],
241
+ table: { caption: "Revenue by region." }
242
+ }
243
+ });
244
+ var RetentionAreaVisualization = defineVisualization2({
245
+ meta: {
246
+ ...META2,
247
+ key: "analytics.visualization.retention-area",
248
+ title: "Retention Curve",
249
+ description: "Weekly retention progression across the active cohort.",
250
+ goal: "Show whether user retention remains above the desired floor.",
251
+ context: "Product health dashboard."
252
+ },
253
+ source: { primary: QUERY_REF2, resultPath: "data" },
254
+ visualization: {
255
+ kind: "cartesian",
256
+ variant: "area",
257
+ xDimension: "week",
258
+ yMeasures: ["retentionRate"],
259
+ dimensions: [
260
+ { key: "week", label: "Week", dataPath: "week", type: "category" }
261
+ ],
262
+ measures: [
263
+ {
264
+ key: "retentionRate",
265
+ label: "Retention",
266
+ dataPath: "retentionRate",
267
+ format: "percentage",
268
+ color: "#16a34a"
269
+ }
270
+ ],
271
+ thresholds: [
272
+ { key: "floor", value: 0.5, label: "Floor", color: "#f59e0b" }
273
+ ],
274
+ table: { caption: "Weekly retention rate." }
275
+ }
276
+ });
277
+ var PipelineScatterVisualization = defineVisualization2({
278
+ meta: {
279
+ ...META2,
280
+ key: "analytics.visualization.pipeline-scatter",
281
+ title: "Pipeline Velocity",
282
+ description: "Deal-cycle length against win rate for active accounts.",
283
+ goal: "Spot outliers where the sales cycle is long but conversion remains weak.",
284
+ context: "Commercial operations dashboard."
285
+ },
286
+ source: { primary: QUERY_REF2, resultPath: "data" },
287
+ visualization: {
288
+ kind: "cartesian",
289
+ variant: "scatter",
290
+ xDimension: "cycleDays",
291
+ yMeasures: ["winRate"],
292
+ dimensions: [
293
+ {
294
+ key: "cycleDays",
295
+ label: "Cycle Days",
296
+ dataPath: "cycleDays",
297
+ type: "number"
298
+ }
299
+ ],
300
+ measures: [
301
+ {
302
+ key: "winRate",
303
+ label: "Win Rate",
304
+ dataPath: "winRate",
305
+ format: "percentage",
306
+ color: "#7c3aed"
307
+ },
308
+ {
309
+ key: "arr",
310
+ label: "ARR",
311
+ dataPath: "arr",
312
+ format: "currency"
313
+ }
314
+ ],
315
+ series: [
316
+ {
317
+ key: "pipeline",
318
+ label: "Accounts",
319
+ measure: "winRate",
320
+ type: "scatter",
321
+ color: "#7c3aed"
322
+ }
323
+ ],
324
+ table: { caption: "Sales cycle and win rate per account." }
325
+ }
326
+ });
327
+
328
+ // src/visualizations/catalog.ts
329
+ import { VisualizationRegistry } from "@contractspec/lib.contracts-spec/visualizations";
330
+ var AnalyticsVisualizationSpecs = [
331
+ RevenueMetricVisualization,
332
+ RevenueTrendVisualization,
333
+ RegionalRevenueVisualization,
334
+ RetentionAreaVisualization,
335
+ PipelineScatterVisualization,
336
+ ChannelMixVisualization,
337
+ EngagementHeatmapVisualization,
338
+ ConversionFunnelVisualization,
339
+ AccountCoverageGeoVisualization
340
+ ];
341
+ var AnalyticsVisualizationRegistry = new VisualizationRegistry([
342
+ ...AnalyticsVisualizationSpecs
343
+ ]);
344
+ var AnalyticsVisualizationRefs = AnalyticsVisualizationSpecs.map((spec) => refOf(spec));
345
+ var AnalyticsVisualizationSpecMap = new Map(AnalyticsVisualizationSpecs.map((spec) => [
346
+ visualizationRefKey(spec.meta),
347
+ spec
348
+ ]));
349
+ var AnalyticsVisualizationSampleData = {
350
+ [visualizationRefKey(RevenueMetricVisualization.meta)]: {
351
+ data: [
352
+ { period: "2025-11-01", totalRevenue: 112000, priorRevenue: 103000 },
353
+ { period: "2025-12-01", totalRevenue: 119000, priorRevenue: 110000 },
354
+ { period: "2026-01-01", totalRevenue: 126500, priorRevenue: 116000 },
355
+ { period: "2026-02-01", totalRevenue: 133000, priorRevenue: 124000 },
356
+ { period: "2026-03-01", totalRevenue: 145500, priorRevenue: 133000 }
357
+ ]
358
+ },
359
+ [visualizationRefKey(RevenueTrendVisualization.meta)]: {
360
+ data: [
361
+ { date: "2025-11-01", revenue: 112000 },
362
+ { date: "2025-12-01", revenue: 119000 },
363
+ { date: "2026-01-01", revenue: 126500 },
364
+ { date: "2026-02-01", revenue: 133000 },
365
+ { date: "2026-03-01", revenue: 145500 }
366
+ ]
367
+ },
368
+ [visualizationRefKey(RegionalRevenueVisualization.meta)]: {
369
+ data: [
370
+ { region: "North America", revenue: 210000 },
371
+ { region: "EMEA", revenue: 174000 },
372
+ { region: "APAC", revenue: 132000 },
373
+ { region: "LATAM", revenue: 88000 }
374
+ ]
375
+ },
376
+ [visualizationRefKey(RetentionAreaVisualization.meta)]: {
377
+ data: [
378
+ { week: "Week 1", retentionRate: 0.71 },
379
+ { week: "Week 2", retentionRate: 0.66 },
380
+ { week: "Week 3", retentionRate: 0.62 },
381
+ { week: "Week 4", retentionRate: 0.58 },
382
+ { week: "Week 5", retentionRate: 0.55 },
383
+ { week: "Week 6", retentionRate: 0.53 }
384
+ ]
385
+ },
386
+ [visualizationRefKey(PipelineScatterVisualization.meta)]: {
387
+ data: [
388
+ { cycleDays: 18, winRate: 0.31, arr: 82000 },
389
+ { cycleDays: 26, winRate: 0.44, arr: 65000 },
390
+ { cycleDays: 33, winRate: 0.27, arr: 91000 },
391
+ { cycleDays: 14, winRate: 0.56, arr: 47000 },
392
+ { cycleDays: 21, winRate: 0.48, arr: 59000 },
393
+ { cycleDays: 39, winRate: 0.22, arr: 114000 }
394
+ ]
395
+ },
396
+ [visualizationRefKey(ChannelMixVisualization.meta)]: {
397
+ data: [
398
+ { channel: "Direct", sessions: 4200 },
399
+ { channel: "Organic Search", sessions: 3600 },
400
+ { channel: "Paid Search", sessions: 2100 },
401
+ { channel: "Partner", sessions: 1400 },
402
+ { channel: "Referral", sessions: 900 }
403
+ ]
404
+ },
405
+ [visualizationRefKey(EngagementHeatmapVisualization.meta)]: {
406
+ data: [
407
+ { weekday: "Mon", timeBand: "09:00", engagementScore: 74 },
408
+ { weekday: "Mon", timeBand: "13:00", engagementScore: 82 },
409
+ { weekday: "Tue", timeBand: "09:00", engagementScore: 69 },
410
+ { weekday: "Tue", timeBand: "13:00", engagementScore: 88 },
411
+ { weekday: "Wed", timeBand: "09:00", engagementScore: 77 },
412
+ { weekday: "Wed", timeBand: "13:00", engagementScore: 91 },
413
+ { weekday: "Thu", timeBand: "09:00", engagementScore: 72 },
414
+ { weekday: "Thu", timeBand: "13:00", engagementScore: 86 },
415
+ { weekday: "Fri", timeBand: "09:00", engagementScore: 65 },
416
+ { weekday: "Fri", timeBand: "13:00", engagementScore: 79 }
417
+ ]
418
+ },
419
+ [visualizationRefKey(ConversionFunnelVisualization.meta)]: {
420
+ data: [
421
+ { stage: "Visited Site", users: 12000 },
422
+ { stage: "Started Trial", users: 4200 },
423
+ { stage: "Activated Workspace", users: 2400 },
424
+ { stage: "Requested Demo", users: 980 },
425
+ { stage: "Closed Won", users: 310 }
426
+ ]
427
+ },
428
+ [visualizationRefKey(AccountCoverageGeoVisualization.meta)]: {
429
+ data: [
430
+ { city: "Paris", latitude: 48.8566, longitude: 2.3522, accounts: 48 },
431
+ { city: "London", latitude: 51.5072, longitude: -0.1276, accounts: 62 },
432
+ { city: "New York", latitude: 40.7128, longitude: -74.006, accounts: 71 },
433
+ { city: "Toronto", latitude: 43.6532, longitude: -79.3832, accounts: 36 },
434
+ {
435
+ city: "Singapore",
436
+ latitude: 1.3521,
437
+ longitude: 103.8198,
438
+ accounts: 29
439
+ }
440
+ ]
441
+ }
442
+ };
443
+ function refOf(spec) {
444
+ return { key: spec.meta.key, version: spec.meta.version };
445
+ }
446
+ function visualizationRefKey(ref) {
447
+ return `${ref.key}.v${ref.version}`;
448
+ }
449
+
450
+ // src/visualizations/widgets.ts
451
+ var LEGACY_VISUALIZATION_REFS = {
452
+ METRIC: refOf(RevenueMetricVisualization),
453
+ LINE_CHART: refOf(RevenueTrendVisualization),
454
+ BAR_CHART: refOf(RegionalRevenueVisualization),
455
+ AREA_CHART: refOf(RetentionAreaVisualization),
456
+ SCATTER_PLOT: refOf(PipelineScatterVisualization),
457
+ PIE_CHART: refOf(ChannelMixVisualization),
458
+ HEATMAP: refOf(EngagementHeatmapVisualization),
459
+ FUNNEL: refOf(ConversionFunnelVisualization),
460
+ MAP: refOf(AccountCoverageGeoVisualization)
461
+ };
462
+ function createExampleWidgets(dashboardId) {
463
+ return [
464
+ widget(dashboardId, "widget_revenue_metric", "Revenue Snapshot", "METRIC", 0, 0, 4, 2, {
465
+ layout: "single",
466
+ bindings: [binding(RevenueMetricVisualization, 200)]
467
+ }),
468
+ widget(dashboardId, "widget_revenue_trend", "Revenue Trend", "LINE_CHART", 4, 0, 8, 4, {
469
+ layout: "single",
470
+ bindings: [binding(RevenueTrendVisualization)]
471
+ }),
472
+ widget(dashboardId, "widget_regional_revenue", "Regional Revenue", "BAR_CHART", 0, 2, 6, 4, {
473
+ layout: "single",
474
+ bindings: [binding(RegionalRevenueVisualization)]
475
+ }),
476
+ widget(dashboardId, "widget_channel_mix", "Channel Mix", "PIE_CHART", 6, 2, 6, 4, {
477
+ layout: "single",
478
+ bindings: [binding(ChannelMixVisualization)]
479
+ }),
480
+ widget(dashboardId, "widget_retention", "Retention Curve", "AREA_CHART", 0, 6, 6, 4, {
481
+ layout: "single",
482
+ bindings: [binding(RetentionAreaVisualization)]
483
+ }),
484
+ widget(dashboardId, "widget_pipeline", "Pipeline Velocity", "SCATTER_PLOT", 6, 6, 6, 4, {
485
+ layout: "single",
486
+ bindings: [binding(PipelineScatterVisualization)]
487
+ }),
488
+ widget(dashboardId, "widget_heatmap", "Engagement Heatmap", "HEATMAP", 0, 10, 8, 4, {
489
+ layout: "single",
490
+ bindings: [binding(EngagementHeatmapVisualization)]
491
+ }),
492
+ widget(dashboardId, "widget_funnel", "Conversion Funnel", "FUNNEL", 8, 10, 4, 4, {
493
+ layout: "single",
494
+ bindings: [binding(ConversionFunnelVisualization)]
495
+ }),
496
+ widget(dashboardId, "widget_geo", "Account Coverage", "MAP", 0, 14, 12, 5, {
497
+ layout: "single",
498
+ bindings: [binding(AccountCoverageGeoVisualization, 360)]
499
+ }),
500
+ widget(dashboardId, "widget_comparison", "Commercial Comparison", "EMBED", 0, 19, 12, 6, {
501
+ layout: "comparison",
502
+ description: "Compare regional distribution, channel balance, and funnel shape.",
503
+ bindings: [
504
+ binding(RegionalRevenueVisualization, 240),
505
+ binding(ChannelMixVisualization, 240),
506
+ binding(ConversionFunnelVisualization, 240)
507
+ ]
508
+ }),
509
+ widget(dashboardId, "widget_timeline", "Growth Timeline", "EMBED", 0, 25, 12, 6, {
510
+ layout: "timeline",
511
+ description: "Track revenue and retention over the same reporting cadence.",
512
+ bindings: [
513
+ binding(RevenueTrendVisualization, 220),
514
+ binding(RetentionAreaVisualization, 220)
515
+ ]
516
+ })
517
+ ];
518
+ }
519
+ function resolveAnalyticsWidget(widget) {
520
+ const config = parseWidgetConfig(widget);
521
+ const bindings = config.bindings.map((binding) => {
522
+ const spec = AnalyticsVisualizationSpecMap.get(visualizationRefKey(binding.ref));
523
+ if (!spec)
524
+ return null;
525
+ return {
526
+ key: `${widget.id}:${visualizationRefKey(binding.ref)}`,
527
+ spec,
528
+ data: binding.data,
529
+ title: binding.title ?? spec.meta.title,
530
+ description: binding.description ?? spec.meta.description,
531
+ height: binding.height
532
+ };
533
+ }).filter((binding) => Boolean(binding));
534
+ if (!bindings.length)
535
+ return null;
536
+ return {
537
+ id: widget.id,
538
+ name: widget.name,
539
+ description: config.description,
540
+ layout: config.layout,
541
+ gridX: widget.gridX,
542
+ gridY: widget.gridY,
543
+ gridWidth: widget.gridWidth,
544
+ gridHeight: widget.gridHeight,
545
+ bindings
546
+ };
547
+ }
548
+ function parseWidgetConfig(widget) {
549
+ if (isAnalyticsWidgetConfig(widget.config)) {
550
+ return widget.config;
551
+ }
552
+ const legacyRef = LEGACY_VISUALIZATION_REFS[widget.type];
553
+ return legacyRef ? {
554
+ layout: "single",
555
+ bindings: [
556
+ {
557
+ ref: legacyRef,
558
+ data: AnalyticsVisualizationSampleData[visualizationRefKey(legacyRef)]
559
+ }
560
+ ]
561
+ } : { layout: "single", bindings: [] };
562
+ }
563
+ function binding(spec, height = 280) {
564
+ return {
565
+ ref: refOf(spec),
566
+ data: AnalyticsVisualizationSampleData[visualizationRefKey(spec.meta)],
567
+ height
568
+ };
569
+ }
570
+ function widget(dashboardId, id, name, type, gridX, gridY, gridWidth, gridHeight, config) {
571
+ const now = new Date;
572
+ return {
573
+ id,
574
+ dashboardId,
575
+ name,
576
+ type,
577
+ gridX,
578
+ gridY,
579
+ gridWidth,
580
+ gridHeight,
581
+ config,
582
+ createdAt: now,
583
+ updatedAt: now
584
+ };
585
+ }
586
+ function isAnalyticsWidgetConfig(value) {
587
+ if (!value || typeof value !== "object")
588
+ return false;
589
+ const candidate = value;
590
+ return (candidate.layout === "single" || candidate.layout === "comparison" || candidate.layout === "timeline") && Array.isArray(candidate.bindings);
591
+ }
1
592
  // src/ui/renderers/analytics.markdown.ts
2
593
  var mockDashboards = [
3
594
  {
@@ -5,83 +596,18 @@ var mockDashboards = [
5
596
  name: "Sales Overview",
6
597
  slug: "sales-overview",
7
598
  status: "PUBLISHED",
8
- widgetCount: 8,
599
+ widgetCount: 11,
9
600
  viewCount: 1250,
10
- lastViewedAt: "2024-01-16T12:00:00Z"
601
+ lastViewedAt: "2026-03-18T12:00:00Z"
11
602
  },
12
603
  {
13
604
  id: "dash-2",
14
605
  name: "User Engagement",
15
606
  slug: "user-engagement",
16
607
  status: "PUBLISHED",
17
- widgetCount: 6,
608
+ widgetCount: 8,
18
609
  viewCount: 890,
19
- lastViewedAt: "2024-01-16T10:00:00Z"
20
- },
21
- {
22
- id: "dash-3",
23
- name: "Product Analytics",
24
- slug: "product-analytics",
25
- status: "PUBLISHED",
26
- widgetCount: 10,
27
- viewCount: 560,
28
- lastViewedAt: "2024-01-15T14:00:00Z"
29
- },
30
- {
31
- id: "dash-4",
32
- name: "Finance Report",
33
- slug: "finance-report",
34
- status: "DRAFT",
35
- widgetCount: 4,
36
- viewCount: 0,
37
- lastViewedAt: null
38
- }
39
- ];
40
- var mockWidgets = [
41
- {
42
- id: "w-1",
43
- dashboardId: "dash-1",
44
- name: "Total Revenue",
45
- type: "METRIC",
46
- value: 125000,
47
- change: 12.5
48
- },
49
- {
50
- id: "w-2",
51
- dashboardId: "dash-1",
52
- name: "Active Users",
53
- type: "METRIC",
54
- value: 4500,
55
- change: 8.2
56
- },
57
- {
58
- id: "w-3",
59
- dashboardId: "dash-1",
60
- name: "Revenue Trend",
61
- type: "LINE_CHART",
62
- dataPoints: 30
63
- },
64
- {
65
- id: "w-4",
66
- dashboardId: "dash-1",
67
- name: "Top Products",
68
- type: "BAR_CHART",
69
- dataPoints: 10
70
- },
71
- {
72
- id: "w-5",
73
- dashboardId: "dash-2",
74
- name: "Daily Active Users",
75
- type: "LINE_CHART",
76
- dataPoints: 30
77
- },
78
- {
79
- id: "w-6",
80
- dashboardId: "dash-2",
81
- name: "Session Duration",
82
- type: "METRIC",
83
- value: 245,
84
- change: -3.1
610
+ lastViewedAt: "2026-03-18T10:00:00Z"
85
611
  }
86
612
  ];
87
613
  var mockQueries = [
@@ -114,19 +640,8 @@ var mockQueries = [
114
640
  executionCount: 450
115
641
  }
116
642
  ];
117
- function formatNumber(value) {
118
- if (value >= 1e6) {
119
- return `${(value / 1e6).toFixed(1)}M`;
120
- }
121
- if (value >= 1000) {
122
- return `${(value / 1000).toFixed(1)}K`;
123
- }
124
- return value.toString();
125
- }
126
- function formatChange(change) {
127
- const icon = change >= 0 ? "\uD83D\uDCC8" : "\uD83D\uDCC9";
128
- const sign = change >= 0 ? "+" : "";
129
- return `${icon} ${sign}${change.toFixed(1)}%`;
643
+ function dashboardWidgets(dashboardId) {
644
+ return createExampleWidgets(dashboardId).map((widget2) => resolveAnalyticsWidget(widget2)).filter((widget2) => Boolean(widget2));
130
645
  }
131
646
  var analyticsDashboardMarkdownRenderer = {
132
647
  target: "markdown",
@@ -134,10 +649,7 @@ var analyticsDashboardMarkdownRenderer = {
134
649
  if (desc.source.type !== "component" || desc.source.componentKey !== "AnalyticsDashboard") {
135
650
  throw new Error("analyticsDashboardMarkdownRenderer: not AnalyticsDashboard");
136
651
  }
137
- const dashboards = mockDashboards;
138
- const widgets = mockWidgets;
139
- const queries = mockQueries;
140
- const dashboard = dashboards[0];
652
+ const dashboard = mockDashboards[0];
141
653
  if (!dashboard) {
142
654
  return {
143
655
  mimeType: "text/markdown",
@@ -146,41 +658,37 @@ var analyticsDashboardMarkdownRenderer = {
146
658
  No dashboards available.`
147
659
  };
148
660
  }
149
- const dashboardWidgets = widgets.filter((w) => w.dashboardId === dashboard.id);
150
- const publishedDashboards = dashboards.filter((d) => d.status === "PUBLISHED");
151
- const totalViews = dashboards.reduce((sum, d) => sum + d.viewCount, 0);
152
- const sharedQueries = queries.filter((q) => q.isShared);
661
+ const widgets = dashboardWidgets(dashboard.id);
662
+ const metricWidgets = widgets.filter((widget2) => widget2.bindings[0]?.spec.visualization.kind === "metric");
153
663
  const lines = [
154
664
  `# ${dashboard.name}`,
155
665
  "",
156
- "> Analytics dashboard overview",
666
+ "> Contract-backed analytics dashboard overview.",
157
667
  "",
158
668
  "## Key Metrics",
159
669
  ""
160
670
  ];
161
- const metricWidgets = dashboardWidgets.filter((w) => w.type === "METRIC");
162
- for (const widget of metricWidgets) {
163
- const w = widget;
164
- lines.push(`### ${w.name}`);
165
- lines.push(`**${formatNumber(w.value)}** ${formatChange(w.change)}`);
166
- lines.push("");
671
+ for (const widget2 of metricWidgets) {
672
+ const binding2 = widget2.bindings[0];
673
+ if (!binding2)
674
+ continue;
675
+ lines.push(`- **${widget2.name}** via \`${binding2.spec.meta.key}\``);
167
676
  }
168
- lines.push("## Visualizations");
169
677
  lines.push("");
170
- const chartWidgets = dashboardWidgets.filter((w) => w.type !== "METRIC");
171
- for (const widget of chartWidgets) {
172
- const w = widget;
173
- lines.push(`- **${w.name}** (${w.type.replace("_", " ")}) - ${w.dataPoints} data points`);
678
+ lines.push("## Visual Blocks");
679
+ lines.push("");
680
+ for (const widget2 of widgets) {
681
+ const kinds = widget2.bindings.map((binding2) => binding2.spec.visualization.kind).join(", ");
682
+ lines.push(`- **${widget2.name}** (${widget2.layout}) → ${kinds}`);
174
683
  }
175
684
  lines.push("");
176
685
  lines.push("## Dashboard Stats");
177
686
  lines.push("");
178
687
  lines.push("| Metric | Value |");
179
688
  lines.push("|--------|-------|");
180
- lines.push(`| Total Dashboards | ${dashboards.length} |`);
181
- lines.push(`| Published | ${publishedDashboards.length} |`);
182
- lines.push(`| Total Views | ${totalViews.toLocaleString()} |`);
183
- lines.push(`| Shared Queries | ${sharedQueries.length} |`);
689
+ lines.push(`| Total Dashboards | ${mockDashboards.length} |`);
690
+ lines.push(`| Published | ${mockDashboards.filter((item) => item.status === "PUBLISHED").length} |`);
691
+ lines.push(`| Shared Queries | ${mockQueries.filter((query) => query.isShared).length} |`);
184
692
  return {
185
693
  mimeType: "text/markdown",
186
694
  body: lines.join(`
@@ -194,7 +702,6 @@ var dashboardListMarkdownRenderer = {
194
702
  if (desc.source.type !== "component" || desc.source.componentKey !== "DashboardList") {
195
703
  throw new Error("dashboardListMarkdownRenderer: not DashboardList");
196
704
  }
197
- const dashboards = mockDashboards;
198
705
  const lines = [
199
706
  "# Dashboards",
200
707
  "",
@@ -203,17 +710,11 @@ var dashboardListMarkdownRenderer = {
203
710
  "| Dashboard | Widgets | Views | Status | Last Viewed |",
204
711
  "|-----------|---------|-------|--------|-------------|"
205
712
  ];
206
- for (const dashboard of dashboards) {
713
+ for (const dashboard of mockDashboards) {
207
714
  const lastViewed = dashboard.lastViewedAt ? new Date(dashboard.lastViewedAt).toLocaleDateString() : "Never";
208
715
  const statusIcon = dashboard.status === "PUBLISHED" ? "\uD83D\uDFE2" : "⚫";
209
716
  lines.push(`| [${dashboard.name}](/dashboards/${dashboard.slug}) | ${dashboard.widgetCount} | ${dashboard.viewCount.toLocaleString()} | ${statusIcon} ${dashboard.status} | ${lastViewed} |`);
210
717
  }
211
- lines.push("");
212
- lines.push("## Quick Actions");
213
- lines.push("");
214
- lines.push("- **Create Dashboard** - Start with a blank canvas");
215
- lines.push("- **Import Template** - Use a pre-built template");
216
- lines.push("- **Clone Dashboard** - Duplicate an existing dashboard");
217
718
  return {
218
719
  mimeType: "text/markdown",
219
720
  body: lines.join(`
@@ -227,40 +728,21 @@ var queryBuilderMarkdownRenderer = {
227
728
  if (desc.source.type !== "component" || desc.source.componentKey !== "QueryBuilder") {
228
729
  throw new Error("queryBuilderMarkdownRenderer: not QueryBuilder");
229
730
  }
230
- const queries = mockQueries;
231
731
  const lines = [
232
732
  "# Query Builder",
233
733
  "",
234
- "> Create and manage data queries",
235
- "",
236
- "## Saved Queries",
734
+ "> Create and manage reusable data queries.",
237
735
  "",
238
736
  "| Query | Type | Shared | Executions |",
239
737
  "|-------|------|--------|------------|"
240
738
  ];
241
- for (const query of queries) {
242
- const sharedIcon = query.isShared ? "\uD83C\uDF10" : "\uD83D\uDD12";
243
- lines.push(`| ${query.name} | ${query.type} | ${sharedIcon} | ${query.executionCount.toLocaleString()} |`);
739
+ for (const query of mockQueries) {
740
+ lines.push(`| ${query.name} | ${query.type} | ${query.isShared ? "\uD83C\uDF10" : "\uD83D\uDD12"} | ${query.executionCount.toLocaleString()} |`);
244
741
  }
245
742
  lines.push("");
246
- lines.push("## Query Types");
247
- lines.push("");
248
- lines.push("### METRIC");
249
- lines.push("Query usage metrics from the metering system.");
250
- lines.push("");
251
- lines.push("### AGGREGATION");
252
- lines.push("Build aggregations with measures and dimensions without writing SQL.");
253
- lines.push("");
254
- lines.push("### SQL");
255
- lines.push("Write custom SQL queries for advanced analysis.");
256
- lines.push("");
257
- lines.push("## Features");
743
+ lines.push("## Visualization Contracts");
258
744
  lines.push("");
259
- lines.push("- Visual query builder");
260
- lines.push("- Auto-complete for fields and functions");
261
- lines.push("- Query validation and optimization");
262
- lines.push("- Result caching");
263
- lines.push("- Query sharing and collaboration");
745
+ lines.push("Widgets reference `VisualizationSpec` contracts instead of rendering ad-hoc widget types.");
264
746
  return {
265
747
  mimeType: "text/markdown",
266
748
  body: lines.join(`