@contractspec/example.crm-pipeline 1.57.0 → 1.59.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 (259) hide show
  1. package/.turbo/turbo-build.log +148 -164
  2. package/.turbo/turbo-prebuild.log +1 -0
  3. package/CHANGELOG.md +39 -0
  4. package/dist/browser/crm-pipeline.feature.js +75 -0
  5. package/dist/browser/deal/deal.enum.js +18 -0
  6. package/dist/browser/deal/deal.operation.js +396 -0
  7. package/dist/browser/deal/deal.schema.js +141 -0
  8. package/dist/browser/deal/deal.test-spec.js +58 -0
  9. package/dist/browser/deal/index.js +408 -0
  10. package/dist/browser/docs/crm-pipeline.docblock.js +113 -0
  11. package/dist/browser/docs/index.js +113 -0
  12. package/dist/browser/entities/company.entity.js +52 -0
  13. package/dist/browser/entities/contact.entity.js +66 -0
  14. package/dist/browser/entities/deal.entity.js +107 -0
  15. package/dist/browser/entities/index.js +343 -0
  16. package/dist/browser/entities/task.entity.js +99 -0
  17. package/dist/browser/events/contact.event.js +31 -0
  18. package/dist/browser/events/deal.event.js +101 -0
  19. package/dist/browser/events/index.js +158 -0
  20. package/dist/browser/events/task.event.js +28 -0
  21. package/dist/browser/example.js +39 -0
  22. package/dist/browser/handlers/crm.handlers.js +160 -0
  23. package/dist/browser/handlers/deal.handlers.js +293 -0
  24. package/dist/browser/handlers/index.js +456 -0
  25. package/dist/browser/handlers/mock-data.js +165 -0
  26. package/dist/browser/index.js +3279 -0
  27. package/dist/browser/operations/index.js +407 -0
  28. package/dist/browser/presentations/dashboard.presentation.js +52 -0
  29. package/dist/browser/presentations/index.js +284 -0
  30. package/dist/browser/presentations/pipeline.presentation.js +233 -0
  31. package/dist/browser/seeders/index.js +22 -0
  32. package/dist/browser/shared/overlay-types.js +0 -0
  33. package/dist/browser/ui/CrmDashboard.js +1325 -0
  34. package/dist/browser/ui/CrmDealCard.js +50 -0
  35. package/dist/browser/ui/CrmPipelineBoard.js +160 -0
  36. package/dist/browser/ui/hooks/index.js +186 -0
  37. package/dist/browser/ui/hooks/useDealList.js +84 -0
  38. package/dist/browser/ui/hooks/useDealMutations.js +100 -0
  39. package/dist/browser/ui/index.js +1972 -0
  40. package/dist/browser/ui/modals/CreateDealModal.js +211 -0
  41. package/dist/browser/ui/modals/DealActionsModal.js +428 -0
  42. package/dist/browser/ui/modals/index.js +638 -0
  43. package/dist/browser/ui/overlays/demo-overlays.js +55 -0
  44. package/dist/browser/ui/overlays/index.js +55 -0
  45. package/dist/browser/ui/renderers/index.js +827 -0
  46. package/dist/browser/ui/renderers/pipeline.markdown.js +564 -0
  47. package/dist/browser/ui/renderers/pipeline.renderer.js +264 -0
  48. package/dist/crm-pipeline.feature.d.ts +1 -6
  49. package/dist/crm-pipeline.feature.d.ts.map +1 -1
  50. package/dist/crm-pipeline.feature.js +74 -164
  51. package/dist/deal/deal.enum.d.ts +2 -7
  52. package/dist/deal/deal.enum.d.ts.map +1 -1
  53. package/dist/deal/deal.enum.js +16 -22
  54. package/dist/deal/deal.operation.d.ts +444 -450
  55. package/dist/deal/deal.operation.d.ts.map +1 -1
  56. package/dist/deal/deal.operation.js +390 -263
  57. package/dist/deal/deal.schema.d.ts +251 -256
  58. package/dist/deal/deal.schema.d.ts.map +1 -1
  59. package/dist/deal/deal.schema.js +131 -275
  60. package/dist/deal/deal.test-spec.d.ts +2 -7
  61. package/dist/deal/deal.test-spec.d.ts.map +1 -1
  62. package/dist/deal/deal.test-spec.js +56 -62
  63. package/dist/deal/index.d.ts +7 -4
  64. package/dist/deal/index.d.ts.map +1 -0
  65. package/dist/deal/index.js +408 -4
  66. package/dist/docs/crm-pipeline.docblock.d.ts +2 -1
  67. package/dist/docs/crm-pipeline.docblock.d.ts.map +1 -0
  68. package/dist/docs/crm-pipeline.docblock.js +45 -51
  69. package/dist/docs/index.d.ts +2 -1
  70. package/dist/docs/index.d.ts.map +1 -0
  71. package/dist/docs/index.js +114 -1
  72. package/dist/entities/company.entity.d.ts +27 -32
  73. package/dist/entities/company.entity.d.ts.map +1 -1
  74. package/dist/entities/company.entity.js +51 -61
  75. package/dist/entities/contact.entity.d.ts +31 -36
  76. package/dist/entities/contact.entity.d.ts.map +1 -1
  77. package/dist/entities/contact.entity.js +65 -76
  78. package/dist/entities/deal.entity.d.ts +52 -57
  79. package/dist/entities/deal.entity.d.ts.map +1 -1
  80. package/dist/entities/deal.entity.js +104 -116
  81. package/dist/entities/index.d.ts +6 -10
  82. package/dist/entities/index.d.ts.map +1 -1
  83. package/dist/entities/index.js +342 -31
  84. package/dist/entities/task.entity.d.ts +42 -47
  85. package/dist/entities/task.entity.d.ts.map +1 -1
  86. package/dist/entities/task.entity.js +95 -124
  87. package/dist/events/contact.event.d.ts +21 -27
  88. package/dist/events/contact.event.d.ts.map +1 -1
  89. package/dist/events/contact.event.js +29 -42
  90. package/dist/events/deal.event.d.ts +100 -106
  91. package/dist/events/deal.event.d.ts.map +1 -1
  92. package/dist/events/deal.event.js +93 -163
  93. package/dist/events/index.d.ts +4 -4
  94. package/dist/events/index.d.ts.map +1 -0
  95. package/dist/events/index.js +158 -4
  96. package/dist/events/task.event.d.ts +21 -27
  97. package/dist/events/task.event.d.ts.map +1 -1
  98. package/dist/events/task.event.js +26 -42
  99. package/dist/example.d.ts +2 -6
  100. package/dist/example.d.ts.map +1 -1
  101. package/dist/example.js +38 -50
  102. package/dist/handlers/crm.handlers.d.ts +80 -78
  103. package/dist/handlers/crm.handlers.d.ts.map +1 -1
  104. package/dist/handlers/crm.handlers.js +155 -166
  105. package/dist/handlers/deal.handlers.d.ts +58 -63
  106. package/dist/handlers/deal.handlers.d.ts.map +1 -1
  107. package/dist/handlers/deal.handlers.js +279 -105
  108. package/dist/handlers/index.d.ts +10 -4
  109. package/dist/handlers/index.d.ts.map +1 -0
  110. package/dist/handlers/index.js +456 -4
  111. package/dist/handlers/mock-data.d.ts +38 -41
  112. package/dist/handlers/mock-data.d.ts.map +1 -1
  113. package/dist/handlers/mock-data.js +162 -184
  114. package/dist/index.d.ts +13 -42
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +3277 -53
  117. package/dist/node/crm-pipeline.feature.js +75 -0
  118. package/dist/node/deal/deal.enum.js +18 -0
  119. package/dist/node/deal/deal.operation.js +396 -0
  120. package/dist/node/deal/deal.schema.js +141 -0
  121. package/dist/node/deal/deal.test-spec.js +58 -0
  122. package/dist/node/deal/index.js +408 -0
  123. package/dist/node/docs/crm-pipeline.docblock.js +113 -0
  124. package/dist/node/docs/index.js +113 -0
  125. package/dist/node/entities/company.entity.js +52 -0
  126. package/dist/node/entities/contact.entity.js +66 -0
  127. package/dist/node/entities/deal.entity.js +107 -0
  128. package/dist/node/entities/index.js +343 -0
  129. package/dist/node/entities/task.entity.js +99 -0
  130. package/dist/node/events/contact.event.js +31 -0
  131. package/dist/node/events/deal.event.js +101 -0
  132. package/dist/node/events/index.js +158 -0
  133. package/dist/node/events/task.event.js +28 -0
  134. package/dist/node/example.js +39 -0
  135. package/dist/node/handlers/crm.handlers.js +160 -0
  136. package/dist/node/handlers/deal.handlers.js +293 -0
  137. package/dist/node/handlers/index.js +456 -0
  138. package/dist/node/handlers/mock-data.js +165 -0
  139. package/dist/node/index.js +3279 -0
  140. package/dist/node/operations/index.js +407 -0
  141. package/dist/node/presentations/dashboard.presentation.js +52 -0
  142. package/dist/node/presentations/index.js +284 -0
  143. package/dist/node/presentations/pipeline.presentation.js +233 -0
  144. package/dist/node/seeders/index.js +22 -0
  145. package/dist/node/shared/overlay-types.js +0 -0
  146. package/dist/node/ui/CrmDashboard.js +1325 -0
  147. package/dist/node/ui/CrmDealCard.js +50 -0
  148. package/dist/node/ui/CrmPipelineBoard.js +160 -0
  149. package/dist/node/ui/hooks/index.js +186 -0
  150. package/dist/node/ui/hooks/useDealList.js +84 -0
  151. package/dist/node/ui/hooks/useDealMutations.js +100 -0
  152. package/dist/node/ui/index.js +1972 -0
  153. package/dist/node/ui/modals/CreateDealModal.js +211 -0
  154. package/dist/node/ui/modals/DealActionsModal.js +428 -0
  155. package/dist/node/ui/modals/index.js +638 -0
  156. package/dist/node/ui/overlays/demo-overlays.js +55 -0
  157. package/dist/node/ui/overlays/index.js +55 -0
  158. package/dist/node/ui/renderers/index.js +827 -0
  159. package/dist/node/ui/renderers/pipeline.markdown.js +564 -0
  160. package/dist/node/ui/renderers/pipeline.renderer.js +264 -0
  161. package/dist/operations/index.d.ts +2 -5
  162. package/dist/operations/index.d.ts.map +1 -0
  163. package/dist/operations/index.js +407 -5
  164. package/dist/presentations/dashboard.presentation.d.ts +2 -7
  165. package/dist/presentations/dashboard.presentation.d.ts.map +1 -1
  166. package/dist/presentations/dashboard.presentation.js +51 -60
  167. package/dist/presentations/index.d.ts +3 -3
  168. package/dist/presentations/index.d.ts.map +1 -0
  169. package/dist/presentations/index.js +284 -3
  170. package/dist/presentations/pipeline.presentation.d.ts +4 -9
  171. package/dist/presentations/pipeline.presentation.d.ts.map +1 -1
  172. package/dist/presentations/pipeline.presentation.js +228 -116
  173. package/dist/seeders/index.d.ts +4 -8
  174. package/dist/seeders/index.d.ts.map +1 -1
  175. package/dist/seeders/index.js +21 -45
  176. package/dist/shared/overlay-types.d.ts +25 -28
  177. package/dist/shared/overlay-types.d.ts.map +1 -1
  178. package/dist/shared/overlay-types.js +1 -0
  179. package/dist/ui/CrmDashboard.d.ts +1 -6
  180. package/dist/ui/CrmDashboard.d.ts.map +1 -1
  181. package/dist/ui/CrmDashboard.js +1318 -296
  182. package/dist/ui/CrmDealCard.d.ts +8 -12
  183. package/dist/ui/CrmDealCard.d.ts.map +1 -1
  184. package/dist/ui/CrmDealCard.js +47 -45
  185. package/dist/ui/CrmPipelineBoard.d.ts +11 -20
  186. package/dist/ui/CrmPipelineBoard.d.ts.map +1 -1
  187. package/dist/ui/CrmPipelineBoard.js +157 -94
  188. package/dist/ui/hooks/index.d.ts +3 -3
  189. package/dist/ui/hooks/index.d.ts.map +1 -0
  190. package/dist/ui/hooks/index.js +185 -4
  191. package/dist/ui/hooks/useDealList.d.ts +28 -32
  192. package/dist/ui/hooks/useDealList.d.ts.map +1 -1
  193. package/dist/ui/hooks/useDealList.js +81 -90
  194. package/dist/ui/hooks/useDealMutations.d.ts +18 -22
  195. package/dist/ui/hooks/useDealMutations.d.ts.map +1 -1
  196. package/dist/ui/hooks/useDealMutations.js +97 -155
  197. package/dist/ui/index.d.ts +8 -14
  198. package/dist/ui/index.d.ts.map +1 -0
  199. package/dist/ui/index.js +1973 -15
  200. package/dist/ui/modals/CreateDealModal.d.ts +19 -29
  201. package/dist/ui/modals/CreateDealModal.d.ts.map +1 -1
  202. package/dist/ui/modals/CreateDealModal.js +209 -180
  203. package/dist/ui/modals/DealActionsModal.d.ts +31 -44
  204. package/dist/ui/modals/DealActionsModal.d.ts.map +1 -1
  205. package/dist/ui/modals/DealActionsModal.js +424 -367
  206. package/dist/ui/modals/index.d.ts +3 -3
  207. package/dist/ui/modals/index.d.ts.map +1 -0
  208. package/dist/ui/modals/index.js +638 -3
  209. package/dist/ui/overlays/demo-overlays.d.ts +10 -8
  210. package/dist/ui/overlays/demo-overlays.d.ts.map +1 -1
  211. package/dist/ui/overlays/demo-overlays.js +54 -66
  212. package/dist/ui/overlays/index.d.ts +2 -2
  213. package/dist/ui/overlays/index.d.ts.map +1 -0
  214. package/dist/ui/overlays/index.js +56 -3
  215. package/dist/ui/renderers/index.d.ts +3 -3
  216. package/dist/ui/renderers/index.d.ts.map +1 -0
  217. package/dist/ui/renderers/index.js +827 -3
  218. package/dist/ui/renderers/pipeline.markdown.d.ts +12 -11
  219. package/dist/ui/renderers/pipeline.markdown.d.ts.map +1 -1
  220. package/dist/ui/renderers/pipeline.markdown.js +560 -114
  221. package/dist/ui/renderers/pipeline.renderer.d.ts +9 -7
  222. package/dist/ui/renderers/pipeline.renderer.d.ts.map +1 -1
  223. package/dist/ui/renderers/pipeline.renderer.js +261 -24
  224. package/package.json +476 -90
  225. package/tsdown.config.js +1 -2
  226. package/.turbo/turbo-build$colon$bundle.log +0 -164
  227. package/dist/crm-pipeline.feature.js.map +0 -1
  228. package/dist/deal/deal.enum.js.map +0 -1
  229. package/dist/deal/deal.operation.js.map +0 -1
  230. package/dist/deal/deal.schema.js.map +0 -1
  231. package/dist/deal/deal.test-spec.js.map +0 -1
  232. package/dist/docs/crm-pipeline.docblock.js.map +0 -1
  233. package/dist/entities/company.entity.js.map +0 -1
  234. package/dist/entities/contact.entity.js.map +0 -1
  235. package/dist/entities/deal.entity.js.map +0 -1
  236. package/dist/entities/index.js.map +0 -1
  237. package/dist/entities/task.entity.js.map +0 -1
  238. package/dist/events/contact.event.js.map +0 -1
  239. package/dist/events/deal.event.js.map +0 -1
  240. package/dist/events/task.event.js.map +0 -1
  241. package/dist/example.js.map +0 -1
  242. package/dist/handlers/crm.handlers.js.map +0 -1
  243. package/dist/handlers/deal.handlers.js.map +0 -1
  244. package/dist/handlers/mock-data.js.map +0 -1
  245. package/dist/index.js.map +0 -1
  246. package/dist/presentations/dashboard.presentation.js.map +0 -1
  247. package/dist/presentations/pipeline.presentation.js.map +0 -1
  248. package/dist/seeders/index.js.map +0 -1
  249. package/dist/ui/CrmDashboard.js.map +0 -1
  250. package/dist/ui/CrmDealCard.js.map +0 -1
  251. package/dist/ui/CrmPipelineBoard.js.map +0 -1
  252. package/dist/ui/hooks/useDealList.js.map +0 -1
  253. package/dist/ui/hooks/useDealMutations.js.map +0 -1
  254. package/dist/ui/modals/CreateDealModal.js.map +0 -1
  255. package/dist/ui/modals/DealActionsModal.js.map +0 -1
  256. package/dist/ui/overlays/demo-overlays.js.map +0 -1
  257. package/dist/ui/renderers/pipeline.markdown.js.map +0 -1
  258. package/dist/ui/renderers/pipeline.renderer.js.map +0 -1
  259. package/tsconfig.tsbuildinfo +0 -1
