@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,391 @@
1
+ // @bun
2
+ // src/ui/hooks/useDealList.ts
3
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
4
+ import { useCallback, useEffect, useMemo, useState } from "react";
5
+ "use client";
6
+ function useDealList(options = {}) {
7
+ const { handlers, projectId } = useTemplateRuntime();
8
+ const { crm } = handlers;
9
+ const [data, setData] = useState(null);
10
+ const [dealsByStage, setDealsByStage] = useState({});
11
+ const [stages, setStages] = useState([]);
12
+ const [loading, setLoading] = useState(true);
13
+ const [error, setError] = useState(null);
14
+ const [internalPage, setInternalPage] = useState(0);
15
+ const pipelineId = options.pipelineId ?? "pipeline-1";
16
+ const pageIndex = options.pageIndex ?? internalPage;
17
+ const pageSize = options.pageSize ?? options.limit ?? 50;
18
+ const [sort] = options.sorting ?? [];
19
+ const sortBy = sort?.id;
20
+ const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
21
+ const fetchData = useCallback(async () => {
22
+ setLoading(true);
23
+ setError(null);
24
+ try {
25
+ const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
26
+ crm.listDeals({
27
+ projectId,
28
+ pipelineId,
29
+ stageId: options.stageId,
30
+ status: options.status === "all" ? undefined : options.status,
31
+ search: options.search,
32
+ limit: pageSize,
33
+ offset: pageIndex * pageSize,
34
+ sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
35
+ sortDirection
36
+ }),
37
+ crm.getDealsByStage({ projectId, pipelineId }),
38
+ crm.getPipelineStages({ pipelineId })
39
+ ]);
40
+ setData(dealsResult);
41
+ setDealsByStage(stageDealsResult);
42
+ setStages(stagesResult);
43
+ } catch (err) {
44
+ setError(err instanceof Error ? err : new Error("Unknown error"));
45
+ } finally {
46
+ setLoading(false);
47
+ }
48
+ }, [
49
+ crm,
50
+ projectId,
51
+ pipelineId,
52
+ options.stageId,
53
+ options.status,
54
+ options.search,
55
+ pageIndex,
56
+ pageSize,
57
+ sortBy,
58
+ sortDirection
59
+ ]);
60
+ useEffect(() => {
61
+ fetchData();
62
+ }, [fetchData]);
63
+ const stats = useMemo(() => {
64
+ if (!data)
65
+ return null;
66
+ const open = data.deals.filter((d) => d.status === "OPEN");
67
+ const won = data.deals.filter((d) => d.status === "WON");
68
+ const lost = data.deals.filter((d) => d.status === "LOST");
69
+ return {
70
+ total: data.total,
71
+ totalValue: data.totalValue,
72
+ openCount: open.length,
73
+ openValue: open.reduce((sum, d) => sum + d.value, 0),
74
+ wonCount: won.length,
75
+ wonValue: won.reduce((sum, d) => sum + d.value, 0),
76
+ lostCount: lost.length
77
+ };
78
+ }, [data]);
79
+ return {
80
+ data,
81
+ dealsByStage,
82
+ stages,
83
+ loading,
84
+ error,
85
+ stats,
86
+ page: pageIndex + 1,
87
+ pageIndex,
88
+ pageSize,
89
+ refetch: fetchData,
90
+ nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
91
+ prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
92
+ };
93
+ }
94
+
95
+ // src/ui/tables/DealListTab.tsx
96
+ import {
97
+ Button,
98
+ DataTable,
99
+ LoaderBlock
100
+ } from "@contractspec/lib.design-system";
101
+ import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
102
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
103
+ import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
104
+ import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
105
+ import * as React from "react";
106
+ import { jsxDEV } from "react/jsx-dev-runtime";
107
+ "use client";
108
+ function formatCurrency(value, currency = "USD") {
109
+ return new Intl.NumberFormat("en-US", {
110
+ style: "currency",
111
+ currency,
112
+ minimumFractionDigits: 0,
113
+ maximumFractionDigits: 0
114
+ }).format(value);
115
+ }
116
+ function statusVariant(status) {
117
+ switch (status) {
118
+ case "WON":
119
+ return "default";
120
+ case "LOST":
121
+ return "destructive";
122
+ case "STALE":
123
+ return "outline";
124
+ default:
125
+ return "secondary";
126
+ }
127
+ }
128
+ function DealListDataTable({
129
+ deals,
130
+ totalItems,
131
+ pageIndex,
132
+ pageSize,
133
+ sorting,
134
+ loading,
135
+ onSortingChange,
136
+ onPaginationChange,
137
+ onDealClick
138
+ }) {
139
+ const controller = useContractTable({
140
+ data: deals,
141
+ columns: [
142
+ {
143
+ id: "deal",
144
+ header: "Deal",
145
+ label: "Deal",
146
+ accessor: (deal) => deal.name,
147
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV(VStack, {
148
+ gap: "xs",
149
+ children: [
150
+ /* @__PURE__ */ jsxDEV(Text, {
151
+ className: "font-medium text-sm",
152
+ children: item.name
153
+ }, undefined, false, undefined, this),
154
+ /* @__PURE__ */ jsxDEV(Text, {
155
+ className: "text-muted-foreground text-xs",
156
+ children: item.companyId ?? "Unassigned company"
157
+ }, undefined, false, undefined, this)
158
+ ]
159
+ }, undefined, true, undefined, this),
160
+ size: 240,
161
+ minSize: 180,
162
+ canSort: true,
163
+ canPin: true,
164
+ canResize: true
165
+ },
166
+ {
167
+ id: "value",
168
+ header: "Value",
169
+ label: "Value",
170
+ accessorKey: "value",
171
+ cell: ({ item }) => formatCurrency(item.value, item.currency),
172
+ align: "right",
173
+ size: 140,
174
+ canSort: true,
175
+ canResize: true
176
+ },
177
+ {
178
+ id: "status",
179
+ header: "Status",
180
+ label: "Status",
181
+ accessorKey: "status",
182
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV(Badge, {
183
+ variant: statusVariant(value),
184
+ children: String(value)
185
+ }, undefined, false, undefined, this),
186
+ size: 130,
187
+ canSort: true,
188
+ canHide: true,
189
+ canPin: true,
190
+ canResize: true
191
+ },
192
+ {
193
+ id: "expectedCloseDate",
194
+ header: "Expected Close",
195
+ label: "Expected Close",
196
+ accessor: (deal) => deal.expectedCloseDate?.toISOString() ?? "",
197
+ cell: ({ item }) => item.expectedCloseDate?.toLocaleDateString() ?? "Not scheduled",
198
+ size: 170,
199
+ canSort: true,
200
+ canHide: true,
201
+ canResize: true
202
+ },
203
+ {
204
+ id: "updatedAt",
205
+ header: "Updated",
206
+ label: "Updated",
207
+ accessor: (deal) => deal.updatedAt.toISOString(),
208
+ cell: ({ item }) => item.updatedAt.toLocaleDateString(),
209
+ size: 140,
210
+ canSort: true,
211
+ canHide: true,
212
+ canResize: true
213
+ },
214
+ {
215
+ id: "actions",
216
+ header: "Actions",
217
+ label: "Actions",
218
+ accessor: (deal) => deal.id,
219
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV(Button, {
220
+ variant: "ghost",
221
+ size: "sm",
222
+ onPress: () => onDealClick?.(item.id),
223
+ children: "Actions"
224
+ }, undefined, false, undefined, this),
225
+ size: 120,
226
+ canSort: false,
227
+ canHide: false,
228
+ canPin: false,
229
+ canResize: false
230
+ }
231
+ ],
232
+ executionMode: "server",
233
+ selectionMode: "multiple",
234
+ totalItems,
235
+ state: {
236
+ sorting,
237
+ pagination: {
238
+ pageIndex,
239
+ pageSize
240
+ }
241
+ },
242
+ onSortingChange,
243
+ onPaginationChange,
244
+ initialState: {
245
+ columnVisibility: { updatedAt: false },
246
+ columnPinning: { left: ["deal", "status"], right: [] }
247
+ },
248
+ renderExpandedContent: (deal) => /* @__PURE__ */ jsxDEV(VStack, {
249
+ gap: "sm",
250
+ className: "py-2",
251
+ children: [
252
+ /* @__PURE__ */ jsxDEV(HStack, {
253
+ justify: "between",
254
+ children: [
255
+ /* @__PURE__ */ jsxDEV(Text, {
256
+ className: "font-medium text-sm",
257
+ children: "Owner"
258
+ }, undefined, false, undefined, this),
259
+ /* @__PURE__ */ jsxDEV(Text, {
260
+ className: "text-muted-foreground text-sm",
261
+ children: deal.ownerId
262
+ }, undefined, false, undefined, this)
263
+ ]
264
+ }, undefined, true, undefined, this),
265
+ /* @__PURE__ */ jsxDEV(HStack, {
266
+ justify: "between",
267
+ children: [
268
+ /* @__PURE__ */ jsxDEV(Text, {
269
+ className: "font-medium text-sm",
270
+ children: "Contact"
271
+ }, undefined, false, undefined, this),
272
+ /* @__PURE__ */ jsxDEV(Text, {
273
+ className: "text-muted-foreground text-sm",
274
+ children: deal.contactId ?? "No linked contact"
275
+ }, undefined, false, undefined, this)
276
+ ]
277
+ }, undefined, true, undefined, this),
278
+ deal.wonSource ? /* @__PURE__ */ jsxDEV(HStack, {
279
+ justify: "between",
280
+ children: [
281
+ /* @__PURE__ */ jsxDEV(Text, {
282
+ className: "font-medium text-sm",
283
+ children: "Won Source"
284
+ }, undefined, false, undefined, this),
285
+ /* @__PURE__ */ jsxDEV(Text, {
286
+ className: "text-muted-foreground text-sm",
287
+ children: deal.wonSource
288
+ }, undefined, false, undefined, this)
289
+ ]
290
+ }, undefined, true, undefined, this) : null,
291
+ deal.lostReason ? /* @__PURE__ */ jsxDEV(HStack, {
292
+ justify: "between",
293
+ children: [
294
+ /* @__PURE__ */ jsxDEV(Text, {
295
+ className: "font-medium text-sm",
296
+ children: "Lost Reason"
297
+ }, undefined, false, undefined, this),
298
+ /* @__PURE__ */ jsxDEV(Text, {
299
+ className: "text-muted-foreground text-sm",
300
+ children: deal.lostReason
301
+ }, undefined, false, undefined, this)
302
+ ]
303
+ }, undefined, true, undefined, this) : null,
304
+ deal.notes ? /* @__PURE__ */ jsxDEV(VStack, {
305
+ gap: "xs",
306
+ children: [
307
+ /* @__PURE__ */ jsxDEV(Text, {
308
+ className: "font-medium text-sm",
309
+ children: "Notes"
310
+ }, undefined, false, undefined, this),
311
+ /* @__PURE__ */ jsxDEV(Text, {
312
+ className: "text-muted-foreground text-sm",
313
+ children: deal.notes
314
+ }, undefined, false, undefined, this)
315
+ ]
316
+ }, undefined, true, undefined, this) : null
317
+ ]
318
+ }, undefined, true, undefined, this),
319
+ getCanExpand: () => true
320
+ });
321
+ return /* @__PURE__ */ jsxDEV(DataTable, {
322
+ controller,
323
+ title: "All Deals",
324
+ description: "Server-mode table using the shared ContractSpec controller.",
325
+ loading,
326
+ toolbar: /* @__PURE__ */ jsxDEV(HStack, {
327
+ gap: "sm",
328
+ className: "flex-wrap",
329
+ children: [
330
+ /* @__PURE__ */ jsxDEV(Text, {
331
+ className: "text-muted-foreground text-sm",
332
+ children: [
333
+ "Selected ",
334
+ controller.selectedRowIds.length
335
+ ]
336
+ }, undefined, true, undefined, this),
337
+ /* @__PURE__ */ jsxDEV(Text, {
338
+ className: "text-muted-foreground text-sm",
339
+ children: [
340
+ totalItems,
341
+ " total deals"
342
+ ]
343
+ }, undefined, true, undefined, this)
344
+ ]
345
+ }, undefined, true, undefined, this),
346
+ footer: `Page ${controller.pageIndex + 1} of ${controller.pageCount}`,
347
+ emptyState: /* @__PURE__ */ jsxDEV("div", {
348
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
349
+ children: "No deals found"
350
+ }, undefined, false, undefined, this)
351
+ }, undefined, false, undefined, this);
352
+ }
353
+ function DealListTab({
354
+ onDealClick
355
+ }) {
356
+ const [sorting, setSorting] = React.useState([
357
+ { id: "value", desc: true }
358
+ ]);
359
+ const [pagination, setPagination] = React.useState({
360
+ pageIndex: 0,
361
+ pageSize: 3
362
+ });
363
+ const { data, loading } = useDealList({
364
+ pageIndex: pagination.pageIndex,
365
+ pageSize: pagination.pageSize,
366
+ sorting
367
+ });
368
+ if (loading && !data) {
369
+ return /* @__PURE__ */ jsxDEV(LoaderBlock, {
370
+ label: "Loading deals..."
371
+ }, undefined, false, undefined, this);
372
+ }
373
+ return /* @__PURE__ */ jsxDEV(DealListDataTable, {
374
+ deals: data?.deals ?? [],
375
+ totalItems: data?.total ?? 0,
376
+ pageIndex: pagination.pageIndex,
377
+ pageSize: pagination.pageSize,
378
+ sorting,
379
+ loading,
380
+ onSortingChange: (nextSorting) => {
381
+ setSorting(nextSorting);
382
+ setPagination((current) => ({ ...current, pageIndex: 0 }));
383
+ },
384
+ onPaginationChange: setPagination,
385
+ onDealClick
386
+ }, undefined, false, undefined, this);
387
+ }
388
+ export {
389
+ DealListTab,
390
+ DealListDataTable
391
+ };
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contractspec/example.crm-pipeline",
3
- "version": "3.7.6",
3
+ "version": "3.7.10",
4
4
  "description": "CRM Pipeline - Contacts, Companies, Deals, Tasks",
