@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
@@ -1,298 +1,989 @@
1
- 'use client';
1
+ // @bun
2
+ // src/ui/hooks/useProjectList.ts
3
+ import { useCallback, useEffect, useMemo, useState } from "react";
4
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
5
+ function useProjectList(options = {}) {
6
+ const { handlers, projectId } = useTemplateRuntime();
7
+ const { saas } = handlers;
8
+ const [data, setData] = useState(null);
9
+ const [subscription, setSubscription] = useState(null);
10
+ const [loading, setLoading] = useState(true);
11
+ const [error, setError] = useState(null);
12
+ const [page, setPage] = useState(1);
13
+ const fetchData = useCallback(async () => {
14
+ setLoading(true);
15
+ setError(null);
16
+ try {
17
+ const [projectsResult, subscriptionResult] = await Promise.all([
18
+ saas.listProjects({
19
+ projectId,
20
+ status: options.status === "all" ? undefined : options.status,
21
+ search: options.search,
22
+ limit: options.limit ?? 20,
23
+ offset: (page - 1) * (options.limit ?? 20)
24
+ }),
25
+ saas.getSubscription({ projectId })
26
+ ]);
27
+ setData({
28
+ items: projectsResult.items,
29
+ total: projectsResult.total
30
+ });
31
+ setSubscription(subscriptionResult);
32
+ } catch (err) {
33
+ setError(err instanceof Error ? err : new Error("Unknown error"));
34
+ } finally {
35
+ setLoading(false);
36
+ }
37
+ }, [saas, projectId, options.status, options.search, options.limit, page]);
38
+ useEffect(() => {
39
+ fetchData();
40
+ }, [fetchData]);
41
+ const stats = useMemo(() => {
42
+ if (!data)
43
+ return null;
44
+ const items = data.items;
45
+ return {
46
+ total: data.total,
47
+ activeCount: items.filter((p) => p.status === "ACTIVE").length,
48
+ draftCount: items.filter((p) => p.status === "DRAFT").length,
49
+ projectLimit: 10,
50
+ usagePercent: Math.min(data.total / 10 * 100, 100)
51
+ };
52
+ }, [data]);
53
+ return {
54
+ data,
55
+ subscription,
56
+ loading,
57
+ error,
58
+ stats,
59
+ page,
60
+ refetch: fetchData,
61
+ nextPage: () => setPage((p) => p + 1),
62
+ prevPage: () => page > 1 && setPage((p) => p - 1)
63
+ };
64
+ }
65
+
66
+ // src/ui/hooks/useProjectMutations.ts
67
+ import { useCallback as useCallback2, useState as useState2 } from "react";
68
+ import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
69
+ function useProjectMutations(options = {}) {
70
+ const { handlers, projectId } = useTemplateRuntime2();
71
+ const { saas } = handlers;
72
+ const [createState, setCreateState] = useState2({
73
+ loading: false,
74
+ error: null,
75
+ data: null
76
+ });
77
+ const [updateState, setUpdateState] = useState2({
78
+ loading: false,
79
+ error: null,
80
+ data: null
81
+ });
82
+ const [deleteState, setDeleteState] = useState2({
83
+ loading: false,
84
+ error: null,
85
+ data: null
86
+ });
87
+ const createProject = useCallback2(async (input) => {
88
+ setCreateState({ loading: true, error: null, data: null });
89
+ try {
90
+ const result = await saas.createProject(input, {
91
+ projectId,
92
+ organizationId: "demo-org"
93
+ });
94
+ setCreateState({ loading: false, error: null, data: result });
95
+ options.onSuccess?.();
96
+ return result;
97
+ } catch (err) {
98
+ const error = err instanceof Error ? err : new Error("Failed to create project");
99
+ setCreateState({ loading: false, error, data: null });
100
+ options.onError?.(error);
101
+ return null;
102
+ }
103
+ }, [saas, projectId, options]);
104
+ const updateProject = useCallback2(async (input) => {
105
+ setUpdateState({ loading: true, error: null, data: null });
106
+ try {
107
+ const result = await saas.updateProject(input);
108
+ setUpdateState({ loading: false, error: null, data: result });
109
+ options.onSuccess?.();
110
+ return result;
111
+ } catch (err) {
112
+ const error = err instanceof Error ? err : new Error("Failed to update project");
113
+ setUpdateState({ loading: false, error, data: null });
114
+ options.onError?.(error);
115
+ return null;
116
+ }
117
+ }, [saas, options]);
118
+ const deleteProject = useCallback2(async (id) => {
119
+ setDeleteState({ loading: true, error: null, data: null });
120
+ try {
121
+ await saas.deleteProject(id);
122
+ setDeleteState({
123
+ loading: false,
124
+ error: null,
125
+ data: { success: true }
126
+ });
127
+ options.onSuccess?.();
128
+ return true;
129
+ } catch (err) {
130
+ const error = err instanceof Error ? err : new Error("Failed to delete project");
131
+ setDeleteState({ loading: false, error, data: null });
132
+ options.onError?.(error);
133
+ return false;
134
+ }
135
+ }, [saas, options]);
136
+ const archiveProject = useCallback2(async (id) => {
137
+ return updateProject({ id, status: "ARCHIVED" });
138
+ }, [updateProject]);
139
+ const activateProject = useCallback2(async (id) => {
140
+ return updateProject({ id, status: "ACTIVE" });
141
+ }, [updateProject]);
142
+ return {
143
+ createProject,
144
+ updateProject,
145
+ deleteProject,
146
+ archiveProject,
147
+ activateProject,
148
+ createState,
149
+ updateState,
150
+ deleteState,
151
+ isLoading: createState.loading || updateState.loading || deleteState.loading
152
+ };
153
+ }
2
154
 
