@contractspec/example.crm-pipeline 3.7.5 → 3.7.7
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.
- package/.turbo/turbo-build.log +8 -8
- package/AGENTS.md +51 -33
- package/CHANGELOG.md +16 -0
- package/README.md +66 -148
- package/dist/browser/events/contact.event.js +1 -1
- package/dist/browser/events/deal.event.js +1 -1
- package/dist/browser/events/index.js +3 -3
- package/dist/browser/events/task.event.js +1 -1
- package/dist/browser/index.js +293 -293
- package/dist/browser/ui/CrmDashboard.js +221 -221
- package/dist/browser/ui/CrmDealCard.js +5 -5
- package/dist/browser/ui/CrmPipelineBoard.js +13 -13
- package/dist/browser/ui/hooks/index.js +2 -2
- package/dist/browser/ui/hooks/useDealList.js +1 -1
- package/dist/browser/ui/hooks/useDealMutations.js +1 -1
- package/dist/browser/ui/index.js +290 -290
- package/dist/browser/ui/modals/CreateDealModal.js +12 -12
- package/dist/browser/ui/modals/DealActionsModal.js +21 -21
- package/dist/browser/ui/modals/index.js +33 -33
- package/dist/browser/ui/renderers/index.js +116 -116
- package/dist/browser/ui/renderers/pipeline.renderer.js +97 -97
- package/dist/deal/index.d.ts +2 -2
- package/dist/events/contact.event.js +1 -1
- package/dist/events/deal.event.js +1 -1
- package/dist/events/index.js +3 -3
- package/dist/events/task.event.js +1 -1
- package/dist/handlers/index.d.ts +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +293 -293
- package/dist/node/events/contact.event.js +1 -1
- package/dist/node/events/deal.event.js +1 -1
- package/dist/node/events/index.js +3 -3
- package/dist/node/events/task.event.js +1 -1
- package/dist/node/index.js +293 -293
- package/dist/node/ui/CrmDashboard.js +221 -221
- package/dist/node/ui/CrmDealCard.js +5 -5
- package/dist/node/ui/CrmPipelineBoard.js +13 -13
- package/dist/node/ui/hooks/index.js +2 -2
- package/dist/node/ui/hooks/useDealList.js +1 -1
- package/dist/node/ui/hooks/useDealMutations.js +1 -1
- package/dist/node/ui/index.js +290 -290
- package/dist/node/ui/modals/CreateDealModal.js +12 -12
- package/dist/node/ui/modals/DealActionsModal.js +21 -21
- package/dist/node/ui/modals/index.js +33 -33
- package/dist/node/ui/renderers/index.js +116 -116
- package/dist/node/ui/renderers/pipeline.renderer.js +97 -97
- package/dist/operations/index.d.ts +1 -1
- package/dist/ui/CrmDashboard.js +221 -221
- package/dist/ui/CrmDealCard.js +5 -5
- package/dist/ui/CrmPipelineBoard.js +13 -13
- package/dist/ui/hooks/index.d.ts +2 -2
- package/dist/ui/hooks/index.js +2 -2
- package/dist/ui/hooks/useDealList.js +1 -1
- package/dist/ui/hooks/useDealMutations.d.ts +9 -0
- package/dist/ui/hooks/useDealMutations.js +1 -1
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +290 -290
- package/dist/ui/modals/CreateDealModal.js +12 -12
- package/dist/ui/modals/DealActionsModal.js +21 -21
- package/dist/ui/modals/index.js +33 -33
- package/dist/ui/renderers/index.d.ts +1 -1
- package/dist/ui/renderers/index.js +116 -116
- package/dist/ui/renderers/pipeline.renderer.d.ts +1 -1
- package/dist/ui/renderers/pipeline.renderer.js +97 -97
- package/package.json +14 -14
- package/src/crm-pipeline.feature.ts +86 -86
- package/src/deal/deal.enum.ts +8 -8
- package/src/deal/deal.operation.ts +255 -255
- package/src/deal/deal.schema.ts +92 -92
- package/src/deal/deal.test-spec.ts +48 -48
- package/src/deal/index.ts +17 -19
- package/src/docs/crm-pipeline.docblock.ts +43 -43
- package/src/entities/company.entity.ts +52 -52
- package/src/entities/contact.entity.ts +67 -67
- package/src/entities/deal.entity.ts +134 -134
- package/src/entities/index.ts +27 -27
- package/src/entities/task.entity.ts +105 -105
- package/src/events/contact.event.ts +22 -22
- package/src/events/deal.event.ts +77 -77
- package/src/events/task.event.ts +19 -19
- package/src/example.ts +32 -32
- package/src/handlers/crm.handlers.ts +358 -357
- package/src/handlers/deal.handlers.ts +179 -179
- package/src/handlers/index.ts +18 -19
- package/src/handlers/mock-data.ts +167 -167
- package/src/index.ts +11 -11
- package/src/operations/index.ts +16 -16
- package/src/presentations/dashboard.presentation.ts +45 -45
- package/src/presentations/pipeline.presentation.ts +90 -90
- package/src/seeders/index.ts +26 -26
- package/src/shared/overlay-types.ts +23 -23
- package/src/ui/CrmDashboard.tsx +256 -256
- package/src/ui/CrmDealCard.tsx +64 -64
- package/src/ui/CrmPipelineBoard.tsx +105 -105
- package/src/ui/hooks/index.ts +3 -3
- package/src/ui/hooks/useDealList.ts +85 -85
- package/src/ui/hooks/useDealMutations.ts +151 -150
- package/src/ui/index.ts +5 -10
- package/src/ui/modals/CreateDealModal.tsx +217 -217
- package/src/ui/modals/DealActionsModal.tsx +390 -390
- package/src/ui/overlays/demo-overlays.ts +43 -43
- package/src/ui/renderers/index.ts +4 -3
- package/src/ui/renderers/pipeline.markdown.ts +165 -165
- package/src/ui/renderers/pipeline.renderer.tsx +17 -16
- package/tsconfig.json +7 -8
- package/tsdown.config.js +7 -3
|
@@ -1,239 +1,239 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
+
import { Button, Input } from '@contractspec/lib.design-system';
|
|
3
4
|
/**
|
|
4
5
|
* CreateDealModal - Form for creating a new deal
|
|
5
6
|
*
|
|
6
7
|
* Wires to CreateDealContract via useDealMutations hook.
|
|
7
8
|
*/
|
|
8
9
|
import { useState } from 'react';
|
|
9
|
-
import { Button, Input } from '@contractspec/lib.design-system';
|
|
10
10
|
|
|
11
11
|
// Local type definition for modal props
|
|
12
12
|
export interface CreateDealInput {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
13
|
+
name: string;
|
|
14
|
+
value: number;
|
|
15
|
+
currency: string;
|
|
16
|
+
pipelineId: string;
|
|
17
|
+
stageId: string;
|
|
18
|
+
expectedCloseDate?: Date;
|
|
19
|
+
contactId?: string;
|
|
20
|
+
companyId?: string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
interface CreateDealModalProps {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
isOpen: boolean;
|
|
25
|
+
onClose: () => void;
|
|
26
|
+
onSubmit: (input: CreateDealInput) => Promise<void>;
|
|
27
|
+
stages: { id: string; name: string }[];
|
|
28
|
+
isLoading?: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
const CURRENCIES = ['USD', 'EUR', 'GBP', 'CAD'];
|
|
32
32
|
const DEFAULT_PIPELINE_ID = 'pipeline-1';
|
|
33
33
|
|
|
34
34
|
export function CreateDealModal({
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
isOpen,
|
|
36
|
+
onClose,
|
|
37
|
+
onSubmit,
|
|
38
|
+
stages,
|
|
39
|
+
isLoading = false,
|
|
40
40
|
}: CreateDealModalProps) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
41
|
+
const [name, setName] = useState('');
|
|
42
|
+
const [value, setValue] = useState('');
|
|
43
|
+
const [currency, setCurrency] = useState('USD');
|
|
44
|
+
const [stageId, setStageId] = useState(stages[0]?.id ?? '');
|
|
45
|
+
const [expectedCloseDate, setExpectedCloseDate] = useState('');
|
|
46
|
+
const [error, setError] = useState<string | null>(null);
|
|
47
|
+
|
|
48
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
49
|
+
e.preventDefault();
|
|
50
|
+
setError(null);
|
|
51
|
+
|
|
52
|
+
// Validation
|
|
53
|
+
if (!name.trim()) {
|
|
54
|
+
setError('Deal name is required');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const numericValue = parseFloat(value);
|
|
59
|
+
if (isNaN(numericValue) || numericValue <= 0) {
|
|
60
|
+
setError('Value must be a positive number');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!stageId) {
|
|
65
|
+
setError('Please select a pipeline stage');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
await onSubmit({
|
|
71
|
+
name: name.trim(),
|
|
72
|
+
value: numericValue,
|
|
73
|
+
currency,
|
|
74
|
+
pipelineId: DEFAULT_PIPELINE_ID,
|
|
75
|
+
stageId,
|
|
76
|
+
expectedCloseDate: expectedCloseDate
|
|
77
|
+
? new Date(expectedCloseDate)
|
|
78
|
+
: undefined,
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// Reset form
|
|
82
|
+
setName('');
|
|
83
|
+
setValue('');
|
|
84
|
+
setCurrency('USD');
|
|
85
|
+
setStageId(stages[0]?.id ?? '');
|
|
86
|
+
setExpectedCloseDate('');
|
|
87
|
+
onClose();
|
|
88
|
+
} catch (err) {
|
|
89
|
+
setError(err instanceof Error ? err.message : 'Failed to create deal');
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (!isOpen) return null;
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div className="fixed inset-0 z-50 flex items-center justify-center">
|
|
97
|
+
{/* Backdrop */}
|
|
98
|
+
<div
|
|
99
|
+
className="absolute inset-0 bg-background/80 backdrop-blur-sm"
|
|
100
|
+
onClick={onClose}
|
|
101
|
+
role="button"
|
|
102
|
+
tabIndex={0}
|
|
103
|
+
onKeyDown={(e) => {
|
|
104
|
+
if (e.key === 'Enter' || e.key === ' ') onClose();
|
|
105
|
+
}}
|
|
106
|
+
aria-label="Close modal"
|
|
107
|
+
/>
|
|
108
|
+
|
|
109
|
+
{/* Modal */}
|
|
110
|
+
<div className="relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl">
|
|
111
|
+
<h2 className="mb-4 font-semibold text-xl">Create New Deal</h2>
|
|
112
|
+
|
|
113
|
+
<form onSubmit={handleSubmit} className="space-y-4">
|
|
114
|
+
{/* Deal Name */}
|
|
115
|
+
<div>
|
|
116
|
+
<label
|
|
117
|
+
htmlFor="deal-name"
|
|
118
|
+
className="mb-1 block font-medium text-muted-foreground text-sm"
|
|
119
|
+
>
|
|
120
|
+
Deal Name *
|
|
121
|
+
</label>
|
|
122
|
+
<Input
|
|
123
|
+
id="deal-name"
|
|
124
|
+
value={name}
|
|
125
|
+
onChange={(e) => setName(e.target.value)}
|
|
126
|
+
placeholder="e.g., Enterprise License - Acme Corp"
|
|
127
|
+
disabled={isLoading}
|
|
128
|
+
/>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{/* Value & Currency */}
|
|
132
|
+
<div className="flex gap-3">
|
|
133
|
+
<div className="flex-1">
|
|
134
|
+
<label
|
|
135
|
+
htmlFor="deal-value"
|
|
136
|
+
className="mb-1 block font-medium text-muted-foreground text-sm"
|
|
137
|
+
>
|
|
138
|
+
Value *
|
|
139
|
+
</label>
|
|
140
|
+
<Input
|
|
141
|
+
id="deal-value"
|
|
142
|
+
type="number"
|
|
143
|
+
min="0"
|
|
144
|
+
step="0.01"
|
|
145
|
+
value={value}
|
|
146
|
+
onChange={(e) => setValue(e.target.value)}
|
|
147
|
+
placeholder="50000"
|
|
148
|
+
disabled={isLoading}
|
|
149
|
+
/>
|
|
150
|
+
</div>
|
|
151
|
+
<div className="w-24">
|
|
152
|
+
<label
|
|
153
|
+
htmlFor="deal-currency"
|
|
154
|
+
className="mb-1 block font-medium text-muted-foreground text-sm"
|
|
155
|
+
>
|
|
156
|
+
Currency
|
|
157
|
+
</label>
|
|
158
|
+
<select
|
|
159
|
+
id="deal-currency"
|
|
160
|
+
value={currency}
|
|
161
|
+
onChange={(e) => setCurrency(e.target.value)}
|
|
162
|
+
disabled={isLoading}
|
|
163
|
+
className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
|
|
164
|
+
>
|
|
165
|
+
{CURRENCIES.map((c) => (
|
|
166
|
+
<option key={c} value={c}>
|
|
167
|
+
{c}
|
|
168
|
+
</option>
|
|
169
|
+
))}
|
|
170
|
+
</select>
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
{/* Stage */}
|
|
175
|
+
<div>
|
|
176
|
+
<label
|
|
177
|
+
htmlFor="deal-stage"
|
|
178
|
+
className="mb-1 block font-medium text-muted-foreground text-sm"
|
|
179
|
+
>
|
|
180
|
+
Pipeline Stage *
|
|
181
|
+
</label>
|
|
182
|
+
<select
|
|
183
|
+
id="deal-stage"
|
|
184
|
+
value={stageId}
|
|
185
|
+
onChange={(e) => setStageId(e.target.value)}
|
|
186
|
+
disabled={isLoading}
|
|
187
|
+
className="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50"
|
|
188
|
+
>
|
|
189
|
+
{stages.map((stage) => (
|
|
190
|
+
<option key={stage.id} value={stage.id}>
|
|
191
|
+
{stage.name}
|
|
192
|
+
</option>
|
|
193
|
+
))}
|
|
194
|
+
</select>
|
|
195
|
+
</div>
|
|
196
|
+
|
|
197
|
+
{/* Expected Close Date */}
|
|
198
|
+
<div>
|
|
199
|
+
<label
|
|
200
|
+
htmlFor="deal-close-date"
|
|
201
|
+
className="mb-1 block font-medium text-muted-foreground text-sm"
|
|
202
|
+
>
|
|
203
|
+
Expected Close Date
|
|
204
|
+
</label>
|
|
205
|
+
<Input
|
|
206
|
+
id="deal-close-date"
|
|
207
|
+
type="date"
|
|
208
|
+
value={expectedCloseDate}
|
|
209
|
+
onChange={(e) => setExpectedCloseDate(e.target.value)}
|
|
210
|
+
disabled={isLoading}
|
|
211
|
+
/>
|
|
212
|
+
</div>
|
|
213
|
+
|
|
214
|
+
{/* Error Message */}
|
|
215
|
+
{error && (
|
|
216
|
+
<div className="rounded-md bg-destructive/10 p-3 text-destructive text-sm">
|
|
217
|
+
{error}
|
|
218
|
+
</div>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
{/* Actions */}
|
|
222
|
+
<div className="flex justify-end gap-3 pt-2">
|
|
223
|
+
<Button
|
|
224
|
+
type="button"
|
|
225
|
+
variant="ghost"
|
|
226
|
+
onPress={onClose}
|
|
227
|
+
disabled={isLoading}
|
|
228
|
+
>
|
|
229
|
+
Cancel
|
|
230
|
+
</Button>
|
|
231
|
+
<Button type="submit" disabled={isLoading}>
|
|
232
|
+
{isLoading ? 'Creating...' : 'Create Deal'}
|
|
233
|
+
</Button>
|
|
234
|
+
</div>
|
|
235
|
+
</form>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
);
|
|
239
239
|
}
|