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