3
- import { useProjectList } from "./hooks/useProjectList.js";
4
- import { useProjectMutations } from "./hooks/useProjectMutations.js";
5
- import { CreateProjectModal } from "./modals/CreateProjectModal.js";
6
- import { ProjectActionsModal } from "./modals/ProjectActionsModal.js";
7
- import { useCallback, useState } from "react";
8
- import { Button, EmptyState, EntityCard, ErrorState, LoaderBlock, StatCard, StatCardGroup, StatusChip } from "@contractspec/lib.design-system";
9
- import { jsx, jsxs } from "react/jsx-runtime";
155
+ // src/ui/modals/CreateProjectModal.tsx
156
+ import { useState as useState3 } from "react";
157
+ import { Button, Input } from "@contractspec/lib.design-system";
158
+ import { jsxDEV } from "react/jsx-dev-runtime";
159
+ "use client";
160
+ var TIERS = [
161
+ { value: "FREE", label: "Free" },
162
+ { value: "PRO", label: "Pro" },
163
+ { value: "ENTERPRISE", label: "Enterprise" }
164
+ ];
165
+ function CreateProjectModal({
166
+ isOpen,
167
+ onClose,
168
+ onSubmit,
169
+ isLoading = false
170
+ }) {
171
+ const [name, setName] = useState3("");
172
+ const [description, setDescription] = useState3("");
173
+ const [tier, setTier] = useState3("FREE");
174
+ const [error, setError] = useState3(null);
175
+ const handleSubmit = async (e) => {
176
+ e.preventDefault();
177
+ setError(null);
178
+ if (!name.trim()) {
179
+ setError("Project name is required");
180
+ return;
181
+ }
182
+ try {
183
+ await onSubmit({
184
+ name: name.trim(),
185
+ description: description.trim() || undefined,
186
+ tier
187
+ });
188
+ setName("");
189
+ setDescription("");
190
+ setTier("FREE");
191
+ onClose();
192
+ } catch (err) {
193
+ setError(err instanceof Error ? err.message : "Failed to create project");
194
+ }
195
+ };
196
+ if (!isOpen)
197
+ return null;
198
+ return /* @__PURE__ */ jsxDEV("div", {
199
+ className: "fixed inset-0 z-50 flex items-center justify-center",
200
+ children: [
201
+ /* @__PURE__ */ jsxDEV("div", {
202
+ className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
203
+ onClick: onClose,
204
+ role: "button",
205
+ tabIndex: 0,
206
+ onKeyDown: (e) => {
207
+ if (e.key === "Enter" || e.key === " ")
208
+ onClose();
209
+ },
210
+ "aria-label": "Close modal"
211
+ }, undefined, false, undefined, this),
212
+ /* @__PURE__ */ jsxDEV("div", {
213
+ className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
214
+ children: [
215
+ /* @__PURE__ */ jsxDEV("h2", {
216
+ className: "mb-4 text-xl font-semibold",
217
+ children: "Create New Project"
218
+ }, undefined, false, undefined, this),
219
+ /* @__PURE__ */ jsxDEV("form", {
220
+ onSubmit: handleSubmit,
221
+ className: "space-y-4",
222
+ children: [
223
+ /* @__PURE__ */ jsxDEV("div", {
224
+ children: [
225
+ /* @__PURE__ */ jsxDEV("label", {
226
+ htmlFor: "project-name",
227
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
228
+ children: "Project Name *"
229
+ }, undefined, false, undefined, this),
230
+ /* @__PURE__ */ jsxDEV(Input, {
231
+ id: "project-name",
232
+ value: name,
233
+ onChange: (e) => setName(e.target.value),
234
+ placeholder: "e.g., My Awesome Project",
235
+ disabled: isLoading
236
+ }, undefined, false, undefined, this)
237
+ ]
238
+ }, undefined, true, undefined, this),
239
+ /* @__PURE__ */ jsxDEV("div", {
240
+ children: [
241
+ /* @__PURE__ */ jsxDEV("label", {
242
+ htmlFor: "project-description",
243
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
244
+ children: "Description"
245
+ }, undefined, false, undefined, this),
246
+ /* @__PURE__ */ jsxDEV("textarea", {
247
+ id: "project-description",
248
+ value: description,
249
+ onChange: (e) => setDescription(e.target.value),
250
+ placeholder: "Describe what this project is about...",
251
+ rows: 3,
252
+ disabled: isLoading,
253
+ 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"
254
+ }, undefined, false, undefined, this)
255
+ ]
256
+ }, undefined, true, undefined, this),
257
+ /* @__PURE__ */ jsxDEV("div", {
258
+ children: [
259
+ /* @__PURE__ */ jsxDEV("label", {
260
+ htmlFor: "project-tier",
261
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
262
+ children: "Tier"
263
+ }, undefined, false, undefined, this),
264
+ /* @__PURE__ */ jsxDEV("select", {
265
+ id: "project-tier",
266
+ value: tier,
267
+ onChange: (e) => setTier(e.target.value),
268
+ disabled: isLoading,
269
+ 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",
270
+ children: TIERS.map((t) => /* @__PURE__ */ jsxDEV("option", {
271
+ value: t.value,
272
+ children: t.label
273
+ }, t.value, false, undefined, this))
274
+ }, undefined, false, undefined, this)
275
+ ]
276
+ }, undefined, true, undefined, this),
277
+ error && /* @__PURE__ */ jsxDEV("div", {
278
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
279
+ children: error
280
+ }, undefined, false, undefined, this),
281
+ /* @__PURE__ */ jsxDEV("div", {
282
+ className: "flex justify-end gap-3 pt-2",
283
+ children: [
284
+ /* @__PURE__ */ jsxDEV(Button, {
285
+ type: "button",
286
+ variant: "ghost",
287
+ onPress: onClose,
288
+ disabled: isLoading,
289
+ children: "Cancel"
290
+ }, undefined, false, undefined, this),
291
+ /* @__PURE__ */ jsxDEV(Button, {
292
+ type: "submit",
293
+ disabled: isLoading,
294
+ children: isLoading ? "Creating..." : "Create Project"
295
+ }, undefined, false, undefined, this)
296
+ ]
297
+ }, undefined, true, undefined, this)
298
+ ]
299
+ }, undefined, true, undefined, this)
300
+ ]
301
+ }, undefined, true, undefined, this)
302
+ ]
303
+ }, undefined, true, undefined, this);
304
+ }
10
305
 
