@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,211 @@
1
+ // src/ui/modals/CreateDealModal.tsx
2
+ import { useState } from "react";
3
+ import { Button, Input } from "@contractspec/lib.design-system";
4
+ import { jsxDEV } from "react/jsx-dev-runtime";
5
+ "use client";
6
+ var CURRENCIES = ["USD", "EUR", "GBP", "CAD"];
7
+ var DEFAULT_PIPELINE_ID = "pipeline-1";
8
+ function CreateDealModal({
9
+ isOpen,
10
+ onClose,
11
+ onSubmit,
12
+ stages,
13
+ isLoading = false
14
+ }) {
15
+ const [name, setName] = useState("");
16
+ const [value, setValue] = useState("");
17
+ const [currency, setCurrency] = useState("USD");
18
+ const [stageId, setStageId] = useState(stages[0]?.id ?? "");
19
+ const [expectedCloseDate, setExpectedCloseDate] = useState("");
20
+ const [error, setError] = useState(null);
21
+ const handleSubmit = async (e) => {
22
+ e.preventDefault();
23
+ setError(null);
24
+ if (!name.trim()) {
25
+ setError("Deal name is required");
26
+ return;
27
+ }
28
+ const numericValue = parseFloat(value);
29
+ if (isNaN(numericValue) || numericValue <= 0) {
30
+ setError("Value must be a positive number");
31
+ return;
32
+ }
33
+ if (!stageId) {
34
+ setError("Please select a pipeline stage");
35
+ return;
36
+ }
37
+ try {
38
+ await onSubmit({
39
+ name: name.trim(),
40
+ value: numericValue,
41
+ currency,
42
+ pipelineId: DEFAULT_PIPELINE_ID,
43
+ stageId,
44
+ expectedCloseDate: expectedCloseDate ? new Date(expectedCloseDate) : undefined
45
+ });
46
+ setName("");
47
+ setValue("");
48
+ setCurrency("USD");
49
+ setStageId(stages[0]?.id ?? "");
50
+ setExpectedCloseDate("");
51
+ onClose();
52
+ } catch (err) {
53
+ setError(err instanceof Error ? err.message : "Failed to create deal");
54
+ }
55
+ };
56
+ if (!isOpen)
57
+ return null;
58
+ return /* @__PURE__ */ jsxDEV("div", {
59
+ className: "fixed inset-0 z-50 flex items-center justify-center",
60
+ children: [
61
+ /* @__PURE__ */ jsxDEV("div", {
62
+ className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
63
+ onClick: onClose,
64
+ role: "button",
65
+ tabIndex: 0,
66
+ onKeyDown: (e) => {
67
+ if (e.key === "Enter" || e.key === " ")
68
+ onClose();
69
+ },
70
+ "aria-label": "Close modal"
71
+ }, undefined, false, undefined, this),
72
+ /* @__PURE__ */ jsxDEV("div", {
73
+ className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
74
+ children: [
75
+ /* @__PURE__ */ jsxDEV("h2", {
76
+ className: "mb-4 text-xl font-semibold",
77
+ children: "Create New Deal"
78
+ }, undefined, false, undefined, this),
79
+ /* @__PURE__ */ jsxDEV("form", {
80
+ onSubmit: handleSubmit,
81
+ className: "space-y-4",
82
+ children: [
83
+ /* @__PURE__ */ jsxDEV("div", {
84
+ children: [
85
+ /* @__PURE__ */ jsxDEV("label", {
86
+ htmlFor: "deal-name",
87
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
88
+ children: "Deal Name *"
89
+ }, undefined, false, undefined, this),
90
+ /* @__PURE__ */ jsxDEV(Input, {
91
+ id: "deal-name",
92
+ value: name,
93
+ onChange: (e) => setName(e.target.value),
94
+ placeholder: "e.g., Enterprise License - Acme Corp",
95
+ disabled: isLoading
96
+ }, undefined, false, undefined, this)
97
+ ]
98
+ }, undefined, true, undefined, this),
99
+ /* @__PURE__ */ jsxDEV("div", {
100
+ className: "flex gap-3",
101
+ children: [
102
+ /* @__PURE__ */ jsxDEV("div", {
103
+ className: "flex-1",
104
+ children: [
105
+ /* @__PURE__ */ jsxDEV("label", {
106
+ htmlFor: "deal-value",
107
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
108
+ children: "Value *"
109
+ }, undefined, false, undefined, this),
110
+ /* @__PURE__ */ jsxDEV(Input, {
111
+ id: "deal-value",
112
+ type: "number",
113
+ min: "0",
114
+ step: "0.01",
115
+ value,
116
+ onChange: (e) => setValue(e.target.value),
117
+ placeholder: "50000",
118
+ disabled: isLoading
119
+ }, undefined, false, undefined, this)
120
+ ]
121
+ }, undefined, true, undefined, this),
122
+ /* @__PURE__ */ jsxDEV("div", {
123
+ className: "w-24",
124
+ children: [
125
+ /* @__PURE__ */ jsxDEV("label", {
126
+ htmlFor: "deal-currency",
127
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
128
+ children: "Currency"
129
+ }, undefined, false, undefined, this),
130
+ /* @__PURE__ */ jsxDEV("select", {
131
+ id: "deal-currency",
132
+ value: currency,
133
+ onChange: (e) => setCurrency(e.target.value),
134
+ disabled: isLoading,
135
+ 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",
136
+ children: CURRENCIES.map((c) => /* @__PURE__ */ jsxDEV("option", {
137
+ value: c,
138
+ children: c
139
+ }, c, false, undefined, this))
140
+ }, undefined, false, undefined, this)
141
+ ]
142
+ }, undefined, true, undefined, this)
143
+ ]
144
+ }, undefined, true, undefined, this),
145
+ /* @__PURE__ */ jsxDEV("div", {
146
+ children: [
147
+ /* @__PURE__ */ jsxDEV("label", {
148
+ htmlFor: "deal-stage",
149
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
150
+ children: "Pipeline Stage *"
151
+ }, undefined, false, undefined, this),
152
+ /* @__PURE__ */ jsxDEV("select", {
153
+ id: "deal-stage",
154
+ value: stageId,
155
+ onChange: (e) => setStageId(e.target.value),
156
+ disabled: isLoading,
157
+ 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",
158
+ children: stages.map((stage) => /* @__PURE__ */ jsxDEV("option", {
159
+ value: stage.id,
160
+ children: stage.name
161
+ }, stage.id, false, undefined, this))
162
+ }, undefined, false, undefined, this)
163
+ ]
164
+ }, undefined, true, undefined, this),
165
+ /* @__PURE__ */ jsxDEV("div", {
166
+ children: [
167
+ /* @__PURE__ */ jsxDEV("label", {
168
+ htmlFor: "deal-close-date",
169
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
170
+ children: "Expected Close Date"
171
+ }, undefined, false, undefined, this),
172
+ /* @__PURE__ */ jsxDEV(Input, {
173
+ id: "deal-close-date",
174
+ type: "date",
175
+ value: expectedCloseDate,
176
+ onChange: (e) => setExpectedCloseDate(e.target.value),
177
+ disabled: isLoading
178
+ }, undefined, false, undefined, this)
179
+ ]
180
+ }, undefined, true, undefined, this),
181
+ error && /* @__PURE__ */ jsxDEV("div", {
182
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
183
+ children: error
184
+ }, undefined, false, undefined, this),
185
+ /* @__PURE__ */ jsxDEV("div", {
186
+ className: "flex justify-end gap-3 pt-2",
187
+ children: [
188
+ /* @__PURE__ */ jsxDEV(Button, {
189
+ type: "button",
190
+ variant: "ghost",
191
+ onPress: onClose,
192
+ disabled: isLoading,
193
+ children: "Cancel"
194
+ }, undefined, false, undefined, this),
195
+ /* @__PURE__ */ jsxDEV(Button, {
196
+ type: "submit",
197
+ disabled: isLoading,
198
+ children: isLoading ? "Creating..." : "Create Deal"
199
+ }, undefined, false, undefined, this)
200
+ ]
201
+ }, undefined, true, undefined, this)
202
+ ]
203
+ }, undefined, true, undefined, this)
204
+ ]
205
+ }, undefined, true, undefined, this)
206
+ ]
207
+ }, undefined, true, undefined, this);
208
+ }
209
+ export {
210
+ CreateDealModal
211
+ };
@@ -0,0 +1,428 @@
1
+ // src/ui/modals/DealActionsModal.tsx
2
+ import { useState } from "react";
3
+ import { Button } from "@contractspec/lib.design-system";
4
+ import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
5
+ "use client";
6
+ function formatCurrency(value, currency) {
7
+ return new Intl.NumberFormat("en-US", {
8
+ style: "currency",
9
+ currency,
10
+ minimumFractionDigits: 0,
11
+ maximumFractionDigits: 0
12
+ }).format(value);
13
+ }
14
+ function DealActionsModal({
15
+ isOpen,
16
+ deal,
17
+ stages,
18
+ onClose,
19
+ onWin,
20
+ onLose,
21
+ onMove,
22
+ isLoading = false
23
+ }) {
24
+ const [mode, setMode] = useState("menu");
25
+ const [wonSource, setWonSource] = useState("");
26
+ const [lostReason, setLostReason] = useState("");
27
+ const [notes, setNotes] = useState("");
28
+ const [selectedStageId, setSelectedStageId] = useState("");
29
+ const [error, setError] = useState(null);
30
+ const resetForm = () => {
31
+ setMode("menu");
32
+ setWonSource("");
33
+ setLostReason("");
34
+ setNotes("");
35
+ setSelectedStageId("");
36
+ setError(null);
37
+ };
38
+ const handleClose = () => {
39
+ resetForm();
40
+ onClose();
41
+ };
42
+ const handleWin = async () => {
43
+ if (!deal)
44
+ return;
45
+ setError(null);
46
+ try {
47
+ await onWin({
48
+ dealId: deal.id,
49
+ wonSource: wonSource.trim() || undefined,
50
+ notes: notes.trim() || undefined
51
+ });
52
+ handleClose();
53
+ } catch (err) {
54
+ setError(err instanceof Error ? err.message : "Failed to mark deal as won");
55
+ }
56
+ };
57
+ const handleLose = async () => {
58
+ if (!deal)
59
+ return;
60
+ setError(null);
61
+ if (!lostReason.trim()) {
62
+ setError("Please provide a reason for losing the deal");
63
+ return;
64
+ }
65
+ try {
66
+ await onLose({
67
+ dealId: deal.id,
68
+ lostReason: lostReason.trim(),
69
+ notes: notes.trim() || undefined
70
+ });
71
+ handleClose();
72
+ } catch (err) {
73
+ setError(err instanceof Error ? err.message : "Failed to mark deal as lost");
74
+ }
75
+ };
76
+ const handleMove = async () => {
77
+ if (!deal)
78
+ return;
79
+ setError(null);
80
+ if (!selectedStageId) {
81
+ setError("Please select a stage");
82
+ return;
83
+ }
84
+ if (selectedStageId === deal.stageId) {
85
+ setError("Deal is already in this stage");
86
+ return;
87
+ }
88
+ try {
89
+ await onMove({
90
+ dealId: deal.id,
91
+ stageId: selectedStageId
92
+ });
93
+ handleClose();
94
+ } catch (err) {
95
+ setError(err instanceof Error ? err.message : "Failed to move deal");
96
+ }
97
+ };
98
+ if (!isOpen || !deal)
99
+ return null;
100
+ return /* @__PURE__ */ jsxDEV("div", {
101
+ className: "fixed inset-0 z-50 flex items-center justify-center",
102
+ children: [
103
+ /* @__PURE__ */ jsxDEV("div", {
104
+ className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
105
+ onClick: handleClose,
106
+ role: "button",
107
+ tabIndex: 0,
108
+ onKeyDown: (e) => {
109
+ if (e.key === "Enter" || e.key === " ")
110
+ handleClose();
111
+ },
112
+ "aria-label": "Close modal"
113
+ }, undefined, false, undefined, this),
114
+ /* @__PURE__ */ jsxDEV("div", {
115
+ className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
116
+ children: [
117
+ /* @__PURE__ */ jsxDEV("div", {
118
+ className: "border-border mb-4 border-b pb-4",
119
+ children: [
120
+ /* @__PURE__ */ jsxDEV("h2", {
121
+ className: "text-xl font-semibold",
122
+ children: deal.name
123
+ }, undefined, false, undefined, this),
124
+ /* @__PURE__ */ jsxDEV("p", {
125
+ className: "text-primary text-lg font-medium",
126
+ children: formatCurrency(deal.value, deal.currency)
127
+ }, undefined, false, undefined, this),
128
+ /* @__PURE__ */ jsxDEV("span", {
129
+ 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"}`,
130
+ children: deal.status
131
+ }, undefined, false, undefined, this)
132
+ ]
133
+ }, undefined, true, undefined, this),
134
+ mode === "menu" && /* @__PURE__ */ jsxDEV("div", {
135
+ className: "space-y-3",
136
+ children: [
137
+ deal.status === "OPEN" && /* @__PURE__ */ jsxDEV(Fragment, {
138
+ children: [
139
+ /* @__PURE__ */ jsxDEV(Button, {
140
+ className: "w-full justify-start",
141
+ variant: "ghost",
142
+ onPress: () => setMode("win"),
143
+ children: [
144
+ /* @__PURE__ */ jsxDEV("span", {
145
+ className: "mr-2",
146
+ children: "\uD83C\uDFC6"
147
+ }, undefined, false, undefined, this),
148
+ " Mark as Won"
149
+ ]
150
+ }, undefined, true, undefined, this),
151
+ /* @__PURE__ */ jsxDEV(Button, {
152
+ className: "w-full justify-start",
153
+ variant: "ghost",
154
+ onPress: () => setMode("lose"),
155
+ children: [
156
+ /* @__PURE__ */ jsxDEV("span", {
157
+ className: "mr-2",
158
+ children: "❌"
159
+ }, undefined, false, undefined, this),
160
+ " Mark as Lost"
161
+ ]
162
+ }, undefined, true, undefined, this),
163
+ /* @__PURE__ */ jsxDEV(Button, {
164
+ className: "w-full justify-start",
165
+ variant: "ghost",
166
+ onPress: () => {
167
+ setSelectedStageId(deal.stageId);
168
+ setMode("move");
169
+ },
170
+ children: [
171
+ /* @__PURE__ */ jsxDEV("span", {
172
+ className: "mr-2",
173
+ children: "➡️"
174
+ }, undefined, false, undefined, this),
175
+ " Move to Stage"
176
+ ]
177
+ }, undefined, true, undefined, this)
178
+ ]
179
+ }, undefined, true, undefined, this),
180
+ deal.status !== "OPEN" && /* @__PURE__ */ jsxDEV("p", {
181
+ className: "text-muted-foreground py-4 text-center",
182
+ children: [
183
+ "This deal is already ",
184
+ deal.status.toLowerCase(),
185
+ ". No actions available."
186
+ ]
187
+ }, undefined, true, undefined, this),
188
+ /* @__PURE__ */ jsxDEV("div", {
189
+ className: "border-border border-t pt-3",
190
+ children: /* @__PURE__ */ jsxDEV(Button, {
191
+ className: "w-full",
192
+ variant: "outline",
193
+ onPress: handleClose,
194
+ children: "Close"
195
+ }, undefined, false, undefined, this)
196
+ }, undefined, false, undefined, this)
197
+ ]
198
+ }, undefined, true, undefined, this),
199
+ mode === "win" && /* @__PURE__ */ jsxDEV("div", {
200
+ className: "space-y-4",
201
+ children: [
202
+ /* @__PURE__ */ jsxDEV("div", {
203
+ children: [
204
+ /* @__PURE__ */ jsxDEV("label", {
205
+ htmlFor: "won-source",
206
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
207
+ children: "How did you win this deal?"
208
+ }, undefined, false, undefined, this),
209
+ /* @__PURE__ */ jsxDEV("select", {
210
+ id: "won-source",
211
+ value: wonSource,
212
+ onChange: (e) => setWonSource(e.target.value),
213
+ 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",
214
+ children: [
215
+ /* @__PURE__ */ jsxDEV("option", {
216
+ value: "",
217
+ children: "Select a source..."
218
+ }, undefined, false, undefined, this),
219
+ /* @__PURE__ */ jsxDEV("option", {
220
+ value: "referral",
221
+ children: "Referral"
222
+ }, undefined, false, undefined, this),
223
+ /* @__PURE__ */ jsxDEV("option", {
224
+ value: "cold_outreach",
225
+ children: "Cold Outreach"
226
+ }, undefined, false, undefined, this),
227
+ /* @__PURE__ */ jsxDEV("option", {
228
+ value: "inbound",
229
+ children: "Inbound Lead"
230
+ }, undefined, false, undefined, this),
231
+ /* @__PURE__ */ jsxDEV("option", {
232
+ value: "upsell",
233
+ children: "Upsell"
234
+ }, undefined, false, undefined, this),
235
+ /* @__PURE__ */ jsxDEV("option", {
236
+ value: "other",
237
+ children: "Other"
238
+ }, undefined, false, undefined, this)
239
+ ]
240
+ }, undefined, true, undefined, this)
241
+ ]
242
+ }, undefined, true, undefined, this),
243
+ /* @__PURE__ */ jsxDEV("div", {
244
+ children: [
245
+ /* @__PURE__ */ jsxDEV("label", {
246
+ htmlFor: "win-notes",
247
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
248
+ children: "Notes (optional)"
249
+ }, undefined, false, undefined, this),
250
+ /* @__PURE__ */ jsxDEV("textarea", {
251
+ id: "win-notes",
252
+ value: notes,
253
+ onChange: (e) => setNotes(e.target.value),
254
+ placeholder: "Any additional notes about the win...",
255
+ rows: 3,
256
+ 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"
257
+ }, undefined, false, undefined, this)
258
+ ]
259
+ }, undefined, true, undefined, this),
260
+ error && /* @__PURE__ */ jsxDEV("div", {
261
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
262
+ children: error
263
+ }, undefined, false, undefined, this),
264
+ /* @__PURE__ */ jsxDEV("div", {
265
+ className: "flex justify-end gap-3 pt-2",
266
+ children: [
267
+ /* @__PURE__ */ jsxDEV(Button, {
268
+ variant: "ghost",
269
+ onPress: () => setMode("menu"),
270
+ disabled: isLoading,
271
+ children: "Back"
272
+ }, undefined, false, undefined, this),
273
+ /* @__PURE__ */ jsxDEV(Button, {
274
+ onPress: handleWin,
275
+ disabled: isLoading,
276
+ children: isLoading ? "Processing..." : "\uD83C\uDFC6 Confirm Win"
277
+ }, undefined, false, undefined, this)
278
+ ]
279
+ }, undefined, true, undefined, this)
280
+ ]
281
+ }, undefined, true, undefined, this),
282
+ mode === "lose" && /* @__PURE__ */ jsxDEV("div", {
283
+ className: "space-y-4",
284
+ children: [
285
+ /* @__PURE__ */ jsxDEV("div", {
286
+ children: [
287
+ /* @__PURE__ */ jsxDEV("label", {
288
+ htmlFor: "lost-reason",
289
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
290
+ children: "Why was this deal lost? *"
291
+ }, undefined, false, undefined, this),
292
+ /* @__PURE__ */ jsxDEV("select", {
293
+ id: "lost-reason",
294
+ value: lostReason,
295
+ onChange: (e) => setLostReason(e.target.value),
296
+ 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",
297
+ children: [
298
+ /* @__PURE__ */ jsxDEV("option", {
299
+ value: "",
300
+ children: "Select a reason..."
301
+ }, undefined, false, undefined, this),
302
+ /* @__PURE__ */ jsxDEV("option", {
303
+ value: "price",
304
+ children: "Price too high"
305
+ }, undefined, false, undefined, this),
306
+ /* @__PURE__ */ jsxDEV("option", {
307
+ value: "competitor",
308
+ children: "Lost to competitor"
309
+ }, undefined, false, undefined, this),
310
+ /* @__PURE__ */ jsxDEV("option", {
311
+ value: "no_budget",
312
+ children: "No budget"
313
+ }, undefined, false, undefined, this),
314
+ /* @__PURE__ */ jsxDEV("option", {
315
+ value: "no_decision",
316
+ children: "No decision made"
317
+ }, undefined, false, undefined, this),
318
+ /* @__PURE__ */ jsxDEV("option", {
319
+ value: "timing",
320
+ children: "Bad timing"
321
+ }, undefined, false, undefined, this),
322
+ /* @__PURE__ */ jsxDEV("option", {
323
+ value: "product_fit",
324
+ children: "Product not a fit"
325
+ }, undefined, false, undefined, this),
326
+ /* @__PURE__ */ jsxDEV("option", {
327
+ value: "other",
328
+ children: "Other"
329
+ }, undefined, false, undefined, this)
330
+ ]
331
+ }, undefined, true, undefined, this)
332
+ ]
333
+ }, undefined, true, undefined, this),
334
+ /* @__PURE__ */ jsxDEV("div", {
335
+ children: [
336
+ /* @__PURE__ */ jsxDEV("label", {
337
+ htmlFor: "lose-notes",
338
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
339
+ children: "Notes (optional)"
340
+ }, undefined, false, undefined, this),
341
+ /* @__PURE__ */ jsxDEV("textarea", {
342
+ id: "lose-notes",
343
+ value: notes,
344
+ onChange: (e) => setNotes(e.target.value),
345
+ placeholder: "Any additional details...",
346
+ rows: 3,
347
+ 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"
348
+ }, undefined, false, undefined, this)
349
+ ]
350
+ }, undefined, true, undefined, this),
351
+ error && /* @__PURE__ */ jsxDEV("div", {
352
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
353
+ children: error
354
+ }, undefined, false, undefined, this),
355
+ /* @__PURE__ */ jsxDEV("div", {
356
+ className: "flex justify-end gap-3 pt-2",
357
+ children: [
358
+ /* @__PURE__ */ jsxDEV(Button, {
359
+ variant: "ghost",
360
+ onPress: () => setMode("menu"),
361
+ disabled: isLoading,
362
+ children: "Back"
363
+ }, undefined, false, undefined, this),
364
+ /* @__PURE__ */ jsxDEV(Button, {
365
+ variant: "destructive",
366
+ onPress: handleLose,
367
+ disabled: isLoading,
368
+ children: isLoading ? "Processing..." : "❌ Confirm Loss"
369
+ }, undefined, false, undefined, this)
370
+ ]
371
+ }, undefined, true, undefined, this)
372
+ ]
373
+ }, undefined, true, undefined, this),
374
+ mode === "move" && /* @__PURE__ */ jsxDEV("div", {
375
+ className: "space-y-4",
376
+ children: [
377
+ /* @__PURE__ */ jsxDEV("div", {
378
+ children: [
379
+ /* @__PURE__ */ jsxDEV("label", {
380
+ htmlFor: "move-stage",
381
+ className: "text-muted-foreground mb-1 block text-sm font-medium",
382
+ children: "Move to Stage"
383
+ }, undefined, false, undefined, this),
384
+ /* @__PURE__ */ jsxDEV("select", {
385
+ id: "move-stage",
386
+ value: selectedStageId,
387
+ onChange: (e) => setSelectedStageId(e.target.value),
388
+ 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",
389
+ children: stages.map((stage) => /* @__PURE__ */ jsxDEV("option", {
390
+ value: stage.id,
391
+ children: [
392
+ stage.name,
393
+ stage.id === deal.stageId ? " (current)" : ""
394
+ ]
395
+ }, stage.id, true, undefined, this))
396
+ }, undefined, false, undefined, this)
397
+ ]
398
+ }, undefined, true, undefined, this),
399
+ error && /* @__PURE__ */ jsxDEV("div", {
400
+ className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
401
+ children: error
402
+ }, undefined, false, undefined, this),
403
+ /* @__PURE__ */ jsxDEV("div", {
404
+ className: "flex justify-end gap-3 pt-2",
405
+ children: [
406
+ /* @__PURE__ */ jsxDEV(Button, {
407
+ variant: "ghost",
408
+ onPress: () => setMode("menu"),
409
+ disabled: isLoading,
410
+ children: "Back"
411
+ }, undefined, false, undefined, this),
412
+ /* @__PURE__ */ jsxDEV(Button, {
413
+ onPress: handleMove,
414
+ disabled: isLoading,
415
+ children: isLoading ? "Moving..." : "➡️ Move Deal"
416
+ }, undefined, false, undefined, this)
417
+ ]
418
+ }, undefined, true, undefined, this)
419
+ ]
420
+ }, undefined, true, undefined, this)
421
+ ]
422
+ }, undefined, true, undefined, this)
423
+ ]
424
+ }, undefined, true, undefined, this);
425
+ }
426
+ export {
427
+ DealActionsModal
428
+ };