@contractspec/example.crm-pipeline 3.7.6 → 3.7.10

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 (130) hide show
  1. package/.turbo/turbo-build.log +45 -42
  2. package/AGENTS.md +51 -33
  3. package/CHANGELOG.md +36 -0
  4. package/README.md +67 -148
  5. package/dist/browser/docs/crm-pipeline.docblock.js +1 -1
  6. package/dist/browser/docs/index.js +1 -1
  7. package/dist/browser/events/contact.event.js +1 -1
  8. package/dist/browser/events/deal.event.js +1 -1
  9. package/dist/browser/events/index.js +3 -3
  10. package/dist/browser/events/task.event.js +1 -1
  11. package/dist/browser/handlers/crm.handlers.js +13 -2
  12. package/dist/browser/handlers/index.js +13 -2
  13. package/dist/browser/index.js +680 -447
  14. package/dist/browser/ui/CrmDashboard.js +574 -352
  15. package/dist/browser/ui/CrmDealCard.js +5 -5
  16. package/dist/browser/ui/CrmPipelineBoard.js +13 -13
  17. package/dist/browser/ui/hooks/index.js +21 -10
  18. package/dist/browser/ui/hooks/useDealList.js +20 -9
  19. package/dist/browser/ui/hooks/useDealMutations.js +1 -1
  20. package/dist/browser/ui/index.js +683 -450
  21. package/dist/browser/ui/modals/CreateDealModal.js +12 -12
  22. package/dist/browser/ui/modals/DealActionsModal.js +21 -21
  23. package/dist/browser/ui/modals/index.js +33 -33
  24. package/dist/browser/ui/renderers/index.js +140 -118
  25. package/dist/browser/ui/renderers/pipeline.markdown.js +13 -2
  26. package/dist/browser/ui/renderers/pipeline.renderer.js +108 -97
  27. package/dist/browser/ui/tables/DealListTab.js +390 -0
  28. package/dist/deal/index.d.ts +2 -2
  29. package/dist/docs/crm-pipeline.docblock.js +1 -1
  30. package/dist/docs/index.js +1 -1
  31. package/dist/events/contact.event.js +1 -1
  32. package/dist/events/deal.event.js +1 -1
  33. package/dist/events/index.js +3 -3
  34. package/dist/events/task.event.js +1 -1
  35. package/dist/handlers/crm.handlers.d.ts +2 -0
  36. package/dist/handlers/crm.handlers.js +13 -2
  37. package/dist/handlers/index.d.ts +2 -2
  38. package/dist/handlers/index.js +13 -2
  39. package/dist/index.d.ts +3 -3
  40. package/dist/index.js +680 -447
  41. package/dist/node/docs/crm-pipeline.docblock.js +1 -1
  42. package/dist/node/docs/index.js +1 -1
  43. package/dist/node/events/contact.event.js +1 -1
  44. package/dist/node/events/deal.event.js +1 -1
  45. package/dist/node/events/index.js +3 -3
  46. package/dist/node/events/task.event.js +1 -1
  47. package/dist/node/handlers/crm.handlers.js +13 -2
  48. package/dist/node/handlers/index.js +13 -2
  49. package/dist/node/index.js +680 -447
  50. package/dist/node/ui/CrmDashboard.js +574 -352
  51. package/dist/node/ui/CrmDealCard.js +5 -5
  52. package/dist/node/ui/CrmPipelineBoard.js +13 -13
  53. package/dist/node/ui/hooks/index.js +21 -10
  54. package/dist/node/ui/hooks/useDealList.js +20 -9
  55. package/dist/node/ui/hooks/useDealMutations.js +1 -1
  56. package/dist/node/ui/index.js +683 -450
  57. package/dist/node/ui/modals/CreateDealModal.js +12 -12
  58. package/dist/node/ui/modals/DealActionsModal.js +21 -21
  59. package/dist/node/ui/modals/index.js +33 -33
  60. package/dist/node/ui/renderers/index.js +140 -118
  61. package/dist/node/ui/renderers/pipeline.markdown.js +13 -2
  62. package/dist/node/ui/renderers/pipeline.renderer.js +108 -97
  63. package/dist/node/ui/tables/DealListTab.js +390 -0
  64. package/dist/operations/index.d.ts +1 -1
  65. package/dist/ui/CrmDashboard.js +574 -352
  66. package/dist/ui/CrmDealCard.js +5 -5
  67. package/dist/ui/CrmPipelineBoard.js +13 -13
  68. package/dist/ui/hooks/index.d.ts +2 -2
  69. package/dist/ui/hooks/index.js +21 -10
  70. package/dist/ui/hooks/useDealList.d.ts +8 -2
  71. package/dist/ui/hooks/useDealList.js +20 -9
  72. package/dist/ui/hooks/useDealMutations.d.ts +9 -0
  73. package/dist/ui/hooks/useDealMutations.js +1 -1
  74. package/dist/ui/index.d.ts +3 -3
  75. package/dist/ui/index.js +683 -450
  76. package/dist/ui/modals/CreateDealModal.js +12 -12
  77. package/dist/ui/modals/DealActionsModal.js +21 -21
  78. package/dist/ui/modals/index.js +33 -33
  79. package/dist/ui/renderers/index.d.ts +1 -1
  80. package/dist/ui/renderers/index.js +140 -118
  81. package/dist/ui/renderers/pipeline.markdown.js +13 -2
  82. package/dist/ui/renderers/pipeline.renderer.d.ts +1 -1
  83. package/dist/ui/renderers/pipeline.renderer.js +108 -97
  84. package/dist/ui/tables/DealListTab.d.ts +20 -0
  85. package/dist/ui/tables/DealListTab.js +391 -0
  86. package/dist/ui/tables/DealListTab.smoke.test.d.ts +1 -0
  87. package/package.json +29 -14
  88. package/src/crm-pipeline.feature.ts +86 -86
  89. package/src/deal/deal.enum.ts +8 -8
  90. package/src/deal/deal.operation.ts +255 -255
  91. package/src/deal/deal.schema.ts +92 -92
  92. package/src/deal/deal.test-spec.ts +48 -48
  93. package/src/deal/index.ts +17 -19
  94. package/src/docs/crm-pipeline.docblock.ts +44 -44
  95. package/src/entities/company.entity.ts +52 -52
  96. package/src/entities/contact.entity.ts +67 -67
  97. package/src/entities/deal.entity.ts +134 -134
  98. package/src/entities/index.ts +27 -27
  99. package/src/entities/task.entity.ts +105 -105
  100. package/src/events/contact.event.ts +22 -22
  101. package/src/events/deal.event.ts +77 -77
  102. package/src/events/task.event.ts +19 -19
  103. package/src/example.ts +32 -32
  104. package/src/handlers/crm.handlers.ts +375 -357
  105. package/src/handlers/deal.handlers.ts +179 -179
  106. package/src/handlers/index.ts +18 -19
  107. package/src/handlers/mock-data.ts +167 -167
  108. package/src/index.ts +11 -11
  109. package/src/operations/index.ts +16 -16
  110. package/src/presentations/dashboard.presentation.ts +45 -45
  111. package/src/presentations/pipeline.presentation.ts +90 -90
  112. package/src/seeders/index.ts +26 -26
  113. package/src/shared/overlay-types.ts +23 -23
  114. package/src/ui/CrmDashboard.tsx +210 -279
  115. package/src/ui/CrmDealCard.tsx +64 -64
  116. package/src/ui/CrmPipelineBoard.tsx +105 -105
  117. package/src/ui/hooks/index.ts +3 -3
  118. package/src/ui/hooks/useDealList.ts +113 -85
  119. package/src/ui/hooks/useDealMutations.ts +151 -150
  120. package/src/ui/index.ts +5 -10
  121. package/src/ui/modals/CreateDealModal.tsx +217 -217
  122. package/src/ui/modals/DealActionsModal.tsx +390 -390
  123. package/src/ui/overlays/demo-overlays.ts +43 -43
  124. package/src/ui/renderers/index.ts +4 -3
  125. package/src/ui/renderers/pipeline.markdown.ts +165 -165
  126. package/src/ui/renderers/pipeline.renderer.tsx +17 -16
  127. package/src/ui/tables/DealListTab.smoke.test.tsx +149 -0
  128. package/src/ui/tables/DealListTab.tsx +276 -0
  129. package/tsconfig.json +7 -8
  130. 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
- name: string;
14
- value: number;
15
- currency: string;
16
- pipelineId: string;
17
- stageId: string;
18
- expectedCloseDate?: Date;
19
- contactId?: string;
20
- companyId?: string;
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
- isOpen: boolean;
25
- onClose: () => void;
26
- onSubmit: (input: CreateDealInput) => Promise<void>;
27
- stages: { id: string; name: string }[];
28
- isLoading?: boolean;
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
- isOpen,
36
- onClose,
37
- onSubmit,
38
- stages,
39
- isLoading = false,
35
+ isOpen,
36
+ onClose,
37
+ onSubmit,
38
+ stages,
39
+ isLoading = false,
40
40
  }: CreateDealModalProps) {
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="bg-background/80 absolute inset-0 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="bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl">
111
- <h2 className="mb-4 text-xl font-semibold">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="text-muted-foreground mb-1 block text-sm font-medium"
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="text-muted-foreground mb-1 block text-sm font-medium"
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="text-muted-foreground mb-1 block text-sm font-medium"
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="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"
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="text-muted-foreground mb-1 block text-sm font-medium"
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="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"
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="text-muted-foreground mb-1 block text-sm font-medium"
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="bg-destructive/10 text-destructive rounded-md p-3 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
- );
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
  }