11
- //#region src/ui/SaasDashboard.tsx
12
- /**
13
- * SaaS Dashboard
14
- *
15
- * Fully integrated with ContractSpec example handlers
16
- * and design-system components.
17
- *
18
- * Commands wired:
19
- * - CreateProjectContract -> Create Project button + modal
20
- * - UpdateProjectContract -> Edit project via modal
21
- * - DeleteProjectContract -> Delete project via modal
22
- */
306
+ // src/ui/modals/ProjectActionsModal.tsx
307
+ import { useEffect as useEffect2, useState as useState4 } from "react";
308
+ import { Button as Button2, Input as Input2 } from "@contractspec/lib.design-system";
309
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
310
+ "use client";
311
+ function ProjectActionsModal({
312
+ isOpen,
313
+ project,
314
+ onClose,
315
+ onUpdate,
316
+ onArchive,
317
+ onActivate,
318
+ onDelete,
319
+ isLoading = false
320
+ }) {
321
+ const [mode, setMode] = useState4("menu");
322
+ const [name, setName] = useState4("");
323
+ const [description, setDescription] = useState4("");
324
+ const [error, setError] = useState4(null);
325
+ const resetForm = () => {
326
+ setMode("menu");
327
+ setError(null);
328
+ if (project) {
329
+ setName(project.name);
330
+ setDescription(project.description ?? "");
331
+ }
332
+ };
333
+ const handleClose = () => {
334
+ resetForm();
335
+ onClose();
336
+ };
337
+ useEffect2(() => {
338
+ if (project) {
339
+ setName(project.name);
340
+ setDescription(project.description ?? "");
341
+ }
342
+ }, [project]);
343
+ const handleEdit = async () => {
344
+ if (!project)
345
+ return;
346
+ setError(null);
347
+ if (!name.trim()) {
348
+ setError("Project name is required");
349
+ return;
350
+ }
351
+ try {
352
+ await onUpdate({
353
+ id: project.id,
354
+ name: name.trim(),
355
+ description: description.trim() || undefined
356
+ });
357
+ handleClose();
358
+ } catch (err) {
359
+ setError(err instanceof Error ? err.message : "Failed to update project");
360
+ }
361
+ };
362
+ const handleArchive = async () => {
363
+ if (!project)
364
+ return;
365
+ setError(null);
366
+ try {
367
+ await onArchive(project.id);
368
+ handleClose();
369
+ } catch (err) {
370
+ setError(err instanceof Error ? err.message : "Failed to archive project");
371
+ }
372
+ };
373
+ const handleActivate = async () => {
374
+ if (!project)
375
+ return;
376
+ setError(null);
377
+ try {
378
+ await onActivate(project.id);
379
+ handleClose();
380
+ } catch (err) {
381
+ setError(err instanceof Error ? err.message : "Failed to activate project");
382
+ }
383
+ };
384
+ const handleDelete = async () => {
385
+ if (!project)
386
+ return;
387
+ setError(null);
388
+ try {
389
+ await onDelete(project.id);
390
+ handleClose();
391
+ } catch (err) {
392
+ setError(err instanceof Error ? err.message : "Failed to delete project");
393
+ }
394
+ };
395
+ if (!isOpen || !project)
396
+ return null;
397
+ return /* @__PURE__ */ jsxDEV2("div", {
398
+ className: "fixed inset-0 z-50 flex items-center justify-center",
399
+ children: [
400
+ /* @__PURE__ */ jsxDEV2("div", {
401
+ className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
402
+ onClick: handleClose,
403
+ role: "button",
404
+ tabIndex: 0,
405
+ onKeyDown: (e) => {
406
+ if (e.key === "Enter" || e.key === " ")
407
+ handleClose();
408
+ },
409
+ "aria-label": "Close modal"
410
+ }, undefined, false, undefined, this),
411
+ /* @__PURE__ */ jsxDEV2("div", {
412
+ className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
413
+ children: [
414
+ /* @__PURE__ */ jsxDEV2("div", {
415
+ className: "border-border mb-4 border-b pb-4",
416
+ children: [
417
+ /* @__PURE__ */ jsxDEV2("h2", {
418
+ className: "text-xl font-semibold",
419
+ children: project.name
420
+ }, undefined, false, undefined, this),
421
+ /* @__PURE__ */ jsxDEV2("p", {
422
+ className: "text-muted-foreground text-sm",
423
+ children: [
424
+ project.tier,
425
+ " \xB7 ",
426
+ project.status
427
+ ]
428
+ }, undefined, true, undefined, this)
429
+ ]
430
+ }, undefined, true, undefined, this),
431
+ mode === "menu" && /* @__PURE__ */ jsxDEV2("div", {
432
+ className: "space-y-3",
433
+ children: [
434
+ /* @__PURE__ */ jsxDEV2(Button2, {
435
+ className: "w-full justify-start",
436
+ variant: "ghost",
437
+ onPress: () => setMode("edit"),
438
+ children: [
439
+ /* @__PURE__ */ jsxDEV2("span", {
440
+ className: "mr-2",
441
+ children: "\u270F\uFE0F"
442
+ }, undefined, false, undefined, this),
443
+ " Edit Project"
444
+ ]
445
+ }, undefined, true, undefined, this),
446
+ project.status === "ACTIVE" || project.status === "DRAFT" ? /* @__PURE__ */ jsxDEV2(Button2, {
447
+ className: "w-full justify-start",
448
+ variant: "ghost",
449
+ onPress: () => setMode("archive"),
450
+ children: [
451
+ /* @__PURE__ */ jsxDEV2("span", {
452
+ className: "mr-2",
453
+ children: "\uD83D\uDCE6"
454
+ }, undefined, false, undefined, this),
455
+ " Archive Project"
456
+ ]
457
+ }, undefined, true, undefined, this) : project.status === "ARCHIVED" ? /* @__PURE__ */ jsxDEV2(Button2, {
458
+ className: "w-full justify-start",
459
+ variant: "ghost",
460
+ onPress: handleActivate,
461
+ disabled: isLoading,
462
+ children: [
463
+ /* @__PURE__ */ jsxDEV2("span", {
464
+ className: "mr-2",
465
+ children: "\uD83D\uDD04"
466
+ }, undefined, false, undefined, this),
467
+ " Restore Project"
468
+ ]
469
+ }, undefined, true, undefined, this) : null,
470
+ /* @__PURE__ */ jsxDEV2(Button2, {
471
+ className: "w-full justify-start text-red-500 hover:text-red-600",
472
+ variant: "ghost",
473
+ onPress: () => setMode("delete"),
474
+ children: [
475
+ /* @__PURE__ */ jsxDEV2("span", {
476
+ className: "mr-2",
477
+ children: "\uD83D\uDDD1\uFE0F"
478
+ }, undefined, false, undefined, this),
479
+ " Delete Project"
480
+ ]
481
+ }, undefined, true, undefined, this),
482
+ /* @__PURE__ */ jsxDEV2("div", {
483
+ className: "border-border border-t pt-3",
484
+ children: /* @__PURE__ */ jsxDEV2(Button2, {
485
+ className: "w-full",
486
+ variant: "outline",
487
+ onPress: handleClose,
488
+ children: "Close"
489
+ }, undefined, false, undefined, this)
490
+ }, undefined, false, undefined, this)
491
+ ]
492
+ }, undefined, true, undefined, this),
493
+ mode === "edit" && /* @__PURE__ */ jsxDEV2("div", {
494
+ className: "space-y-4",
495
+ children: [
496
+ /* @__PURE__ */ jsxDEV2("div", {
497
+ children: [
498
+ /* @__PURE__ */ jsxDEV2("label", {
499
+ htmlFor: "edit-name",
500
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
501
+ children: "Project Name *"
502
+ }, undefined, false, undefined, this),
503
+ /* @__PURE__ */ jsxDEV2(Input2, {
504
+ id: "edit-name",
505
+ value: name,
506
+ onChange: (e) => setName(e.target.value),
507
+ disabled: isLoading
508
+ }, undefined, false, undefined, this)
509
+ ]
510
+ }, undefined, true, undefined, this),
511
+ /* @__PURE__ */ jsxDEV2("div", {
512
+ children: [
513
+ /* @__PURE__ */ jsxDEV2("label", {
514
+ htmlFor: "edit-description",
515
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
516
+ children: "Description"
517
+ }, undefined, false, undefined, this),
518
+ /* @__PURE__ */ jsxDEV2("textarea", {
519
+ id: "edit-description",
520
+ value: description,
521
+ onChange: (e) => setDescription(e.target.value),
522
+ rows: 3,
523
+ disabled: isLoading,
524
+ 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"
525
+ }, undefined, false, undefined, this)
526
+ ]
527
+ }, undefined, true, undefined, this),
528
+ error && /* @__PURE__ */ jsxDEV2("div", {
529
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
530
+ children: error
531
+ }, undefined, false, undefined, this),
532
+ /* @__PURE__ */ jsxDEV2("div", {
533
+ className: "flex justify-end gap-3 pt-2",
534
+ children: [
535
+ /* @__PURE__ */ jsxDEV2(Button2, {
536
+ variant: "ghost",
537
+ onPress: () => setMode("menu"),
538
+ disabled: isLoading,
539
+ children: "Back"
540
+ }, undefined, false, undefined, this),
541
+ /* @__PURE__ */ jsxDEV2(Button2, {
542
+ onPress: handleEdit,
543
+ disabled: isLoading,
544
+ children: isLoading ? "Saving..." : "Save Changes"
545
+ }, undefined, false, undefined, this)
546
+ ]
547
+ }, undefined, true, undefined, this)
548
+ ]
549
+ }, undefined, true, undefined, this),
550
+ mode === "archive" && /* @__PURE__ */ jsxDEV2("div", {
551
+ className: "space-y-4",
552
+ children: [
553
+ /* @__PURE__ */ jsxDEV2("p", {
554
+ className: "text-muted-foreground",
555
+ children: [
556
+ "Are you sure you want to archive",
557
+ " ",
558
+ /* @__PURE__ */ jsxDEV2("span", {
559
+ className: "text-foreground font-medium",
560
+ children: project.name
561
+ }, undefined, false, undefined, this),
562
+ "?"
563
+ ]
564
+ }, undefined, true, undefined, this),
565
+ /* @__PURE__ */ jsxDEV2("p", {
566
+ className: "text-muted-foreground text-sm",
567
+ children: "Archived projects can be restored later."
568
+ }, undefined, false, undefined, this),
569
+ error && /* @__PURE__ */ jsxDEV2("div", {
570
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
571
+ children: error
572
+ }, undefined, false, undefined, this),
573
+ /* @__PURE__ */ jsxDEV2("div", {
574
+ className: "flex justify-end gap-3 pt-2",
575
+ children: [
576
+ /* @__PURE__ */ jsxDEV2(Button2, {
577
+ variant: "ghost",
578
+ onPress: () => setMode("menu"),
579
+ disabled: isLoading,
580
+ children: "Cancel"
581
+ }, undefined, false, undefined, this),
582
+ /* @__PURE__ */ jsxDEV2(Button2, {
583
+ onPress: handleArchive,
584
+ disabled: isLoading,
585
+ children: isLoading ? "Archiving..." : "\uD83D\uDCE6 Archive"
586
+ }, undefined, false, undefined, this)
587
+ ]
588
+ }, undefined, true, undefined, this)
589
+ ]
590
+ }, undefined, true, undefined, this),
591
+ mode === "delete" && /* @__PURE__ */ jsxDEV2("div", {
592
+ className: "space-y-4",
593
+ children: [
594
+ /* @__PURE__ */ jsxDEV2("p", {
595
+ className: "text-muted-foreground",
596
+ children: [
597
+ "Are you sure you want to delete",
598
+ " ",
599
+ /* @__PURE__ */ jsxDEV2("span", {
600
+ className: "text-foreground font-medium",
601
+ children: project.name
602
+ }, undefined, false, undefined, this),
603
+ "?"
604
+ ]
605
+ }, undefined, true, undefined, this),
606
+ /* @__PURE__ */ jsxDEV2("p", {
607
+ className: "text-destructive text-sm",
608
+ children: "This action cannot be undone."
609
+ }, undefined, false, undefined, this),
610
+ error && /* @__PURE__ */ jsxDEV2("div", {
611
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
612
+ children: error
613
+ }, undefined, false, undefined, this),
614
+ /* @__PURE__ */ jsxDEV2("div", {
615
+ className: "flex justify-end gap-3 pt-2",
616
+ children: [
617
+ /* @__PURE__ */ jsxDEV2(Button2, {
618
+ variant: "ghost",
619
+ onPress: () => setMode("menu"),
620
+ disabled: isLoading,
621
+ children: "Cancel"
622
+ }, undefined, false, undefined, this),
623
+ /* @__PURE__ */ jsxDEV2(Button2, {
624
+ variant: "destructive",
625
+ onPress: handleDelete,
626
+ disabled: isLoading,
627
+ children: isLoading ? "Deleting..." : "\uD83D\uDDD1\uFE0F Delete"
628
+ }, undefined, false, undefined, this)
629
+ ]
630
+ }, undefined, true, undefined, this)
631
+ ]
632
+ }, undefined, true, undefined, this)
633
+ ]
634
+ }, undefined, true, undefined, this)
635
+ ]
636
+ }, undefined, true, undefined, this);
637
+ }
638
+
639
+ // src/ui/SaasDashboard.tsx
640
+ import { useState as useState5, useCallback as useCallback3 } from "react";
641
+ import {
642
+ StatCard,
643
+ StatCardGroup,
644
+ StatusChip,
645
+ EntityCard,
646
+ EmptyState,
647
+ LoaderBlock,
648
+ ErrorState,
649
+ Button as Button3
650
+ } from "@contractspec/lib.design-system";
651
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
652
+ "use client";
23
653
  function getStatusTone(status) {
24
- switch (status) {
25
- case "ACTIVE": return "success";
26
- case "DRAFT": return "neutral";
27
- case "ARCHIVED": return "warning";
28
- default: return "neutral";
29
- }
654
+ switch (status) {
655
+ case "ACTIVE":
656
+ return "success";
657
+ case "DRAFT":
658
+ return "neutral";
659
+ case "ARCHIVED":
660
+ return "warning";
661
+ default:
662
+ return "neutral";
663
+ }
30
664
  }
