@contractspec/example.analytics-dashboard 1.56.1 → 1.58.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 (171) hide show
  1. package/dist/browser/dashboard/dashboard.enum.js +34 -0
  2. package/dist/browser/dashboard/dashboard.operation.js +289 -0
  3. package/dist/browser/dashboard/dashboard.presentation.js +197 -0
  4. package/dist/browser/dashboard/dashboard.schema.js +126 -0
  5. package/dist/browser/dashboard/dashboard.test-spec.js +213 -0
  6. package/dist/browser/dashboard/index.js +299 -0
  7. package/dist/browser/dashboard.feature.js +84 -0
  8. package/dist/browser/datasource/posthog-datasource.js +289 -0
  9. package/dist/browser/docs/analytics-dashboard.docblock.js +103 -0
  10. package/dist/browser/docs/index.js +103 -0
  11. package/dist/browser/events.js +81 -0
  12. package/dist/browser/example.js +42 -0
  13. package/dist/browser/handlers/analytics.handlers.js +278 -0
  14. package/dist/browser/handlers/index.js +571 -0
  15. package/dist/browser/handlers/query.handlers.js +294 -0
  16. package/dist/browser/index.js +1677 -0
  17. package/dist/browser/query/index.js +159 -0
  18. package/dist/browser/query/query.enum.js +11 -0
  19. package/dist/browser/query/query.operation.js +154 -0
  20. package/dist/browser/query/query.presentation.js +119 -0
  21. package/dist/browser/query/query.schema.js +70 -0
  22. package/dist/browser/query/query.test-spec.js +113 -0
  23. package/dist/browser/query-engine/index.js +491 -0
  24. package/dist/browser/seeders/index.js +20 -0
  25. package/dist/browser/ui/AnalyticsDashboard.js +394 -0
  26. package/dist/browser/ui/hooks/index.js +69 -0
  27. package/dist/browser/ui/hooks/useAnalyticsData.js +66 -0
  28. package/dist/browser/ui/index.js +671 -0
  29. package/dist/browser/ui/renderers/analytics.markdown.js +275 -0
  30. package/dist/browser/ui/renderers/index.js +275 -0
  31. package/dist/dashboard/dashboard.enum.d.ts +3 -8
  32. package/dist/dashboard/dashboard.enum.d.ts.map +1 -1
  33. package/dist/dashboard/dashboard.enum.js +31 -39
  34. package/dist/dashboard/dashboard.operation.d.ts +444 -450
  35. package/dist/dashboard/dashboard.operation.d.ts.map +1 -1
  36. package/dist/dashboard/dashboard.operation.js +284 -207
  37. package/dist/dashboard/dashboard.presentation.d.ts +3 -8
  38. package/dist/dashboard/dashboard.presentation.d.ts.map +1 -1
  39. package/dist/dashboard/dashboard.presentation.js +193 -85
  40. package/dist/dashboard/dashboard.schema.d.ts +289 -294
  41. package/dist/dashboard/dashboard.schema.d.ts.map +1 -1
  42. package/dist/dashboard/dashboard.schema.js +119 -228
  43. package/dist/dashboard/dashboard.test-spec.d.ts +4 -9
  44. package/dist/dashboard/dashboard.test-spec.d.ts.map +1 -1
  45. package/dist/dashboard/dashboard.test-spec.js +209 -228
  46. package/dist/dashboard/index.d.ts +7 -4
  47. package/dist/dashboard/index.d.ts.map +1 -0
  48. package/dist/dashboard/index.js +299 -4
  49. package/dist/dashboard.feature.d.ts +1 -7
  50. package/dist/dashboard.feature.d.ts.map +1 -1
  51. package/dist/dashboard.feature.js +83 -175
  52. package/dist/datasource/posthog-datasource.d.ts +18 -0
  53. package/dist/datasource/posthog-datasource.d.ts.map +1 -0
  54. package/dist/datasource/posthog-datasource.js +290 -0
  55. package/dist/docs/analytics-dashboard.docblock.d.ts +2 -1
  56. package/dist/docs/analytics-dashboard.docblock.d.ts.map +1 -0
  57. package/dist/docs/analytics-dashboard.docblock.js +46 -56
  58. package/dist/docs/index.d.ts +2 -1
  59. package/dist/docs/index.d.ts.map +1 -0
  60. package/dist/docs/index.js +104 -1
  61. package/dist/events.d.ts +109 -115
  62. package/dist/events.d.ts.map +1 -1
  63. package/dist/events.js +74 -120
  64. package/dist/example.d.ts +2 -6
  65. package/dist/example.d.ts.map +1 -1
  66. package/dist/example.js +40 -55
  67. package/dist/handlers/analytics.handlers.d.ts +110 -110
  68. package/dist/handlers/analytics.handlers.d.ts.map +1 -1
  69. package/dist/handlers/analytics.handlers.js +267 -298
  70. package/dist/handlers/index.d.ts +3 -2
  71. package/dist/handlers/index.d.ts.map +1 -0
  72. package/dist/handlers/index.js +571 -2
  73. package/dist/handlers/query.handlers.d.ts +9 -0
  74. package/dist/handlers/query.handlers.d.ts.map +1 -0
  75. package/dist/handlers/query.handlers.js +295 -0
  76. package/dist/index.d.ts +12 -12
  77. package/dist/index.d.ts.map +1 -0
  78. package/dist/index.js +1678 -13
  79. package/dist/node/dashboard/dashboard.enum.js +34 -0
  80. package/dist/node/dashboard/dashboard.operation.js +289 -0
  81. package/dist/node/dashboard/dashboard.presentation.js +197 -0
  82. package/dist/node/dashboard/dashboard.schema.js +126 -0
  83. package/dist/node/dashboard/dashboard.test-spec.js +213 -0
  84. package/dist/node/dashboard/index.js +299 -0
  85. package/dist/node/dashboard.feature.js +84 -0
  86. package/dist/node/datasource/posthog-datasource.js +289 -0
  87. package/dist/node/docs/analytics-dashboard.docblock.js +103 -0
  88. package/dist/node/docs/index.js +103 -0
  89. package/dist/node/events.js +81 -0
  90. package/dist/node/example.js +42 -0
  91. package/dist/node/handlers/analytics.handlers.js +278 -0
  92. package/dist/node/handlers/index.js +571 -0
  93. package/dist/node/handlers/query.handlers.js +294 -0
  94. package/dist/node/index.js +1677 -0
  95. package/dist/node/query/index.js +159 -0
  96. package/dist/node/query/query.enum.js +11 -0
  97. package/dist/node/query/query.operation.js +154 -0
  98. package/dist/node/query/query.presentation.js +119 -0
  99. package/dist/node/query/query.schema.js +70 -0
  100. package/dist/node/query/query.test-spec.js +113 -0
  101. package/dist/node/query-engine/index.js +491 -0
  102. package/dist/node/seeders/index.js +20 -0
  103. package/dist/node/ui/AnalyticsDashboard.js +394 -0
  104. package/dist/node/ui/hooks/index.js +69 -0
  105. package/dist/node/ui/hooks/useAnalyticsData.js +66 -0
  106. package/dist/node/ui/index.js +671 -0
  107. package/dist/node/ui/renderers/analytics.markdown.js +275 -0
  108. package/dist/node/ui/renderers/index.js +275 -0
  109. package/dist/query/index.d.ts +7 -4
  110. package/dist/query/index.d.ts.map +1 -0
  111. package/dist/query/index.js +159 -4
  112. package/dist/query/query.enum.d.ts +1 -6
  113. package/dist/query/query.enum.d.ts.map +1 -1
  114. package/dist/query/query.enum.js +10 -14
  115. package/dist/query/query.operation.d.ts +148 -154
  116. package/dist/query/query.operation.d.ts.map +1 -1
  117. package/dist/query/query.operation.js +151 -109
  118. package/dist/query/query.presentation.d.ts +2 -7
  119. package/dist/query/query.presentation.d.ts.map +1 -1
  120. package/dist/query/query.presentation.js +116 -56
  121. package/dist/query/query.schema.d.ts +121 -126
  122. package/dist/query/query.schema.d.ts.map +1 -1
  123. package/dist/query/query.schema.js +66 -152
  124. package/dist/query/query.test-spec.d.ts +2 -7
  125. package/dist/query/query.test-spec.d.ts.map +1 -1
  126. package/dist/query/query.test-spec.js +111 -121
  127. package/dist/query-engine/index.d.ts +84 -86
  128. package/dist/query-engine/index.d.ts.map +1 -1
  129. package/dist/query-engine/index.js +490 -187
  130. package/dist/seeders/index.d.ts +4 -8
  131. package/dist/seeders/index.d.ts.map +1 -1
  132. package/dist/seeders/index.js +18 -16
  133. package/dist/ui/AnalyticsDashboard.d.ts +1 -6
  134. package/dist/ui/AnalyticsDashboard.d.ts.map +1 -1
  135. package/dist/ui/AnalyticsDashboard.js +389 -259
  136. package/dist/ui/hooks/index.d.ts +2 -2
  137. package/dist/ui/hooks/index.d.ts.map +1 -0
  138. package/dist/ui/hooks/index.js +69 -4
  139. package/dist/ui/hooks/useAnalyticsData.d.ts +16 -20
  140. package/dist/ui/hooks/useAnalyticsData.d.ts.map +1 -1
  141. package/dist/ui/hooks/useAnalyticsData.js +63 -69
  142. package/dist/ui/index.d.ts +7 -6
  143. package/dist/ui/index.d.ts.map +1 -0
  144. package/dist/ui/index.js +671 -5
  145. package/dist/ui/renderers/analytics.markdown.d.ts +13 -15
  146. package/dist/ui/renderers/analytics.markdown.d.ts.map +1 -1
  147. package/dist/ui/renderers/analytics.markdown.js +266 -254
  148. package/dist/ui/renderers/index.d.ts +2 -2
  149. package/dist/ui/renderers/index.d.ts.map +1 -0
  150. package/dist/ui/renderers/index.js +275 -2
  151. package/package.json +329 -62
  152. package/dist/dashboard/dashboard.enum.js.map +0 -1
  153. package/dist/dashboard/dashboard.operation.js.map +0 -1
  154. package/dist/dashboard/dashboard.presentation.js.map +0 -1
  155. package/dist/dashboard/dashboard.schema.js.map +0 -1
  156. package/dist/dashboard/dashboard.test-spec.js.map +0 -1
  157. package/dist/dashboard.feature.js.map +0 -1
  158. package/dist/docs/analytics-dashboard.docblock.js.map +0 -1
  159. package/dist/events.js.map +0 -1
  160. package/dist/example.js.map +0 -1
  161. package/dist/handlers/analytics.handlers.js.map +0 -1
  162. package/dist/query/query.enum.js.map +0 -1
  163. package/dist/query/query.operation.js.map +0 -1
  164. package/dist/query/query.presentation.js.map +0 -1
  165. package/dist/query/query.schema.js.map +0 -1
  166. package/dist/query/query.test-spec.js.map +0 -1
  167. package/dist/query-engine/index.js.map +0 -1
  168. package/dist/seeders/index.js.map +0 -1
  169. package/dist/ui/AnalyticsDashboard.js.map +0 -1
  170. package/dist/ui/hooks/useAnalyticsData.js.map +0 -1
  171. package/dist/ui/renderers/analytics.markdown.js.map +0 -1
