@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
@@ -0,0 +1,276 @@
1
+ 'use client';
2
+
3
+ import {
4
+ Button,
5
+ DataTable,
6
+ LoaderBlock,
7
+ } from '@contractspec/lib.design-system';
8
+ import type { ContractTableSort } from '@contractspec/lib.presentation-runtime-core';
9
+ import { useContractTable } from '@contractspec/lib.presentation-runtime-react';
10
+ import { Badge } from '@contractspec/lib.ui-kit-web/ui/badge';
11
+ import { HStack, VStack } from '@contractspec/lib.ui-kit-web/ui/stack';
12
+ import { Text } from '@contractspec/lib.ui-kit-web/ui/text';
13
+ import * as React from 'react';
14
+ import { type Deal, useDealList } from '../hooks/useDealList';
15
+
16
+ function formatCurrency(value: number, currency = 'USD') {
17
+ return new Intl.NumberFormat('en-US', {
18
+ style: 'currency',
19
+ currency,
20
+ minimumFractionDigits: 0,
21
+ maximumFractionDigits: 0,
22
+ }).format(value);
23
+ }
24
+
25
+ function statusVariant(status: Deal['status']) {
26
+ switch (status) {
27
+ case 'WON':
28
+ return 'default';
29
+ case 'LOST':
30
+ return 'destructive';
31
+ case 'STALE':
32
+ return 'outline';
33
+ default:
34
+ return 'secondary';
35
+ }
36
+ }
37
+
38
+ export interface DealListDataTableProps {
39
+ deals: Deal[];
40
+ totalItems: number;
41
+ pageIndex: number;
42
+ pageSize: number;
43
+ sorting: ContractTableSort[];
44
+ loading?: boolean;
45
+ onSortingChange: (sorting: ContractTableSort[]) => void;
46
+ onPaginationChange: (pagination: {
47
+ pageIndex: number;
48
+ pageSize: number;
49
+ }) => void;
50
+ onDealClick?: (dealId: string) => void;
51
+ }
52
+
53
+ export function DealListDataTable({
54
+ deals,
55
+ totalItems,
56
+ pageIndex,
57
+ pageSize,
58
+ sorting,
59
+ loading,
60
+ onSortingChange,
61
+ onPaginationChange,
62
+ onDealClick,
63
+ }: DealListDataTableProps) {
64
+ const controller = useContractTable<Deal>({
65
+ data: deals,
66
+ columns: [
67
+ {
68
+ id: 'deal',
69
+ header: 'Deal',
70
+ label: 'Deal',
71
+ accessor: (deal) => deal.name,
72
+ cell: ({ item }) => (
73
+ <VStack gap="xs">
74
+ <Text className="font-medium text-sm">{item.name}</Text>
75
+ <Text className="text-muted-foreground text-xs">
76
+ {item.companyId ?? 'Unassigned company'}
77
+ </Text>
78
+ </VStack>
79
+ ),
80
+ size: 240,
81
+ minSize: 180,
82
+ canSort: true,
83
+ canPin: true,
84
+ canResize: true,
85
+ },
86
+ {
87
+ id: 'value',
88
+ header: 'Value',
89
+ label: 'Value',
90
+ accessorKey: 'value',
91
+ cell: ({ item }) => formatCurrency(item.value, item.currency),
92
+ align: 'right',
93
+ size: 140,
94
+ canSort: true,
95
+ canResize: true,
96
+ },
97
+ {
98
+ id: 'status',
99
+ header: 'Status',
100
+ label: 'Status',
101
+ accessorKey: 'status',
102
+ cell: ({ value }) => (
103
+ <Badge variant={statusVariant(value as Deal['status'])}>
104
+ {String(value)}
105
+ </Badge>
106
+ ),
107
+ size: 130,
108
+ canSort: true,
109
+ canHide: true,
110
+ canPin: true,
111
+ canResize: true,
112
+ },
113
+ {
114
+ id: 'expectedCloseDate',
115
+ header: 'Expected Close',
116
+ label: 'Expected Close',
117
+ accessor: (deal) => deal.expectedCloseDate?.toISOString() ?? '',
118
+ cell: ({ item }) =>
119
+ item.expectedCloseDate?.toLocaleDateString() ?? 'Not scheduled',
120
+ size: 170,
121
+ canSort: true,
122
+ canHide: true,
123
+ canResize: true,
124
+ },
125
+ {
126
+ id: 'updatedAt',
127
+ header: 'Updated',
128
+ label: 'Updated',
129
+ accessor: (deal) => deal.updatedAt.toISOString(),
130
+ cell: ({ item }) => item.updatedAt.toLocaleDateString(),
131
+ size: 140,
132
+ canSort: true,
133
+ canHide: true,
134
+ canResize: true,
135
+ },
136
+ {
137
+ id: 'actions',
138
+ header: 'Actions',
139
+ label: 'Actions',
140
+ accessor: (deal) => deal.id,
141
+ cell: ({ item }) => (
142
+ <Button
143
+ variant="ghost"
144
+ size="sm"
145
+ onPress={() => onDealClick?.(item.id)}
146
+ >
147
+ Actions
148
+ </Button>
149
+ ),
150
+ size: 120,
151
+ canSort: false,
152
+ canHide: false,
153
+ canPin: false,
154
+ canResize: false,
155
+ },
156
+ ],
157
+ executionMode: 'server',
158
+ selectionMode: 'multiple',
159
+ totalItems,
160
+ state: {
161
+ sorting,
162
+ pagination: {
163
+ pageIndex,
164
+ pageSize,
165
+ },
166
+ },
167
+ onSortingChange: onSortingChange,
168
+ onPaginationChange: onPaginationChange,
169
+ initialState: {
170
+ columnVisibility: { updatedAt: false },
171
+ columnPinning: { left: ['deal', 'status'], right: [] },
172
+ },
173
+ renderExpandedContent: (deal) => (
174
+ <VStack gap="sm" className="py-2">
175
+ <HStack justify="between">
176
+ <Text className="font-medium text-sm">Owner</Text>
177
+ <Text className="text-muted-foreground text-sm">{deal.ownerId}</Text>
178
+ </HStack>
179
+ <HStack justify="between">
180
+ <Text className="font-medium text-sm">Contact</Text>
181
+ <Text className="text-muted-foreground text-sm">
182
+ {deal.contactId ?? 'No linked contact'}
183
+ </Text>
184
+ </HStack>
185
+ {deal.wonSource ? (
186
+ <HStack justify="between">
187
+ <Text className="font-medium text-sm">Won Source</Text>
188
+ <Text className="text-muted-foreground text-sm">
189
+ {deal.wonSource}
190
+ </Text>
191
+ </HStack>
192
+ ) : null}
193
+ {deal.lostReason ? (
194
+ <HStack justify="between">
195
+ <Text className="font-medium text-sm">Lost Reason</Text>
196
+ <Text className="text-muted-foreground text-sm">
197
+ {deal.lostReason}
198
+ </Text>
199
+ </HStack>
200
+ ) : null}
201
+ {deal.notes ? (
202
+ <VStack gap="xs">
203
+ <Text className="font-medium text-sm">Notes</Text>
204
+ <Text className="text-muted-foreground text-sm">{deal.notes}</Text>
205
+ </VStack>
206
+ ) : null}
207
+ </VStack>
208
+ ),
209
+ getCanExpand: () => true,
210
+ });
211
+
212
+ return (
213
+ <DataTable
214
+ controller={controller}
215
+ title="All Deals"
216
+ description="Server-mode table using the shared ContractSpec controller."
217
+ loading={loading}
218
+ toolbar={
219
+ <HStack gap="sm" className="flex-wrap">
220
+ <Text className="text-muted-foreground text-sm">
221
+ Selected {controller.selectedRowIds.length}
222
+ </Text>
223
+ <Text className="text-muted-foreground text-sm">
224
+ {totalItems} total deals
225
+ </Text>
226
+ </HStack>
227
+ }
228
+ footer={`Page ${controller.pageIndex + 1} of ${controller.pageCount}`}
229
+ emptyState={
230
+ <div className="rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm">
231
+ No deals found
232
+ </div>
233
+ }
234
+ />
235
+ );
236
+ }
237
+
238
+ export function DealListTab({
239
+ onDealClick,
240
+ }: {
241
+ onDealClick?: (dealId: string) => void;
242
+ }) {
243
+ const [sorting, setSorting] = React.useState<ContractTableSort[]>([
244
+ { id: 'value', desc: true },
245
+ ]);
246
+ const [pagination, setPagination] = React.useState({
247
+ pageIndex: 0,
248
+ pageSize: 3,
249
+ });
250
+ const { data, loading } = useDealList({
251
+ pageIndex: pagination.pageIndex,
252
+ pageSize: pagination.pageSize,
253
+ sorting,
254
+ });
255
+
256
+ if (loading && !data) {
257
+ return <LoaderBlock label="Loading deals..." />;
258
+ }
259
+
260
+ return (
261
+ <DealListDataTable
262
+ deals={data?.deals ?? []}
263
+ totalItems={data?.total ?? 0}
264
+ pageIndex={pagination.pageIndex}
265
+ pageSize={pagination.pageSize}
266
+ sorting={sorting}
267
+ loading={loading}
268
+ onSortingChange={(nextSorting) => {
269
+ setSorting(nextSorting);
270
+ setPagination((current) => ({ ...current, pageIndex: 0 }));
271
+ }}
272
+ onPaginationChange={setPagination}
273
+ onDealClick={onDealClick}
274
+ />
275
+ );
276
+ }
package/tsconfig.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
- "extends": "@contractspec/tool.typescript/react-library.json",
3
- "include": ["src"],
4
- "exclude": ["node_modules", "dist"],
5
- "compilerOptions": {
6
- "rootDir": "src",
7
- "outDir": "dist"
8
- }
2
+ "extends": "@contractspec/tool.typescript/react-library.json",
3
+ "include": ["src"],
4
+ "exclude": ["node_modules", "dist"],
5
+ "compilerOptions": {
6
+ "rootDir": "src",
7
+ "outDir": "dist"
8
+ }
9
9
  }
10
-
package/tsdown.config.js CHANGED
@@ -1,6 +1,10 @@
1
- import { defineConfig, moduleLibrary, withDevExports } from '@contractspec/tool.bun';
1
+ import {
2
+ defineConfig,
3
+ moduleLibrary,
4
+ withDevExports,
5
+ } from '@contractspec/tool.bun';
2
6
 
3
7
  export default defineConfig((options) => ({
4
- ...moduleLibrary,
5
- ...withDevExports,
8
+ ...moduleLibrary,
9
+ ...withDevExports,
6
10
  }));