31
665
  function SaasDashboard() {
32
- const [activeTab, setActiveTab] = useState("projects");
33
- const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
34
- const [selectedProject, setSelectedProject] = useState(null);
35
- const [isProjectActionsOpen, setIsProjectActionsOpen] = useState(false);
36
- const { data, subscription, loading, error, stats, refetch } = useProjectList();
37
- const mutations = useProjectMutations({ onSuccess: () => {
38
- refetch();
39
- } });
40
- const handleProjectClick = useCallback((project) => {
41
- setSelectedProject(project);
42
- setIsProjectActionsOpen(true);
43
- }, []);
44
- const tabs = [
45
- {
46
- id: "projects",
47
- label: "Projects",
48
- icon: "📁"
49
- },
50
- {
51
- id: "billing",
52
- label: "Billing",
53
- icon: "💳"
54
- },
55
- {
56
- id: "settings",
57
- label: "Settings",
58
- icon: "⚙️"
59
- }
60
- ];
61
- if (loading && !data) return /* @__PURE__ */ jsx(LoaderBlock, { label: "Loading dashboard..." });
62
- if (error) return /* @__PURE__ */ jsx(ErrorState, {
63
- title: "Failed to load dashboard",
64
- description: error.message,
65
- onRetry: refetch,
66
- retryLabel: "Retry"
67
- });
68
- return /* @__PURE__ */ jsxs("div", {
69
- className: "space-y-6",
70
- children: [
71
- /* @__PURE__ */ jsxs("div", {
72
- className: "flex items-center justify-between",
73
- children: [/* @__PURE__ */ jsx("h2", {
74
- className: "text-2xl font-bold",
75
- children: "SaaS Dashboard"
76
- }), activeTab === "projects" && /* @__PURE__ */ jsxs(Button, {
77
- onPress: () => setIsCreateModalOpen(true),
78
- children: [/* @__PURE__ */ jsx("span", {
79
- className: "mr-2",
80
- children: "+"
81
- }), " New Project"]
82
- })]
83
- }),
84
- stats && subscription && /* @__PURE__ */ jsxs(StatCardGroup, { children: [
85
- /* @__PURE__ */ jsx(StatCard, {
86
- label: "Projects",
87
- value: stats.total.toString()
88
- }),
89
- /* @__PURE__ */ jsx(StatCard, {
90
- label: "Active",
91
- value: stats.activeCount.toString()
92
- }),
93
- /* @__PURE__ */ jsx(StatCard, {
94
- label: "Draft",
95
- value: stats.draftCount.toString()
96
- }),
97
- /* @__PURE__ */ jsx(StatCard, {
98
- label: "Plan",
99
- value: subscription.plan,
100
- hint: subscription.status
101
- })
102
- ] }),
103
- /* @__PURE__ */ jsx("nav", {
104
- className: "bg-muted flex gap-1 rounded-lg p-1",
105
- role: "tablist",
106
- children: tabs.map((tab) => /* @__PURE__ */ jsxs("button", {
107
- type: "button",
108
- role: "tab",
109
- "aria-selected": activeTab === tab.id,
110
- onClick: () => setActiveTab(tab.id),
111
- 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"}`,
112
- children: [/* @__PURE__ */ jsx("span", { children: tab.icon }), tab.label]
113
- }, tab.id))
114
- }),
115
- /* @__PURE__ */ jsxs("div", {
116
- className: "min-h-[400px]",
117
- role: "tabpanel",
118
- children: [
119
- activeTab === "projects" && /* @__PURE__ */ jsx(ProjectsTab, {
120
- data,
121
- onProjectClick: handleProjectClick
122
- }),
123
- activeTab === "billing" && /* @__PURE__ */ jsx(BillingTab, { subscription }),
124
- activeTab === "settings" && /* @__PURE__ */ jsx(SettingsTab, {})
125
- ]
126
- }),
127
- /* @__PURE__ */ jsx(CreateProjectModal, {
128
- isOpen: isCreateModalOpen,
129
- onClose: () => setIsCreateModalOpen(false),
130
- onSubmit: async (input) => {
131
- await mutations.createProject(input);
132
- },
133
- isLoading: mutations.createState.loading
134
- }),
135
- /* @__PURE__ */ jsx(ProjectActionsModal, {
136
- isOpen: isProjectActionsOpen,
137
- project: selectedProject,
138
- onClose: () => {
139
- setIsProjectActionsOpen(false);
140
- setSelectedProject(null);
141
- },
142
- onUpdate: async (input) => {
143
- await mutations.updateProject(input);
144
- },
145
- onArchive: async (projectId) => {
146
- await mutations.archiveProject(projectId);
147
- },
148
- onActivate: async (projectId) => {
149
- await mutations.activateProject(projectId);
150
- },
151
- onDelete: async (projectId) => {
152
- await mutations.deleteProject(projectId);
153
- },
154
- isLoading: mutations.isLoading
155
- })
156
- ]
157
- });
666
+ const [activeTab, setActiveTab] = useState5("projects");
667
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState5(false);
668
+ const [selectedProject, setSelectedProject] = useState5(null);
669
+ const [isProjectActionsOpen, setIsProjectActionsOpen] = useState5(false);
670
+ const { data, subscription, loading, error, stats, refetch } = useProjectList();
671
+ const mutations = useProjectMutations({
672
+ onSuccess: () => {
673
+ refetch();
674
+ }
675
+ });
676
+ const handleProjectClick = useCallback3((project) => {
677
+ setSelectedProject(project);
678
+ setIsProjectActionsOpen(true);
679
+ }, []);
680
+ const tabs = [
681
+ { id: "projects", label: "Projects", icon: "\uD83D\uDCC1" },
682
+ { id: "billing", label: "Billing", icon: "\uD83D\uDCB3" },
683
+ { id: "settings", label: "Settings", icon: "\u2699\uFE0F" }
684
+ ];
685
+ if (loading && !data) {
686
+ return /* @__PURE__ */ jsxDEV3(LoaderBlock, {
687
+ label: "Loading dashboard..."
688
+ }, undefined, false, undefined, this);
689
+ }
690
+ if (error) {
691
+ return /* @__PURE__ */ jsxDEV3(ErrorState, {
692
+ title: "Failed to load dashboard",
693
+ description: error.message,
694
+ onRetry: refetch,
695
+ retryLabel: "Retry"
696
+ }, undefined, false, undefined, this);
697
+ }
698
+ return /* @__PURE__ */ jsxDEV3("div", {
699
+ className: "space-y-6",
700
+ children: [
701
+ /* @__PURE__ */ jsxDEV3("div", {
702
+ className: "flex items-center justify-between",
703
+ children: [
704
+ /* @__PURE__ */ jsxDEV3("h2", {
705
+ className: "text-2xl font-bold",
706
+ children: "SaaS Dashboard"
707
+ }, undefined, false, undefined, this),
708
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV3(Button3, {
709
+ onPress: () => setIsCreateModalOpen(true),
710
+ children: [
711
+ /* @__PURE__ */ jsxDEV3("span", {
712
+ className: "mr-2",
713
+ children: "+"
714
+ }, undefined, false, undefined, this),
715
+ " New Project"
716
+ ]
717
+ }, undefined, true, undefined, this)
718
+ ]
719
+ }, undefined, true, undefined, this),
720
+ stats && subscription && /* @__PURE__ */ jsxDEV3(StatCardGroup, {
721
+ children: [
722
+ /* @__PURE__ */ jsxDEV3(StatCard, {
723
+ label: "Projects",
724
+ value: stats.total.toString()
725
+ }, undefined, false, undefined, this),
726
+ /* @__PURE__ */ jsxDEV3(StatCard, {
727
+ label: "Active",
728
+ value: stats.activeCount.toString()
729
+ }, undefined, false, undefined, this),
730
+ /* @__PURE__ */ jsxDEV3(StatCard, {
731
+ label: "Draft",
732
+ value: stats.draftCount.toString()
733
+ }, undefined, false, undefined, this),
734
+ /* @__PURE__ */ jsxDEV3(StatCard, {
735
+ label: "Plan",
736
+ value: subscription.plan,
737
+ hint: subscription.status
738
+ }, undefined, false, undefined, this)
739
+ ]
740
+ }, undefined, true, undefined, this),
741
+ /* @__PURE__ */ jsxDEV3("nav", {
742
+ className: "bg-muted flex gap-1 rounded-lg p-1",
743
+ role: "tablist",
744
+ children: tabs.map((tab) => /* @__PURE__ */ jsxDEV3("button", {
745
+ type: "button",
746
+ role: "tab",
747
+ "aria-selected": activeTab === tab.id,
748
+ onClick: () => setActiveTab(tab.id),
749
+ 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"}`,
750
+ children: [
751
+ /* @__PURE__ */ jsxDEV3("span", {
752
+ children: tab.icon
753
+ }, undefined, false, undefined, this),
754
+ tab.label
755
+ ]
756
+ }, tab.id, true, undefined, this))
757
+ }, undefined, false, undefined, this),
758
+ /* @__PURE__ */ jsxDEV3("div", {
759
+ className: "min-h-[400px]",
760
+ role: "tabpanel",
761
+ children: [
762
+ activeTab === "projects" && /* @__PURE__ */ jsxDEV3(ProjectsTab, {
763
+ data,
764
+ onProjectClick: handleProjectClick
765
+ }, undefined, false, undefined, this),
766
+ activeTab === "billing" && /* @__PURE__ */ jsxDEV3(BillingTab, {
767
+ subscription
768
+ }, undefined, false, undefined, this),
769
+ activeTab === "settings" && /* @__PURE__ */ jsxDEV3(SettingsTab, {}, undefined, false, undefined, this)
770
+ ]
771
+ }, undefined, true, undefined, this),
772
+ /* @__PURE__ */ jsxDEV3(CreateProjectModal, {
773
+ isOpen: isCreateModalOpen,
774
+ onClose: () => setIsCreateModalOpen(false),
775
+ onSubmit: async (input) => {
776
+ await mutations.createProject(input);
777
+ },
778
+ isLoading: mutations.createState.loading
779
+ }, undefined, false, undefined, this),
780
+ /* @__PURE__ */ jsxDEV3(ProjectActionsModal, {
781
+ isOpen: isProjectActionsOpen,
782
+ project: selectedProject,
783
+ onClose: () => {
784
+ setIsProjectActionsOpen(false);
785
+ setSelectedProject(null);
786
+ },
787
+ onUpdate: async (input) => {
788
+ await mutations.updateProject(input);
789
+ },
790
+ onArchive: async (projectId) => {
791
+ await mutations.archiveProject(projectId);
792
+ },
793
+ onActivate: async (projectId) => {
794
+ await mutations.activateProject(projectId);
795
+ },
796
+ onDelete: async (projectId) => {
797
+ await mutations.deleteProject(projectId);
798
+ },
799
+ isLoading: mutations.isLoading
800
+ }, undefined, false, undefined, this)
801
+ ]
802
+ }, undefined, true, undefined, this);
158
803
  }
