@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
@@ -50,9 +50,257 @@ function useIntegrationData(projectId = "local-project") {
50
50
  };
51
51
  }
52
52
 
53
+ // src/visualizations/catalog.ts
54
+ import {
55
+ defineVisualization,
56
+ VisualizationRegistry
57
+ } from "@contractspec/lib.contracts-spec/visualizations";
58
+ var INTEGRATION_LIST_REF = {
59
+ key: "integration.list",
60
+ version: "1.0.0"
61
+ };
62
+ var CONNECTION_LIST_REF = {
63
+ key: "integration.connection.list",
64
+ version: "1.0.0"
65
+ };
66
+ var SYNC_CONFIG_REF = {
67
+ key: "integration.syncConfig.list",
68
+ version: "1.0.0"
69
+ };
70
+ var META = {
71
+ version: "1.0.0",
72
+ domain: "integration",
73
+ stability: "experimental",
74
+ owners: ["@example.integration-hub"],
75
+ tags: ["integration", "visualization", "sync"]
76
+ };
77
+ var IntegrationTypeVisualization = defineVisualization({
78
+ meta: {
79
+ ...META,
80
+ key: "integration-hub.visualization.integration-types",
81
+ title: "Integration Types",
82
+ description: "Distribution of configured integration categories.",
83
+ goal: "Show where integration coverage is concentrated.",
84
+ context: "Integration overview."
85
+ },
86
+ source: { primary: INTEGRATION_LIST_REF, resultPath: "data" },
87
+ visualization: {
88
+ kind: "pie",
89
+ nameDimension: "type",
90
+ valueMeasure: "count",
91
+ dimensions: [
92
+ { key: "type", label: "Type", dataPath: "type", type: "category" }
93
+ ],
94
+ measures: [
95
+ { key: "count", label: "Count", dataPath: "count", format: "number" }
96
+ ],
97
+ table: { caption: "Integration counts by type." }
98
+ }
99
+ });
100
+ var ConnectionStatusVisualization = defineVisualization({
101
+ meta: {
102
+ ...META,
103
+ key: "integration-hub.visualization.connection-status",
104
+ title: "Connection Status",
105
+ description: "Status distribution across configured connections.",
106
+ goal: "Highlight connection health and instability.",
107
+ context: "Connection monitoring."
108
+ },
109
+ source: { primary: CONNECTION_LIST_REF, resultPath: "data" },
110
+ visualization: {
111
+ kind: "cartesian",
112
+ variant: "bar",
113
+ xDimension: "status",
114
+ yMeasures: ["count"],
115
+ dimensions: [
116
+ { key: "status", label: "Status", dataPath: "status", type: "category" }
117
+ ],
118
+ measures: [
119
+ {
120
+ key: "count",
121
+ label: "Connections",
122
+ dataPath: "count",
123
+ format: "number",
124
+ color: "#1d4ed8"
125
+ }
126
+ ],
127
+ table: { caption: "Connection counts by status." }
128
+ }
129
+ });
130
+ var HealthySyncMetricVisualization = defineVisualization({
131
+ meta: {
132
+ ...META,
133
+ key: "integration-hub.visualization.sync-healthy",
134
+ title: "Healthy Syncs",
135
+ description: "Sync configurations currently healthy or recently successful.",
136
+ goal: "Summarize healthy synchronization capacity.",
137
+ context: "Sync-state comparison."
138
+ },
139
+ source: { primary: SYNC_CONFIG_REF, resultPath: "data" },
140
+ visualization: {
141
+ kind: "metric",
142
+ measure: "value",
143
+ measures: [
144
+ { key: "value", label: "Syncs", dataPath: "value", format: "number" }
145
+ ],
146
+ table: { caption: "Healthy sync count." }
147
+ }
148
+ });
149
+ var AttentionSyncMetricVisualization = defineVisualization({
150
+ meta: {
151
+ ...META,
152
+ key: "integration-hub.visualization.sync-attention",
153
+ title: "Attention Needed",
154
+ description: "Sync configurations paused, failing, or otherwise needing review.",
155
+ goal: "Summarize syncs needing action.",
156
+ context: "Sync-state comparison."
157
+ },
158
+ source: { primary: SYNC_CONFIG_REF, resultPath: "data" },
159
+ visualization: {
160
+ kind: "metric",
161
+ measure: "value",
162
+ measures: [
163
+ { key: "value", label: "Syncs", dataPath: "value", format: "number" }
164
+ ],
165
+ table: { caption: "Syncs requiring attention." }
166
+ }
167
+ });
168
+ var IntegrationVisualizationSpecs = [
169
+ IntegrationTypeVisualization,
170
+ ConnectionStatusVisualization,
171
+ HealthySyncMetricVisualization,
172
+ AttentionSyncMetricVisualization
173
+ ];
174
+ var IntegrationVisualizationRegistry = new VisualizationRegistry([
175
+ ...IntegrationVisualizationSpecs
176
+ ]);
177
+ var IntegrationVisualizationRefs = IntegrationVisualizationSpecs.map((spec) => ({
178
+ key: spec.meta.key,
179
+ version: spec.meta.version
180
+ }));
181
+
182
+ // src/visualizations/selectors.ts
183
+ function isHealthySync(status) {
184
+ return status === "ACTIVE" || status === "SUCCESS";
185
+ }
186
+ function createIntegrationVisualizationSections(integrations, connections, syncConfigs) {
187
+ const integrationTypes = new Map;
188
+ const connectionStatuses = new Map;
189
+ let healthySyncs = 0;
190
+ let attentionSyncs = 0;
191
+ for (const integration of integrations) {
192
+ integrationTypes.set(integration.type, (integrationTypes.get(integration.type) ?? 0) + 1);
193
+ }
194
+ for (const connection of connections) {
195
+ connectionStatuses.set(connection.status, (connectionStatuses.get(connection.status) ?? 0) + 1);
196
+ }
197
+ for (const syncConfig of syncConfigs) {
198
+ if (isHealthySync(syncConfig.status)) {
199
+ healthySyncs += 1;
200
+ } else {
201
+ attentionSyncs += 1;
202
+ }
203
+ }
204
+ const primaryItems = [
205
+ {
206
+ key: "integration-types",
207
+ spec: IntegrationTypeVisualization,
208
+ data: {
209
+ data: Array.from(integrationTypes.entries()).map(([type, count]) => ({
210
+ type,
211
+ count
212
+ }))
213
+ },
214
+ title: "Integration Types",
215
+ description: "Configured integrations grouped by category.",
216
+ height: 260
217
+ },
218
+ {
219
+ key: "connection-status",
220
+ spec: ConnectionStatusVisualization,
221
+ data: {
222
+ data: Array.from(connectionStatuses.entries()).map(([status, count]) => ({
223
+ status,
224
+ count
225
+ }))
226
+ },
227
+ title: "Connection Status",
228
+ description: "Operational health across current connections."
229
+ }
230
+ ];
231
+ const comparisonItems = [
232
+ {
233
+ key: "healthy-syncs",
234
+ spec: HealthySyncMetricVisualization,
235
+ data: { data: [{ value: healthySyncs }] },
236
+ title: "Healthy Syncs",
237
+ description: "Active or recently successful sync configurations.",
238
+ height: 200
239
+ },
240
+ {
241
+ key: "attention-syncs",
242
+ spec: AttentionSyncMetricVisualization,
243
+ data: { data: [{ value: attentionSyncs }] },
244
+ title: "Attention Needed",
245
+ description: "Paused, failed, or degraded sync configurations.",
246
+ height: 200
247
+ }
248
+ ];
249
+ return {
250
+ primaryItems,
251
+ comparisonItems
252
+ };
253
+ }
254
+ // src/ui/IntegrationDashboard.visualizations.tsx
255
+ import {
256
+ ComparisonView,
257
+ VisualizationCard,
258
+ VisualizationGrid
259
+ } from "@contractspec/lib.design-system";
260
+ import { jsxDEV } from "react/jsx-dev-runtime";
261
+ "use client";
262
+ function IntegrationVisualizationOverview({
263
+ integrations,
264
+ connections,
265
+ syncConfigs
266
+ }) {
267
+ const { primaryItems, comparisonItems } = createIntegrationVisualizationSections(integrations, connections, syncConfigs);
268
+ return /* @__PURE__ */ jsxDEV("section", {
269
+ className: "space-y-4",
270
+ children: [
271
+ /* @__PURE__ */ jsxDEV("div", {
272
+ children: [
273
+ /* @__PURE__ */ jsxDEV("h3", {
274
+ className: "font-semibold text-lg",
275
+ children: "Integration Visualizations"
276
+ }, undefined, false, undefined, this),
277
+ /* @__PURE__ */ jsxDEV("p", {
278
+ className: "text-muted-foreground text-sm",
279
+ children: "Contract-backed charts for integration coverage and sync health."
280
+ }, undefined, false, undefined, this)
281
+ ]
282
+ }, undefined, true, undefined, this),
283
+ /* @__PURE__ */ jsxDEV(VisualizationGrid, {
284
+ children: primaryItems.map((item) => /* @__PURE__ */ jsxDEV(VisualizationCard, {
285
+ data: item.data,
286
+ description: item.description,
287
+ height: item.height,
288
+ spec: item.spec,
289
+ title: item.title
290
+ }, item.key, false, undefined, this))
291
+ }, undefined, false, undefined, this),
292
+ /* @__PURE__ */ jsxDEV(ComparisonView, {
293
+ description: "Comparison surface for healthy versus attention-needed syncs.",
294
+ items: comparisonItems,
295
+ title: "Sync-State Comparison"
296
+ }, undefined, false, undefined, this)
297
+ ]
298
+ }, undefined, true, undefined, this);
299
+ }
300
+
53
301
  // src/ui/IntegrationHubChat.tsx