@@ -1,304 +1,1326 @@
1
- 'use client';
1
+ // @bun
2
+ // src/ui/hooks/useDealList.ts
3
+ import { useCallback, useEffect, useMemo, useState } from "react";
4
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
5
+ "use client";
6
+ function useDealList(options = {}) {
7
+ const { handlers, projectId } = useTemplateRuntime();
8
+ const { crm } = handlers;
9
+ const [data, setData] = useState(null);
10
+ const [dealsByStage, setDealsByStage] = useState({});
11
+ const [stages, setStages] = useState([]);
12
+ const [loading, setLoading] = useState(true);
13
+ const [error, setError] = useState(null);
14
+ const [page, setPage] = useState(1);
15
+ const pipelineId = options.pipelineId ?? "pipeline-1";
16
+ const fetchData = useCallback(async () => {
17
+ setLoading(true);
18
+ setError(null);
19
+ try {
20
+ const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
21
+ crm.listDeals({
22
+ projectId,
23
+ pipelineId,
24
+ stageId: options.stageId,
25
+ status: options.status === "all" ? undefined : options.status,
26
+ search: options.search,
27
+ limit: options.limit ?? 50,
28
+ offset: (page - 1) * (options.limit ?? 50)
29
+ }),
30
+ crm.getDealsByStage({ projectId, pipelineId }),
31
+ crm.getPipelineStages({ pipelineId })
32
+ ]);
33
+ setData(dealsResult);
34
+ setDealsByStage(stageDealsResult);
35
+ setStages(stagesResult);
36
+ } catch (err) {
37
+ setError(err instanceof Error ? err : new Error("Unknown error"));
38
+ } finally {
39
+ setLoading(false);
40
+ }
41
+ }, [
42
+ crm,
43
+ projectId,
44
+ pipelineId,
45
+ options.stageId,
46
+ options.status,
47
+ options.search,
48
+ options.limit,
49
+ page
50
+ ]);
51
+ useEffect(() => {
52
+ fetchData();
53
+ }, [fetchData]);
54
+ const stats = useMemo(() => {
55
+ if (!data)
56
+ return null;
57
+ const open = data.deals.filter((d) => d.status === "OPEN");
58
+ const won = data.deals.filter((d) => d.status === "WON");
59
+ const lost = data.deals.filter((d) => d.status === "LOST");
60
+ return {
61
+ total: data.total,
62
+ totalValue: data.totalValue,
63
+ openCount: open.length,
64
+ openValue: open.reduce((sum, d) => sum + d.value, 0),
65
+ wonCount: won.length,
66
+ wonValue: won.reduce((sum, d) => sum + d.value, 0),
67
+ lostCount: lost.length
68
+ };
69
+ }, [data]);
70
+ return {
71
+ data,
72
+ dealsByStage,
73
+ stages,
74
+ loading,
75
+ error,
76
+ stats,
77
+ page,
78
+ refetch: fetchData,
79
+ nextPage: () => setPage((p) => p + 1),
80
+ prevPage: () => page > 1 && setPage((p) => p - 1)
81
+ };
82
+ }
83
+
84
+ // src/ui/hooks/useDealMutations.ts
85
+ import { useCallback as useCallback2, useState as useState2 } from "react";
86
+ import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
87
+ function useDealMutations(options = {}) {
88
+ const { handlers, projectId } = useTemplateRuntime2();
89
+ const { crm } = handlers;
90
+ const [createState, setCreateState] = useState2({
91
+ loading: false,
92
+ error: null,
93
+ data: null
94
+ });
95
+ const [moveState, setMoveState] = useState2({
96
+ loading: false,
97
+ error: null,
98
+ data: null
99
+ });
100
+ const [winState, setWinState] = useState2({
101
+ loading: false,
102
+ error: null,
103
+ data: null
104
+ });
105
+ const [loseState, setLoseState] = useState2({
106
+ loading: false,
107
+ error: null,
108
+ data: null
109
+ });
110
+ const createDeal = useCallback2(async (input) => {
111
+ setCreateState({ loading: true, error: null, data: null });
112
+ try {
113
+ const result = await crm.createDeal(input, {
114
+ projectId,
115
+ ownerId: "user-1"
116
+ });
117
+ setCreateState({ loading: false, error: null, data: result });
118
+ options.onSuccess?.();
119
+ return result;
120
+ } catch (err) {
121
+ const error = err instanceof Error ? err : new Error("Failed to create deal");
122
+ setCreateState({ loading: false, error, data: null });
123
+ options.onError?.(error);
124
+ return null;
125
+ }
126
+ }, [crm, projectId, options]);
127
+ const moveDeal = useCallback2(async (input) => {
128
+ setMoveState({ loading: true, error: null, data: null });
129
+ try {
130
+ const result = await crm.moveDeal(input);
131
+ setMoveState({ loading: false, error: null, data: result });
132
+ options.onSuccess?.();
133
+ return result;
134
+ } catch (err) {
135
+ const error = err instanceof Error ? err : new Error("Failed to move deal");
136
+ setMoveState({ loading: false, error, data: null });
137
+ options.onError?.(error);
138
+ return null;
139
+ }
140
+ }, [crm, options]);
141
+ const winDeal = useCallback2(async (input) => {
142
+ setWinState({ loading: true, error: null, data: null });
143
+ try {
144
+ const result = await crm.winDeal(input);
145
+ setWinState({ loading: false, error: null, data: result });
146
+ options.onSuccess?.();
147
+ return result;
148
+ } catch (err) {
149
+ const error = err instanceof Error ? err : new Error("Failed to mark deal as won");
150
+ setWinState({ loading: false, error, data: null });
151
+ options.onError?.(error);
152
+ return null;
153
+ }
154
+ }, [crm, options]);
155
+ const loseDeal = useCallback2(async (input) => {
156
+ setLoseState({ loading: true, error: null, data: null });
157
+ try {
158
+ const result = await crm.loseDeal(input);
159
+ setLoseState({ loading: false, error: null, data: result });
160
+ options.onSuccess?.();
161
+ return result;
162
+ } catch (err) {
163
+ const error = err instanceof Error ? err : new Error("Failed to mark deal as lost");
164
+ setLoseState({ loading: false, error, data: null });
165
+ options.onError?.(error);
166
+ return null;
167
+ }
168
+ }, [crm, options]);
169
+ return {
170
+ createDeal,
171
+ moveDeal,
172
+ winDeal,
173
+ loseDeal,
174
+ createState,
175
+ moveState,
176
+ winState,
177
+ loseState,
178
+ isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
179
+ };
180
+ }
181
+
182
+ // src/ui/CrmDealCard.tsx
183
+ import { jsxDEV } from "react/jsx-dev-runtime";
184
+ "use client";
185
+ function formatCurrency(value, currency) {
186
+ return new Intl.NumberFormat("en-US", {
187
+ style: "currency",
188
+ currency,
189
+ minimumFractionDigits: 0,
190
+ maximumFractionDigits: 0
191
+ }).format(value);
192
+ }
193
+ function CrmDealCard({ deal, onClick }) {
194
+ const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
195
+ return /* @__PURE__ */ jsxDEV("div", {
196
+ onClick,
197
+ className: "border-border bg-card cursor-pointer rounded-lg border p-3 shadow-sm transition-shadow hover:shadow-md",
198
+ role: "button",
199
+ tabIndex: 0,
200
+ onKeyDown: (e) => {
201
+ if (e.key === "Enter" || e.key === " ")
202
+ onClick?.();
203
+ },
204
+ children: [
205
+ /* @__PURE__ */ jsxDEV("h4", {
206
+ className: "leading-snug font-medium",
207
+ children: deal.name
208
+ }, undefined, false, undefined, this),
209
+ /* @__PURE__ */ jsxDEV("div", {
210
+ className: "text-primary mt-2 text-lg font-semibold",
211
+ children: formatCurrency(deal.value, deal.currency)
212
+ }, undefined, false, undefined, this),
213
+ /* @__PURE__ */ jsxDEV("div", {
214
+ className: "text-muted-foreground mt-3 flex items-center justify-between text-xs",
215
+ children: [
216
+ daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
217
+ className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
218
+ children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
219
+ }, undefined, false, undefined, this),
220
+ /* @__PURE__ */ jsxDEV("span", {
221
+ className: `rounded px-1.5 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
222
+ children: deal.status
223
+ }, undefined, false, undefined, this)
224
+ ]
225
+ }, undefined, true, undefined, this)
226
+ ]
227
+ }, undefined, true, undefined, this);
228
+ }
229
+
230
+ // src/ui/CrmPipelineBoard.tsx
231
+ import { useState as useState3 } from "react";
232
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
233
+ "use client";
234
+ function formatCurrency2(value) {
235
+ if (value >= 1e6)
236
+ return `$${(value / 1e6).toFixed(1)}M`;
237
+ if (value >= 1000)
238
+ return `$${(value / 1000).toFixed(0)}K`;
239
+ return `$${value}`;
240
+ }
241
+ function CrmPipelineBoard({
242
+ dealsByStage,
243
+ stages,
244
+ onDealClick,
245
+ onDealMove
246
+ }) {
247
+ const [quickMoveOpen, setQuickMoveOpen] = useState3(null);
248
+ const sortedStages = [...stages].sort((a, b) => a.position - b.position);
249
+ const handleQuickMove = (dealId, toStageId) => {
250
+ onDealMove?.(dealId, toStageId);
251
+ setQuickMoveOpen(null);
252
+ };
253
+ return /* @__PURE__ */ jsxDEV2("div", {
254
+ className: "flex gap-4 overflow-x-auto pb-4",
255
+ children: sortedStages.map((stage) => {
256
+ const deals = dealsByStage[stage.id] ?? [];
257
+ const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
258
+ return /* @__PURE__ */ jsxDEV2("div", {
259
+ className: "bg-muted/30 flex w-72 flex-shrink-0 flex-col rounded-lg",
260
+ children: [
261
+ /* @__PURE__ */ jsxDEV2("div", {
262
+ className: "border-border flex items-center justify-between border-b px-3 py-2",
263
+ children: [
264
+ /* @__PURE__ */ jsxDEV2("div", {
265
+ children: [
266
+ /* @__PURE__ */ jsxDEV2("h3", {
267
+ className: "font-medium",
268
+ children: stage.name
269
+ }, undefined, false, undefined, this),
270
+ /* @__PURE__ */ jsxDEV2("p", {
271
+ className: "text-muted-foreground text-xs",
272
+ children: [
273
+ deals.length,
274
+ " deals \xB7 ",
275
+ formatCurrency2(stageValue)
276
+ ]
277
+ }, undefined, true, undefined, this)
278
+ ]
279
+ }, undefined, true, undefined, this),
280
+ /* @__PURE__ */ jsxDEV2("span", {
281
+ className: "bg-muted flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium",
282
+ children: deals.length
283
+ }, undefined, false, undefined, this)
284
+ ]
285
+ }, undefined, true, undefined, this),
286
+ /* @__PURE__ */ jsxDEV2("div", {
287
+ className: "flex flex-1 flex-col gap-2 p-2",
288
+ children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
289
+ className: "border-muted-foreground/20 text-muted-foreground flex h-24 items-center justify-center rounded-md border-2 border-dashed text-xs",
290
+ children: "No deals"
291
+ }, undefined, false, undefined, this) : deals.map((deal) => /* @__PURE__ */ jsxDEV2("div", {
292
+ className: "group relative",
293
+ children: [
294
+ /* @__PURE__ */ jsxDEV2(CrmDealCard, {
295
+ deal,
296
+ onClick: () => onDealClick?.(deal.id)
297
+ }, undefined, false, undefined, this),
298
+ deal.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
299
+ className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
300
+ children: [
301
+ /* @__PURE__ */ jsxDEV2("button", {
302
+ type: "button",
303
+ onClick: (e) => {
304
+ e.stopPropagation();
305
+ setQuickMoveOpen(quickMoveOpen === deal.id ? null : deal.id);
306
+ },
307
+ className: "bg-background border-border hover:bg-muted flex h-6 w-6 items-center justify-center rounded border text-xs shadow-sm",
308
+ title: "Quick move",
309
+ children: "\u27A1\uFE0F"
310
+ }, undefined, false, undefined, this),
311
+ quickMoveOpen === deal.id && /* @__PURE__ */ jsxDEV2("div", {
312
+ className: "bg-card border-border absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border py-1 shadow-lg",
313
+ children: [
314
+ /* @__PURE__ */ jsxDEV2("p", {
315
+ className: "text-muted-foreground px-3 py-1 text-xs font-medium",
316
+ children: "Move to:"
317
+ }, undefined, false, undefined, this),
318
+ sortedStages.filter((s) => s.id !== deal.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
319
+ type: "button",
320
+ onClick: (e) => {
321
+ e.stopPropagation();
322
+ handleQuickMove(deal.id, s.id);
323
+ },
324
+ className: "hover:bg-muted w-full px-3 py-1.5 text-left text-sm",
325
+ children: s.name
326
+ }, s.id, false, undefined, this))
327
+ ]
328
+ }, undefined, true, undefined, this)
329
+ ]
330
+ }, undefined, true, undefined, this)
331
+ ]
332
+ }, deal.id, true, undefined, this))
333
+ }, undefined, false, undefined, this)
334
+ ]
335
+ }, stage.id, true, undefined, this);
336
+ })
337
+ }, undefined, false, undefined, this);
338
+ }
2
339
 
3
- import { useDealList } from "./hooks/useDealList.js";
4
- import { useDealMutations } from "./hooks/useDealMutations.js";
5
- import { CrmPipelineBoard } from "./CrmPipelineBoard.js";
6
- import { CreateDealModal } from "./modals/CreateDealModal.js";
7
- import { DealActionsModal } from "./modals/DealActionsModal.js";
8
- import { useCallback, useState } from "react";
9
- import { Button, ErrorState, LoaderBlock, StatCard, StatCardGroup } from "@contractspec/lib.design-system";
10
- import { Tabs, TabsContent, TabsList, TabsTrigger } from "@contractspec/lib.ui-kit-web/ui/tabs";
11
- import { jsx, jsxs } from "react/jsx-runtime";
340
+ // src/ui/modals/CreateDealModal.tsx
341
+ import { useState as useState4 } from "react";
342
+ import { Button, Input } from "@contractspec/lib.design-system";
343
+ import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
344
+ "use client";
345
+ var CURRENCIES = ["USD", "EUR", "GBP", "CAD"];
346
+ var DEFAULT_PIPELINE_ID = "pipeline-1";
347
+ function CreateDealModal({
348
+ isOpen,
349
+ onClose,
350
+ onSubmit,
351
+ stages,
352
+ isLoading = false
353
+ }) {
354
+ const [name, setName] = useState4("");
355
+ const [value, setValue] = useState4("");
356
+ const [currency, setCurrency] = useState4("USD");
357
+ const [stageId, setStageId] = useState4(stages[0]?.id ?? "");
358
+ const [expectedCloseDate, setExpectedCloseDate] = useState4("");
359
+ const [error, setError] = useState4(null);
360
+ const handleSubmit = async (e) => {
361
+ e.preventDefault();
362
+ setError(null);
363
+ if (!name.trim()) {
364
+ setError("Deal name is required");
365
+ return;
366
+ }
367
+ const numericValue = parseFloat(value);
368
+ if (isNaN(numericValue) || numericValue <= 0) {
369
+ setError("Value must be a positive number");
370
+ return;
371
+ }
372
+ if (!stageId) {
373
+ setError("Please select a pipeline stage");
374
+ return;
375
+ }
376
+ try {
377
+ await onSubmit({
378
+ name: name.trim(),
379
+ value: numericValue,
380
+ currency,
381
+ pipelineId: DEFAULT_PIPELINE_ID,
382
+ stageId,
383
+ expectedCloseDate: expectedCloseDate ? new Date(expectedCloseDate) : undefined
384
+ });
385
+ setName("");
386
+ setValue("");
387
+ setCurrency("USD");
388
+ setStageId(stages[0]?.id ?? "");
389
+ setExpectedCloseDate("");
390
+ onClose();
391
+ } catch (err) {
392
+ setError(err instanceof Error ? err.message : "Failed to create deal");
393
+ }
394
+ };
395
+ if (!isOpen)
396
+ return null;
397
+ return /* @__PURE__ */ jsxDEV3("div", {
398
+ className: "fixed inset-0 z-50 flex items-center justify-center",
399
+ children: [
400
+ /* @__PURE__ */ jsxDEV3("div", {
401
+ className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
402
+ onClick: onClose,
403
+ role: "button",
404
+ tabIndex: 0,
405
+ onKeyDown: (e) => {
406
+ if (e.key === "Enter" || e.key === " ")
407
+ onClose();
408
+ },
409
+ "aria-label": "Close modal"
410
+ }, undefined, false, undefined, this),
411
+ /* @__PURE__ */ jsxDEV3("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__ */ jsxDEV3("h2", {
415
+ className: "mb-4 text-xl font-semibold",
416
+ children: "Create New Deal"
417
+ }, undefined, false, undefined, this),
418
+ /* @__PURE__ */ jsxDEV3("form", {
419
+ onSubmit: handleSubmit,
420
+ className: "space-y-4",
421
+ children: [
422
+ /* @__PURE__ */ jsxDEV3("div", {
423
+ children: [
424
+ /* @__PURE__ */ jsxDEV3("label", {
425
+ htmlFor: "deal-name",
426
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
427
+ children: "Deal Name *"
428
+ }, undefined, false, undefined, this),
429
+ /* @__PURE__ */ jsxDEV3(Input, {
430
+ id: "deal-name",
431
+ value: name,
432
+ onChange: (e) => setName(e.target.value),
433
+ placeholder: "e.g., Enterprise License - Acme Corp",
434
+ disabled: isLoading
435
+ }, undefined, false, undefined, this)
436
+ ]
437
+ }, undefined, true, undefined, this),
438
+ /* @__PURE__ */ jsxDEV3("div", {
439
+ className: "flex gap-3",
440
+ children: [
441
+ /* @__PURE__ */ jsxDEV3("div", {
442
+ className: "flex-1",
443
+ children: [
444
+ /* @__PURE__ */ jsxDEV3("label", {
445
+ htmlFor: "deal-value",
446
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
447
+ children: "Value *"
448
+ }, undefined, false, undefined, this),
449
+ /* @__PURE__ */ jsxDEV3(Input, {
450
+ id: "deal-value",
451
+ type: "number",
452
+ min: "0",
453
+ step: "0.01",
454
+ value,
455
+ onChange: (e) => setValue(e.target.value),
456
+ placeholder: "50000",
457
+ disabled: isLoading
458
+ }, undefined, false, undefined, this)
459
+ ]
460
+ }, undefined, true, undefined, this),
461
+ /* @__PURE__ */ jsxDEV3("div", {
462
+ className: "w-24",
463
+ children: [
464
+ /* @__PURE__ */ jsxDEV3("label", {
465
+ htmlFor: "deal-currency",
466
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
467
+ children: "Currency"
468
+ }, undefined, false, undefined, this),
469
+ /* @__PURE__ */ jsxDEV3("select", {
470
+ id: "deal-currency",
471
+ value: currency,
472
+ onChange: (e) => setCurrency(e.target.value),
473
+ disabled: isLoading,
474
+ 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",
475
+ children: CURRENCIES.map((c) => /* @__PURE__ */ jsxDEV3("option", {
476
+ value: c,
477
+ children: c
478
+ }, c, false, undefined, this))
479
+ }, undefined, false, undefined, this)
480
+ ]
481
+ }, undefined, true, undefined, this)
482
+ ]
483
+ }, undefined, true, undefined, this),
484
+ /* @__PURE__ */ jsxDEV3("div", {
485
+ children: [
486
+ /* @__PURE__ */ jsxDEV3("label", {
487
+ htmlFor: "deal-stage",
488
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
489
+ children: "Pipeline Stage *"
490
+ }, undefined, false, undefined, this),
491
+ /* @__PURE__ */ jsxDEV3("select", {
492
+ id: "deal-stage",
493
+ value: stageId,
494
+ onChange: (e) => setStageId(e.target.value),
495
+ disabled: isLoading,
496
+ 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",
497
+ children: stages.map((stage) => /* @__PURE__ */ jsxDEV3("option", {
498
+ value: stage.id,
499
+ children: stage.name
500
+ }, stage.id, false, undefined, this))
501
+ }, undefined, false, undefined, this)
502
+ ]
503
+ }, undefined, true, undefined, this),
504
+ /* @__PURE__ */ jsxDEV3("div", {
505
+ children: [
506
+ /* @__PURE__ */ jsxDEV3("label", {
507
+ htmlFor: "deal-close-date",
508
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
509
+ children: "Expected Close Date"
510
+ }, undefined, false, undefined, this),
511
+ /* @__PURE__ */ jsxDEV3(Input, {
512
+ id: "deal-close-date",
513
+ type: "date",
514
+ value: expectedCloseDate,
515
+ onChange: (e) => setExpectedCloseDate(e.target.value),
516
+ disabled: isLoading
517
+ }, undefined, false, undefined, this)
518
+ ]
519
+ }, undefined, true, undefined, this),
520
+ error && /* @__PURE__ */ jsxDEV3("div", {
521
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
522
+ children: error
523
+ }, undefined, false, undefined, this),
524
+ /* @__PURE__ */ jsxDEV3("div", {
525
+ className: "flex justify-end gap-3 pt-2",
526
+ children: [
527
+ /* @__PURE__ */ jsxDEV3(Button, {
528
+ type: "button",
529
+ variant: "ghost",
530
+ onPress: onClose,
531
+ disabled: isLoading,
532
+ children: "Cancel"
533
+ }, undefined, false, undefined, this),
534
+ /* @__PURE__ */ jsxDEV3(Button, {
535
+ type: "submit",
536
+ disabled: isLoading,
537
+ children: isLoading ? "Creating..." : "Create Deal"
538
+ }, undefined, false, undefined, this)
539
+ ]
540
+ }, undefined, true, undefined, this)
541
+ ]
542
+ }, undefined, true, undefined, this)
543
+ ]
544
+ }, undefined, true, undefined, this)
545
+ ]
546
+ }, undefined, true, undefined, this);
547
+ }
12
548
 
13
- //#region src/ui/CrmDashboard.tsx
14
- /**
15
- * CRM Dashboard
16
- *
17
- * Fully integrated with ContractSpec example handlers
18
- * and design-system components.
19
- *
20
- * Commands wired:
21
- * - CreateDealContract -> Create Deal button + modal
22
- * - MoveDealContract -> Move deal between stages
23
- * - WinDealContract -> Mark deal as won
24
- * - LoseDealContract -> Mark deal as lost
25
- */
26
- function formatCurrency(value, currency = "USD") {
27
- return new Intl.NumberFormat("en-US", {
28
- style: "currency",
29
- currency,
30
- minimumFractionDigits: 0,
31
- maximumFractionDigits: 0
32
- }).format(value);
549
+ // src/ui/modals/DealActionsModal.tsx
550
+ import { useState as useState5 } from "react";
551
+ import { Button as Button2 } from "@contractspec/lib.design-system";
552
+ import { jsxDEV as jsxDEV4, Fragment } from "react/jsx-dev-runtime";
553
+ "use client";
554
+ function formatCurrency3(value, currency) {
555
+ return new Intl.NumberFormat("en-US", {
556
+ style: "currency",
557
+ currency,
558
+ minimumFractionDigits: 0,
559
+ maximumFractionDigits: 0
560
+ }).format(value);
561
+ }
562
+ function DealActionsModal({
563
+ isOpen,
564
+ deal,
565
+ stages,
566
+ onClose,
567
+ onWin,
568
+ onLose,
569
+ onMove,
570
+ isLoading = false
571
+ }) {
572
+ const [mode, setMode] = useState5("menu");
573
+ const [wonSource, setWonSource] = useState5("");
574
+ const [lostReason, setLostReason] = useState5("");
575
+ const [notes, setNotes] = useState5("");
576
+ const [selectedStageId, setSelectedStageId] = useState5("");
577
+ const [error, setError] = useState5(null);
578
+ const resetForm = () => {
579
+ setMode("menu");
580
+ setWonSource("");
581
+ setLostReason("");
582
+ setNotes("");
583
+ setSelectedStageId("");
584
+ setError(null);
585
+ };
586
+ const handleClose = () => {
587
+ resetForm();
588
+ onClose();
589
+ };
590
+ const handleWin = async () => {
591
+ if (!deal)
592
+ return;
593
+ setError(null);
594
+ try {
595
+ await onWin({
596
+ dealId: deal.id,
597
+ wonSource: wonSource.trim() || undefined,
598
+ notes: notes.trim() || undefined
599
+ });
600
+ handleClose();
601
+ } catch (err) {
602
+ setError(err instanceof Error ? err.message : "Failed to mark deal as won");
603
+ }
604
+ };
605
+ const handleLose = async () => {
606
+ if (!deal)
607
+ return;
608
+ setError(null);
609
+ if (!lostReason.trim()) {
610
+ setError("Please provide a reason for losing the deal");
611
+ return;
612
+ }
613
+ try {
614
+ await onLose({
615
+ dealId: deal.id,
616
+ lostReason: lostReason.trim(),
617
+ notes: notes.trim() || undefined
618
+ });
619
+ handleClose();
620
+ } catch (err) {
621
+ setError(err instanceof Error ? err.message : "Failed to mark deal as lost");
622
+ }
623
+ };
624
+ const handleMove = async () => {
625
+ if (!deal)
626
+ return;
627
+ setError(null);
628
+ if (!selectedStageId) {
629
+ setError("Please select a stage");
630
+ return;
631
+ }
632
+ if (selectedStageId === deal.stageId) {
633
+ setError("Deal is already in this stage");
634
+ return;
635
+ }
636
+ try {
637
+ await onMove({
638
+ dealId: deal.id,
639
+ stageId: selectedStageId
640
+ });
641
+ handleClose();
642
+ } catch (err) {
643
+ setError(err instanceof Error ? err.message : "Failed to move deal");
644
+ }
645
+ };
646
+ if (!isOpen || !deal)
647
+ return null;
648
+ return /* @__PURE__ */ jsxDEV4("div", {
649
+ className: "fixed inset-0 z-50 flex items-center justify-center",
650
+ children: [
651
+ /* @__PURE__ */ jsxDEV4("div", {
652
+ className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
653
+ onClick: handleClose,
654
+ role: "button",
655
+ tabIndex: 0,
656
+ onKeyDown: (e) => {
657
+ if (e.key === "Enter" || e.key === " ")
658
+ handleClose();
659
+ },
660
+ "aria-label": "Close modal"
661
+ }, undefined, false, undefined, this),
662
+ /* @__PURE__ */ jsxDEV4("div", {
663
+ className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
664
+ children: [
665
+ /* @__PURE__ */ jsxDEV4("div", {
666
+ className: "border-border mb-4 border-b pb-4",
667
+ children: [
668
+ /* @__PURE__ */ jsxDEV4("h2", {
669
+ className: "text-xl font-semibold",
670
+ children: deal.name
671
+ }, undefined, false, undefined, this),
672
+ /* @__PURE__ */ jsxDEV4("p", {
673
+ className: "text-primary text-lg font-medium",
674
+ children: formatCurrency3(deal.value, deal.currency)
675
+ }, undefined, false, undefined, this),
676
+ /* @__PURE__ */ jsxDEV4("span", {
677
+ className: `mt-2 inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
678
+ children: deal.status
679
+ }, undefined, false, undefined, this)
680
+ ]
681
+ }, undefined, true, undefined, this),
682
+ mode === "menu" && /* @__PURE__ */ jsxDEV4("div", {
683
+ className: "space-y-3",
684
+ children: [
685
+ deal.status === "OPEN" && /* @__PURE__ */ jsxDEV4(Fragment, {
686
+ children: [
687
+ /* @__PURE__ */ jsxDEV4(Button2, {
688
+ className: "w-full justify-start",
689
+ variant: "ghost",
690
+ onPress: () => setMode("win"),
691
+ children: [
692
+ /* @__PURE__ */ jsxDEV4("span", {
693
+ className: "mr-2",
694
+ children: "\uD83C\uDFC6"
695
+ }, undefined, false, undefined, this),
696
+ " Mark as Won"
697
+ ]
698
+ }, undefined, true, undefined, this),
699
+ /* @__PURE__ */ jsxDEV4(Button2, {
700
+ className: "w-full justify-start",
701
+ variant: "ghost",
702
+ onPress: () => setMode("lose"),
703
+ children: [
704
+ /* @__PURE__ */ jsxDEV4("span", {
705
+ className: "mr-2",
706
+ children: "\u274C"
707
+ }, undefined, false, undefined, this),
708
+ " Mark as Lost"
709
+ ]
710
+ }, undefined, true, undefined, this),
711
+ /* @__PURE__ */ jsxDEV4(Button2, {
712
+ className: "w-full justify-start",
713
+ variant: "ghost",
714
+ onPress: () => {
715
+ setSelectedStageId(deal.stageId);
716
+ setMode("move");
717
+ },
718
+ children: [
719
+ /* @__PURE__ */ jsxDEV4("span", {
720
+ className: "mr-2",
721
+ children: "\u27A1\uFE0F"
722
+ }, undefined, false, undefined, this),
723
+ " Move to Stage"
724
+ ]
725
+ }, undefined, true, undefined, this)
726
+ ]
727
+ }, undefined, true, undefined, this),
728
+ deal.status !== "OPEN" && /* @__PURE__ */ jsxDEV4("p", {
729
+ className: "text-muted-foreground py-4 text-center",
730
+ children: [
731
+ "This deal is already ",
732
+ deal.status.toLowerCase(),
733
+ ". No actions available."
734
+ ]
735
+ }, undefined, true, undefined, this),
736
+ /* @__PURE__ */ jsxDEV4("div", {
737
+ className: "border-border border-t pt-3",
738
+ children: /* @__PURE__ */ jsxDEV4(Button2, {
739
+ className: "w-full",
740
+ variant: "outline",
741
+ onPress: handleClose,
742
+ children: "Close"
743
+ }, undefined, false, undefined, this)
744
+ }, undefined, false, undefined, this)
745
+ ]
746
+ }, undefined, true, undefined, this),
747
+ mode === "win" && /* @__PURE__ */ jsxDEV4("div", {
748
+ className: "space-y-4",
749
+ children: [
750
+ /* @__PURE__ */ jsxDEV4("div", {
751
+ children: [
752
+ /* @__PURE__ */ jsxDEV4("label", {
753
+ htmlFor: "won-source",
754
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
755
+ children: "How did you win this deal?"
756
+ }, undefined, false, undefined, this),
757
+ /* @__PURE__ */ jsxDEV4("select", {
758
+ id: "won-source",
759
+ value: wonSource,
760
+ onChange: (e) => setWonSource(e.target.value),
761
+ 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",
762
+ children: [
763
+ /* @__PURE__ */ jsxDEV4("option", {
764
+ value: "",
765
+ children: "Select a source..."
766
+ }, undefined, false, undefined, this),
767
+ /* @__PURE__ */ jsxDEV4("option", {
768
+ value: "referral",
769
+ children: "Referral"
770
+ }, undefined, false, undefined, this),
771
+ /* @__PURE__ */ jsxDEV4("option", {
772
+ value: "cold_outreach",
773
+ children: "Cold Outreach"
774
+ }, undefined, false, undefined, this),
775
+ /* @__PURE__ */ jsxDEV4("option", {
776
+ value: "inbound",
777
+ children: "Inbound Lead"
778
+ }, undefined, false, undefined, this),
779
+ /* @__PURE__ */ jsxDEV4("option", {
780
+ value: "upsell",
781
+ children: "Upsell"
782
+ }, undefined, false, undefined, this),
783
+ /* @__PURE__ */ jsxDEV4("option", {
784
+ value: "other",
785
+ children: "Other"
786
+ }, undefined, false, undefined, this)
787
+ ]
788
+ }, undefined, true, undefined, this)
789
+ ]
790
+ }, undefined, true, undefined, this),
791
+ /* @__PURE__ */ jsxDEV4("div", {
792
+ children: [
793
+ /* @__PURE__ */ jsxDEV4("label", {
794
+ htmlFor: "win-notes",
795
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
796
+ children: "Notes (optional)"
797
+ }, undefined, false, undefined, this),
798
+ /* @__PURE__ */ jsxDEV4("textarea", {
799
+ id: "win-notes",
800
+ value: notes,
801
+ onChange: (e) => setNotes(e.target.value),
802
+ placeholder: "Any additional notes about the win...",
803
+ rows: 3,
804
+ 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"
805
+ }, undefined, false, undefined, this)
806
+ ]
807
+ }, undefined, true, undefined, this),
808
+ error && /* @__PURE__ */ jsxDEV4("div", {
809
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
810
+ children: error
811
+ }, undefined, false, undefined, this),
812
+ /* @__PURE__ */ jsxDEV4("div", {
813
+ className: "flex justify-end gap-3 pt-2",
814
+ children: [
815
+ /* @__PURE__ */ jsxDEV4(Button2, {
816
+ variant: "ghost",
817
+ onPress: () => setMode("menu"),
818
+ disabled: isLoading,
819
+ children: "Back"
820
+ }, undefined, false, undefined, this),
821
+ /* @__PURE__ */ jsxDEV4(Button2, {
822
+ onPress: handleWin,
823
+ disabled: isLoading,
824
+ children: isLoading ? "Processing..." : "\uD83C\uDFC6 Confirm Win"
825
+ }, undefined, false, undefined, this)
826
+ ]
827
+ }, undefined, true, undefined, this)
828
+ ]
829
+ }, undefined, true, undefined, this),
830
+ mode === "lose" && /* @__PURE__ */ jsxDEV4("div", {
831
+ className: "space-y-4",
832
+ children: [
833
+ /* @__PURE__ */ jsxDEV4("div", {
834
+ children: [
835
+ /* @__PURE__ */ jsxDEV4("label", {
836
+ htmlFor: "lost-reason",
837
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
838
+ children: "Why was this deal lost? *"
839
+ }, undefined, false, undefined, this),
840
+ /* @__PURE__ */ jsxDEV4("select", {
841
+ id: "lost-reason",
842
+ value: lostReason,
843
+ onChange: (e) => setLostReason(e.target.value),
844
+ 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",
845
+ children: [
846
+ /* @__PURE__ */ jsxDEV4("option", {
847
+ value: "",
848
+ children: "Select a reason..."
849
+ }, undefined, false, undefined, this),
850
+ /* @__PURE__ */ jsxDEV4("option", {
851
+ value: "price",
852
+ children: "Price too high"
853
+ }, undefined, false, undefined, this),
854
+ /* @__PURE__ */ jsxDEV4("option", {
855
+ value: "competitor",
856
+ children: "Lost to competitor"
857
+ }, undefined, false, undefined, this),
858
+ /* @__PURE__ */ jsxDEV4("option", {
859
+ value: "no_budget",
860
+ children: "No budget"
861
+ }, undefined, false, undefined, this),
862
+ /* @__PURE__ */ jsxDEV4("option", {
863
+ value: "no_decision",
864
+ children: "No decision made"
865
+ }, undefined, false, undefined, this),
866
+ /* @__PURE__ */ jsxDEV4("option", {
867
+ value: "timing",
868
+ children: "Bad timing"
869
+ }, undefined, false, undefined, this),
870
+ /* @__PURE__ */ jsxDEV4("option", {
871
+ value: "product_fit",
872
+ children: "Product not a fit"
873
+ }, undefined, false, undefined, this),
874
+ /* @__PURE__ */ jsxDEV4("option", {
875
+ value: "other",
876
+ children: "Other"
877
+ }, undefined, false, undefined, this)
878
+ ]
879
+ }, undefined, true, undefined, this)
880
+ ]
881
+ }, undefined, true, undefined, this),
882
+ /* @__PURE__ */ jsxDEV4("div", {
883
+ children: [
884
+ /* @__PURE__ */ jsxDEV4("label", {
885
+ htmlFor: "lose-notes",
886
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
887
+ children: "Notes (optional)"
888
+ }, undefined, false, undefined, this),
889
+ /* @__PURE__ */ jsxDEV4("textarea", {
890
+ id: "lose-notes",
891
+ value: notes,
892
+ onChange: (e) => setNotes(e.target.value),
893
+ placeholder: "Any additional details...",
894
+ rows: 3,
895
+ 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"
896
+ }, undefined, false, undefined, this)
897
+ ]
898
+ }, undefined, true, undefined, this),
899
+ error && /* @__PURE__ */ jsxDEV4("div", {
900
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
901
+ children: error
902
+ }, undefined, false, undefined, this),
903
+ /* @__PURE__ */ jsxDEV4("div", {
904
+ className: "flex justify-end gap-3 pt-2",
905
+ children: [
906
+ /* @__PURE__ */ jsxDEV4(Button2, {
907
+ variant: "ghost",
908
+ onPress: () => setMode("menu"),
909
+ disabled: isLoading,
910
+ children: "Back"
911
+ }, undefined, false, undefined, this),
912
+ /* @__PURE__ */ jsxDEV4(Button2, {
913
+ variant: "destructive",
914
+ onPress: handleLose,
915
+ disabled: isLoading,
916
+ children: isLoading ? "Processing..." : "\u274C Confirm Loss"
917
+ }, undefined, false, undefined, this)
918
+ ]
919
+ }, undefined, true, undefined, this)
920
+ ]
921
+ }, undefined, true, undefined, this),
922
+ mode === "move" && /* @__PURE__ */ jsxDEV4("div", {
923
+ className: "space-y-4",
924
+ children: [
925
+ /* @__PURE__ */ jsxDEV4("div", {
926
+ children: [
927
+ /* @__PURE__ */ jsxDEV4("label", {
928
+ htmlFor: "move-stage",
929
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
930
+ children: "Move to Stage"
931
+ }, undefined, false, undefined, this),
932
+ /* @__PURE__ */ jsxDEV4("select", {
933
+ id: "move-stage",
934
+ value: selectedStageId,
935
+ onChange: (e) => setSelectedStageId(e.target.value),
936
+ 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",
937
+ children: stages.map((stage) => /* @__PURE__ */ jsxDEV4("option", {
938
+ value: stage.id,
939
+ children: [
940
+ stage.name,
941
+ stage.id === deal.stageId ? " (current)" : ""
942
+ ]
943
+ }, stage.id, true, undefined, this))
944
+ }, undefined, false, undefined, this)
945
+ ]
946
+ }, undefined, true, undefined, this),
947
+ error && /* @__PURE__ */ jsxDEV4("div", {
948
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
949
+ children: error
950
+ }, undefined, false, undefined, this),
951
+ /* @__PURE__ */ jsxDEV4("div", {
952
+ className: "flex justify-end gap-3 pt-2",
953
+ children: [
954
+ /* @__PURE__ */ jsxDEV4(Button2, {
955
+ variant: "ghost",
956
+ onPress: () => setMode("menu"),
957
+ disabled: isLoading,
958
+ children: "Back"
959
+ }, undefined, false, undefined, this),
960
+ /* @__PURE__ */ jsxDEV4(Button2, {
961
+ onPress: handleMove,
962
+ disabled: isLoading,
963
+ children: isLoading ? "Moving..." : "\u27A1\uFE0F Move Deal"
964
+ }, undefined, false, undefined, this)
965
+ ]
966
+ }, undefined, true, undefined, this)
967
+ ]
968
+ }, undefined, true, undefined, this)
969
+ ]
970
+ }, undefined, true, undefined, this)
971
+ ]
972
+ }, undefined, true, undefined, this);
973
+ }
974
+
975
+ // src/ui/CrmDashboard.tsx
976
+ import { useCallback as useCallback3, useState as useState6 } from "react";
977
+ import {
978
+ Button as Button3,
979
+ ErrorState,
980
+ LoaderBlock,
981
+ StatCard,
982
+ StatCardGroup
983
+ } from "@contractspec/lib.design-system";
984
+ import {
985
+ Tabs,
986
+ TabsContent,
987
+ TabsList,
988
+ TabsTrigger
989
+ } from "@contractspec/lib.ui-kit-web/ui/tabs";
990
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
991
+ "use client";
992
+ function formatCurrency4(value, currency = "USD") {
993
+ return new Intl.NumberFormat("en-US", {
994
+ style: "currency",
995
+ currency,
996
+ minimumFractionDigits: 0,
997
+ maximumFractionDigits: 0
998
+ }).format(value);
33
999
  }