159
804
  function ProjectsTab({ data, onProjectClick }) {
160
- if (!data?.items.length) return /* @__PURE__ */ jsx(EmptyState, {
161
- title: "No projects yet",
162
- description: "Create your first project to get started."
163
- });
164
- return /* @__PURE__ */ jsx("div", {
165
- className: "space-y-4",
166
- children: /* @__PURE__ */ jsx("div", {
167
- className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
168
- children: data.items.map((project) => /* @__PURE__ */ jsx(EntityCard, {
169
- cardTitle: project.name,
170
- cardSubtitle: project.tier,
171
- meta: /* @__PURE__ */ jsx("p", {
172
- className: "text-muted-foreground text-sm",
173
- children: project.description
174
- }),
175
- chips: /* @__PURE__ */ jsx(StatusChip, {
176
- tone: getStatusTone(project.status),
177
- label: project.status
178
- }),
179
- footer: /* @__PURE__ */ jsxs("div", {
180
- className: "flex w-full items-center justify-between",
181
- children: [/* @__PURE__ */ jsx("span", {
182
- className: "text-muted-foreground text-xs",
183
- children: project.updatedAt.toLocaleDateString()
184
- }), /* @__PURE__ */ jsx(Button, {
185
- variant: "ghost",
186
- size: "sm",
187
- onPress: () => onProjectClick?.(project),
188
- children: "Actions"
189
- })]
190
- })
191
- }, project.id))
192
- })
193
- });
805
+ if (!data?.items.length) {
806
+ return /* @__PURE__ */ jsxDEV3(EmptyState, {
807
+ title: "No projects yet",
808
+ description: "Create your first project to get started."
809
+ }, undefined, false, undefined, this);
810
+ }
811
+ return /* @__PURE__ */ jsxDEV3("div", {
812
+ className: "space-y-4",
813
+ children: /* @__PURE__ */ jsxDEV3("div", {
814
+ className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
815
+ children: data.items.map((project) => /* @__PURE__ */ jsxDEV3(EntityCard, {
816
+ cardTitle: project.name,
817
+ cardSubtitle: project.tier,
818
+ meta: /* @__PURE__ */ jsxDEV3("p", {
819
+ className: "text-muted-foreground text-sm",
820
+ children: project.description
821
+ }, undefined, false, undefined, this),
822
+ chips: /* @__PURE__ */ jsxDEV3(StatusChip, {
823
+ tone: getStatusTone(project.status),
824
+ label: project.status
825
+ }, undefined, false, undefined, this),
826
+ footer: /* @__PURE__ */ jsxDEV3("div", {
827
+ className: "flex w-full items-center justify-between",
828
+ children: [
829
+ /* @__PURE__ */ jsxDEV3("span", {
830
+ className: "text-muted-foreground text-xs",
831
+ children: project.updatedAt.toLocaleDateString()
832
+ }, undefined, false, undefined, this),
833
+ /* @__PURE__ */ jsxDEV3(Button3, {
834
+ variant: "ghost",
835
+ size: "sm",
836
+ onPress: () => onProjectClick?.(project),
837
+ children: "Actions"
838
+ }, undefined, false, undefined, this)
839
+ ]
840
+ }, undefined, true, undefined, this)
841
+ }, project.id, false, undefined, this))
842
+ }, undefined, false, undefined, this)
843
+ }, undefined, false, undefined, this);
194
844
  }
