@contractspec/example.crm-pipeline 3.7.7 → 3.7.12

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 (57) hide show
  1. package/.turbo/turbo-build.log +45 -42
  2. package/CHANGELOG.md +72 -0
  3. package/README.md +2 -1
  4. package/dist/browser/docs/crm-pipeline.docblock.js +1 -1
  5. package/dist/browser/docs/index.js +1 -1
  6. package/dist/browser/handlers/crm.handlers.js +13 -2
  7. package/dist/browser/handlers/index.js +13 -2
  8. package/dist/browser/index.js +392 -159
  9. package/dist/browser/ui/CrmDashboard.js +366 -144
  10. package/dist/browser/ui/hooks/index.js +19 -8
  11. package/dist/browser/ui/hooks/useDealList.js +19 -8
  12. package/dist/browser/ui/index.js +391 -158
  13. package/dist/browser/ui/renderers/index.js +32 -10
  14. package/dist/browser/ui/renderers/pipeline.markdown.js +13 -2
  15. package/dist/browser/ui/renderers/pipeline.renderer.js +19 -8
  16. package/dist/browser/ui/tables/DealListTab.js +390 -0
  17. package/dist/docs/crm-pipeline.docblock.js +1 -1
  18. package/dist/docs/index.js +1 -1
  19. package/dist/handlers/crm.handlers.d.ts +2 -0
  20. package/dist/handlers/crm.handlers.js +13 -2
  21. package/dist/handlers/index.js +13 -2
  22. package/dist/index.js +392 -159
  23. package/dist/node/docs/crm-pipeline.docblock.js +1 -1
  24. package/dist/node/docs/index.js +1 -1
  25. package/dist/node/handlers/crm.handlers.js +13 -2
  26. package/dist/node/handlers/index.js +13 -2
  27. package/dist/node/index.js +392 -159
  28. package/dist/node/ui/CrmDashboard.js +366 -144
  29. package/dist/node/ui/hooks/index.js +19 -8
  30. package/dist/node/ui/hooks/useDealList.js +19 -8
  31. package/dist/node/ui/index.js +391 -158
  32. package/dist/node/ui/renderers/index.js +32 -10
  33. package/dist/node/ui/renderers/pipeline.markdown.js +13 -2
  34. package/dist/node/ui/renderers/pipeline.renderer.js +19 -8
  35. package/dist/node/ui/tables/DealListTab.js +390 -0
  36. package/dist/ui/CrmDashboard.js +366 -144
  37. package/dist/ui/hooks/index.js +19 -8
  38. package/dist/ui/hooks/useDealList.d.ts +8 -2
  39. package/dist/ui/hooks/useDealList.js +19 -8
  40. package/dist/ui/index.js +391 -158
  41. package/dist/ui/renderers/index.js +32 -10
  42. package/dist/ui/renderers/pipeline.markdown.d.ts +1 -1
  43. package/dist/ui/renderers/pipeline.markdown.js +13 -2
  44. package/dist/ui/renderers/pipeline.renderer.d.ts +1 -1
  45. package/dist/ui/renderers/pipeline.renderer.js +19 -8
  46. package/dist/ui/tables/DealListTab.d.ts +20 -0
  47. package/dist/ui/tables/DealListTab.js +391 -0
  48. package/dist/ui/tables/DealListTab.smoke.test.d.ts +1 -0
  49. package/package.json +29 -13
  50. package/src/docs/crm-pipeline.docblock.ts +1 -1
  51. package/src/handlers/crm.handlers.ts +18 -1
  52. package/src/ui/CrmDashboard.tsx +2 -71
  53. package/src/ui/hooks/useDealList.ts +36 -8
  54. package/src/ui/renderers/pipeline.markdown.ts +1 -1
  55. package/src/ui/renderers/pipeline.renderer.tsx +1 -1
  56. package/src/ui/tables/DealListTab.smoke.test.tsx +149 -0
  57. package/src/ui/tables/DealListTab.tsx +276 -0
@@ -22,6 +22,13 @@ function rowToDeal(row) {
22
22
  updatedAt: new Date(row.updatedAt)
23
23
  };
24
24
  }
