@contractspec/example.saas-boilerplate 3.7.7 → 3.8.4

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 (52) hide show
  1. package/.turbo/turbo-build.log +36 -24
  2. package/CHANGELOG.md +72 -0
  3. package/README.md +1 -0
  4. package/dist/browser/index.js +371 -92
  5. package/dist/browser/saas-boilerplate.feature.js +208 -0
  6. package/dist/browser/ui/SaasDashboard.js +311 -60
  7. package/dist/browser/ui/SaasDashboard.visualizations.js +249 -0
  8. package/dist/browser/ui/index.js +362 -92
  9. package/dist/browser/ui/renderers/index.js +229 -3
  10. package/dist/browser/ui/renderers/project-list.markdown.js +229 -3
  11. package/dist/browser/visualizations/catalog.js +155 -0
  12. package/dist/browser/visualizations/index.js +217 -0
  13. package/dist/browser/visualizations/selectors.js +210 -0
  14. package/dist/index.d.ts +1 -0
  15. package/dist/index.js +371 -92
  16. package/dist/node/index.js +371 -92
  17. package/dist/node/saas-boilerplate.feature.js +208 -0
  18. package/dist/node/ui/SaasDashboard.js +311 -60
  19. package/dist/node/ui/SaasDashboard.visualizations.js +249 -0
  20. package/dist/node/ui/index.js +362 -92
  21. package/dist/node/ui/renderers/index.js +229 -3
  22. package/dist/node/ui/renderers/project-list.markdown.js +229 -3
  23. package/dist/node/visualizations/catalog.js +155 -0
  24. package/dist/node/visualizations/index.js +217 -0
  25. package/dist/node/visualizations/selectors.js +210 -0
  26. package/dist/saas-boilerplate.feature.js +208 -0
  27. package/dist/ui/SaasDashboard.js +311 -60
  28. package/dist/ui/SaasDashboard.visualizations.d.ts +5 -0
  29. package/dist/ui/SaasDashboard.visualizations.js +250 -0
  30. package/dist/ui/index.js +362 -92
  31. package/dist/ui/renderers/index.js +229 -3
  32. package/dist/ui/renderers/project-list.markdown.d.ts +1 -1
  33. package/dist/ui/renderers/project-list.markdown.js +229 -3
  34. package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
  35. package/dist/visualizations/catalog.d.ts +11 -0
  36. package/dist/visualizations/catalog.js +156 -0
  37. package/dist/visualizations/index.d.ts +2 -0
  38. package/dist/visualizations/index.js +218 -0
  39. package/dist/visualizations/selectors.d.ts +8 -0
  40. package/dist/visualizations/selectors.js +211 -0
  41. package/dist/visualizations/selectors.test.d.ts +1 -0
  42. package/package.json +70 -13
  43. package/src/index.ts +1 -0
  44. package/src/saas-boilerplate.feature.ts +3 -0
  45. package/src/ui/SaasDashboard.tsx +8 -0
  46. package/src/ui/SaasDashboard.visualizations.tsx +41 -0
  47. package/src/ui/renderers/project-list.markdown.ts +39 -15
  48. package/src/ui/renderers/project-list.renderer.tsx +1 -1
  49. package/src/visualizations/catalog.ts +153 -0
  50. package/src/visualizations/index.ts +2 -0
  51. package/src/visualizations/selectors.test.ts +25 -0
  52. package/src/visualizations/selectors.ts +85 -0
package/dist/index.js CHANGED
@@ -1633,6 +1633,213 @@ var ProjectDetailPresentation = definePresentation3({
1633
1633
  flags: ["saas.projects.enabled"]
1634
1634
  }
1635
1635
  });