5
5
  "type": "module",
6
6
  "types": "./dist/index.d.ts",
@@ -312,6 +312,13 @@
312
312
  "bun": "./dist/ui/renderers/pipeline.renderer.js",
313
313
  "node": "./dist/node/ui/renderers/pipeline.renderer.js",
314
314
  "default": "./dist/ui/renderers/pipeline.renderer.js"
315
+ },
316
+ "./ui/tables/DealListTab": {
317
+ "types": "./dist/ui/tables/DealListTab.d.ts",
318
+ "browser": "./dist/browser/ui/tables/DealListTab.js",
319
+ "bun": "./dist/ui/tables/DealListTab.js",
320
+ "node": "./dist/node/ui/tables/DealListTab.js",
321
+ "default": "./dist/ui/tables/DealListTab.js"
315
322
  }
316
323
  },
317
324
  "scripts": {
@@ -323,32 +330,33 @@
323
330
  "dev": "contractspec-bun-build dev",
324
331
  "clean": "rimraf dist .turbo",
325
332
  "lint": "bun lint:fix",
326
- "lint:fix": "eslint src --fix",
327
- "lint:check": "eslint src",
333
+ "lint:fix": "biome check --write --unsafe --only=nursery/useSortedClasses . && biome check --write .",
334
+ "lint:check": "biome check .",
328
335
  "test": "bun test --pass-with-no-tests",
329
336
  "validate": "contractspec validate \"src/**/*\"",
330
337
  "prebuild": "contractspec-bun-build prebuild",
331
338
  "typecheck": "tsc --noEmit"
332
339
  },
