@contractspec/example.analytics-dashboard 3.7.6 → 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.
- package/README.md +64 -271
- package/dist/browser/dashboard.feature.js +592 -0
- package/dist/browser/events.js +1 -1
- package/dist/browser/index.js +1171 -627
- package/dist/browser/ui/AnalyticsDashboard.js +826 -194
- package/dist/browser/ui/AnalyticsDashboard.widgets.js +94 -0
- package/dist/browser/ui/AnalyticsQueriesTable.js +99 -0
- package/dist/browser/ui/hooks/index.js +594 -3
- package/dist/browser/ui/hooks/useAnalyticsData.js +594 -3
- package/dist/browser/ui/index.js +964 -440
- package/dist/browser/ui/renderers/analytics.markdown.js +620 -138
- package/dist/browser/ui/renderers/index.js +620 -138
- package/dist/browser/visualizations/catalog.js +457 -0
- package/dist/browser/visualizations/index.js +611 -0
- package/dist/browser/visualizations/specs.breakdown.js +140 -0
- package/dist/browser/visualizations/specs.performance.js +198 -0
- package/dist/browser/visualizations/widgets.js +595 -0
- package/dist/dashboard/index.d.ts +3 -3
- package/dist/dashboard.feature.js +592 -0
- package/dist/events.js +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1171 -627
- package/dist/node/dashboard.feature.js +592 -0
- package/dist/node/events.js +1 -1
- package/dist/node/index.js +1171 -627
- package/dist/node/ui/AnalyticsDashboard.js +826 -194
- package/dist/node/ui/AnalyticsDashboard.widgets.js +94 -0
- package/dist/node/ui/AnalyticsQueriesTable.js +99 -0
- package/dist/node/ui/hooks/index.js +594 -3
- package/dist/node/ui/hooks/useAnalyticsData.js +594 -3
- package/dist/node/ui/index.js +964 -440
- package/dist/node/ui/renderers/analytics.markdown.js +620 -138
- package/dist/node/ui/renderers/index.js +620 -138
- package/dist/node/visualizations/catalog.js +457 -0
- package/dist/node/visualizations/index.js +611 -0
- package/dist/node/visualizations/specs.breakdown.js +140 -0
- package/dist/node/visualizations/specs.performance.js +198 -0
- package/dist/node/visualizations/widgets.js +595 -0
- package/dist/query/index.d.ts +1 -1
- package/dist/query-engine/index.d.ts +1 -1
- package/dist/ui/AnalyticsDashboard.js +826 -194
- package/dist/ui/AnalyticsDashboard.widgets.d.ts +5 -0
- package/dist/ui/AnalyticsDashboard.widgets.js +95 -0
- package/dist/ui/AnalyticsQueriesTable.d.ts +4 -0
- package/dist/ui/AnalyticsQueriesTable.js +100 -0
- package/dist/ui/hooks/index.d.ts +1 -1
- package/dist/ui/hooks/index.js +594 -3
- package/dist/ui/hooks/useAnalyticsData.js +594 -3
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +964 -440
- package/dist/ui/renderers/analytics.markdown.d.ts +0 -9
- package/dist/ui/renderers/analytics.markdown.js +620 -138
- package/dist/ui/renderers/index.js +620 -138
- package/dist/visualizations/catalog.d.ts +9 -0
- package/dist/visualizations/catalog.js +458 -0
- package/dist/visualizations/index.d.ts +4 -0
- package/dist/visualizations/index.js +612 -0
- package/dist/visualizations/specs.breakdown.d.ts +4 -0
- package/dist/visualizations/specs.breakdown.js +141 -0
- package/dist/visualizations/specs.performance.d.ts +5 -0
- package/dist/visualizations/specs.performance.js +199 -0
- package/dist/visualizations/widgets.d.ts +24 -0
- package/dist/visualizations/widgets.js +596 -0
- package/dist/visualizations/widgets.test.d.ts +1 -0
- package/package.json +105 -7
|
@@ -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:
|
|
599
|
+
widgetCount: 11,
|
|
9
600
|
viewCount: 1250,
|
|
10
|
-
lastViewedAt: "
|
|
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:
|
|
608
|
+
widgetCount: 8,
|
|
18
609
|
viewCount: 890,
|
|
19
|
-
lastViewedAt: "
|
|
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
|
|
118
|
-
|
|
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
|
|
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
|
|
150
|
-
const
|
|
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
|
-
">
|
|
666
|
+
"> Contract-backed analytics dashboard overview.",
|
|
157
667
|
"",
|
|
158
668
|
"## Key Metrics",
|
|
159
669
|
""
|
|
160
670
|
];
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
lines.push(
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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 | ${
|
|
181
|
-
lines.push(`| Published | ${
|
|
182
|
-
lines.push(`|
|
|
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
|
|
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
|
|
242
|
-
|
|
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("##
|
|
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("-
|
|
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(`
|