34
1000
  function CrmDashboard() {
35
- const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
36
- const [selectedDeal, setSelectedDeal] = useState(null);
37
- const [isDealActionsOpen, setIsDealActionsOpen] = useState(false);
38
- const { data, dealsByStage, stages, loading, error, stats, refetch } = useDealList();
39
- const mutations = useDealMutations({ onSuccess: () => {
40
- refetch();
41
- } });
42
- const handleDealClick = useCallback((dealId) => {
43
- const deal = dealsByStage ? Object.values(dealsByStage).flat().find((d) => d.id === dealId) : null;
44
- if (deal) {
45
- setSelectedDeal(deal);
46
- setIsDealActionsOpen(true);
47
- }
48
- }, [dealsByStage]);
49
- const handleDealMove = useCallback(async (dealId, toStageId) => {
50
- await mutations.moveDeal({
51
- dealId,
52
- stageId: toStageId
53
- });
54
- }, [mutations]);
55
- if (loading && !data) return /* @__PURE__ */ jsx(LoaderBlock, { label: "Loading CRM..." });
56
- if (error) return /* @__PURE__ */ jsx(ErrorState, {
57
- title: "Failed to load CRM",
58
- description: error.message,
59
- onRetry: refetch,
60
- retryLabel: "Retry"
61
- });
62
- return /* @__PURE__ */ jsxs("div", {
63
- className: "space-y-6",
64
- children: [
65
- /* @__PURE__ */ jsxs("div", {
66
- className: "flex items-center justify-between",
67
- children: [/* @__PURE__ */ jsx("h2", {
68
- className: "text-2xl font-bold",
69
- children: "CRM Pipeline"
70
- }), /* @__PURE__ */ jsxs(Button, {
71
- onClick: () => setIsCreateModalOpen(true),
72
- children: [/* @__PURE__ */ jsx("span", {
73
- className: "mr-2",
74
- children: "+"
75
- }), " Create Deal"]
76
- })]
77
- }),
78
- stats && /* @__PURE__ */ jsxs(StatCardGroup, { children: [
79
- /* @__PURE__ */ jsx(StatCard, {
80
- label: "Total Pipeline",
81
- value: formatCurrency(stats.totalValue),
82
- hint: `${stats.total} deals`
83
- }),
84
- /* @__PURE__ */ jsx(StatCard, {
85
- label: "Open Deals",
86
- value: formatCurrency(stats.openValue),
87
- hint: `${stats.openCount} active`
88
- }),
89
- /* @__PURE__ */ jsx(StatCard, {
90
- label: "Won",
91
- value: formatCurrency(stats.wonValue),
92
- hint: `${stats.wonCount} closed`
93
- }),
94
- /* @__PURE__ */ jsx(StatCard, {
95
- label: "Lost",
96
- value: stats.lostCount,
97
- hint: "deals lost"
98
- })
99
- ] }),
100
- /* @__PURE__ */ jsxs(Tabs, {
101
- defaultValue: "pipeline",
102
- className: "w-full",
103
- children: [
104
- /* @__PURE__ */ jsxs(TabsList, { children: [
105
- /* @__PURE__ */ jsxs(TabsTrigger, {
106
- value: "pipeline",
107
- children: [/* @__PURE__ */ jsx("span", {
108
- className: "mr-2",
109
- children: "📊"
110
- }), "Pipeline"]
111
- }),
112
- /* @__PURE__ */ jsxs(TabsTrigger, {
113
- value: "list",
114
- children: [/* @__PURE__ */ jsx("span", {
115
- className: "mr-2",
116
- children: "📋"
117
- }), "All Deals"]
118
- }),
119
- /* @__PURE__ */ jsxs(TabsTrigger, {
120
- value: "metrics",
121
- children: [/* @__PURE__ */ jsx("span", {
122
- className: "mr-2",
123
- children: "📈"
124
- }), "Metrics"]
125
- })
126
- ] }),
127
- /* @__PURE__ */ jsx(TabsContent, {
128
- value: "pipeline",
129
- className: "min-h-[400px]",
130
- children: /* @__PURE__ */ jsx(CrmPipelineBoard, {
131
- dealsByStage,
132
- stages,
133
- onDealClick: handleDealClick,
134
- onDealMove: handleDealMove
135
- })
136
- }),
137
- /* @__PURE__ */ jsx(TabsContent, {
138
- value: "list",
139
- className: "min-h-[400px]",
140
- children: /* @__PURE__ */ jsx(DealListTab, {
141
- data,
142
- onDealClick: handleDealClick
143
- })
144
- }),
145
- /* @__PURE__ */ jsx(TabsContent, {
146
- value: "metrics",
147
- className: "min-h-[400px]",
148
- children: /* @__PURE__ */ jsx(MetricsTab, { stats })
149
- })
150
- ]
151
- }),
152
- /* @__PURE__ */ jsx(CreateDealModal, {
153
- isOpen: isCreateModalOpen,
154
- onClose: () => setIsCreateModalOpen(false),
155
- onSubmit: async (input) => {
156
- await mutations.createDeal(input);
157
- },
158
- stages,
159
- isLoading: mutations.createState.loading
160
- }),
161
- /* @__PURE__ */ jsx(DealActionsModal, {
162
- isOpen: isDealActionsOpen,
163
- deal: selectedDeal,
164
- stages,
165
- onClose: () => {
166
- setIsDealActionsOpen(false);
167
- setSelectedDeal(null);
168
- },
169
- onWin: async (input) => {
170
- await mutations.winDeal(input);
171
- },
172
- onLose: async (input) => {
173
- await mutations.loseDeal(input);
174
- },
175
- onMove: async (input) => {
176
- await mutations.moveDeal(input);
177
- refetch();
178
- },
179
- isLoading: mutations.isLoading
180
- })
181
- ]
182
- });
1001
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState6(false);
1002
+ const [selectedDeal, setSelectedDeal] = useState6(null);
1003
+ const [isDealActionsOpen, setIsDealActionsOpen] = useState6(false);
1004
+ const { data, dealsByStage, stages, loading, error, stats, refetch } = useDealList();
1005
+ const mutations = useDealMutations({
1006
+ onSuccess: () => {
1007
+ refetch();
1008
+ }
1009
+ });
1010
+ const handleDealClick = useCallback3((dealId) => {
1011
+ const deal = dealsByStage ? Object.values(dealsByStage).flat().find((d) => d.id === dealId) : null;
1012
+ if (deal) {
1013
+ setSelectedDeal(deal);
1014
+ setIsDealActionsOpen(true);
1015
+ }
1016
+ }, [dealsByStage]);
1017
+ const handleDealMove = useCallback3(async (dealId, toStageId) => {
1018
+ await mutations.moveDeal({ dealId, stageId: toStageId });
1019
+ }, [mutations]);
1020
+ if (loading && !data) {
1021
+ return /* @__PURE__ */ jsxDEV5(LoaderBlock, {
1022
+ label: "Loading CRM..."
1023
+ }, undefined, false, undefined, this);
1024
+ }
1025
+ if (error) {
1026
+ return /* @__PURE__ */ jsxDEV5(ErrorState, {
1027
+ title: "Failed to load CRM",
1028
+ description: error.message,
1029
+ onRetry: refetch,
1030
+ retryLabel: "Retry"
1031
+ }, undefined, false, undefined, this);
1032
+ }
1033
+ return /* @__PURE__ */ jsxDEV5("div", {
1034
+ className: "space-y-6",
1035
+ children: [
1036
+ /* @__PURE__ */ jsxDEV5("div", {
1037
+ className: "flex items-center justify-between",
1038
+ children: [
1039
+ /* @__PURE__ */ jsxDEV5("h2", {
1040
+ className: "text-2xl font-bold",
1041
+ children: "CRM Pipeline"
1042
+ }, undefined, false, undefined, this),
1043
+ /* @__PURE__ */ jsxDEV5(Button3, {
1044
+ onClick: () => setIsCreateModalOpen(true),
1045
+ children: [
1046
+ /* @__PURE__ */ jsxDEV5("span", {
1047
+ className: "mr-2",
1048
+ children: "+"
1049
+ }, undefined, false, undefined, this),
1050
+ " Create Deal"
1051
+ ]
1052
+ }, undefined, true, undefined, this)
1053
+ ]
1054
+ }, undefined, true, undefined, this),
1055
+ stats && /* @__PURE__ */ jsxDEV5(StatCardGroup, {
1056
+ children: [
1057
+ /* @__PURE__ */ jsxDEV5(StatCard, {
1058
+ label: "Total Pipeline",
1059
+ value: formatCurrency4(stats.totalValue),
1060
+ hint: `${stats.total} deals`
1061
+ }, undefined, false, undefined, this),
1062
+ /* @__PURE__ */ jsxDEV5(StatCard, {
1063
+ label: "Open Deals",
1064
+ value: formatCurrency4(stats.openValue),
1065
+ hint: `${stats.openCount} active`
1066
+ }, undefined, false, undefined, this),
1067
+ /* @__PURE__ */ jsxDEV5(StatCard, {
1068
+ label: "Won",
1069
+ value: formatCurrency4(stats.wonValue),
1070
+ hint: `${stats.wonCount} closed`
1071
+ }, undefined, false, undefined, this),
1072
+ /* @__PURE__ */ jsxDEV5(StatCard, {
1073
+ label: "Lost",
1074
+ value: stats.lostCount,
1075
+ hint: "deals lost"
1076
+ }, undefined, false, undefined, this)
1077
+ ]
1078
+ }, undefined, true, undefined, this),
1079
+ /* @__PURE__ */ jsxDEV5(Tabs, {
1080
+ defaultValue: "pipeline",
1081
+ className: "w-full",
1082
+ children: [
1083
+ /* @__PURE__ */ jsxDEV5(TabsList, {
1084
+ children: [
1085
+ /* @__PURE__ */ jsxDEV5(TabsTrigger, {
1086
+ value: "pipeline",
1087
+ children: [
1088
+ /* @__PURE__ */ jsxDEV5("span", {
1089
+ className: "mr-2",
1090
+ children: "\uD83D\uDCCA"
1091
+ }, undefined, false, undefined, this),
1092
+ "Pipeline"
1093
+ ]
1094
+ }, undefined, true, undefined, this),
1095
+ /* @__PURE__ */ jsxDEV5(TabsTrigger, {
1096
+ value: "list",
1097
+ children: [
1098
+ /* @__PURE__ */ jsxDEV5("span", {
1099
+ className: "mr-2",
1100
+ children: "\uD83D\uDCCB"
1101
+ }, undefined, false, undefined, this),
1102
+ "All Deals"
1103
+ ]
1104
+ }, undefined, true, undefined, this),
1105
+ /* @__PURE__ */ jsxDEV5(TabsTrigger, {
1106
+ value: "metrics",
1107
+ children: [
1108
+ /* @__PURE__ */ jsxDEV5("span", {
1109
+ className: "mr-2",
1110
+ children: "\uD83D\uDCC8"
1111
+ }, undefined, false, undefined, this),
1112
+ "Metrics"
1113
+ ]
1114
+ }, undefined, true, undefined, this)
1115
+ ]
1116
+ }, undefined, true, undefined, this),
1117
+ /* @__PURE__ */ jsxDEV5(TabsContent, {
1118
+ value: "pipeline",
1119
+ className: "min-h-[400px]",
1120
+ children: /* @__PURE__ */ jsxDEV5(CrmPipelineBoard, {
1121
+ dealsByStage,
1122
+ stages,
1123
+ onDealClick: handleDealClick,
1124
+ onDealMove: handleDealMove
1125
+ }, undefined, false, undefined, this)
1126
+ }, undefined, false, undefined, this),
1127
+ /* @__PURE__ */ jsxDEV5(TabsContent, {
1128
+ value: "list",
1129
+ className: "min-h-[400px]",
1130
+ children: /* @__PURE__ */ jsxDEV5(DealListTab, {
1131
+ data,
1132
+ onDealClick: handleDealClick
1133
+ }, undefined, false, undefined, this)
1134
+ }, undefined, false, undefined, this),
1135
+ /* @__PURE__ */ jsxDEV5(TabsContent, {
1136
+ value: "metrics",
1137
+ className: "min-h-[400px]",
1138
+ children: /* @__PURE__ */ jsxDEV5(MetricsTab, {
1139
+ stats
1140
+ }, undefined, false, undefined, this)
1141
+ }, undefined, false, undefined, this)
1142
+ ]
1143
+ }, undefined, true, undefined, this),
1144
+ /* @__PURE__ */ jsxDEV5(CreateDealModal, {
1145
+ isOpen: isCreateModalOpen,
1146
+ onClose: () => setIsCreateModalOpen(false),
1147
+ onSubmit: async (input) => {
1148
+ await mutations.createDeal(input);
1149
+ },
1150
+ stages,
1151
+ isLoading: mutations.createState.loading
1152
+ }, undefined, false, undefined, this),
1153
+ /* @__PURE__ */ jsxDEV5(DealActionsModal, {
1154
+ isOpen: isDealActionsOpen,
1155
+ deal: selectedDeal,
1156
+ stages,
1157
+ onClose: () => {
1158
+ setIsDealActionsOpen(false);
1159
+ setSelectedDeal(null);
1160
+ },
1161
+ onWin: async (input) => {
1162
+ await mutations.winDeal(input);
1163
+ },
1164
+ onLose: async (input) => {
1165
+ await mutations.loseDeal(input);
1166
+ },
1167
+ onMove: async (input) => {
1168
+ await mutations.moveDeal(input);
1169
+ refetch();
1170
+ },
1171
+ isLoading: mutations.isLoading
1172
+ }, undefined, false, undefined, this)
1173
+ ]
1174
+ }, undefined, true, undefined, this);
183
1175
  }
