@contractspec/example.saas-boilerplate 3.7.6 → 3.7.7

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 (114) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/AGENTS.md +50 -27
  3. package/README.md +64 -144
  4. package/dist/billing/billing.event.js +1 -1
  5. package/dist/billing/index.d.ts +6 -6
  6. package/dist/billing/index.js +1 -1
  7. package/dist/browser/billing/billing.event.js +1 -1
  8. package/dist/browser/billing/index.js +1 -1
  9. package/dist/browser/index.js +931 -932
  10. package/dist/browser/project/index.js +209 -209
  11. package/dist/browser/project/project.event.js +1 -1
  12. package/dist/browser/ui/SaasDashboard.js +45 -45
  13. package/dist/browser/ui/SaasProjectList.js +7 -7
  14. package/dist/browser/ui/SaasSettingsPanel.js +12 -12
  15. package/dist/browser/ui/hooks/index.js +2 -2
  16. package/dist/browser/ui/hooks/useProjectList.js +1 -1
  17. package/dist/browser/ui/hooks/useProjectMutations.js +1 -1
  18. package/dist/browser/ui/index.js +483 -484
  19. package/dist/browser/ui/modals/CreateProjectModal.js +10 -10
  20. package/dist/browser/ui/modals/ProjectActionsModal.js +13 -13
  21. package/dist/browser/ui/modals/index.js +23 -23
  22. package/dist/browser/ui/renderers/index.js +112 -112
  23. package/dist/browser/ui/renderers/project-list.renderer.js +7 -7
  24. package/dist/handlers/index.d.ts +2 -2
  25. package/dist/index.d.ts +4 -4
  26. package/dist/index.js +931 -932
  27. package/dist/node/billing/billing.event.js +1 -1
  28. package/dist/node/billing/index.js +1 -1
  29. package/dist/node/index.js +931 -932
  30. package/dist/node/project/index.js +209 -209
  31. package/dist/node/project/project.event.js +1 -1
  32. package/dist/node/ui/SaasDashboard.js +45 -45
  33. package/dist/node/ui/SaasProjectList.js +7 -7
  34. package/dist/node/ui/SaasSettingsPanel.js +12 -12
  35. package/dist/node/ui/hooks/index.js +2 -2
  36. package/dist/node/ui/hooks/useProjectList.js +1 -1
  37. package/dist/node/ui/hooks/useProjectMutations.js +1 -1
  38. package/dist/node/ui/index.js +483 -484
  39. package/dist/node/ui/modals/CreateProjectModal.js +10 -10
  40. package/dist/node/ui/modals/ProjectActionsModal.js +13 -13
  41. package/dist/node/ui/modals/index.js +23 -23
  42. package/dist/node/ui/renderers/index.js +112 -112
  43. package/dist/node/ui/renderers/project-list.renderer.js +7 -7
  44. package/dist/presentations/index.d.ts +1 -1
  45. package/dist/project/index.d.ts +7 -7
  46. package/dist/project/index.js +209 -209
  47. package/dist/project/project.event.js +1 -1
  48. package/dist/settings/index.d.ts +1 -1
  49. package/dist/ui/SaasDashboard.js +45 -45
  50. package/dist/ui/SaasProjectList.js +7 -7
  51. package/dist/ui/SaasSettingsPanel.js +12 -12
  52. package/dist/ui/hooks/index.d.ts +2 -2
  53. package/dist/ui/hooks/index.js +2 -2
  54. package/dist/ui/hooks/useProjectList.d.ts +5 -0
  55. package/dist/ui/hooks/useProjectList.js +1 -1
  56. package/dist/ui/hooks/useProjectMutations.d.ts +8 -0
  57. package/dist/ui/hooks/useProjectMutations.js +1 -1
  58. package/dist/ui/index.d.ts +4 -4
  59. package/dist/ui/index.js +483 -484
  60. package/dist/ui/modals/CreateProjectModal.js +10 -10
  61. package/dist/ui/modals/ProjectActionsModal.js +13 -13
  62. package/dist/ui/modals/index.js +23 -23
  63. package/dist/ui/renderers/index.d.ts +1 -1
  64. package/dist/ui/renderers/index.js +112 -112
  65. package/dist/ui/renderers/project-list.renderer.d.ts +1 -1
  66. package/dist/ui/renderers/project-list.renderer.js +7 -7
  67. package/package.json +10 -10
  68. package/src/billing/billing.entity.ts +132 -132
  69. package/src/billing/billing.enum.ts +9 -9
  70. package/src/billing/billing.event.ts +71 -71
  71. package/src/billing/billing.handler.ts +87 -87
  72. package/src/billing/billing.operations.ts +158 -158
  73. package/src/billing/billing.presentation.ts +45 -45
  74. package/src/billing/billing.schema.ts +76 -76
  75. package/src/billing/index.ts +43 -48
  76. package/src/dashboard/dashboard.presentation.ts +45 -45
  77. package/src/dashboard/index.ts +2 -2
  78. package/src/docs/saas-boilerplate.docblock.ts +43 -43
  79. package/src/example.ts +32 -32
  80. package/src/handlers/index.ts +9 -9
  81. package/src/handlers/saas.handlers.ts +250 -249
  82. package/src/index.ts +40 -41
  83. package/src/presentations/index.ts +18 -20
  84. package/src/project/index.ts +45 -50
  85. package/src/project/project.entity.ts +68 -68
  86. package/src/project/project.enum.ts +8 -8
  87. package/src/project/project.event.ts +79 -79
  88. package/src/project/project.handler.ts +103 -103
  89. package/src/project/project.operations.ts +236 -236
  90. package/src/project/project.presentation.ts +46 -46
  91. package/src/project/project.schema.ts +90 -90
  92. package/src/saas-boilerplate.feature.ts +100 -100
  93. package/src/seeders/index.ts +20 -20
  94. package/src/settings/index.ts +2 -3
  95. package/src/settings/settings.entity.ts +65 -65
  96. package/src/settings/settings.enum.ts +4 -4
  97. package/src/shared/mock-data.ts +92 -92
  98. package/src/shared/overlay-types.ts +23 -23
  99. package/src/tests/operations.test-spec.ts +96 -96
  100. package/src/ui/SaasDashboard.tsx +270 -270
  101. package/src/ui/SaasProjectList.tsx +90 -90
  102. package/src/ui/SaasSettingsPanel.tsx +84 -84
  103. package/src/ui/hooks/index.ts +3 -3
  104. package/src/ui/hooks/useProjectList.ts +69 -68
  105. package/src/ui/hooks/useProjectMutations.ts +144 -143
  106. package/src/ui/index.ts +8 -12
  107. package/src/ui/modals/CreateProjectModal.tsx +154 -154
  108. package/src/ui/modals/ProjectActionsModal.tsx +321 -321
  109. package/src/ui/overlays/demo-overlays.ts +49 -49
  110. package/src/ui/renderers/index.ts +5 -4
  111. package/src/ui/renderers/project-list.markdown.ts +204 -204
  112. package/src/ui/renderers/project-list.renderer.tsx +14 -13
  113. package/tsconfig.json +7 -8
  114. package/tsdown.config.js +7 -3
@@ -345,8 +345,8 @@ function createSaasHandlers(db) {
345
345
  };
346
346
  }
347
347
  // src/ui/hooks/useProjectList.ts
348
- import { useCallback, useEffect, useMemo, useState } from "react";
349
348
  import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
