@contractspec/example.integration-hub 3.7.6 → 3.8.2

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 (57) hide show
  1. package/README.md +73 -183
  2. package/dist/connection/index.d.ts +1 -1
  3. package/dist/docs/index.js +2 -1
  4. package/dist/docs/integration-hub.docblock.js +2 -1
  5. package/dist/events.js +1 -1
  6. package/dist/index.d.ts +5 -4
  7. package/dist/index.js +1243 -749
  8. package/dist/integration/index.d.ts +1 -1
  9. package/dist/integration-hub.feature.js +202 -0
  10. package/dist/node/docs/index.js +2 -1
  11. package/dist/node/docs/integration-hub.docblock.js +2 -1
  12. package/dist/node/events.js +1 -1
  13. package/dist/node/index.js +1243 -749
  14. package/dist/node/integration-hub.feature.js +202 -0
  15. package/dist/node/ui/IntegrationDashboard.js +654 -180
  16. package/dist/node/ui/IntegrationDashboard.visualizations.js +250 -0
  17. package/dist/node/ui/hooks/index.js +1 -1
  18. package/dist/node/ui/hooks/useIntegrationData.js +1 -1
  19. package/dist/node/ui/index.js +970 -485
  20. package/dist/node/ui/renderers/index.js +216 -5
  21. package/dist/node/ui/renderers/integration.markdown.js +216 -5
  22. package/dist/node/ui/tables/ConnectionsTable.js +211 -0
  23. package/dist/node/ui/tables/IntegrationTables.js +361 -0
  24. package/dist/node/ui/tables/SyncConfigsTable.js +230 -0
  25. package/dist/node/ui/tables/integration-table.shared.js +84 -0
  26. package/dist/node/visualizations/catalog.js +137 -0
  27. package/dist/node/visualizations/index.js +211 -0
  28. package/dist/node/visualizations/selectors.js +204 -0
  29. package/dist/sync/index.d.ts +3 -3
  30. package/dist/ui/IntegrationDashboard.js +654 -180
  31. package/dist/ui/IntegrationDashboard.visualizations.d.ts +6 -0
  32. package/dist/ui/IntegrationDashboard.visualizations.js +251 -0
  33. package/dist/ui/hooks/index.d.ts +1 -1
  34. package/dist/ui/hooks/index.js +1 -1
  35. package/dist/ui/hooks/useIntegrationData.js +1 -1
  36. package/dist/ui/index.d.ts +2 -2
  37. package/dist/ui/index.js +970 -485
  38. package/dist/ui/renderers/index.d.ts +1 -1
  39. package/dist/ui/renderers/index.js +216 -5
  40. package/dist/ui/renderers/integration.markdown.js +216 -5
  41. package/dist/ui/tables/ConnectionsTable.d.ts +4 -0
  42. package/dist/ui/tables/ConnectionsTable.js +212 -0
  43. package/dist/ui/tables/IntegrationTables.d.ts +2 -0
  44. package/dist/ui/tables/IntegrationTables.js +362 -0
  45. package/dist/ui/tables/IntegrationTables.smoke.test.d.ts +1 -0
  46. package/dist/ui/tables/SyncConfigsTable.d.ts +4 -0
  47. package/dist/ui/tables/SyncConfigsTable.js +231 -0
  48. package/dist/ui/tables/integration-table.shared.d.ts +18 -0
  49. package/dist/ui/tables/integration-table.shared.js +85 -0
  50. package/dist/visualizations/catalog.d.ts +11 -0
  51. package/dist/visualizations/catalog.js +138 -0
  52. package/dist/visualizations/index.d.ts +2 -0
  53. package/dist/visualizations/index.js +212 -0
  54. package/dist/visualizations/selectors.d.ts +10 -0
  55. package/dist/visualizations/selectors.js +205 -0
  56. package/dist/visualizations/selectors.test.d.ts +1 -0
  57. package/package.json +110 -12
