@contractspec/example.crm-pipeline 3.7.19 → 3.7.22
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 +32 -32
- package/CHANGELOG.md +62 -0
- package/dist/browser/crm-pipeline.feature.js +1 -1
- package/dist/browser/example.js +1 -1
- package/dist/browser/index.js +5 -5
- package/dist/browser/ui/CrmDashboard.js +1 -1
- package/dist/browser/ui/hooks/index.js +1 -1
- package/dist/browser/ui/hooks/useDealList.js +1 -1
- package/dist/browser/ui/index.js +4 -4
- package/dist/browser/ui/renderers/index.js +3 -3
- package/dist/browser/ui/renderers/pipeline.renderer.js +1 -1
- package/dist/browser/ui/tables/DealListTab.js +1 -1
- package/dist/crm-pipeline.feature.js +1 -1
- package/dist/example.d.ts +3 -2
- package/dist/example.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +5 -5
- package/dist/node/crm-pipeline.feature.js +1 -1
- package/dist/node/example.js +1 -1
- package/dist/node/index.js +5 -5
- package/dist/node/ui/CrmDashboard.js +1 -1
- package/dist/node/ui/hooks/index.js +1 -1
- package/dist/node/ui/hooks/useDealList.js +1 -1
- package/dist/node/ui/index.js +4 -4
- package/dist/node/ui/renderers/index.js +3 -3
- package/dist/node/ui/renderers/pipeline.renderer.js +1 -1
- package/dist/node/ui/tables/DealListTab.js +1 -1
- package/dist/ui/CrmDashboard.js +1 -1
- package/dist/ui/hooks/index.js +1 -1
- package/dist/ui/hooks/useDealList.js +1 -1
- package/dist/ui/index.js +4 -4
- package/dist/ui/renderers/index.js +3 -3
- package/dist/ui/renderers/pipeline.renderer.js +1 -1
- package/dist/ui/tables/DealListTab.d.ts +5 -1
- package/dist/ui/tables/DealListTab.js +1 -1
- package/package.json +11 -11
- package/src/crm-pipeline.feature.ts +1 -1
- package/src/example.ts +15 -26
- package/src/index.ts +1 -0
- package/src/ui/hooks/useDealList.ts +1 -1
- package/src/ui/tables/DealListTab.smoke.test.tsx +70 -5
- package/src/ui/tables/DealListTab.tsx +96 -8
|
@@ -20,16 +20,26 @@ beforeAll(() => {
|
|
|
20
20
|
value: SyntaxError,
|
|
21
21
|
configurable: true,
|
|
22
22
|
});
|
|
23
|
+
const encodeURIComponentFromGlobal =
|
|
24
|
+
globalThis.encodeURIComponent.bind(globalThis);
|
|
25
|
+
const decodeURIComponentFromGlobal =
|
|
26
|
+
globalThis.decodeURIComponent.bind(globalThis);
|
|
27
|
+
Object.assign(windowInstance, {
|
|
28
|
+
encodeURIComponent: encodeURIComponentFromGlobal,
|
|
29
|
+
decodeURIComponent: decodeURIComponentFromGlobal,
|
|
30
|
+
});
|
|
23
31
|
Object.assign(globalThis, {
|
|
24
32
|
window: windowInstance,
|
|
25
33
|
document: windowInstance.document,
|
|
26
34
|
navigator: windowInstance.navigator,
|
|
35
|
+
location: windowInstance.location,
|
|
27
36
|
HTMLElement: windowInstance.HTMLElement,
|
|
28
37
|
HTMLButtonElement: windowInstance.HTMLButtonElement,
|
|
29
38
|
Node: windowInstance.Node,
|
|
30
39
|
Event: windowInstance.Event,
|
|
31
40
|
MouseEvent: windowInstance.MouseEvent,
|
|
32
41
|
MutationObserver: windowInstance.MutationObserver,
|
|
42
|
+
DocumentFragment: windowInstance.DocumentFragment,
|
|
33
43
|
getComputedStyle: windowInstance.getComputedStyle.bind(windowInstance),
|
|
34
44
|
requestAnimationFrame: (callback: FrameRequestCallback) =>
|
|
35
45
|
setTimeout(() => callback(Date.now()), 0),
|
|
@@ -45,10 +55,23 @@ afterEach(() => {
|
|
|
45
55
|
function sortDeals(
|
|
46
56
|
pageIndex: number,
|
|
47
57
|
pageSize: number,
|
|
48
|
-
sorting: { id: string; desc: boolean }[]
|
|
58
|
+
sorting: { id: string; desc: boolean }[],
|
|
59
|
+
search: string,
|
|
60
|
+
status: 'OPEN' | 'WON' | 'LOST' | 'all'
|
|
49
61
|
) {
|
|
50
62
|
const [sort] = sorting;
|
|
51
|
-
const
|
|
63
|
+
const filtered = TEST_DEALS.filter((deal) => {
|
|
64
|
+
const matchesStatus = status === 'all' ? true : deal.status === status;
|
|
65
|
+
const matchesSearch =
|
|
66
|
+
!search ||
|
|
67
|
+
[deal.name, deal.companyId, deal.contactId, deal.notes, deal.ownerId]
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.join(' ')
|
|
70
|
+
.toLowerCase()
|
|
71
|
+
.includes(search.toLowerCase());
|
|
72
|
+
return matchesStatus && matchesSearch;
|
|
73
|
+
});
|
|
74
|
+
const sorted = [...filtered].sort((left, right) => {
|
|
52
75
|
const leftValue =
|
|
53
76
|
sort?.id === 'deal'
|
|
54
77
|
? left.name
|
|
@@ -82,15 +105,50 @@ function Harness() {
|
|
|
82
105
|
pageIndex: 0,
|
|
83
106
|
pageSize: 3,
|
|
84
107
|
});
|
|
108
|
+
const [search, setSearch] = React.useState('');
|
|
109
|
+
const [status, setStatus] = React.useState<'OPEN' | 'WON' | 'LOST' | 'all'>(
|
|
110
|
+
'all'
|
|
111
|
+
);
|
|
112
|
+
const filteredDeals = sortDeals(
|
|
113
|
+
pagination.pageIndex,
|
|
114
|
+
pagination.pageSize,
|
|
115
|
+
sorting,
|
|
116
|
+
search,
|
|
117
|
+
status
|
|
118
|
+
);
|
|
119
|
+
const totalItems = TEST_DEALS.filter((deal) => {
|
|
120
|
+
const matchesStatus = status === 'all' ? true : deal.status === status;
|
|
121
|
+
const matchesSearch =
|
|
122
|
+
!search ||
|
|
123
|
+
[deal.name, deal.companyId, deal.contactId, deal.notes, deal.ownerId]
|
|
124
|
+
.filter(Boolean)
|
|
125
|
+
.join(' ')
|
|
126
|
+
.toLowerCase()
|
|
127
|
+
.includes(search.toLowerCase());
|
|
128
|
+
return matchesStatus && matchesSearch;
|
|
129
|
+
}).length;
|
|
85
130
|
return (
|
|
86
131
|
<DealListDataTable
|
|
87
|
-
deals={
|
|
88
|
-
totalItems={
|
|
132
|
+
deals={filteredDeals}
|
|
133
|
+
totalItems={totalItems}
|
|
89
134
|
pageIndex={pagination.pageIndex}
|
|
90
135
|
pageSize={pagination.pageSize}
|
|
91
136
|
sorting={sorting}
|
|
92
|
-
|
|
137
|
+
search={search}
|
|
138
|
+
status={status}
|
|
139
|
+
onSortingChange={(nextSorting) => {
|
|
140
|
+
setSorting(nextSorting);
|
|
141
|
+
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
142
|
+
}}
|
|
93
143
|
onPaginationChange={setPagination}
|
|
144
|
+
onSearchChange={(value) => {
|
|
145
|
+
setSearch(value);
|
|
146
|
+
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
147
|
+
}}
|
|
148
|
+
onStatusChange={(value) => {
|
|
149
|
+
setStatus(value);
|
|
150
|
+
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
151
|
+
}}
|
|
94
152
|
/>
|
|
95
153
|
);
|
|
96
154
|
}
|
|
@@ -142,6 +200,13 @@ describe('DealListDataTable', () => {
|
|
|
142
200
|
'Affichage de 4 à 6 sur 6 résultats'
|
|
143
201
|
);
|
|
144
202
|
|
|
203
|
+
await click(
|
|
204
|
+
[...container.getElementsByTagName('button')].find(
|
|
205
|
+
(button) => button.textContent?.trim() === 'Won Only'
|
|
206
|
+
)
|
|
207
|
+
);
|
|
208
|
+
expect(container.textContent).toContain('Status: WON');
|
|
209
|
+
|
|
145
210
|
await act(async () => {
|
|
146
211
|
root.unmount();
|
|
147
212
|
});
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import {
|
|
4
4
|
Button,
|
|
5
5
|
DataTable,
|
|
6
|
+
DataTableToolbar,
|
|
6
7
|
LoaderBlock,
|
|
7
8
|
} from '@contractspec/lib.design-system';
|
|
8
9
|
import type { ContractTableSort } from '@contractspec/lib.presentation-runtime-core';
|
|
@@ -41,24 +42,73 @@ export interface DealListDataTableProps {
|
|
|
41
42
|
pageIndex: number;
|
|
42
43
|
pageSize: number;
|
|
43
44
|
sorting: ContractTableSort[];
|
|
45
|
+
search: string;
|
|
46
|
+
status: 'OPEN' | 'WON' | 'LOST' | 'all';
|
|
44
47
|
loading?: boolean;
|
|
45
48
|
onSortingChange: (sorting: ContractTableSort[]) => void;
|
|
46
49
|
onPaginationChange: (pagination: {
|
|
47
50
|
pageIndex: number;
|
|
48
51
|
pageSize: number;
|
|
49
52
|
}) => void;
|
|
53
|
+
onSearchChange: (value: string) => void;
|
|
54
|
+
onStatusChange: (value: 'OPEN' | 'WON' | 'LOST' | 'all') => void;
|
|
50
55
|
onDealClick?: (dealId: string) => void;
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
function buildStatusActions({
|
|
59
|
+
value,
|
|
60
|
+
onChange,
|
|
61
|
+
}: {
|
|
62
|
+
value: 'OPEN' | 'WON' | 'LOST' | 'all';
|
|
63
|
+
onChange: (value: 'OPEN' | 'WON' | 'LOST' | 'all') => void;
|
|
64
|
+
}) {
|
|
65
|
+
return (
|
|
66
|
+
<HStack gap="sm" className="flex-wrap">
|
|
67
|
+
<Button
|
|
68
|
+
variant={value === 'all' ? 'secondary' : 'outline'}
|
|
69
|
+
size="sm"
|
|
70
|
+
onPress={() => onChange('all')}
|
|
71
|
+
>
|
|
72
|
+
All Deals
|
|
73
|
+
</Button>
|
|
74
|
+
<Button
|
|
75
|
+
variant={value === 'OPEN' ? 'secondary' : 'outline'}
|
|
76
|
+
size="sm"
|
|
77
|
+
onPress={() => onChange('OPEN')}
|
|
78
|
+
>
|
|
79
|
+
Open Only
|
|
80
|
+
</Button>
|
|
81
|
+
<Button
|
|
82
|
+
variant={value === 'WON' ? 'secondary' : 'outline'}
|
|
83
|
+
size="sm"
|
|
84
|
+
onPress={() => onChange('WON')}
|
|
85
|
+
>
|
|
86
|
+
Won Only
|
|
87
|
+
</Button>
|
|
88
|
+
<Button
|
|
89
|
+
variant={value === 'LOST' ? 'secondary' : 'outline'}
|
|
90
|
+
size="sm"
|
|
91
|
+
onPress={() => onChange('LOST')}
|
|
92
|
+
>
|
|
93
|
+
Lost Only
|
|
94
|
+
</Button>
|
|
95
|
+
</HStack>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
53
99
|
export function DealListDataTable({
|
|
54
100
|
deals,
|
|
55
101
|
totalItems,
|
|
56
102
|
pageIndex,
|
|
57
103
|
pageSize,
|
|
58
104
|
sorting,
|
|
105
|
+
search,
|
|
106
|
+
status,
|
|
59
107
|
loading,
|
|
60
108
|
onSortingChange,
|
|
61
109
|
onPaginationChange,
|
|
110
|
+
onSearchChange,
|
|
111
|
+
onStatusChange,
|
|
62
112
|
onDealClick,
|
|
63
113
|
}: DealListDataTableProps) {
|
|
64
114
|
const controller = useContractTable<Deal>({
|
|
@@ -216,14 +266,36 @@ export function DealListDataTable({
|
|
|
216
266
|
description="Server-mode table using the shared ContractSpec controller."
|
|
217
267
|
loading={loading}
|
|
218
268
|
toolbar={
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
269
|
+
<DataTableToolbar
|
|
270
|
+
controller={controller}
|
|
271
|
+
searchPlaceholder="Search deals, companies, contacts, or notes"
|
|
272
|
+
searchValue={search}
|
|
273
|
+
onSearchChange={onSearchChange}
|
|
274
|
+
activeChips={
|
|
275
|
+
status === 'all'
|
|
276
|
+
? []
|
|
277
|
+
: [
|
|
278
|
+
{
|
|
279
|
+
key: 'status',
|
|
280
|
+
label: `Status: ${status}`,
|
|
281
|
+
onRemove: () => onStatusChange('all'),
|
|
282
|
+
},
|
|
283
|
+
]
|
|
284
|
+
}
|
|
285
|
+
onClearAll={() => {
|
|
286
|
+
onSearchChange('');
|
|
287
|
+
onStatusChange('all');
|
|
288
|
+
}}
|
|
289
|
+
actionsStart={buildStatusActions({
|
|
290
|
+
value: status,
|
|
291
|
+
onChange: onStatusChange,
|
|
292
|
+
})}
|
|
293
|
+
actionsEnd={
|
|
294
|
+
<Text className="text-muted-foreground text-sm">
|
|
295
|
+
{totalItems} total deals
|
|
296
|
+
</Text>
|
|
297
|
+
}
|
|
298
|
+
/>
|
|
227
299
|
}
|
|
228
300
|
footer={`Page ${controller.pageIndex + 1} of ${controller.pageCount}`}
|
|
229
301
|
emptyState={
|
|
@@ -247,9 +319,15 @@ export function DealListTab({
|
|
|
247
319
|
pageIndex: 0,
|
|
248
320
|
pageSize: 3,
|
|
249
321
|
});
|
|
322
|
+
const [search, setSearch] = React.useState('');
|
|
323
|
+
const [status, setStatus] = React.useState<'OPEN' | 'WON' | 'LOST' | 'all'>(
|
|
324
|
+
'all'
|
|
325
|
+
);
|
|
250
326
|
const { data, loading } = useDealList({
|
|
251
327
|
pageIndex: pagination.pageIndex,
|
|
252
328
|
pageSize: pagination.pageSize,
|
|
329
|
+
search,
|
|
330
|
+
status,
|
|
253
331
|
sorting,
|
|
254
332
|
});
|
|
255
333
|
|
|
@@ -264,12 +342,22 @@ export function DealListTab({
|
|
|
264
342
|
pageIndex={pagination.pageIndex}
|
|
265
343
|
pageSize={pagination.pageSize}
|
|
266
344
|
sorting={sorting}
|
|
345
|
+
search={search}
|
|
346
|
+
status={status}
|
|
267
347
|
loading={loading}
|
|
268
348
|
onSortingChange={(nextSorting) => {
|
|
269
349
|
setSorting(nextSorting);
|
|
270
350
|
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
271
351
|
}}
|
|
272
352
|
onPaginationChange={setPagination}
|
|
353
|
+
onSearchChange={(value) => {
|
|
354
|
+
setSearch(value);
|
|
355
|
+
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
356
|
+
}}
|
|
357
|
+
onStatusChange={(value) => {
|
|
358
|
+
setStatus(value);
|
|
359
|
+
setPagination((current) => ({ ...current, pageIndex: 0 }));
|
|
360
|
+
}}
|
|
273
361
|
onDealClick={onDealClick}
|
|
274
362
|
/>
|
|
275
363
|
);
|