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