package/dist/ui/index.js CHANGED
@@ -1,6 +1,672 @@
1
- import { analyticsDashboardMarkdownRenderer, dashboardListMarkdownRenderer, queryBuilderMarkdownRenderer } from "./renderers/analytics.markdown.js";
2
- import { useAnalyticsData } from "./hooks/useAnalyticsData.js";
3
- import { AnalyticsDashboard } from "./AnalyticsDashboard.js";
4
- import "./hooks/index.js";
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"
12
+ },
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
+ },
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"
30
+ },
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
39
+ }
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
49
+ },
50
+ {
51
+ id: "w-2",
52
+ dashboardId: "dash-1",
53
+ name: "Active Users",
54
+ type: "METRIC",
55
+ value: 4500,
56
+ change: 8.2
57
+ },
58
+ {
59
+ id: "w-3",
60
+ dashboardId: "dash-1",
61
+ name: "Revenue Trend",
62
+ type: "LINE_CHART",
63
+ dataPoints: 30
64
+ },
65
+ {
66
+ id: "w-4",
67
+ dashboardId: "dash-1",
68
+ name: "Top Products",
69
+ type: "BAR_CHART",
70
+ dataPoints: 10
71
+ },
72
+ {
73
+ id: "w-5",
74
+ dashboardId: "dash-2",
75
+ name: "Daily Active Users",
76
+ type: "LINE_CHART",
77
+ dataPoints: 30
78
+ },
79
+ {
80
+ id: "w-6",
81
+ dashboardId: "dash-2",
82
+ name: "Session Duration",
83
+ type: "METRIC",
84
+ value: 245,
85
+ change: -3.1
86
+ }
87
+ ];
88
+ var mockQueries = [
89
+ {
90
+ id: "q-1",
91
+ name: "Monthly Revenue",
92
+ type: "AGGREGATION",
93
+ isShared: true,
94
+ executionCount: 1500
95
+ },
96
+ {
97
+ id: "q-2",
98
+ name: "User Growth",
99
+ type: "METRIC",
100
+ isShared: true,
101
+ executionCount: 890
102
+ },
103
+ {
104
+ id: "q-3",
105
+ name: "Product Sales",
106
+ type: "SQL",
107
+ isShared: false,
108
+ executionCount: 340
109
+ },
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`;
124
+ }
125
+ return value.toString();
126
+ }
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)}%`;
131
+ }
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
5
146
 