1636
+ // src/visualizations/catalog.ts
1637
+ import {
1638
+ defineVisualization,
1639
+ VisualizationRegistry
1640
+ } from "@contractspec/lib.contracts-spec/visualizations";
1641
+ var PROJECT_LIST_REF = {
1642
+ key: "saas.project.list",
1643
+ version: "1.0.0"
1644
+ };
1645
+ var META = {
1646
+ version: "1.0.0",
1647
+ domain: "saas",
1648
+ stability: "experimental",
1649
+ owners: ["@example.saas-boilerplate"],
1650
+ tags: ["saas", "visualization", "projects"]
1651
+ };
1652
+ var SaasProjectUsageVisualization = defineVisualization({
1653
+ meta: {
1654
+ ...META,
1655
+ key: "saas-boilerplate.visualization.project-usage",
1656
+ title: "Project Capacity",
1657
+ description: "Current project count against the current plan limit.",
1658
+ goal: "Show usage against the active plan allowance.",
1659
+ context: "SaaS account overview."
1660
+ },
1661
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
1662
+ visualization: {
1663
+ kind: "metric",
1664
+ measure: "totalProjects",
1665
+ comparisonMeasure: "projectLimit",
1666
+ measures: [
1667
+ {
1668
+ key: "totalProjects",
1669
+ label: "Projects",
1670
+ dataPath: "totalProjects",
1671
+ format: "number"
1672
+ },
1673
+ {
1674
+ key: "projectLimit",
1675
+ label: "Plan Limit",
1676
+ dataPath: "projectLimit",
1677
+ format: "number"
1678
+ }
1679
+ ],
1680
+ table: { caption: "Current project count and plan limit." }
1681
+ }
1682
+ });
1683
+ var SaasProjectStatusVisualization = defineVisualization({
1684
+ meta: {
1685
+ ...META,
1686
+ key: "saas-boilerplate.visualization.project-status",
1687
+ title: "Project Status",
1688
+ description: "Distribution of project states.",
1689
+ goal: "Show the mix of active, draft, and archived projects.",
1690
+ context: "Project portfolio overview."
1691
+ },
1692
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
1693
+ visualization: {
1694
+ kind: "pie",
1695
+ nameDimension: "status",
1696
+ valueMeasure: "projects",
1697
+ dimensions: [
1698
+ { key: "status", label: "Status", dataPath: "status", type: "category" }
1699
+ ],
1700
+ measures: [
1701
+ {
1702
+ key: "projects",
1703
+ label: "Projects",
1704
+ dataPath: "projects",
1705
+ format: "number"
1706
+ }
1707
+ ],
1708
+ table: { caption: "Project counts by status." }
1709
+ }
1710
+ });
1711
+ var SaasProjectTierVisualization = defineVisualization({
1712
+ meta: {
1713
+ ...META,
1714
+ key: "saas-boilerplate.visualization.project-tiers",
1715
+ title: "Tier Comparison",
1716
+ description: "Distribution of projects across tiers.",
1717
+ goal: "Compare how the current portfolio is distributed by tier.",
1718
+ context: "Plan and packaging overview."
1719
+ },
1720
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
1721
+ visualization: {
1722
+ kind: "cartesian",
1723
+ variant: "bar",
1724
+ xDimension: "tier",
1725
+ yMeasures: ["projects"],
1726
+ dimensions: [
1727
+ { key: "tier", label: "Tier", dataPath: "tier", type: "category" }
1728
+ ],
1729
+ measures: [
1730
+ {
1731
+ key: "projects",
1732
+ label: "Projects",
1733
+ dataPath: "projects",
1734
+ format: "number",
1735
+ color: "#1d4ed8"
1736
+ }
1737
+ ],
1738
+ table: { caption: "Project counts by tier." }
1739
+ }
1740
+ });
1741
+ var SaasProjectActivityVisualization = defineVisualization({
1742
+ meta: {
1743
+ ...META,
1744
+ key: "saas-boilerplate.visualization.project-activity",
1745
+ title: "Recent Project Activity",
1746
+ description: "Daily project creation activity.",
1747
+ goal: "Show recent project activity over time.",
1748
+ context: "Project portfolio trend view."
1749
+ },
1750
+ source: { primary: PROJECT_LIST_REF, resultPath: "data" },
1751
+ visualization: {
1752
+ kind: "cartesian",
1753
+ variant: "line",
1754
+ xDimension: "day",
1755
+ yMeasures: ["projects"],
1756
+ dimensions: [{ key: "day", label: "Day", dataPath: "day", type: "time" }],
1757
+ measures: [
1758
+ {
1759
+ key: "projects",
1760
+ label: "Projects",
1761
+ dataPath: "projects",
1762
+ format: "number",
1763
+ color: "#0f766e"
1764
+ }
1765
+ ],
1766
+ table: { caption: "Daily project creation counts." }
1767
+ }
1768
+ });
1769
+ var SaasVisualizationSpecs = [
1770
+ SaasProjectUsageVisualization,
1771
+ SaasProjectStatusVisualization,
1772
+ SaasProjectTierVisualization,
1773
+ SaasProjectActivityVisualization
1774
+ ];
1775
+ var SaasVisualizationRegistry = new VisualizationRegistry([
1776
+ ...SaasVisualizationSpecs
1777
+ ]);
1778
+ var SaasVisualizationRefs = SaasVisualizationSpecs.map((spec) => ({
1779
+ key: spec.meta.key,
1780
+ version: spec.meta.version
1781
+ }));
1782
+
1783
+ // src/visualizations/selectors.ts
1784
+ function toDayKey(value) {
1785
+ const date = value instanceof Date ? value : new Date(value);
1786
+ return date.toISOString().slice(0, 10);
1787
+ }
1788
+ function createSaasVisualizationItems(projects, projectLimit = 10) {
1789
+ const statusCounts = new Map;
1790
+ const tierCounts = new Map;
1791
+ const activityCounts = new Map;
1792
+ for (const project of projects) {
1793
+ statusCounts.set(project.status, (statusCounts.get(project.status) ?? 0) + 1);
1794
+ tierCounts.set(project.tier, (tierCounts.get(project.tier) ?? 0) + 1);
1795
+ const day = toDayKey(project.createdAt);
1796
+ activityCounts.set(day, (activityCounts.get(day) ?? 0) + 1);
1797
+ }
1798
+ return [
1799
+ {
1800
+ key: "saas-capacity",
1801
+ spec: SaasProjectUsageVisualization,
1802
+ data: { data: [{ totalProjects: projects.length, projectLimit }] },
1803
+ title: "Project Capacity",
1804
+ description: "Current project count compared to the active limit.",
1805
+ height: 220
1806
+ },
1807
+ {
1808
+ key: "saas-status",
1809
+ spec: SaasProjectStatusVisualization,
1810
+ data: {
1811
+ data: Array.from(statusCounts.entries()).map(([status, count]) => ({
1812
+ status,
1813
+ projects: count
1814
+ }))
1815
+ },
1816
+ title: "Project Status",
1817
+ description: "Status mix across the current project portfolio.",
1818
+ height: 260
1819
+ },
1820
+ {
1821
+ key: "saas-tier",
1822
+ spec: SaasProjectTierVisualization,
1823
+ data: {
1824
+ data: Array.from(tierCounts.entries()).map(([tier, count]) => ({
1825
+ tier,
1826
+ projects: count
1827
+ }))
1828
+ },
1829
+ title: "Tier Comparison",
1830
+ description: "How projects are distributed across tiers."
1831
+ },
1832
+ {
1833
+ key: "saas-activity",
1834
+ spec: SaasProjectActivityVisualization,
1835
+ data: {
1836
+ data: Array.from(activityCounts.entries()).sort(([left], [right]) => left.localeCompare(right)).map(([day, count]) => ({ day, projects: count }))
1837
+ },
1838
+ title: "Recent Project Activity",
1839
+ description: "Daily project creation activity."
1840
+ }
1841
+ ];
1842
+ }
1636
1843
  // src/saas-boilerplate.feature.ts