333
340
  "dependencies": {
334
- "@contractspec/lib.contracts-spec": "3.7.6",
335
- "@contractspec/lib.design-system": "3.7.6",
336
- "@contractspec/lib.example-shared-ui": "6.0.6",
337
- "@contractspec/lib.identity-rbac": "3.7.6",
338
- "@contractspec/lib.runtime-sandbox": "2.7.6",
339
- "@contractspec/lib.schema": "3.7.6",
340
- "@contractspec/lib.ui-kit-web": "3.7.6",
341
- "@contractspec/module.audit-trail": "3.7.6",
342
- "@contractspec/module.notifications": "3.7.6",
341
+ "@contractspec/lib.contracts-spec": "4.1.2",
342
+ "@contractspec/lib.design-system": "3.8.3",
343
+ "@contractspec/lib.example-shared-ui": "6.0.10",
344
+ "@contractspec/lib.identity-rbac": "3.7.10",
345
+ "@contractspec/lib.runtime-sandbox": "2.7.9",
346
+ "@contractspec/lib.schema": "3.7.8",
347
+ "@contractspec/lib.ui-kit-web": "3.9.2",
348
+ "@contractspec/module.audit-trail": "3.7.10",
349
+ "@contractspec/module.notifications": "3.7.10",
343
350
  "react": "19.2.0",
344
351
  "react-dom": "19.2.0"
345
352
  },
