@ebowwa/crm 0.1.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 (187) hide show
  1. package/README.md +174 -0
  2. package/dist/cli/commands/activities.d.ts +11 -0
  3. package/dist/cli/commands/activities.d.ts.map +1 -0
  4. package/dist/cli/commands/activities.js +427 -0
  5. package/dist/cli/commands/activities.js.map +1 -0
  6. package/dist/cli/commands/contacts.d.ts +11 -0
  7. package/dist/cli/commands/contacts.d.ts.map +1 -0
  8. package/dist/cli/commands/contacts.js +458 -0
  9. package/dist/cli/commands/contacts.js.map +1 -0
  10. package/dist/cli/commands/deals.d.ts +11 -0
  11. package/dist/cli/commands/deals.d.ts.map +1 -0
  12. package/dist/cli/commands/deals.js +498 -0
  13. package/dist/cli/commands/deals.js.map +1 -0
  14. package/dist/cli/commands/media.d.ts +11 -0
  15. package/dist/cli/commands/media.d.ts.map +1 -0
  16. package/dist/cli/commands/media.js +417 -0
  17. package/dist/cli/commands/media.js.map +1 -0
  18. package/dist/cli/commands/search.d.ts +11 -0
  19. package/dist/cli/commands/search.d.ts.map +1 -0
  20. package/dist/cli/commands/search.js +346 -0
  21. package/dist/cli/commands/search.js.map +1 -0
  22. package/dist/cli/index.d.ts +13 -0
  23. package/dist/cli/index.d.ts.map +1 -0
  24. package/dist/cli/index.js +173 -0
  25. package/dist/cli/index.js.map +1 -0
  26. package/dist/cli/repl.d.ts +15 -0
  27. package/dist/cli/repl.d.ts.map +1 -0
  28. package/dist/cli/repl.js +318 -0
  29. package/dist/cli/repl.js.map +1 -0
  30. package/dist/cli/utils/config.d.ts +91 -0
  31. package/dist/cli/utils/config.d.ts.map +1 -0
  32. package/dist/cli/utils/config.js +212 -0
  33. package/dist/cli/utils/config.js.map +1 -0
  34. package/dist/cli/utils/output.d.ts +136 -0
  35. package/dist/cli/utils/output.d.ts.map +1 -0
  36. package/dist/cli/utils/output.js +323 -0
  37. package/dist/cli/utils/output.js.map +1 -0
  38. package/dist/cli/utils/prompt.d.ts +81 -0
  39. package/dist/cli/utils/prompt.d.ts.map +1 -0
  40. package/dist/cli/utils/prompt.js +341 -0
  41. package/dist/cli/utils/prompt.js.map +1 -0
  42. package/dist/cli.d.ts +3 -0
  43. package/dist/cli.d.ts.map +1 -0
  44. package/dist/cli.js +8 -0
  45. package/dist/cli.js.map +1 -0
  46. package/dist/core/index.d.ts +6 -0
  47. package/dist/core/index.d.ts.map +1 -0
  48. package/dist/core/index.js +32 -0
  49. package/dist/core/index.js.map +1 -0
  50. package/dist/core/schemas.d.ts +3050 -0
  51. package/dist/core/schemas.d.ts.map +1 -0
  52. package/dist/core/schemas.js +667 -0
  53. package/dist/core/schemas.js.map +1 -0
  54. package/dist/core/types.d.ts +597 -0
  55. package/dist/core/types.d.ts.map +1 -0
  56. package/dist/core/types.js +8 -0
  57. package/dist/core/types.js.map +1 -0
  58. package/dist/index.d.ts +7 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +8 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/mcp/index.d.ts +14 -0
  63. package/dist/mcp/index.d.ts.map +1 -0
  64. package/dist/mcp/index.js +11 -0
  65. package/dist/mcp/index.js.map +1 -0
  66. package/dist/mcp/server.d.ts +13 -0
  67. package/dist/mcp/server.d.ts.map +1 -0
  68. package/dist/mcp/server.js +18 -0
  69. package/dist/mcp/server.js.map +1 -0
  70. package/dist/mcp/storage/client.d.ts +109 -0
  71. package/dist/mcp/storage/client.d.ts.map +1 -0
  72. package/dist/mcp/storage/client.js +355 -0
  73. package/dist/mcp/storage/client.js.map +1 -0
  74. package/dist/mcp/storage/index.d.ts +7 -0
  75. package/dist/mcp/storage/index.d.ts.map +1 -0
  76. package/dist/mcp/storage/index.js +6 -0
  77. package/dist/mcp/storage/index.js.map +1 -0
  78. package/dist/mcp/storage/types.d.ts +44 -0
  79. package/dist/mcp/storage/types.d.ts.map +1 -0
  80. package/dist/mcp/storage/types.js +35 -0
  81. package/dist/mcp/storage/types.js.map +1 -0
  82. package/dist/mcp/tools/definitions.d.ts +16 -0
  83. package/dist/mcp/tools/definitions.d.ts.map +1 -0
  84. package/dist/mcp/tools/definitions.js +914 -0
  85. package/dist/mcp/tools/definitions.js.map +1 -0
  86. package/dist/mcp/tools/handlers.d.ts +50 -0
  87. package/dist/mcp/tools/handlers.d.ts.map +1 -0
  88. package/dist/mcp/tools/handlers.js +760 -0
  89. package/dist/mcp/tools/handlers.js.map +1 -0
  90. package/dist/mcp/tools/index.d.ts +7 -0
  91. package/dist/mcp/tools/index.d.ts.map +1 -0
  92. package/dist/mcp/tools/index.js +6 -0
  93. package/dist/mcp/tools/index.js.map +1 -0
  94. package/dist/mcp/tools/types.d.ts +314 -0
  95. package/dist/mcp/tools/types.d.ts.map +1 -0
  96. package/dist/mcp/tools/types.js +5 -0
  97. package/dist/mcp/tools/types.js.map +1 -0
  98. package/dist/mcp/transports/stdio.d.ts +27 -0
  99. package/dist/mcp/transports/stdio.d.ts.map +1 -0
  100. package/dist/mcp/transports/stdio.js +237 -0
  101. package/dist/mcp/transports/stdio.js.map +1 -0
  102. package/dist/telemetry/index.d.ts +58 -0
  103. package/dist/telemetry/index.d.ts.map +1 -0
  104. package/dist/telemetry/index.js +109 -0
  105. package/dist/telemetry/index.js.map +1 -0
  106. package/dist/telemetry/logger.d.ts +116 -0
  107. package/dist/telemetry/logger.d.ts.map +1 -0
  108. package/dist/telemetry/logger.js +256 -0
  109. package/dist/telemetry/logger.js.map +1 -0
  110. package/dist/telemetry/metrics.d.ts +115 -0
  111. package/dist/telemetry/metrics.d.ts.map +1 -0
  112. package/dist/telemetry/metrics.js +292 -0
  113. package/dist/telemetry/metrics.js.map +1 -0
  114. package/dist/telemetry/tracer.d.ts +227 -0
  115. package/dist/telemetry/tracer.d.ts.map +1 -0
  116. package/dist/telemetry/tracer.js +355 -0
  117. package/dist/telemetry/tracer.js.map +1 -0
  118. package/dist/web/app.d.ts +2 -0
  119. package/dist/web/app.d.ts.map +1 -0
  120. package/dist/web/app.js +115 -0
  121. package/dist/web/app.js.map +1 -0
  122. package/dist/web/components/ContactList.d.ts +3 -0
  123. package/dist/web/components/ContactList.d.ts.map +1 -0
  124. package/dist/web/components/ContactList.js +262 -0
  125. package/dist/web/components/ContactList.js.map +1 -0
  126. package/dist/web/components/Dashboard.d.ts +3 -0
  127. package/dist/web/components/Dashboard.d.ts.map +1 -0
  128. package/dist/web/components/Dashboard.js +158 -0
  129. package/dist/web/components/Dashboard.js.map +1 -0
  130. package/dist/web/components/DealPipeline.d.ts +3 -0
  131. package/dist/web/components/DealPipeline.d.ts.map +1 -0
  132. package/dist/web/components/DealPipeline.js +306 -0
  133. package/dist/web/components/DealPipeline.js.map +1 -0
  134. package/dist/web/index.d.ts +2 -0
  135. package/dist/web/index.d.ts.map +1 -0
  136. package/dist/web/index.js +269 -0
  137. package/dist/web/index.js.map +1 -0
  138. package/dist/web/types.d.ts +75 -0
  139. package/dist/web/types.d.ts.map +1 -0
  140. package/dist/web/types.js +3 -0
  141. package/dist/web/types.js.map +1 -0
  142. package/native/index.d.ts +571 -0
  143. package/native/index.js +687 -0
  144. package/package.json +105 -0
  145. package/src/cli/commands/activities.ts +543 -0
  146. package/src/cli/commands/contacts.ts +563 -0
  147. package/src/cli/commands/deals.ts +637 -0
  148. package/src/cli/commands/media.ts +521 -0
  149. package/src/cli/commands/search.ts +426 -0
  150. package/src/cli/index.ts +203 -0
  151. package/src/cli/repl.ts +379 -0
  152. package/src/cli/utils/config.ts +299 -0
  153. package/src/cli/utils/output.ts +386 -0
  154. package/src/cli/utils/prompt.ts +444 -0
  155. package/src/cli.ts +11 -0
  156. package/src/core/index.ts +184 -0
  157. package/src/core/schemas.ts +770 -0
  158. package/src/core/types.ts +969 -0
  159. package/src/index.ts +8 -0
  160. package/src/mcp/index.ts +17 -0
  161. package/src/mcp/server.ts +26 -0
  162. package/src/mcp/storage/client.ts +408 -0
  163. package/src/mcp/storage/index.ts +7 -0
  164. package/src/mcp/storage/types.ts +72 -0
  165. package/src/mcp/tools/definitions.ts +961 -0
  166. package/src/mcp/tools/handlers.ts +805 -0
  167. package/src/mcp/tools/index.ts +7 -0
  168. package/src/mcp/tools/types.ts +390 -0
  169. package/src/mcp/transports/stdio.ts +225 -0
  170. package/src/telemetry/index.ts +131 -0
  171. package/src/telemetry/logger.ts +318 -0
  172. package/src/telemetry/metrics.ts +393 -0
  173. package/src/telemetry/tracer.ts +487 -0
  174. package/src/web/api/activities.ts +41 -0
  175. package/src/web/api/contacts.ts +114 -0
  176. package/src/web/api/deals.ts +108 -0
  177. package/src/web/api/media.ts +98 -0
  178. package/src/web/app.tsx +143 -0
  179. package/src/web/components/ActivityFeed.tsx +195 -0
  180. package/src/web/components/ContactList.tsx +340 -0
  181. package/src/web/components/Dashboard.tsx +214 -0
  182. package/src/web/components/DealPipeline.tsx +405 -0
  183. package/src/web/components/MediaGallery.tsx +334 -0
  184. package/src/web/index.html +14 -0
  185. package/src/web/index.ts +326 -0
  186. package/src/web/styles/main.css +180 -0
  187. package/src/web/types.ts +311 -0
