@contractspec/example.analytics-dashboard 3.8.0 → 3.9.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 CHANGED
@@ -9,7 +9,8 @@ Website: https://contractspec.io
9
9
  - Dashboard feature with presentation, schema, enum, and test-spec.
10
10
  - Query engine with typed operations and handlers.
11
11
  - PostHog datasource adapter.
12
- - React UI with hooks, renderers, and markdown output.
12
+ - React UI with hooks, renderers, markdown output, and a shared ContractSpec `DataTable` for saved queries.
13
+ - Client-mode table capabilities including sorting, pagination, column visibility, column resizing, pinning, and expandable row details.
13
14
  - Event definitions for analytics tracking.
14
15
  - Seeders for demo data.
15
16
 
@@ -18,6 +19,7 @@ Website: https://contractspec.io
18
19
  From `packages/examples/analytics-dashboard`:
19
20
  - `bun run dev`
20
21
  - `bun run build`
22
+ - `bun run test`
21
23
  - `bun run typecheck`
22
24
 
23
25
  ## Usage
@@ -26,7 +26,7 @@ var analyticsDashboardDocBlocks = [
26
26
 
27
27
  ## UI / Presentations
28
28
 
29
- - Dashboard list, dashboard view, query builder, widget gallery.
29
+ - Dashboard list, dashboard view, query builder, widget gallery, and a shared ContractSpec table for saved queries.
30
30
  - Registered under \`analytics-dashboard\` template in Template Registry.
31
31
 
32
32
  ## Notes
@@ -34,6 +34,7 @@ var analyticsDashboardDocBlocks = [
34
34
  - Enforce org scoping for multi-tenant isolation.
35
35
  - Use Feature Flags for beta widgets; Metering to track query volume.
36
36
  - PostHog datasource can back query execution via HogQL for dashboard widgets.
37
+ - The saved-queries table demonstrates client-mode sorting, pagination, visibility, resizing, pinning, and row expansion on the shared table stack.
37
38
  `
38
39
  },