346
353
  "devDependencies": {
347
- "@contractspec/tool.typescript": "3.7.6",
354
+ "@contractspec/tool.typescript": "3.7.8",
348
355
  "typescript": "^5.9.3",
349
356
  "@types/react": "^19.2.14",
350
357
  "@types/react-dom": "^19.2.2",
351
- "@contractspec/tool.bun": "3.7.6"
358
+ "@contractspec/tool.bun": "3.7.8",
359
+ "happy-dom": "^20.8.4"
352
360
  },
353
361
  "publishConfig": {
354
362
  "exports": {
@@ -659,6 +667,13 @@
659
667
  "bun": "./dist/ui/renderers/pipeline.renderer.js",
660
668
  "node": "./dist/node/ui/renderers/pipeline.renderer.js",
661
669
  "default": "./dist/ui/renderers/pipeline.renderer.js"
670
+ },
671
+ "./ui/tables/DealListTab": {
672
+ "types": "./dist/ui/tables/DealListTab.d.ts",
673
+ "browser": "./dist/browser/ui/tables/DealListTab.js",
674
+ "bun": "./dist/ui/tables/DealListTab.js",
675
+ "node": "./dist/node/ui/tables/DealListTab.js",
676
+ "default": "./dist/ui/tables/DealListTab.js"
662
677
  }
663
678
  },