1637
1844
  import { defineFeature } from "@contractspec/lib.contracts-spec";
1638
1845
  var SaasBoilerplateFeature = defineFeature({
@@ -1710,6 +1917,7 @@ var SaasBoilerplateFeature = defineFeature({
1710
1917
  targets: ["react", "markdown"]
1711
1918
  }
1712
1919
  ],
1920
+ visualizations: SaasVisualizationRefs,
1713
1921
  capabilities: {
1714
1922
  requires: [
1715
1923
  { key: "identity", version: "1.0.0" },
@@ -2493,6 +2701,18 @@ var saasOverlays = [
2493
2701
  saasDemoOverlay
2494
2702
  ];
2495
2703
  // src/ui/renderers/project-list.markdown.ts
2704
+ var PROJECT_TIERS = [
2705
+ "FREE",
2706
+ "PRO",
2707
+ "ENTERPRISE"
2708
+ ];
2709
+ function toVisualizationProject(project, index4) {
2710
+ return {
2711
+ status: project.status === "DELETED" ? "ARCHIVED" : project.status,
2712
+ tier: PROJECT_TIERS[index4 % PROJECT_TIERS.length] ?? "FREE",
2713
+ createdAt: project.createdAt
2714
+ };
2715
+ }
2496
2716
  var projectListMarkdownRenderer = {
2497
2717
  target: "markdown",
2498
2718
  render: async (desc, _ctx) => {
@@ -2503,7 +2723,7 @@ var projectListMarkdownRenderer = {
2503
2723
  limit: 20,
2504
2724
  offset: 0
2505
2725
  });
2506
- const items = data.projects ?? data.items ?? [];
2726
+ const items = data.projects ?? [];
2507
2727
  const lines = [
2508
2728
  "# Projects",
2509
2729
  "",
@@ -2540,6 +2760,7 @@ var saasDashboardMarkdownRenderer = {
2540
2760
  const projects = projectsData.projects ?? [];
2541
2761
  const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
2542
2762
  const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
2763
+ const visualizations = createSaasVisualizationItems(projects.map(toVisualizationProject), 10);
2543
2764
  const lines = [
2544
2765
  "# SaaS Dashboard",
2545
2766
  "",
@@ -2554,10 +2775,16 @@ var saasDashboardMarkdownRenderer = {
2554
2775
  `| Archived Projects | ${archivedProjects} |`,
2555
2776
  `| Subscription Plan | ${subscription.planName} |`,
2556
2777
  `| Subscription Status | ${subscription.status} |`,
2557
- "",
2558
- "## Projects",
2559
2778
  ""
2560
2779
  ];
2780
+ lines.push("## Visualization Overview");
2781
+ lines.push("");
2782
+ for (const item of visualizations) {
2783
+ lines.push(`- **${item.title}** via \`${item.spec.meta.key}\``);
2784
+ }
2785
+ lines.push("");
2786
+ lines.push("## Projects");
2787
+ lines.push("");
2561
2788
  if (projects.length === 0) {
2562
2789
  lines.push("_No projects yet._");
2563
2790
  } else {
@@ -2751,6 +2978,46 @@ var projectListReactRenderer = {
2751
2978
  return /* @__PURE__ */ jsxDEV4(SaasProjectList, {}, undefined, false, undefined, this);
2752
2979
  }
2753
2980
  };
2981
+ // src/ui/SaasDashboard.visualizations.tsx
2982
+ import {
2983
+ VisualizationCard,
2984
+ VisualizationGrid
2985
+ } from "@contractspec/lib.design-system";
2986
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
2987
+ "use client";
2988
+ function SaasVisualizationOverview({
2989
+ projects,
2990
+ projectLimit
2991
+ }) {
2992
+ const items = createSaasVisualizationItems(projects, projectLimit);
2993
+ return /* @__PURE__ */ jsxDEV5("section", {
2994
+ className: "space-y-3",
2995
+ children: [
2996
+ /* @__PURE__ */ jsxDEV5("div", {
2997
+ children: [
2998
+ /* @__PURE__ */ jsxDEV5("h3", {
2999
+ className: "font-semibold text-lg",
3000
+ children: "Portfolio Visualizations"
3001
+ }, undefined, false, undefined, this),
3002
+ /* @__PURE__ */ jsxDEV5("p", {
3003
+ className: "text-muted-foreground text-sm",
3004
+ children: "Contract-backed charts for project mix, capacity, and activity."
3005
+ }, undefined, false, undefined, this)
3006
+ ]
3007
+ }, undefined, true, undefined, this),
3008
+ /* @__PURE__ */ jsxDEV5(VisualizationGrid, {
3009
+ children: items.map((item) => /* @__PURE__ */ jsxDEV5(VisualizationCard, {
3010
+ data: item.data,
3011
+ description: item.description,
3012
+ height: item.height,
3013
+ spec: item.spec,
3014
+ title: item.title
3015
+ }, item.key, false, undefined, this))
3016
+ }, undefined, false, undefined, this)
3017
+ ]
3018
+ }, undefined, true, undefined, this);
3019
+ }
3020
+
2754
3021
  // src/ui/SaasDashboard.tsx
2755
3022
  import {
2756
3023
  Button as Button4,
@@ -2763,7 +3030,7 @@ import {
2763
3030
  StatusChip as StatusChip2
2764
3031
  } from "@contractspec/lib.design-system";
2765
3032
  import { useCallback as useCallback3, useState as useState5 } from "react";
2766
- import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
3033
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
2767
3034
  "use client";
2768
3035
  function getStatusTone2(status) {
2769
3036
  switch (status) {
@@ -2798,32 +3065,32 @@ function SaasDashboard() {
2798
3065
  { id: "settings", label: "Settings", icon: "\u2699\uFE0F" }
2799
3066
  ];
2800
3067
  if (loading && !data) {
2801
- return /* @__PURE__ */ jsxDEV5(LoaderBlock2, {
3068
+ return /* @__PURE__ */ jsxDEV6(LoaderBlock2, {
2802
3069
  label: "Loading dashboard..."
2803
3070
  }, undefined, false, undefined, this);
2804
3071
  }
2805
3072
  if (error) {
2806
- return /* @__PURE__ */ jsxDEV5(ErrorState2, {
3073
+ return /* @__PURE__ */ jsxDEV6(ErrorState2, {
2807
3074
  title: "Failed to load dashboard",
2808
3075
  description: error.message,
2809
3076
  onRetry: refetch,
2810
3077
  retryLabel: "Retry"
2811
3078
  }, undefined, false, undefined, this);
2812
3079
  }
2813
- return /* @__PURE__ */ jsxDEV5("div", {
3080
+ return /* @__PURE__ */ jsxDEV6("div", {
2814
3081
  className: "space-y-6",
2815
3082
  children: [
2816
- /* @__PURE__ */ jsxDEV5("div", {
3083
+ /* @__PURE__ */ jsxDEV6("div", {
2817
3084
  className: "flex items-center justify-between",
2818
3085
  children: [
2819
- /* @__PURE__ */ jsxDEV5("h2", {
3086
+ /* @__PURE__ */ jsxDEV6("h2", {
2820
3087
  className: "font-bold text-2xl",
2821
3088
  children: "SaaS Dashboard"
2822
3089
  }, undefined, false, undefined, this),
2823
- activeTab === "projects" && /* @__PURE__ */ jsxDEV5(Button4, {
3090
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV6(Button4, {
2824
3091
  onPress: () => setIsCreateModalOpen(true),
2825
3092
  children: [
2826
- /* @__PURE__ */ jsxDEV5("span", {
3093
+ /* @__PURE__ */ jsxDEV6("span", {
2827
3094
  className: "mr-2",
2828
3095
  children: "+"
2829
3096
  }, undefined, false, undefined, this),
@@ -2832,59 +3099,63 @@ function SaasDashboard() {
2832
3099
  }, undefined, true, undefined, this)
2833
3100
  ]
2834
3101
  }, undefined, true, undefined, this),
2835
- stats && subscription && /* @__PURE__ */ jsxDEV5(StatCardGroup2, {
3102
+ stats && subscription && /* @__PURE__ */ jsxDEV6(StatCardGroup2, {
2836
3103
  children: [
2837
- /* @__PURE__ */ jsxDEV5(StatCard2, {
3104
+ /* @__PURE__ */ jsxDEV6(StatCard2, {
2838
3105
  label: "Projects",
2839
3106
  value: stats.total.toString()
2840
3107
  }, undefined, false, undefined, this),
2841
- /* @__PURE__ */ jsxDEV5(StatCard2, {
3108
+ /* @__PURE__ */ jsxDEV6(StatCard2, {
2842
3109
  label: "Active",
2843
3110
  value: stats.activeCount.toString()
2844
3111
  }, undefined, false, undefined, this),
2845
- /* @__PURE__ */ jsxDEV5(StatCard2, {
3112
+ /* @__PURE__ */ jsxDEV6(StatCard2, {
2846
3113
  label: "Draft",
2847
3114
  value: stats.draftCount.toString()
2848
3115
  }, undefined, false, undefined, this),
2849
- /* @__PURE__ */ jsxDEV5(StatCard2, {
3116
+ /* @__PURE__ */ jsxDEV6(StatCard2, {
2850
3117
  label: "Plan",
2851
3118
  value: subscription.plan,
2852
3119
  hint: subscription.status
2853
3120
  }, undefined, false, undefined, this)
2854
3121
  ]
2855
3122
  }, undefined, true, undefined, this),
2856
- /* @__PURE__ */ jsxDEV5("nav", {
3123
+ data && stats && /* @__PURE__ */ jsxDEV6(SaasVisualizationOverview, {
3124
+ projectLimit: stats.projectLimit,
3125
+ projects: data.items
3126
+ }, undefined, false, undefined, this),
3127
+ /* @__PURE__ */ jsxDEV6("nav", {
2857
3128
  className: "flex gap-1 rounded-lg bg-muted p-1",
2858
3129
  role: "tablist",
2859
- children: tabs.map((tab) => /* @__PURE__ */ jsxDEV5("button", {
3130
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV6("button", {
2860
3131
  type: "button",
2861
3132
  role: "tab",
2862
3133
  "aria-selected": activeTab === tab.id,
2863
3134
  onClick: () => setActiveTab(tab.id),
2864
3135
  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"}`,
2865
3136
  children: [
2866
- /* @__PURE__ */ jsxDEV5("span", {
3137
+ /* @__PURE__ */ jsxDEV6("span", {
2867
3138
  children: tab.icon
2868
3139
  }, undefined, false, undefined, this),
2869
3140
  tab.label
2870
3141
  ]
2871
3142
  }, tab.id, true, undefined, this))
2872
3143
  }, undefined, false, undefined, this),
2873
- /* @__PURE__ */ jsxDEV5("div", {
3144
+ /* @__PURE__ */ jsxDEV6("div", {
2874
3145
  className: "min-h-[400px]",
2875
3146
  role: "tabpanel",
2876
3147
  children: [
2877
- activeTab === "projects" && /* @__PURE__ */ jsxDEV5(ProjectsTab, {
3148
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV6(ProjectsTab, {
2878
3149
  data,
2879
3150
  onProjectClick: handleProjectClick
2880
3151
  }, undefined, false, undefined, this),
2881
- activeTab === "billing" && /* @__PURE__ */ jsxDEV5(BillingTab, {
3152
+ activeTab === "billing" && /* @__PURE__ */ jsxDEV6(BillingTab, {
2882
3153
  subscription
2883
3154
  }, undefined, false, undefined, this),
2884
- activeTab === "settings" && /* @__PURE__ */ jsxDEV5(SettingsTab, {}, undefined, false, undefined, this)
3155
+ activeTab === "settings" && /* @__PURE__ */ jsxDEV6(SettingsTab, {}, undefined, false, undefined, this)
2885
3156
  ]
2886
3157
  }, undefined, true, undefined, this),
2887
- /* @__PURE__ */ jsxDEV5(CreateProjectModal, {
3158
+ /* @__PURE__ */ jsxDEV6(CreateProjectModal, {
2888
3159
  isOpen: isCreateModalOpen,
2889
3160
  onClose: () => setIsCreateModalOpen(false),
2890
3161
  onSubmit: async (input) => {
@@ -2892,7 +3163,7 @@ function SaasDashboard() {
2892
3163
  },
2893
3164
  isLoading: mutations.createState.loading
2894
3165
  }, undefined, false, undefined, this),
2895
- /* @__PURE__ */ jsxDEV5(ProjectActionsModal, {
3166
+ /* @__PURE__ */ jsxDEV6(ProjectActionsModal, {
2896
3167
  isOpen: isProjectActionsOpen,
2897
3168
  project: selectedProject,
2898
3169
  onClose: () => {
@@ -2918,34 +3189,34 @@ function SaasDashboard() {
2918
3189
  }
2919
3190
  function ProjectsTab({ data, onProjectClick }) {
2920
3191
  if (!data?.items.length) {
2921
- return /* @__PURE__ */ jsxDEV5(EmptyState2, {
3192
+ return /* @__PURE__ */ jsxDEV6(EmptyState2, {
2922
3193
  title: "No projects yet",
2923
3194
  description: "Create your first project to get started."
2924
3195
  }, undefined, false, undefined, this);
2925
3196
  }
2926
- return /* @__PURE__ */ jsxDEV5("div", {
3197
+ return /* @__PURE__ */ jsxDEV6("div", {
2927
3198
  className: "space-y-4",
2928
- children: /* @__PURE__ */ jsxDEV5("div", {
3199
+ children: /* @__PURE__ */ jsxDEV6("div", {
2929
3200
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
2930
- children: data.items.map((project) => /* @__PURE__ */ jsxDEV5(EntityCard2, {
3201
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV6(EntityCard2, {
2931
3202
  cardTitle: project.name,
2932
3203
  cardSubtitle: project.tier,
2933
- meta: /* @__PURE__ */ jsxDEV5("p", {
3204
+ meta: /* @__PURE__ */ jsxDEV6("p", {
2934
3205
  className: "text-muted-foreground text-sm",
2935
3206
  children: project.description
2936
3207
  }, undefined, false, undefined, this),
2937
- chips: /* @__PURE__ */ jsxDEV5(StatusChip2, {
3208
+ chips: /* @__PURE__ */ jsxDEV6(StatusChip2, {
2938
3209
  tone: getStatusTone2(project.status),
2939
3210
  label: project.status
2940
3211
  }, undefined, false, undefined, this),
2941
- footer: /* @__PURE__ */ jsxDEV5("div", {
3212
+ footer: /* @__PURE__ */ jsxDEV6("div", {
2942
3213
  className: "flex w-full items-center justify-between",
2943
3214
  children: [
2944
- /* @__PURE__ */ jsxDEV5("span", {
3215
+ /* @__PURE__ */ jsxDEV6("span", {
2945
3216
  className: "text-muted-foreground text-xs",
2946
3217
  children: project.updatedAt.toLocaleDateString()
2947
3218
  }, undefined, false, undefined, this),
2948
- /* @__PURE__ */ jsxDEV5(Button4, {
3219
+ /* @__PURE__ */ jsxDEV6(Button4, {
2949
3220
  variant: "ghost",
2950
3221
  size: "sm",
2951
3222
  onPress: () => onProjectClick?.(project),
@@ -2960,25 +3231,25 @@ function ProjectsTab({ data, onProjectClick }) {
2960
3231
  function BillingTab({ subscription }) {
2961
3232
  if (!subscription)
2962
3233
  return null;
2963
- return /* @__PURE__ */ jsxDEV5("div", {
3234
+ return /* @__PURE__ */ jsxDEV6("div", {
2964
3235
  className: "space-y-6",
2965
3236
  children: [
2966
- /* @__PURE__ */ jsxDEV5("div", {
3237
+ /* @__PURE__ */ jsxDEV6("div", {
2967
3238
  className: "rounded-xl border border-border bg-card p-6",
2968
3239
  children: [
2969
- /* @__PURE__ */ jsxDEV5("div", {
3240
+ /* @__PURE__ */ jsxDEV6("div", {
2970
3241
  className: "flex items-start justify-between",
2971
3242
  children: [
2972
- /* @__PURE__ */ jsxDEV5("div", {
3243
+ /* @__PURE__ */ jsxDEV6("div", {
2973
3244
  children: [
2974
- /* @__PURE__ */ jsxDEV5("h3", {
3245
+ /* @__PURE__ */ jsxDEV6("h3", {
2975
3246
  className: "font-semibold text-lg",
2976
3247
  children: [
2977
3248
  subscription.plan,
2978
3249
  " Plan"
2979
3250
  ]
2980
3251
  }, undefined, true, undefined, this),
2981
- /* @__PURE__ */ jsxDEV5("p", {
3252
+ /* @__PURE__ */ jsxDEV6("p", {
2982
3253
  className: "text-muted-foreground text-sm",
2983
3254
  children: [
2984
3255
  "Current period:",
@@ -2989,7 +3260,7 @@ function BillingTab({ subscription }) {
2989
3260
  subscription.currentPeriodEnd.toLocaleDateString()
2990
3261
  ]
2991
3262
  }, undefined, true, undefined, this),
2992
- /* @__PURE__ */ jsxDEV5("p", {
3263
+ /* @__PURE__ */ jsxDEV6("p", {
2993
3264
  className: "text-muted-foreground text-sm",
2994
3265
  children: [
2995
3266
  "Billing cycle: ",
@@ -2998,21 +3269,21 @@ function BillingTab({ subscription }) {
2998
3269
  }, undefined, true, undefined, this)
2999
3270
  ]
3000
3271
  }, undefined, true, undefined, this),
3001
- /* @__PURE__ */ jsxDEV5(StatusChip2, {
3272
+ /* @__PURE__ */ jsxDEV6(StatusChip2, {
3002
3273
  tone: "success",
3003
3274
  label: subscription.status
3004
3275
  }, undefined, false, undefined, this)
3005
3276
  ]
3006
3277
  }, undefined, true, undefined, this),
3007
- /* @__PURE__ */ jsxDEV5("div", {
3278
+ /* @__PURE__ */ jsxDEV6("div", {
3008
3279
  className: "mt-4 flex gap-3",
3009
3280
  children: [
3010
- /* @__PURE__ */ jsxDEV5(Button4, {
3281
+ /* @__PURE__ */ jsxDEV6(Button4, {
3011
3282
  variant: "outline",
3012
3283
  onPress: () => alert("Upgrade clicked!"),
3013
3284
  children: "Upgrade Plan"
3014
3285
  }, undefined, false, undefined, this),
3015
- /* @__PURE__ */ jsxDEV5(Button4, {
3286
+ /* @__PURE__ */ jsxDEV6(Button4, {
3016
3287
  variant: "ghost",
3017
3288
  onPress: () => alert("Manage Billing clicked!"),
3018
3289
  children: "Manage Billing"
@@ -3021,9 +3292,9 @@ function BillingTab({ subscription }) {
3021
3292
  }, undefined, true, undefined, this)
3022
3293
  ]
3023
3294
  }, undefined, true, undefined, this),
3024
- subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV5("div", {
3295
+ subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV6("div", {
3025
3296
  className: "rounded-xl border border-border bg-destructive/10 p-4 text-destructive",
3026
- children: /* @__PURE__ */ jsxDEV5("p", {
3297
+ children: /* @__PURE__ */ jsxDEV6("p", {
3027
3298
  className: "font-medium text-sm",
3028
3299
  children: "\u26A0\uFE0F Your subscription will be cancelled at the end of the current period."
3029
3300
  }, undefined, false, undefined, this)
@@ -3032,26 +3303,26 @@ function BillingTab({ subscription }) {
3032
3303
  }, undefined, true, undefined, this);
3033
3304
  }
3034
3305
  function SettingsTab() {
3035
- return /* @__PURE__ */ jsxDEV5("div", {
3306
+ return /* @__PURE__ */ jsxDEV6("div", {
3036
3307
  className: "space-y-6",
3037
- children: /* @__PURE__ */ jsxDEV5("div", {
3308
+ children: /* @__PURE__ */ jsxDEV6("div", {
3038
3309
  className: "rounded-xl border border-border bg-card p-6",
3039
3310
  children: [
3040
- /* @__PURE__ */ jsxDEV5("h3", {
3311
+ /* @__PURE__ */ jsxDEV6("h3", {
3041
3312
  className: "mb-4 font-semibold text-lg",
3042
3313
  children: "Organization Settings"
3043
3314
  }, undefined, false, undefined, this),
3044
- /* @__PURE__ */ jsxDEV5("div", {
3315
+ /* @__PURE__ */ jsxDEV6("div", {
3045
3316
  className: "space-y-4",
3046
3317
  children: [
3047
- /* @__PURE__ */ jsxDEV5("div", {
3318
+ /* @__PURE__ */ jsxDEV6("div", {
3048
3319
  children: [
3049
- /* @__PURE__ */ jsxDEV5("label", {
3320
+ /* @__PURE__ */ jsxDEV6("label", {
3050
3321
  htmlFor: "org-name",
3051
3322
  className: "font-medium text-sm",
3052
3323
  children: "Organization Name"
3053
3324
  }, undefined, false, undefined, this),
3054
- /* @__PURE__ */ jsxDEV5("input", {
3325
+ /* @__PURE__ */ jsxDEV6("input", {
3055
3326
  id: "org-name",
3056
3327
  type: "text",
3057
3328
  defaultValue: "Demo Organization",
@@ -3059,36 +3330,36 @@ function SettingsTab() {
3059
3330
  }, undefined, false, undefined, this)
3060
3331
  ]
3061
3332
  }, undefined, true, undefined, this),
3062
- /* @__PURE__ */ jsxDEV5("div", {
3333
+ /* @__PURE__ */ jsxDEV6("div", {
3063
3334
  children: [
3064
- /* @__PURE__ */ jsxDEV5("label", {
3335
+ /* @__PURE__ */ jsxDEV6("label", {
3065
3336
  htmlFor: "timezone",
3066
3337
  className: "font-medium text-sm",
3067
3338
  children: "Default Timezone"
3068
3339
  }, undefined, false, undefined, this),
3069
- /* @__PURE__ */ jsxDEV5("select", {
3340
+ /* @__PURE__ */ jsxDEV6("select", {
3070
3341
  id: "timezone",
3071
3342
  className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
3072
3343
  children: [
3073
- /* @__PURE__ */ jsxDEV5("option", {
3344
+ /* @__PURE__ */ jsxDEV6("option", {
3074
3345
  children: "UTC"
3075
3346
  }, undefined, false, undefined, this),
3076
- /* @__PURE__ */ jsxDEV5("option", {
3347
+ /* @__PURE__ */ jsxDEV6("option", {
3077
3348
  children: "America/New_York"
3078
3349
  }, undefined, false, undefined, this),
3079
- /* @__PURE__ */ jsxDEV5("option", {
3350
+ /* @__PURE__ */ jsxDEV6("option", {
3080
3351
  children: "Europe/London"
3081
3352
  }, undefined, false, undefined, this),
3082
- /* @__PURE__ */ jsxDEV5("option", {
3353
+ /* @__PURE__ */ jsxDEV6("option", {
3083
3354
  children: "Asia/Tokyo"
3084
3355
  }, undefined, false, undefined, this)
3085
3356
  ]
3086
3357
  }, undefined, true, undefined, this)
3087
3358
  ]
3088
3359
  }, undefined, true, undefined, this),
3089
- /* @__PURE__ */ jsxDEV5("div", {
3360
+ /* @__PURE__ */ jsxDEV6("div", {
3090
3361
  className: "pt-2",
3091
- children: /* @__PURE__ */ jsxDEV5(Button4, {
3362
+ children: /* @__PURE__ */ jsxDEV6(Button4, {
3092
3363
  onPress: () => alert("Settings saved!"),
3093
3364
  children: "Save Settings"
3094
3365
  }, undefined, false, undefined, this)
@@ -3103,32 +3374,32 @@ function SettingsTab() {
3103
3374
  // src/ui/SaasSettingsPanel.tsx
3104
3375
  import { Button as Button5 } from "@contractspec/lib.design-system";
3105
3376
  import { useState as useState6 } from "react";
3106
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
3377
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
3107
3378
  "use client";
3108
3379
  function SaasSettingsPanel() {
3109
3380
  const [orgName, setOrgName] = useState6("Demo Organization");
3110
3381
  const [timezone, setTimezone] = useState6("UTC");
3111
- return /* @__PURE__ */ jsxDEV6("div", {
3382
+ return /* @__PURE__ */ jsxDEV7("div", {
3112
3383
  className: "space-y-6",
3113
3384
  children: [
3114
- /* @__PURE__ */ jsxDEV6("div", {
3385
+ /* @__PURE__ */ jsxDEV7("div", {
3115
3386
  className: "rounded-xl border border-border bg-card p-6",
3116
3387
  children: [
3117
- /* @__PURE__ */ jsxDEV6("h3", {
3388
+ /* @__PURE__ */ jsxDEV7("h3", {
3118
3389
  className: "mb-4 font-semibold text-lg",
3119
3390
  children: "Organization Settings"
3120
3391
  }, undefined, false, undefined, this),
3121
- /* @__PURE__ */ jsxDEV6("div", {
3392
+ /* @__PURE__ */ jsxDEV7("div", {
3122
3393
  className: "space-y-4",
3123
3394
  children: [
3124
- /* @__PURE__ */ jsxDEV6("div", {
3395
+ /* @__PURE__ */ jsxDEV7("div", {
3125
3396
  children: [
3126
- /* @__PURE__ */ jsxDEV6("label", {
3397
+ /* @__PURE__ */ jsxDEV7("label", {
3127
3398
  htmlFor: "setting-org-name",
3128
3399
  className: "block font-medium text-sm",
3129
3400
  children: "Organization Name"
3130
3401
  }, undefined, false, undefined, this),
3131
- /* @__PURE__ */ jsxDEV6("input", {
3402
+ /* @__PURE__ */ jsxDEV7("input", {
3132
3403
  id: "setting-org-name",
3133
3404
  type: "text",
3134
3405
  value: orgName,
@@ -3137,32 +3408,32 @@ function SaasSettingsPanel() {
3137
3408
  }, undefined, false, undefined, this)
3138
3409
  ]
3139
3410
  }, undefined, true, undefined, this),
3140
- /* @__PURE__ */ jsxDEV6("div", {
3411
+ /* @__PURE__ */ jsxDEV7("div", {
3141
3412
  children: [
3142
- /* @__PURE__ */ jsxDEV6("label", {
3413
+ /* @__PURE__ */ jsxDEV7("label", {
3143
3414
  htmlFor: "setting-timezone",
3144
3415
  className: "block font-medium text-sm",
3145
3416
  children: "Default Timezone"
3146
3417
  }, undefined, false, undefined, this),
3147
- /* @__PURE__ */ jsxDEV6("select", {
3418
+ /* @__PURE__ */ jsxDEV7("select", {
3148
3419
  id: "setting-timezone",
3149
3420
  value: timezone,
3150
3421
  onChange: (e) => setTimezone(e.target.value),
3151
3422
  className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
3152
3423
  children: [
3153
- /* @__PURE__ */ jsxDEV6("option", {
3424
+ /* @__PURE__ */ jsxDEV7("option", {
3154
3425
  value: "UTC",
3155
3426
  children: "UTC"
3156
3427
  }, undefined, false, undefined, this),
3157
- /* @__PURE__ */ jsxDEV6("option", {
3428
+ /* @__PURE__ */ jsxDEV7("option", {
3158
3429
  value: "America/New_York",
3159
3430
  children: "America/New_York"
3160
3431
  }, undefined, false, undefined, this),
3161
- /* @__PURE__ */ jsxDEV6("option", {
3432
+ /* @__PURE__ */ jsxDEV7("option", {
3162
3433
  value: "Europe/London",
3163
3434
  children: "Europe/London"
3164
3435
  }, undefined, false, undefined, this),
3165
- /* @__PURE__ */ jsxDEV6("option", {
3436
+ /* @__PURE__ */ jsxDEV7("option", {
3166
3437
  value: "Asia/Tokyo",
3167
3438
  children: "Asia/Tokyo"
3168
3439
  }, undefined, false, undefined, this)
@@ -3172,37 +3443,37 @@ function SaasSettingsPanel() {
3172
3443
  }, undefined, true, undefined, this)
3173
3444
  ]
3174
3445
  }, undefined, true, undefined, this),
3175
- /* @__PURE__ */ jsxDEV6("div", {
3446
+ /* @__PURE__ */ jsxDEV7("div", {
3176
3447
  className: "mt-6",
3177
- children: /* @__PURE__ */ jsxDEV6(Button5, {
3448
+ children: /* @__PURE__ */ jsxDEV7(Button5, {
3178
3449
  variant: "default",
3179
3450
  children: "Save Changes"
3180
3451
  }, undefined, false, undefined, this)
3181
3452
  }, undefined, false, undefined, this)
3182
3453
  ]
3183
3454
  }, undefined, true, undefined, this),
3184
- /* @__PURE__ */ jsxDEV6("div", {
3455
+ /* @__PURE__ */ jsxDEV7("div", {
3185
3456
  className: "rounded-xl border border-border bg-card p-6",
3186
3457
  children: [
3187
- /* @__PURE__ */ jsxDEV6("h3", {
3458
+ /* @__PURE__ */ jsxDEV7("h3", {
3188
3459
  className: "mb-4 font-semibold text-lg",
3189
3460
  children: "Notifications"
3190
3461
  }, undefined, false, undefined, this),
3191
- /* @__PURE__ */ jsxDEV6("div", {
3462
+ /* @__PURE__ */ jsxDEV7("div", {
3192
3463
  className: "space-y-3",
3193
3464
  children: [
3194
3465
  { label: "Email notifications", defaultChecked: true },
3195
3466
  { label: "Usage alerts", defaultChecked: true },
3196
3467
  { label: "Weekly digest", defaultChecked: false }
3197
- ].map((item) => /* @__PURE__ */ jsxDEV6("label", {
3468
+ ].map((item) => /* @__PURE__ */ jsxDEV7("label", {
3198
3469
  className: "flex items-center gap-3",
3199
3470
  children: [
3200
- /* @__PURE__ */ jsxDEV6("input", {
3471
+ /* @__PURE__ */ jsxDEV7("input", {
3201
3472
  type: "checkbox",
3202
3473
  defaultChecked: item.defaultChecked,
3203
3474
  className: "h-4 w-4 rounded border-input"
3204
3475
  }, undefined, false, undefined, this),
3205
- /* @__PURE__ */ jsxDEV6("span", {
3476
+ /* @__PURE__ */ jsxDEV7("span", {
3206
3477
  className: "text-sm",
3207
3478
  children: item.label
3208
3479
  }, undefined, false, undefined, this)
@@ -3211,26 +3482,26 @@ function SaasSettingsPanel() {
3211
3482
  }, undefined, false, undefined, this)
3212
3483
  ]
3213
3484
  }, undefined, true, undefined, this),
3214
- /* @__PURE__ */ jsxDEV6("div", {
3485
+ /* @__PURE__ */ jsxDEV7("div", {
3215
3486
  className: "rounded-xl border border-red-200 bg-red-50 p-6 dark:border-red-900 dark:bg-red-950/20",
3216
3487
  children: [
3217
- /* @__PURE__ */ jsxDEV6("h3", {
3488
+ /* @__PURE__ */ jsxDEV7("h3", {
3218
3489
  className: "mb-2 font-semibold text-lg text-red-700 dark:text-red-400",
3219
3490
  children: "Danger Zone"
3220
3491
  }, undefined, false, undefined, this),
3221
- /* @__PURE__ */ jsxDEV6("p", {
3492
+ /* @__PURE__ */ jsxDEV7("p", {
3222
3493
  className: "mb-4 text-red-600 text-sm dark:text-red-300",
3223
3494
  children: "These actions are irreversible. Please proceed with caution."
3224
3495
  }, undefined, false, undefined, this),
3225
- /* @__PURE__ */ jsxDEV6("div", {
3496
+ /* @__PURE__ */ jsxDEV7("div", {
3226
3497
  className: "flex gap-3",
3227
3498
  children: [
3228
- /* @__PURE__ */ jsxDEV6(Button5, {
3499
+ /* @__PURE__ */ jsxDEV7(Button5, {
3229
3500
  variant: "secondary",
3230
3501
  size: "sm",
3231
3502
  children: "Export Data"
3232
3503
  }, undefined, false, undefined, this),
3233
- /* @__PURE__ */ jsxDEV6(Button5, {
3504
+ /* @__PURE__ */ jsxDEV7(Button5, {
3234
3505
  variant: "secondary",
3235
3506
  size: "sm",
3236
3507
  children: "Delete Organization"
@@ -3293,6 +3564,7 @@ export {
3293
3564
  mockCreateProjectHandler,
3294
3565
  mockCheckFeatureAccessHandler,
3295
3566
  example_default as example,
3567
+ createSaasVisualizationItems,
3296
3568
  createSaasHandlers,
3297
3569
  UsageSummaryModel,
3298
3570
  UsageRecordedPayloadModel,
@@ -3311,8 +3583,15 @@ export {
3311
3583
  SettingsScopeEnum,
3312
3584
  SettingsPanelPresentation,
3313
3585
  SettingsEntity,
3586
+ SaasVisualizationSpecs,
3587
+ SaasVisualizationRegistry,
3588
+ SaasVisualizationRefs,
3314
3589
  SaasSettingsPanel,
3590
+ SaasProjectUsageVisualization,
3591
+ SaasProjectTierVisualization,
3592
+ SaasProjectStatusVisualization,
3315
3593
  SaasProjectList,
3594
+ SaasProjectActivityVisualization,
3316
3595
  SaasDashboardPresentation,
3317
3596
  SaasDashboard,
3318
3597
  SaasBoilerplateFeature,