54
302
  import { ChatWithSidebar } from "@contractspec/module.ai-chat";
55
- import { jsxDEV } from "react/jsx-dev-runtime";
303
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
56
304
  "use client";
57
305
  var DEFAULT_SUGGESTIONS = [
58
306
  "List my integrations",
@@ -68,9 +316,9 @@ function IntegrationHubChat({
68
316
  systemPrompt = DEFAULT_SYSTEM_PROMPT,
69
317
  className
70
318
  }) {
71
- return /* @__PURE__ */ jsxDEV("div", {
319
+ return /* @__PURE__ */ jsxDEV2("div", {
72
320
  className: className ?? "flex h-[500px] flex-col",
73
- children: /* @__PURE__ */ jsxDEV(ChatWithSidebar, {
321
+ children: /* @__PURE__ */ jsxDEV2(ChatWithSidebar, {
74
322
  className: "flex-1",
75
323
  systemPrompt,
76
324
  proxyUrl,
@@ -82,16 +330,373 @@ function IntegrationHubChat({
82
330
  }, undefined, false, undefined, this);
83
331
  }
84
332
 
333
+ // src/ui/tables/integration-table.shared.tsx
334
+ import { Button } from "@contractspec/lib.design-system";
335
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
336
+ import { HStack } from "@contractspec/lib.ui-kit-web/ui/stack";
337
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
338
+ "use client";
339
+ var STATUS_VARIANTS = {
340
+ ACTIVE: "default",
341
+ CONNECTED: "default",
342
+ SUCCESS: "default",
343
+ PENDING: "secondary",
344
+ PAUSED: "secondary",
345
+ ERROR: "destructive",
346
+ DISCONNECTED: "outline"
347
+ };
348
+ function formatDateTime(value) {
349
+ return value ? value.toLocaleString() : "Never";
350
+ }
351
+ function formatJson(value) {
352
+ return value ? JSON.stringify(value, null, 2) : "No configuration";
353
+ }
354
+ function StatusBadge({ status }) {
355
+ return /* @__PURE__ */ jsxDEV3(Badge, {
356
+ variant: STATUS_VARIANTS[status] ?? "outline",
357
+ children: status
358
+ }, undefined, false, undefined, this);
359
+ }
360
+ function IntegrationTableToolbar({
361
+ controller,
362
+ label,
363
+ toggleColumnId,
364
+ toggleVisibleLabel,
365
+ toggleHiddenLabel,
366
+ pinColumnId,
367
+ pinLabel,
368
+ resizeColumnId,
369
+ resizeLabel
370
+ }) {
371
+ const firstRow = controller.rows[0];
372
+ const toggleColumn = controller.columns.find((column) => column.id === toggleColumnId);
373
+ const pinColumn = controller.columns.find((column) => column.id === pinColumnId);
374
+ const resizeColumn = controller.columns.find((column) => column.id === resizeColumnId);
375
+ const pinTarget = pinColumn?.pinState === "left" ? false : "left";
376
+ return /* @__PURE__ */ jsxDEV3(HStack, {
377
+ gap: "sm",
378
+ className: "flex-wrap",
379
+ children: [
380
+ /* @__PURE__ */ jsxDEV3(Badge, {
381
+ variant: "outline",
382
+ children: label
383
+ }, undefined, false, undefined, this),
384
+ /* @__PURE__ */ jsxDEV3(Button, {
385
+ variant: "outline",
386
+ size: "sm",
387
+ onPress: () => firstRow?.toggleExpanded?.(!firstRow?.isExpanded),
388
+ children: "Expand First Row"
389
+ }, undefined, false, undefined, this),
390
+ /* @__PURE__ */ jsxDEV3(Button, {
391
+ variant: "outline",
392
+ size: "sm",
393
+ onPress: () => toggleColumn?.toggleVisibility?.(!toggleColumn?.visible),
394
+ children: toggleColumn?.visible ? toggleVisibleLabel : toggleHiddenLabel
395
+ }, undefined, false, undefined, this),
396
+ /* @__PURE__ */ jsxDEV3(Button, {
397
+ variant: "outline",
398
+ size: "sm",
399
+ onPress: () => pinColumn?.pin?.(pinTarget),
400
+ children: pinColumn?.pinState === "left" ? `Unpin ${pinLabel}` : `Pin ${pinLabel}`
401
+ }, undefined, false, undefined, this),
402
+ /* @__PURE__ */ jsxDEV3(Button, {
403
+ variant: "outline",
404
+ size: "sm",
405
+ onPress: () => resizeColumn?.resizeBy?.(40),
406
+ children: resizeLabel
407
+ }, undefined, false, undefined, this)
408
+ ]
409
+ }, undefined, true, undefined, this);
410
+ }
411
+
412
+ // src/ui/tables/ConnectionsTable.tsx
413
+ import { DataTable } from "@contractspec/lib.design-system";
414
+ import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
415
+ import { VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
416
+ import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
417
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
418
+ "use client";
419
+ function ConnectionsTable({
420
+ connections
421
+ }) {
422
+ const controller = useContractTable({
423
+ data: connections,
424
+ columns: [
425
+ {
426
+ id: "connection",
427
+ header: "Connection",
428
+ label: "Connection",
429
+ accessor: (connection) => connection.name,
430
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV4(VStack, {
431
+ gap: "xs",
432
+ children: [
433
+ /* @__PURE__ */ jsxDEV4(Text, {
434
+ className: "font-medium text-sm",
435
+ children: item.name
436
+ }, undefined, false, undefined, this),
437
+ /* @__PURE__ */ jsxDEV4(Text, {
438
+ className: "text-muted-foreground text-xs",
439
+ children: [
440
+ "Created ",
441
+ item.createdAt.toLocaleDateString()
442
+ ]
443
+ }, undefined, true, undefined, this)
444
+ ]
445
+ }, undefined, true, undefined, this),
446
+ size: 240,
447
+ minSize: 180,
448
+ canSort: true,
449
+ canPin: true,
450
+ canResize: true
451
+ },
452
+ {
453
+ id: "status",
454
+ header: "Status",
455
+ label: "Status",
456
+ accessorKey: "status",
457
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV4(StatusBadge, {
458
+ status: String(value)
459
+ }, undefined, false, undefined, this),
460
+ size: 150,
461
+ canSort: true,
462
+ canPin: true,
463
+ canResize: true
464
+ },
465
+ {
466
+ id: "lastSyncAt",
467
+ header: "Last Sync",
468
+ label: "Last Sync",
469
+ accessor: (connection) => connection.lastSyncAt?.getTime() ?? 0,
470
+ cell: ({ item }) => formatDateTime(item.lastSyncAt),
471
+ size: 200,
472
+ canSort: true,
473
+ canHide: true,
474
+ canResize: true
475
+ },
476
+ {
477
+ id: "errorMessage",
478
+ header: "Errors",
479
+ label: "Errors",
480
+ accessor: (connection) => connection.errorMessage ?? "",
481
+ cell: ({ value }) => String(value || "No errors"),
482
+ size: 240,
483
+ canHide: true,
484
+ canResize: true
485
+ }
486
+ ],
487
+ initialState: {
488
+ pagination: { pageIndex: 0, pageSize: 3 },
489
+ columnVisibility: { errorMessage: false },
490
+ columnPinning: { left: ["connection"], right: [] }
491
+ },
492
+ renderExpandedContent: (connection) => /* @__PURE__ */ jsxDEV4(VStack, {
493
+ gap: "sm",
494
+ className: "py-2",
495
+ children: [
496
+ /* @__PURE__ */ jsxDEV4(Text, {
497
+ className: "font-medium text-sm",
498
+ children: "Credentials"
499
+ }, undefined, false, undefined, this),
500
+ /* @__PURE__ */ jsxDEV4("pre", {
501
+ className: "overflow-auto rounded-md bg-muted/40 p-3 text-xs",
502
+ children: formatJson(connection.credentials)
503
+ }, undefined, false, undefined, this),
504
+ /* @__PURE__ */ jsxDEV4(Text, {
505
+ className: "font-medium text-sm",
506
+ children: "Config"
507
+ }, undefined, false, undefined, this),
508
+ /* @__PURE__ */ jsxDEV4("pre", {
509
+ className: "overflow-auto rounded-md bg-muted/40 p-3 text-xs",
510
+ children: formatJson(connection.config)
511
+ }, undefined, false, undefined, this),
512
+ /* @__PURE__ */ jsxDEV4(Text, {
513
+ className: "text-muted-foreground text-sm",
514
+ children: connection.errorMessage ?? "No sync errors recorded."
515
+ }, undefined, false, undefined, this)
516
+ ]
517
+ }, undefined, true, undefined, this),
518
+ getCanExpand: () => true
519
+ });
520
+ return /* @__PURE__ */ jsxDEV4(DataTable, {
521
+ controller,
522
+ title: "Connections",
523
+ description: "Client-mode ContractSpec table with visibility, pinning, resizing, and expanded diagnostics.",
524
+ toolbar: /* @__PURE__ */ jsxDEV4(IntegrationTableToolbar, {
525
+ controller,
526
+ label: `${connections.length} total connections`,
527
+ toggleColumnId: "errorMessage",
528
+ toggleVisibleLabel: "Hide Error Column",
529
+ toggleHiddenLabel: "Show Error Column",
530
+ pinColumnId: "status",
531
+ pinLabel: "Status",
532
+ resizeColumnId: "connection",
533
+ resizeLabel: "Widen Connection"
534
+ }, undefined, false, undefined, this),
535
+ emptyState: /* @__PURE__ */ jsxDEV4("div", {
536
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
537
+ children: "No connections found"
538
+ }, undefined, false, undefined, this)
539
+ }, undefined, false, undefined, this);
540
+ }
541
+
542
+ // src/ui/tables/SyncConfigsTable.tsx
543
+ import { DataTable as DataTable2 } from "@contractspec/lib.design-system";
544
+ import { useContractTable as useContractTable2 } from "@contractspec/lib.presentation-runtime-react";
545
+ import { VStack as VStack2 } from "@contractspec/lib.ui-kit-web/ui/stack";
546
+ import { Text as Text2 } from "@contractspec/lib.ui-kit-web/ui/text";
547
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
548
+ "use client";
549
+ function SyncConfigsTable({
550
+ syncConfigs
551
+ }) {
552
+ const controller = useContractTable2({
553
+ data: syncConfigs,
554
+ columns: [
555
+ {
556
+ id: "sync",
557
+ header: "Sync Config",
558
+ label: "Sync Config",
559
+ accessor: (sync) => sync.name,
560
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV5(VStack2, {
561
+ gap: "xs",
562
+ children: [
563
+ /* @__PURE__ */ jsxDEV5(Text2, {
564
+ className: "font-medium text-sm",
565
+ children: item.name
566
+ }, undefined, false, undefined, this),
567
+ /* @__PURE__ */ jsxDEV5(Text2, {
568
+ className: "text-muted-foreground text-xs",
569
+ children: [
570
+ item.sourceEntity,
571
+ " → ",
572
+ item.targetEntity
573
+ ]
574
+ }, undefined, true, undefined, this)
575
+ ]
576
+ }, undefined, true, undefined, this),
577
+ size: 260,
578
+ minSize: 200,
579
+ canSort: true,
580
+ canPin: true,
581
+ canResize: true
582
+ },
583
+ {
584
+ id: "frequency",
585
+ header: "Frequency",
586
+ label: "Frequency",
587
+ accessorKey: "frequency",
588
+ size: 160,
589
+ canSort: true,
590
+ canHide: true,
591
+ canResize: true
592
+ },
593
+ {
594
+ id: "status",
595
+ header: "Status",
596
+ label: "Status",
597
+ accessorKey: "status",
598
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV5(StatusBadge, {
599
+ status: String(value)
600
+ }, undefined, false, undefined, this),
601
+ size: 150,
602
+ canSort: true,
603
+ canPin: true,
604
+ canResize: true
605
+ },
606
+ {
607
+ id: "recordsSynced",
608
+ header: "Records",
609
+ label: "Records",
610
+ accessorKey: "recordsSynced",
611
+ align: "right",
612
+ size: 140,
613
+ canSort: true,
614
+ canResize: true
615
+ },
616
+ {
617
+ id: "lastRunAt",
618
+ header: "Last Run",
619
+ label: "Last Run",
620
+ accessor: (sync) => sync.lastRunAt?.getTime() ?? 0,
621
+ cell: ({ item }) => formatDateTime(item.lastRunAt),
622
+ size: 200,
623
+ canSort: true,
624
+ canHide: true,
625
+ canResize: true
626
+ }
627
+ ],
628
+ initialState: {
629
+ pagination: { pageIndex: 0, pageSize: 3 },
630
+ columnVisibility: { lastRunAt: false },
631
+ columnPinning: { left: ["sync"], right: [] }
632
+ },
633
+ renderExpandedContent: (sync) => /* @__PURE__ */ jsxDEV5(VStack2, {
634
+ gap: "sm",
635
+ className: "py-2",
636
+ children: [
637
+ /* @__PURE__ */ jsxDEV5(Text2, {
638
+ className: "text-muted-foreground text-sm",
639
+ children: [
640
+ "Connection ",
641
+ sync.connectionId
642
+ ]
643
+ }, undefined, true, undefined, this),
644
+ /* @__PURE__ */ jsxDEV5(Text2, {
645
+ className: "text-muted-foreground text-sm",
646
+ children: [
647
+ "Last run: ",
648
+ formatDateTime(sync.lastRunAt)
649
+ ]
650
+ }, undefined, true, undefined, this),
651
+ /* @__PURE__ */ jsxDEV5(Text2, {
652
+ className: "text-muted-foreground text-sm",
653
+ children: [
654
+ "Last status: ",
655
+ sync.lastRunStatus ?? "No runs recorded"
656
+ ]
657
+ }, undefined, true, undefined, this),
658
+ /* @__PURE__ */ jsxDEV5(Text2, {
659
+ className: "text-muted-foreground text-sm",
660
+ children: [
661
+ "Updated ",
662
+ sync.updatedAt.toLocaleString()
663
+ ]
664
+ }, undefined, true, undefined, this)
665
+ ]
666
+ }, undefined, true, undefined, this),
667
+ getCanExpand: () => true
668
+ });
669
+ return /* @__PURE__ */ jsxDEV5(DataTable2, {
670
+ controller,
671
+ title: "Sync Configs",
672
+ description: "Shared table primitives applied to sync monitoring without changing the surrounding dashboard layout.",
673
+ toolbar: /* @__PURE__ */ jsxDEV5(IntegrationTableToolbar, {
674
+ controller,
675
+ label: `${syncConfigs.length} syncs`,
676
+ toggleColumnId: "lastRunAt",
677
+ toggleVisibleLabel: "Hide Last Run",
678
+ toggleHiddenLabel: "Show Last Run",
679
+ pinColumnId: "status",
680
+ pinLabel: "Status",
681
+ resizeColumnId: "sync",
682
+ resizeLabel: "Widen Sync"
683
+ }, undefined, false, undefined, this),
684
+ emptyState: /* @__PURE__ */ jsxDEV5("div", {
685
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
686
+ children: "No sync configurations found"
687
+ }, undefined, false, undefined, this)
688
+ }, undefined, false, undefined, this);
689
+ }
85
690
  // src/ui/IntegrationDashboard.tsx
86
691
  import {
87
- Button,
692
+ Button as Button2,
88
693
  ErrorState,
89
694
  LoaderBlock,
90
695
  StatCard,
91
696
  StatCardGroup
92
697
  } from "@contractspec/lib.design-system";
93
698
  import { useState as useState2 } from "react";
94
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
699
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
95
700
  "use client";
96
701
  var STATUS_COLORS = {
97
702
  ACTIVE: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400",
@@ -128,32 +733,32 @@ function IntegrationDashboard() {
128
733
  { id: "chat", label: "Chat", icon: "\uD83D\uDCAC" }
129
734
  ];
130
735
  if (loading) {
131
- return /* @__PURE__ */ jsxDEV2(LoaderBlock, {
736
+ return /* @__PURE__ */ jsxDEV6(LoaderBlock, {
132
737
  label: "Loading Integrations..."
133
738
  }, undefined, false, undefined, this);
134
739
  }
135
740
  if (error) {
136
- return /* @__PURE__ */ jsxDEV2(ErrorState, {
741
+ return /* @__PURE__ */ jsxDEV6(ErrorState, {
137
742
  title: "Failed to load Integrations",
138
743
  description: error.message,
139
744
  onRetry: refetch,
140
745
  retryLabel: "Retry"
141
746
  }, undefined, false, undefined, this);
142
747
  }
143
- return /* @__PURE__ */ jsxDEV2("div", {
748
+ return /* @__PURE__ */ jsxDEV6("div", {
144
749
  className: "space-y-6",
145
750
  children: [
146
- /* @__PURE__ */ jsxDEV2("div", {
751
+ /* @__PURE__ */ jsxDEV6("div", {
147
752
  className: "flex items-center justify-between",
148
753
  children: [
149
- /* @__PURE__ */ jsxDEV2("h2", {
754
+ /* @__PURE__ */ jsxDEV6("h2", {
150
755
  className: "font-bold text-2xl",
151
756
  children: "Integration Hub"
152
757
  }, undefined, false, undefined, this),
153
- /* @__PURE__ */ jsxDEV2(Button, {
758
+ /* @__PURE__ */ jsxDEV6(Button2, {
154
759
  onClick: () => alert("Add integration modal"),
155
760
  children: [
156
- /* @__PURE__ */ jsxDEV2("span", {
761
+ /* @__PURE__ */ jsxDEV6("span", {
157
762
  className: "mr-2",
158
763
  children: "+"
159
764
  }, undefined, false, undefined, this),
@@ -162,66 +767,71 @@ function IntegrationDashboard() {
162
767
  }, undefined, true, undefined, this)
163
768
  ]
164
769
  }, undefined, true, undefined, this),
165
- /* @__PURE__ */ jsxDEV2(StatCardGroup, {
770
+ /* @__PURE__ */ jsxDEV6(StatCardGroup, {
166
771
  children: [
167
- /* @__PURE__ */ jsxDEV2(StatCard, {
772
+ /* @__PURE__ */ jsxDEV6(StatCard, {
168
773
  label: "Integrations",
169
774
  value: stats.totalIntegrations,
170
775
  hint: `${stats.activeIntegrations} active`
171
776
  }, undefined, false, undefined, this),
172
- /* @__PURE__ */ jsxDEV2(StatCard, {
777
+ /* @__PURE__ */ jsxDEV6(StatCard, {
173
778
  label: "Connections",
174
779
  value: stats.totalConnections,
175
780
  hint: `${stats.connectedCount} connected`
176
781
  }, undefined, false, undefined, this),
177
- /* @__PURE__ */ jsxDEV2(StatCard, {
782
+ /* @__PURE__ */ jsxDEV6(StatCard, {
178
783
  label: "Syncs",
179
784
  value: stats.totalSyncs,
180
785
  hint: `${stats.activeSyncs} active`
181
786
  }, undefined, false, undefined, this)
182
787
  ]
183
788
  }, undefined, true, undefined, this),
184
- /* @__PURE__ */ jsxDEV2("nav", {
789
+ /* @__PURE__ */ jsxDEV6(IntegrationVisualizationOverview, {
790
+ connections,
791
+ integrations,
792
+ syncConfigs
793
+ }, undefined, false, undefined, this),
794
+ /* @__PURE__ */ jsxDEV6("nav", {
185
795
  className: "flex gap-1 rounded-lg bg-muted p-1",
186
796
  role: "tablist",
187
- children: tabs.map((tab) => /* @__PURE__ */ jsxDEV2(Button, {
797
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV6(Button2, {
188
798
  type: "button",
189
799
  role: "tab",
190
800
  "aria-selected": activeTab === tab.id,
191
801
  onClick: () => setActiveTab(tab.id),
192
802
  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"}`,
193
803
  children: [
194
- /* @__PURE__ */ jsxDEV2("span", {
804
+ /* @__PURE__ */ jsxDEV6("span", {
195
805
  children: tab.icon
196
806
  }, undefined, false, undefined, this),
197
807
  tab.label
198
808
  ]
199
809
  }, tab.id, true, undefined, this))
200
810
  }, undefined, false, undefined, this),
201
- /* @__PURE__ */ jsxDEV2("div", {
811
+ /* @__PURE__ */ jsxDEV6("div", {
202
812
  className: "min-h-[400px]",
203
813
  role: "tabpanel",
204
814
  children: [
205
- activeTab === "integrations" && /* @__PURE__ */ jsxDEV2("div", {
815
+ activeTab === "integrations" && /* @__PURE__ */ jsxDEV6("div", {
206
816
  className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-3",
207
817
  children: [
208
- integrations.map((integration) => /* @__PURE__ */ jsxDEV2("div", {
818
+ integrations.map((integration) => /* @__PURE__ */ jsxDEV6("div", {
209
819
  className: "cursor-pointer rounded-lg border border-border bg-card p-4 transition-colors hover:bg-muted/50",
210
820
  children: [
211
- /* @__PURE__ */ jsxDEV2("div", {
821
+ /* @__PURE__ */ jsxDEV6("div", {
212
822
  className: "mb-3 flex items-center gap-3",
213
823
  children: [
214
- /* @__PURE__ */ jsxDEV2("span", {
824
+ /* @__PURE__ */ jsxDEV6("span", {
215
825
  className: "text-2xl",
216
826
  children: TYPE_ICONS[integration.type] ?? "⚙️"
217
827
  }, undefined, false, undefined, this),
218
- /* @__PURE__ */ jsxDEV2("div", {
828
+ /* @__PURE__ */ jsxDEV6("div", {
219
829
  children: [
220
- /* @__PURE__ */ jsxDEV2("h3", {
830
+ /* @__PURE__ */ jsxDEV6("h3", {
221
831
  className: "font-medium",
222
832
  children: integration.name
223
833
  }, undefined, false, undefined, this),
224
- /* @__PURE__ */ jsxDEV2("p", {
834
+ /* @__PURE__ */ jsxDEV6("p", {
225
835
  className: "text-muted-foreground text-sm",
226
836
  children: integration.type
227
837
  }, undefined, false, undefined, this)
@@ -229,14 +839,14 @@ function IntegrationDashboard() {
229
839
  }, undefined, true, undefined, this)
230
840
  ]
231
841
  }, undefined, true, undefined, this),
232
- /* @__PURE__ */ jsxDEV2("div", {
842
+ /* @__PURE__ */ jsxDEV6("div", {
233
843
  className: "flex items-center justify-between",
234
844
  children: [
235
- /* @__PURE__ */ jsxDEV2("span", {
845
+ /* @__PURE__ */ jsxDEV6("span", {
236
846
  className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[integration.status] ?? ""}`,
237
847
  children: integration.status
238
848
  }, undefined, false, undefined, this),
239
- /* @__PURE__ */ jsxDEV2("span", {
849
+ /* @__PURE__ */ jsxDEV6("span", {
240
850
  className: "text-muted-foreground text-xs",
241
851
  children: integration.createdAt.toLocaleDateString()
242
852
  }, undefined, false, undefined, this)
@@ -244,75 +854,16 @@ function IntegrationDashboard() {
244
854
  }, undefined, true, undefined, this)
245
855
  ]
246
856
  }, integration.id, true, undefined, this)),
247
- integrations.length === 0 && /* @__PURE__ */ jsxDEV2("div", {
857
+ integrations.length === 0 && /* @__PURE__ */ jsxDEV6("div", {
248
858
  className: "col-span-full flex h-64 items-center justify-center text-muted-foreground",
249
859
  children: "No integrations configured"
250
860
  }, undefined, false, undefined, this)
251
861
  ]
252
862
  }, undefined, true, undefined, this),
253
- activeTab === "connections" && /* @__PURE__ */ jsxDEV2("div", {
254
- className: "rounded-lg border border-border",
255
- children: /* @__PURE__ */ jsxDEV2("table", {
256
- className: "w-full",
257
- children: [
258
- /* @__PURE__ */ jsxDEV2("thead", {
259
- className: "border-border border-b bg-muted/30",
260
- children: /* @__PURE__ */ jsxDEV2("tr", {
261
- children: [
262
- /* @__PURE__ */ jsxDEV2("th", {
263
- className: "px-4 py-3 text-left font-medium text-sm",
264
- children: "Connection"
265
- }, undefined, false, undefined, this),
266
- /* @__PURE__ */ jsxDEV2("th", {
267
- className: "px-4 py-3 text-left font-medium text-sm",
268
- children: "Status"
269
- }, undefined, false, undefined, this),
270
- /* @__PURE__ */ jsxDEV2("th", {
271
- className: "px-4 py-3 text-left font-medium text-sm",
272
- children: "Last Sync"
273
- }, undefined, false, undefined, this)
274
- ]
275
- }, undefined, true, undefined, this)
276
- }, undefined, false, undefined, this),
277
- /* @__PURE__ */ jsxDEV2("tbody", {
278
- className: "divide-y divide-border",
279
- children: [
280
- connections.map((conn) => /* @__PURE__ */ jsxDEV2("tr", {
281
- className: "hover:bg-muted/50",
282
- children: [
283
- /* @__PURE__ */ jsxDEV2("td", {
284
- className: "px-4 py-3",
285
- children: /* @__PURE__ */ jsxDEV2("div", {
286
- className: "font-medium",
287
- children: conn.name
288
- }, undefined, false, undefined, this)
289
- }, undefined, false, undefined, this),
290
- /* @__PURE__ */ jsxDEV2("td", {
291
- className: "px-4 py-3",
292
- children: /* @__PURE__ */ jsxDEV2("span", {
293
- className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[conn.status] ?? ""}`,
294
- children: conn.status
295
- }, undefined, false, undefined, this)
296
- }, undefined, false, undefined, this),
297
- /* @__PURE__ */ jsxDEV2("td", {
298
- className: "px-4 py-3 text-muted-foreground text-sm",
299
- children: conn.lastSyncAt?.toLocaleString() ?? "Never"
300
- }, undefined, false, undefined, this)
301
- ]
302
- }, conn.id, true, undefined, this)),
303
- connections.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
304
- children: /* @__PURE__ */ jsxDEV2("td", {
305
- colSpan: 3,
306
- className: "px-4 py-8 text-center text-muted-foreground",
307
- children: "No connections found"
308
- }, undefined, false, undefined, this)
309
- }, undefined, false, undefined, this)
310
- ]
311
- }, undefined, true, undefined, this)
312
- ]
313
- }, undefined, true, undefined, this)
863
+ activeTab === "connections" && /* @__PURE__ */ jsxDEV6(ConnectionsTable, {
864
+ connections
314
865
  }, undefined, false, undefined, this),
315
- activeTab === "chat" && /* @__PURE__ */ jsxDEV2(IntegrationHubChat, {
866
+ activeTab === "chat" && /* @__PURE__ */ jsxDEV6(IntegrationHubChat, {
316
867
  proxyUrl: "/api/chat",
317
868
  thinkingLevel: "thinking",
318
869
  suggestions: [
@@ -322,85 +873,8 @@ function IntegrationDashboard() {
322
873
  ],
323
874
  className: "min-h-[400px]"
324
875
  }, undefined, false, undefined, this),
325
- activeTab === "syncs" && /* @__PURE__ */ jsxDEV2("div", {
326
- className: "rounded-lg border border-border",
327
- children: /* @__PURE__ */ jsxDEV2("table", {
328
- className: "w-full",
329
- children: [
330
- /* @__PURE__ */ jsxDEV2("thead", {
331
- className: "border-border border-b bg-muted/30",
332
- children: /* @__PURE__ */ jsxDEV2("tr", {
333
- children: [
334
- /* @__PURE__ */ jsxDEV2("th", {
335
- className: "px-4 py-3 text-left font-medium text-sm",
336
- children: "Sync Config"
337
- }, undefined, false, undefined, this),
338
- /* @__PURE__ */ jsxDEV2("th", {
339
- className: "px-4 py-3 text-left font-medium text-sm",
340
- children: "Frequency"
341
- }, undefined, false, undefined, this),
342
- /* @__PURE__ */ jsxDEV2("th", {
343
- className: "px-4 py-3 text-left font-medium text-sm",
344
- children: "Status"
345
- }, undefined, false, undefined, this),
346
- /* @__PURE__ */ jsxDEV2("th", {
347
- className: "px-4 py-3 text-left font-medium text-sm",
348
- children: "Records"
349
- }, undefined, false, undefined, this)
350
- ]
351
- }, undefined, true, undefined, this)
352
- }, undefined, false, undefined, this),
353
- /* @__PURE__ */ jsxDEV2("tbody", {
354
- className: "divide-y divide-border",
355
- children: [
356
- syncConfigs.map((sync) => /* @__PURE__ */ jsxDEV2("tr", {
357
- className: "hover:bg-muted/50",
358
- children: [
359
- /* @__PURE__ */ jsxDEV2("td", {
360
- className: "px-4 py-3",
361
- children: [
362
- /* @__PURE__ */ jsxDEV2("div", {
363
- className: "font-medium",
364
- children: sync.name
365
- }, undefined, false, undefined, this),
366
- /* @__PURE__ */ jsxDEV2("div", {
367
- className: "text-muted-foreground text-sm",
368
- children: [
369
- sync.sourceEntity,
370
- " → ",
371
- sync.targetEntity
372
- ]
373
- }, undefined, true, undefined, this)
374
- ]
375
- }, undefined, true, undefined, this),
376
- /* @__PURE__ */ jsxDEV2("td", {
377
- className: "px-4 py-3 text-sm",
378
- children: sync.frequency
379
- }, undefined, false, undefined, this),
380
- /* @__PURE__ */ jsxDEV2("td", {
381
- className: "px-4 py-3",
382
- children: /* @__PURE__ */ jsxDEV2("span", {
383
- className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${STATUS_COLORS[sync.status] ?? ""}`,
384
- children: sync.status
385
- }, undefined, false, undefined, this)
386
- }, undefined, false, undefined, this),
387
- /* @__PURE__ */ jsxDEV2("td", {
388
- className: "px-4 py-3 text-muted-foreground text-sm",
389
- children: sync.recordsSynced.toLocaleString()
390
- }, undefined, false, undefined, this)
391
- ]
392
- }, sync.id, true, undefined, this)),
393
- syncConfigs.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
394
- children: /* @__PURE__ */ jsxDEV2("td", {
395
- colSpan: 4,
396
- className: "px-4 py-8 text-center text-muted-foreground",
397
- children: "No sync configurations found"
398
- }, undefined, false, undefined, this)
399
- }, undefined, false, undefined, this)
400
- ]
401
- }, undefined, true, undefined, this)
402
- ]
403
- }, undefined, true, undefined, this)
876
+ activeTab === "syncs" && /* @__PURE__ */ jsxDEV6(SyncConfigsTable, {
877
+ syncConfigs
404
878
  }, undefined, false, undefined, this)
405
879
  ]
406
880
  }, undefined, true, undefined, this)