@contractspec/example.integration-hub 3.7.7 → 3.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +4 -1
  2. package/dist/docs/index.js +2 -1
  3. package/dist/docs/integration-hub.docblock.js +2 -1
  4. package/dist/index.d.ts +1 -0
  5. package/dist/index.js +669 -177
  6. package/dist/integration-hub.feature.js +202 -0
  7. package/dist/node/docs/index.js +2 -1
  8. package/dist/node/docs/integration-hub.docblock.js +2 -1
  9. package/dist/node/index.js +669 -177
  10. package/dist/node/integration-hub.feature.js +202 -0
  11. package/dist/node/ui/IntegrationDashboard.js +646 -172
  12. package/dist/node/ui/IntegrationDashboard.visualizations.js +250 -0
  13. package/dist/node/ui/index.js +661 -177
  14. package/dist/node/ui/renderers/index.js +216 -5
  15. package/dist/node/ui/renderers/integration.markdown.js +216 -5
  16. package/dist/node/ui/tables/ConnectionsTable.js +211 -0
  17. package/dist/node/ui/tables/IntegrationTables.js +361 -0
  18. package/dist/node/ui/tables/SyncConfigsTable.js +230 -0
  19. package/dist/node/ui/tables/integration-table.shared.js +84 -0
  20. package/dist/node/visualizations/catalog.js +137 -0
  21. package/dist/node/visualizations/index.js +211 -0
  22. package/dist/node/visualizations/selectors.js +204 -0
  23. package/dist/ui/IntegrationDashboard.js +646 -172
  24. package/dist/ui/IntegrationDashboard.visualizations.d.ts +6 -0
  25. package/dist/ui/IntegrationDashboard.visualizations.js +251 -0
  26. package/dist/ui/index.js +661 -177
  27. package/dist/ui/renderers/index.js +216 -5
  28. package/dist/ui/renderers/integration.markdown.js +216 -5
  29. package/dist/ui/tables/ConnectionsTable.d.ts +4 -0
  30. package/dist/ui/tables/ConnectionsTable.js +212 -0
  31. package/dist/ui/tables/IntegrationTables.d.ts +2 -0
  32. package/dist/ui/tables/IntegrationTables.js +362 -0
  33. package/dist/ui/tables/IntegrationTables.smoke.test.d.ts +1 -0
  34. package/dist/ui/tables/SyncConfigsTable.d.ts +4 -0
  35. package/dist/ui/tables/SyncConfigsTable.js +231 -0
  36. package/dist/ui/tables/integration-table.shared.d.ts +18 -0
  37. package/dist/ui/tables/integration-table.shared.js +85 -0
  38. package/dist/visualizations/catalog.d.ts +11 -0
  39. package/dist/visualizations/catalog.js +138 -0
  40. package/dist/visualizations/index.d.ts +2 -0
  41. package/dist/visualizations/index.js +212 -0
  42. package/dist/visualizations/selectors.d.ts +10 -0
  43. package/dist/visualizations/selectors.js +205 -0
  44. package/dist/visualizations/selectors.test.d.ts +1 -0
  45. package/package.json +108 -10
@@ -53,9 +53,257 @@ function useIntegrationData(projectId = "local-project") {
53
53
  // src/ui/hooks/index.ts
54
54
  "use client";
55
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."
88
+ },
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." }
101
+ }
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."
111
+ },
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." }
131
+ }
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."
141
+ },
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."
160
+ },
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." }
169
+ }
170
+ });
171
+ var IntegrationVisualizationSpecs = [
172
+ IntegrationTypeVisualization,
173
+ ConnectionStatusVisualization,
174
+ HealthySyncMetricVisualization,
175
+ AttentionSyncMetricVisualization
176
+ ];
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;
205
+ }
206
+ }
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."
232
+ }
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
250
+ }
251
+ ];
252
+ return {
253
+ primaryItems,
254
+ comparisonItems
255
+ };
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
+ }
303
+
56
304
  // src/ui/IntegrationHubChat.tsx
57
305
  import { ChatWithSidebar } from "@contractspec/module.ai-chat";
58
- import { jsxDEV } from "react/jsx-dev-runtime";
306
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
59
307
  "use client";