39
40
  {
@@ -26,7 +26,7 @@ var analyticsDashboardDocBlocks = [
26
26
 
27
27
  ## UI / Presentations
28
28
 
29
- - Dashboard list, dashboard view, query builder, widget gallery.
29
+ - Dashboard list, dashboard view, query builder, widget gallery, and a shared ContractSpec table for saved queries.
30
30
  - Registered under \`analytics-dashboard\` template in Template Registry.
31
31
 
32
32
  ## Notes
@@ -34,6 +34,7 @@ var analyticsDashboardDocBlocks = [
34
34
  - Enforce org scoping for multi-tenant isolation.
35
35
  - Use Feature Flags for beta widgets; Metering to track query volume.
36
36
  - PostHog datasource can back query execution via HogQL for dashboard widgets.
37
+ - The saved-queries table demonstrates client-mode sorting, pagination, visibility, resizing, pinning, and row expansion on the shared table stack.
37
38
  `
38
39
  },
39
40
  {
@@ -1683,99 +1683,188 @@ function gridSpanClass(gridWidth) {
1683
1683
  }
1684
1684
 
1685
1685
  // src/ui/AnalyticsQueriesTable.tsx
1686
+ import { DataTable } from "@contractspec/lib.design-system";
1687
+ import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
1688
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
1689
+ import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
1690
+ import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
1686
1691
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
1687
1692
  "use client";
1688
1693
  var QUERY_TYPE_COLORS = {
1689
- SQL: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
1690
- METRIC: "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400",
1691
- AGGREGATION: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400",
1692
- CUSTOM: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400"
1694
+ SQL: "default",
1695
+ METRIC: "secondary",
1696
+ AGGREGATION: "secondary",
1697
+ CUSTOM: "outline"
1693
1698
  };
1699
+ function formatJson(value) {
1700
+ return JSON.stringify(value, null, 2);
1701
+ }
1694
1702
  function AnalyticsQueriesTable({ queries }) {
1695
- return /* @__PURE__ */ jsxDEV2("div", {
1696
- className: "rounded-lg border border-border",
1697
- children: /* @__PURE__ */ jsxDEV2("table", {
1698
- className: "w-full",
1699
- children: [
1700
- /* @__PURE__ */ jsxDEV2("thead", {
1701
- className: "border-border border-b bg-muted/30",
1702
- children: /* @__PURE__ */ jsxDEV2("tr", {
1703
- children: [
1704
- /* @__PURE__ */ jsxDEV2("th", {
1705
- className: "px-4 py-3 text-left font-medium text-sm",
1706
- children: "Query"
1707
- }, undefined, false, undefined, this),
1708
- /* @__PURE__ */ jsxDEV2("th", {
1709
- className: "px-4 py-3 text-left font-medium text-sm",
1710
- children: "Type"
1711
- }, undefined, false, undefined, this),
1712
- /* @__PURE__ */ jsxDEV2("th", {
1713
- className: "px-4 py-3 text-left font-medium text-sm",
1714
- children: "Cache TTL"
1715
- }, undefined, false, undefined, this),
1716
- /* @__PURE__ */ jsxDEV2("th", {
1717
- className: "px-4 py-3 text-left font-medium text-sm",
1718
- children: "Shared"
1719
- }, undefined, false, undefined, this)
1720
- ]
1721
- }, undefined, true, undefined, this)
1722
- }, undefined, false, undefined, this),
1723
- /* @__PURE__ */ jsxDEV2("tbody", {
1724
- className: "divide-y divide-border",
1703
+ const controller = useContractTable({
1704
+ data: queries,
1705
+ columns: [
1706
+ {
1707
+ id: "query",
1708
+ header: "Query",
1709
+ label: "Query",
1710
+ accessor: (query) => query.name,
1711
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV2(VStack, {
1712
+ gap: "xs",
1725
1713
  children: [
1726
- queries.map((query) => /* @__PURE__ */ jsxDEV2("tr", {
1727
- className: "hover:bg-muted/50",
1714
+ /* @__PURE__ */ jsxDEV2(Text, {
1715
+ className: "font-medium text-sm",
1716
+ children: item.name
1717
+ }, undefined, false, undefined, this),
1718
+ /* @__PURE__ */ jsxDEV2(Text, {
1719
+ className: "text-muted-foreground text-xs",
1728
1720
  children: [
1729
- /* @__PURE__ */ jsxDEV2("td", {
1730
- className: "px-4 py-3",
1731
- children: [
1732
- /* @__PURE__ */ jsxDEV2("div", {
1733
- className: "font-medium",
1734
- children: query.name
1735
- }, undefined, false, undefined, this),
1736
- /* @__PURE__ */ jsxDEV2("div", {
1737
- className: "text-muted-foreground text-sm",
1738
- children: query.description
1739
- }, undefined, false, undefined, this)
1740
- ]
1741
- }, undefined, true, undefined, this),
1742
- /* @__PURE__ */ jsxDEV2("td", {
1743
- className: "px-4 py-3",
1744
- children: /* @__PURE__ */ jsxDEV2("span", {
1745
- className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${QUERY_TYPE_COLORS[query.type] ?? ""}`,
1746
- children: query.type
1747
- }, undefined, false, undefined, this)
1748
- }, undefined, false, undefined, this),
1749
- /* @__PURE__ */ jsxDEV2("td", {
1750
- className: "px-4 py-3 text-muted-foreground text-sm",
1751
- children: [
1752
- query.cacheTtlSeconds,
1753
- "s"
1754
- ]
1755
- }, undefined, true, undefined, this),
1756
- /* @__PURE__ */ jsxDEV2("td", {
1757
- className: "px-4 py-3",
1758
- children: query.isShared ? /* @__PURE__ */ jsxDEV2("span", {
1759
- className: "text-green-600 dark:text-green-400",
1760
- children: "✓"
1761
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV2("span", {
1762
- className: "text-muted-foreground",
1763
- children: "—"
1764
- }, undefined, false, undefined, this)
1765
- }, undefined, false, undefined, this)
1721
+ "Updated ",
1722
+ item.updatedAt.toLocaleDateString()
1766
1723
  ]
1767
- }, query.id, true, undefined, this)),
1768
- queries.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
1769
- children: /* @__PURE__ */ jsxDEV2("td", {
1770
- colSpan: 4,
1771
- className: "px-4 py-8 text-center text-muted-foreground",
1772
- children: "No queries saved"
1773
- }, undefined, false, undefined, this)
1724
+ }, undefined, true, undefined, this)
1725
+ ]
1726
+ }, undefined, true, undefined, this),
1727
+ size: 240,
1728
+ minSize: 180,
1729
+ canSort: true,
1730
+ canPin: true,
1731
+ canResize: true
1732
+ },
1733
+ {
1734
+ id: "description",
1735
+ header: "Description",
1736
+ label: "Description",
1737
+ accessor: (query) => query.description ?? "",
1738
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV2(Text, {
1739
+ className: "line-clamp-2 text-muted-foreground text-sm",
1740
+ children: String(value || "No description")
1741
+ }, undefined, false, undefined, this),
1742
+ size: 300,
1743
+ minSize: 220,
1744
+ canSort: false,
1745
+ canHide: true,
1746
+ canResize: true
1747
+ },
1748
+ {
1749
+ id: "type",
1750
+ header: "Type",
1751
+ label: "Type",
1752
+ accessorKey: "type",
1753
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV2(Badge, {
1754
+ variant: QUERY_TYPE_COLORS[String(value)],
1755
+ children: String(value)
1756
+ }, undefined, false, undefined, this),
1757
+ size: 150,
1758
+ canSort: true,
1759
+ canHide: true,
1760
+ canResize: true
1761
+ },
1762
+ {
1763
+ id: "cacheTtlSeconds",
1764
+ header: "Cache TTL",
1765
+ label: "Cache TTL",
1766
+ accessorKey: "cacheTtlSeconds",
1767
+ cell: ({ value }) => `${String(value)}s`,
1768
+ align: "right",
1769
+ size: 140,
1770
+ canSort: true,
1771
+ canResize: true
1772
+ },
1773
+ {
1774
+ id: "isShared",
1775
+ header: "Shared",
1776
+ label: "Shared",
1777
+ accessorKey: "isShared",
1778
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV2(Badge, {
1779
+ variant: value ? "default" : "outline",
1780
+ children: value ? "Shared" : "Private"
1781
+ }, undefined, false, undefined, this),
1782
+ size: 140,
1783
+ canSort: true,
1784
+ canHide: true,
1785
+ canResize: true
1786
+ }
1787
+ ],
1788
+ initialState: {
1789
+ pagination: { pageIndex: 0, pageSize: 3 },
1790
+ columnVisibility: { description: false },
1791
+ columnPinning: { left: ["query"], right: [] }
1792
+ },
1793
+ renderExpandedContent: (query) => /* @__PURE__ */ jsxDEV2(VStack, {
1794
+ gap: "sm",
1795
+ className: "py-2",
1796
+ children: [
1797
+ /* @__PURE__ */ jsxDEV2(VStack, {
1798
+ gap: "xs",
1799
+ children: [
1800
+ /* @__PURE__ */ jsxDEV2(Text, {
1801
+ className: "font-medium text-sm",
1802
+ children: "Description"
1803
+ }, undefined, false, undefined, this),
1804
+ /* @__PURE__ */ jsxDEV2(Text, {
1805
+ className: "text-muted-foreground text-sm",
1806
+ children: query.description ?? "No description"
1807
+ }, undefined, false, undefined, this)
1808
+ ]
1809
+ }, undefined, true, undefined, this),
1810
+ query.sql ? /* @__PURE__ */ jsxDEV2(VStack, {
1811
+ gap: "xs",
1812
+ children: [
1813
+ /* @__PURE__ */ jsxDEV2(Text, {
1814
+ className: "font-medium text-sm",
1815
+ children: "SQL"
1816
+ }, undefined, false, undefined, this),
1817
+ /* @__PURE__ */ jsxDEV2("pre", {
1818
+ className: "overflow-auto rounded-md bg-muted/40 p-3 text-xs",
1819
+ children: query.sql
1774
1820
  }, undefined, false, undefined, this)
1775
1821
  ]
1822
+ }, undefined, true, undefined, this) : null,
1823
+ /* @__PURE__ */ jsxDEV2(VStack, {
1824
+ gap: "xs",
1825
+ children: [
1826
+ /* @__PURE__ */ jsxDEV2(Text, {
1827
+ className: "font-medium text-sm",
1828
+ children: "Definition"
1829
+ }, undefined, false, undefined, this),
1830
+ /* @__PURE__ */ jsxDEV2("pre", {
1831
+ className: "overflow-auto rounded-md bg-muted/40 p-3 text-xs",
1832
+ children: formatJson(query.definition)
1833
+ }, undefined, false, undefined, this)
1834
+ ]
1835
+ }, undefined, true, undefined, this)
1836
+ ]
1837
+ }, undefined, true, undefined, this),
1838
+ getCanExpand: () => true
1839
+ });
1840
+ return /* @__PURE__ */ jsxDEV2(DataTable, {
1841
+ controller,
1842
+ title: "Saved Queries",
1843
+ description: "Client-mode table using the shared ContractSpec controller and renderer.",
1844
+ toolbar: /* @__PURE__ */ jsxDEV2(HStack, {
1845
+ gap: "sm",
1846
+ className: "flex-wrap",
1847
+ children: [
1848
+ /* @__PURE__ */ jsxDEV2(Text, {
1849
+ className: "text-muted-foreground text-sm",
1850
+ children: [
1851
+ queries.length,
1852
+ " queries"
1853
+ ]
1854
+ }, undefined, true, undefined, this),
1855
+ /* @__PURE__ */ jsxDEV2(Text, {
1856
+ className: "text-muted-foreground text-sm",
1857
+ children: [
1858
+ queries.filter((query) => query.isShared).length,
1859
+ " shared"
1860
+ ]
1776
1861
  }, undefined, true, undefined, this)
1777
1862
  ]
1778
- }, undefined, true, undefined, this)
1863
+ }, undefined, true, undefined, this),
1864
+ emptyState: /* @__PURE__ */ jsxDEV2("div", {
1865
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
1866
+ children: "No queries saved"
1867
+ }, undefined, false, undefined, this)
1779
1868
  }, undefined, false, undefined, this);
1780
1869
  }
1781
1870
 
@@ -682,99 +682,188 @@ function gridSpanClass(gridWidth) {
682
682
  }
683
683
 
684
684
  // src/ui/AnalyticsQueriesTable.tsx
685
+ import { DataTable } from "@contractspec/lib.design-system";
686
+ import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
687
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
688
+ import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
689
+ import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
685
690
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
686
691
  "use client";
687
692
  var QUERY_TYPE_COLORS = {
688
- SQL: "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400",
689
- METRIC: "bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400",
690
- AGGREGATION: "bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-400",
691
- CUSTOM: "bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400"
693
+ SQL: "default",
694
+ METRIC: "secondary",
695
+ AGGREGATION: "secondary",
696
+ CUSTOM: "outline"
692
697
  };
698
+ function formatJson(value) {
699
+ return JSON.stringify(value, null, 2);
700
+ }
693
701
  function AnalyticsQueriesTable({ queries }) {
694
- return /* @__PURE__ */ jsxDEV2("div", {
695
- className: "rounded-lg border border-border",
696
- children: /* @__PURE__ */ jsxDEV2("table", {
697
- className: "w-full",
698
- children: [
699
- /* @__PURE__ */ jsxDEV2("thead", {
700
- className: "border-border border-b bg-muted/30",
701
- children: /* @__PURE__ */ jsxDEV2("tr", {
702
- children: [
703
- /* @__PURE__ */ jsxDEV2("th", {
704
- className: "px-4 py-3 text-left font-medium text-sm",
705
- children: "Query"
706
- }, undefined, false, undefined, this),
707
- /* @__PURE__ */ jsxDEV2("th", {
708
- className: "px-4 py-3 text-left font-medium text-sm",
709
- children: "Type"
710
- }, undefined, false, undefined, this),
711
- /* @__PURE__ */ jsxDEV2("th", {
712
- className: "px-4 py-3 text-left font-medium text-sm",
713
- children: "Cache TTL"
714
- }, undefined, false, undefined, this),
715
- /* @__PURE__ */ jsxDEV2("th", {
716
- className: "px-4 py-3 text-left font-medium text-sm",
717
- children: "Shared"
718
- }, undefined, false, undefined, this)
719
- ]
720
- }, undefined, true, undefined, this)
721
- }, undefined, false, undefined, this),
722
- /* @__PURE__ */ jsxDEV2("tbody", {
723
- className: "divide-y divide-border",
702
+ const controller = useContractTable({
703
+ data: queries,
704
+ columns: [
705
+ {
706
+ id: "query",
707
+ header: "Query",
708
+ label: "Query",
709
+ accessor: (query) => query.name,
710
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV2(VStack, {
711
+ gap: "xs",
724
712
  children: [
725
- queries.map((query) => /* @__PURE__ */ jsxDEV2("tr", {
726
- className: "hover:bg-muted/50",
713
+ /* @__PURE__ */ jsxDEV2(Text, {
714
+ className: "font-medium text-sm",
715
+ children: item.name
716
+ }, undefined, false, undefined, this),
717
+ /* @__PURE__ */ jsxDEV2(Text, {
718
+ className: "text-muted-foreground text-xs",
727
719
  children: [
728
- /* @__PURE__ */ jsxDEV2("td", {
729
- className: "px-4 py-3",
730
- children: [
731
- /* @__PURE__ */ jsxDEV2("div", {
732
- className: "font-medium",
733
- children: query.name
734
- }, undefined, false, undefined, this),
735
- /* @__PURE__ */ jsxDEV2("div", {
736
- className: "text-muted-foreground text-sm",
737
- children: query.description
738
- }, undefined, false, undefined, this)
739
- ]
740
- }, undefined, true, undefined, this),
741
- /* @__PURE__ */ jsxDEV2("td", {
742
- className: "px-4 py-3",
743
- children: /* @__PURE__ */ jsxDEV2("span", {
744
- className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${QUERY_TYPE_COLORS[query.type] ?? ""}`,
745
- children: query.type
746
- }, undefined, false, undefined, this)
747
- }, undefined, false, undefined, this),
748
- /* @__PURE__ */ jsxDEV2("td", {
749
- className: "px-4 py-3 text-muted-foreground text-sm",
750
- children: [
751
- query.cacheTtlSeconds,
752
- "s"
753
- ]
754
- }, undefined, true, undefined, this),
755
- /* @__PURE__ */ jsxDEV2("td", {
756
- className: "px-4 py-3",
757
- children: query.isShared ? /* @__PURE__ */ jsxDEV2("span", {
758
- className: "text-green-600 dark:text-green-400",
759
- children: "✓"
760
- }, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV2("span", {
761
- className: "text-muted-foreground",
762
- children: "—"
763
- }, undefined, false, undefined, this)
764
- }, undefined, false, undefined, this)
720
+ "Updated ",
721
+ item.updatedAt.toLocaleDateString()
765
722
  ]
766
- }, query.id, true, undefined, this)),
767
- queries.length === 0 && /* @__PURE__ */ jsxDEV2("tr", {
768
- children: /* @__PURE__ */ jsxDEV2("td", {
769
- colSpan: 4,
770
- className: "px-4 py-8 text-center text-muted-foreground",
771
- children: "No queries saved"
772
- }, undefined, false, undefined, this)
723
+ }, undefined, true, undefined, this)
724
+ ]
725
+ }, undefined, true, undefined, this),
726
+ size: 240,
727
+ minSize: 180,
728
+ canSort: true,
729
+ canPin: true,
730
+ canResize: true
731
+ },
732
+ {
733
+ id: "description",
734
+ header: "Description",
735
+ label: "Description",
736
+ accessor: (query) => query.description ?? "",
737
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV2(Text, {
738
+ className: "line-clamp-2 text-muted-foreground text-sm",
739
+ children: String(value || "No description")
740
+ }, undefined, false, undefined, this),
741
+ size: 300,
742
+ minSize: 220,
743
+ canSort: false,
744
+ canHide: true,
745
+ canResize: true
746
+ },
747
+ {
748
+ id: "type",
749
+ header: "Type",
750
+ label: "Type",
751
+ accessorKey: "type",
752
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV2(Badge, {
753
+ variant: QUERY_TYPE_COLORS[String(value)],
754
+ children: String(value)
755
+ }, undefined, false, undefined, this),
756
+ size: 150,
757
+ canSort: true,
758
+ canHide: true,
759
+ canResize: true
760
+ },
761
+ {
762
+ id: "cacheTtlSeconds",
763
+ header: "Cache TTL",
764
+ label: "Cache TTL",
765
+ accessorKey: "cacheTtlSeconds",
766
+ cell: ({ value }) => `${String(value)}s`,
767
+ align: "right",
768
+ size: 140,
769
+ canSort: true,
770
+ canResize: true
771
+ },
772
+ {
773
+ id: "isShared",
774
+ header: "Shared",
775
+ label: "Shared",
776
+ accessorKey: "isShared",
777
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV2(Badge, {
778
+ variant: value ? "default" : "outline",
779
+ children: value ? "Shared" : "Private"
780
+ }, undefined, false, undefined, this),
781
+ size: 140,
782
+ canSort: true,
783
+ canHide: true,
784
+ canResize: true
785
+ }
786
+ ],
787
+ initialState: {
788
+ pagination: { pageIndex: 0, pageSize: 3 },
789
+ columnVisibility: { description: false },
790
+ columnPinning: { left: ["query"], right: [] }
791
+ },
792
+ renderExpandedContent: (query) => /* @__PURE__ */ jsxDEV2(VStack, {
793
+ gap: "sm",
794
+ className: "py-2",
795
+ children: [
796
+ /* @__PURE__ */ jsxDEV2(VStack, {
797
+ gap: "xs",
798
+ children: [
799
+ /* @__PURE__ */ jsxDEV2(Text, {
800
+ className: "font-medium text-sm",
801
+ children: "Description"
802
+ }, undefined, false, undefined, this),
803
+ /* @__PURE__ */ jsxDEV2(Text, {
804
+ className: "text-muted-foreground text-sm",
805
+ children: query.description ?? "No description"
806
+ }, undefined, false, undefined, this)
807
+ ]
808
+ }, undefined, true, undefined, this),
809
+ query.sql ? /* @__PURE__ */ jsxDEV2(VStack, {
810
+ gap: "xs",
811
+ children: [
812
+ /* @__PURE__ */ jsxDEV2(Text, {
813
+ className: "font-medium text-sm",
814
+ children: "SQL"
815
+ }, undefined, false, undefined, this),
816
+ /* @__PURE__ */ jsxDEV2("pre", {
817
+ className: "overflow-auto rounded-md bg-muted/40 p-3 text-xs",
818
+ children: query.sql
773
819
  }, undefined, false, undefined, this)
774
820
  ]
821
+ }, undefined, true, undefined, this) : null,
822
+ /* @__PURE__ */ jsxDEV2(VStack, {
823
+ gap: "xs",
824
+ children: [
825
+ /* @__PURE__ */ jsxDEV2(Text, {
826
+ className: "font-medium text-sm",
827
+ children: "Definition"
828
+ }, undefined, false, undefined, this),
829
+ /* @__PURE__ */ jsxDEV2("pre", {
830
+ className: "overflow-auto rounded-md bg-muted/40 p-3 text-xs",
831
+ children: formatJson(query.definition)
832
+ }, undefined, false, undefined, this)
833
+ ]
834
+ }, undefined, true, undefined, this)
835
+ ]
836
+ }, undefined, true, undefined, this),
837
+ getCanExpand: () => true
838
+ });
839
+ return /* @__PURE__ */ jsxDEV2(DataTable, {
840
+ controller,
841
+ title: "Saved Queries",
842
+ description: "Client-mode table using the shared ContractSpec controller and renderer.",
843
+ toolbar: /* @__PURE__ */ jsxDEV2(HStack, {
844
+ gap: "sm",
845
+ className: "flex-wrap",
846
+ children: [
847
+ /* @__PURE__ */ jsxDEV2(Text, {
848
+ className: "text-muted-foreground text-sm",
849
+ children: [
850
+ queries.length,
851
+ " queries"
852
+ ]
853
+ }, undefined, true, undefined, this),
854
+ /* @__PURE__ */ jsxDEV2(Text, {
855
+ className: "text-muted-foreground text-sm",
856
+ children: [
857
+ queries.filter((query) => query.isShared).length,
858
+ " shared"
859
+ ]
775
860
  }, undefined, true, undefined, this)
776
861
  ]
777
- }, undefined, true, undefined, this)
862
+ }, undefined, true, undefined, this),
863
+ emptyState: /* @__PURE__ */ jsxDEV2("div", {
864
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
865
+ children: "No queries saved"
866
+ }, undefined, false, undefined, this)
778
867
  }, undefined, false, undefined, this);
779
868
  }
780
869