@contractspec/example.saas-boilerplate 1.56.1 → 1.58.0

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 (284) hide show
  1. package/.turbo/turbo-build.log +160 -188
  2. package/.turbo/turbo-prebuild.log +1 -0
  3. package/CHANGELOG.md +45 -0
  4. package/dist/billing/billing.entity.d.ts +40 -45
  5. package/dist/billing/billing.entity.d.ts.map +1 -1
  6. package/dist/billing/billing.entity.js +110 -118
  7. package/dist/billing/billing.enum.d.ts +2 -8
  8. package/dist/billing/billing.enum.d.ts.map +1 -1
  9. package/dist/billing/billing.enum.js +17 -24
  10. package/dist/billing/billing.event.d.ts +67 -73
  11. package/dist/billing/billing.event.d.ts.map +1 -1
  12. package/dist/billing/billing.event.js +84 -146
  13. package/dist/billing/billing.handler.d.ts +59 -62
  14. package/dist/billing/billing.handler.d.ts.map +1 -1
  15. package/dist/billing/billing.handler.js +140 -49
  16. package/dist/billing/billing.operations.d.ts +138 -144
  17. package/dist/billing/billing.operations.d.ts.map +1 -1
  18. package/dist/billing/billing.operations.js +273 -175
  19. package/dist/billing/billing.presentation.d.ts +2 -7
  20. package/dist/billing/billing.presentation.d.ts.map +1 -1
  21. package/dist/billing/billing.presentation.js +51 -57
  22. package/dist/billing/billing.schema.d.ts +159 -164
  23. package/dist/billing/billing.schema.d.ts.map +1 -1
  24. package/dist/billing/billing.schema.js +112 -204
  25. package/dist/billing/index.d.ts +11 -8
  26. package/dist/billing/index.d.ts.map +1 -0
  27. package/dist/billing/index.js +689 -9
  28. package/dist/browser/billing/billing.entity.js +113 -0
  29. package/dist/browser/billing/billing.enum.js +19 -0
  30. package/dist/browser/billing/billing.event.js +90 -0
  31. package/dist/browser/billing/billing.handler.js +148 -0
  32. package/dist/browser/billing/billing.operations.js +278 -0
  33. package/dist/browser/billing/billing.presentation.js +52 -0
  34. package/dist/browser/billing/billing.schema.js +121 -0
  35. package/dist/browser/billing/index.js +688 -0
  36. package/dist/browser/dashboard/dashboard.presentation.js +52 -0
  37. package/dist/browser/dashboard/index.js +52 -0
  38. package/dist/browser/docs/index.js +93 -0
  39. package/dist/browser/docs/saas-boilerplate.docblock.js +93 -0
  40. package/dist/browser/example.js +39 -0
  41. package/dist/browser/handlers/index.js +358 -0
  42. package/dist/browser/handlers/saas.handlers.js +134 -0
  43. package/dist/browser/index.js +3340 -0
  44. package/dist/browser/presentations/index.js +290 -0
  45. package/dist/browser/project/index.js +790 -0
  46. package/dist/browser/project/project.entity.js +77 -0
  47. package/dist/browser/project/project.enum.js +18 -0
  48. package/dist/browser/project/project.event.js +103 -0
  49. package/dist/browser/project/project.handler.js +178 -0
  50. package/dist/browser/project/project.operations.js +372 -0
  51. package/dist/browser/project/project.presentation.js +177 -0
  52. package/dist/browser/project/project.schema.js +134 -0
  53. package/dist/browser/saas-boilerplate.feature.js +88 -0
  54. package/dist/browser/seeders/index.js +20 -0
  55. package/dist/browser/settings/index.js +75 -0
  56. package/dist/browser/settings/settings.entity.js +74 -0
  57. package/dist/browser/settings/settings.enum.js +11 -0
  58. package/dist/browser/shared/mock-data.js +104 -0
  59. package/dist/browser/shared/overlay-types.js +0 -0
  60. package/dist/browser/tests/operations.test-spec.js +112 -0
  61. package/dist/browser/ui/SaasDashboard.js +988 -0
  62. package/dist/browser/ui/SaasProjectList.js +162 -0
  63. package/dist/browser/ui/SaasSettingsPanel.js +145 -0
  64. package/dist/browser/ui/hooks/index.js +159 -0
  65. package/dist/browser/ui/hooks/useProjectList.js +66 -0
  66. package/dist/browser/ui/hooks/useProjectMutations.js +91 -0
  67. package/dist/browser/ui/index.js +1808 -0
  68. package/dist/browser/ui/modals/CreateProjectModal.js +153 -0
  69. package/dist/browser/ui/modals/ProjectActionsModal.js +335 -0
  70. package/dist/browser/ui/modals/index.js +487 -0
  71. package/dist/browser/ui/overlays/demo-overlays.js +61 -0
  72. package/dist/browser/ui/overlays/index.js +61 -0
  73. package/dist/browser/ui/renderers/index.js +675 -0
  74. package/dist/browser/ui/renderers/project-list.markdown.js +499 -0
  75. package/dist/browser/ui/renderers/project-list.renderer.js +177 -0
  76. package/dist/dashboard/dashboard.presentation.d.ts +2 -7
  77. package/dist/dashboard/dashboard.presentation.d.ts.map +1 -1
  78. package/dist/dashboard/dashboard.presentation.js +51 -53
  79. package/dist/dashboard/index.d.ts +5 -2
  80. package/dist/dashboard/index.d.ts.map +1 -0
  81. package/dist/dashboard/index.js +53 -3
  82. package/dist/docs/index.d.ts +2 -1
  83. package/dist/docs/index.d.ts.map +1 -0
  84. package/dist/docs/index.js +94 -1
  85. package/dist/docs/saas-boilerplate.docblock.d.ts +2 -1
  86. package/dist/docs/saas-boilerplate.docblock.d.ts.map +1 -0
  87. package/dist/docs/saas-boilerplate.docblock.js +45 -51
  88. package/dist/example.d.ts +2 -6
  89. package/dist/example.d.ts.map +1 -1
  90. package/dist/example.js +37 -50
  91. package/dist/handlers/index.d.ts +7 -4
  92. package/dist/handlers/index.d.ts.map +1 -0
  93. package/dist/handlers/index.js +358 -4
  94. package/dist/handlers/saas.handlers.d.ts +60 -60
  95. package/dist/handlers/saas.handlers.d.ts.map +1 -1
  96. package/dist/handlers/saas.handlers.js +127 -140
  97. package/dist/index.d.ts +15 -45
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/index.js +3335 -75
  100. package/dist/node/billing/billing.entity.js +113 -0
  101. package/dist/node/billing/billing.enum.js +19 -0
  102. package/dist/node/billing/billing.event.js +90 -0
  103. package/dist/node/billing/billing.handler.js +148 -0
  104. package/dist/node/billing/billing.operations.js +278 -0
  105. package/dist/node/billing/billing.presentation.js +52 -0
  106. package/dist/node/billing/billing.schema.js +121 -0
  107. package/dist/node/billing/index.js +688 -0
  108. package/dist/node/dashboard/dashboard.presentation.js +52 -0
  109. package/dist/node/dashboard/index.js +52 -0
  110. package/dist/node/docs/index.js +93 -0
  111. package/dist/node/docs/saas-boilerplate.docblock.js +93 -0
  112. package/dist/node/example.js +39 -0
  113. package/dist/node/handlers/index.js +358 -0
  114. package/dist/node/handlers/saas.handlers.js +134 -0
  115. package/dist/node/index.js +3340 -0
  116. package/dist/node/presentations/index.js +290 -0
  117. package/dist/node/project/index.js +790 -0
  118. package/dist/node/project/project.entity.js +77 -0
  119. package/dist/node/project/project.enum.js +18 -0
  120. package/dist/node/project/project.event.js +103 -0
  121. package/dist/node/project/project.handler.js +178 -0
  122. package/dist/node/project/project.operations.js +372 -0
  123. package/dist/node/project/project.presentation.js +177 -0
  124. package/dist/node/project/project.schema.js +134 -0
  125. package/dist/node/saas-boilerplate.feature.js +88 -0
  126. package/dist/node/seeders/index.js +20 -0
  127. package/dist/node/settings/index.js +75 -0
  128. package/dist/node/settings/settings.entity.js +74 -0
  129. package/dist/node/settings/settings.enum.js +11 -0
  130. package/dist/node/shared/mock-data.js +104 -0
  131. package/dist/node/shared/overlay-types.js +0 -0
  132. package/dist/node/tests/operations.test-spec.js +112 -0
  133. package/dist/node/ui/SaasDashboard.js +988 -0
  134. package/dist/node/ui/SaasProjectList.js +162 -0
  135. package/dist/node/ui/SaasSettingsPanel.js +145 -0
  136. package/dist/node/ui/hooks/index.js +159 -0
  137. package/dist/node/ui/hooks/useProjectList.js +66 -0
  138. package/dist/node/ui/hooks/useProjectMutations.js +91 -0
  139. package/dist/node/ui/index.js +1808 -0
  140. package/dist/node/ui/modals/CreateProjectModal.js +153 -0
  141. package/dist/node/ui/modals/ProjectActionsModal.js +335 -0
  142. package/dist/node/ui/modals/index.js +487 -0
  143. package/dist/node/ui/overlays/demo-overlays.js +61 -0
  144. package/dist/node/ui/overlays/index.js +61 -0
  145. package/dist/node/ui/renderers/index.js +675 -0
  146. package/dist/node/ui/renderers/project-list.markdown.js +499 -0
  147. package/dist/node/ui/renderers/project-list.renderer.js +177 -0
  148. package/dist/presentations/index.d.ts +13 -15
  149. package/dist/presentations/index.d.ts.map +1 -1
  150. package/dist/presentations/index.js +289 -15
  151. package/dist/project/index.d.ts +11 -8
  152. package/dist/project/index.d.ts.map +1 -0
  153. package/dist/project/index.js +791 -9
  154. package/dist/project/project.entity.d.ts +23 -28
  155. package/dist/project/project.entity.d.ts.map +1 -1
  156. package/dist/project/project.entity.js +75 -82
  157. package/dist/project/project.enum.d.ts +2 -8
  158. package/dist/project/project.enum.d.ts.map +1 -1
  159. package/dist/project/project.enum.js +16 -23
  160. package/dist/project/project.event.d.ts +69 -75
  161. package/dist/project/project.event.d.ts.map +1 -1
  162. package/dist/project/project.event.js +95 -156
  163. package/dist/project/project.handler.d.ts +44 -47
  164. package/dist/project/project.handler.d.ts.map +1 -1
  165. package/dist/project/project.handler.js +168 -71
  166. package/dist/project/project.operations.d.ts +341 -347
  167. package/dist/project/project.operations.d.ts.map +1 -1
  168. package/dist/project/project.operations.js +366 -253
  169. package/dist/project/project.presentation.d.ts +2 -7
  170. package/dist/project/project.presentation.d.ts.map +1 -1
  171. package/dist/project/project.presentation.js +174 -61
  172. package/dist/project/project.schema.d.ts +191 -196
  173. package/dist/project/project.schema.d.ts.map +1 -1
  174. package/dist/project/project.schema.js +125 -205
  175. package/dist/saas-boilerplate.feature.d.ts +1 -7
  176. package/dist/saas-boilerplate.feature.d.ts.map +1 -1
  177. package/dist/saas-boilerplate.feature.js +87 -206
  178. package/dist/seeders/index.d.ts +4 -8
  179. package/dist/seeders/index.d.ts.map +1 -1
  180. package/dist/seeders/index.js +18 -16
  181. package/dist/settings/index.d.ts +6 -3
  182. package/dist/settings/index.d.ts.map +1 -0
  183. package/dist/settings/index.js +75 -3
  184. package/dist/settings/settings.entity.d.ts +23 -28
  185. package/dist/settings/settings.entity.d.ts.map +1 -1
  186. package/dist/settings/settings.entity.js +72 -75
  187. package/dist/settings/settings.enum.d.ts +1 -6
  188. package/dist/settings/settings.enum.d.ts.map +1 -1
  189. package/dist/settings/settings.enum.js +10 -19
  190. package/dist/shared/mock-data.d.ts +74 -77
  191. package/dist/shared/mock-data.d.ts.map +1 -1
  192. package/dist/shared/mock-data.js +102 -135
  193. package/dist/shared/overlay-types.d.ts +25 -28
  194. package/dist/shared/overlay-types.d.ts.map +1 -1
  195. package/dist/shared/overlay-types.js +1 -0
  196. package/dist/tests/operations.test-spec.d.ts +4 -9
  197. package/dist/tests/operations.test-spec.d.ts.map +1 -1
  198. package/dist/tests/operations.test-spec.js +108 -118
  199. package/dist/ui/SaasDashboard.d.ts +1 -6
  200. package/dist/ui/SaasDashboard.d.ts.map +1 -1
  201. package/dist/ui/SaasDashboard.js +977 -286
  202. package/dist/ui/SaasProjectList.d.ts +4 -11
  203. package/dist/ui/SaasProjectList.d.ts.map +1 -1
  204. package/dist/ui/SaasProjectList.js +159 -72
  205. package/dist/ui/SaasSettingsPanel.d.ts +1 -6
  206. package/dist/ui/SaasSettingsPanel.d.ts.map +1 -1
  207. package/dist/ui/SaasSettingsPanel.js +142 -134
  208. package/dist/ui/hooks/index.d.ts +3 -3
  209. package/dist/ui/hooks/index.d.ts.map +1 -0
  210. package/dist/ui/hooks/index.js +158 -4
  211. package/dist/ui/hooks/useProjectList.d.ts +26 -30
  212. package/dist/ui/hooks/useProjectList.d.ts.map +1 -1
  213. package/dist/ui/hooks/useProjectList.js +63 -71
  214. package/dist/ui/hooks/useProjectMutations.d.ts +20 -24
  215. package/dist/ui/hooks/useProjectMutations.d.ts.map +1 -1
  216. package/dist/ui/hooks/useProjectMutations.js +88 -142
  217. package/dist/ui/index.d.ts +8 -14
  218. package/dist/ui/index.d.ts.map +1 -0
  219. package/dist/ui/index.js +1809 -15
  220. package/dist/ui/modals/CreateProjectModal.d.ts +10 -19
  221. package/dist/ui/modals/CreateProjectModal.d.ts.map +1 -1
  222. package/dist/ui/modals/CreateProjectModal.js +150 -135
  223. package/dist/ui/modals/ProjectActionsModal.d.ts +20 -33
  224. package/dist/ui/modals/ProjectActionsModal.d.ts.map +1 -1
  225. package/dist/ui/modals/ProjectActionsModal.js +333 -289
  226. package/dist/ui/modals/index.d.ts +3 -3
  227. package/dist/ui/modals/index.d.ts.map +1 -0
  228. package/dist/ui/modals/index.js +487 -3
  229. package/dist/ui/overlays/demo-overlays.d.ts +10 -9
  230. package/dist/ui/overlays/demo-overlays.d.ts.map +1 -1
  231. package/dist/ui/overlays/demo-overlays.js +60 -68
  232. package/dist/ui/overlays/index.d.ts +2 -2
  233. package/dist/ui/overlays/index.d.ts.map +1 -0
  234. package/dist/ui/overlays/index.js +62 -3
  235. package/dist/ui/renderers/index.d.ts +3 -3
  236. package/dist/ui/renderers/index.d.ts.map +1 -0
  237. package/dist/ui/renderers/index.js +675 -3
  238. package/dist/ui/renderers/project-list.markdown.d.ts +15 -15
  239. package/dist/ui/renderers/project-list.markdown.d.ts.map +1 -1
  240. package/dist/ui/renderers/project-list.markdown.js +496 -144
  241. package/dist/ui/renderers/project-list.renderer.d.ts +6 -8
  242. package/dist/ui/renderers/project-list.renderer.d.ts.map +1 -1
  243. package/dist/ui/renderers/project-list.renderer.js +176 -15
  244. package/package.json +509 -99
  245. package/src/ui/renderers/project-list.markdown.ts +1 -1
  246. package/tsdown.config.js +1 -2
  247. package/.turbo/turbo-build$colon$bundle.log +0 -188
  248. package/dist/billing/billing.entity.js.map +0 -1
  249. package/dist/billing/billing.enum.js.map +0 -1
  250. package/dist/billing/billing.event.js.map +0 -1
  251. package/dist/billing/billing.handler.js.map +0 -1
  252. package/dist/billing/billing.operations.js.map +0 -1
  253. package/dist/billing/billing.presentation.js.map +0 -1
  254. package/dist/billing/billing.schema.js.map +0 -1
  255. package/dist/dashboard/dashboard.presentation.js.map +0 -1
  256. package/dist/docs/saas-boilerplate.docblock.js.map +0 -1
  257. package/dist/example.js.map +0 -1
  258. package/dist/handlers/saas.handlers.js.map +0 -1
  259. package/dist/index.js.map +0 -1
  260. package/dist/presentations/index.js.map +0 -1
  261. package/dist/project/project.entity.js.map +0 -1
  262. package/dist/project/project.enum.js.map +0 -1
  263. package/dist/project/project.event.js.map +0 -1
  264. package/dist/project/project.handler.js.map +0 -1
  265. package/dist/project/project.operations.js.map +0 -1
  266. package/dist/project/project.presentation.js.map +0 -1
  267. package/dist/project/project.schema.js.map +0 -1
  268. package/dist/saas-boilerplate.feature.js.map +0 -1
  269. package/dist/seeders/index.js.map +0 -1
  270. package/dist/settings/settings.entity.js.map +0 -1
  271. package/dist/settings/settings.enum.js.map +0 -1
  272. package/dist/shared/mock-data.js.map +0 -1
  273. package/dist/tests/operations.test-spec.js.map +0 -1
  274. package/dist/ui/SaasDashboard.js.map +0 -1
  275. package/dist/ui/SaasProjectList.js.map +0 -1
  276. package/dist/ui/SaasSettingsPanel.js.map +0 -1
  277. package/dist/ui/hooks/useProjectList.js.map +0 -1
  278. package/dist/ui/hooks/useProjectMutations.js.map +0 -1
  279. package/dist/ui/modals/CreateProjectModal.js.map +0 -1
  280. package/dist/ui/modals/ProjectActionsModal.js.map +0 -1
  281. package/dist/ui/overlays/demo-overlays.js.map +0 -1
  282. package/dist/ui/renderers/project-list.markdown.js.map +0 -1
  283. package/dist/ui/renderers/project-list.renderer.js.map +0 -1
  284. package/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,499 @@
