@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
@@ -0,0 +1,50 @@
1
+ // src/ui/CrmDealCard.tsx
2
+ import { jsxDEV } from "react/jsx-dev-runtime";
3
+ "use client";
4
+ function formatCurrency(value, currency) {
5
+ return new Intl.NumberFormat("en-US", {
6
+ style: "currency",
7
+ currency,
8
+ minimumFractionDigits: 0,
9
+ maximumFractionDigits: 0
10
+ }).format(value);
11
+ }
12
+ function CrmDealCard({ deal, onClick }) {
13
+ const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
14
+ return /* @__PURE__ */ jsxDEV("div", {
15
+ onClick,
16
+ className: "border-border bg-card cursor-pointer rounded-lg border p-3 shadow-sm transition-shadow hover:shadow-md",
17
+ role: "button",
18
+ tabIndex: 0,
19
+ onKeyDown: (e) => {
20
+ if (e.key === "Enter" || e.key === " ")
21
+ onClick?.();
22
+ },
23
+ children: [
24
+ /* @__PURE__ */ jsxDEV("h4", {
25
+ className: "leading-snug font-medium",
26
+ children: deal.name
27
+ }, undefined, false, undefined, this),
28
+ /* @__PURE__ */ jsxDEV("div", {
29
+ className: "text-primary mt-2 text-lg font-semibold",
30
+ children: formatCurrency(deal.value, deal.currency)
31
+ }, undefined, false, undefined, this),
32
+ /* @__PURE__ */ jsxDEV("div", {
33
+ className: "text-muted-foreground mt-3 flex items-center justify-between text-xs",
34
+ children: [
35
+ daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
36
+ className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
37
+ children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
38
+ }, undefined, false, undefined, this),
39
+ /* @__PURE__ */ jsxDEV("span", {
40
+ 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"}`,
41
+ children: deal.status
42
+ }, undefined, false, undefined, this)
43
+ ]
44
+ }, undefined, true, undefined, this)
45
+ ]
46
+ }, undefined, true, undefined, this);
47
+ }
48
+ export {
49
+ CrmDealCard
50
+ };
@@ -0,0 +1,160 @@
1
+ // src/ui/CrmDealCard.tsx
2
+ import { jsxDEV } from "react/jsx-dev-runtime";
3
+ "use client";
4
+ function formatCurrency(value, currency) {
5
+ return new Intl.NumberFormat("en-US", {
6
+ style: "currency",
7
+ currency,
8
+ minimumFractionDigits: 0,
9
+ maximumFractionDigits: 0
10
+ }).format(value);
11
+ }
12
+ function CrmDealCard({ deal, onClick }) {
13
+ const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
14
+ return /* @__PURE__ */ jsxDEV("div", {
15
+ onClick,
16
+ className: "border-border bg-card cursor-pointer rounded-lg border p-3 shadow-sm transition-shadow hover:shadow-md",
17
+ role: "button",
18
+ tabIndex: 0,
19
+ onKeyDown: (e) => {
20
+ if (e.key === "Enter" || e.key === " ")
21
+ onClick?.();
22
+ },
23
+ children: [
24
+ /* @__PURE__ */ jsxDEV("h4", {
25
+ className: "leading-snug font-medium",
26
+ children: deal.name
27
+ }, undefined, false, undefined, this),
28
+ /* @__PURE__ */ jsxDEV("div", {
29
+ className: "text-primary mt-2 text-lg font-semibold",
30
+ children: formatCurrency(deal.value, deal.currency)
31
+ }, undefined, false, undefined, this),
32
+ /* @__PURE__ */ jsxDEV("div", {
33
+ className: "text-muted-foreground mt-3 flex items-center justify-between text-xs",
34
+ children: [
35
+ daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
36
+ className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
37
+ children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
38
+ }, undefined, false, undefined, this),
39
+ /* @__PURE__ */ jsxDEV("span", {
40
+ 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"}`,
41
+ children: deal.status
42
+ }, undefined, false, undefined, this)
43
+ ]
44
+ }, undefined, true, undefined, this)
45
+ ]
46
+ }, undefined, true, undefined, this);
47
+ }
48
+
49
+ // src/ui/CrmPipelineBoard.tsx
50
+ import { useState } from "react";
51
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
52
+ "use client";
53
+ function formatCurrency2(value) {
54
+ if (value >= 1e6)
55
+ return `$${(value / 1e6).toFixed(1)}M`;
56
+ if (value >= 1000)
57
+ return `$${(value / 1000).toFixed(0)}K`;
58
+ return `$${value}`;
59
+ }
60
+ function CrmPipelineBoard({
61
+ dealsByStage,
62
+ stages,
63
+ onDealClick,
64
+ onDealMove
65
+ }) {
66
+ const [quickMoveOpen, setQuickMoveOpen] = useState(null);
67
+ const sortedStages = [...stages].sort((a, b) => a.position - b.position);
68
+ const handleQuickMove = (dealId, toStageId) => {
69
+ onDealMove?.(dealId, toStageId);
70
+ setQuickMoveOpen(null);
71
+ };
72
+ return /* @__PURE__ */ jsxDEV2("div", {
73
+ className: "flex gap-4 overflow-x-auto pb-4",
74
+ children: sortedStages.map((stage) => {
75
+ const deals = dealsByStage[stage.id] ?? [];
76
+ const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
77
+ return /* @__PURE__ */ jsxDEV2("div", {
78
+ className: "bg-muted/30 flex w-72 flex-shrink-0 flex-col rounded-lg",
79
+ children: [
80
+ /* @__PURE__ */ jsxDEV2("div", {
81
+ className: "border-border flex items-center justify-between border-b px-3 py-2",
82
+ children: [
83
+ /* @__PURE__ */ jsxDEV2("div", {
84
+ children: [
85
+ /* @__PURE__ */ jsxDEV2("h3", {
86
+ className: "font-medium",
87
+ children: stage.name
88
+ }, undefined, false, undefined, this),
89
+ /* @__PURE__ */ jsxDEV2("p", {
90
+ className: "text-muted-foreground text-xs",
91
+ children: [
92
+ deals.length,
93
+ " deals · ",
94
+ formatCurrency2(stageValue)
95
+ ]
96
+ }, undefined, true, undefined, this)
97
+ ]
98
+ }, undefined, true, undefined, this),
99
+ /* @__PURE__ */ jsxDEV2("span", {
100
+ className: "bg-muted flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium",
101
+ children: deals.length
102
+ }, undefined, false, undefined, this)
103
+ ]
104
+ }, undefined, true, undefined, this),
105
+ /* @__PURE__ */ jsxDEV2("div", {
106
+ className: "flex flex-1 flex-col gap-2 p-2",
107
+ children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
108
+ className: "border-muted-foreground/20 text-muted-foreground flex h-24 items-center justify-center rounded-md border-2 border-dashed text-xs",
109
+ children: "No deals"
110
+ }, undefined, false, undefined, this) : deals.map((deal) => /* @__PURE__ */ jsxDEV2("div", {
111
+ className: "group relative",
112
+ children: [
113
+ /* @__PURE__ */ jsxDEV2(CrmDealCard, {
114
+ deal,
115
+ onClick: () => onDealClick?.(deal.id)
116
+ }, undefined, false, undefined, this),
117
+ deal.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
118
+ className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
119
+ children: [
120
+ /* @__PURE__ */ jsxDEV2("button", {
121
+ type: "button",
122
+ onClick: (e) => {
123
+ e.stopPropagation();
124
+ setQuickMoveOpen(quickMoveOpen === deal.id ? null : deal.id);
125
+ },
126
+ className: "bg-background border-border hover:bg-muted flex h-6 w-6 items-center justify-center rounded border text-xs shadow-sm",
127
+ title: "Quick move",
128
+ children: "➡️"
129
+ }, undefined, false, undefined, this),
130
+ quickMoveOpen === deal.id && /* @__PURE__ */ jsxDEV2("div", {
131
+ className: "bg-card border-border absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border py-1 shadow-lg",
132
+ children: [
133
+ /* @__PURE__ */ jsxDEV2("p", {
134
+ className: "text-muted-foreground px-3 py-1 text-xs font-medium",
135
+ children: "Move to:"
136
+ }, undefined, false, undefined, this),
137
+ sortedStages.filter((s) => s.id !== deal.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
138
+ type: "button",
139
+ onClick: (e) => {
140
+ e.stopPropagation();
141
+ handleQuickMove(deal.id, s.id);
142
+ },
143
+ className: "hover:bg-muted w-full px-3 py-1.5 text-left text-sm",
144
+ children: s.name
145
+ }, s.id, false, undefined, this))
146
+ ]
147
+ }, undefined, true, undefined, this)
148
+ ]
149
+ }, undefined, true, undefined, this)
150
+ ]
151
+ }, deal.id, true, undefined, this))
152
+ }, undefined, false, undefined, this)
153
+ ]
154
+ }, stage.id, true, undefined, this);
155
+ })
156
+ }, undefined, false, undefined, this);
157
+ }
158
+ export {
159
+ CrmPipelineBoard
160
+ };
@@ -0,0 +1,186 @@
1
+ // src/ui/hooks/useDealList.ts
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
4
+ "use client";
5
+ function useDealList(options = {}) {
6
+ const { handlers, projectId } = useTemplateRuntime();
7
+ const { crm } = handlers;
8
+ const [data, setData] = useState(null);
9
+ const [dealsByStage, setDealsByStage] = useState({});
10
+ const [stages, setStages] = useState([]);
11
+ const [loading, setLoading] = useState(true);
12
+ const [error, setError] = useState(null);
13
+ const [page, setPage] = useState(1);
14
+ const pipelineId = options.pipelineId ?? "pipeline-1";
15
+ const fetchData = useCallback(async () => {
16
+ setLoading(true);
17
+ setError(null);
18
+ try {
19
+ const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
20
+ crm.listDeals({
21
+ projectId,
22
+ pipelineId,
23
+ stageId: options.stageId,
24
+ status: options.status === "all" ? undefined : options.status,
25
+ search: options.search,
26
+ limit: options.limit ?? 50,
27
+ offset: (page - 1) * (options.limit ?? 50)
28
+ }),
29
+ crm.getDealsByStage({ projectId, pipelineId }),
30
+ crm.getPipelineStages({ pipelineId })
31
+ ]);
32
+ setData(dealsResult);
33
+ setDealsByStage(stageDealsResult);
34
+ setStages(stagesResult);
35
+ } catch (err) {
36
+ setError(err instanceof Error ? err : new Error("Unknown error"));
37
+ } finally {
38
+ setLoading(false);
39
+ }
40
+ }, [
41
+ crm,
42
+ projectId,
43
+ pipelineId,
44
+ options.stageId,
45
+ options.status,
46
+ options.search,
47
+ options.limit,
48
+ page
49
+ ]);
50
+ useEffect(() => {
51
+ fetchData();
52
+ }, [fetchData]);
53
+ const stats = useMemo(() => {
54
+ if (!data)
55
+ return null;
56
+ const open = data.deals.filter((d) => d.status === "OPEN");
57
+ const won = data.deals.filter((d) => d.status === "WON");
58
+ const lost = data.deals.filter((d) => d.status === "LOST");
59
+ return {
60
+ total: data.total,
61
+ totalValue: data.totalValue,
62
+ openCount: open.length,
63
+ openValue: open.reduce((sum, d) => sum + d.value, 0),
64
+ wonCount: won.length,
65
+ wonValue: won.reduce((sum, d) => sum + d.value, 0),
66
+ lostCount: lost.length
67
+ };
68
+ }, [data]);
69
+ return {
70
+ data,
71
+ dealsByStage,
72
+ stages,
73
+ loading,
74
+ error,
75
+ stats,
76
+ page,
77
+ refetch: fetchData,
78
+ nextPage: () => setPage((p) => p + 1),
79
+ prevPage: () => page > 1 && setPage((p) => p - 1)
80
+ };
81
+ }
82
+
83
+ // src/ui/hooks/useDealMutations.ts
84
+ import { useCallback as useCallback2, useState as useState2 } from "react";
85
+ import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
86
+ function useDealMutations(options = {}) {
87
+ const { handlers, projectId } = useTemplateRuntime2();
88
+ const { crm } = handlers;
89
+ const [createState, setCreateState] = useState2({
90
+ loading: false,
91
+ error: null,
92
+ data: null
93
+ });
94
+ const [moveState, setMoveState] = useState2({
95
+ loading: false,
96
+ error: null,
97
+ data: null
98
+ });
99
+ const [winState, setWinState] = useState2({
100
+ loading: false,
101
+ error: null,
102
+ data: null
103
+ });
104
+ const [loseState, setLoseState] = useState2({
105
+ loading: false,
106
+ error: null,
107
+ data: null
108
+ });
109
+ const createDeal = useCallback2(async (input) => {
110
+ setCreateState({ loading: true, error: null, data: null });
111
+ try {
112
+ const result = await crm.createDeal(input, {
113
+ projectId,
114
+ ownerId: "user-1"
115
+ });
116
+ setCreateState({ loading: false, error: null, data: result });
117
+ options.onSuccess?.();
118
+ return result;
119
+ } catch (err) {
120
+ const error = err instanceof Error ? err : new Error("Failed to create deal");
121
+ setCreateState({ loading: false, error, data: null });
122
+ options.onError?.(error);
123
+ return null;
124
+ }
125
+ }, [crm, projectId, options]);
126
+ const moveDeal = useCallback2(async (input) => {
127
+ setMoveState({ loading: true, error: null, data: null });
128
+ try {
129
+ const result = await crm.moveDeal(input);
130
+ setMoveState({ loading: false, error: null, data: result });
131
+ options.onSuccess?.();
132
+ return result;
133
+ } catch (err) {
134
+ const error = err instanceof Error ? err : new Error("Failed to move deal");
135
+ setMoveState({ loading: false, error, data: null });
136
+ options.onError?.(error);
137
+ return null;
138
+ }
139
+ }, [crm, options]);
140
+ const winDeal = useCallback2(async (input) => {
141
+ setWinState({ loading: true, error: null, data: null });
142
+ try {
143
+ const result = await crm.winDeal(input);
144
+ setWinState({ loading: false, error: null, data: result });
145
+ options.onSuccess?.();
146
+ return result;
147
+ } catch (err) {
148
+ const error = err instanceof Error ? err : new Error("Failed to mark deal as won");
149
+ setWinState({ loading: false, error, data: null });
150
+ options.onError?.(error);
151
+ return null;
152
+ }
153
+ }, [crm, options]);
154
+ const loseDeal = useCallback2(async (input) => {
155
+ setLoseState({ loading: true, error: null, data: null });
156
+ try {
157
+ const result = await crm.loseDeal(input);
158
+ setLoseState({ loading: false, error: null, data: result });
159
+ options.onSuccess?.();
160
+ return result;
161
+ } catch (err) {
162
+ const error = err instanceof Error ? err : new Error("Failed to mark deal as lost");
163
+ setLoseState({ loading: false, error, data: null });
164
+ options.onError?.(error);
165
+ return null;
166
+ }
167
+ }, [crm, options]);
168
+ return {
169
+ createDeal,
170
+ moveDeal,
171
+ winDeal,
172
+ loseDeal,
173
+ createState,
174
+ moveState,
175
+ winState,
176
+ loseState,
177
+ isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
178
+ };
179
+ }
180
+
181
+ // src/ui/hooks/index.ts
182
+ "use client";
183
+ export {
184
+ useDealMutations,
185
+ useDealList
186
+ };
@@ -0,0 +1,84 @@
1
+ // src/ui/hooks/useDealList.ts
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
3
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
4
+ "use client";
5
+ function useDealList(options = {}) {
6
+ const { handlers, projectId } = useTemplateRuntime();
7
+ const { crm } = handlers;
8
+ const [data, setData] = useState(null);
9
+ const [dealsByStage, setDealsByStage] = useState({});
10
+ const [stages, setStages] = useState([]);
11
+ const [loading, setLoading] = useState(true);
12
+ const [error, setError] = useState(null);
13
+ const [page, setPage] = useState(1);
14
+ const pipelineId = options.pipelineId ?? "pipeline-1";
15
+ const fetchData = useCallback(async () => {
16
+ setLoading(true);
17
+ setError(null);
18
+ try {
19
+ const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
20
+ crm.listDeals({
21
+ projectId,
22
+ pipelineId,
23
+ stageId: options.stageId,
24
+ status: options.status === "all" ? undefined : options.status,
25
+ search: options.search,
26
+ limit: options.limit ?? 50,
27
+ offset: (page - 1) * (options.limit ?? 50)
28
+ }),
29
+ crm.getDealsByStage({ projectId, pipelineId }),
30
+ crm.getPipelineStages({ pipelineId })
31
+ ]);
32
+ setData(dealsResult);
33
+ setDealsByStage(stageDealsResult);
34
+ setStages(stagesResult);
35
+ } catch (err) {
36
+ setError(err instanceof Error ? err : new Error("Unknown error"));
37
+ } finally {
38
+ setLoading(false);
39
+ }
40
+ }, [
41
+ crm,
42
+ projectId,
43
+ pipelineId,
44
+ options.stageId,
45
+ options.status,
46
+ options.search,
47
+ options.limit,
48
+ page
49
+ ]);
50
+ useEffect(() => {
51
+ fetchData();
52
+ }, [fetchData]);
53
+ const stats = useMemo(() => {
54
+ if (!data)
55
+ return null;
56
+ const open = data.deals.filter((d) => d.status === "OPEN");
57
+ const won = data.deals.filter((d) => d.status === "WON");
58
+ const lost = data.deals.filter((d) => d.status === "LOST");
59
+ return {
60
+ total: data.total,
61
+ totalValue: data.totalValue,
62
+ openCount: open.length,
63
+ openValue: open.reduce((sum, d) => sum + d.value, 0),
64
+ wonCount: won.length,
65
+ wonValue: won.reduce((sum, d) => sum + d.value, 0),
66
+ lostCount: lost.length
67
+ };
68
+ }, [data]);
69
+ return {
70
+ data,
71
+ dealsByStage,
72
+ stages,
73
+ loading,
74
+ error,
75
+ stats,
76
+ page,
77
+ refetch: fetchData,
78
+ nextPage: () => setPage((p) => p + 1),
79
+ prevPage: () => page > 1 && setPage((p) => p - 1)
80
+ };
81
+ }
82
+ export {
83
+ useDealList
84
+ };
@@ -0,0 +1,100 @@
1
+ // src/ui/hooks/useDealMutations.ts
2
+ import { useCallback, useState } from "react";
3
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
4
+ function useDealMutations(options = {}) {
5
+ const { handlers, projectId } = useTemplateRuntime();
6
+ const { crm } = handlers;
7
+ const [createState, setCreateState] = useState({
8
+ loading: false,
9
+ error: null,
10
+ data: null
11
+ });
12
+ const [moveState, setMoveState] = useState({
13
+ loading: false,
14
+ error: null,
15
+ data: null
16
+ });
17
+ const [winState, setWinState] = useState({
18
+ loading: false,
19
+ error: null,
20
+ data: null
21
+ });
22
+ const [loseState, setLoseState] = useState({
23
+ loading: false,
24
+ error: null,
25
+ data: null
26
+ });
27
+ const createDeal = useCallback(async (input) => {
28
+ setCreateState({ loading: true, error: null, data: null });
29
+ try {
30
+ const result = await crm.createDeal(input, {
31
+ projectId,
32
+ ownerId: "user-1"
33
+ });
34
+ setCreateState({ loading: false, error: null, data: result });
35
+ options.onSuccess?.();
36
+ return result;
37
+ } catch (err) {
38
+ const error = err instanceof Error ? err : new Error("Failed to create deal");
39
+ setCreateState({ loading: false, error, data: null });
40
+ options.onError?.(error);
41
+ return null;
42
+ }
43
+ }, [crm, projectId, options]);
44
+ const moveDeal = useCallback(async (input) => {
45
+ setMoveState({ loading: true, error: null, data: null });
46
+ try {
47
+ const result = await crm.moveDeal(input);
48
+ setMoveState({ loading: false, error: null, data: result });
49
+ options.onSuccess?.();
50
+ return result;
51
+ } catch (err) {
52
+ const error = err instanceof Error ? err : new Error("Failed to move deal");
53
+ setMoveState({ loading: false, error, data: null });
54
+ options.onError?.(error);
55
+ return null;
56
+ }
57
+ }, [crm, options]);
58
+ const winDeal = useCallback(async (input) => {
59
+ setWinState({ loading: true, error: null, data: null });
60
+ try {
61
+ const result = await crm.winDeal(input);
62
+ setWinState({ loading: false, error: null, data: result });
63
+ options.onSuccess?.();
64
+ return result;
65
+ } catch (err) {
66
+ const error = err instanceof Error ? err : new Error("Failed to mark deal as won");
67
+ setWinState({ loading: false, error, data: null });
68
+ options.onError?.(error);
69
+ return null;
70
+ }
71
+ }, [crm, options]);
72
+ const loseDeal = useCallback(async (input) => {
73
+ setLoseState({ loading: true, error: null, data: null });
74
+ try {
75
+ const result = await crm.loseDeal(input);
76
+ setLoseState({ loading: false, error: null, data: result });
77
+ options.onSuccess?.();
78
+ return result;
79
+ } catch (err) {
80
+ const error = err instanceof Error ? err : new Error("Failed to mark deal as lost");
81
+ setLoseState({ loading: false, error, data: null });
82
+ options.onError?.(error);
83
+ return null;
84
+ }
85
+ }, [crm, options]);
86
+ return {
87
+ createDeal,
88
+ moveDeal,
89
+ winDeal,
90
+ loseDeal,
91
+ createState,
92
+ moveState,
93
+ winState,
94
+ loseState,
95
+ isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
96
+ };
97
+ }
98
+ export {
99
+ useDealMutations
100
+ };