@@ -0,0 +1,405 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import type { Deal, Contact, APIResponse } from '../types';
3
+
4
+ interface PipelineColumn {
5
+ id: string;
6
+ title: string;
7
+ stages: string[];
8
+ color: string;
9
+ }
10
+
11
+ const pipelineColumns: PipelineColumn[] = [
12
+ { id: 'early', title: 'Early Stage', stages: ['discovery', 'qualification'], color: 'border-blue-500' },
13
+ { id: 'middle', title: 'Middle Stage', stages: ['proposal'], color: 'border-yellow-500' },
14
+ { id: 'late', title: 'Late Stage', stages: ['negotiation'], color: 'border-purple-500' },
15
+ { id: 'closed', title: 'Closed', stages: ['closed_won', 'closed_lost'], color: 'border-green-500' },
16
+ ];
17
+
18
+ const stageLabels: Record<string, string> = {
19
+ discovery: 'Discovery',
20
+ qualification: 'Qualification',
21
+ proposal: 'Proposal',
22
+ negotiation: 'Negotiation',
23
+ closed_won: 'Won',
24
+ closed_lost: 'Lost',
25
+ };
26
+
27
+ export default function DealPipeline() {
28
+ const [deals, setDeals] = useState<Deal[]>([]);
29
+ const [contacts, setContacts] = useState<Contact[]>([]);
30
+ const [loading, setLoading] = useState(true);
31
+ const [showModal, setShowModal] = useState(false);
32
+ const [selectedDeal, setSelectedDeal] = useState<Deal | null>(null);
33
+ const [draggedDeal, setDraggedDeal] = useState<Deal | null>(null);
34
+ const [formData, setFormData] = useState({
35
+ title: '',
36
+ contactId: '',
37
+ value: 0,
38
+ currency: 'USD',
39
+ stage: 'discovery' as Deal['stage'],
40
+ probability: 10,
41
+ expectedCloseDate: '',
42
+ description: '',
43
+ });
44
+
45
+ const fetchData = async () => {
46
+ try {
47
+ const [dealsRes, contactsRes] = await Promise.all([
48
+ fetch('/api/deals'),
49
+ fetch('/api/contacts'),
50
+ ]);
51
+
52
+ const dealsData: APIResponse<Deal[]> = await dealsRes.json();
53
+ const contactsData: APIResponse<Contact[]> = await contactsRes.json();
54
+
55
+ if (dealsData.success) setDeals(dealsData.data || []);
56
+ if (contactsData.success) setContacts(contactsData.data || []);
57
+ } catch (error) {
58
+ console.error('Failed to fetch data:', error);
59
+ } finally {
60
+ setLoading(false);
61
+ }
62
+ };
63
+
64
+ useEffect(() => {
65
+ fetchData();
66
+ }, []);
67
+
68
+ const handleCreate = () => {
69
+ setSelectedDeal(null);
70
+ setFormData({
71
+ title: '',
72
+ contactId: '',
73
+ value: 0,
74
+ currency: 'USD',
75
+ stage: 'discovery',
76
+ probability: 10,
77
+ expectedCloseDate: '',
78
+ description: '',
79
+ });
80
+ setShowModal(true);
81
+ };
82
+
83
+ const handleEdit = (deal: Deal) => {
84
+ setSelectedDeal(deal);
85
+ setFormData({
86
+ title: deal.title,
87
+ contactId: deal.contactId,
88
+ value: deal.value,
89
+ currency: deal.currency,
90
+ stage: deal.stage,
91
+ probability: deal.probability,
92
+ expectedCloseDate: deal.expectedCloseDate || '',
93
+ description: deal.description || '',
94
+ });
95
+ setShowModal(true);
96
+ };
97
+
98
+ const handleSubmit = async (e: React.FormEvent) => {
99
+ e.preventDefault();
100
+
101
+ try {
102
+ const url = selectedDeal ? `/api/deals/${selectedDeal.id}` : '/api/deals';
103
+ const method = selectedDeal ? 'PUT' : 'POST';
104
+
105
+ const response = await fetch(url, {
106
+ method,
107
+ headers: { 'Content-Type': 'application/json' },
108
+ body: JSON.stringify(formData),
109
+ });
110
+
111
+ const data: APIResponse<Deal> = await response.json();
112
+
113
+ if (data.success && data.data) {
114
+ if (selectedDeal) {
115
+ setDeals(deals.map(d => d.id === data.data!.id ? data.data! : d));
116
+ } else {
117
+ setDeals([...deals, data.data]);
118
+ }
119
+ setShowModal(false);
120
+ }
121
+ } catch (error) {
122
+ console.error('Failed to save deal:', error);
123
+ }
124
+ };
125
+
126
+ const handleDragStart = (deal: Deal) => {
127
+ setDraggedDeal(deal);
128
+ };
129
+
130
+ const handleDragOver = (e: React.DragEvent) => {
131
+ e.preventDefault();
132
+ };
133
+
134
+ const handleDrop = async (e: React.DragEvent, newStage: Deal['stage']) => {
135
+ e.preventDefault();
136
+ if (!draggedDeal || draggedDeal.stage === newStage) {
137
+ setDraggedDeal(null);
138
+ return;
139
+ }
140
+
141
+ try {
142
+ const response = await fetch(`/api/deals/${draggedDeal.id}`, {
143
+ method: 'PUT',
144
+ headers: { 'Content-Type': 'application/json' },
145
+ body: JSON.stringify({ ...draggedDeal, stage: newStage }),
146
+ });
147
+
148
+ const data: APIResponse<Deal> = await response.json();
149
+
150
+ if (data.success && data.data) {
151
+ setDeals(deals.map(d => d.id === data.data!.id ? data.data! : d));
152
+ }
153
+ } catch (error) {
154
+ console.error('Failed to update deal stage:', error);
155
+ } finally {
156
+ setDraggedDeal(null);
157
+ }
158
+ };
159
+
160
+ const getContactName = (contactId: string) => {
161
+ const contact = contacts.find(c => c.id === contactId);
162
+ return contact?.name || 'Unknown';
163
+ };
164
+
165
+ const formatCurrency = (value: number, currency: string = 'USD') => {
166
+ return new Intl.NumberFormat('en-US', {
167
+ style: 'currency',
168
+ currency,
169
+ minimumFractionDigits: 0,
170
+ maximumFractionDigits: 0,
171
+ }).format(value);
172
+ };
173
+
174
+ const getDealsByStage = (stage: string) => {
175
+ return deals.filter(d => d.stage === stage);
176
+ };
177
+
178
+ const getColumnTotal = (column: PipelineColumn) => {
179
+ return column.stages.reduce((sum, stage) => {
180
+ return sum + getDealsByStage(stage).reduce((s, d) => s + d.value, 0);
181
+ }, 0);
182
+ };
183
+
184
+ if (loading) {
185
+ return (
186
+ <div className="flex items-center justify-center h-64">
187
+ <div className="crm-spinner" />
188
+ </div>
189
+ );
190
+ }
191
+
192
+ return (
193
+ <div className="space-y-6">
194
+ {/* Header */}
195
+ <div className="flex items-center justify-between">
196
+ <div>
197
+ <h2 className="text-2xl font-bold">Deal Pipeline</h2>
198
+ <p className="text-gray-400">
199
+ Total Pipeline: {formatCurrency(deals.reduce((sum, d) => sum + d.value, 0))}
200
+ </p>
201
+ </div>
202
+ <button onClick={handleCreate} className="crm-btn crm-btn-primary">
203
+ + Add Deal
204
+ </button>
205
+ </div>
206
+
207
+ {/* Pipeline Board */}
208
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 overflow-x-auto">
209
+ {pipelineColumns.map(column => (
210
+ <div key={column.id} className="pipeline-column">
211
+ <div className={`border-t-4 ${column.color} rounded-t-lg pt-3 mb-3`}>
212
+ <div className="flex items-center justify-between px-2">
213
+ <h3 className="font-semibold">{column.title}</h3>
214
+ <span className="text-sm text-gray-400">
215
+ {formatCurrency(getColumnTotal(column))}
216
+ </span>
217
+ </div>
218
+ </div>
219
+
220
+ {column.stages.map(stage => (
221
+ <div
222
+ key={stage}
223
+ className="mb-4"
224
+ onDragOver={handleDragOver}
225
+ onDrop={(e) => handleDrop(e, stage as Deal['stage'])}
226
+ >
227
+ <div className="flex items-center gap-2 mb-2 px-2">
228
+ <span className="text-sm text-gray-400">{stageLabels[stage]}</span>
229
+ <span className="text-xs bg-gray-700 px-2 py-0.5 rounded-full">
230
+ {getDealsByStage(stage).length}
231
+ </span>
232
+ </div>
233
+
234
+ <div className="space-y-2">
235
+ {getDealsByStage(stage).map(deal => (
236
+ <div
237
+ key={deal.id}
238
+ draggable
239
+ onDragStart={() => handleDragStart(deal)}
240
+ onClick={() => handleEdit(deal)}
241
+ className={`pipeline-card ${
242
+ draggedDeal?.id === deal.id ? 'dragging' : ''
243
+ }`}
244
+ >
245
+ <h4 className="font-medium mb-2">{deal.title}</h4>
246
+ <p className="text-sm text-gray-400 mb-2">
247
+ {getContactName(deal.contactId)}
248
+ </p>
249
+ <div className="flex items-center justify-between">
250
+ <span className="text-lg font-bold text-green-400">
251
+ {formatCurrency(deal.value, deal.currency)}
252
+ </span>
253
+ <span className="text-xs bg-gray-700 px-2 py-1 rounded">
254
+ {deal.probability}%
255
+ </span>
256
+ </div>
257
+ </div>
258
+ ))}
259
+ </div>
260
+ </div>
261
+ ))}
262
+ </div>
263
+ ))}
264
+ </div>
265
+
266
+ {/* Modal */}
267
+ {showModal && (
268
+ <div className="crm-modal-backdrop" onClick={() => setShowModal(false)}>
269
+ <div className="crm-modal max-w-2xl" onClick={(e) => e.stopPropagation()}>
270
+ <h3 className="text-xl font-bold mb-4">
271
+ {selectedDeal ? 'Edit Deal' : 'Add Deal'}
272
+ </h3>
273
+ <form onSubmit={handleSubmit} className="space-y-4">
274
+ <div>
275
+ <label className="block text-sm font-medium text-gray-400 mb-1">
276
+ Title *
277
+ </label>
278
+ <input
279
+ type="text"
280
+ required
281
+ value={formData.title}
282
+ onChange={(e) => setFormData({ ...formData, title: e.target.value })}
283
+ className="crm-input"
284
+ />
285
+ </div>
286
+
287
+ <div className="grid grid-cols-2 gap-4">
288
+ <div>
289
+ <label className="block text-sm font-medium text-gray-400 mb-1">
290
+ Contact
291
+ </label>
292
+ <select
293
+ value={formData.contactId}
294
+ onChange={(e) => setFormData({ ...formData, contactId: e.target.value })}
295
+ className="crm-input"
296
+ >
297
+ <option value="">Select contact...</option>
298
+ {contacts.map(contact => (
299
+ <option key={contact.id} value={contact.id}>
300
+ {contact.name} ({contact.company || 'No company'})
301
+ </option>
302
+ ))}
303
+ </select>
304
+ </div>
305
+ <div>
306
+ <label className="block text-sm font-medium text-gray-400 mb-1">
307
+ Stage
308
+ </label>
309
+ <select
310
+ value={formData.stage}
311
+ onChange={(e) => setFormData({ ...formData, stage: e.target.value as Deal['stage'] })}
312
+ className="crm-input"
313
+ >
314
+ {Object.entries(stageLabels).map(([value, label]) => (
315
+ <option key={value} value={value}>{label}</option>
316
+ ))}
317
+ </select>
318
+ </div>
319
+ </div>
320
+
321
+ <div className="grid grid-cols-3 gap-4">
322
+ <div>
323
+ <label className="block text-sm font-medium text-gray-400 mb-1">
324
+ Value *
325
+ </label>
326
+ <input
327
+ type="number"
328
+ required
329
+ min="0"
330
+ value={formData.value}
331
+ onChange={(e) => setFormData({ ...formData, value: parseFloat(e.target.value) })}
332
+ className="crm-input"
333
+ />
334
+ </div>
335
+ <div>
336
+ <label className="block text-sm font-medium text-gray-400 mb-1">
337
+ Currency
338
+ </label>
339
+ <select
340
+ value={formData.currency}
341
+ onChange={(e) => setFormData({ ...formData, currency: e.target.value })}
342
+ className="crm-input"
343
+ >
344
+ <option value="USD">USD</option>
345
+ <option value="EUR">EUR</option>
346
+ <option value="GBP">GBP</option>
347
+ </select>
348
+ </div>
349
+ <div>
350
+ <label className="block text-sm font-medium text-gray-400 mb-1">
351
+ Probability (%)
352
+ </label>
353
+ <input
354
+ type="number"
355
+ min="0"
356
+ max="100"
357
+ value={formData.probability}
358
+ onChange={(e) => setFormData({ ...formData, probability: parseInt(e.target.value) })}
359
+ className="crm-input"
360
+ />
361
+ </div>
362
+ </div>
363
+
364
+ <div>
365
+ <label className="block text-sm font-medium text-gray-400 mb-1">
366
+ Expected Close Date
367
+ </label>
368
+ <input
369
+ type="date"
370
+ value={formData.expectedCloseDate}
371
+ onChange={(e) => setFormData({ ...formData, expectedCloseDate: e.target.value })}
372
+ className="crm-input"
373
+ />
374
+ </div>
375
+
376
+ <div>
377
+ <label className="block text-sm font-medium text-gray-400 mb-1">
378
+ Description
379
+ </label>
380
+ <textarea
381
+ value={formData.description}
382
+ onChange={(e) => setFormData({ ...formData, description: e.target.value })}
383
+ className="crm-input h-24 resize-none"
384
+ />
385
+ </div>
386
+
387
+ <div className="flex gap-3 pt-4">
388
+ <button type="submit" className="crm-btn crm-btn-primary flex-1">
389
+ {selectedDeal ? 'Update' : 'Create'}
390
+ </button>
391
+ <button
392
+ type="button"
393
+ onClick={() => setShowModal(false)}
394
+ className="crm-btn crm-btn-secondary flex-1"
395
+ >
396
+ Cancel
397
+ </button>
398
+ </div>
399
+ </form>
400
+ </div>
401
+ </div>
402
+ )}
403
+ </div>
404
+ );
405
+ }