1
+ // src/shared/mock-data.ts
2
+ var MOCK_PROJECTS = [
3
+ {
4
+ id: "proj-1",
5
+ name: "Marketing Website",
6
+ description: "Main company website redesign project",
7
+ slug: "marketing-website",
8
+ organizationId: "demo-org",
9
+ createdBy: "user-1",
10
+ status: "ACTIVE",
11
+ isPublic: false,
12
+ tags: ["marketing", "website", "redesign"],
13
+ createdAt: new Date("2024-01-15T10:00:00Z"),
14
+ updatedAt: new Date("2024-03-20T14:30:00Z")
15
+ },
16
+ {
17
+ id: "proj-2",
18
+ name: "Mobile App v2",
19
+ description: "Next generation mobile application",
20
+ slug: "mobile-app-v2",
21
+ organizationId: "demo-org",
22
+ createdBy: "user-2",
23
+ status: "ACTIVE",
24
+ isPublic: false,
25
+ tags: ["mobile", "app", "v2"],
26
+ createdAt: new Date("2024-02-01T09:00:00Z"),
27
+ updatedAt: new Date("2024-04-05T11:15:00Z")
28
+ },
29
+ {
30
+ id: "proj-3",
31
+ name: "API Integration",
32
+ description: "Third-party API integration project",
33
+ slug: "api-integration",
34
+ organizationId: "demo-org",
35
+ createdBy: "user-1",
36
+ status: "DRAFT",
37
+ isPublic: false,
38
+ tags: ["api", "integration"],
39
+ createdAt: new Date("2024-03-10T08:00:00Z"),
40
+ updatedAt: new Date("2024-03-10T08:00:00Z")
41
+ },
42
+ {
43
+ id: "proj-4",
44
+ name: "Analytics Dashboard",
45
+ description: "Internal analytics and reporting dashboard",
46
+ slug: "analytics-dashboard",
47
+ organizationId: "demo-org",
48
+ createdBy: "user-3",
49
+ status: "ARCHIVED",
50
+ isPublic: true,
51
+ tags: ["analytics", "dashboard", "reporting"],
52
+ createdAt: new Date("2023-10-01T12:00:00Z"),
53
+ updatedAt: new Date("2024-02-28T16:45:00Z")
54
+ }
55
+ ];
56
+ var MOCK_SUBSCRIPTION = {
57
+ id: "sub-1",
58
+ organizationId: "demo-org",
59
+ planId: "pro",
60
+ planName: "Professional",
61
+ status: "ACTIVE",
62
+ currentPeriodStart: new Date("2024-04-01T00:00:00Z"),
63
+ currentPeriodEnd: new Date("2024-05-01T00:00:00Z"),
64
+ limits: {
65
+ projects: 25,
66
+ users: 10,
67
+ storage: 50,
68
+ apiCalls: 1e5
69
+ },
70
+ usage: {
71
+ projects: 4,
72
+ users: 5,
73
+ storage: 12.5,
74
+ apiCalls: 45230
75
+ }
76
+ };
77
+ var MOCK_USAGE_SUMMARY = {
78
+ organizationId: "demo-org",
79
+ period: "current_month",
80
+ apiCalls: {
81
+ total: 45230,
82
+ limit: 1e5,
83
+ percentUsed: 45.23
84
+ },
85
+ storage: {
86
+ totalGb: 12.5,
87
+ limitGb: 50,
88
+ percentUsed: 25
89
+ },
90
+ activeProjects: 4,
91
+ activeUsers: 5,
92
+ breakdown: [
93
+ { date: "2024-04-01", apiCalls: 3200, storageGb: 12.1 },
94
+ { date: "2024-04-02", apiCalls: 2800, storageGb: 12.2 },
95
+ { date: "2024-04-03", apiCalls: 4100, storageGb: 12.3 },
96
+ { date: "2024-04-04", apiCalls: 3600, storageGb: 12.4 },
97
+ { date: "2024-04-05", apiCalls: 3800, storageGb: 12.5 }
98
+ ]
99
+ };
100
+
101
+ // src/billing/billing.handler.ts
102
+ async function mockGetSubscriptionHandler() {
103
+ return MOCK_SUBSCRIPTION;
104
+ }
105
+ async function mockGetUsageSummaryHandler(input) {
106
+ return {
107
+ ...MOCK_USAGE_SUMMARY,
108
+ period: input.period ?? "current_month"
109
+ };
110
+ }
111
+ async function mockRecordUsageHandler(input) {
112
+ const currentUsage = MOCK_USAGE_SUMMARY.apiCalls.total;
113
+ const newTotal = currentUsage + input.quantity;
114
+ return {
115
+ recorded: true,
116
+ newTotal
117
+ };
118
+ }
119
+ async function mockCheckFeatureAccessHandler(input) {
120
+ const { feature } = input;
121
+ const featureMap = {
122
+ custom_domains: {
123
+ allowed: true
124
+ },
125
+ api_access: {
126
+ allowed: true,
127
+ currentUsage: MOCK_USAGE_SUMMARY.apiCalls.total,
128
+ limit: MOCK_USAGE_SUMMARY.apiCalls.limit
129
+ },
130
+ advanced_analytics: {
131
+ allowed: false,
132
+ reason: "FEATURE_NOT_INCLUDED"
133
+ },
134
+ unlimited_projects: {
135
+ allowed: false,
136
+ reason: "PLAN_LIMIT",
137
+ currentUsage: MOCK_SUBSCRIPTION.usage.projects,
138
+ limit: MOCK_SUBSCRIPTION.limits.projects
139
+ }
140
+ };
141
+ return featureMap[feature] ?? { allowed: true };
142
+ }
143
+
144
+ // src/project/project.handler.ts
145
+ async function mockListProjectsHandler(input) {
146
+ const { status, search, limit = 20, offset = 0 } = input;
147
+ let filtered = [...MOCK_PROJECTS];
148
+ if (status && status !== "all") {
149
+ filtered = filtered.filter((p) => p.status === status);
150
+ }
151
+ if (search) {
152
+ const q = search.toLowerCase();
153
+ filtered = filtered.filter((p) => p.name.toLowerCase().includes(q) || p.description?.toLowerCase().includes(q) || p.tags.some((t) => t.toLowerCase().includes(q)));
154
+ }
155
+ filtered.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
156
+ const total = filtered.length;
157
+ const projects = filtered.slice(offset, offset + limit);
158
+ return {
159
+ projects,
160
+ total
161
+ };
162
+ }
163
+ async function mockGetProjectHandler(input) {
164
+ const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
165
+ if (!project) {
166
+ throw new Error("NOT_FOUND");
167
+ }
168
+ return project;
169
+ }
170
+ async function mockCreateProjectHandler(input, context) {
171
+ if (input.slug) {
172
+ const exists = MOCK_PROJECTS.some((p) => p.slug === input.slug);
173
+ if (exists) {
174
+ throw new Error("SLUG_EXISTS");
175
+ }
176
+ }
177
+ const now = new Date;
178
+ return {
179
+ id: `proj-${Date.now()}`,
180
+ name: input.name,
181
+ description: input.description,
182
+ slug: input.slug ?? input.name.toLowerCase().replace(/\s+/g, "-"),
183
+ organizationId: context.organizationId,
184
+ createdBy: context.userId,
185
+ status: "DRAFT",
186
+ isPublic: input.isPublic ?? false,
187
+ tags: input.tags ?? [],
188
+ createdAt: now,
189
+ updatedAt: now
190
+ };
191
+ }
192
+ async function mockUpdateProjectHandler(input) {
193
+ const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
194
+ if (!project) {
195
+ throw new Error("NOT_FOUND");
196
+ }
197
+ return {
198
+ ...project,
199
+ name: input.name ?? project.name,
200
+ description: input.description ?? project.description,
201
+ slug: input.slug ?? project.slug,
202
+ isPublic: input.isPublic ?? project.isPublic,
203
+ tags: input.tags ?? project.tags,
204
+ status: input.status ?? project.status,
205
+ updatedAt: new Date
206
+ };
207
+ }
208
+ async function mockDeleteProjectHandler(input) {
209
+ const project = MOCK_PROJECTS.find((p) => p.id === input.projectId);
210
+ if (!project) {
211
+ throw new Error("NOT_FOUND");
212
+ }
213
+ return { success: true };
214
+ }
215
+
216
+ // src/handlers/saas.handlers.ts
217
+ import { web } from "@contractspec/lib.runtime-sandbox";
218
+ var { generateId } = web;
219
+ function rowToProject(row) {
220
+ return {
221
+ id: row.id,
222
+ projectId: row.projectId,
223
+ organizationId: row.organizationId,
224
+ name: row.name,
225
+ description: row.description ?? undefined,
226
+ status: row.status,
227
+ tier: row.tier,
228
+ createdAt: new Date(row.createdAt),
229
+ updatedAt: new Date(row.updatedAt)
230
+ };
231
+ }
232
+ function rowToSubscription(row) {
233
+ return {
234
+ id: row.id,
235
+ projectId: row.projectId,
236
+ organizationId: row.organizationId,
237
+ plan: row.plan,
238
+ status: row.status,
239
+ billingCycle: row.billingCycle,
240
+ currentPeriodStart: new Date(row.currentPeriodStart),
241
+ currentPeriodEnd: new Date(row.currentPeriodEnd),
242
+ cancelAtPeriodEnd: Boolean(row.cancelAtPeriodEnd)
243
+ };
244
+ }
245
+ function createSaasHandlers(db) {
246
+ async function listProjects(input) {
247
+ const {
248
+ projectId,
249
+ organizationId,
250
+ status,
251
+ search,
252
+ limit = 20,
253
+ offset = 0
254
+ } = input;
255
+ let whereClause = "WHERE projectId = ?";
256
+ const params = [projectId];
257
+ if (organizationId) {
258
+ whereClause += " AND organizationId = ?";
259
+ params.push(organizationId);
260
+ }
261
+ if (status && status !== "all") {
262
+ whereClause += " AND status = ?";
263
+ params.push(status);
264
+ }
265
+ if (search) {
266
+ whereClause += " AND (name LIKE ? OR description LIKE ?)";
267
+ params.push(`%${search}%`, `%${search}%`);
268
+ }
269
+ const countResult = (await db.query(`SELECT COUNT(*) as count FROM saas_project ${whereClause}`, params)).rows;
270
+ const total = countResult[0]?.count ?? 0;
271
+ const rows = (await db.query(`SELECT * FROM saas_project ${whereClause} ORDER BY createdAt DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
272
+ return {
273
+ items: rows.map(rowToProject),
274
+ total,
275
+ hasMore: offset + rows.length < total
276
+ };
277
+ }
278
+ async function getProject(id) {
279
+ const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
280
+ return rows[0] ? rowToProject(rows[0]) : null;
281
+ }
282
+ async function createProject(input, context) {
283
+ const id = generateId("proj");
284
+ const now = new Date().toISOString();
285
+ await db.execute(`INSERT INTO saas_project (id, projectId, organizationId, name, description, status, tier, createdAt, updatedAt)
286
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
287
+ id,
288
+ context.projectId,
289
+ context.organizationId,
290
+ input.name,
291
+ input.description ?? null,
292
+ "DRAFT",
293
+ input.tier ?? "FREE",
294
+ now,
295
+ now
296
+ ]);
297
+ const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [id])).rows;
298
+ return rowToProject(rows[0]);
299
+ }
300
+ async function updateProject(input) {
301
+ const now = new Date().toISOString();
302
+ const updates = ["updatedAt = ?"];
303
+ const params = [now];
304
+ if (input.name !== undefined) {
305
+ updates.push("name = ?");
306
+ params.push(input.name);
307
+ }
308
+ if (input.description !== undefined) {
309
+ updates.push("description = ?");
310
+ params.push(input.description);
311
+ }
312
+ if (input.status !== undefined) {
313
+ updates.push("status = ?");
314
+ params.push(input.status);
315
+ }
316
+ params.push(input.id);
317
+ await db.execute(`UPDATE saas_project SET ${updates.join(", ")} WHERE id = ?`, params);
318
+ const rows = (await db.query(`SELECT * FROM saas_project WHERE id = ?`, [input.id])).rows;
319
+ if (!rows[0]) {
320
+ throw new Error("NOT_FOUND");
321
+ }
322
+ return rowToProject(rows[0]);
323
+ }
324
+ async function deleteProject(id) {
325
+ await db.execute(`DELETE FROM saas_project WHERE id = ?`, [id]);
326
+ }
327
+ async function getSubscription(input) {
328
+ let query = `SELECT * FROM saas_subscription WHERE projectId = ?`;
329
+ const params = [input.projectId];
330
+ if (input.organizationId) {
331
+ query += " AND organizationId = ?";
332
+ params.push(input.organizationId);
333
+ }
334
+ query += " LIMIT 1";
335
+ const rows = (await db.query(query, params)).rows;
336
+ return rows[0] ? rowToSubscription(rows[0]) : null;
337
+ }
338
+ return {
339
+ listProjects,
340
+ getProject,
341
+ createProject,
342
+ updateProject,
343
+ deleteProject,
344
+ getSubscription
345
+ };
346
+ }
347
+ // src/ui/renderers/project-list.markdown.ts
348
+ var projectListMarkdownRenderer = {
349
+ target: "markdown",
350
+ render: async (desc, _ctx) => {
351
+ if (desc.source.type !== "component" || desc.source.componentKey !== "ProjectListView") {
352
+ throw new Error("projectListMarkdownRenderer: not ProjectListView");
353
+ }
354
+ const data = await mockListProjectsHandler({
355
+ limit: 20,
356
+ offset: 0
357
+ });
358
+ const items = data.projects ?? data.items ?? [];
359
+ const lines = [
360
+ "# Projects",
361
+ "",
362
+ `**Total**: ${data.total} projects`,
363
+ ""
364
+ ];
365
+ if (items.length === 0) {
366
+ lines.push("_No projects found._");
367
+ } else {
368
+ lines.push("| Status | Project | Description |");
369
+ lines.push("|--------|---------|-------------|");
370
+ for (const project of items) {
371
+ const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
372
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
373
+ }
374
+ }
375
+ return {
376
+ mimeType: "text/markdown",
377
+ body: lines.join(`
378
+ `)
379
+ };
380
+ }
381
+ };
382
+ var saasDashboardMarkdownRenderer = {
383
+ target: "markdown",
384
+ render: async (desc, _ctx) => {
385
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SaasDashboard") {
386
+ throw new Error("saasDashboardMarkdownRenderer: not SaasDashboard");
387
+ }
388
+ const [projectsData, subscription] = await Promise.all([
389
+ mockListProjectsHandler({ limit: 50 }),
390
+ mockGetSubscriptionHandler()
391
+ ]);
392
+ const projects = projectsData.projects ?? [];
393
+ const activeProjects = projects.filter((p) => p.status === "ACTIVE").length;
394
+ const archivedProjects = projects.filter((p) => p.status === "ARCHIVED").length;
395
+ const lines = [
396
+ "# SaaS Dashboard",
397
+ "",
398
+ "> Organization overview and usage summary",
399
+ "",
400
+ "## Summary",
401
+ "",
402
+ "| Metric | Value |",
403
+ "|--------|-------|",
404
+ `| Total Projects | ${projectsData.total} |`,
405
+ `| Active Projects | ${activeProjects} |`,
406
+ `| Archived Projects | ${archivedProjects} |`,
407
+ `| Subscription Plan | ${subscription.planName} |`,
408
+ `| Subscription Status | ${subscription.status} |`,
409
+ "",
410
+ "## Projects",
411
+ ""
412
+ ];
413
+ if (projects.length === 0) {
414
+ lines.push("_No projects yet._");
415
+ } else {
416
+ lines.push("| Status | Project | Description |");
417
+ lines.push("|--------|---------|-------------|");
418
+ for (const project of projects.slice(0, 10)) {
419
+ const status = project.status === "ACTIVE" ? "✅" : project.status === "ARCHIVED" ? "\uD83D\uDCE6" : "⏸️";
420
+ lines.push(`| ${status} | **${project.name}** | ${project.description ?? "-"} |`);
421
+ }
422
+ if (projects.length > 10) {
423
+ lines.push(`| ... | ... | _${projectsData.total - 10} more projects_ |`);
424
+ }
425
+ }
426
+ lines.push("");
427
+ lines.push("## Subscription");
428
+ lines.push("");
429
+ lines.push(`- **Plan**: ${subscription.planName}`);
430
+ lines.push(`- **Status**: ${subscription.status}`);
431
+ if (subscription.currentPeriodEnd) {
432
+ lines.push(`- **Period End**: ${new Date(subscription.currentPeriodEnd).toLocaleDateString()}`);
433
+ }
434
+ return {
435
+ mimeType: "text/markdown",
436
+ body: lines.join(`
437
+ `)
438
+ };
439
+ }
440
+ };
441
+ var saasBillingMarkdownRenderer = {
442
+ target: "markdown",
443
+ render: async (desc, _ctx) => {
444
+ if (desc.source.type !== "component" || desc.source.componentKey !== "SubscriptionView") {
445
+ throw new Error("saasBillingMarkdownRenderer: not SubscriptionView");
446
+ }
447
+ const subscription = await mockGetSubscriptionHandler();
448
+ const lines = [
449
+ "# Billing & Subscription",
450
+ "",
451
+ "> Current subscription details and billing information",
452
+ "",
453
+ "## Subscription Details",
454
+ "",
455
+ "| Property | Value |",
456
+ "|----------|-------|",
457
+ `| Plan | ${subscription.planName} |`,
458
+ `| Status | ${subscription.status} |`,
459
+ `| ID | ${subscription.id} |`,
460
+ `| Period Start | ${new Date(subscription.currentPeriodStart).toLocaleDateString()} |`,
461
+ `| Period End | ${new Date(subscription.currentPeriodEnd).toLocaleDateString()} |`
462
+ ];
463
+ lines.push("");
464
+ lines.push("## Plan Limits");
465
+ lines.push("");
466
+ lines.push(`- **Projects**: ${subscription.limits.projects}`);
467
+ lines.push(`- **Users**: ${subscription.limits.users}`);
468
+ lines.push("");
469
+ lines.push("## Plan Features");
470
+ lines.push("");
471
+ if (subscription.planName.toLowerCase().includes("free")) {
472
+ lines.push("- ✅ Up to 3 projects");
473
+ lines.push("- ✅ Basic support");
474
+ lines.push("- ❌ Priority support");
475
+ lines.push("- ❌ Advanced analytics");
476
+ } else if (subscription.planName.toLowerCase().includes("pro")) {
477
+ lines.push("- ✅ Unlimited projects");
478
+ lines.push("- ✅ Priority support");
479
+ lines.push("- ✅ Advanced analytics");
480
+ lines.push("- ❌ Custom integrations");
481
+ } else {
482
+ lines.push("- ✅ Unlimited projects");
483
+ lines.push("- ✅ Priority support");
484
+ lines.push("- ✅ Advanced analytics");
485
+ lines.push("- ✅ Custom integrations");
486
+ lines.push("- ✅ Dedicated support");
487
+ }
488
+ return {
489
+ mimeType: "text/markdown",
490
+ body: lines.join(`
491
+ `)
492
+ };
493
+ }
494
+ };
495
+ export {
496
+ saasDashboardMarkdownRenderer,
497
+ saasBillingMarkdownRenderer,
498
+ projectListMarkdownRenderer
499
+ };
@@ -0,0 +1,177 @@
1
+ // src/ui/hooks/useProjectList.ts
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
4
+ function useProjectList(options = {}) {
5
+ const { handlers, projectId } = useTemplateRuntime();
6
+ const { saas } = handlers;
7
+ const [data, setData] = useState(null);
8
+ const [subscription, setSubscription] = useState(null);
9
+ const [loading, setLoading] = useState(true);
10
+ const [error, setError] = useState(null);
11
+ const [page, setPage] = useState(1);
12
+ const fetchData = useCallback(async () => {
13
+ setLoading(true);
14
+ setError(null);
15
+ try {
16
+ const [projectsResult, subscriptionResult] = await Promise.all([
17
+ saas.listProjects({
18
+ projectId,
19
+ status: options.status === "all" ? undefined : options.status,
20
+ search: options.search,
21
+ limit: options.limit ?? 20,
22
+ offset: (page - 1) * (options.limit ?? 20)
23
+ }),
24
+ saas.getSubscription({ projectId })
25
+ ]);
26
+ setData({
27
+ items: projectsResult.items,
28
+ total: projectsResult.total
29
+ });
30
+ setSubscription(subscriptionResult);
31
+ } catch (err) {
32
+ setError(err instanceof Error ? err : new Error("Unknown error"));
33
+ } finally {
34
+ setLoading(false);
35
+ }
36
+ }, [saas, projectId, options.status, options.search, options.limit, page]);
37
+ useEffect(() => {
38
+ fetchData();
39
+ }, [fetchData]);
40
+ const stats = useMemo(() => {
41
+ if (!data)
42
+ return null;
43
+ const items = data.items;
44
+ return {
45
+ total: data.total,
46
+ activeCount: items.filter((p) => p.status === "ACTIVE").length,
47
+ draftCount: items.filter((p) => p.status === "DRAFT").length,
48
+ projectLimit: 10,
49
+ usagePercent: Math.min(data.total / 10 * 100, 100)
50
+ };
51
+ }, [data]);
52
+ return {
53
+ data,
54
+ subscription,
55
+ loading,
56
+ error,
57
+ stats,
58
+ page,
59
+ refetch: fetchData,
60
+ nextPage: () => setPage((p) => p + 1),
61
+ prevPage: () => page > 1 && setPage((p) => p - 1)
62
+ };
63
+ }
64
+
65
+ // src/ui/SaasProjectList.tsx
66
+ import {
67
+ StatCard,
68
+ StatCardGroup,
69
+ StatusChip,
70
+ EntityCard,
71
+ EmptyState,
72
+ LoaderBlock,
73
+ ErrorState,
74
+ Button
75
+ } from "@contractspec/lib.design-system";
76
+ import { jsxDEV } from "react/jsx-dev-runtime";
77
+ "use client";
78
+ function getStatusTone(status) {
79
+ switch (status) {
80
+ case "ACTIVE":
81
+ return "success";
82
+ case "DRAFT":
83
+ return "neutral";
84
+ case "ARCHIVED":
85
+ return "danger";
86
+ default:
87
+ return "neutral";
88
+ }
89
+ }
90
+ function SaasProjectList({
91
+ onProjectClick,
92
+ onCreateProject
93
+ }) {
94
+ const { data, loading, error, stats, refetch } = useProjectList();
95
+ if (loading && !data) {
96
+ return /* @__PURE__ */ jsxDEV(LoaderBlock, {
97
+ label: "Loading projects..."
98
+ }, undefined, false, undefined, this);
99
+ }
100
+ if (error) {
101
+ return /* @__PURE__ */ jsxDEV(ErrorState, {
102
+ title: "Failed to load projects",
103
+ description: error.message,
104
+ onRetry: refetch,
105
+ retryLabel: "Retry"
106
+ }, undefined, false, undefined, this);
107
+ }
108
+ if (!data?.items.length) {
109
+ return /* @__PURE__ */ jsxDEV(EmptyState, {
110
+ title: "No projects found",
111
+ description: "Create your first project to get started.",
112
+ primaryAction: onCreateProject ? /* @__PURE__ */ jsxDEV(Button, {
113
+ onPress: onCreateProject,
114
+ children: "Create Project"
115
+ }, undefined, false, undefined, this) : undefined
116
+ }, undefined, false, undefined, this);
117
+ }
118
+ return /* @__PURE__ */ jsxDEV("div", {
119
+ className: "space-y-6",
120
+ children: [
121
+ stats && /* @__PURE__ */ jsxDEV(StatCardGroup, {
122
+ children: [
123
+ /* @__PURE__ */ jsxDEV(StatCard, {
124
+ label: "Total Projects",
125
+ value: stats.total.toString()
126
+ }, undefined, false, undefined, this),
127
+ /* @__PURE__ */ jsxDEV(StatCard, {
128
+ label: "Active",
129
+ value: stats.activeCount.toString()
130
+ }, undefined, false, undefined, this),
131
+ /* @__PURE__ */ jsxDEV(StatCard, {
132
+ label: "Draft",
133
+ value: stats.draftCount.toString()
134
+ }, undefined, false, undefined, this)
135
+ ]
136
+ }, undefined, true, undefined, this),
137
+ /* @__PURE__ */ jsxDEV("div", {
138
+ className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
139
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV(EntityCard, {
140
+ cardTitle: project.name,
141
+ cardSubtitle: project.tier,
142
+ meta: /* @__PURE__ */ jsxDEV("p", {
143
+ className: "text-muted-foreground text-sm",
144
+ children: project.description
145
+ }, undefined, false, undefined, this),
146
+ chips: /* @__PURE__ */ jsxDEV(StatusChip, {
147
+ tone: getStatusTone(project.status),
148
+ label: project.status
149
+ }, undefined, false, undefined, this),
150
+ footer: /* @__PURE__ */ jsxDEV("span", {
151
+ className: "text-muted-foreground text-xs",
152
+ children: project.updatedAt.toLocaleDateString()
153
+ }, undefined, false, undefined, this),
154
+ onClick: onProjectClick ? () => onProjectClick(project.id) : undefined
155
+ }, project.id, false, undefined, this))
156
+ }, undefined, false, undefined, this)
157
+ ]
158
+ }, undefined, true, undefined, this);
159
+ }
160
+
161
+ // src/ui/renderers/project-list.renderer.tsx
162
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
163
+ var projectListReactRenderer = {
164
+ target: "react",
165
+ render: async (desc, _ctx) => {
166
+ if (desc.source.type !== "component") {
167
+ throw new Error("Invalid source type");
168
+ }
169
+ if (desc.source.componentKey !== "SaasProjectListView") {
170
+ throw new Error(`Unknown component: ${desc.source.componentKey}`);
171
+ }
172
+ return /* @__PURE__ */ jsxDEV2(SaasProjectList, {}, undefined, false, undefined, this);
173
+ }
174
+ };
175
+ export {
176
+ projectListReactRenderer
177
+ };
@@ -1,17 +1,15 @@
1
- import { SubscriptionPresentation, UsageDashboardPresentation } from "../billing/billing.presentation.js";
2
- import { SaasDashboardPresentation, SettingsPanelPresentation } from "../dashboard/dashboard.presentation.js";
3
- import { ProjectDetailPresentation, ProjectListPresentation } from "../project/project.presentation.js";
4
-
5
- //#region src/presentations/index.d.ts
6
-
7
- declare const SaasBoilerplatePresentations: {
8
- SubscriptionPresentation: undefined;
9
- UsageDashboardPresentation: undefined;
10
- ProjectListPresentation: undefined;
11
- ProjectDetailPresentation: undefined;
12
- SaasDashboardPresentation: undefined;
13
- SettingsPanelPresentation: undefined;
1
+ /**
2
+ * SaaS Boilerplate Presentations - re-exports from domain modules for backward compatibility.
3
+ */
4
+ export { SubscriptionPresentation, UsageDashboardPresentation, } from '../billing/billing.presentation';
5
+ export { ProjectListPresentation, ProjectDetailPresentation, } from '../project/project.presentation';
6
+ export { SaasDashboardPresentation, SettingsPanelPresentation, } from '../dashboard/dashboard.presentation';
7
+ export declare const SaasBoilerplatePresentations: {
8
+ SubscriptionPresentation: undefined;
9
+ UsageDashboardPresentation: undefined;
10
+ ProjectListPresentation: undefined;
11
+ ProjectDetailPresentation: undefined;
12
+ SaasDashboardPresentation: undefined;
13
+ SettingsPanelPresentation: undefined;
14
14
  };
15
- //#endregion
16
- export { ProjectDetailPresentation, ProjectListPresentation, SaasBoilerplatePresentations, SaasDashboardPresentation, SettingsPanelPresentation, SubscriptionPresentation, UsageDashboardPresentation };
17
15
  //# sourceMappingURL=index.d.ts.map