@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.
- package/README.md +73 -183
- package/dist/connection/index.d.ts +1 -1
- package/dist/docs/index.js +2 -1
- package/dist/docs/integration-hub.docblock.js +2 -1
- package/dist/events.js +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.js +1243 -749
- package/dist/integration/index.d.ts +1 -1
- package/dist/integration-hub.feature.js +202 -0
- package/dist/node/docs/index.js +2 -1
- package/dist/node/docs/integration-hub.docblock.js +2 -1
- package/dist/node/events.js +1 -1
- package/dist/node/index.js +1243 -749
- package/dist/node/integration-hub.feature.js +202 -0
- package/dist/node/ui/IntegrationDashboard.js +654 -180
- package/dist/node/ui/IntegrationDashboard.visualizations.js +250 -0
- package/dist/node/ui/hooks/index.js +1 -1
- package/dist/node/ui/hooks/useIntegrationData.js +1 -1
- package/dist/node/ui/index.js +970 -485
- package/dist/node/ui/renderers/index.js +216 -5
- package/dist/node/ui/renderers/integration.markdown.js +216 -5
- package/dist/node/ui/tables/ConnectionsTable.js +211 -0
- package/dist/node/ui/tables/IntegrationTables.js +361 -0
- package/dist/node/ui/tables/SyncConfigsTable.js +230 -0
- package/dist/node/ui/tables/integration-table.shared.js +84 -0
- package/dist/node/visualizations/catalog.js +137 -0
- package/dist/node/visualizations/index.js +211 -0
- package/dist/node/visualizations/selectors.js +204 -0
- package/dist/sync/index.d.ts +3 -3
- package/dist/ui/IntegrationDashboard.js +654 -180
- package/dist/ui/IntegrationDashboard.visualizations.d.ts +6 -0
- package/dist/ui/IntegrationDashboard.visualizations.js +251 -0
- package/dist/ui/hooks/index.d.ts +1 -1
- package/dist/ui/hooks/index.js +1 -1
- package/dist/ui/hooks/useIntegrationData.js +1 -1
- package/dist/ui/index.d.ts +2 -2
- package/dist/ui/index.js +970 -485
- package/dist/ui/renderers/index.d.ts +1 -1
- package/dist/ui/renderers/index.js +216 -5
- package/dist/ui/renderers/integration.markdown.js +216 -5
- package/dist/ui/tables/ConnectionsTable.d.ts +4 -0
- package/dist/ui/tables/ConnectionsTable.js +212 -0
- package/dist/ui/tables/IntegrationTables.d.ts +2 -0
- package/dist/ui/tables/IntegrationTables.js +362 -0
- package/dist/ui/tables/IntegrationTables.smoke.test.d.ts +1 -0
- package/dist/ui/tables/SyncConfigsTable.d.ts +4 -0
- package/dist/ui/tables/SyncConfigsTable.js +231 -0
- package/dist/ui/tables/integration-table.shared.d.ts +18 -0
- package/dist/ui/tables/integration-table.shared.js +85 -0
- package/dist/visualizations/catalog.d.ts +11 -0
- package/dist/visualizations/catalog.js +138 -0
- package/dist/visualizations/index.d.ts +2 -0
- package/dist/visualizations/index.js +212 -0
- package/dist/visualizations/selectors.d.ts +10 -0
- package/dist/visualizations/selectors.js +205 -0
- package/dist/visualizations/selectors.test.d.ts +1 -0
- package/package.json +110 -12
package/dist/node/ui/index.js
CHANGED
|
@@ -1,326 +1,309 @@
|
|
|
1
|
-
// src/ui/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
|
47
|
-
{
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
|
92
|
-
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
""
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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
|
-
|
|
312
|
-
|
|
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__ */
|
|
322
|
+
return /* @__PURE__ */ jsxDEV2("div", {
|
|
340
323
|
className: className ?? "flex h-[500px] flex-col",
|
|
341
|
-
children: /* @__PURE__ */
|
|
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 {
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
751
|
+
return /* @__PURE__ */ jsxDEV6("div", {
|
|
412
752
|
className: "space-y-6",
|
|
413
753
|
children: [
|
|
414
|
-
/* @__PURE__ */
|
|
754
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
415
755
|
className: "flex items-center justify-between",
|
|
416
756
|
children: [
|
|
417
|
-
/* @__PURE__ */
|
|
418
|
-
className: "text-2xl
|
|
757
|
+
/* @__PURE__ */ jsxDEV6("h2", {
|
|
758
|
+
className: "font-bold text-2xl",
|
|
419
759
|
children: "Integration Hub"
|
|
420
760
|
}, undefined, false, undefined, this),
|
|
421
|
-
/* @__PURE__ */
|
|
761
|
+
/* @__PURE__ */ jsxDEV6(Button2, {
|
|
422
762
|
onClick: () => alert("Add integration modal"),
|
|
423
763
|
children: [
|
|
424
|
-
/* @__PURE__ */
|
|
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__ */
|
|
773
|
+
/* @__PURE__ */ jsxDEV6(StatCardGroup, {
|
|
434
774
|
children: [
|
|
435
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
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__ */
|
|
453
|
-
|
|
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__ */
|
|
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
|
|
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__ */
|
|
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__ */
|
|
814
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
470
815
|
className: "min-h-[400px]",
|
|
471
816
|
role: "tabpanel",
|
|
472
817
|
children: [
|
|
473
|
-
activeTab === "integrations" && /* @__PURE__ */
|
|
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__ */
|
|
477
|
-
className: "
|
|
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__ */
|
|
824
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
480
825
|
className: "mb-3 flex items-center gap-3",
|
|
481
826
|
children: [
|
|
482
|
-
/* @__PURE__ */
|
|
827
|
+
/* @__PURE__ */ jsxDEV6("span", {
|
|
483
828
|
className: "text-2xl",
|
|
484
829
|
children: TYPE_ICONS[integration.type] ?? "⚙️"
|
|
485
830
|
}, undefined, false, undefined, this),
|
|
486
|
-
/* @__PURE__ */
|
|
831
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
487
832
|
children: [
|
|
488
|
-
/* @__PURE__ */
|
|
833
|
+
/* @__PURE__ */ jsxDEV6("h3", {
|
|
489
834
|
className: "font-medium",
|
|
490
835
|
children: integration.name
|
|
491
836
|
}, undefined, false, undefined, this),
|
|
492
|
-
/* @__PURE__ */
|
|
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__ */
|
|
845
|
+
/* @__PURE__ */ jsxDEV6("div", {
|
|
501
846
|
className: "flex items-center justify-between",
|
|
502
847
|
children: [
|
|
503
|
-
/* @__PURE__ */
|
|
504
|
-
className: `inline-flex rounded-full px-2 py-0.5 text-xs
|
|
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__ */
|
|
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__ */
|
|
516
|
-
className: "
|
|
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__ */
|
|
522
|
-
|
|
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__ */
|
|
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__ */
|
|
594
|
-
|
|
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/
|
|
680
|
-
|
|
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,
|