@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
@@ -0,0 +1,394 @@
1
+ // src/ui/hooks/useAnalyticsData.ts
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
4
+ "use client";
5
+ function useAnalyticsData(projectId = "local-project") {
6
+ const { handlers } = useTemplateRuntime();
7
+ const analytics = handlers.analytics;
8
+ const [dashboards, setDashboards] = useState([]);
9
+ const [queries, setQueries] = useState([]);
10
+ const [selectedDashboard, setSelectedDashboard] = useState(null);
11
+ const [widgets, setWidgets] = useState([]);
12
+ const [loading, setLoading] = useState(true);
13
+ const [error, setError] = useState(null);
14
+ const fetchData = useCallback(async () => {
15
+ try {
16
+ setLoading(true);
17
+ setError(null);
18
+ const [dashResult, queryResult] = await Promise.all([
19
+ analytics.listDashboards({ projectId, limit: 100 }),
20
+ analytics.listQueries({ projectId, limit: 100 })
21
+ ]);
22
+ setDashboards(dashResult.dashboards);
23
+ setQueries(queryResult.queries);
24
+ if (dashResult.dashboards.length > 0 && !selectedDashboard) {
25
+ const first = dashResult.dashboards[0];
26
+ if (first) {
27
+ setSelectedDashboard(first);
28
+ const dashboardWidgets = await analytics.getWidgets(first.id);
29
+ setWidgets(dashboardWidgets);
30
+ }
31
+ }
32
+ } catch (err) {
33
+ setError(err instanceof Error ? err : new Error("Failed to load analytics"));
34
+ } finally {
35
+ setLoading(false);
36
+ }
37
+ }, [analytics, projectId, selectedDashboard]);
38
+ useEffect(() => {
39
+ fetchData();
40
+ }, [fetchData]);
41
+ const selectDashboard = useCallback(async (dashboard) => {
42
+ setSelectedDashboard(dashboard);
43
+ const dashboardWidgets = await analytics.getWidgets(dashboard.id);
44
+ setWidgets(dashboardWidgets);
45
+ }, [analytics]);
46
+ const stats = {
47
+ totalDashboards: dashboards.length,
48
+ publishedDashboards: dashboards.filter((d) => d.status === "PUBLISHED").length,
49
+ totalQueries: queries.length,
50
+ sharedQueries: queries.filter((q) => q.isShared).length
51
+ };
52
+ return {
53
+ dashboards,
54
+ queries,
55
+ selectedDashboard,
56
+ widgets,
57
+ loading,
58
+ error,
59
+ stats,
60
+ refetch: fetchData,
61
+ selectDashboard
62
+ };
63
+ }
64
+
65
+ // src/ui/AnalyticsDashboard.tsx
66
+ import { useState as useState2 } from "react";
67
+ import {
68
+ Button,
69
+ ErrorState,
70
+ LoaderBlock,
71
+ StatCard,
72
+ StatCardGroup
73
+ } from "@contractspec/lib.design-system";
74
+ import { jsxDEV } from "react/jsx-dev-runtime";
75
+ "use client";
76
+ var STATUS_COLORS = {
77
+ PUBLISHED: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
78
+ DRAFT: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400",
79
+ ARCHIVED: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400"
80
+ };
81
+ var WIDGET_ICONS = {
82
+ LINE_CHART: "\uD83D\uDCC8",
83
+ BAR_CHART: "\uD83D\uDCCA",
84
+ PIE_CHART: "\uD83E\uDD67",
85
+ AREA_CHART: "\uD83D\uDCC9",
86
+ SCATTER_PLOT: "⚬",
87
+ METRIC: "\uD83D\uDD22",
88
+ TABLE: "\uD83D\uDCCB",
89
+ HEATMAP: "\uD83D\uDDFA️",
90
+ FUNNEL: "⏬",
91
+ MAP: "\uD83C\uDF0D",
92
+ TEXT: "\uD83D\uDCDD",
93
+ EMBED: "\uD83D\uDD17"
94
+ };
95
+ var QUERY_TYPE_COLORS = {
96
+ SQL: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
97
+ METRIC: "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400",
98
+ AGGREGATION: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400",
99
+ CUSTOM: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400"
100
+ };
101
+ function AnalyticsDashboard() {
102
+ const [activeTab, setActiveTab] = useState2("dashboards");
103
+ const {
104
+ dashboards,
105
+ queries,
106
+ selectedDashboard,
107
+ widgets,
108
+ loading,
109
+ error,
110
+ stats,
111
+ refetch,
112
+ selectDashboard
113
+ } = useAnalyticsData();
114
+ const tabs = [
115
+ { id: "dashboards", label: "Dashboards", icon: "\uD83D\uDCCA" },
116
+ { id: "queries", label: "Queries", icon: "\uD83D\uDD0D" }
117
+ ];
118
+ if (loading) {
119
+ return /* @__PURE__ */ jsxDEV(LoaderBlock, {
120
+ label: "Loading Analytics..."
121
+ }, undefined, false, undefined, this);
122
+ }
123
+ if (error) {
124
+ return /* @__PURE__ */ jsxDEV(ErrorState, {
125
+ title: "Failed to load Analytics",
126
+ description: error.message,
127
+ onRetry: refetch,
128
+ retryLabel: "Retry"
129
+ }, undefined, false, undefined, this);
130
+ }
131
+ return /* @__PURE__ */ jsxDEV("div", {
132
+ className: "space-y-6",
133
+ children: [
134
+ /* @__PURE__ */ jsxDEV("div", {
135
+ className: "flex items-center justify-between",
136
+ children: [
137
+ /* @__PURE__ */ jsxDEV("h2", {
138
+ className: "text-2xl font-bold",
139
+ children: "Analytics Dashboard"
140
+ }, undefined, false, undefined, this),
141
+ /* @__PURE__ */ jsxDEV(Button, {
142
+ onClick: () => alert("Create dashboard modal"),
143
+ children: [
144
+ /* @__PURE__ */ jsxDEV("span", {
145
+ className: "mr-2",
146
+ children: "+"
147
+ }, undefined, false, undefined, this),
148
+ " New Dashboard"
149
+ ]
150
+ }, undefined, true, undefined, this)
151
+ ]
152
+ }, undefined, true, undefined, this),
153
+ /* @__PURE__ */ jsxDEV(StatCardGroup, {
154
+ children: [
155
+ /* @__PURE__ */ jsxDEV(StatCard, {
156
+ label: "Dashboards",
157
+ value: stats.totalDashboards,
158
+ hint: `${stats.publishedDashboards} published`
159
+ }, undefined, false, undefined, this),
160
+ /* @__PURE__ */ jsxDEV(StatCard, {
161
+ label: "Queries",
162
+ value: stats.totalQueries,
163
+ hint: `${stats.sharedQueries} shared`
164
+ }, undefined, false, undefined, this),
165
+ /* @__PURE__ */ jsxDEV(StatCard, {
166
+ label: "Widgets",
167
+ value: widgets.length,
168
+ hint: "on current dashboard"
169
+ }, undefined, false, undefined, this)
170
+ ]
171
+ }, undefined, true, undefined, this),
172
+ /* @__PURE__ */ jsxDEV("nav", {
173
+ className: "bg-muted flex gap-1 rounded-lg p-1",
174
+ role: "tablist",
175
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV(Button, {
176
+ type: "button",
177
+ role: "tab",
178
+ "aria-selected": activeTab === tab.id,
179
+ onClick: () => setActiveTab(tab.id),
180
+ 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"}`,
181
+ children: [
182
+ /* @__PURE__ */ jsxDEV("span", {
183
+ children: tab.icon
184
+ }, undefined, false, undefined, this),
185
+ tab.label
186
+ ]
187
+ }, tab.id, true, undefined, this))
188
+ }, undefined, false, undefined, this),
189
+ /* @__PURE__ */ jsxDEV("div", {
190
+ className: "min-h-[400px]",
191
+ role: "tabpanel",
192
+ children: [
193
+ activeTab === "dashboards" && /* @__PURE__ */ jsxDEV("div", {
194
+ className: "space-y-6",
195
+ children: [
196
+ /* @__PURE__ */ jsxDEV("div", {
197
+ className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
198
+ children: [
199
+ dashboards.map((dashboard) => /* @__PURE__ */ jsxDEV("div", {
200
+ onClick: () => selectDashboard(dashboard),
201
+ 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"}`,
202
+ role: "button",
203
+ tabIndex: 0,
204
+ onKeyDown: (e) => {
205
+ if (e.key === "Enter" || e.key === " ")
206
+ selectDashboard(dashboard);
207
+ },
208
+ children: [
209
+ /* @__PURE__ */ jsxDEV("div", {
210
+ className: "mb-2 flex items-center justify-between",
211
+ children: [
212
+ /* @__PURE__ */ jsxDEV("h3", {
213
+ className: "font-medium",
214
+ children: dashboard.name
215
+ }, undefined, false, undefined, this),
216
+ /* @__PURE__ */ jsxDEV("span", {
217
+ className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[dashboard.status] ?? ""}`,
218
+ children: dashboard.status
219
+ }, undefined, false, undefined, this)
220
+ ]
221
+ }, undefined, true, undefined, this),
222
+ /* @__PURE__ */ jsxDEV("p", {
223
+ className: "text-muted-foreground mb-3 text-sm",
224
+ children: dashboard.description
225
+ }, undefined, false, undefined, this),
226
+ /* @__PURE__ */ jsxDEV("div", {
227
+ className: "text-muted-foreground flex items-center justify-between text-xs",
228
+ children: [
229
+ /* @__PURE__ */ jsxDEV("span", {
230
+ children: [
231
+ "/",
232
+ dashboard.slug
233
+ ]
234
+ }, undefined, true, undefined, this),
235
+ dashboard.isPublic && /* @__PURE__ */ jsxDEV("span", {
236
+ className: "text-green-600 dark:text-green-400",
237
+ children: "\uD83C\uDF10 Public"
238
+ }, undefined, false, undefined, this)
239
+ ]
240
+ }, undefined, true, undefined, this)
241
+ ]
242
+ }, dashboard.id, true, undefined, this)),
243
+ dashboards.length === 0 && /* @__PURE__ */ jsxDEV("div", {
244
+ className: "text-muted-foreground col-span-full flex h-64 items-center justify-center",
245
+ children: "No dashboards created yet"
246
+ }, undefined, false, undefined, this)
247
+ ]
248
+ }, undefined, true, undefined, this),
249
+ selectedDashboard && widgets.length > 0 && /* @__PURE__ */ jsxDEV("div", {
250
+ children: [
251
+ /* @__PURE__ */ jsxDEV("h3", {
252
+ className: "mb-4 text-lg font-semibold",
253
+ children: [
254
+ 'Widgets in "',
255
+ selectedDashboard.name,
256
+ '"'
257
+ ]
258
+ }, undefined, true, undefined, this),
259
+ /* @__PURE__ */ jsxDEV("div", {
260
+ className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
261
+ children: widgets.map((widget) => /* @__PURE__ */ jsxDEV("div", {
262
+ className: "border-border bg-card rounded-lg border p-4",
263
+ children: [
264
+ /* @__PURE__ */ jsxDEV("div", {
265
+ className: "mb-2 flex items-center gap-2",
266
+ children: [
267
+ /* @__PURE__ */ jsxDEV("span", {
268
+ className: "text-xl",
269
+ children: WIDGET_ICONS[widget.type] ?? "\uD83D\uDCCA"
270
+ }, undefined, false, undefined, this),
271
+ /* @__PURE__ */ jsxDEV("span", {
272
+ className: "font-medium",
273
+ children: widget.name
274
+ }, undefined, false, undefined, this)
275
+ ]
276
+ }, undefined, true, undefined, this),
277
+ /* @__PURE__ */ jsxDEV("div", {
278
+ className: "text-muted-foreground text-sm",
279
+ children: widget.type.replace(/_/g, " ")
280
+ }, undefined, false, undefined, this),
281
+ /* @__PURE__ */ jsxDEV("div", {
282
+ className: "text-muted-foreground mt-2 text-xs",
283
+ children: [
284
+ "Position: (",
285
+ widget.gridX,
286
+ ", ",
287
+ widget.gridY,
288
+ ") •",
289
+ " ",
290
+ widget.gridWidth,
291
+ "x",
292
+ widget.gridHeight
293
+ ]
294
+ }, undefined, true, undefined, this)
295
+ ]
296
+ }, widget.id, true, undefined, this))
297
+ }, undefined, false, undefined, this)
298
+ ]
299
+ }, undefined, true, undefined, this)
300
+ ]
301
+ }, undefined, true, undefined, this),
302
+ activeTab === "queries" && /* @__PURE__ */ jsxDEV("div", {
303
+ className: "border-border rounded-lg border",
304
+ children: /* @__PURE__ */ jsxDEV("table", {
305
+ className: "w-full",
306
+ children: [
307
+ /* @__PURE__ */ jsxDEV("thead", {
308
+ className: "border-border bg-muted/30 border-b",
309
+ children: /* @__PURE__ */ jsxDEV("tr", {
310
+ children: [
311
+ /* @__PURE__ */ jsxDEV("th", {
312
+ className: "px-4 py-3 text-left text-sm font-medium",
313
+ children: "Query"
314
+ }, undefined, false, undefined, this),
315
+ /* @__PURE__ */ jsxDEV("th", {
316
+ className: "px-4 py-3 text-left text-sm font-medium",
317
+ children: "Type"
318
+ }, undefined, false, undefined, this),
319
+ /* @__PURE__ */ jsxDEV("th", {
320
+ className: "px-4 py-3 text-left text-sm font-medium",
321
+ children: "Cache TTL"
322
+ }, undefined, false, undefined, this),
323
+ /* @__PURE__ */ jsxDEV("th", {
324
+ className: "px-4 py-3 text-left text-sm font-medium",
325
+ children: "Shared"
326
+ }, undefined, false, undefined, this)
327
+ ]
328
+ }, undefined, true, undefined, this)
329
+ }, undefined, false, undefined, this),
330
+ /* @__PURE__ */ jsxDEV("tbody", {
331
+ className: "divide-border divide-y",
332
+ children: [
333
+ queries.map((query) => /* @__PURE__ */ jsxDEV("tr", {
334
+ className: "hover:bg-muted/50",
335
+ children: [
336
+ /* @__PURE__ */ jsxDEV("td", {
337
+ className: "px-4 py-3",
338
+ children: [
339
+ /* @__PURE__ */ jsxDEV("div", {
340
+ className: "font-medium",
341
+ children: query.name
342
+ }, undefined, false, undefined, this),
343
+ /* @__PURE__ */ jsxDEV("div", {
344
+ className: "text-muted-foreground text-sm",
345
+ children: query.description
346
+ }, undefined, false, undefined, this)
347
+ ]
348
+ }, undefined, true, undefined, this),
349
+ /* @__PURE__ */ jsxDEV("td", {
350
+ className: "px-4 py-3",
351
+ children: /* @__PURE__ */ jsxDEV("span", {
352
+ className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${QUERY_TYPE_COLORS[query.type] ?? ""}`,
353
+ children: query.type
354
+ }, undefined, false, undefined, this)
355
+ }, undefined, false, undefined, this),
356
+ /* @__PURE__ */ jsxDEV("td", {
357
+ className: "text-muted-foreground px-4 py-3 text-sm",
358
+ children: [
359
+ query.cacheTtlSeconds,
360
+ "s"
361
+ ]
362
+ }, undefined, true, undefined, this),
363
+ /* @__PURE__ */ jsxDEV("td", {
364
+ className: "px-4 py-3",
365
+ children: query.isShared ? /* @__PURE__ */ jsxDEV("span", {
366
+ className: "text-green-600 dark:text-green-400",
367
+ children: "✓"
368
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV("span", {
369
+ className: "text-muted-foreground",
370
+ children: "—"
371
+ }, undefined, false, undefined, this)
372
+ }, undefined, false, undefined, this)
373
+ ]
374
+ }, query.id, true, undefined, this)),
375
+ queries.length === 0 && /* @__PURE__ */ jsxDEV("tr", {
376
+ children: /* @__PURE__ */ jsxDEV("td", {
377
+ colSpan: 4,
378
+ className: "text-muted-foreground px-4 py-8 text-center",
379
+ children: "No queries saved"
380
+ }, undefined, false, undefined, this)
381
+ }, undefined, false, undefined, this)
382
+ ]
383
+ }, undefined, true, undefined, this)
384
+ ]
385
+ }, undefined, true, undefined, this)
386
+ }, undefined, false, undefined, this)
387
+ ]
388
+ }, undefined, true, undefined, this)
389
+ ]
390
+ }, undefined, true, undefined, this);
391
+ }
392
+ export {
393
+ AnalyticsDashboard
394
+ };
@@ -0,0 +1,69 @@
1
+ // src/ui/hooks/useAnalyticsData.ts
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
4
+ "use client";
5
+ function useAnalyticsData(projectId = "local-project") {
6
+ const { handlers } = useTemplateRuntime();
7
+ const analytics = handlers.analytics;
8
+ const [dashboards, setDashboards] = useState([]);
9
+ const [queries, setQueries] = useState([]);
10
+ const [selectedDashboard, setSelectedDashboard] = useState(null);
11
+ const [widgets, setWidgets] = useState([]);
12
+ const [loading, setLoading] = useState(true);
13
+ const [error, setError] = useState(null);
14
+ const fetchData = useCallback(async () => {
15
+ try {
16
+ setLoading(true);
17
+ setError(null);
18
+ const [dashResult, queryResult] = await Promise.all([
19
+ analytics.listDashboards({ projectId, limit: 100 }),
20
+ analytics.listQueries({ projectId, limit: 100 })
21
+ ]);
22
+ setDashboards(dashResult.dashboards);
23
+ setQueries(queryResult.queries);
24
+ if (dashResult.dashboards.length > 0 && !selectedDashboard) {
25
+ const first = dashResult.dashboards[0];
26
+ if (first) {
27
+ setSelectedDashboard(first);
28
+ const dashboardWidgets = await analytics.getWidgets(first.id);
29
+ setWidgets(dashboardWidgets);
30
+ }
31
+ }
32
+ } catch (err) {
33
+ setError(err instanceof Error ? err : new Error("Failed to load analytics"));
34
+ } finally {
35
+ setLoading(false);
36
+ }
37
+ }, [analytics, projectId, selectedDashboard]);
38
+ useEffect(() => {
39
+ fetchData();
40
+ }, [fetchData]);
41
+ const selectDashboard = useCallback(async (dashboard) => {
42
+ setSelectedDashboard(dashboard);
43
+ const dashboardWidgets = await analytics.getWidgets(dashboard.id);
44
+ setWidgets(dashboardWidgets);
45
+ }, [analytics]);
46
+ const stats = {
47
+ totalDashboards: dashboards.length,
48
+ publishedDashboards: dashboards.filter((d) => d.status === "PUBLISHED").length,
49
+ totalQueries: queries.length,
50
+ sharedQueries: queries.filter((q) => q.isShared).length
51
+ };
52
+ return {
53
+ dashboards,
54
+ queries,
55
+ selectedDashboard,
56
+ widgets,
57
+ loading,
58
+ error,
59
+ stats,
60
+ refetch: fetchData,
61
+ selectDashboard
62
+ };
63
+ }
64
+
65
+ // src/ui/hooks/index.ts
66
+ "use client";
67
+ export {
68
+ useAnalyticsData
69
+ };
@@ -0,0 +1,66 @@
1
+ // src/ui/hooks/useAnalyticsData.ts
2
+ import { useCallback, useEffect, useState } from "react";
3
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
4
+ "use client";
5
+ function useAnalyticsData(projectId = "local-project") {
6
+ const { handlers } = useTemplateRuntime();
7
+ const analytics = handlers.analytics;
8
+ const [dashboards, setDashboards] = useState([]);
9
+ const [queries, setQueries] = useState([]);
10
+ const [selectedDashboard, setSelectedDashboard] = useState(null);
11
+ const [widgets, setWidgets] = useState([]);
12
+ const [loading, setLoading] = useState(true);
13
+ const [error, setError] = useState(null);
14
+ const fetchData = useCallback(async () => {
15
+ try {
16
+ setLoading(true);
17
+ setError(null);
18
+ const [dashResult, queryResult] = await Promise.all([
19
+ analytics.listDashboards({ projectId, limit: 100 }),
20
+ analytics.listQueries({ projectId, limit: 100 })
21
+ ]);
22
+ setDashboards(dashResult.dashboards);
23
+ setQueries(queryResult.queries);
24
+ if (dashResult.dashboards.length > 0 && !selectedDashboard) {
25
+ const first = dashResult.dashboards[0];
26
+ if (first) {
27
+ setSelectedDashboard(first);
28
+ const dashboardWidgets = await analytics.getWidgets(first.id);
29
+ setWidgets(dashboardWidgets);
30
+ }
31
+ }
32
+ } catch (err) {
33
+ setError(err instanceof Error ? err : new Error("Failed to load analytics"));
34
+ } finally {
35
+ setLoading(false);
36
+ }
37
+ }, [analytics, projectId, selectedDashboard]);
38
+ useEffect(() => {
39
+ fetchData();
40
+ }, [fetchData]);
41
+ const selectDashboard = useCallback(async (dashboard) => {
42
+ setSelectedDashboard(dashboard);
43
+ const dashboardWidgets = await analytics.getWidgets(dashboard.id);
44
+ setWidgets(dashboardWidgets);
45
+ }, [analytics]);
46
+ const stats = {
47
+ totalDashboards: dashboards.length,
48
+ publishedDashboards: dashboards.filter((d) => d.status === "PUBLISHED").length,
49
+ totalQueries: queries.length,
50
+ sharedQueries: queries.filter((q) => q.isShared).length
51
+ };
52
+ return {
53
+ dashboards,
54
+ queries,
55
+ selectedDashboard,
56
+ widgets,
57
+ loading,
58
+ error,
59
+ stats,
60
+ refetch: fetchData,
61
+ selectDashboard
62
+ };
63
+ }
64
+ export {
65
+ useAnalyticsData
66
+ };