60
308
  var DEFAULT_SUGGESTIONS = [
61
309
  "List my integrations",
@@ -71,9 +319,9 @@ function IntegrationHubChat({
71
319
  systemPrompt = DEFAULT_SYSTEM_PROMPT,
72
320
  className
73
321
  }) {
74
- return /* @__PURE__ */ jsxDEV("div", {
322
+ return /* @__PURE__ */ jsxDEV2("div", {
75
323
  className: className ?? "flex h-[500px] flex-col",
76
- children: /* @__PURE__ */ jsxDEV(ChatWithSidebar, {
324
+ children: /* @__PURE__ */ jsxDEV2(ChatWithSidebar, {
77
325
  className: "flex-1",
78
326
  systemPrompt,
79
327
  proxyUrl,
@@ -85,16 +333,373 @@ function IntegrationHubChat({
85
333
  }, undefined, false, undefined, this);
86
334
  }
87
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
+ }
88
693
  // src/ui/IntegrationDashboard.tsx
89
694
  import {
90
- Button,
695
+ Button as Button2,
91
696
  ErrorState,
92
697
  LoaderBlock,
93
698
  StatCard,
94
699
  StatCardGroup
95
700
  } from "@contractspec/lib.design-system";
96
701
  import { useState as useState2 } from "react";
97
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
702
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
98
703
  "use client";
99
704
  var STATUS_COLORS = {
100
705
  ACTIVE: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
@@ -131,32 +736,32 @@ function IntegrationDashboard() {
131
736
  { id: "chat", label: "Chat", icon: "\uD83D\uDCAC" }
132
737
  ];
133
738
  if (loading) {
134
- return /* @__PURE__ */ jsxDEV2(LoaderBlock, {
739
+ return /* @__PURE__ */ jsxDEV6(LoaderBlock, {
135
740
  label: "Loading Integrations..."
136
741
  }, undefined, false, undefined, this);
137
742
  }
138
743
  if (error) {
139
- return /* @__PURE__ */ jsxDEV2(ErrorState, {
744
+ return /* @__PURE__ */ jsxDEV6(ErrorState, {
140
745
  title: "Failed to load Integrations",
141
746
  description: error.message,
142
747
  onRetry: refetch,
143
748
  retryLabel: "Retry"
144
749
  }, undefined, false, undefined, this);
145
750
  }
146
- return /* @__PURE__ */ jsxDEV2("div", {
751
+ return /* @__PURE__ */ jsxDEV6("div", {
147
752
  className: "space-y-6",
148
753
  children: [
149
- /* @__PURE__ */ jsxDEV2("div", {
754
+ /* @__PURE__ */ jsxDEV6("div", {
150
755
  className: "flex items-center justify-between",
151
756
  children: [
152
- /* @__PURE__ */ jsxDEV2("h2", {
757
+ /* @__PURE__ */ jsxDEV6("h2", {
153
758
  className: "font-bold text-2xl",
154
759
  children: "Integration Hub"
155
760
  }, undefined, false, undefined, this),
156
- /* @__PURE__ */ jsxDEV2(Button, {
761
+ /* @__PURE__ */ jsxDEV6(Button2, {
157
762
  onClick: () => alert("Add integration modal"),
158
763
  children: [
159
- /* @__PURE__ */ jsxDEV2("span", {
764
+ /* @__PURE__ */ jsxDEV6("span", {
160
765
  className: "mr-2",
161
766
  children: "+"
162
767
  }, undefined, false, undefined, this),
@@ -165,66 +770,71 @@ function IntegrationDashboard() {
165
770
  }, undefined, true, undefined, this)
166
771
  ]
167
772
  }, undefined, true, undefined, this),
168
- /* @__PURE__ */ jsxDEV2(StatCardGroup, {
773
+ /* @__PURE__ */ jsxDEV6(StatCardGroup, {
169
774
  children: [
170
- /* @__PURE__ */ jsxDEV2(StatCard, {
775
+ /* @__PURE__ */ jsxDEV6(StatCard, {
171
776
  label: "Integrations",
172
777
  value: stats.totalIntegrations,
173
778
  hint: `${stats.activeIntegrations} active`
174
779
  }, undefined, false, undefined, this),
175
- /* @__PURE__ */ jsxDEV2(StatCard, {
780
+ /* @__PURE__ */ jsxDEV6(StatCard, {
176
781
  label: "Connections",
177
782
  value: stats.totalConnections,
178
783
  hint: `${stats.connectedCount} connected`
179
784
  }, undefined, false, undefined, this),
180
- /* @__PURE__ */ jsxDEV2(StatCard, {
785
+ /* @__PURE__ */ jsxDEV6(StatCard, {
181
786
  label: "Syncs",
182
787
  value: stats.totalSyncs,
183
788
  hint: `${stats.activeSyncs} active`
184
789
  }, undefined, false, undefined, this)
185
790
  ]
186
791
  }, undefined, true, undefined, this),
187
- /* @__PURE__ */ jsxDEV2("nav", {
792
+ /* @__PURE__ */ jsxDEV6(IntegrationVisualizationOverview, {
793
+ connections,
794
+ integrations,
795
+ syncConfigs
796
+ }, undefined, false, undefined, this),
797
+ /* @__PURE__ */ jsxDEV6("nav", {
188
798
  className: "flex gap-1 rounded-lg bg-muted p-1",
189
799
  role: "tablist",
190
- children: tabs.map((tab) => /* @__PURE__ */ jsxDEV2(Button, {
800
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV6(Button2, {
191
801
  type: "button",
192
802
  role: "tab",
193
803
  "aria-selected": activeTab === tab.id,
194
804
  onClick: () => setActiveTab(tab.id),
195
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"}`,
196
806
  children: [
197
- /* @__PURE__ */ jsxDEV2("span", {
807
+ /* @__PURE__ */ jsxDEV6("span", {
198
808
  children: tab.icon
199
809
  }, undefined, false, undefined, this),
200
810
  tab.label
201
811
  ]
202
812
  }, tab.id, true, undefined, this))
203
813
  }, undefined, false, undefined, this),
204
- /* @__PURE__ */ jsxDEV2("div", {
814
+ /* @__PURE__ */ jsxDEV6("div", {
205
815
  className: "min-h-[400px]",
206
816
  role: "tabpanel",
207
817
  children: [
208
- activeTab === "integrations" && /* @__PURE__ */ jsxDEV2("div", {
818
+ activeTab === "integrations" && /* @__PURE__ */ jsxDEV6("div", {
209
819
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
210
820
  children: [
211
- integrations.map((integration) => /* @__PURE__ */ jsxDEV2("div", {
821
+ integrations.map((integration) => /* @__PURE__ */ jsxDEV6("div", {
212
822
  className: "cursor-pointer rounded-lg border border-border bg-card p-4 transition-colors hover:bg-muted/50",
213
823
  children: [
214
- /* @__PURE__ */ jsxDEV2("div", {
824
+ /* @__PURE__ */ jsxDEV6("div", {
215
825
  className: "mb-3 flex items-center gap-3",
216
826
  children: [
217
- /* @__PURE__ */ jsxDEV2("span", {
827
+ /* @__PURE__ */ jsxDEV6("span", {
218
828
  className: "text-2xl",
219
829
  children: TYPE_ICONS[integration.type] ?? "⚙️"
220
830
  }, undefined, false, undefined, this),
221
- /* @__PURE__ */ jsxDEV2("div", {
831
+ /* @__PURE__ */ jsxDEV6("div", {
222
832
  children: [
223
- /* @__PURE__ */ jsxDEV2("h3", {
833
+ /* @__PURE__ */ jsxDEV6("h3", {
224
834
  className: "font-medium",
225
835
  children: integration.name
226
836
  }, undefined, false, undefined, this),
227
- /* @__PURE__ */ jsxDEV2("p", {
837
+ /* @__PURE__ */ jsxDEV6("p", {
228
838
  className: "text-muted-foreground text-sm",
229
839
  children: integration.type
230
840
  }, undefined, false, undefined, this)
@@ -232,14 +842,14 @@ function IntegrationDashboard() {
232
842
  }, undefined, true, undefined, this)
233
843
  ]
234
844
  }, undefined, true, undefined, this),
235
- /* @__PURE__ */ jsxDEV2("div", {
845
+ /* @__PURE__ */ jsxDEV6("div", {
236
846
  className: "flex items-center justify-between",
237
847
  children: [
238
- /* @__PURE__ */ jsxDEV2("span", {
848
+ /* @__PURE__ */ jsxDEV6("span", {
239
849
  className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[integration.status] ?? ""}`,
240
850
  children: integration.status
241
851
  }, undefined, false, undefined, this),
242
- /* @__PURE__ */ jsxDEV2("span", {
852
+ /* @__PURE__ */ jsxDEV6("span", {
243
853
  className: "text-muted-foreground text-xs",
244
854
  children: integration.createdAt.toLocaleDateString()
245
855
  }, undefined, false, undefined, this)
@@ -247,75 +857,16 @@ function IntegrationDashboard() {
247
857
  }, undefined, true, undefined, this)
248
858
  ]
249
859
  }, integration.id, true, undefined, this)),
250
- integrations.length === 0 && /* @__PURE__ */ jsxDEV2("div", {
860
+ integrations.length === 0 && /* @__PURE__ */ jsxDEV6("div", {
251
861
  className: "col-span-full flex h-64 items-center justify-center text-muted-foreground",
252
862
  children: "No integrations configured"
253
863
  }, undefined, false, undefined, this)
254
864
  ]
255
865
  }, undefined, true, undefined, this),
256
- activeTab === "connections" && /* @__PURE__ */ jsxDEV2("div", {
257
- className: "rounded-lg border border-border",
258
- children: /* @__PURE__ */ jsxDEV2("table", {
259
- className: "w-full",
260
- children: [
261
- /* @__PURE__ */ jsxDEV2("thead", {
262
- className: "border-border border-b bg-muted/30",
263
- children: /* @__PURE__ */ jsxDEV2("tr", {
264
- children: [
265
- /* @__PURE__ */ jsxDEV2("th", {
266
- className: "px-4 py-3 text-left font-medium text-sm",
267
- children: "Connection"
268
- }, undefined, false, undefined, this),
269
- /* @__PURE__ */ jsxDEV2("th", {
270
- className: "px-4 py-3 text-left font-medium text-sm",
271
- children: "Status"
272
- }, undefined, false, undefined, this),
273
- /* @__PURE__ */ jsxDEV2("th", {
274
- className: "px-4 py-3 text-left font-medium text-sm",
275
- children: "Last Sync"
276
- }, undefined, false, undefined, this)
277
- ]
278
- }, undefined, true, undefined, this)
279
- }, undefined, false, undefined, this),
280
- /* @__PURE__ */ jsxDEV2("tbody", {
281
- className: "divide-y divide-border",
282
- children: [
283
- connections.map((conn) => /* @__PURE__ */ jsxDEV2("tr", {
284
- className: "hover:bg-muted/50",
285
- children: [
286
- /* @__PURE__ */ jsxDEV2("td", {
287
- className: "px-4 py-3",
288
- children: /* @__PURE__ */ jsxDEV2("div", {
289
- className: "font-medium",
290
- children: conn.name
291
- }, undefined, false, undefined, this)
292
- }, undefined, false, undefined, this),
293
- /* @__PURE__ */ jsxDEV2("td", {
294
- className: "px-4 py-3",
295
- children: /* @__PURE__ */ jsxDEV2("span", {
296
- className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[conn.status] ?? ""}`,
297
- children: conn.status
298
- }, undefined, false, undefined, this)
299
- }, undefined, false, undefined, this),
300
- /* @__PURE__ */ jsxDEV2("td", {
301
- className: "px-4 py-3 text-muted-foreground text-sm",
302
- children: conn.lastSyncAt?.toLocaleString() ?? "Never"
303
- }, undefined, false, undefined, this)
304
- ]
305
- }, conn.id, true, undefined, this)),
306
- connections.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
307
- children: /* @__PURE__ */ jsxDEV2("td", {
308
- colSpan: 3,
309
- className: "px-4 py-8 text-center text-muted-foreground",
310
- children: "No connections found"
311
- }, undefined, false, undefined, this)
312
- }, undefined, false, undefined, this)
313
- ]
314
- }, undefined, true, undefined, this)
315
- ]
316
- }, undefined, true, undefined, this)
866
+ activeTab === "connections" && /* @__PURE__ */ jsxDEV6(ConnectionsTable, {
867
+ connections
317
868
  }, undefined, false, undefined, this),
318
- activeTab === "chat" && /* @__PURE__ */ jsxDEV2(IntegrationHubChat, {
869
+ activeTab === "chat" && /* @__PURE__ */ jsxDEV6(IntegrationHubChat, {
319
870
  proxyUrl: "/api/chat",
320
871
  thinkingLevel: "thinking",
321
872
  suggestions: [
@@ -325,85 +876,8 @@ function IntegrationDashboard() {
325
876
  ],
326
877
  className: "min-h-[400px]"
327
878
  }, undefined, false, undefined, this),
328
- activeTab === "syncs" && /* @__PURE__ */ jsxDEV2("div", {
329
- className: "rounded-lg border border-border",
330
- children: /* @__PURE__ */ jsxDEV2("table", {
331
- className: "w-full",
332
- children: [
333
- /* @__PURE__ */ jsxDEV2("thead", {
334
- className: "border-border border-b bg-muted/30",
335
- children: /* @__PURE__ */ jsxDEV2("tr", {
336
- children: [
337
- /* @__PURE__ */ jsxDEV2("th", {
338
- className: "px-4 py-3 text-left font-medium text-sm",
339
- children: "Sync Config"
340
- }, undefined, false, undefined, this),
341
- /* @__PURE__ */ jsxDEV2("th", {
342
- className: "px-4 py-3 text-left font-medium text-sm",
343
- children: "Frequency"
344
- }, undefined, false, undefined, this),
345
- /* @__PURE__ */ jsxDEV2("th", {
346
- className: "px-4 py-3 text-left font-medium text-sm",
347
- children: "Status"
348
- }, undefined, false, undefined, this),
349
- /* @__PURE__ */ jsxDEV2("th", {
350
- className: "px-4 py-3 text-left font-medium text-sm",
351
- children: "Records"
352
- }, undefined, false, undefined, this)
353
- ]
354
- }, undefined, true, undefined, this)
355
- }, undefined, false, undefined, this),
356
- /* @__PURE__ */ jsxDEV2("tbody", {
357
- className: "divide-y divide-border",
358
- children: [
359
- syncConfigs.map((sync) => /* @__PURE__ */ jsxDEV2("tr", {
360
- className: "hover:bg-muted/50",
361
- children: [
362
- /* @__PURE__ */ jsxDEV2("td", {
363
- className: "px-4 py-3",
364
- children: [
365
- /* @__PURE__ */ jsxDEV2("div", {
366
- className: "font-medium",
367
- children: sync.name
368
- }, undefined, false, undefined, this),
369
- /* @__PURE__ */ jsxDEV2("div", {
370
- className: "text-muted-foreground text-sm",
371
- children: [
372
- sync.sourceEntity,
373
- " → ",
374
- sync.targetEntity
375
- ]
376
- }, undefined, true, undefined, this)
377
- ]
378
- }, undefined, true, undefined, this),
379
- /* @__PURE__ */ jsxDEV2("td", {
380
- className: "px-4 py-3 text-sm",
381
- children: sync.frequency
382
- }, undefined, false, undefined, this),
383
- /* @__PURE__ */ jsxDEV2("td", {
384
- className: "px-4 py-3",
385
- children: /* @__PURE__ */ jsxDEV2("span", {
386
- className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[sync.status] ?? ""}`,
387
- children: sync.status
388
- }, undefined, false, undefined, this)
389
- }, undefined, false, undefined, this),
390
- /* @__PURE__ */ jsxDEV2("td", {
391
- className: "px-4 py-3 text-muted-foreground text-sm",
392
- children: sync.recordsSynced.toLocaleString()
393
- }, undefined, false, undefined, this)
394
- ]
395
- }, sync.id, true, undefined, this)),
396
- syncConfigs.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
397
- children: /* @__PURE__ */ jsxDEV2("td", {
398
- colSpan: 4,
399
- className: "px-4 py-8 text-center text-muted-foreground",
400
- children: "No sync configurations found"
401
- }, undefined, false, undefined, this)
402
- }, undefined, false, undefined, this)
403
- ]
404
- }, undefined, true, undefined, this)
405
- ]
406
- }, undefined, true, undefined, this)
879
+ activeTab === "syncs" && /* @__PURE__ */ jsxDEV6(SyncConfigsTable, {
880
+ syncConfigs
407
881
  }, undefined, false, undefined, this)
408
882
  ]
409
883
  }, undefined, true, undefined, this)
@@ -548,6 +1022,7 @@ var integrationDashboardMarkdownRenderer = {
548
1022
  const integrations = mockIntegrations;
549
1023
  const connections = mockConnections;
550
1024
  const syncs = mockSyncConfigs;
1025
+ const visualizations = createIntegrationVisualizationSections(integrations, connections, syncs);
551
1026
  const activeIntegrations = integrations.filter((i) => i.status === "ACTIVE");
552
1027
  const connectedConnections = connections.filter((c) => c.status === "CONNECTED");
553
1028
  const errorConnections = connections.filter((c) => c.status === "ERROR");
@@ -567,12 +1042,21 @@ var integrationDashboardMarkdownRenderer = {
567
1042
  `| Error Connections | ${errorConnections.length} |`,
568
1043
  `| Sync Configs | ${syncs.length} |`,
569
1044
  `| Records Synced (24h) | ${totalRecordsSynced.toLocaleString()} |`,
570
- "",
571
- "## Integrations",
572
- "",
573
- "| Name | Type | Connections | Status |",
574
- "|------|------|-------------|--------|"
1045
+ ""
575
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("|------|------|-------------|--------|");
576
1060
  for (const integration of integrations) {
577
1061
  const statusIcon = integration.status === "ACTIVE" ? "\uD83D\uDFE2" : "⚫";
578
1062
  lines.push(`| ${integration.name} | ${integration.type} | ${integration.connectionCount} | ${statusIcon} ${integration.status} |`);