184
1176
  function DealListTab({ data, onDealClick }) {
185
- if (!data?.deals.length) return /* @__PURE__ */ jsx("div", {
186
- className: "text-muted-foreground flex h-64 items-center justify-center",
187
- children: "No deals found"
188
- });
189
- return /* @__PURE__ */ jsx("div", {
190
- className: "border-border rounded-lg border",
191
- children: /* @__PURE__ */ jsxs("table", {
192
- className: "w-full",
193
- children: [/* @__PURE__ */ jsx("thead", {
194
- className: "border-border bg-muted/30 border-b",
195
- children: /* @__PURE__ */ jsxs("tr", { children: [
196
- /* @__PURE__ */ jsx("th", {
197
- className: "px-4 py-3 text-left text-sm font-medium",
198
- children: "Deal"
199
- }),
200
- /* @__PURE__ */ jsx("th", {
201
- className: "px-4 py-3 text-left text-sm font-medium",
202
- children: "Value"
203
- }),
204
- /* @__PURE__ */ jsx("th", {
205
- className: "px-4 py-3 text-left text-sm font-medium",
206
- children: "Status"
207
- }),
208
- /* @__PURE__ */ jsx("th", {
209
- className: "px-4 py-3 text-left text-sm font-medium",
210
- children: "Expected Close"
211
- }),
212
- /* @__PURE__ */ jsx("th", {
213
- className: "px-4 py-3 text-left text-sm font-medium",
214
- children: "Actions"
215
- })
216
- ] })
217
- }), /* @__PURE__ */ jsx("tbody", {
218
- className: "divide-border divide-y",
219
- children: data.deals.map((deal) => /* @__PURE__ */ jsxs("tr", {
220
- className: "hover:bg-muted/50",
221
- children: [
222
- /* @__PURE__ */ jsx("td", {
223
- className: "px-4 py-3",
224
- children: /* @__PURE__ */ jsx("div", {
225
- className: "font-medium",
226
- children: deal.name
227
- })
228
- }),
229
- /* @__PURE__ */ jsx("td", {
230
- className: "px-4 py-3 font-mono",
231
- children: formatCurrency(deal.value, deal.currency)
232
- }),
233
- /* @__PURE__ */ jsx("td", {
234
- className: "px-4 py-3",
235
- children: /* @__PURE__ */ jsx("span", {
236
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
237
- children: deal.status
238
- })
239
- }),
240
- /* @__PURE__ */ jsx("td", {
241
- className: "text-muted-foreground px-4 py-3",
242
- children: deal.expectedCloseDate?.toLocaleDateString() ?? "-"
243
- }),
244
- /* @__PURE__ */ jsx("td", {
245
- className: "px-4 py-3",
246
- children: /* @__PURE__ */ jsx(Button, {
247
- variant: "ghost",
248
- size: "sm",
249
- onPress: () => onDealClick?.(deal.id),
250
- children: "Actions"
251
- })
252
- })
253
- ]
254
- }, deal.id))
255
- })]
256
- })
257
- });
1177
+ if (!data?.deals.length) {
1178
+ return /* @__PURE__ */ jsxDEV5("div", {
1179
+ className: "text-muted-foreground flex h-64 items-center justify-center",
1180
+ children: "No deals found"
1181
+ }, undefined, false, undefined, this);
1182
+ }
1183
+ return /* @__PURE__ */ jsxDEV5("div", {
1184
+ className: "border-border rounded-lg border",
1185
+ children: /* @__PURE__ */ jsxDEV5("table", {
1186
+ className: "w-full",
1187
+ children: [
1188
+ /* @__PURE__ */ jsxDEV5("thead", {
1189
+ className: "border-border bg-muted/30 border-b",
1190
+ children: /* @__PURE__ */ jsxDEV5("tr", {
1191
+ children: [
1192
+ /* @__PURE__ */ jsxDEV5("th", {
1193
+ className: "px-4 py-3 text-left text-sm font-medium",
1194
+ children: "Deal"
1195
+ }, undefined, false, undefined, this),
1196
+ /* @__PURE__ */ jsxDEV5("th", {
1197
+ className: "px-4 py-3 text-left text-sm font-medium",
1198
+ children: "Value"
1199
+ }, undefined, false, undefined, this),
1200
+ /* @__PURE__ */ jsxDEV5("th", {
1201
+ className: "px-4 py-3 text-left text-sm font-medium",
1202
+ children: "Status"
1203
+ }, undefined, false, undefined, this),
1204
+ /* @__PURE__ */ jsxDEV5("th", {
1205
+ className: "px-4 py-3 text-left text-sm font-medium",
1206
+ children: "Expected Close"
1207
+ }, undefined, false, undefined, this),
1208
+ /* @__PURE__ */ jsxDEV5("th", {
1209
+ className: "px-4 py-3 text-left text-sm font-medium",
1210
+ children: "Actions"
1211
+ }, undefined, false, undefined, this)
1212
+ ]
1213
+ }, undefined, true, undefined, this)
1214
+ }, undefined, false, undefined, this),
1215
+ /* @__PURE__ */ jsxDEV5("tbody", {
1216
+ className: "divide-border divide-y",
1217
+ children: data.deals.map((deal) => /* @__PURE__ */ jsxDEV5("tr", {
1218
+ className: "hover:bg-muted/50",
1219
+ children: [
1220
+ /* @__PURE__ */ jsxDEV5("td", {
1221
+ className: "px-4 py-3",
1222
+ children: /* @__PURE__ */ jsxDEV5("div", {
1223
+ className: "font-medium",
1224
+ children: deal.name
1225
+ }, undefined, false, undefined, this)
1226
+ }, undefined, false, undefined, this),
1227
+ /* @__PURE__ */ jsxDEV5("td", {
1228
+ className: "px-4 py-3 font-mono",
1229
+ children: formatCurrency4(deal.value, deal.currency)
1230
+ }, undefined, false, undefined, this),
1231
+ /* @__PURE__ */ jsxDEV5("td", {
1232
+ className: "px-4 py-3",
1233
+ children: /* @__PURE__ */ jsxDEV5("span", {
1234
+ className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
1235
+ children: deal.status
1236
+ }, undefined, false, undefined, this)
1237
+ }, undefined, false, undefined, this),
1238
+ /* @__PURE__ */ jsxDEV5("td", {
1239
+ className: "text-muted-foreground px-4 py-3",
1240
+ children: deal.expectedCloseDate?.toLocaleDateString() ?? "-"
1241
+ }, undefined, false, undefined, this),
1242
+ /* @__PURE__ */ jsxDEV5("td", {
1243
+ className: "px-4 py-3",
1244
+ children: /* @__PURE__ */ jsxDEV5(Button3, {
1245
+ variant: "ghost",
1246
+ size: "sm",
1247
+ onPress: () => onDealClick?.(deal.id),
1248
+ children: "Actions"
1249
+ }, undefined, false, undefined, this)
1250
+ }, undefined, false, undefined, this)
1251
+ ]
1252
+ }, deal.id, true, undefined, this))
1253
+ }, undefined, false, undefined, this)
1254
+ ]
1255
+ }, undefined, true, undefined, this)
1256
+ }, undefined, false, undefined, this);
258
1257
  }