@@ -1,326 +1,309 @@
1
- // src/ui/renderers/integration.markdown.ts
2
- var mockIntegrations = [
3
- {
4
- id: "int-1",
5
- name: "Salesforce",
6
- type: "CRM",
7
- status: "ACTIVE",
8
- connectionCount: 3
9
- },
10
- {
11
- id: "int-2",
12
- name: "HubSpot",
13
- type: "MARKETING",
14
- status: "ACTIVE",
15
- connectionCount: 2
16
- },
17
- {
18
- id: "int-3",
19
- name: "Stripe",
20
- type: "PAYMENT",
21
- status: "ACTIVE",
22
- connectionCount: 1
23
- },
24
- {
25
- id: "int-4",
26
- name: "Slack",
27
- type: "COMMUNICATION",
28
- status: "INACTIVE",
29
- connectionCount: 0
30
- },
31
- {
32
- id: "int-5",
33
- name: "Google Sheets",
34
- type: "DATA",
35
- status: "ACTIVE",
36
- connectionCount: 5
1
+ // src/ui/hooks/useIntegrationData.ts
2
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
3
+ import { useCallback, useEffect, useState } from "react";
4
+ "use client";
5
+ function useIntegrationData(projectId = "local-project") {
6
+ const { handlers } = useTemplateRuntime();
7
+ const integration = handlers.integration;
8
+ const [integrations, setIntegrations] = useState([]);
9
+ const [connections, setConnections] = useState([]);
10
+ const [syncConfigs, setSyncConfigs] = useState([]);
11
+ const [loading, setLoading] = useState(true);
12
+ const [error, setError] = useState(null);
13
+ const fetchData = useCallback(async () => {
14
+ try {
15
+ setLoading(true);
16
+ setError(null);
17
+ const [integResult, connResult, syncResult] = await Promise.all([
18
+ integration.listIntegrations({ projectId, limit: 100 }),
19
+ integration.listConnections({ limit: 100 }),
20
+ integration.listSyncConfigs({ limit: 100 })
21
+ ]);
22
+ setIntegrations(integResult.integrations);
23
+ setConnections(connResult.connections);
24
+ setSyncConfigs(syncResult.configs);
25
+ } catch (err) {
26
+ setError(err instanceof Error ? err : new Error("Failed to load integrations"));
27
+ } finally {
28
+ setLoading(false);
29
+ }
30
+ }, [integration, projectId]);
31
+ useEffect(() => {
32
+ fetchData();
33
+ }, [fetchData]);
34
+ const stats = {
35
+ totalIntegrations: integrations.length,
36
+ activeIntegrations: integrations.filter((i) => i.status === "ACTIVE").length,
37
+ totalConnections: connections.length,
38
+ connectedCount: connections.filter((c) => c.status === "CONNECTED").length,
39
+ totalSyncs: syncConfigs.length,
40
+ activeSyncs: syncConfigs.filter((s) => s.status === "ACTIVE").length
41
+ };
42
+ return {
43
+ integrations,
44
+ connections,
45
+ syncConfigs,
46
+ loading,
47
+ error,
48
+ stats,
49
+ refetch: fetchData
50
+ };
51
+ }
52
+
53
+ // src/ui/hooks/index.ts
54
+ "use client";
55
+
56
+ // src/visualizations/catalog.ts
57
+ import {
58
+ defineVisualization,
59
+ VisualizationRegistry
60
+ } from "@contractspec/lib.contracts-spec/visualizations";
61
+ var INTEGRATION_LIST_REF = {
62
+ key: "integration.list",
63
+ version: "1.0.0"
64
+ };
65
+ var CONNECTION_LIST_REF = {
66
+ key: "integration.connection.list",
67
+ version: "1.0.0"
68
+ };
69
+ var SYNC_CONFIG_REF = {
70
+ key: "integration.syncConfig.list",
71
+ version: "1.0.0"
72
+ };
73
+ var META = {
74
+ version: "1.0.0",
75
+ domain: "integration",
76
+ stability: "experimental",
77
+ owners: ["@example.integration-hub"],
78
+ tags: ["integration", "visualization", "sync"]
79
+ };
80
+ var IntegrationTypeVisualization = defineVisualization({
81
+ meta: {
82
+ ...META,
83
+ key: "integration-hub.visualization.integration-types",
84
+ title: "Integration Types",
85
+ description: "Distribution of configured integration categories.",
86
+ goal: "Show where integration coverage is concentrated.",
87
+ context: "Integration overview."
37
88
  },
38
- {
39
- id: "int-6",
40
- name: "PostHog",
41
- type: "ANALYTICS",
42
- status: "ACTIVE",
43
- connectionCount: 1
89
+ source: { primary: INTEGRATION_LIST_REF, resultPath: "data" },
90
+ visualization: {
91
+ kind: "pie",
92
+ nameDimension: "type",
93
+ valueMeasure: "count",
94
+ dimensions: [
95
+ { key: "type", label: "Type", dataPath: "type", type: "category" }
96
+ ],
97
+ measures: [
98
+ { key: "count", label: "Count", dataPath: "count", format: "number" }
99
+ ],
100
+ table: { caption: "Integration counts by type." }
44
101
  }
45
- ];
46
- var mockConnections = [
47
- {
48
- id: "conn-1",
49
- integrationId: "int-1",
50
- name: "Production Salesforce",
51
- status: "CONNECTED",
52
- lastSyncAt: "2024-01-16T10:00:00Z"
53
- },
54
- {
55
- id: "conn-2",
56
- integrationId: "int-1",
57
- name: "Sandbox Salesforce",
58
- status: "CONNECTED",
59
- lastSyncAt: "2024-01-15T14:00:00Z"
60
- },
61
- {
62
- id: "conn-3",
63
- integrationId: "int-2",
64
- name: "Marketing HubSpot",
65
- status: "CONNECTED",
66
- lastSyncAt: "2024-01-16T08:00:00Z"
67
- },
68
- {
69
- id: "conn-4",
70
- integrationId: "int-3",
71
- name: "Stripe Live",
72
- status: "CONNECTED",
73
- lastSyncAt: "2024-01-16T12:00:00Z"
74
- },
75
- {
76
- id: "conn-5",
77
- integrationId: "int-5",
78
- name: "Analytics Sheet",
79
- status: "ERROR",
80
- lastSyncAt: "2024-01-14T09:00:00Z",
81
- error: "Authentication expired"
102
+ });
103
+ var ConnectionStatusVisualization = defineVisualization({
104
+ meta: {
105
+ ...META,
106
+ key: "integration-hub.visualization.connection-status",
107
+ title: "Connection Status",
108
+ description: "Status distribution across configured connections.",
109
+ goal: "Highlight connection health and instability.",
110
+ context: "Connection monitoring."
82
111
  },
83
- {
84
- id: "conn-6",
85
- integrationId: "int-6",
86
- name: "PostHog Workspace",
87
- status: "CONNECTED",
88
- lastSyncAt: "2024-01-16T11:45:00Z"
112
+ source: { primary: CONNECTION_LIST_REF, resultPath: "data" },
113
+ visualization: {
114
+ kind: "cartesian",
115
+ variant: "bar",
116
+ xDimension: "status",
117
+ yMeasures: ["count"],
118
+ dimensions: [
119
+ { key: "status", label: "Status", dataPath: "status", type: "category" }
120
+ ],
121
+ measures: [
122
+ {
123
+ key: "count",
124
+ label: "Connections",
125
+ dataPath: "count",
126
+ format: "number",
127
+ color: "#1d4ed8"
128
+ }
129
+ ],
130
+ table: { caption: "Connection counts by status." }
89
131
  }
90
- ];
91
- var mockSyncConfigs = [
92
- {
93
- id: "sync-1",
94
- connectionId: "conn-1",
95
- name: "Contacts Sync",
96
- frequency: "HOURLY",
97
- lastRunAt: "2024-01-16T10:00:00Z",
98
- status: "SUCCESS",
99
- recordsSynced: 1250
100
- },
101
- {
102
- id: "sync-2",
103
- connectionId: "conn-1",
104
- name: "Opportunities Sync",
105
- frequency: "DAILY",
106
- lastRunAt: "2024-01-16T00:00:00Z",
107
- status: "SUCCESS",
108
- recordsSynced: 340
132
+ });
133
+ var HealthySyncMetricVisualization = defineVisualization({
134
+ meta: {
135
+ ...META,
136
+ key: "integration-hub.visualization.sync-healthy",
137
+ title: "Healthy Syncs",
138
+ description: "Sync configurations currently healthy or recently successful.",
139
+ goal: "Summarize healthy synchronization capacity.",
140
+ context: "Sync-state comparison."
109
141
  },
110
- {
111
- id: "sync-3",
112
- connectionId: "conn-3",
113
- name: "Orders Sync",
114
- frequency: "REALTIME",
115
- lastRunAt: "2024-01-16T12:30:00Z",
116
- status: "SUCCESS",
117
- recordsSynced: 89
142
+ source: { primary: SYNC_CONFIG_REF, resultPath: "data" },
143
+ visualization: {
144
+ kind: "metric",
145
+ measure: "value",
146
+ measures: [
147
+ { key: "value", label: "Syncs", dataPath: "value", format: "number" }
148
+ ],
149
+ table: { caption: "Healthy sync count." }
150
+ }
151
+ });
152
+ var AttentionSyncMetricVisualization = defineVisualization({
153
+ meta: {
154
+ ...META,
155
+ key: "integration-hub.visualization.sync-attention",
156
+ title: "Attention Needed",
157
+ description: "Sync configurations paused, failing, or otherwise needing review.",
158
+ goal: "Summarize syncs needing action.",
159
+ context: "Sync-state comparison."
118
160
  },
119
- {
120
- id: "sync-4",
121
- connectionId: "conn-5",
122
- name: "Metrics Export",
123
- frequency: "DAILY",
124
- lastRunAt: "2024-01-14T09:00:00Z",
125
- status: "FAILED",
126
- recordsSynced: 0
161
+ source: { primary: SYNC_CONFIG_REF, resultPath: "data" },
162
+ visualization: {
163
+ kind: "metric",
164
+ measure: "value",
165
+ measures: [
166
+ { key: "value", label: "Syncs", dataPath: "value", format: "number" }
167
+ ],
168
+ table: { caption: "Syncs requiring attention." }
127
169
  }
170
+ });
171
+ var IntegrationVisualizationSpecs = [
172
+ IntegrationTypeVisualization,
173
+ ConnectionStatusVisualization,
174
+ HealthySyncMetricVisualization,
175
+ AttentionSyncMetricVisualization
128
176
  ];
129
- var integrationDashboardMarkdownRenderer = {
130
- target: "markdown",
131
- render: async (desc) => {
132
- if (desc.source.type !== "component" || desc.source.componentKey !== "IntegrationDashboard") {
133
- throw new Error("integrationDashboardMarkdownRenderer: not IntegrationDashboard");
134
- }
135
- const integrations = mockIntegrations;
136
- const connections = mockConnections;
137
- const syncs = mockSyncConfigs;
138
- const activeIntegrations = integrations.filter((i) => i.status === "ACTIVE");
139
- const connectedConnections = connections.filter((c) => c.status === "CONNECTED");
140
- const errorConnections = connections.filter((c) => c.status === "ERROR");
141
- const successfulSyncs = syncs.filter((s) => s.status === "SUCCESS");
142
- const totalRecordsSynced = successfulSyncs.reduce((sum, s) => sum + s.recordsSynced, 0);
143
- const lines = [
144
- "# Integration Hub",
145
- "",
146
- "> Connect and sync data with external services",
147
- "",
148
- "## Overview",
149
- "",
150
- "| Metric | Value |",
151
- "|--------|-------|",
152
- `| Active Integrations | ${activeIntegrations.length} |`,
153
- `| Connected Services | ${connectedConnections.length} |`,
154
- `| Error Connections | ${errorConnections.length} |`,
155
- `| Sync Configs | ${syncs.length} |`,
156
- `| Records Synced (24h) | ${totalRecordsSynced.toLocaleString()} |`,
157
- "",
158
- "## Integrations",
159
- "",
160
- "| Name | Type | Connections | Status |",
161
- "|------|------|-------------|--------|"
162
- ];
163
- for (const integration of integrations) {
164
- const statusIcon = integration.status === "ACTIVE" ? "\uD83D\uDFE2" : "⚫";
165
- lines.push(`| ${integration.name} | ${integration.type} | ${integration.connectionCount} | ${statusIcon} ${integration.status} |`);
166
- }
167
- lines.push("");
168
- lines.push("## Recent Sync Activity");
169
- lines.push("");
170
- lines.push("| Sync | Frequency | Last Run | Records | Status |");
171
- lines.push("|------|-----------|----------|---------|--------|");
172
- for (const sync of syncs) {
173
- const lastRun = new Date(sync.lastRunAt).toLocaleString();
174
- const statusIcon = sync.status === "SUCCESS" ? "✅" : "❌";
175
- lines.push(`| ${sync.name} | ${sync.frequency} | ${lastRun} | ${sync.recordsSynced} | ${statusIcon} ${sync.status} |`);
176
- }
177
- if (errorConnections.length > 0) {
178
- lines.push("");
179
- lines.push("## ⚠️ Connections with Errors");
180
- lines.push("");
181
- for (const conn of errorConnections) {
182
- const integration = integrations.find((i) => i.id === conn.integrationId);
183
- lines.push(`- **${conn.name}** (${integration?.name ?? "Unknown"}): ${conn.error ?? "Unknown error"}`);
184
- }
177
+ var IntegrationVisualizationRegistry = new VisualizationRegistry([
178
+ ...IntegrationVisualizationSpecs
179
+ ]);
180
+ var IntegrationVisualizationRefs = IntegrationVisualizationSpecs.map((spec) => ({
181
+ key: spec.meta.key,
182
+ version: spec.meta.version
183
+ }));
184
+
185
+ // src/visualizations/selectors.ts
186
+ function isHealthySync(status) {
187
+ return status === "ACTIVE" || status === "SUCCESS";
188
+ }
189
+ function createIntegrationVisualizationSections(integrations, connections, syncConfigs) {
190
+ const integrationTypes = new Map;
191
+ const connectionStatuses = new Map;
192
+ let healthySyncs = 0;
193
+ let attentionSyncs = 0;
194
+ for (const integration of integrations) {
195
+ integrationTypes.set(integration.type, (integrationTypes.get(integration.type) ?? 0) + 1);
196
+ }
197
+ for (const connection of connections) {
198
+ connectionStatuses.set(connection.status, (connectionStatuses.get(connection.status) ?? 0) + 1);
199
+ }
200
+ for (const syncConfig of syncConfigs) {
201
+ if (isHealthySync(syncConfig.status)) {
202
+ healthySyncs += 1;
203
+ } else {
204
+ attentionSyncs += 1;
185
205
  }
186
- return {
187
- mimeType: "text/markdown",
188
- body: lines.join(`
189
- `)
190
- };
191
206
  }
192
- };
193
- var connectionListMarkdownRenderer = {
194
- target: "markdown",
195
- render: async (desc) => {
196
- if (desc.source.type !== "component" || desc.source.componentKey !== "ConnectionList") {
197
- throw new Error("connectionListMarkdownRenderer: not ConnectionList");
207
+ const primaryItems = [
208
+ {
209
+ key: "integration-types",
210
+ spec: IntegrationTypeVisualization,
211
+ data: {
212
+ data: Array.from(integrationTypes.entries()).map(([type, count]) => ({
213
+ type,
214
+ count
215
+ }))
216
+ },
217
+ title: "Integration Types",
218
+ description: "Configured integrations grouped by category.",
219
+ height: 260
220
+ },
221
+ {
222
+ key: "connection-status",
223
+ spec: ConnectionStatusVisualization,
224
+ data: {
225
+ data: Array.from(connectionStatuses.entries()).map(([status, count]) => ({
226
+ status,
227
+ count
228
+ }))
229
+ },
230
+ title: "Connection Status",
231
+ description: "Operational health across current connections."
198
232
  }
199
- const connections = mockConnections;
200
- const integrations = mockIntegrations;
201
- const lines = [
202
- "# Connections",
203
- "",
204
- "> Manage connections to external services",
205
- ""
206
- ];
207
- for (const integration of integrations) {
208
- const intConnections = connections.filter((c) => c.integrationId === integration.id);
209
- if (intConnections.length === 0)
210
- continue;
211
- lines.push(`## ${integration.name}`);
212
- lines.push("");
213
- lines.push("| Connection | Status | Last Sync |");
214
- lines.push("|------------|--------|-----------|");
215
- for (const conn of intConnections) {
216
- const lastSync = new Date(conn.lastSyncAt).toLocaleString();
217
- const statusIcon = conn.status === "CONNECTED" ? "\uD83D\uDFE2" : conn.status === "ERROR" ? "\uD83D\uDD34" : "⚫";
218
- lines.push(`| ${conn.name} | ${statusIcon} ${conn.status} | ${lastSync} |`);
219
- }
220
- lines.push("");
221
- }
222
- return {
223
- mimeType: "text/markdown",
224
- body: lines.join(`
225
- `)
226
- };
227
- }
228
- };
229
- var syncConfigMarkdownRenderer = {
230
- target: "markdown",
231
- render: async (desc) => {
232
- if (desc.source.type !== "component" || desc.source.componentKey !== "SyncConfigEditor") {
233
- throw new Error("syncConfigMarkdownRenderer: not SyncConfigEditor");
234
- }
235
- const syncs = mockSyncConfigs;
236
- const connections = mockConnections;
237
- const lines = [
238
- "# Sync Configurations",
239
- "",
240
- "> Configure automated data synchronization",
241
- ""
242
- ];
243
- for (const sync of syncs) {
244
- const connection = connections.find((c) => c.id === sync.connectionId);
245
- const statusIcon = sync.status === "SUCCESS" ? "✅" : "❌";
246
- lines.push(`## ${sync.name}`);
247
- lines.push("");
248
- lines.push(`**Connection:** ${connection?.name ?? "Unknown"}`);
249
- lines.push(`**Frequency:** ${sync.frequency}`);
250
- lines.push(`**Status:** ${statusIcon} ${sync.status}`);
251
- lines.push(`**Last Run:** ${new Date(sync.lastRunAt).toLocaleString()}`);
252
- lines.push(`**Records Synced:** ${sync.recordsSynced.toLocaleString()}`);
253
- lines.push("");
254
- }
255
- lines.push("## Frequency Options");
256
- lines.push("");
257
- lines.push("- **REALTIME**: Sync on every change");
258
- lines.push("- **HOURLY**: Sync every hour");
259
- lines.push("- **DAILY**: Sync once per day");
260
- lines.push("- **WEEKLY**: Sync once per week");
261
- lines.push("- **MANUAL**: Sync only when triggered");
262
- return {
263
- mimeType: "text/markdown",
264
- body: lines.join(`
265
- `)
266
- };
267
- }
268
- };
269
- // src/ui/hooks/useIntegrationData.ts
270
- import { useCallback, useEffect, useState } from "react";
271
- import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
272
- "use client";
273
- function useIntegrationData(projectId = "local-project") {
274
- const { handlers } = useTemplateRuntime();
275
- const integration = handlers.integration;
276
- const [integrations, setIntegrations] = useState([]);
277
- const [connections, setConnections] = useState([]);
278
- const [syncConfigs, setSyncConfigs] = useState([]);
279
- const [loading, setLoading] = useState(true);
280
- const [error, setError] = useState(null);
281
- const fetchData = useCallback(async () => {
282
- try {
283
- setLoading(true);
284
- setError(null);
285
- const [integResult, connResult, syncResult] = await Promise.all([
286
- integration.listIntegrations({ projectId, limit: 100 }),
287
- integration.listConnections({ limit: 100 }),
288
- integration.listSyncConfigs({ limit: 100 })
289
- ]);
290
- setIntegrations(integResult.integrations);
291
- setConnections(connResult.connections);
292
- setSyncConfigs(syncResult.configs);
293
- } catch (err) {
294
- setError(err instanceof Error ? err : new Error("Failed to load integrations"));
295
- } finally {
296
- setLoading(false);
233
+ ];
234
+ const comparisonItems = [
235
+ {
236
+ key: "healthy-syncs",
237
+ spec: HealthySyncMetricVisualization,
238
+ data: { data: [{ value: healthySyncs }] },
239
+ title: "Healthy Syncs",
240
+ description: "Active or recently successful sync configurations.",
241
+ height: 200
242
+ },
243
+ {
244
+ key: "attention-syncs",
245
+ spec: AttentionSyncMetricVisualization,
246
+ data: { data: [{ value: attentionSyncs }] },
247
+ title: "Attention Needed",
248
+ description: "Paused, failed, or degraded sync configurations.",
249
+ height: 200
297
250
  }
298
- }, [integration, projectId]);
299
- useEffect(() => {
300
- fetchData();
301
- }, [fetchData]);
302
- const stats = {
303
- totalIntegrations: integrations.length,
304
- activeIntegrations: integrations.filter((i) => i.status === "ACTIVE").length,
305
- totalConnections: connections.length,
306
- connectedCount: connections.filter((c) => c.status === "CONNECTED").length,
307
- totalSyncs: syncConfigs.length,
308
- activeSyncs: syncConfigs.filter((s) => s.status === "ACTIVE").length
309
- };
251
+ ];
310
252
  return {
311
- integrations,
312
- connections,
313
- syncConfigs,
314
- loading,
315
- error,
316
- stats,
317
- refetch: fetchData
253
+ primaryItems,
254
+ comparisonItems
318
255
  };
319
256
  }
257
+ // src/ui/IntegrationDashboard.visualizations.tsx
258
+ import {
259
+ ComparisonView,
260
+ VisualizationCard,
261
+ VisualizationGrid
262
+ } from "@contractspec/lib.design-system";
263
+ import { jsxDEV } from "react/jsx-dev-runtime";
264
+ "use client";
265
+ function IntegrationVisualizationOverview({
266
+ integrations,
267
+ connections,
268
+ syncConfigs
269
+ }) {
270
+ const { primaryItems, comparisonItems } = createIntegrationVisualizationSections(integrations, connections, syncConfigs);
271
+ return /* @__PURE__ */ jsxDEV("section", {
272
+ className: "space-y-4",
273
+ children: [
274
+ /* @__PURE__ */ jsxDEV("div", {
275
+ children: [
276
+ /* @__PURE__ */ jsxDEV("h3", {
277
+ className: "font-semibold text-lg",
278
+ children: "Integration Visualizations"
279
+ }, undefined, false, undefined, this),
280
+ /* @__PURE__ */ jsxDEV("p", {
281
+ className: "text-muted-foreground text-sm",
282
+ children: "Contract-backed charts for integration coverage and sync health."
283
+ }, undefined, false, undefined, this)
284
+ ]
285
+ }, undefined, true, undefined, this),
286
+ /* @__PURE__ */ jsxDEV(VisualizationGrid, {
287
+ children: primaryItems.map((item) => /* @__PURE__ */ jsxDEV(VisualizationCard, {
288
+ data: item.data,
289
+ description: item.description,
290
+ height: item.height,
291
+ spec: item.spec,
292
+ title: item.title
293
+ }, item.key, false, undefined, this))
294
+ }, undefined, false, undefined, this),
295
+ /* @__PURE__ */ jsxDEV(ComparisonView, {
296
+ description: "Comparison surface for healthy versus attention-needed syncs.",
297
+ items: comparisonItems,
298
+ title: "Sync-State Comparison"
299
+ }, undefined, false, undefined, this)
300
+ ]
301
+ }, undefined, true, undefined, this);
302
+ }
320
303
 
321
304
  // src/ui/IntegrationHubChat.tsx
322
305
  import { ChatWithSidebar } from "@contractspec/module.ai-chat";
323
- import { jsxDEV } from "react/jsx-dev-runtime";
306
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
324
307
  "use client";
325
308
  var DEFAULT_SUGGESTIONS = [
326
309
  "List my integrations",
@@ -336,9 +319,9 @@ function IntegrationHubChat({
336
319
  systemPrompt = DEFAULT_SYSTEM_PROMPT,
337
320
  className
338
321
  }) {
339
- return /* @__PURE__ */ jsxDEV("div", {
322
+ return /* @__PURE__ */ jsxDEV2("div", {
340
323
  className: className ?? "flex h-[500px] flex-col",
341
- children: /* @__PURE__ */ jsxDEV(ChatWithSidebar, {
324
+ children: /* @__PURE__ */ jsxDEV2(ChatWithSidebar, {
342
325
  className: "flex-1",
343
326
  systemPrompt,
344
327
  proxyUrl,
@@ -350,16 +333,373 @@ function IntegrationHubChat({
350
333
  }, undefined, false, undefined, this);
351
334
  }
352
335
 
336
+ // src/ui/tables/integration-table.shared.tsx
337
+ import { Button } from "@contractspec/lib.design-system";
338
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
339
+ import { HStack } from "@contractspec/lib.ui-kit-web/ui/stack";
340
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
341
+ "use client";
342
+ var STATUS_VARIANTS = {
343
+ ACTIVE: "default",
344
+ CONNECTED: "default",
345
+ SUCCESS: "default",
346
+ PENDING: "secondary",
347
+ PAUSED: "secondary",
348
+ ERROR: "destructive",
349
+ DISCONNECTED: "outline"
350
+ };
351
+ function formatDateTime(value) {
352
+ return value ? value.toLocaleString() : "Never";
353
+ }
354
+ function formatJson(value) {
355
+ return value ? JSON.stringify(value, null, 2) : "No configuration";
356
+ }
357
+ function StatusBadge({ status }) {
358
+ return /* @__PURE__ */ jsxDEV3(Badge, {
359
+ variant: STATUS_VARIANTS[status] ?? "outline",
360
+ children: status
361
+ }, undefined, false, undefined, this);
362
+ }
363
+ function IntegrationTableToolbar({
364
+ controller,
365
+ label,
366
+ toggleColumnId,
367
+ toggleVisibleLabel,
368
+ toggleHiddenLabel,
369
+ pinColumnId,
370
+ pinLabel,
371
+ resizeColumnId,
372
+ resizeLabel
373
+ }) {
374
+ const firstRow = controller.rows[0];
375
+ const toggleColumn = controller.columns.find((column) => column.id === toggleColumnId);
376
+ const pinColumn = controller.columns.find((column) => column.id === pinColumnId);
377
+ const resizeColumn = controller.columns.find((column) => column.id === resizeColumnId);
378
+ const pinTarget = pinColumn?.pinState === "left" ? false : "left";
379
+ return /* @__PURE__ */ jsxDEV3(HStack, {
380
+ gap: "sm",
381
+ className: "flex-wrap",
382
+ children: [
383
+ /* @__PURE__ */ jsxDEV3(Badge, {
384
+ variant: "outline",
385
+ children: label
386
+ }, undefined, false, undefined, this),
387
+ /* @__PURE__ */ jsxDEV3(Button, {
388
+ variant: "outline",
389
+ size: "sm",
390
+ onPress: () => firstRow?.toggleExpanded?.(!firstRow?.isExpanded),
391
+ children: "Expand First Row"
392
+ }, undefined, false, undefined, this),
393
+ /* @__PURE__ */ jsxDEV3(Button, {
394
+ variant: "outline",
395
+ size: "sm",
396
+ onPress: () => toggleColumn?.toggleVisibility?.(!toggleColumn?.visible),
397
+ children: toggleColumn?.visible ? toggleVisibleLabel : toggleHiddenLabel
398
+ }, undefined, false, undefined, this),
399
+ /* @__PURE__ */ jsxDEV3(Button, {
400
+ variant: "outline",
401
+ size: "sm",
402
+ onPress: () => pinColumn?.pin?.(pinTarget),
403
+ children: pinColumn?.pinState === "left" ? `Unpin ${pinLabel}` : `Pin ${pinLabel}`
404
+ }, undefined, false, undefined, this),
405
+ /* @__PURE__ */ jsxDEV3(Button, {
406
+ variant: "outline",
407
+ size: "sm",
408
+ onPress: () => resizeColumn?.resizeBy?.(40),
409
+ children: resizeLabel
410
+ }, undefined, false, undefined, this)
411
+ ]
412
+ }, undefined, true, undefined, this);
413
+ }
414
+
415
+ // src/ui/tables/ConnectionsTable.tsx
416
+ import { DataTable } from "@contractspec/lib.design-system";
417
+ import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
418
+ import { VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
419
+ import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
420
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
421
+ "use client";
422
+ function ConnectionsTable({
423
+ connections
424
+ }) {
425
+ const controller = useContractTable({
426
+ data: connections,
427
+ columns: [
428
+ {
429
+ id: "connection",
430
+ header: "Connection",
431
+ label: "Connection",
432
+ accessor: (connection) => connection.name,
433
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV4(VStack, {
434
+ gap: "xs",
435
+ children: [
436
+ /* @__PURE__ */ jsxDEV4(Text, {
437
+ className: "font-medium text-sm",
438
+ children: item.name
439
+ }, undefined, false, undefined, this),
440
+ /* @__PURE__ */ jsxDEV4(Text, {
441
+ className: "text-muted-foreground text-xs",
442
+ children: [
443
+ "Created ",
444
+ item.createdAt.toLocaleDateString()
445
+ ]
446
+ }, undefined, true, undefined, this)
447
+ ]
448
+ }, undefined, true, undefined, this),
449
+ size: 240,
450
+ minSize: 180,
451
+ canSort: true,
452
+ canPin: true,
453
+ canResize: true
454
+ },
455
+ {
456
+ id: "status",
457
+ header: "Status",
458
+ label: "Status",
459
+ accessorKey: "status",
460
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV4(StatusBadge, {
461
+ status: String(value)
462
+ }, undefined, false, undefined, this),
463
+ size: 150,
464
+ canSort: true,
465
+ canPin: true,
466
+ canResize: true
467
+ },
468
+ {
469
+ id: "lastSyncAt",
470
+ header: "Last Sync",
471
+ label: "Last Sync",
472
+ accessor: (connection) => connection.lastSyncAt?.getTime() ?? 0,
473
+ cell: ({ item }) => formatDateTime(item.lastSyncAt),
474
+ size: 200,
475
+ canSort: true,
476
+ canHide: true,
477
+ canResize: true
478
+ },
479
+ {
480
+ id: "errorMessage",
481
+ header: "Errors",
482
+ label: "Errors",
483
+ accessor: (connection) => connection.errorMessage ?? "",
484
+ cell: ({ value }) => String(value || "No errors"),
485
+ size: 240,
486
+ canHide: true,
487
+ canResize: true
488
+ }
489
+ ],
490
+ initialState: {
491
+ pagination: { pageIndex: 0, pageSize: 3 },
492
+ columnVisibility: { errorMessage: false },
493
+ columnPinning: { left: ["connection"], right: [] }
494
+ },
495
+ renderExpandedContent: (connection) => /* @__PURE__ */ jsxDEV4(VStack, {
496
+ gap: "sm",
497
+ className: "py-2",
498
+ children: [
499
+ /* @__PURE__ */ jsxDEV4(Text, {
500
+ className: "font-medium text-sm",
501
+ children: "Credentials"
502
+ }, undefined, false, undefined, this),
503
+ /* @__PURE__ */ jsxDEV4("pre", {
504
+ className: "overflow-auto rounded-md bg-muted/40 p-3 text-xs",
505
+ children: formatJson(connection.credentials)
506
+ }, undefined, false, undefined, this),
507
+ /* @__PURE__ */ jsxDEV4(Text, {
508
+ className: "font-medium text-sm",
509
+ children: "Config"
510
+ }, undefined, false, undefined, this),
511
+ /* @__PURE__ */ jsxDEV4("pre", {
512
+ className: "overflow-auto rounded-md bg-muted/40 p-3 text-xs",
513
+ children: formatJson(connection.config)
514
+ }, undefined, false, undefined, this),
515
+ /* @__PURE__ */ jsxDEV4(Text, {
516
+ className: "text-muted-foreground text-sm",
517
+ children: connection.errorMessage ?? "No sync errors recorded."
518
+ }, undefined, false, undefined, this)
519
+ ]
520
+ }, undefined, true, undefined, this),
521
+ getCanExpand: () => true
522
+ });
523
+ return /* @__PURE__ */ jsxDEV4(DataTable, {
524
+ controller,
525
+ title: "Connections",
526
+ description: "Client-mode ContractSpec table with visibility, pinning, resizing, and expanded diagnostics.",
527
+ toolbar: /* @__PURE__ */ jsxDEV4(IntegrationTableToolbar, {
528
+ controller,
529
+ label: `${connections.length} total connections`,
530
+ toggleColumnId: "errorMessage",
531
+ toggleVisibleLabel: "Hide Error Column",
532
+ toggleHiddenLabel: "Show Error Column",
533
+ pinColumnId: "status",
534
+ pinLabel: "Status",
535
+ resizeColumnId: "connection",
536
+ resizeLabel: "Widen Connection"
537
+ }, undefined, false, undefined, this),
538
+ emptyState: /* @__PURE__ */ jsxDEV4("div", {
539
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
540
+ children: "No connections found"
541
+ }, undefined, false, undefined, this)
542
+ }, undefined, false, undefined, this);
543
+ }
544
+
545
+ // src/ui/tables/SyncConfigsTable.tsx
546
+ import { DataTable as DataTable2 } from "@contractspec/lib.design-system";
547
+ import { useContractTable as useContractTable2 } from "@contractspec/lib.presentation-runtime-react";
548
+ import { VStack as VStack2 } from "@contractspec/lib.ui-kit-web/ui/stack";
549
+ import { Text as Text2 } from "@contractspec/lib.ui-kit-web/ui/text";
550
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
551
+ "use client";
552
+ function SyncConfigsTable({
553
+ syncConfigs
554
+ }) {
555
+ const controller = useContractTable2({
556
+ data: syncConfigs,
557
+ columns: [
558
+ {
559
+ id: "sync",
560
+ header: "Sync Config",
561
+ label: "Sync Config",
562
+ accessor: (sync) => sync.name,
563
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV5(VStack2, {
564
+ gap: "xs",
565
+ children: [
566
+ /* @__PURE__ */ jsxDEV5(Text2, {
567
+ className: "font-medium text-sm",
568
+ children: item.name
569
+ }, undefined, false, undefined, this),
570
+ /* @__PURE__ */ jsxDEV5(Text2, {
571
+ className: "text-muted-foreground text-xs",
572
+ children: [
573
+ item.sourceEntity,
574
+ " → ",
575
+ item.targetEntity
576
+ ]
577
+ }, undefined, true, undefined, this)
578
+ ]
579
+ }, undefined, true, undefined, this),
580
+ size: 260,
581
+ minSize: 200,
582
+ canSort: true,
583
+ canPin: true,
584
+ canResize: true
585
+ },
586
+ {
587
+ id: "frequency",
588
+ header: "Frequency",
589
+ label: "Frequency",
590
+ accessorKey: "frequency",
591
+ size: 160,
592
+ canSort: true,
593
+ canHide: true,
594
+ canResize: true
595
+ },
596
+ {
597
+ id: "status",
598
+ header: "Status",
599
+ label: "Status",
600
+ accessorKey: "status",
601
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV5(StatusBadge, {
602
+ status: String(value)
603
+ }, undefined, false, undefined, this),
604
+ size: 150,
605
+ canSort: true,
606
+ canPin: true,
607
+ canResize: true
608
+ },
609
+ {
610
+ id: "recordsSynced",
611
+ header: "Records",
612
+ label: "Records",
613
+ accessorKey: "recordsSynced",
614
+ align: "right",
615
+ size: 140,
616
+ canSort: true,
617
+ canResize: true
618
+ },
619
+ {
620
+ id: "lastRunAt",
621
+ header: "Last Run",
622
+ label: "Last Run",
623
+ accessor: (sync) => sync.lastRunAt?.getTime() ?? 0,
624
+ cell: ({ item }) => formatDateTime(item.lastRunAt),
625
+ size: 200,
626
+ canSort: true,
627
+ canHide: true,
628
+ canResize: true
629
+ }
630
+ ],
631
+ initialState: {
632
+ pagination: { pageIndex: 0, pageSize: 3 },
633
+ columnVisibility: { lastRunAt: false },
634
+ columnPinning: { left: ["sync"], right: [] }
635
+ },
636
+ renderExpandedContent: (sync) => /* @__PURE__ */ jsxDEV5(VStack2, {
637
+ gap: "sm",
638
+ className: "py-2",
639
+ children: [
640
+ /* @__PURE__ */ jsxDEV5(Text2, {
641
+ className: "text-muted-foreground text-sm",
642
+ children: [
643
+ "Connection ",
644
+ sync.connectionId
645
+ ]
646
+ }, undefined, true, undefined, this),
647
+ /* @__PURE__ */ jsxDEV5(Text2, {
648
+ className: "text-muted-foreground text-sm",
649
+ children: [
650
+ "Last run: ",
651
+ formatDateTime(sync.lastRunAt)
652
+ ]
653
+ }, undefined, true, undefined, this),
654
+ /* @__PURE__ */ jsxDEV5(Text2, {
655
+ className: "text-muted-foreground text-sm",
656
+ children: [
657
+ "Last status: ",
658
+ sync.lastRunStatus ?? "No runs recorded"
659
+ ]
660
+ }, undefined, true, undefined, this),
661
+ /* @__PURE__ */ jsxDEV5(Text2, {
662
+ className: "text-muted-foreground text-sm",
663
+ children: [
664
+ "Updated ",
665
+ sync.updatedAt.toLocaleString()
666
+ ]
667
+ }, undefined, true, undefined, this)
668
+ ]
669
+ }, undefined, true, undefined, this),
670
+ getCanExpand: () => true
671
+ });
672
+ return /* @__PURE__ */ jsxDEV5(DataTable2, {
673
+ controller,
674
+ title: "Sync Configs",
675
+ description: "Shared table primitives applied to sync monitoring without changing the surrounding dashboard layout.",
676
+ toolbar: /* @__PURE__ */ jsxDEV5(IntegrationTableToolbar, {
677
+ controller,
678
+ label: `${syncConfigs.length} syncs`,
679
+ toggleColumnId: "lastRunAt",
680
+ toggleVisibleLabel: "Hide Last Run",
681
+ toggleHiddenLabel: "Show Last Run",
682
+ pinColumnId: "status",
683
+ pinLabel: "Status",
684
+ resizeColumnId: "sync",
685
+ resizeLabel: "Widen Sync"
686
+ }, undefined, false, undefined, this),
687
+ emptyState: /* @__PURE__ */ jsxDEV5("div", {
688
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
689
+ children: "No sync configurations found"
690
+ }, undefined, false, undefined, this)
691
+ }, undefined, false, undefined, this);
692
+ }
353
693
  // src/ui/IntegrationDashboard.tsx
354
- import { useState as useState2 } from "react";
355
694
  import {
356
- Button,
695
+ Button as Button2,
357
696
  ErrorState,
358
697
  LoaderBlock,
359
698
  StatCard,
360
699
  StatCardGroup
361
700
  } from "@contractspec/lib.design-system";
362
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
701
+ import { useState as useState2 } from "react";
702
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
363
703
  "use client";
364
704
  var STATUS_COLORS = {
365
705
  ACTIVE: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
@@ -396,32 +736,32 @@ function IntegrationDashboard() {
396
736
  { id: "chat", label: "Chat", icon: "\uD83D\uDCAC" }
397
737
  ];
398
738
  if (loading) {
399
- return /* @__PURE__ */ jsxDEV2(LoaderBlock, {
739
+ return /* @__PURE__ */ jsxDEV6(LoaderBlock, {
400
740
  label: "Loading Integrations..."
401
741
  }, undefined, false, undefined, this);
402
742
  }
403
743
  if (error) {
404
- return /* @__PURE__ */ jsxDEV2(ErrorState, {
744
+ return /* @__PURE__ */ jsxDEV6(ErrorState, {
405
745
  title: "Failed to load Integrations",
406
746
  description: error.message,
407
747
  onRetry: refetch,
408
748
  retryLabel: "Retry"
409
749
  }, undefined, false, undefined, this);
410
750
  }
411
- return /* @__PURE__ */ jsxDEV2("div", {
751
+ return /* @__PURE__ */ jsxDEV6("div", {
412
752
  className: "space-y-6",
413
753
  children: [
414
- /* @__PURE__ */ jsxDEV2("div", {
754
+ /* @__PURE__ */ jsxDEV6("div", {
415
755
  className: "flex items-center justify-between",
416
756
  children: [
417
- /* @__PURE__ */ jsxDEV2("h2", {
418
- className: "text-2xl font-bold",
757
+ /* @__PURE__ */ jsxDEV6("h2", {
758
+ className: "font-bold text-2xl",
419
759
  children: "Integration Hub"
420
760
  }, undefined, false, undefined, this),
421
- /* @__PURE__ */ jsxDEV2(Button, {
761
+ /* @__PURE__ */ jsxDEV6(Button2, {
422
762
  onClick: () => alert("Add integration modal"),
423
763
  children: [
424
- /* @__PURE__ */ jsxDEV2("span", {
764
+ /* @__PURE__ */ jsxDEV6("span", {
425
765
  className: "mr-2",
426
766
  children: "+"
427
767
  }, undefined, false, undefined, this),
@@ -430,66 +770,71 @@ function IntegrationDashboard() {
430
770
  }, undefined, true, undefined, this)
431
771
  ]
432
772
  }, undefined, true, undefined, this),
433
- /* @__PURE__ */ jsxDEV2(StatCardGroup, {
773
+ /* @__PURE__ */ jsxDEV6(StatCardGroup, {
434
774
  children: [
435
- /* @__PURE__ */ jsxDEV2(StatCard, {
775
+ /* @__PURE__ */ jsxDEV6(StatCard, {
436
776
  label: "Integrations",
437
777
  value: stats.totalIntegrations,
438
778
  hint: `${stats.activeIntegrations} active`
439
779
  }, undefined, false, undefined, this),
440
- /* @__PURE__ */ jsxDEV2(StatCard, {
780
+ /* @__PURE__ */ jsxDEV6(StatCard, {
441
781
  label: "Connections",
442
782
  value: stats.totalConnections,
443
783
  hint: `${stats.connectedCount} connected`
444
784
  }, undefined, false, undefined, this),
445
- /* @__PURE__ */ jsxDEV2(StatCard, {
785
+ /* @__PURE__ */ jsxDEV6(StatCard, {
446
786
  label: "Syncs",
447
787
  value: stats.totalSyncs,
448
788
  hint: `${stats.activeSyncs} active`
449
789
  }, undefined, false, undefined, this)
450
790
  ]
451
791
  }, undefined, true, undefined, this),
452
- /* @__PURE__ */ jsxDEV2("nav", {
453
- className: "bg-muted flex gap-1 rounded-lg p-1",
792
+ /* @__PURE__ */ jsxDEV6(IntegrationVisualizationOverview, {
793
+ connections,
794
+ integrations,
795
+ syncConfigs
796
+ }, undefined, false, undefined, this),
797
+ /* @__PURE__ */ jsxDEV6("nav", {
798
+ className: "flex gap-1 rounded-lg bg-muted p-1",
454
799
  role: "tablist",
455
- children: tabs.map((tab) => /* @__PURE__ */ jsxDEV2(Button, {
800
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV6(Button2, {
456
801
  type: "button",
457
802
  role: "tab",
458
803
  "aria-selected": activeTab === tab.id,
459
804
  onClick: () => setActiveTab(tab.id),
460
- 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"}`,
805
+ className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 font-medium text-sm transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
461
806
  children: [
462
- /* @__PURE__ */ jsxDEV2("span", {
807
+ /* @__PURE__ */ jsxDEV6("span", {
463
808
  children: tab.icon
464
809
  }, undefined, false, undefined, this),
465
810
  tab.label
466
811
  ]
467
812
  }, tab.id, true, undefined, this))
468
813
  }, undefined, false, undefined, this),
469
- /* @__PURE__ */ jsxDEV2("div", {
814
+ /* @__PURE__ */ jsxDEV6("div", {
470
815
  className: "min-h-[400px]",
471
816
  role: "tabpanel",
472
817
  children: [
473
- activeTab === "integrations" && /* @__PURE__ */ jsxDEV2("div", {
818
+ activeTab === "integrations" && /* @__PURE__ */ jsxDEV6("div", {
474
819
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
475
820
  children: [
476
- integrations.map((integration) => /* @__PURE__ */ jsxDEV2("div", {
477
- className: "border-border bg-card hover:bg-muted/50 cursor-pointer rounded-lg border p-4 transition-colors",
821
+ integrations.map((integration) => /* @__PURE__ */ jsxDEV6("div", {
822
+ className: "cursor-pointer rounded-lg border border-border bg-card p-4 transition-colors hover:bg-muted/50",
478
823
  children: [
479
- /* @__PURE__ */ jsxDEV2("div", {
824
+ /* @__PURE__ */ jsxDEV6("div", {
480
825
  className: "mb-3 flex items-center gap-3",
481
826
  children: [
482
- /* @__PURE__ */ jsxDEV2("span", {
827
+ /* @__PURE__ */ jsxDEV6("span", {
483
828
  className: "text-2xl",
484
829
  children: TYPE_ICONS[integration.type] ?? "⚙️"
485
830
  }, undefined, false, undefined, this),
486
- /* @__PURE__ */ jsxDEV2("div", {
831
+ /* @__PURE__ */ jsxDEV6("div", {
487
832
  children: [
488
- /* @__PURE__ */ jsxDEV2("h3", {
833
+ /* @__PURE__ */ jsxDEV6("h3", {
489
834
  className: "font-medium",
490
835
  children: integration.name
491
836
  }, undefined, false, undefined, this),
492
- /* @__PURE__ */ jsxDEV2("p", {
837
+ /* @__PURE__ */ jsxDEV6("p", {
493
838
  className: "text-muted-foreground text-sm",
494
839
  children: integration.type
495
840
  }, undefined, false, undefined, this)
@@ -497,14 +842,14 @@ function IntegrationDashboard() {
497
842
  }, undefined, true, undefined, this)
498
843
  ]
499
844
  }, undefined, true, undefined, this),
500
- /* @__PURE__ */ jsxDEV2("div", {
845
+ /* @__PURE__ */ jsxDEV6("div", {
501
846
  className: "flex items-center justify-between",
502
847
  children: [
503
- /* @__PURE__ */ jsxDEV2("span", {
504
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[integration.status] ?? ""}`,
848
+ /* @__PURE__ */ jsxDEV6("span", {
849
+ className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[integration.status] ?? ""}`,
505
850
  children: integration.status
506
851
  }, undefined, false, undefined, this),
507
- /* @__PURE__ */ jsxDEV2("span", {
852
+ /* @__PURE__ */ jsxDEV6("span", {
508
853
  className: "text-muted-foreground text-xs",
509
854
  children: integration.createdAt.toLocaleDateString()
510
855
  }, undefined, false, undefined, this)
@@ -512,75 +857,16 @@ function IntegrationDashboard() {
512
857
  }, undefined, true, undefined, this)
513
858
  ]
514
859
  }, integration.id, true, undefined, this)),
515
- integrations.length === 0 && /* @__PURE__ */ jsxDEV2("div", {
516
- className: "text-muted-foreground col-span-full flex h-64 items-center justify-center",
860
+ integrations.length === 0 && /* @__PURE__ */ jsxDEV6("div", {
861
+ className: "col-span-full flex h-64 items-center justify-center text-muted-foreground",
517
862
  children: "No integrations configured"
518
863
  }, undefined, false, undefined, this)
519
864
  ]
520
865
  }, undefined, true, undefined, this),
521
- activeTab === "connections" && /* @__PURE__ */ jsxDEV2("div", {
522
- className: "border-border rounded-lg border",
523
- children: /* @__PURE__ */ jsxDEV2("table", {
524
- className: "w-full",
525
- children: [
526
- /* @__PURE__ */ jsxDEV2("thead", {
527
- className: "border-border bg-muted/30 border-b",
528
- children: /* @__PURE__ */ jsxDEV2("tr", {
529
- children: [
530
- /* @__PURE__ */ jsxDEV2("th", {
531
- className: "px-4 py-3 text-left text-sm font-medium",
532
- children: "Connection"
533
- }, undefined, false, undefined, this),
534
- /* @__PURE__ */ jsxDEV2("th", {
535
- className: "px-4 py-3 text-left text-sm font-medium",
536
- children: "Status"
537
- }, undefined, false, undefined, this),
538
- /* @__PURE__ */ jsxDEV2("th", {
539
- className: "px-4 py-3 text-left text-sm font-medium",
540
- children: "Last Sync"
541
- }, undefined, false, undefined, this)
542
- ]
543
- }, undefined, true, undefined, this)
544
- }, undefined, false, undefined, this),
545
- /* @__PURE__ */ jsxDEV2("tbody", {
546
- className: "divide-border divide-y",
547
- children: [
548
- connections.map((conn) => /* @__PURE__ */ jsxDEV2("tr", {
549
- className: "hover:bg-muted/50",
550
- children: [
551
- /* @__PURE__ */ jsxDEV2("td", {
552
- className: "px-4 py-3",
553
- children: /* @__PURE__ */ jsxDEV2("div", {
554
- className: "font-medium",
555
- children: conn.name
556
- }, undefined, false, undefined, this)
557
- }, undefined, false, undefined, this),
558
- /* @__PURE__ */ jsxDEV2("td", {
559
- className: "px-4 py-3",
560
- children: /* @__PURE__ */ jsxDEV2("span", {
561
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[conn.status] ?? ""}`,
562
- children: conn.status
563
- }, undefined, false, undefined, this)
564
- }, undefined, false, undefined, this),
565
- /* @__PURE__ */ jsxDEV2("td", {
566
- className: "text-muted-foreground px-4 py-3 text-sm",
567
- children: conn.lastSyncAt?.toLocaleString() ?? "Never"
568
- }, undefined, false, undefined, this)
569
- ]
570
- }, conn.id, true, undefined, this)),
571
- connections.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
572
- children: /* @__PURE__ */ jsxDEV2("td", {
573
- colSpan: 3,
574
- className: "text-muted-foreground px-4 py-8 text-center",
575
- children: "No connections found"
576
- }, undefined, false, undefined, this)
577
- }, undefined, false, undefined, this)
578
- ]
579
- }, undefined, true, undefined, this)
580
- ]
581
- }, undefined, true, undefined, this)
866
+ activeTab === "connections" && /* @__PURE__ */ jsxDEV6(ConnectionsTable, {
867
+ connections
582
868
  }, undefined, false, undefined, this),
583
- activeTab === "chat" && /* @__PURE__ */ jsxDEV2(IntegrationHubChat, {
869
+ activeTab === "chat" && /* @__PURE__ */ jsxDEV6(IntegrationHubChat, {
584
870
  proxyUrl: "/api/chat",
585
871
  thinkingLevel: "thinking",
586
872
  suggestions: [
@@ -590,85 +876,8 @@ function IntegrationDashboard() {
590
876
  ],
591
877
  className: "min-h-[400px]"
592
878
  }, undefined, false, undefined, this),
593
- activeTab === "syncs" && /* @__PURE__ */ jsxDEV2("div", {
594
- className: "border-border rounded-lg border",
595
- children: /* @__PURE__ */ jsxDEV2("table", {
596
- className: "w-full",
597
- children: [
598
- /* @__PURE__ */ jsxDEV2("thead", {
599
- className: "border-border bg-muted/30 border-b",
600
- children: /* @__PURE__ */ jsxDEV2("tr", {
601
- children: [
602
- /* @__PURE__ */ jsxDEV2("th", {
603
- className: "px-4 py-3 text-left text-sm font-medium",
604
- children: "Sync Config"
605
- }, undefined, false, undefined, this),
606
- /* @__PURE__ */ jsxDEV2("th", {
607
- className: "px-4 py-3 text-left text-sm font-medium",
608
- children: "Frequency"
609
- }, undefined, false, undefined, this),
610
- /* @__PURE__ */ jsxDEV2("th", {
611
- className: "px-4 py-3 text-left text-sm font-medium",
612
- children: "Status"
613
- }, undefined, false, undefined, this),
614
- /* @__PURE__ */ jsxDEV2("th", {
615
- className: "px-4 py-3 text-left text-sm font-medium",
616
- children: "Records"
617
- }, undefined, false, undefined, this)
618
- ]
619
- }, undefined, true, undefined, this)
620
- }, undefined, false, undefined, this),
621
- /* @__PURE__ */ jsxDEV2("tbody", {
622
- className: "divide-border divide-y",
623
- children: [
624
- syncConfigs.map((sync) => /* @__PURE__ */ jsxDEV2("tr", {
625
- className: "hover:bg-muted/50",
626
- children: [
627
- /* @__PURE__ */ jsxDEV2("td", {
628
- className: "px-4 py-3",
629
- children: [
630
- /* @__PURE__ */ jsxDEV2("div", {
631
- className: "font-medium",
632
- children: sync.name
633
- }, undefined, false, undefined, this),
634
- /* @__PURE__ */ jsxDEV2("div", {
635
- className: "text-muted-foreground text-sm",
636
- children: [
637
- sync.sourceEntity,
638
- " → ",
639
- sync.targetEntity
640
- ]
641
- }, undefined, true, undefined, this)
642
- ]
643
- }, undefined, true, undefined, this),
644
- /* @__PURE__ */ jsxDEV2("td", {
645
- className: "px-4 py-3 text-sm",
646
- children: sync.frequency
647
- }, undefined, false, undefined, this),
648
- /* @__PURE__ */ jsxDEV2("td", {
649
- className: "px-4 py-3",
650
- children: /* @__PURE__ */ jsxDEV2("span", {
651
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${STATUS_COLORS[sync.status] ?? ""}`,
652
- children: sync.status
653
- }, undefined, false, undefined, this)
654
- }, undefined, false, undefined, this),
655
- /* @__PURE__ */ jsxDEV2("td", {
656
- className: "text-muted-foreground px-4 py-3 text-sm",
657
- children: sync.recordsSynced.toLocaleString()
658
- }, undefined, false, undefined, this)
659
- ]
660
- }, sync.id, true, undefined, this)),
661
- syncConfigs.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
662
- children: /* @__PURE__ */ jsxDEV2("td", {
663
- colSpan: 4,
664
- className: "text-muted-foreground px-4 py-8 text-center",
665
- children: "No sync configurations found"
666
- }, undefined, false, undefined, this)
667
- }, undefined, false, undefined, this)
668
- ]
669
- }, undefined, true, undefined, this)
670
- ]
671
- }, undefined, true, undefined, this)
879
+ activeTab === "syncs" && /* @__PURE__ */ jsxDEV6(SyncConfigsTable, {
880
+ syncConfigs
672
881
  }, undefined, false, undefined, this)
673
882
  ]
674
883
  }, undefined, true, undefined, this)
@@ -676,8 +885,284 @@ function IntegrationDashboard() {
676
885
  }, undefined, true, undefined, this);
677
886
  }
678
887
 
679
- // src/ui/hooks/index.ts
680
- "use client";
888
+ // src/ui/renderers/integration.markdown.ts
889
+ var mockIntegrations = [
890
+ {
891
+ id: "int-1",
892
+ name: "Salesforce",
893
+ type: "CRM",
894
+ status: "ACTIVE",
895
+ connectionCount: 3
896
+ },
897
+ {
898
+ id: "int-2",
899
+ name: "HubSpot",
900
+ type: "MARKETING",
901
+ status: "ACTIVE",
902
+ connectionCount: 2
903
+ },
904
+ {
905
+ id: "int-3",
906
+ name: "Stripe",
907
+ type: "PAYMENT",
908
+ status: "ACTIVE",
909
+ connectionCount: 1
910
+ },
911
+ {
912
+ id: "int-4",
913
+ name: "Slack",
914
+ type: "COMMUNICATION",
915
+ status: "INACTIVE",
916
+ connectionCount: 0
917
+ },
918
+ {
919
+ id: "int-5",
920
+ name: "Google Sheets",
921
+ type: "DATA",
922
+ status: "ACTIVE",
923
+ connectionCount: 5
924
+ },
925
+ {
926
+ id: "int-6",
927
+ name: "PostHog",
928
+ type: "ANALYTICS",
929
+ status: "ACTIVE",
930
+ connectionCount: 1
931
+ }
932
+ ];
933
+ var mockConnections = [
934
+ {
935
+ id: "conn-1",
936
+ integrationId: "int-1",
937
+ name: "Production Salesforce",
938
+ status: "CONNECTED",
939
+ lastSyncAt: "2024-01-16T10:00:00Z"
940
+ },
941
+ {
942
+ id: "conn-2",
943
+ integrationId: "int-1",
944
+ name: "Sandbox Salesforce",
945
+ status: "CONNECTED",
946
+ lastSyncAt: "2024-01-15T14:00:00Z"
947
+ },
948
+ {
949
+ id: "conn-3",
950
+ integrationId: "int-2",
951
+ name: "Marketing HubSpot",
952
+ status: "CONNECTED",
953
+ lastSyncAt: "2024-01-16T08:00:00Z"
954
+ },
955
+ {
956
+ id: "conn-4",
957
+ integrationId: "int-3",
958
+ name: "Stripe Live",
959
+ status: "CONNECTED",
960
+ lastSyncAt: "2024-01-16T12:00:00Z"
961
+ },
962
+ {
963
+ id: "conn-5",
964
+ integrationId: "int-5",
965
+ name: "Analytics Sheet",
966
+ status: "ERROR",
967
+ lastSyncAt: "2024-01-14T09:00:00Z",
968
+ error: "Authentication expired"
969
+ },
970
+ {
971
+ id: "conn-6",
972
+ integrationId: "int-6",
973
+ name: "PostHog Workspace",
974
+ status: "CONNECTED",
975
+ lastSyncAt: "2024-01-16T11:45:00Z"
976
+ }
977
+ ];
978
+ var mockSyncConfigs = [
979
+ {
980
+ id: "sync-1",
981
+ connectionId: "conn-1",
982
+ name: "Contacts Sync",
983
+ frequency: "HOURLY",
984
+ lastRunAt: "2024-01-16T10:00:00Z",
985
+ status: "SUCCESS",
986
+ recordsSynced: 1250
987
+ },
988
+ {
989
+ id: "sync-2",
990
+ connectionId: "conn-1",
991
+ name: "Opportunities Sync",
992
+ frequency: "DAILY",
993
+ lastRunAt: "2024-01-16T00:00:00Z",
994
+ status: "SUCCESS",
995
+ recordsSynced: 340
996
+ },
997
+ {
998
+ id: "sync-3",
999
+ connectionId: "conn-3",
1000
+ name: "Orders Sync",
1001
+ frequency: "REALTIME",
1002
+ lastRunAt: "2024-01-16T12:30:00Z",
1003
+ status: "SUCCESS",
1004
+ recordsSynced: 89
1005
+ },
1006
+ {
1007
+ id: "sync-4",
1008
+ connectionId: "conn-5",
1009
+ name: "Metrics Export",
1010
+ frequency: "DAILY",
1011
+ lastRunAt: "2024-01-14T09:00:00Z",
1012
+ status: "FAILED",
1013
+ recordsSynced: 0
1014
+ }
1015
+ ];
1016
+ var integrationDashboardMarkdownRenderer = {
1017
+ target: "markdown",
1018
+ render: async (desc) => {
1019
+ if (desc.source.type !== "component" || desc.source.componentKey !== "IntegrationDashboard") {
1020
+ throw new Error("integrationDashboardMarkdownRenderer: not IntegrationDashboard");
1021
+ }
1022
+ const integrations = mockIntegrations;
1023
+ const connections = mockConnections;
1024
+ const syncs = mockSyncConfigs;
1025
+ const visualizations = createIntegrationVisualizationSections(integrations, connections, syncs);
1026
+ const activeIntegrations = integrations.filter((i) => i.status === "ACTIVE");
1027
+ const connectedConnections = connections.filter((c) => c.status === "CONNECTED");
1028
+ const errorConnections = connections.filter((c) => c.status === "ERROR");
1029
+ const successfulSyncs = syncs.filter((s) => s.status === "SUCCESS");
1030
+ const totalRecordsSynced = successfulSyncs.reduce((sum, s) => sum + s.recordsSynced, 0);
1031
+ const lines = [
1032
+ "# Integration Hub",
1033
+ "",
1034
+ "> Connect and sync data with external services",
1035
+ "",
1036
+ "## Overview",
1037
+ "",
1038
+ "| Metric | Value |",
1039
+ "|--------|-------|",
1040
+ `| Active Integrations | ${activeIntegrations.length} |`,
1041
+ `| Connected Services | ${connectedConnections.length} |`,
1042
+ `| Error Connections | ${errorConnections.length} |`,
1043
+ `| Sync Configs | ${syncs.length} |`,
1044
+ `| Records Synced (24h) | ${totalRecordsSynced.toLocaleString()} |`,
1045
+ ""
1046
+ ];
1047
+ lines.push("## Visualization Overview");
1048
+ lines.push("");
1049
+ for (const item of [
1050
+ ...visualizations.primaryItems,
1051
+ ...visualizations.comparisonItems
1052
+ ]) {
1053
+ lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
1054
+ }
1055
+ lines.push("");
1056
+ lines.push("## Integrations");
1057
+ lines.push("");
1058
+ lines.push("| Name | Type | Connections | Status |");
1059
+ lines.push("|------|------|-------------|--------|");
1060
+ for (const integration of integrations) {
1061
+ const statusIcon = integration.status === "ACTIVE" ? "\uD83D\uDFE2" : "⚫";
1062
+ lines.push(`| ${integration.name} | ${integration.type} | ${integration.connectionCount} | ${statusIcon} ${integration.status} |`);
1063
+ }
1064
+ lines.push("");
1065
+ lines.push("## Recent Sync Activity");
1066
+ lines.push("");
1067
+ lines.push("| Sync | Frequency | Last Run | Records | Status |");
1068
+ lines.push("|------|-----------|----------|---------|--------|");
1069
+ for (const sync of syncs) {
1070
+ const lastRun = new Date(sync.lastRunAt).toLocaleString();
1071
+ const statusIcon = sync.status === "SUCCESS" ? "✅" : "❌";
1072
+ lines.push(`| ${sync.name} | ${sync.frequency} | ${lastRun} | ${sync.recordsSynced} | ${statusIcon} ${sync.status} |`);
1073
+ }
1074
+ if (errorConnections.length > 0) {
1075
+ lines.push("");
1076
+ lines.push("## ⚠️ Connections with Errors");
1077
+ lines.push("");
1078
+ for (const conn of errorConnections) {
1079
+ const integration = integrations.find((i) => i.id === conn.integrationId);
1080
+ lines.push(`- **${conn.name}** (${integration?.name ?? "Unknown"}): ${conn.error ?? "Unknown error"}`);
1081
+ }
1082
+ }
1083
+ return {
1084
+ mimeType: "text/markdown",
1085
+ body: lines.join(`
1086
+ `)
1087
+ };
1088
+ }
1089
+ };
1090
+ var connectionListMarkdownRenderer = {
1091
+ target: "markdown",
1092
+ render: async (desc) => {
1093
+ if (desc.source.type !== "component" || desc.source.componentKey !== "ConnectionList") {
1094
+ throw new Error("connectionListMarkdownRenderer: not ConnectionList");
1095
+ }
1096
+ const connections = mockConnections;
1097
+ const integrations = mockIntegrations;
1098
+ const lines = [
1099
+ "# Connections",
1100
+ "",
1101
+ "> Manage connections to external services",
1102
+ ""
1103
+ ];
1104
+ for (const integration of integrations) {
1105
+ const intConnections = connections.filter((c) => c.integrationId === integration.id);
1106
+ if (intConnections.length === 0)
1107
+ continue;
1108
+ lines.push(`## ${integration.name}`);
1109
+ lines.push("");
1110
+ lines.push("| Connection | Status | Last Sync |");
1111
+ lines.push("|------------|--------|-----------|");
1112
+ for (const conn of intConnections) {
1113
+ const lastSync = new Date(conn.lastSyncAt).toLocaleString();
1114
+ const statusIcon = conn.status === "CONNECTED" ? "\uD83D\uDFE2" : conn.status === "ERROR" ? "\uD83D\uDD34" : "⚫";
1115
+ lines.push(`| ${conn.name} | ${statusIcon} ${conn.status} | ${lastSync} |`);
1116
+ }
1117
+ lines.push("");
1118
+ }
1119
+ return {
1120
+ mimeType: "text/markdown",
1121
+ body: lines.join(`
1122
+ `)
1123
+ };
1124
+ }
1125
+ };
1126
+ var syncConfigMarkdownRenderer = {
1127
+ target: "markdown",
1128
+ render: async (desc) => {
1129
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SyncConfigEditor") {
1130
+ throw new Error("syncConfigMarkdownRenderer: not SyncConfigEditor");
1131
+ }
1132
+ const syncs = mockSyncConfigs;
1133
+ const connections = mockConnections;
1134
+ const lines = [
1135
+ "# Sync Configurations",
1136
+ "",
1137
+ "> Configure automated data synchronization",
1138
+ ""
1139
+ ];
1140
+ for (const sync of syncs) {
1141
+ const connection = connections.find((c) => c.id === sync.connectionId);
1142
+ const statusIcon = sync.status === "SUCCESS" ? "✅" : "❌";
1143
+ lines.push(`## ${sync.name}`);
1144
+ lines.push("");
1145
+ lines.push(`**Connection:** ${connection?.name ?? "Unknown"}`);
1146
+ lines.push(`**Frequency:** ${sync.frequency}`);
1147
+ lines.push(`**Status:** ${statusIcon} ${sync.status}`);
1148
+ lines.push(`**Last Run:** ${new Date(sync.lastRunAt).toLocaleString()}`);
1149
+ lines.push(`**Records Synced:** ${sync.recordsSynced.toLocaleString()}`);
1150
+ lines.push("");
1151
+ }
1152
+ lines.push("## Frequency Options");
1153
+ lines.push("");
1154
+ lines.push("- **REALTIME**: Sync on every change");
1155
+ lines.push("- **HOURLY**: Sync every hour");
1156
+ lines.push("- **DAILY**: Sync once per day");
1157
+ lines.push("- **WEEKLY**: Sync once per week");
1158
+ lines.push("- **MANUAL**: Sync only when triggered");
1159
+ return {
1160
+ mimeType: "text/markdown",
1161
+ body: lines.join(`
1162
+ `)
1163
+ };
1164
+ }
1165
+ };
681
1166
  export {
682
1167
  useIntegrationData,
683
1168
  syncConfigMarkdownRenderer,