349
+ import { useCallback, useEffect, useMemo, useState } from "react";
350
350
  function useProjectList(options = {}) {
351
351
  const { handlers, projectId } = useTemplateRuntime();
352
352
  const { saas: saas2 } = handlers;
@@ -409,8 +409,8 @@ function useProjectList(options = {}) {
409
409
  }
410
410
 
411
411
  // src/ui/hooks/useProjectMutations.ts
412
- import { useCallback as useCallback2, useState as useState2 } from "react";
413
412
  import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
413
+ import { useCallback as useCallback2, useState as useState2 } from "react";
414
414
  function useProjectMutations(options = {}) {
415
415
  const { handlers, projectId } = useTemplateRuntime2();
416
416
  const { saas: saas2 } = handlers;
@@ -497,9 +497,12 @@ function useProjectMutations(options = {}) {
497
497
  };
498
498
  }
499
499
 
500
+ // src/ui/hooks/index.ts
501
+ "use client";
502
+
500
503
  // src/ui/modals/CreateProjectModal.tsx
501
- import { useState as useState3 } from "react";
502
504
  import { Button, Input } from "@contractspec/lib.design-system";
505
+ import { useState as useState3 } from "react";
503
506
  import { jsxDEV } from "react/jsx-dev-runtime";
504
507
  "use client";
505
508
  var TIERS = [
@@ -544,7 +547,7 @@ function CreateProjectModal({
544
547
  className: "fixed inset-0 z-50 flex items-center justify-center",
545
548
  children: [
546
549
  /* @__PURE__ */ jsxDEV("div", {
547
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
550
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
548
551
  onClick: onClose,
549
552
  role: "button",
550
553
  tabIndex: 0,
@@ -555,10 +558,10 @@ function CreateProjectModal({
555
558
  "aria-label": "Close modal"
556
559
  }, undefined, false, undefined, this),
557
560
  /* @__PURE__ */ jsxDEV("div", {
558
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
561
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
559
562
  children: [
560
563
  /* @__PURE__ */ jsxDEV("h2", {
561
- className: "mb-4 text-xl font-semibold",
564
+ className: "mb-4 font-semibold text-xl",
562
565
  children: "Create New Project"
563
566
  }, undefined, false, undefined, this),
564
567
  /* @__PURE__ */ jsxDEV("form", {
@@ -569,7 +572,7 @@ function CreateProjectModal({
569
572
  children: [
570
573
  /* @__PURE__ */ jsxDEV("label", {
571
574
  htmlFor: "project-name",
572
- className: "text-muted-foreground mb-1 block text-sm font-medium",
575
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
573
576
  children: "Project Name *"
574
577
  }, undefined, false, undefined, this),
575
578
  /* @__PURE__ */ jsxDEV(Input, {
@@ -585,7 +588,7 @@ function CreateProjectModal({
585
588
  children: [
586
589
  /* @__PURE__ */ jsxDEV("label", {
587
590
  htmlFor: "project-description",
588
- className: "text-muted-foreground mb-1 block text-sm font-medium",
591
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
589
592
  children: "Description"
590
593
  }, undefined, false, undefined, this),
591
594
  /* @__PURE__ */ jsxDEV("textarea", {
@@ -595,7 +598,7 @@ function CreateProjectModal({
595
598
  placeholder: "Describe what this project is about...",
596
599
  rows: 3,
597
600
  disabled: isLoading,
598
- className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50"
601
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
599
602
  }, undefined, false, undefined, this)
600
603
  ]
601
604
  }, undefined, true, undefined, this),
@@ -603,7 +606,7 @@ function CreateProjectModal({
603
606
  children: [
604
607
  /* @__PURE__ */ jsxDEV("label", {
605
608
  htmlFor: "project-tier",
606
- className: "text-muted-foreground mb-1 block text-sm font-medium",
609
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
607
610
  children: "Tier"
608
611
  }, undefined, false, undefined, this),
609
612
  /* @__PURE__ */ jsxDEV("select", {
@@ -611,7 +614,7 @@ function CreateProjectModal({
611
614
  value: tier,
612
615
  onChange: (e) => setTier(e.target.value),
613
616
  disabled: isLoading,
614
- className: "border-input bg-background focus:ring-ring h-10 w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50",
617
+ className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50",
615
618
  children: TIERS.map((t) => /* @__PURE__ */ jsxDEV("option", {
616
619
  value: t.value,
617
620
  children: t.label
@@ -620,7 +623,7 @@ function CreateProjectModal({
620
623
  ]
621
624
  }, undefined, true, undefined, this),
622
625
  error && /* @__PURE__ */ jsxDEV("div", {
623
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
626
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
624
627
  children: error
625
628
  }, undefined, false, undefined, this),
626
629
  /* @__PURE__ */ jsxDEV("div", {
@@ -649,8 +652,8 @@ function CreateProjectModal({
649
652
  }
650
653
 
651
654
  // src/ui/modals/ProjectActionsModal.tsx
652
- import { useEffect as useEffect2, useState as useState4 } from "react";
653
655
  import { Button as Button2, Input as Input2 } from "@contractspec/lib.design-system";
656
+ import { useEffect as useEffect2, useState as useState4 } from "react";
654
657
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
655
658
  "use client";
656
659
  function ProjectActionsModal({
@@ -743,7 +746,7 @@ function ProjectActionsModal({
743
746
  className: "fixed inset-0 z-50 flex items-center justify-center",
744
747
  children: [
745
748
  /* @__PURE__ */ jsxDEV2("div", {
746
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
749
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
747
750
  onClick: handleClose,
748
751
  role: "button",
749
752
  tabIndex: 0,
@@ -754,13 +757,13 @@ function ProjectActionsModal({
754
757
  "aria-label": "Close modal"
755
758
  }, undefined, false, undefined, this),
756
759
  /* @__PURE__ */ jsxDEV2("div", {
757
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
760
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
758
761
  children: [
759
762
  /* @__PURE__ */ jsxDEV2("div", {
760
- className: "border-border mb-4 border-b pb-4",
763
+ className: "mb-4 border-border border-b pb-4",
761
764
  children: [
762
765
  /* @__PURE__ */ jsxDEV2("h2", {
763
- className: "text-xl font-semibold",
766
+ className: "font-semibold text-xl",
764
767
  children: project.name
765
768
  }, undefined, false, undefined, this),
766
769
  /* @__PURE__ */ jsxDEV2("p", {
@@ -842,7 +845,7 @@ function ProjectActionsModal({
842
845
  children: [
843
846
  /* @__PURE__ */ jsxDEV2("label", {
844
847
  htmlFor: "edit-name",
845
- className: "text-muted-foreground mb-1 block text-sm font-medium",
848
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
846
849
  children: "Project Name *"
847
850
  }, undefined, false, undefined, this),
848
851
  /* @__PURE__ */ jsxDEV2(Input2, {
@@ -857,7 +860,7 @@ function ProjectActionsModal({
857
860
  children: [
858
861
  /* @__PURE__ */ jsxDEV2("label", {
859
862
  htmlFor: "edit-description",
860
- className: "text-muted-foreground mb-1 block text-sm font-medium",
863
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
861
864
  children: "Description"
862
865
  }, undefined, false, undefined, this),
863
866
  /* @__PURE__ */ jsxDEV2("textarea", {
@@ -866,12 +869,12 @@ function ProjectActionsModal({
866
869
  onChange: (e) => setDescription(e.target.value),
867
870
  rows: 3,
868
871
  disabled: isLoading,
869
- className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none disabled:opacity-50"
872
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
870
873
  }, undefined, false, undefined, this)
871
874
  ]
872
875
  }, undefined, true, undefined, this),
873
876
  error && /* @__PURE__ */ jsxDEV2("div", {
874
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
877
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
875
878
  children: error
876
879
  }, undefined, false, undefined, this),
877
880
  /* @__PURE__ */ jsxDEV2("div", {
@@ -901,7 +904,7 @@ function ProjectActionsModal({
901
904
  "Are you sure you want to archive",
902
905
  " ",
903
906
  /* @__PURE__ */ jsxDEV2("span", {
904
- className: "text-foreground font-medium",
907
+ className: "font-medium text-foreground",
905
908
  children: project.name
906
909
  }, undefined, false, undefined, this),
907
910
  "?"
@@ -912,7 +915,7 @@ function ProjectActionsModal({
912
915
  children: "Archived projects can be restored later."
913
916
  }, undefined, false, undefined, this),
914
917
  error && /* @__PURE__ */ jsxDEV2("div", {
915
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
918
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
916
919
  children: error
917
920
  }, undefined, false, undefined, this),
918
921
  /* @__PURE__ */ jsxDEV2("div", {
@@ -942,7 +945,7 @@ function ProjectActionsModal({
942
945
  "Are you sure you want to delete",
943
946
  " ",
944
947
  /* @__PURE__ */ jsxDEV2("span", {
945
- className: "text-foreground font-medium",
948
+ className: "font-medium text-foreground",
946
949
  children: project.name
947
950
  }, undefined, false, undefined, this),
948
951
  "?"
@@ -953,7 +956,7 @@ function ProjectActionsModal({
953
956
  children: "This action cannot be undone."
954
957
  }, undefined, false, undefined, this),
955
958
  error && /* @__PURE__ */ jsxDEV2("div", {
956
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
959
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
957
960
  children: error
958
961
  }, undefined, false, undefined, this),
959
962
  /* @__PURE__ */ jsxDEV2("div", {
@@ -980,18 +983,221 @@ function ProjectActionsModal({
980
983
  ]
981
984
  }, undefined, true, undefined, this);
982
985
  }
986
+ // src/ui/overlays/demo-overlays.ts
987
+ var saasFreeUserOverlay = {
988
+ overlayId: "saas-boilerplate.free-tier",
989
+ version: "1.0.0",
990
+ description: "Shows limitations for free tier users",
991
+ appliesTo: {
992
+ feature: "saas-boilerplate",
993
+ tier: "free"
994
+ },
995
+ modifications: [
996
+ {
997
+ type: "setLimit",
998
+ field: "projects",
999
+ max: 3,
1000
+ message: "Upgrade to create more projects"
1001
+ },
1002
+ { type: "hideField", field: "advancedSettings", reason: "Pro feature" },
1003
+ {
1004
+ type: "addBadge",
1005
+ position: "header",
1006
+ label: "Free Plan",
1007
+ variant: "default"
1008
+ }
1009
+ ]
1010
+ };
1011
+ var saasDemoOverlay = {
1012
+ overlayId: "saas-boilerplate.demo-user",
1013
+ version: "1.0.0",
1014
+ description: "Demo mode for SaaS boilerplate",
1015
+ appliesTo: {
1016
+ feature: "saas-boilerplate",
1017
+ role: "demo"
1018
+ },
1019
+ modifications: [
1020
+ {
1021
+ type: "hideField",
1022
+ field: "billingSection",
1023
+ reason: "Demo users cannot access billing"
1024
+ },
1025
+ {
1026
+ type: "hideField",
1027
+ field: "deleteAccount",
1028
+ reason: "Not available in demo"
1029
+ },
1030
+ {
1031
+ type: "addBadge",
1032
+ position: "header",
1033
+ label: "Demo Mode",
1034
+ variant: "warning"
1035
+ }
1036
+ ]
1037
+ };
1038
+ var saasOverlays = [
1039
+ saasFreeUserOverlay,
1040
+ saasDemoOverlay
1041
+ ];
1042
+ // src/ui/renderers/project-list.markdown.ts
1043
+ var projectListMarkdownRenderer = {
1044
+ target: "markdown",
1045
+ render: async (desc, _ctx) => {
1046
+ if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
1047
+ throw new Error("projectListMarkdownRenderer: not ProjectListView");
1048
+ }
1049
+ const data = await mockListProjectsHandler({
1050
+ limit: 20,
1051
+ offset: 0
1052
+ });
1053
+ const items = data.projects ?? data.items ?? [];
1054
+ const lines = [
1055
+ "# Projects",
1056
+ "",
1057
+ `**Total**: ${data.total} projects`,
1058
+ ""
1059
+ ];
1060
+ if (items.length === 0) {
1061
+ lines.push("_No projects found._");
1062
+ } else {
1063
+ lines.push("| Status | Project | Description |");
1064
+ lines.push("|--------|---------|-------------|");
1065
+ for (const project of items) {
1066
+ const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
1067
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
1068
+ }
1069
+ }
1070
+ return {
1071
+ mimeType: "text/markdown",
1072
+ body: lines.join(`
1073
+ `)
1074
+ };
1075
+ }
1076
+ };
1077
+ var saasDashboardMarkdownRenderer = {
1078
+ target: "markdown",
1079
+ render: async (desc, _ctx) => {
1080
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
1081
+ throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
1082
+ }
1083
+ const [projectsData, subscription] = await Promise.all([
1084
+ mockListProjectsHandler({ limit: 50 }),
1085
+ mockGetSubscriptionHandler()
1086
+ ]);
1087
+ const projects = projectsData.projects ?? [];
1088
+ const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
1089
+ const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
1090
+ const lines = [
1091
+ "# SaaS Dashboard",
1092
+ "",
1093
+ "> Organization overview and usage summary",
1094
+ "",
1095
+ "## Summary",
1096
+ "",
1097
+ "| Metric | Value |",
1098
+ "|--------|-------|",
1099
+ `| Total Projects | ${projectsData.total} |`,
1100
+ `| Active Projects | ${activeProjects} |`,
1101
+ `| Archived Projects | ${archivedProjects} |`,
1102
+ `| Subscription Plan | ${subscription.planName} |`,
1103
+ `| Subscription Status | ${subscription.status} |`,
1104
+ "",
1105
+ "## Projects",
1106
+ ""
1107
+ ];
1108
+ if (projects.length === 0) {
1109
+ lines.push("_No projects yet._");
1110
+ } else {
1111
+ lines.push("| Status | Project | Description |");
1112
+ lines.push("|--------|---------|-------------|");
1113
+ for (const project of projects.slice(0, 10)) {
1114
+ const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
1115
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
1116
+ }
1117
+ if (projects.length > 10) {
1118
+ lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
1119
+ }
1120
+ }
1121
+ lines.push("");
1122
+ lines.push("## Subscription");
1123
+ lines.push("");
1124
+ lines.push(`- **Plan**: ${subscription.planName}`);
1125
+ lines.push(`- **Status**: ${subscription.status}`);
1126
+ if (subscription.currentPeriodEnd) {
1127
+ lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
1128
+ }
1129
+ return {
1130
+ mimeType: "text/markdown",
1131
+ body: lines.join(`
1132
+ `)
1133
+ };
1134
+ }
1135
+ };
1136
+ var saasBillingMarkdownRenderer = {
1137
+ target: "markdown",
1138
+ render: async (desc, _ctx) => {
1139
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
1140
+ throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
1141
+ }
1142
+ const subscription = await mockGetSubscriptionHandler();
1143
+ const lines = [
1144
+ "# Billing & Subscription",
1145
+ "",
1146
+ "> Current subscription details and billing information",
1147
+ "",
1148
+ "## Subscription Details",
1149
+ "",
1150
+ "| Property | Value |",
1151
+ "|----------|-------|",
1152
+ `| Plan | ${subscription.planName} |`,
1153
+ `| Status | ${subscription.status} |`,
1154
+ `| ID | ${subscription.id} |`,
1155
+ `| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
1156
+ `| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
1157
+ ];
1158
+ lines.push("");
1159
+ lines.push("## Plan Limits");
1160
+ lines.push("");
1161
+ lines.push(`- **Projects**: ${subscription.limits.projects}`);
1162
+ lines.push(`- **Users**: ${subscription.limits.users}`);
1163
+ lines.push("");
1164
+ lines.push("## Plan Features");
1165
+ lines.push("");
1166
+ if (subscription.planName.toLowerCase().includes("free")) {
1167
+ lines.push("- ✅ Up to 3 projects");
1168
+ lines.push("- ✅ Basic support");
1169
+ lines.push("- ❌ Priority support");
1170
+ lines.push("- ❌ Advanced analytics");
1171
+ } else if (subscription.planName.toLowerCase().includes("pro")) {
1172
+ lines.push("- ✅ Unlimited projects");
1173
+ lines.push("- ✅ Priority support");
1174
+ lines.push("- ✅ Advanced analytics");
1175
+ lines.push("- ❌ Custom integrations");
1176
+ } else {
1177
+ lines.push("- ✅ Unlimited projects");
1178
+ lines.push("- ✅ Priority support");
1179
+ lines.push("- ✅ Advanced analytics");
1180
+ lines.push("- ✅ Custom integrations");
1181
+ lines.push("- ✅ Dedicated support");
1182
+ }
1183
+ return {
1184
+ mimeType: "text/markdown",
1185
+ body: lines.join(`
1186
+ `)
1187
+ };
1188
+ }
1189
+ };
983
1190
 
984
- // src/ui/SaasDashboard.tsx
985
- import { useState as useState5, useCallback as useCallback3 } from "react";
1191
+ // src/ui/SaasProjectList.tsx
986
1192
  import {
987
- StatCard,
988
- StatCardGroup,
989
- StatusChip,
990
- EntityCard,
1193
+ Button as Button3,
991
1194
  EmptyState,
992
- LoaderBlock,
1195
+ EntityCard,
993
1196
  ErrorState,
994
- Button as Button3
1197
+ LoaderBlock,
1198
+ StatCard,
1199
+ StatCardGroup,
1200
+ StatusChip
995
1201
  } from "@contractspec/lib.design-system";
996
1202
  import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
997
1203
  "use client";
@@ -1002,58 +1208,169 @@ function getStatusTone(status) {
1002
1208
  case "DRAFT":
1003
1209
  return "neutral";
1004
1210
  case "ARCHIVED":
1005
- return "warning";
1211
+ return "danger";
1006
1212
  default:
1007
1213
  return "neutral";
1008
1214
  }
1009
1215
  }
1010
- function SaasDashboard() {
1011
- const [activeTab, setActiveTab] = useState5("projects");
1012
- const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
1013
- const [selectedProject, setSelectedProject] = useState5(null);
1014
- const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
1015
- const { data, subscription, loading, error, stats, refetch } = useProjectList();
1016
- const mutations = useProjectMutations({
1017
- onSuccess: () => {
1018
- refetch();
1019
- }
1020
- });
1021
- const handleProjectClick = useCallback3((project) => {
1022
- setSelectedProject(project);
1023
- setIsProjectActionsOpen(true);
1024
- }, []);
1025
- const tabs = [
1026
- { id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
1027
- { id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
1028
- { id: "settings", label: "Settings", icon: "⚙️" }
1029
- ];
1216
+ function SaasProjectList({
1217
+ onProjectClick,
1218
+ onCreateProject
1219
+ }) {
1220
+ const { data, loading, error, stats, refetch } = useProjectList();
1030
1221
  if (loading && !data) {
1031
1222
  return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
1032
- label: "Loading dashboard..."
1223
+ label: "Loading projects..."
1033
1224
  }, undefined, false, undefined, this);
1034
1225
  }
1035
1226
  if (error) {
1036
1227
  return /* @__PURE__ */ jsxDEV3(ErrorState, {
1037
- title: "Failed to load dashboard",
1228
+ title: "Failed to load projects",
1038
1229
  description: error.message,
1039
1230
  onRetry: refetch,
1040
1231
  retryLabel: "Retry"
1041
1232
  }, undefined, false, undefined, this);
1042
1233
  }
1234
+ if (!data?.items.length) {
1235
+ return /* @__PURE__ */ jsxDEV3(EmptyState, {
1236
+ title: "No projects found",
1237
+ description: "Create your first project to get started.",
1238
+ primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV3(Button3, {
1239
+ onPress: onCreateProject,
1240
+ children: "Create Project"
1241
+ }, undefined, false, undefined, this) : undefined
1242
+ }, undefined, false, undefined, this);
1243
+ }
1043
1244
  return /* @__PURE__ */ jsxDEV3("div", {
1044
1245
  className: "space-y-6",
1045
1246
  children: [
1247
+ stats && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
1248
+ children: [
1249
+ /* @__PURE__ */ jsxDEV3(StatCard, {
1250
+ label: "Total Projects",
1251
+ value: stats.total.toString()
1252
+ }, undefined, false, undefined, this),
1253
+ /* @__PURE__ */ jsxDEV3(StatCard, {
1254
+ label: "Active",
1255
+ value: stats.activeCount.toString()
1256
+ }, undefined, false, undefined, this),
1257
+ /* @__PURE__ */ jsxDEV3(StatCard, {
1258
+ label: "Draft",
1259
+ value: stats.draftCount.toString()
1260
+ }, undefined, false, undefined, this)
1261
+ ]
1262
+ }, undefined, true, undefined, this),
1046
1263
  /* @__PURE__ */ jsxDEV3("div", {
1264
+ className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
1265
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
1266
+ cardTitle: project.name,
1267
+ cardSubtitle: project.tier,
1268
+ meta: /* @__PURE__ */ jsxDEV3("p", {
1269
+ className: "text-muted-foreground text-sm",
1270
+ children: project.description
1271
+ }, undefined, false, undefined, this),
1272
+ chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
1273
+ tone: getStatusTone(project.status),
1274
+ label: project.status
1275
+ }, undefined, false, undefined, this),
1276
+ footer: /* @__PURE__ */ jsxDEV3("span", {
1277
+ className: "text-muted-foreground text-xs",
1278
+ children: project.updatedAt.toLocaleDateString()
1279
+ }, undefined, false, undefined, this),
1280
+ onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
1281
+ }, project.id, false, undefined, this))
1282
+ }, undefined, false, undefined, this)
1283
+ ]
1284
+ }, undefined, true, undefined, this);
1285
+ }
1286
+
1287
+ // src/ui/renderers/project-list.renderer.tsx
1288
+ import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
1289
+ var projectListReactRenderer = {
1290
+ target: "react",
1291
+ render: async (desc, _ctx) => {
1292
+ if (desc.source.type !== "component") {
1293
+ throw new Error("Invalid source type");
1294
+ }
1295
+ if (desc.source.componentKey !== "SaasProjectListView") {
1296
+ throw new Error(`Unknown component: ${desc.source.componentKey}`);
1297
+ }
1298
+ return /* @__PURE__ */ jsxDEV4(SaasProjectList, {}, undefined, false, undefined, this);
1299
+ }
1300
+ };
1301
+ // src/ui/SaasDashboard.tsx
1302
+ import {
1303
+ Button as Button4,
1304
+ EmptyState as EmptyState2,
1305
+ EntityCard as EntityCard2,
1306
+ ErrorState as ErrorState2,
1307
+ LoaderBlock as LoaderBlock2,
1308
+ StatCard as StatCard2,
1309
+ StatCardGroup as StatCardGroup2,
1310
+ StatusChip as StatusChip2
1311
+ } from "@contractspec/lib.design-system";
1312
+ import { useCallback as useCallback3, useState as useState5 } from "react";
1313
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
1314
+ "use client";
1315
+ function getStatusTone2(status) {
1316
+ switch (status) {
1317
+ case "ACTIVE":
1318
+ return "success";
1319
+ case "DRAFT":
1320
+ return "neutral";
1321
+ case "ARCHIVED":
1322
+ return "warning";
1323
+ default:
1324
+ return "neutral";
1325
+ }
1326
+ }
1327
+ function SaasDashboard() {
1328
+ const [activeTab, setActiveTab] = useState5("projects");
1329
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
1330
+ const [selectedProject, setSelectedProject] = useState5(null);
1331
+ const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
1332
+ const { data, subscription, loading, error, stats, refetch } = useProjectList();
1333
+ const mutations = useProjectMutations({
1334
+ onSuccess: () => {
1335
+ refetch();
1336
+ }
1337
+ });
1338
+ const handleProjectClick = useCallback3((project) => {
1339
+ setSelectedProject(project);
1340
+ setIsProjectActionsOpen(true);
1341
+ }, []);
1342
+ const tabs = [
1343
+ { id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
1344
+ { id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
1345
+ { id: "settings", label: "Settings", icon: "⚙️" }
1346
+ ];
1347
+ if (loading && !data) {
1348
+ return /* @__PURE__ */ jsxDEV5(LoaderBlock2, {
1349
+ label: "Loading dashboard..."
1350
+ }, undefined, false, undefined, this);
1351
+ }
1352
+ if (error) {
1353
+ return /* @__PURE__ */ jsxDEV5(ErrorState2, {
1354
+ title: "Failed to load dashboard",
1355
+ description: error.message,
1356
+ onRetry: refetch,
1357
+ retryLabel: "Retry"
1358
+ }, undefined, false, undefined, this);
1359
+ }
1360
+ return /* @__PURE__ */ jsxDEV5("div", {
1361
+ className: "space-y-6",
1362
+ children: [
1363
+ /* @__PURE__ */ jsxDEV5("div", {
1047
1364
  className: "flex items-center justify-between",
1048
1365
  children: [
1049
- /* @__PURE__ */ jsxDEV3("h2", {
1050
- className: "text-2xl font-bold",
1366
+ /* @__PURE__ */ jsxDEV5("h2", {
1367
+ className: "font-bold text-2xl",
1051
1368
  children: "SaaS Dashboard"
1052
1369
  }, undefined, false, undefined, this),
1053
- activeTab === "projects" && /* @__PURE__ */ jsxDEV3(Button3, {
1370
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV5(Button4, {
1054
1371
  onPress: () => setIsCreateModalOpen(true),
1055
1372
  children: [
1056
- /* @__PURE__ */ jsxDEV3("span", {
1373
+ /* @__PURE__ */ jsxDEV5("span", {
1057
1374
  className: "mr-2",
1058
1375
  children: "+"
1059
1376
  }, undefined, false, undefined, this),
@@ -1062,59 +1379,59 @@ function SaasDashboard() {
1062
1379
  }, undefined, true, undefined, this)
1063
1380
  ]
1064
1381
  }, undefined, true, undefined, this),
1065
- stats && subscription && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
1382
+ stats && subscription && /* @__PURE__ */ jsxDEV5(StatCardGroup2, {
1066
1383
  children: [
1067
- /* @__PURE__ */ jsxDEV3(StatCard, {
1384
+ /* @__PURE__ */ jsxDEV5(StatCard2, {
1068
1385
  label: "Projects",
1069
1386
  value: stats.total.toString()
1070
1387
  }, undefined, false, undefined, this),
1071
- /* @__PURE__ */ jsxDEV3(StatCard, {
1388
+ /* @__PURE__ */ jsxDEV5(StatCard2, {
1072
1389
  label: "Active",
1073
1390
  value: stats.activeCount.toString()
1074
1391
  }, undefined, false, undefined, this),
1075
- /* @__PURE__ */ jsxDEV3(StatCard, {
1392
+ /* @__PURE__ */ jsxDEV5(StatCard2, {
1076
1393
  label: "Draft",
1077
1394
  value: stats.draftCount.toString()
1078
1395
  }, undefined, false, undefined, this),
1079
- /* @__PURE__ */ jsxDEV3(StatCard, {
1396
+ /* @__PURE__ */ jsxDEV5(StatCard2, {
1080
1397
  label: "Plan",
1081
1398
  value: subscription.plan,
1082
1399
  hint: subscription.status
1083
1400
  }, undefined, false, undefined, this)
1084
1401
  ]
1085
1402
  }, undefined, true, undefined, this),
1086
- /* @__PURE__ */ jsxDEV3("nav", {
1087
- className: "bg-muted flex gap-1 rounded-lg p-1",
1403
+ /* @__PURE__ */ jsxDEV5("nav", {
1404
+ className: "flex gap-1 rounded-lg bg-muted p-1",
1088
1405
  role: "tablist",
1089
- children: tabs.map((tab) => /* @__PURE__ */ jsxDEV3("button", {
1406
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV5("button", {
1090
1407
  type: "button",
1091
1408
  role: "tab",
1092
1409
  "aria-selected": activeTab === tab.id,
1093
1410
  onClick: () => setActiveTab(tab.id),
1094
- className: `flex flex-1 items-center justify-center gap-2 rounded-md px-4 py-2 text-sm font-medium transition-colors ${activeTab === tab.id ? "bg-background text-foreground shadow-sm" : "text-muted-foreground hover:text-foreground"}`,
1411
+ 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"}`,
1095
1412
  children: [
1096
- /* @__PURE__ */ jsxDEV3("span", {
1413
+ /* @__PURE__ */ jsxDEV5("span", {
1097
1414
  children: tab.icon
1098
1415
  }, undefined, false, undefined, this),
1099
1416
  tab.label
1100
1417
  ]
1101
1418
  }, tab.id, true, undefined, this))
1102
1419
  }, undefined, false, undefined, this),
1103
- /* @__PURE__ */ jsxDEV3("div", {
1420
+ /* @__PURE__ */ jsxDEV5("div", {
1104
1421
  className: "min-h-[400px]",
1105
1422
  role: "tabpanel",
1106
1423
  children: [
1107
- activeTab === "projects" && /* @__PURE__ */ jsxDEV3(ProjectsTab, {
1424
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV5(ProjectsTab, {
1108
1425
  data,
1109
1426
  onProjectClick: handleProjectClick
1110
1427
  }, undefined, false, undefined, this),
1111
- activeTab === "billing" && /* @__PURE__ */ jsxDEV3(BillingTab, {
1428
+ activeTab === "billing" && /* @__PURE__ */ jsxDEV5(BillingTab, {
1112
1429
  subscription
1113
1430
  }, undefined, false, undefined, this),
1114
- activeTab === "settings" && /* @__PURE__ */ jsxDEV3(SettingsTab, {}, undefined, false, undefined, this)
1431
+ activeTab === "settings" && /* @__PURE__ */ jsxDEV5(SettingsTab, {}, undefined, false, undefined, this)
1115
1432
  ]
1116
1433
  }, undefined, true, undefined, this),
1117
- /* @__PURE__ */ jsxDEV3(CreateProjectModal, {
1434
+ /* @__PURE__ */ jsxDEV5(CreateProjectModal, {
1118
1435
  isOpen: isCreateModalOpen,
1119
1436
  onClose: () => setIsCreateModalOpen(false),
1120
1437
  onSubmit: async (input) => {
@@ -1122,7 +1439,7 @@ function SaasDashboard() {
1122
1439
  },
1123
1440
  isLoading: mutations.createState.loading
1124
1441
  }, undefined, false, undefined, this),
1125
- /* @__PURE__ */ jsxDEV3(ProjectActionsModal, {
1442
+ /* @__PURE__ */ jsxDEV5(ProjectActionsModal, {
1126
1443
  isOpen: isProjectActionsOpen,
1127
1444
  project: selectedProject,
1128
1445
  onClose: () => {
@@ -1148,34 +1465,34 @@ function SaasDashboard() {
1148
1465
  }
1149
1466
  function ProjectsTab({ data, onProjectClick }) {
1150
1467
  if (!data?.items.length) {
1151
- return /* @__PURE__ */ jsxDEV3(EmptyState, {
1468
+ return /* @__PURE__ */ jsxDEV5(EmptyState2, {
1152
1469
  title: "No projects yet",
1153
1470
  description: "Create your first project to get started."
1154
1471
  }, undefined, false, undefined, this);
1155
1472
  }
1156
- return /* @__PURE__ */ jsxDEV3("div", {
1473
+ return /* @__PURE__ */ jsxDEV5("div", {
1157
1474
  className: "space-y-4",
1158
- children: /* @__PURE__ */ jsxDEV3("div", {
1475
+ children: /* @__PURE__ */ jsxDEV5("div", {
1159
1476
  className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
1160
- children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
1477
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV5(EntityCard2, {
1161
1478
  cardTitle: project.name,
1162
1479
  cardSubtitle: project.tier,
1163
- meta: /* @__PURE__ */ jsxDEV3("p", {
1480
+ meta: /* @__PURE__ */ jsxDEV5("p", {
1164
1481
  className: "text-muted-foreground text-sm",
1165
1482
  children: project.description
1166
1483
  }, undefined, false, undefined, this),
1167
- chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
1168
- tone: getStatusTone(project.status),
1484
+ chips: /* @__PURE__ */ jsxDEV5(StatusChip2, {
1485
+ tone: getStatusTone2(project.status),
1169
1486
  label: project.status
1170
1487
  }, undefined, false, undefined, this),
1171
- footer: /* @__PURE__ */ jsxDEV3("div", {
1488
+ footer: /* @__PURE__ */ jsxDEV5("div", {
1172
1489
  className: "flex w-full items-center justify-between",
1173
1490
  children: [
1174
- /* @__PURE__ */ jsxDEV3("span", {
1491
+ /* @__PURE__ */ jsxDEV5("span", {
1175
1492
  className: "text-muted-foreground text-xs",
1176
1493
  children: project.updatedAt.toLocaleDateString()
1177
1494
  }, undefined, false, undefined, this),
1178
- /* @__PURE__ */ jsxDEV3(Button3, {
1495
+ /* @__PURE__ */ jsxDEV5(Button4, {
1179
1496
  variant: "ghost",
1180
1497
  size: "sm",
1181
1498
  onPress: () => onProjectClick?.(project),
@@ -1190,25 +1507,25 @@ function ProjectsTab({ data, onProjectClick }) {
1190
1507
  function BillingTab({ subscription }) {
1191
1508
  if (!subscription)
1192
1509
  return null;
1193
- return /* @__PURE__ */ jsxDEV3("div", {
1510
+ return /* @__PURE__ */ jsxDEV5("div", {
1194
1511
  className: "space-y-6",
1195
1512
  children: [
1196
- /* @__PURE__ */ jsxDEV3("div", {
1197
- className: "border-border bg-card rounded-xl border p-6",
1513
+ /* @__PURE__ */ jsxDEV5("div", {
1514
+ className: "rounded-xl border border-border bg-card p-6",
1198
1515
  children: [
1199
- /* @__PURE__ */ jsxDEV3("div", {
1516
+ /* @__PURE__ */ jsxDEV5("div", {
1200
1517
  className: "flex items-start justify-between",
1201
1518
  children: [
1202
- /* @__PURE__ */ jsxDEV3("div", {
1519
+ /* @__PURE__ */ jsxDEV5("div", {
1203
1520
  children: [
1204
- /* @__PURE__ */ jsxDEV3("h3", {
1205
- className: "text-lg font-semibold",
1521
+ /* @__PURE__ */ jsxDEV5("h3", {
1522
+ className: "font-semibold text-lg",
1206
1523
  children: [
1207
1524
  subscription.plan,
1208
1525
  " Plan"
1209
1526
  ]
1210
1527
  }, undefined, true, undefined, this),
1211
- /* @__PURE__ */ jsxDEV3("p", {
1528
+ /* @__PURE__ */ jsxDEV5("p", {
1212
1529
  className: "text-muted-foreground text-sm",
1213
1530
  children: [
1214
1531
  "Current period:",
@@ -1219,7 +1536,7 @@ function BillingTab({ subscription }) {
1219
1536
  subscription.currentPeriodEnd.toLocaleDateString()
1220
1537
  ]
1221
1538
  }, undefined, true, undefined, this),
1222
- /* @__PURE__ */ jsxDEV3("p", {
1539
+ /* @__PURE__ */ jsxDEV5("p", {
1223
1540
  className: "text-muted-foreground text-sm",
1224
1541
  children: [
1225
1542
  "Billing cycle: ",
@@ -1228,21 +1545,21 @@ function BillingTab({ subscription }) {
1228
1545
  }, undefined, true, undefined, this)
1229
1546
  ]
1230
1547
  }, undefined, true, undefined, this),
1231
- /* @__PURE__ */ jsxDEV3(StatusChip, {
1548
+ /* @__PURE__ */ jsxDEV5(StatusChip2, {
1232
1549
  tone: "success",
1233
1550
  label: subscription.status
1234
1551
  }, undefined, false, undefined, this)
1235
1552
  ]
1236
1553
  }, undefined, true, undefined, this),
1237
- /* @__PURE__ */ jsxDEV3("div", {
1554
+ /* @__PURE__ */ jsxDEV5("div", {
1238
1555
  className: "mt-4 flex gap-3",
1239
1556
  children: [
1240
- /* @__PURE__ */ jsxDEV3(Button3, {
1557
+ /* @__PURE__ */ jsxDEV5(Button4, {
1241
1558
  variant: "outline",
1242
1559
  onPress: () => alert("Upgrade clicked!"),
1243
1560
  children: "Upgrade Plan"
1244
1561
  }, undefined, false, undefined, this),
1245
- /* @__PURE__ */ jsxDEV3(Button3, {
1562
+ /* @__PURE__ */ jsxDEV5(Button4, {
1246
1563
  variant: "ghost",
1247
1564
  onPress: () => alert("Manage Billing clicked!"),
1248
1565
  children: "Manage Billing"
@@ -1251,10 +1568,10 @@ function BillingTab({ subscription }) {
1251
1568
  }, undefined, true, undefined, this)
1252
1569
  ]
1253
1570
  }, undefined, true, undefined, this),
1254
- subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV3("div", {
1255
- className: "border-border bg-destructive/10 text-destructive rounded-xl border p-4",
1256
- children: /* @__PURE__ */ jsxDEV3("p", {
1257
- className: "text-sm font-medium",
1571
+ subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV5("div", {
1572
+ className: "rounded-xl border border-border bg-destructive/10 p-4 text-destructive",
1573
+ children: /* @__PURE__ */ jsxDEV5("p", {
1574
+ className: "font-medium text-sm",
1258
1575
  children: "⚠️ Your subscription will be cancelled at the end of the current period."
1259
1576
  }, undefined, false, undefined, this)
1260
1577
  }, undefined, false, undefined, this)
@@ -1262,63 +1579,63 @@ function BillingTab({ subscription }) {
1262
1579
  }, undefined, true, undefined, this);
1263
1580
  }
1264
1581
  function SettingsTab() {
1265
- return /* @__PURE__ */ jsxDEV3("div", {
1582
+ return /* @__PURE__ */ jsxDEV5("div", {
1266
1583
  className: "space-y-6",
1267
- children: /* @__PURE__ */ jsxDEV3("div", {
1268
- className: "border-border bg-card rounded-xl border p-6",
1584
+ children: /* @__PURE__ */ jsxDEV5("div", {
1585
+ className: "rounded-xl border border-border bg-card p-6",
1269
1586
  children: [
1270
- /* @__PURE__ */ jsxDEV3("h3", {
1271
- className: "mb-4 text-lg font-semibold",
1587
+ /* @__PURE__ */ jsxDEV5("h3", {
1588
+ className: "mb-4 font-semibold text-lg",
1272
1589
  children: "Organization Settings"
1273
1590
  }, undefined, false, undefined, this),
1274
- /* @__PURE__ */ jsxDEV3("div", {
1591
+ /* @__PURE__ */ jsxDEV5("div", {
1275
1592
  className: "space-y-4",
1276
1593
  children: [
1277
- /* @__PURE__ */ jsxDEV3("div", {
1594
+ /* @__PURE__ */ jsxDEV5("div", {
1278
1595
  children: [
1279
- /* @__PURE__ */ jsxDEV3("label", {
1596
+ /* @__PURE__ */ jsxDEV5("label", {
1280
1597
  htmlFor: "org-name",
1281
- className: "text-sm font-medium",
1598
+ className: "font-medium text-sm",
1282
1599
  children: "Organization Name"
1283
1600
  }, undefined, false, undefined, this),
1284
- /* @__PURE__ */ jsxDEV3("input", {
1601
+ /* @__PURE__ */ jsxDEV5("input", {
1285
1602
  id: "org-name",
1286
1603
  type: "text",
1287
1604
  defaultValue: "Demo Organization",
1288
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
1605
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
1289
1606
  }, undefined, false, undefined, this)
1290
1607
  ]
1291
1608
  }, undefined, true, undefined, this),
1292
- /* @__PURE__ */ jsxDEV3("div", {
1609
+ /* @__PURE__ */ jsxDEV5("div", {
1293
1610
  children: [
1294
- /* @__PURE__ */ jsxDEV3("label", {
1611
+ /* @__PURE__ */ jsxDEV5("label", {
1295
1612
  htmlFor: "timezone",
1296
- className: "text-sm font-medium",
1613
+ className: "font-medium text-sm",
1297
1614
  children: "Default Timezone"
1298
1615
  }, undefined, false, undefined, this),
1299
- /* @__PURE__ */ jsxDEV3("select", {
1616
+ /* @__PURE__ */ jsxDEV5("select", {
1300
1617
  id: "timezone",
1301
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2",
1618
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
1302
1619
  children: [
1303
- /* @__PURE__ */ jsxDEV3("option", {
1620
+ /* @__PURE__ */ jsxDEV5("option", {
1304
1621
  children: "UTC"
1305
1622
  }, undefined, false, undefined, this),
1306
- /* @__PURE__ */ jsxDEV3("option", {
1623
+ /* @__PURE__ */ jsxDEV5("option", {
1307
1624
  children: "America/New_York"
1308
1625
  }, undefined, false, undefined, this),
1309
- /* @__PURE__ */ jsxDEV3("option", {
1626
+ /* @__PURE__ */ jsxDEV5("option", {
1310
1627
  children: "Europe/London"
1311
1628
  }, undefined, false, undefined, this),
1312
- /* @__PURE__ */ jsxDEV3("option", {
1629
+ /* @__PURE__ */ jsxDEV5("option", {
1313
1630
  children: "Asia/Tokyo"
1314
1631
  }, undefined, false, undefined, this)
1315
1632
  ]
1316
1633
  }, undefined, true, undefined, this)
1317
1634
  ]
1318
1635
  }, undefined, true, undefined, this),
1319
- /* @__PURE__ */ jsxDEV3("div", {
1636
+ /* @__PURE__ */ jsxDEV5("div", {
1320
1637
  className: "pt-2",
1321
- children: /* @__PURE__ */ jsxDEV3(Button3, {
1638
+ children: /* @__PURE__ */ jsxDEV5(Button4, {
1322
1639
  onPress: () => alert("Settings saved!"),
1323
1640
  children: "Save Settings"
1324
1641
  }, undefined, false, undefined, this)
@@ -1330,165 +1647,69 @@ function SettingsTab() {
1330
1647
  }, undefined, false, undefined, this);
1331
1648
  }
1332
1649
 
1333
- // src/ui/SaasProjectList.tsx
1334
- import {
1335
- StatCard as StatCard2,
1336
- StatCardGroup as StatCardGroup2,
1337
- StatusChip as StatusChip2,
1338
- EntityCard as EntityCard2,
1339
- EmptyState as EmptyState2,
1340
- LoaderBlock as LoaderBlock2,
1341
- ErrorState as ErrorState2,
1342
- Button as Button4
1343
- } from "@contractspec/lib.design-system";
1344
- import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
1345
- "use client";
1346
- function getStatusTone2(status) {
1347
- switch (status) {
1348
- case "ACTIVE":
1349
- return "success";
1350
- case "DRAFT":
1351
- return "neutral";
1352
- case "ARCHIVED":
1353
- return "danger";
1354
- default:
1355
- return "neutral";
1356
- }
1357
- }
1358
- function SaasProjectList({
1359
- onProjectClick,
1360
- onCreateProject
1361
- }) {
1362
- const { data, loading, error, stats, refetch } = useProjectList();
1363
- if (loading && !data) {
1364
- return /* @__PURE__ */ jsxDEV4(LoaderBlock2, {
1365
- label: "Loading projects..."
1366
- }, undefined, false, undefined, this);
1367
- }
1368
- if (error) {
1369
- return /* @__PURE__ */ jsxDEV4(ErrorState2, {
1370
- title: "Failed to load projects",
1371
- description: error.message,
1372
- onRetry: refetch,
1373
- retryLabel: "Retry"
1374
- }, undefined, false, undefined, this);
1375
- }
1376
- if (!data?.items.length) {
1377
- return /* @__PURE__ */ jsxDEV4(EmptyState2, {
1378
- title: "No projects found",
1379
- description: "Create your first project to get started.",
1380
- primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV4(Button4, {
1381
- onPress: onCreateProject,
1382
- children: "Create Project"
1383
- }, undefined, false, undefined, this) : undefined
1384
- }, undefined, false, undefined, this);
1385
- }
1386
- return /* @__PURE__ */ jsxDEV4("div", {
1387
- className: "space-y-6",
1388
- children: [
1389
- stats && /* @__PURE__ */ jsxDEV4(StatCardGroup2, {
1390
- children: [
1391
- /* @__PURE__ */ jsxDEV4(StatCard2, {
1392
- label: "Total Projects",
1393
- value: stats.total.toString()
1394
- }, undefined, false, undefined, this),
1395
- /* @__PURE__ */ jsxDEV4(StatCard2, {
1396
- label: "Active",
1397
- value: stats.activeCount.toString()
1398
- }, undefined, false, undefined, this),
1399
- /* @__PURE__ */ jsxDEV4(StatCard2, {
1400
- label: "Draft",
1401
- value: stats.draftCount.toString()
1402
- }, undefined, false, undefined, this)
1403
- ]
1404
- }, undefined, true, undefined, this),
1405
- /* @__PURE__ */ jsxDEV4("div", {
1406
- className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
1407
- children: data.items.map((project) => /* @__PURE__ */ jsxDEV4(EntityCard2, {
1408
- cardTitle: project.name,
1409
- cardSubtitle: project.tier,
1410
- meta: /* @__PURE__ */ jsxDEV4("p", {
1411
- className: "text-muted-foreground text-sm",
1412
- children: project.description
1413
- }, undefined, false, undefined, this),
1414
- chips: /* @__PURE__ */ jsxDEV4(StatusChip2, {
1415
- tone: getStatusTone2(project.status),
1416
- label: project.status
1417
- }, undefined, false, undefined, this),
1418
- footer: /* @__PURE__ */ jsxDEV4("span", {
1419
- className: "text-muted-foreground text-xs",
1420
- children: project.updatedAt.toLocaleDateString()
1421
- }, undefined, false, undefined, this),
1422
- onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
1423
- }, project.id, false, undefined, this))
1424
- }, undefined, false, undefined, this)
1425
- ]
1426
- }, undefined, true, undefined, this);
1427
- }
1428
-
1429
1650
  // src/ui/SaasSettingsPanel.tsx
1430
- import { useState as useState6 } from "react";
1431
1651
  import { Button as Button5 } from "@contractspec/lib.design-system";
1432
- import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
1652
+ import { useState as useState6 } from "react";
1653
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1433
1654
  "use client";
1434
1655
  function SaasSettingsPanel() {
1435
1656
  const [orgName, setOrgName] = useState6("Demo Organization");
1436
1657
  const [timezone, setTimezone] = useState6("UTC");
1437
- return /* @__PURE__ */ jsxDEV5("div", {
1658
+ return /* @__PURE__ */ jsxDEV6("div", {
1438
1659
  className: "space-y-6",
1439
1660
  children: [
1440
- /* @__PURE__ */ jsxDEV5("div", {
1441
- className: "border-border bg-card rounded-xl border p-6",
1661
+ /* @__PURE__ */ jsxDEV6("div", {
1662
+ className: "rounded-xl border border-border bg-card p-6",
1442
1663
  children: [
1443
- /* @__PURE__ */ jsxDEV5("h3", {
1444
- className: "mb-4 text-lg font-semibold",
1664
+ /* @__PURE__ */ jsxDEV6("h3", {
1665
+ className: "mb-4 font-semibold text-lg",
1445
1666
  children: "Organization Settings"
1446
1667
  }, undefined, false, undefined, this),
1447
- /* @__PURE__ */ jsxDEV5("div", {
1668
+ /* @__PURE__ */ jsxDEV6("div", {
1448
1669
  className: "space-y-4",
1449
1670
  children: [
1450
- /* @__PURE__ */ jsxDEV5("div", {
1671
+ /* @__PURE__ */ jsxDEV6("div", {
1451
1672
  children: [
1452
- /* @__PURE__ */ jsxDEV5("label", {
1673
+ /* @__PURE__ */ jsxDEV6("label", {
1453
1674
  htmlFor: "setting-org-name",
1454
- className: "block text-sm font-medium",
1675
+ className: "block font-medium text-sm",
1455
1676
  children: "Organization Name"
1456
1677
  }, undefined, false, undefined, this),
1457
- /* @__PURE__ */ jsxDEV5("input", {
1678
+ /* @__PURE__ */ jsxDEV6("input", {
1458
1679
  id: "setting-org-name",
1459
1680
  type: "text",
1460
1681
  value: orgName,
1461
1682
  onChange: (e) => setOrgName(e.target.value),
1462
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
1683
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2"
1463
1684
  }, undefined, false, undefined, this)
1464
1685
  ]
1465
1686
  }, undefined, true, undefined, this),
1466
- /* @__PURE__ */ jsxDEV5("div", {
1687
+ /* @__PURE__ */ jsxDEV6("div", {
1467
1688
  children: [
1468
- /* @__PURE__ */ jsxDEV5("label", {
1689
+ /* @__PURE__ */ jsxDEV6("label", {
1469
1690
  htmlFor: "setting-timezone",
1470
- className: "block text-sm font-medium",
1691
+ className: "block font-medium text-sm",
1471
1692
  children: "Default Timezone"
1472
1693
  }, undefined, false, undefined, this),
1473
- /* @__PURE__ */ jsxDEV5("select", {
1694
+ /* @__PURE__ */ jsxDEV6("select", {
1474
1695
  id: "setting-timezone",
1475
1696
  value: timezone,
1476
1697
  onChange: (e) => setTimezone(e.target.value),
1477
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2",
1698
+ className: "mt-1 block w-full rounded-md border border-input bg-background px-3 py-2",
1478
1699
  children: [
1479
- /* @__PURE__ */ jsxDEV5("option", {
1700
+ /* @__PURE__ */ jsxDEV6("option", {
1480
1701
  value: "UTC",
1481
1702
  children: "UTC"
1482
1703
  }, undefined, false, undefined, this),
1483
- /* @__PURE__ */ jsxDEV5("option", {
1704
+ /* @__PURE__ */ jsxDEV6("option", {
1484
1705
  value: "America/New_York",
1485
1706
  children: "America/New_York"
1486
1707
  }, undefined, false, undefined, this),
1487
- /* @__PURE__ */ jsxDEV5("option", {
1708
+ /* @__PURE__ */ jsxDEV6("option", {
1488
1709
  value: "Europe/London",
1489
1710
  children: "Europe/London"
1490
1711
  }, undefined, false, undefined, this),
1491
- /* @__PURE__ */ jsxDEV5("option", {
1712
+ /* @__PURE__ */ jsxDEV6("option", {
1492
1713
  value: "Asia/Tokyo",
1493
1714
  children: "Asia/Tokyo"
1494
1715
  }, undefined, false, undefined, this)
@@ -1498,37 +1719,37 @@ function SaasSettingsPanel() {
1498
1719
  }, undefined, true, undefined, this)
1499
1720
  ]
1500
1721
  }, undefined, true, undefined, this),
1501
- /* @__PURE__ */ jsxDEV5("div", {
1722
+ /* @__PURE__ */ jsxDEV6("div", {
1502
1723
  className: "mt-6",
1503
- children: /* @__PURE__ */ jsxDEV5(Button5, {
1724
+ children: /* @__PURE__ */ jsxDEV6(Button5, {
1504
1725
  variant: "default",
1505
1726
  children: "Save Changes"
1506
1727
  }, undefined, false, undefined, this)
1507
1728
  }, undefined, false, undefined, this)
1508
1729
  ]
1509
1730
  }, undefined, true, undefined, this),
1510
- /* @__PURE__ */ jsxDEV5("div", {
1511
- className: "border-border bg-card rounded-xl border p-6",
1731
+ /* @__PURE__ */ jsxDEV6("div", {
1732
+ className: "rounded-xl border border-border bg-card p-6",
1512
1733
  children: [
1513
- /* @__PURE__ */ jsxDEV5("h3", {
1514
- className: "mb-4 text-lg font-semibold",
1734
+ /* @__PURE__ */ jsxDEV6("h3", {
1735
+ className: "mb-4 font-semibold text-lg",
1515
1736
  children: "Notifications"
1516
1737
  }, undefined, false, undefined, this),
1517
- /* @__PURE__ */ jsxDEV5("div", {
1738
+ /* @__PURE__ */ jsxDEV6("div", {
1518
1739
  className: "space-y-3",
1519
1740
  children: [
1520
1741
  { label: "Email notifications", defaultChecked: true },
1521
1742
  { label: "Usage alerts", defaultChecked: true },
1522
1743
  { label: "Weekly digest", defaultChecked: false }
1523
- ].map((item) => /* @__PURE__ */ jsxDEV5("label", {
1744
+ ].map((item) => /* @__PURE__ */ jsxDEV6("label", {
1524
1745
  className: "flex items-center gap-3",
1525
1746
  children: [
1526
- /* @__PURE__ */ jsxDEV5("input", {
1747
+ /* @__PURE__ */ jsxDEV6("input", {
1527
1748
  type: "checkbox",
1528
1749
  defaultChecked: item.defaultChecked,
1529
- className: "border-input h-4 w-4 rounded"
1750
+ className: "h-4 w-4 rounded border-input"
1530
1751
  }, undefined, false, undefined, this),
1531
- /* @__PURE__ */ jsxDEV5("span", {
1752
+ /* @__PURE__ */ jsxDEV6("span", {
1532
1753
  className: "text-sm",
1533
1754
  children: item.label
1534
1755
  }, undefined, false, undefined, this)
@@ -1537,26 +1758,26 @@ function SaasSettingsPanel() {
1537
1758
  }, undefined, false, undefined, this)
1538
1759
  ]
1539
1760
  }, undefined, true, undefined, this),
1540
- /* @__PURE__ */ jsxDEV5("div", {
1761
+ /* @__PURE__ */ jsxDEV6("div", {
1541
1762
  className: "rounded-xl border border-red-200 bg-red-50 p-6 dark:border-red-900 dark:bg-red-950/20",
1542
1763
  children: [
1543
- /* @__PURE__ */ jsxDEV5("h3", {
1544
- className: "mb-2 text-lg font-semibold text-red-700 dark:text-red-400",
1764
+ /* @__PURE__ */ jsxDEV6("h3", {
1765
+ className: "mb-2 font-semibold text-lg text-red-700 dark:text-red-400",
1545
1766
  children: "Danger Zone"
1546
1767
  }, undefined, false, undefined, this),
1547
- /* @__PURE__ */ jsxDEV5("p", {
1548
- className: "mb-4 text-sm text-red-600 dark:text-red-300",
1768
+ /* @__PURE__ */ jsxDEV6("p", {
1769
+ className: "mb-4 text-red-600 text-sm dark:text-red-300",
1549
1770
  children: "These actions are irreversible. Please proceed with caution."
1550
1771
  }, undefined, false, undefined, this),
1551
- /* @__PURE__ */ jsxDEV5("div", {
1772
+ /* @__PURE__ */ jsxDEV6("div", {
1552
1773
  className: "flex gap-3",
1553
1774
  children: [
1554
- /* @__PURE__ */ jsxDEV5(Button5, {
1775
+ /* @__PURE__ */ jsxDEV6(Button5, {
1555
1776
  variant: "secondary",
1556
1777
  size: "sm",
1557
1778
  children: "Export Data"
1558
1779
  }, undefined, false, undefined, this),
1559
- /* @__PURE__ */ jsxDEV5(Button5, {
1780
+ /* @__PURE__ */ jsxDEV6(Button5, {
1560
1781
  variant: "secondary",
1561
1782
  size: "sm",
1562
1783
  children: "Delete Organization"
@@ -1568,228 +1789,6 @@ function SaasSettingsPanel() {
1568
1789
  ]
1569
1790
  }, undefined, true, undefined, this);
1570
1791
  }
1571
- // src/ui/hooks/index.ts
1572
- "use client";
1573
-
1574
- // src/ui/renderers/project-list.renderer.tsx
1575
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1576
- var projectListReactRenderer = {
1577
- target: "react",
1578
- render: async (desc, _ctx) => {
1579
- if (desc.source.type !== "component") {
1580
- throw new Error("Invalid source type");
1581
- }
1582
- if (desc.source.componentKey !== "SaasProjectListView") {
1583
- throw new Error(`Unknown component: ${desc.source.componentKey}`);
1584
- }
1585
- return /* @__PURE__ */ jsxDEV6(SaasProjectList, {}, undefined, false, undefined, this);
1586
- }
1587
- };
1588
-
1589
- // src/ui/renderers/project-list.markdown.ts
1590
- var projectListMarkdownRenderer = {
1591
- target: "markdown",
1592
- render: async (desc, _ctx) => {
1593
- if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
1594
- throw new Error("projectListMarkdownRenderer: not ProjectListView");
1595
- }
1596
- const data = await mockListProjectsHandler({
1597
- limit: 20,
1598
- offset: 0
1599
- });
1600
- const items = data.projects ?? data.items ?? [];
1601
- const lines = [
1602
- "# Projects",
1603
- "",
1604
- `**Total**: ${data.total} projects`,
1605
- ""
1606
- ];
1607
- if (items.length === 0) {
1608
- lines.push("_No projects found._");
1609
- } else {
1610
- lines.push("| Status | Project | Description |");
1611
- lines.push("|--------|---------|-------------|");
1612
- for (const project of items) {
1613
- const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
1614
- lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
1615
- }
1616
- }
1617
- return {
1618
- mimeType: "text/markdown",
1619
- body: lines.join(`
1620
- `)
1621
- };
1622
- }
1623
- };
1624
- var saasDashboardMarkdownRenderer = {
1625
- target: "markdown",
1626
- render: async (desc, _ctx) => {
1627
- if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
1628
- throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
1629
- }
1630
- const [projectsData, subscription] = await Promise.all([
1631
- mockListProjectsHandler({ limit: 50 }),
1632
- mockGetSubscriptionHandler()
1633
- ]);
1634
- const projects = projectsData.projects ?? [];
1635
- const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
1636
- const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
1637
- const lines = [
1638
- "# SaaS Dashboard",
1639
- "",
1640
- "> Organization overview and usage summary",
1641
- "",
1642
- "## Summary",
1643
- "",
1644
- "| Metric | Value |",
1645
- "|--------|-------|",
1646
- `| Total Projects | ${projectsData.total} |`,
1647
- `| Active Projects | ${activeProjects} |`,
1648
- `| Archived Projects | ${archivedProjects} |`,
1649
- `| Subscription Plan | ${subscription.planName} |`,
1650
- `| Subscription Status | ${subscription.status} |`,
1651
- "",
1652
- "## Projects",
1653
- ""
1654
- ];
1655
- if (projects.length === 0) {
1656
- lines.push("_No projects yet._");
1657
- } else {
1658
- lines.push("| Status | Project | Description |");
1659
- lines.push("|--------|---------|-------------|");
1660
- for (const project of projects.slice(0, 10)) {
1661
- const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
1662
- lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
1663
- }
1664
- if (projects.length > 10) {
1665
- lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
1666
- }
1667
- }
1668
- lines.push("");
1669
- lines.push("## Subscription");
1670
- lines.push("");
1671
- lines.push(`- **Plan**: ${subscription.planName}`);
1672
- lines.push(`- **Status**: ${subscription.status}`);
1673
- if (subscription.currentPeriodEnd) {
1674
- lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
1675
- }
1676
- return {
1677
- mimeType: "text/markdown",
1678
- body: lines.join(`
1679
- `)
1680
- };
1681
- }
1682
- };
1683
- var saasBillingMarkdownRenderer = {
1684
- target: "markdown",
1685
- render: async (desc, _ctx) => {
1686
- if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
1687
- throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
1688
- }
1689
- const subscription = await mockGetSubscriptionHandler();
1690
- const lines = [
1691
- "# Billing & Subscription",
1692
- "",
1693
- "> Current subscription details and billing information",
1694
- "",
1695
- "## Subscription Details",
1696
- "",
1697
- "| Property | Value |",
1698
- "|----------|-------|",
1699
- `| Plan | ${subscription.planName} |`,
1700
- `| Status | ${subscription.status} |`,
1701
- `| ID | ${subscription.id} |`,
1702
- `| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
1703
- `| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
1704
- ];
1705
- lines.push("");
1706
- lines.push("## Plan Limits");
1707
- lines.push("");
1708
- lines.push(`- **Projects**: ${subscription.limits.projects}`);
1709
- lines.push(`- **Users**: ${subscription.limits.users}`);
1710
- lines.push("");
1711
- lines.push("## Plan Features");
1712
- lines.push("");
1713
- if (subscription.planName.toLowerCase().includes("free")) {
1714
- lines.push("- ✅ Up to 3 projects");
1715
- lines.push("- ✅ Basic support");
1716
- lines.push("- ❌ Priority support");
1717
- lines.push("- ❌ Advanced analytics");
1718
- } else if (subscription.planName.toLowerCase().includes("pro")) {
1719
- lines.push("- ✅ Unlimited projects");
1720
- lines.push("- ✅ Priority support");
1721
- lines.push("- ✅ Advanced analytics");
1722
- lines.push("- ❌ Custom integrations");
1723
- } else {
1724
- lines.push("- ✅ Unlimited projects");
1725
- lines.push("- ✅ Priority support");
1726
- lines.push("- ✅ Advanced analytics");
1727
- lines.push("- ✅ Custom integrations");
1728
- lines.push("- ✅ Dedicated support");
1729
- }
1730
- return {
1731
- mimeType: "text/markdown",
1732
- body: lines.join(`
1733
- `)
1734
- };
1735
- }
1736
- };
1737
- // src/ui/overlays/demo-overlays.ts
1738
- var saasFreeUserOverlay = {
1739
- overlayId: "saas-boilerplate.free-tier",
1740
- version: "1.0.0",
1741
- description: "Shows limitations for free tier users",
1742
- appliesTo: {
1743
- feature: "saas-boilerplate",
1744
- tier: "free"
1745
- },
1746
- modifications: [
1747
- {
1748
- type: "setLimit",
1749
- field: "projects",
1750
- max: 3,
1751
- message: "Upgrade to create more projects"
1752
- },
1753
- { type: "hideField", field: "advancedSettings", reason: "Pro feature" },
1754
- {
1755
- type: "addBadge",
1756
- position: "header",
1757
- label: "Free Plan",
1758
- variant: "default"
1759
- }
1760
- ]
1761
- };
1762
- var saasDemoOverlay = {
1763
- overlayId: "saas-boilerplate.demo-user",
1764
- version: "1.0.0",
1765
- description: "Demo mode for SaaS boilerplate",
1766
- appliesTo: {
1767
- feature: "saas-boilerplate",
1768
- role: "demo"
1769
- },
1770
- modifications: [
1771
- {
1772
- type: "hideField",
1773
- field: "billingSection",
1774
- reason: "Demo users cannot access billing"
1775
- },
1776
- {
1777
- type: "hideField",
1778
- field: "deleteAccount",
1779
- reason: "Not available in demo"
1780
- },
1781
- {
1782
- type: "addBadge",
1783
- position: "header",
1784
- label: "Demo Mode",
1785
- variant: "warning"
1786
- }
1787
- ]
1788
- };
1789
- var saasOverlays = [
1790
- saasFreeUserOverlay,
1791
- saasDemoOverlay
1792
- ];
1793
1792
  export {
1794
1793
  useProjectMutations,
1795
1794
  useProjectList,