195
845
  function BillingTab({ subscription }) {
196
- if (!subscription) return null;
197
- return /* @__PURE__ */ jsxs("div", {
198
- className: "space-y-6",
199
- children: [/* @__PURE__ */ jsxs("div", {
200
- className: "border-border bg-card rounded-xl border p-6",
201
- children: [/* @__PURE__ */ jsxs("div", {
202
- className: "flex items-start justify-between",
203
- children: [/* @__PURE__ */ jsxs("div", { children: [
204
- /* @__PURE__ */ jsxs("h3", {
205
- className: "text-lg font-semibold",
206
- children: [subscription.plan, " Plan"]
207
- }),
208
- /* @__PURE__ */ jsxs("p", {
209
- className: "text-muted-foreground text-sm",
210
- children: [
211
- "Current period:",
212
- " ",
213
- subscription.currentPeriodStart.toLocaleDateString(),
214
- " -",
215
- " ",
216
- subscription.currentPeriodEnd.toLocaleDateString()
217
- ]
218
- }),
219
- /* @__PURE__ */ jsxs("p", {
220
- className: "text-muted-foreground text-sm",
221
- children: ["Billing cycle: ", subscription.billingCycle]
222
- })
223
- ] }), /* @__PURE__ */ jsx(StatusChip, {
224
- tone: "success",
225
- label: subscription.status
226
- })]
227
- }), /* @__PURE__ */ jsxs("div", {
228
- className: "mt-4 flex gap-3",
229
- children: [/* @__PURE__ */ jsx(Button, {
230
- variant: "outline",
231
- onPress: () => alert("Upgrade clicked!"),
232
- children: "Upgrade Plan"
233
- }), /* @__PURE__ */ jsx(Button, {
234
- variant: "ghost",
235
- onPress: () => alert("Manage Billing clicked!"),
236
- children: "Manage Billing"
237
- })]
238
- })]
239
- }), subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsx("div", {
240
- className: "border-border bg-destructive/10 text-destructive rounded-xl border p-4",
241
- children: /* @__PURE__ */ jsx("p", {
242
- className: "text-sm font-medium",
243
- children: "⚠️ Your subscription will be cancelled at the end of the current period."
244
- })
245
- })]
246
- });
846
+ if (!subscription)
847
+ return null;
848
+ return /* @__PURE__ */ jsxDEV3("div", {
849
+ className: "space-y-6",
850
+ children: [
851
+ /* @__PURE__ */ jsxDEV3("div", {
852
+ className: "border-border bg-card rounded-xl border p-6",
853
+ children: [
854
+ /* @__PURE__ */ jsxDEV3("div", {
855
+ className: "flex items-start justify-between",
856
+ children: [
857
+ /* @__PURE__ */ jsxDEV3("div", {
858
+ children: [
859
+ /* @__PURE__ */ jsxDEV3("h3", {
860
+ className: "text-lg font-semibold",
861
+ children: [
862
+ subscription.plan,
863
+ " Plan"
864
+ ]
865
+ }, undefined, true, undefined, this),
866
+ /* @__PURE__ */ jsxDEV3("p", {
867
+ className: "text-muted-foreground text-sm",
868
+ children: [
869
+ "Current period:",
870
+ " ",
871
+ subscription.currentPeriodStart.toLocaleDateString(),
872
+ " -",
873
+ " ",
874
+ subscription.currentPeriodEnd.toLocaleDateString()
875
+ ]
876
+ }, undefined, true, undefined, this),
877
+ /* @__PURE__ */ jsxDEV3("p", {
878
+ className: "text-muted-foreground text-sm",
879
+ children: [
880
+ "Billing cycle: ",
881
+ subscription.billingCycle
882
+ ]
883
+ }, undefined, true, undefined, this)
884
+ ]
885
+ }, undefined, true, undefined, this),
886
+ /* @__PURE__ */ jsxDEV3(StatusChip, {
887
+ tone: "success",
888
+ label: subscription.status
889
+ }, undefined, false, undefined, this)
890
+ ]
891
+ }, undefined, true, undefined, this),
892
+ /* @__PURE__ */ jsxDEV3("div", {
893
+ className: "mt-4 flex gap-3",
894
+ children: [
895
+ /* @__PURE__ */ jsxDEV3(Button3, {
896
+ variant: "outline",
897
+ onPress: () => alert("Upgrade clicked!"),
898
+ children: "Upgrade Plan"
899
+ }, undefined, false, undefined, this),
900
+ /* @__PURE__ */ jsxDEV3(Button3, {
901
+ variant: "ghost",
902
+ onPress: () => alert("Manage Billing clicked!"),
903
+ children: "Manage Billing"
904
+ }, undefined, false, undefined, this)
905
+ ]
906
+ }, undefined, true, undefined, this)
907
+ ]
908
+ }, undefined, true, undefined, this),
909
+ subscription.cancelAtPeriodEnd && /* @__PURE__ */ jsxDEV3("div", {
910
+ className: "border-border bg-destructive/10 text-destructive rounded-xl border p-4",
911
+ children: /* @__PURE__ */ jsxDEV3("p", {
912
+ className: "text-sm font-medium",
913
+ children: "\u26A0\uFE0F Your subscription will be cancelled at the end of the current period."
914
+ }, undefined, false, undefined, this)
915
+ }, undefined, false, undefined, this)
916
+ ]
917
+ }, undefined, true, undefined, this);
247
918
  }