259
- function MetricsTab({ stats }) {
260
- if (!stats) return null;
261
- return /* @__PURE__ */ jsx("div", {
262
- className: "space-y-6",
263
- children: /* @__PURE__ */ jsxs("div", {
264
- className: "border-border bg-card rounded-xl border p-6",
265
- children: [/* @__PURE__ */ jsx("h3", {
266
- className: "mb-4 text-lg font-semibold",
267
- children: "Pipeline Overview"
268
- }), /* @__PURE__ */ jsxs("dl", {
269
- className: "grid gap-4 sm:grid-cols-3",
270
- children: [
271
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
272
- className: "text-muted-foreground text-sm",
273
- children: "Win Rate"
274
- }), /* @__PURE__ */ jsxs("dd", {
275
- className: "text-2xl font-semibold",
276
- children: [stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0, "%"]
277
- })] }),
278
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
279
- className: "text-muted-foreground text-sm",
280
- children: "Avg Deal Size"
281
- }), /* @__PURE__ */ jsx("dd", {
282
- className: "text-2xl font-semibold",
283
- children: formatCurrency(stats.total > 0 ? stats.totalValue / stats.total : 0)
284
- })] }),
285
- /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("dt", {
286
- className: "text-muted-foreground text-sm",
287
- children: "Conversion"
288
- }), /* @__PURE__ */ jsxs("dd", {
289
- className: "text-2xl font-semibold",
290
- children: [
291
- stats.wonCount,
292
- " / ",
293
- stats.total
294
- ]
295
- })] })
296
- ]
297
- })]
298
- })
299
- });
1258
+ function MetricsTab({
1259
+ stats
1260
+ }) {
1261
+ if (!stats)
1262
+ return null;
1263
+ return /* @__PURE__ */ jsxDEV5("div", {
1264
+ className: "space-y-6",
1265
+ children: /* @__PURE__ */ jsxDEV5("div", {
1266
+ className: "border-border bg-card rounded-xl border p-6",
1267
+ children: [
1268
+ /* @__PURE__ */ jsxDEV5("h3", {
1269
+ className: "mb-4 text-lg font-semibold",
1270
+ children: "Pipeline Overview"
1271
+ }, undefined, false, undefined, this),
1272
+ /* @__PURE__ */ jsxDEV5("dl", {
1273
+ className: "grid gap-4 sm:grid-cols-3",
1274
+ children: [
1275
+ /* @__PURE__ */ jsxDEV5("div", {
1276
+ children: [
1277
+ /* @__PURE__ */ jsxDEV5("dt", {
1278
+ className: "text-muted-foreground text-sm",
1279
+ children: "Win Rate"
1280
+ }, undefined, false, undefined, this),
1281
+ /* @__PURE__ */ jsxDEV5("dd", {
1282
+ className: "text-2xl font-semibold",
1283
+ children: [
1284
+ stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0,
1285
+ "%"
1286
+ ]
1287
+ }, undefined, true, undefined, this)
1288
+ ]
1289
+ }, undefined, true, undefined, this),
1290
+ /* @__PURE__ */ jsxDEV5("div", {
1291
+ children: [
1292
+ /* @__PURE__ */ jsxDEV5("dt", {
1293
+ className: "text-muted-foreground text-sm",
1294
+ children: "Avg Deal Size"
1295
+ }, undefined, false, undefined, this),
1296
+ /* @__PURE__ */ jsxDEV5("dd", {
1297
+ className: "text-2xl font-semibold",
1298
+ children: formatCurrency4(stats.total > 0 ? stats.totalValue / stats.total : 0)
1299
+ }, undefined, false, undefined, this)
1300
+ ]
1301
+ }, undefined, true, undefined, this),
1302
+ /* @__PURE__ */ jsxDEV5("div", {
1303
+ children: [
1304
+ /* @__PURE__ */ jsxDEV5("dt", {
1305
+ className: "text-muted-foreground text-sm",
1306
+ children: "Conversion"
1307
+ }, undefined, false, undefined, this),
1308
+ /* @__PURE__ */ jsxDEV5("dd", {
1309
+ className: "text-2xl font-semibold",
1310
+ children: [
1311
+ stats.wonCount,
1312
+ " / ",
1313
+ stats.total
1314
+ ]
1315
+ }, undefined, true, undefined, this)
1316
+ ]
1317
+ }, undefined, true, undefined, this)
1318
+ ]
1319
+ }, undefined, true, undefined, this)
1320
+ ]
1321
+ }, undefined, true, undefined, this)
1322
+ }, undefined, false, undefined, this);
300
1323
  }
301
-
302
- //#endregion
303
- export { CrmDashboard };
304
- //# sourceMappingURL=CrmDashboard.js.map
1324
+ export {
1325
+ CrmDashboard
1326
+ };