664
679
  "registry": "https://registry.npmjs.org/",
@@ -10,100 +10,100 @@ import { defineFeature } from '@contractspec/lib.contracts-spec';
10
10
  * pipeline operations, and contact management into an installable feature.
11
11
  */
12
12
  export const CrmPipelineFeature = defineFeature({
13
- meta: {
14
- key: 'crm-pipeline',
15
- title: 'CRM Pipeline',
16
- description:
17
- 'CRM and sales pipeline management with deals, contacts, and companies',
18
- domain: 'crm',
19
- owners: ['@crm-team'],
20
- tags: ['crm', 'sales', 'pipeline', 'deals'],
21
- stability: 'experimental',
22
- version: '1.0.0',
23
- },
13
+ meta: {
14
+ key: 'crm-pipeline',
15
+ title: 'CRM Pipeline',
16
+ description:
17
+ 'CRM and sales pipeline management with deals, contacts, and companies',
18
+ domain: 'crm',
19
+ owners: ['@crm-team'],
20
+ tags: ['crm', 'sales', 'pipeline', 'deals'],
21
+ stability: 'experimental',
22
+ version: '1.0.0',
23
+ },
24
24
 
25
- // All contract operations included in this feature
26
- operations: [
27
- // Deal operations
28
- { key: 'crm.deal.create', version: '1.0.0' },
29
- { key: 'crm.deal.move', version: '1.0.0' },
30
- { key: 'crm.deal.win', version: '1.0.0' },
31
- { key: 'crm.deal.lose', version: '1.0.0' },
32
- { key: 'crm.deal.list', version: '1.0.0' },
33
- ],
25
+ // All contract operations included in this feature
26
+ operations: [
27
+ // Deal operations
28
+ { key: 'crm.deal.create', version: '1.0.0' },
29
+ { key: 'crm.deal.move', version: '1.0.0' },
30
+ { key: 'crm.deal.win', version: '1.0.0' },
31
+ { key: 'crm.deal.lose', version: '1.0.0' },
32
+ { key: 'crm.deal.list', version: '1.0.0' },
33
+ ],
34
34
 
35
- // Events emitted by this feature
36
- events: [
37
- // Deal events
38
- { key: 'deal.created', version: '1.0.0' },
39
- { key: 'deal.moved', version: '1.0.0' },
40
- { key: 'deal.won', version: '1.0.0' },
41
- { key: 'deal.lost', version: '1.0.0' },
35
+ // Events emitted by this feature
36
+ events: [
37
+ // Deal events
38
+ { key: 'deal.created', version: '1.0.0' },
39
+ { key: 'deal.moved', version: '1.0.0' },
40
+ { key: 'deal.won', version: '1.0.0' },
41
+ { key: 'deal.lost', version: '1.0.0' },
42
42
 
43
- // Contact events
44
- { key: 'contact.created', version: '1.0.0' },
43
+ // Contact events
44
+ { key: 'contact.created', version: '1.0.0' },
45
45
 
46
- // Task events
47
- { key: 'task.completed', version: '1.0.0' },
48
- ],
46
+ // Task events
47
+ { key: 'task.completed', version: '1.0.0' },
48
+ ],
49
49
 
50
- // Presentations associated with this feature
51
- presentations: [
52
- { key: 'crm.dashboard', version: '1.0.0' },
53
- { key: 'crm.pipeline.kanban', version: '1.0.0' },
54
- { key: 'crm.deal.viewList', version: '1.0.0' },
55
- { key: 'crm.deal.detail', version: '1.0.0' },
56
- { key: 'crm.deal.card', version: '1.0.0' },
57
- { key: 'crm.pipeline.metrics', version: '1.0.0' },
58
- ],
50
+ // Presentations associated with this feature
51
+ presentations: [
52
+ { key: 'crm.dashboard', version: '1.0.0' },
53
+ { key: 'crm.pipeline.kanban', version: '1.0.0' },
54
+ { key: 'crm.deal.viewList', version: '1.0.0' },
55
+ { key: 'crm.deal.detail', version: '1.0.0' },
56
+ { key: 'crm.deal.card', version: '1.0.0' },
57
+ { key: 'crm.pipeline.metrics', version: '1.0.0' },
58
+ ],
59
59
 
60
- // Link operations to their primary presentations
61
- opToPresentation: [
62
- {
63
- op: { key: 'crm.deal.list', version: '1.0.0' },
64
- pres: { key: 'crm.pipeline.kanban', version: '1.0.0' },
65
- },
66
- {
67
- op: { key: 'crm.deal.move', version: '1.0.0' },
68
- pres: { key: 'crm.pipeline.kanban', version: '1.0.0' },
69
- },
70
- ],
60
+ // Link operations to their primary presentations
61
+ opToPresentation: [
62
+ {
63
+ op: { key: 'crm.deal.list', version: '1.0.0' },
64
+ pres: { key: 'crm.pipeline.kanban', version: '1.0.0' },
65
+ },
66
+ {
67
+ op: { key: 'crm.deal.move', version: '1.0.0' },
68
+ pres: { key: 'crm.pipeline.kanban', version: '1.0.0' },
69
+ },
70
+ ],
71
71
 
72
- // Target requirements for multi-surface rendering
73
- presentationsTargets: [
74
- { key: 'crm.dashboard', version: '1.0.0', targets: ['react', 'markdown'] },
75
- {
76
- key: 'crm.pipeline.kanban',
77
- version: '1.0.0',
78
- targets: ['react', 'markdown'],
79
- },
80
- {
81
- key: 'crm.deal.viewList',
82
- version: '1.0.0',
83
- targets: ['react', 'markdown', 'application/json'],
84
- },
85
- {
86
- key: 'crm.pipeline.metrics',
87
- version: '1.0.0',
88
- targets: ['react', 'markdown'],
89
- },
90
- ],
72
+ // Target requirements for multi-surface rendering
73
+ presentationsTargets: [
74
+ { key: 'crm.dashboard', version: '1.0.0', targets: ['react', 'markdown'] },
75
+ {
76
+ key: 'crm.pipeline.kanban',
77
+ version: '1.0.0',
78
+ targets: ['react', 'markdown'],
79
+ },
80
+ {
81
+ key: 'crm.deal.viewList',
82
+ version: '1.0.0',
83
+ targets: ['react', 'markdown', 'application/json'],
84
+ },
85
+ {
86
+ key: 'crm.pipeline.metrics',
87
+ version: '1.0.0',
88
+ targets: ['react', 'markdown'],
89
+ },
90
+ ],
91
91
 
92
- // Capability requirements
93
- capabilities: {
94
- requires: [
95
- { key: 'identity', version: '1.0.0' },
96
- { key: 'audit-trail', version: '1.0.0' },
97
- { key: 'notifications', version: '1.0.0' },
98
- ],
99
- },
92
+ // Capability requirements
93
+ capabilities: {
94
+ requires: [
95
+ { key: 'identity', version: '1.0.0' },
96
+ { key: 'audit-trail', version: '1.0.0' },
97
+ { key: 'notifications', version: '1.0.0' },
98
+ ],
99
+ },
100
100
 
101
- telemetry: [{ key: 'crm-pipeline.telemetry', version: '1.0.0' }],
101
+ telemetry: [{ key: 'crm-pipeline.telemetry', version: '1.0.0' }],
102
102
 
103
- docs: [
104
- 'docs.examples.crm-pipeline.goal',
105
- 'docs.examples.crm-pipeline.usage',
106
- 'docs.examples.crm-pipeline.reference',
107
- 'docs.examples.crm-pipeline.constraints',
108
- ],
103
+ docs: [
104
+ 'docs.examples.crm-pipeline.goal',
105
+ 'docs.examples.crm-pipeline.usage',
106
+ 'docs.examples.crm-pipeline.reference',
107
+ 'docs.examples.crm-pipeline.constraints',
108
+ ],
109
109
  });