248
919
  function SettingsTab() {
249
- return /* @__PURE__ */ jsx("div", {
250
- className: "space-y-6",
251
- children: /* @__PURE__ */ jsxs("div", {
252
- className: "border-border bg-card rounded-xl border p-6",
253
- children: [/* @__PURE__ */ jsx("h3", {
254
- className: "mb-4 text-lg font-semibold",
255
- children: "Organization Settings"
256
- }), /* @__PURE__ */ jsxs("div", {
257
- className: "space-y-4",
258
- children: [
259
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
260
- htmlFor: "org-name",
261
- className: "text-sm font-medium",
262
- children: "Organization Name"
263
- }), /* @__PURE__ */ jsx("input", {
264
- id: "org-name",
265
- type: "text",
266
- defaultValue: "Demo Organization",
267
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
268
- })] }),
269
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("label", {
270
- htmlFor: "timezone",
271
- className: "text-sm font-medium",
272
- children: "Default Timezone"
273
- }), /* @__PURE__ */ jsxs("select", {
274
- id: "timezone",
275
- className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2",
276
- children: [
277
- /* @__PURE__ */ jsx("option", { children: "UTC" }),
278
- /* @__PURE__ */ jsx("option", { children: "America/New_York" }),
279
- /* @__PURE__ */ jsx("option", { children: "Europe/London" }),
280
- /* @__PURE__ */ jsx("option", { children: "Asia/Tokyo" })
281
- ]
282
- })] }),
283
- /* @__PURE__ */ jsx("div", {
284
- className: "pt-2",
285
- children: /* @__PURE__ */ jsx(Button, {
286
- onPress: () => alert("Settings saved!"),
287
- children: "Save Settings"
288
- })
289
- })
290
- ]
291
- })]
292
- })
293
- });
920
+ return /* @__PURE__ */ jsxDEV3("div", {
921
+ className: "space-y-6",
922
+ children: /* @__PURE__ */ jsxDEV3("div", {
923
+ className: "border-border bg-card rounded-xl border p-6",
924
+ children: [
925
+ /* @__PURE__ */ jsxDEV3("h3", {
926
+ className: "mb-4 text-lg font-semibold",
927
+ children: "Organization Settings"
928
+ }, undefined, false, undefined, this),
929
+ /* @__PURE__ */ jsxDEV3("div", {
930
+ className: "space-y-4",
931
+ children: [
932
+ /* @__PURE__ */ jsxDEV3("div", {
933
+ children: [
934
+ /* @__PURE__ */ jsxDEV3("label", {
935
+ htmlFor: "org-name",
936
+ className: "text-sm font-medium",
937
+ children: "Organization Name"
938
+ }, undefined, false, undefined, this),
939
+ /* @__PURE__ */ jsxDEV3("input", {
940
+ id: "org-name",
941
+ type: "text",
942
+ defaultValue: "Demo Organization",
943
+ className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2"
944
+ }, undefined, false, undefined, this)
945
+ ]
946
+ }, undefined, true, undefined, this),
947
+ /* @__PURE__ */ jsxDEV3("div", {
948
+ children: [
949
+ /* @__PURE__ */ jsxDEV3("label", {
950
+ htmlFor: "timezone",
951
+ className: "text-sm font-medium",
952
+ children: "Default Timezone"
953
+ }, undefined, false, undefined, this),
954
+ /* @__PURE__ */ jsxDEV3("select", {
955
+ id: "timezone",
956
+ className: "border-input bg-background mt-1 block w-full rounded-md border px-3 py-2",
957
+ children: [
958
+ /* @__PURE__ */ jsxDEV3("option", {
959
+ children: "UTC"
960
+ }, undefined, false, undefined, this),
961
+ /* @__PURE__ */ jsxDEV3("option", {
962
+ children: "America/New_York"
963
+ }, undefined, false, undefined, this),
964
+ /* @__PURE__ */ jsxDEV3("option", {
965
+ children: "Europe/London"
966
+ }, undefined, false, undefined, this),
967
+ /* @__PURE__ */ jsxDEV3("option", {
968
+ children: "Asia/Tokyo"
969
+ }, undefined, false, undefined, this)
970
+ ]
971
+ }, undefined, true, undefined, this)
972
+ ]
973
+ }, undefined, true, undefined, this),
974
+ /* @__PURE__ */ jsxDEV3("div", {
975
+ className: "pt-2",
976
+ children: /* @__PURE__ */ jsxDEV3(Button3, {
977
+ onPress: () => alert("Settings saved!"),
978
+ children: "Save Settings"
979
+ }, undefined, false, undefined, this)
980
+ }, undefined, false, undefined, this)
981
+ ]
982
+ }, undefined, true, undefined, this)
983
+ ]
984
+ }, undefined, true, undefined, this)
985
+ }, undefined, false, undefined, this);
294
986
  }
295
-
296
- //#endregion
297
- export { SaasDashboard };
298
- //# sourceMappingURL=SaasDashboard.js.map
987
+ export {
988
+ SaasDashboard
989
+ };