6
- export { AnalyticsDashboard, analyticsDashboardMarkdownRenderer, dashboardListMarkdownRenderer, queryBuilderMarkdownRenderer, useAnalyticsData };
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
+ }
191
+ };
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");
218
+ return {
219
+ mimeType: "text/markdown",
220
+ body: lines.join(`
221
+ `)
222
+ };
223
+ }
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
+ };
270
+ }
271
+ };
272
+ // src/ui/hooks/useAnalyticsData.ts
273
+ import { useCallback, useEffect, useState } from "react";
274
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
275
+ "use client";
276
+ function useAnalyticsData(projectId = "local-project") {
277
+ const { handlers } = useTemplateRuntime();
278
+ const analytics = handlers.analytics;
279
+ const [dashboards, setDashboards] = useState([]);
280
+ const [queries, setQueries] = useState([]);
281
+ const [selectedDashboard, setSelectedDashboard] = useState(null);
282
+ const [widgets, setWidgets] = useState([]);
283
+ const [loading, setLoading] = useState(true);
284
+ const [error, setError] = useState(null);
285
+ const fetchData = useCallback(async () => {
286
+ try {
287
+ setLoading(true);
288
+ setError(null);
289
+ const [dashResult, queryResult] = await Promise.all([
290
+ analytics.listDashboards({ projectId, limit: 100 }),
291
+ analytics.listQueries({ projectId, limit: 100 })
292
+ ]);
293
+ setDashboards(dashResult.dashboards);
294
+ setQueries(queryResult.queries);
295
+ if (dashResult.dashboards.length > 0 && !selectedDashboard) {
296
+ const first = dashResult.dashboards[0];
297
+ if (first) {
298
+ setSelectedDashboard(first);
299
+ const dashboardWidgets = await analytics.getWidgets(first.id);
300
+ setWidgets(dashboardWidgets);
301
+ }
302
+ }
303
+ } catch (err) {
304
+ setError(err instanceof Error ? err : new Error("Failed to load analytics"));
305
+ } finally {
306
+ setLoading(false);
307
+ }
308
+ }, [analytics, projectId, selectedDashboard]);
309
+ useEffect(() => {
310
+ fetchData();
311
+ }, [fetchData]);
312
+ const selectDashboard = useCallback(async (dashboard) => {
313
+ setSelectedDashboard(dashboard);
314
+ const dashboardWidgets = await analytics.getWidgets(dashboard.id);
315
+ setWidgets(dashboardWidgets);
316
+ }, [analytics]);
317
+ const stats = {
318
+ totalDashboards: dashboards.length,
319
+ publishedDashboards: dashboards.filter((d) => d.status === "PUBLISHED").length,
320
+ totalQueries: queries.length,
321
+ sharedQueries: queries.filter((q) => q.isShared).length
322
+ };
323
+ return {
324
+ dashboards,
325
+ queries,
326
+ selectedDashboard,
327
+ widgets,
328
+ loading,
329
+ error,
330
+ stats,
331
+ refetch: fetchData,
332
+ selectDashboard
333
+ };
334
+ }
335
+
336
+ // src/ui/AnalyticsDashboard.tsx
337
+ import { useState as useState2 } from "react";
338
+ import {
339
+ Button,
340
+ ErrorState,
341
+ LoaderBlock,
342
+ StatCard,
343
+ StatCardGroup
344
+ } from "@contractspec/lib.design-system";
345
+ import { jsxDEV } from "react/jsx-dev-runtime";
346
+ "use client";
347
+ var STATUS_COLORS = {
348
+ PUBLISHED: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
349
+ DRAFT: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400",
350
+ ARCHIVED: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400"
351
+ };
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
+ function AnalyticsDashboard() {
373
+ const [activeTab, setActiveTab] = useState2("dashboards");
374
+ const {
375
+ dashboards,
376
+ queries,
377
+ selectedDashboard,
378
+ widgets,
379
+ loading,
380
+ error,
381
+ stats,
382
+ refetch,
383
+ selectDashboard
384
+ } = useAnalyticsData();
385
+ const tabs = [
386
+ { id: "dashboards", label: "Dashboards", icon: "\uD83D\uDCCA" },
387
+ { id: "queries", label: "Queries", icon: "\uD83D\uDD0D" }
388
+ ];
389
+ if (loading) {
390
+ return /* @__PURE__ */ jsxDEV(LoaderBlock, {
391
+ label: "Loading Analytics..."
392
+ }, undefined, false, undefined, this);
393
+ }
394
+ if (error) {
395
+ return /* @__PURE__ */ jsxDEV(ErrorState, {
396
+ title: "Failed to load Analytics",
397
+ description: error.message,
398
+ onRetry: refetch,
399
+ retryLabel: "Retry"
400
+ }, undefined, false, undefined, this);
401
+ }
402
+ return /* @__PURE__ */ jsxDEV("div", {
403
+ className: "space-y-6",
404
+ children: [
405
+ /* @__PURE__ */ jsxDEV("div", {
406
+ className: "flex items-center justify-between",
407
+ children: [
408
+ /* @__PURE__ */ jsxDEV("h2", {
409
+ className: "text-2xl font-bold",
410
+ children: "Analytics Dashboard"
411
+ }, undefined, false, undefined, this),
412
+ /* @__PURE__ */ jsxDEV(Button, {
413
+ onClick: () => alert("Create dashboard modal"),
414
+ children: [
415
+ /* @__PURE__ */ jsxDEV("span", {
416
+ className: "mr-2",
417
+ children: "+"
418
+ }, undefined, false, undefined, this),
419
+ " New Dashboard"
420
+ ]
421
+ }, undefined, true, undefined, this)
422
+ ]
423
+ }, undefined, true, undefined, this),
424
+ /* @__PURE__ */ jsxDEV(StatCardGroup, {
425
+ children: [
426
+ /* @__PURE__ */ jsxDEV(StatCard, {
427
+ label: "Dashboards",
428
+ value: stats.totalDashboards,
429
+ hint: `${stats.publishedDashboards} published`
430
+ }, undefined, false, undefined, this),
431
+ /* @__PURE__ */ jsxDEV(StatCard, {
432
+ label: "Queries",
433
+ value: stats.totalQueries,
434
+ hint: `${stats.sharedQueries} shared`
435
+ }, undefined, false, undefined, this),
436
+ /* @__PURE__ */ jsxDEV(StatCard, {
437
+ label: "Widgets",
438
+ value: widgets.length,
439
+ hint: "on current dashboard"
440
+ }, undefined, false, undefined, this)
441
+ ]
442
+ }, undefined, true, undefined, this),
443
+ /* @__PURE__ */ jsxDEV("nav", {
444
+ className: "bg-muted flex gap-1 rounded-lg p-1",
445
+ role: "tablist",
446
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV(Button, {
447
+ type: "button",
448
+ role: "tab",
449
+ "aria-selected": activeTab === tab.id,
450
+ 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"}`,
452
+ children: [
453
+ /* @__PURE__ */ jsxDEV("span", {
454
+ children: tab.icon
455
+ }, undefined, false, undefined, this),
456
+ tab.label
457
+ ]
458
+ }, tab.id, true, undefined, this))
459
+ }, undefined, false, undefined, this),
460
+ /* @__PURE__ */ jsxDEV("div", {
461
+ className: "min-h-[400px]",
462
+ role: "tabpanel",
463
+ children: [
464
+ activeTab === "dashboards" && /* @__PURE__ */ jsxDEV("div", {
465
+ className: "space-y-6",
466
+ children: [
467
+ /* @__PURE__ */ jsxDEV("div", {
468
+ className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
469
+ children: [
470
+ dashboards.map((dashboard) => /* @__PURE__ */ jsxDEV("div", {
471
+ 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"}`,
473
+ role: "button",
474
+ tabIndex: 0,
475
+ onKeyDown: (e) => {
476
+ if (e.key === "Enter" || e.key === " ")
477
+ selectDashboard(dashboard);
478
+ },
479
+ children: [
480
+ /* @__PURE__ */ jsxDEV("div", {
481
+ className: "mb-2 flex items-center justify-between",
482
+ children: [
483
+ /* @__PURE__ */ jsxDEV("h3", {
484
+ className: "font-medium",
485
+ children: dashboard.name
486
+ }, 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] ?? ""}`,
489
+ children: dashboard.status
490
+ }, undefined, false, undefined, this)
491
+ ]
492
+ }, undefined, true, undefined, this),
493
+ /* @__PURE__ */ jsxDEV("p", {
494
+ className: "text-muted-foreground mb-3 text-sm",
495
+ children: dashboard.description
496
+ }, undefined, false, undefined, this),
497
+ /* @__PURE__ */ jsxDEV("div", {
498
+ className: "text-muted-foreground flex items-center justify-between text-xs",
499
+ children: [
500
+ /* @__PURE__ */ jsxDEV("span", {
501
+ children: [
502
+ "/",
503
+ dashboard.slug
504
+ ]
505
+ }, undefined, true, undefined, this),
506
+ dashboard.isPublic && /* @__PURE__ */ jsxDEV("span", {
507
+ className: "text-green-600 dark:text-green-400",
508
+ children: "\uD83C\uDF10 Public"
509
+ }, undefined, false, undefined, this)
510
+ ]
511
+ }, undefined, true, undefined, this)
512
+ ]
513
+ }, 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",
516
+ children: "No dashboards created yet"
517
+ }, undefined, false, undefined, this)
518
+ ]
519
+ }, 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)
571
+ ]
572
+ }, 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)
657
+ }, undefined, false, undefined, this)
658
+ ]
659
+ }, undefined, true, undefined, this)
660
+ ]
661
+ }, undefined, true, undefined, this);
662
+ }
663
+
664
+ // src/ui/hooks/index.ts
665
+ "use client";
666
+ export {
667
+ useAnalyticsData,
668
+ queryBuilderMarkdownRenderer,
669
+ dashboardListMarkdownRenderer,
670
+ analyticsDashboardMarkdownRenderer,
671
+ AnalyticsDashboard
672
+ };