25
+ var DEAL_SORT_COLUMNS = {
26
+ name: "name",
27
+ value: "value",
28
+ status: "status",
29
+ expectedCloseDate: "expectedCloseDate",
30
+ updatedAt: "updatedAt"
31
+ };
25
32
  function createCrmHandlers(db) {
26
33
  async function listDeals(input) {
27
34
  const {
@@ -32,7 +39,9 @@ function createCrmHandlers(db) {
32
39
  ownerId,
33
40
  search,
34
41
  limit = 20,
35
- offset = 0
42
+ offset = 0,
43
+ sortBy = "value",
44
+ sortDirection = "desc"
36
45
  } = input;
37
46
  let whereClause = "WHERE projectId = ?";
38
47
  const params = [projectId];
@@ -60,7 +69,9 @@ function createCrmHandlers(db) {
60
69
  const total = countResult[0]?.count ?? 0;
61
70
  const valueResult = (await db.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${whereClause}`, params)).rows;
62
71
  const totalValue = valueResult[0]?.total ?? 0;
63
- const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY value DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
72
+ const orderByColumn = DEAL_SORT_COLUMNS[sortBy] ?? DEAL_SORT_COLUMNS.value;
73
+ const orderByDirection = sortDirection === "asc" ? "ASC" : "DESC";
74
+ const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY ${orderByColumn} ${orderByDirection} LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
64
75
  return {
65
76
  deals: dealRows.map(rowToDeal),
66
77
  total,
@@ -610,8 +621,13 @@ function useDealList(options = {}) {
610
621
  const [stages, setStages] = useState2([]);
611
622
  const [loading, setLoading] = useState2(true);
612
623
  const [error, setError] = useState2(null);
613
- const [page, setPage] = useState2(1);
624
+ const [internalPage, setInternalPage] = useState2(0);
614
625
  const pipelineId = options.pipelineId ?? "pipeline-1";
626
+ const pageIndex = options.pageIndex ?? internalPage;
627
+ const pageSize = options.pageSize ?? options.limit ?? 50;
628
+ const [sort] = options.sorting ?? [];
629
+ const sortBy = sort?.id;
630
+ const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
615
631
  const fetchData = useCallback(async () => {
616
632
  setLoading(true);
617
633
  setError(null);
@@ -623,8 +639,10 @@ function useDealList(options = {}) {
623
639
  stageId: options.stageId,
624
640
  status: options.status === "all" ? undefined : options.status,
625
641
  search: options.search,
626
- limit: options.limit ?? 50,
627
- offset: (page - 1) * (options.limit ?? 50)
642
+ limit: pageSize,
643
+ offset: pageIndex * pageSize,
644
+ sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
645
+ sortDirection
628
646
  }),
629
647
  crm2.getDealsByStage({ projectId, pipelineId }),
630
648
  crm2.getPipelineStages({ pipelineId })
@@ -644,8 +662,10 @@ function useDealList(options = {}) {
644
662
  options.stageId,
645
663
  options.status,
646
664
  options.search,
647
- options.limit,
648
- page
665
+ pageIndex,
666
+ pageSize,
667
+ sortBy,
668
+ sortDirection
649
669
  ]);
650
670
  useEffect(() => {
651
671
  fetchData();
@@ -673,10 +693,12 @@ function useDealList(options = {}) {
673
693
  loading,
674
694
  error,
675
695
  stats,
676
- page,
696
+ page: pageIndex + 1,
697
+ pageIndex,
698
+ pageSize,
677
699
  refetch: fetchData,
678
- nextPage: () => setPage((p) => p + 1),
679
- prevPage: () => page > 1 && setPage((p) => p - 1)
700
+ nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
701
+ prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
680
702
  };
681
703
  }
682
704
 
@@ -22,6 +22,13 @@ function rowToDeal(row) {
22
22
  updatedAt: new Date(row.updatedAt)
23
23
  };
24
24
  }
25
+ var DEAL_SORT_COLUMNS = {
26
+ name: "name",
27
+ value: "value",
28
+ status: "status",
29
+ expectedCloseDate: "expectedCloseDate",
30
+ updatedAt: "updatedAt"
31
+ };
25
32
  function createCrmHandlers(db) {
26
33
  async function listDeals(input) {
27
34
  const {
@@ -32,7 +39,9 @@ function createCrmHandlers(db) {
32
39
  ownerId,
33
40
  search,
34
41
  limit = 20,
35
- offset = 0
42
+ offset = 0,
43
+ sortBy = "value",
44
+ sortDirection = "desc"
36
45
  } = input;
37
46
  let whereClause = "WHERE projectId = ?";
38
47
  const params = [projectId];
@@ -60,7 +69,9 @@ function createCrmHandlers(db) {
60
69
  const total = countResult[0]?.count ?? 0;
61
70
  const valueResult = (await db.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${whereClause}`, params)).rows;
62
71
  const totalValue = valueResult[0]?.total ?? 0;
63
- const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY value DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
72
+ const orderByColumn = DEAL_SORT_COLUMNS[sortBy] ?? DEAL_SORT_COLUMNS.value;
73
+ const orderByDirection = sortDirection === "asc" ? "ASC" : "DESC";
74
+ const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY ${orderByColumn} ${orderByDirection} LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
64
75
  return {
65
76
  deals: dealRows.map(rowToDeal),
66
77
  total,
@@ -168,8 +168,13 @@ function useDealList(options = {}) {
168
168
  const [stages, setStages] = useState2([]);
169
169
  const [loading, setLoading] = useState2(true);
170
170
  const [error, setError] = useState2(null);
171
- const [page, setPage] = useState2(1);
171
+ const [internalPage, setInternalPage] = useState2(0);
172
172
  const pipelineId = options.pipelineId ?? "pipeline-1";
173
+ const pageIndex = options.pageIndex ?? internalPage;
174
+ const pageSize = options.pageSize ?? options.limit ?? 50;
175
+ const [sort] = options.sorting ?? [];
176
+ const sortBy = sort?.id;
177
+ const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
173
178
  const fetchData = useCallback(async () => {
174
179
  setLoading(true);
175
180
  setError(null);
@@ -181,8 +186,10 @@ function useDealList(options = {}) {
181
186
  stageId: options.stageId,
182
187
  status: options.status === "all" ? undefined : options.status,
183
188
  search: options.search,
184
- limit: options.limit ?? 50,
185
- offset: (page - 1) * (options.limit ?? 50)
189
+ limit: pageSize,
190
+ offset: pageIndex * pageSize,
191
+ sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
192
+ sortDirection
186
193
  }),
187
194
  crm.getDealsByStage({ projectId, pipelineId }),
188
195
  crm.getPipelineStages({ pipelineId })
@@ -202,8 +209,10 @@ function useDealList(options = {}) {
202
209
  options.stageId,
203
210
  options.status,
204
211
  options.search,
205
- options.limit,
206
- page
212
+ pageIndex,
213
+ pageSize,
214
+ sortBy,
215
+ sortDirection
207
216
  ]);
208
217
  useEffect(() => {
209
218
  fetchData();
@@ -231,10 +240,12 @@ function useDealList(options = {}) {
231
240
  loading,
232
241
  error,
233
242
  stats,
234
- page,
243
+ page: pageIndex + 1,
244
+ pageIndex,
245
+ pageSize,
235
246
  refetch: fetchData,
236
- nextPage: () => setPage((p) => p + 1),
237
- prevPage: () => page > 1 && setPage((p) => p - 1)
247
+ nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
248
+ prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
238
249
  };
239
250
  }
240
251
 
@@ -0,0 +1,390 @@
1
+ // src/ui/hooks/useDealList.ts
2
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
3
+ import { useCallback, useEffect, useMemo, useState } from "react";
4
+ "use client";
5
+ function useDealList(options = {}) {
6
+ const { handlers, projectId } = useTemplateRuntime();
7
+ const { crm } = handlers;
8
+ const [data, setData] = useState(null);
9
+ const [dealsByStage, setDealsByStage] = useState({});
10
+ const [stages, setStages] = useState([]);
11
+ const [loading, setLoading] = useState(true);
12
+ const [error, setError] = useState(null);
13
+ const [internalPage, setInternalPage] = useState(0);
14
+ const pipelineId = options.pipelineId ?? "pipeline-1";
15
+ const pageIndex = options.pageIndex ?? internalPage;
16
+ const pageSize = options.pageSize ?? options.limit ?? 50;
17
+ const [sort] = options.sorting ?? [];
18
+ const sortBy = sort?.id;
19
+ const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
20
+ const fetchData = useCallback(async () => {
21
+ setLoading(true);
22
+ setError(null);
23
+ try {
24
+ const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
25
+ crm.listDeals({
26
+ projectId,
27
+ pipelineId,
28
+ stageId: options.stageId,
29
+ status: options.status === "all" ? undefined : options.status,
30
+ search: options.search,
31
+ limit: pageSize,
32
+ offset: pageIndex * pageSize,
33
+ sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
34
+ sortDirection
35
+ }),
36
+ crm.getDealsByStage({ projectId, pipelineId }),
37
+ crm.getPipelineStages({ pipelineId })
38
+ ]);
39
+ setData(dealsResult);
40
+ setDealsByStage(stageDealsResult);
41
+ setStages(stagesResult);
42
+ } catch (err) {
43
+ setError(err instanceof Error ? err : new Error("Unknown error"));
44
+ } finally {
45
+ setLoading(false);
46
+ }
47
+ }, [
48
+ crm,
49
+ projectId,
50
+ pipelineId,
51
+ options.stageId,
52
+ options.status,
53
+ options.search,
54
+ pageIndex,
55
+ pageSize,
56
+ sortBy,
57
+ sortDirection
58
+ ]);
59
+ useEffect(() => {
60
+ fetchData();
61
+ }, [fetchData]);
62
+ const stats = useMemo(() => {
63
+ if (!data)
64
+ return null;
65
+ const open = data.deals.filter((d) => d.status === "OPEN");
66
+ const won = data.deals.filter((d) => d.status === "WON");
67
+ const lost = data.deals.filter((d) => d.status === "LOST");
68
+ return {
69
+ total: data.total,
70
+ totalValue: data.totalValue,
71
+ openCount: open.length,
72
+ openValue: open.reduce((sum, d) => sum + d.value, 0),
73
+ wonCount: won.length,
74
+ wonValue: won.reduce((sum, d) => sum + d.value, 0),
75
+ lostCount: lost.length
76
+ };
77
+ }, [data]);
78
+ return {
79
+ data,
80
+ dealsByStage,
81
+ stages,
82
+ loading,
83
+ error,
84
+ stats,
85
+ page: pageIndex + 1,
86
+ pageIndex,
87
+ pageSize,
88
+ refetch: fetchData,
89
+ nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
90
+ prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
91
+ };
92
+ }
93
+
94
+ // src/ui/tables/DealListTab.tsx
95
+ import {
96
+ Button,
97
+ DataTable,
98
+ LoaderBlock
99
+ } from "@contractspec/lib.design-system";
100
+ import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
101
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
102
+ import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
103
+ import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
104
+ import * as React from "react";
105
+ import { jsxDEV } from "react/jsx-dev-runtime";
106
+ "use client";
107
+ function formatCurrency(value, currency = "USD") {
108
+ return new Intl.NumberFormat("en-US", {
109
+ style: "currency",
110
+ currency,
111
+ minimumFractionDigits: 0,
112
+ maximumFractionDigits: 0
113
+ }).format(value);
114
+ }
115
+ function statusVariant(status) {
116
+ switch (status) {
117
+ case "WON":
118
+ return "default";
119
+ case "LOST":
120
+ return "destructive";
121
+ case "STALE":
122
+ return "outline";
123
+ default:
124
+ return "secondary";
125
+ }
126
+ }
127
+ function DealListDataTable({
128
+ deals,
129
+ totalItems,
130
+ pageIndex,
131
+ pageSize,
132
+ sorting,
133
+ loading,
134
+ onSortingChange,
135
+ onPaginationChange,
136
+ onDealClick
137
+ }) {
138
+ const controller = useContractTable({
139
+ data: deals,
140
+ columns: [
141
+ {
142
+ id: "deal",
143
+ header: "Deal",
144
+ label: "Deal",
145
+ accessor: (deal) => deal.name,
146
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV(VStack, {
147
+ gap: "xs",
148
+ children: [
149
+ /* @__PURE__ */ jsxDEV(Text, {
150
+ className: "font-medium text-sm",
151
+ children: item.name
152
+ }, undefined, false, undefined, this),
153
+ /* @__PURE__ */ jsxDEV(Text, {
154
+ className: "text-muted-foreground text-xs",
155
+ children: item.companyId ?? "Unassigned company"
156
+ }, undefined, false, undefined, this)
157
+ ]
158
+ }, undefined, true, undefined, this),
159
+ size: 240,
160
+ minSize: 180,
161
+ canSort: true,
162
+ canPin: true,
163
+ canResize: true
164
+ },
165
+ {
166
+ id: "value",
167
+ header: "Value",
168
+ label: "Value",
169
+ accessorKey: "value",
170
+ cell: ({ item }) => formatCurrency(item.value, item.currency),
171
+ align: "right",
172
+ size: 140,
173
+ canSort: true,
174
+ canResize: true
175
+ },
176
+ {
177
+ id: "status",
178
+ header: "Status",
179
+ label: "Status",
180
+ accessorKey: "status",
181
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV(Badge, {
182
+ variant: statusVariant(value),
183
+ children: String(value)
184
+ }, undefined, false, undefined, this),
185
+ size: 130,
186
+ canSort: true,
187
+ canHide: true,
188
+ canPin: true,
189
+ canResize: true
190
+ },
191
+ {
192
+ id: "expectedCloseDate",
193
+ header: "Expected Close",
194
+ label: "Expected Close",
195
+ accessor: (deal) => deal.expectedCloseDate?.toISOString() ?? "",
196
+ cell: ({ item }) => item.expectedCloseDate?.toLocaleDateString() ?? "Not scheduled",
197
+ size: 170,
198
+ canSort: true,
199
+ canHide: true,
200
+ canResize: true
201
+ },
202
+ {
203
+ id: "updatedAt",
204
+ header: "Updated",
205
+ label: "Updated",
206
+ accessor: (deal) => deal.updatedAt.toISOString(),
207
+ cell: ({ item }) => item.updatedAt.toLocaleDateString(),
208
+ size: 140,
209
+ canSort: true,
210
+ canHide: true,
211
+ canResize: true
212
+ },
213
+ {
214
+ id: "actions",
215
+ header: "Actions",
216
+ label: "Actions",
217
+ accessor: (deal) => deal.id,
218
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV(Button, {
219
+ variant: "ghost",
220
+ size: "sm",
221
+ onPress: () => onDealClick?.(item.id),
222
+ children: "Actions"
223
+ }, undefined, false, undefined, this),
224
+ size: 120,
225
+ canSort: false,
226
+ canHide: false,
227
+ canPin: false,
228
+ canResize: false
229
+ }
230
+ ],
231
+ executionMode: "server",
232
+ selectionMode: "multiple",
233
+ totalItems,
234
+ state: {
235
+ sorting,
236
+ pagination: {
237
+ pageIndex,
238
+ pageSize
239
+ }
240
+ },
241
+ onSortingChange,
242
+ onPaginationChange,
243
+ initialState: {
244
+ columnVisibility: { updatedAt: false },
245
+ columnPinning: { left: ["deal", "status"], right: [] }
246
+ },
247
+ renderExpandedContent: (deal) => /* @__PURE__ */ jsxDEV(VStack, {
248
+ gap: "sm",
249
+ className: "py-2",
250
+ children: [
251
+ /* @__PURE__ */ jsxDEV(HStack, {
252
+ justify: "between",
253
+ children: [
254
+ /* @__PURE__ */ jsxDEV(Text, {
255
+ className: "font-medium text-sm",
256
+ children: "Owner"
257
+ }, undefined, false, undefined, this),
258
+ /* @__PURE__ */ jsxDEV(Text, {
259
+ className: "text-muted-foreground text-sm",
260
+ children: deal.ownerId
261
+ }, undefined, false, undefined, this)
262
+ ]
263
+ }, undefined, true, undefined, this),
264
+ /* @__PURE__ */ jsxDEV(HStack, {
265
+ justify: "between",
266
+ children: [
267
+ /* @__PURE__ */ jsxDEV(Text, {
268
+ className: "font-medium text-sm",
269
+ children: "Contact"
270
+ }, undefined, false, undefined, this),
271
+ /* @__PURE__ */ jsxDEV(Text, {
272
+ className: "text-muted-foreground text-sm",
273
+ children: deal.contactId ?? "No linked contact"
274
+ }, undefined, false, undefined, this)
275
+ ]
276
+ }, undefined, true, undefined, this),
277
+ deal.wonSource ? /* @__PURE__ */ jsxDEV(HStack, {
278
+ justify: "between",
279
+ children: [
280
+ /* @__PURE__ */ jsxDEV(Text, {
281
+ className: "font-medium text-sm",
282
+ children: "Won Source"
283
+ }, undefined, false, undefined, this),
284
+ /* @__PURE__ */ jsxDEV(Text, {
285
+ className: "text-muted-foreground text-sm",
286
+ children: deal.wonSource
287
+ }, undefined, false, undefined, this)
288
+ ]
289
+ }, undefined, true, undefined, this) : null,
290
+ deal.lostReason ? /* @__PURE__ */ jsxDEV(HStack, {
291
+ justify: "between",
292
+ children: [
293
+ /* @__PURE__ */ jsxDEV(Text, {
294
+ className: "font-medium text-sm",
295
+ children: "Lost Reason"
296
+ }, undefined, false, undefined, this),
297
+ /* @__PURE__ */ jsxDEV(Text, {
298
+ className: "text-muted-foreground text-sm",
299
+ children: deal.lostReason
300
+ }, undefined, false, undefined, this)
301
+ ]
302
+ }, undefined, true, undefined, this) : null,
303
+ deal.notes ? /* @__PURE__ */ jsxDEV(VStack, {
304
+ gap: "xs",
305
+ children: [
306
+ /* @__PURE__ */ jsxDEV(Text, {
307
+ className: "font-medium text-sm",
308
+ children: "Notes"
309
+ }, undefined, false, undefined, this),
310
+ /* @__PURE__ */ jsxDEV(Text, {
311
+ className: "text-muted-foreground text-sm",
312
+ children: deal.notes
313
+ }, undefined, false, undefined, this)
314
+ ]
315
+ }, undefined, true, undefined, this) : null
316
+ ]
317
+ }, undefined, true, undefined, this),
318
+ getCanExpand: () => true
319
+ });
320
+ return /* @__PURE__ */ jsxDEV(DataTable, {
321
+ controller,
322
+ title: "All Deals",
323
+ description: "Server-mode table using the shared ContractSpec controller.",
324
+ loading,
325
+ toolbar: /* @__PURE__ */ jsxDEV(HStack, {
326
+ gap: "sm",
327
+ className: "flex-wrap",
328
+ children: [
329
+ /* @__PURE__ */ jsxDEV(Text, {
330
+ className: "text-muted-foreground text-sm",
331
+ children: [
332
+ "Selected ",
333
+ controller.selectedRowIds.length
334
+ ]
335
+ }, undefined, true, undefined, this),
336
+ /* @__PURE__ */ jsxDEV(Text, {
337
+ className: "text-muted-foreground text-sm",
338
+ children: [
339
+ totalItems,
340
+ " total deals"
341
+ ]
342
+ }, undefined, true, undefined, this)
343
+ ]
344
+ }, undefined, true, undefined, this),
345
+ footer: `Page ${controller.pageIndex + 1} of ${controller.pageCount}`,
346
+ emptyState: /* @__PURE__ */ jsxDEV("div", {
347
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
348
+ children: "No deals found"
349
+ }, undefined, false, undefined, this)
350
+ }, undefined, false, undefined, this);
351
+ }
352
+ function DealListTab({
353
+ onDealClick
354
+ }) {
355
+ const [sorting, setSorting] = React.useState([
356
+ { id: "value", desc: true }
357
+ ]);
358
+ const [pagination, setPagination] = React.useState({
359
+ pageIndex: 0,
360
+ pageSize: 3
361
+ });
362
+ const { data, loading } = useDealList({
363
+ pageIndex: pagination.pageIndex,
364
+ pageSize: pagination.pageSize,
365
+ sorting
366
+ });
367
+ if (loading && !data) {
368
+ return /* @__PURE__ */ jsxDEV(LoaderBlock, {
369
+ label: "Loading deals..."
370
+ }, undefined, false, undefined, this);
371
+ }
372
+ return /* @__PURE__ */ jsxDEV(DealListDataTable, {
373
+ deals: data?.deals ?? [],
374
+ totalItems: data?.total ?? 0,
375
+ pageIndex: pagination.pageIndex,
376
+ pageSize: pagination.pageSize,
377
+ sorting,
378
+ loading,
379
+ onSortingChange: (nextSorting) => {
380
+ setSorting(nextSorting);
381
+ setPagination((current) => ({ ...current, pageIndex: 0 }));
382
+ },
383
+ onPaginationChange: setPagination,
384
+ onDealClick
385
+ }, undefined, false, undefined, this);
386
+ }
387
+ export {
388
+ DealListTab,
389
+ DealListDataTable
390
+ };