@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
@@ -550,7 +550,7 @@ var crmPipelineDocBlocks = [
550
550
  - deal.created, stage.moved, task.completed, contact.updated.
551
551
 
552
552
  ## Presentations
553
- - Pipelines/kanban, deal detail, contact/company profiles, task lists.
553
+ - Pipelines/kanban, deal detail, contact/company profiles, task lists, and a server-mode shared table for the deal list.
554
554
 
555
555
  ## Notes
556
556
  - Stage definitions should be declarative; enforce via spec and regeneration.
@@ -909,8 +909,8 @@ var crmPipelineSchemaContribution = {
909
909
  };
910
910
 
911
911
  // src/events/contact.event.ts
912
- import { ScalarTypeEnum as ScalarTypeEnum2, defineSchemaModel as defineSchemaModel2 } from "@contractspec/lib.schema";
913
912
  import { defineEvent } from "@contractspec/lib.contracts-spec";
913
+ import { defineSchemaModel as defineSchemaModel2, ScalarTypeEnum as ScalarTypeEnum2 } from "@contractspec/lib.schema";
914
914
  var ContactCreatedPayload = defineSchemaModel2({
915
915
  name: "ContactCreatedPayload",
916
916
  description: "Payload when a contact is created",
@@ -938,8 +938,8 @@ var ContactCreatedEvent = defineEvent({
938
938
  });
939
939
 
940
940
  // src/events/deal.event.ts
941
- import { ScalarTypeEnum as ScalarTypeEnum3, defineSchemaModel as defineSchemaModel3 } from "@contractspec/lib.schema";
942
941
  import { defineEvent as defineEvent2 } from "@contractspec/lib.contracts-spec";
942
+ import { defineSchemaModel as defineSchemaModel3, ScalarTypeEnum as ScalarTypeEnum3 } from "@contractspec/lib.schema";
943
943
  var DealCreatedPayload = defineSchemaModel3({
944
944
  name: "DealCreatedPayload",
945
945
  description: "Payload when a deal is created",
@@ -1034,8 +1034,8 @@ var DealLostEvent = defineEvent2({
1034
1034
  });
1035
1035
 
1036
1036
  // src/events/task.event.ts
1037
- import { ScalarTypeEnum as ScalarTypeEnum4, defineSchemaModel as defineSchemaModel4 } from "@contractspec/lib.schema";
1038
1037
  import { defineEvent as defineEvent3 } from "@contractspec/lib.contracts-spec";
1038
+ import { defineSchemaModel as defineSchemaModel4, ScalarTypeEnum as ScalarTypeEnum4 } from "@contractspec/lib.schema";
1039
1039
  var TaskCompletedPayload = defineSchemaModel4({
1040
1040
  name: "TaskCompletedPayload",
1041
1041
  description: "Payload when a task is completed",
@@ -1119,6 +1119,13 @@ function rowToDeal(row) {
1119
1119
  updatedAt: new Date(row.updatedAt)
1120
1120
  };
1121
1121
  }
1122
+ var DEAL_SORT_COLUMNS = {
1123
+ name: "name",
1124
+ value: "value",
1125
+ status: "status",
1126
+ expectedCloseDate: "expectedCloseDate",
1127
+ updatedAt: "updatedAt"
1128
+ };
1122
1129
  function createCrmHandlers(db) {
1123
1130
  async function listDeals(input) {
1124
1131
  const {
@@ -1129,7 +1136,9 @@ function createCrmHandlers(db) {
1129
1136
  ownerId,
1130
1137
  search,
1131
1138
  limit = 20,
1132
- offset = 0
1139
+ offset = 0,
1140
+ sortBy = "value",
1141
+ sortDirection = "desc"
1133
1142
  } = input;
1134
1143
  let whereClause = "WHERE projectId = ?";
1135
1144
  const params = [projectId];
@@ -1157,7 +1166,9 @@ function createCrmHandlers(db) {
1157
1166
  const total = countResult[0]?.count ?? 0;
1158
1167
  const valueResult = (await db.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${whereClause}`, params)).rows;
1159
1168
  const totalValue = valueResult[0]?.total ?? 0;
1160
- const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY value DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
1169
+ const orderByColumn = DEAL_SORT_COLUMNS[sortBy] ?? DEAL_SORT_COLUMNS.value;
1170
+ const orderByDirection = sortDirection === "asc" ? "ASC" : "DESC";
1171
+ const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY ${orderByColumn} ${orderByDirection} LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
1161
1172
  return {
1162
1173
  deals: dealRows.map(rowToDeal),
1163
1174
  total,
@@ -1689,20 +1700,183 @@ var DealCardPresentation = definePresentation2({
1689
1700
  flags: ["crm.deals.enabled"]
1690
1701
  }
1691
1702
  });
1703
+ // src/ui/CrmDealCard.tsx
1704
+ import { jsxDEV } from "react/jsx-dev-runtime";
1705
+ "use client";
1706
+ function formatCurrency(value, currency) {
1707
+ return new Intl.NumberFormat("en-US", {
1708
+ style: "currency",
1709
+ currency,
1710
+ minimumFractionDigits: 0,
1711
+ maximumFractionDigits: 0
1712
+ }).format(value);
1713
+ }
1714
+ function CrmDealCard({ deal: deal3, onClick }) {
1715
+ const daysUntilClose = deal3.expectedCloseDate ? Math.ceil((deal3.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
1716
+ return /* @__PURE__ */ jsxDEV("div", {
1717
+ onClick,
1718
+ className: "cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",
1719
+ role: "button",
1720
+ tabIndex: 0,
1721
+ onKeyDown: (e) => {
1722
+ if (e.key === "Enter" || e.key === " ")
1723
+ onClick?.();
1724
+ },
1725
+ children: [
1726
+ /* @__PURE__ */ jsxDEV("h4", {
1727
+ className: "font-medium leading-snug",
1728
+ children: deal3.name
1729
+ }, undefined, false, undefined, this),
1730
+ /* @__PURE__ */ jsxDEV("div", {
1731
+ className: "mt-2 font-semibold text-lg text-primary",
1732
+ children: formatCurrency(deal3.value, deal3.currency)
1733
+ }, undefined, false, undefined, this),
1734
+ /* @__PURE__ */ jsxDEV("div", {
1735
+ className: "mt-3 flex items-center justify-between text-muted-foreground text-xs",
1736
+ children: [
1737
+ daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
1738
+ className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
1739
+ children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
1740
+ }, undefined, false, undefined, this),
1741
+ /* @__PURE__ */ jsxDEV("span", {
1742
+ className: `rounded px-1.5 py-0.5 font-medium text-xs ${deal3.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal3.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
1743
+ children: deal3.status
1744
+ }, undefined, false, undefined, this)
1745
+ ]
1746
+ }, undefined, true, undefined, this)
1747
+ ]
1748
+ }, undefined, true, undefined, this);
1749
+ }
1750
+
1751
+ // src/ui/CrmPipelineBoard.tsx
1752
+ import { useState } from "react";
1753
+ import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
1754
+ "use client";
1755
+ function formatCurrency2(value) {
1756
+ if (value >= 1e6)
1757
+ return `$${(value / 1e6).toFixed(1)}M`;
1758
+ if (value >= 1000)
1759
+ return `$${(value / 1000).toFixed(0)}K`;
1760
+ return `$${value}`;
1761
+ }
1762
+ function CrmPipelineBoard({
1763
+ dealsByStage,
1764
+ stages,
1765
+ onDealClick,
1766
+ onDealMove
1767
+ }) {
1768
+ const [quickMoveOpen, setQuickMoveOpen] = useState(null);
1769
+ const sortedStages = [...stages].sort((a, b) => a.position - b.position);
1770
+ const handleQuickMove = (dealId, toStageId) => {
1771
+ onDealMove?.(dealId, toStageId);
1772
+ setQuickMoveOpen(null);
1773
+ };
1774
+ return /* @__PURE__ */ jsxDEV2("div", {
1775
+ className: "flex gap-4 overflow-x-auto pb-4",
1776
+ children: sortedStages.map((stage) => {
1777
+ const deals = dealsByStage[stage.id] ?? [];
1778
+ const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
1779
+ return /* @__PURE__ */ jsxDEV2("div", {
1780
+ className: "flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",
1781
+ children: [
1782
+ /* @__PURE__ */ jsxDEV2("div", {
1783
+ className: "flex items-center justify-between border-border border-b px-3 py-2",
1784
+ children: [
1785
+ /* @__PURE__ */ jsxDEV2("div", {
1786
+ children: [
1787
+ /* @__PURE__ */ jsxDEV2("h3", {
1788
+ className: "font-medium",
1789
+ children: stage.name
1790
+ }, undefined, false, undefined, this),
1791
+ /* @__PURE__ */ jsxDEV2("p", {
1792
+ className: "text-muted-foreground text-xs",
1793
+ children: [
1794
+ deals.length,
1795
+ " deals · ",
1796
+ formatCurrency2(stageValue)
1797
+ ]
1798
+ }, undefined, true, undefined, this)
1799
+ ]
1800
+ }, undefined, true, undefined, this),
1801
+ /* @__PURE__ */ jsxDEV2("span", {
1802
+ className: "flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",
1803
+ children: deals.length
1804
+ }, undefined, false, undefined, this)
1805
+ ]
1806
+ }, undefined, true, undefined, this),
1807
+ /* @__PURE__ */ jsxDEV2("div", {
1808
+ className: "flex flex-1 flex-col gap-2 p-2",
1809
+ children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
1810
+ className: "flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",
1811
+ children: "No deals"
1812
+ }, undefined, false, undefined, this) : deals.map((deal3) => /* @__PURE__ */ jsxDEV2("div", {
1813
+ className: "group relative",
1814
+ children: [
1815
+ /* @__PURE__ */ jsxDEV2(CrmDealCard, {
1816
+ deal: deal3,
1817
+ onClick: () => onDealClick?.(deal3.id)
1818
+ }, undefined, false, undefined, this),
1819
+ deal3.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
1820
+ className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
1821
+ children: [
1822
+ /* @__PURE__ */ jsxDEV2("button", {
1823
+ type: "button",
1824
+ onClick: (e) => {
1825
+ e.stopPropagation();
1826
+ setQuickMoveOpen(quickMoveOpen === deal3.id ? null : deal3.id);
1827
+ },
1828
+ className: "flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",
1829
+ title: "Quick move",
1830
+ children: "➡️"
1831
+ }, undefined, false, undefined, this),
1832
+ quickMoveOpen === deal3.id && /* @__PURE__ */ jsxDEV2("div", {
1833
+ className: "absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",
1834
+ children: [
1835
+ /* @__PURE__ */ jsxDEV2("p", {
1836
+ className: "px-3 py-1 font-medium text-muted-foreground text-xs",
1837
+ children: "Move to:"
1838
+ }, undefined, false, undefined, this),
1839
+ sortedStages.filter((s) => s.id !== deal3.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
1840
+ type: "button",
1841
+ onClick: (e) => {
1842
+ e.stopPropagation();
1843
+ handleQuickMove(deal3.id, s.id);
1844
+ },
1845
+ className: "w-full px-3 py-1.5 text-left text-sm hover:bg-muted",
1846
+ children: s.name
1847
+ }, s.id, false, undefined, this))
1848
+ ]
1849
+ }, undefined, true, undefined, this)
1850
+ ]
1851
+ }, undefined, true, undefined, this)
1852
+ ]
1853
+ }, deal3.id, true, undefined, this))
1854
+ }, undefined, false, undefined, this)
1855
+ ]
1856
+ }, stage.id, true, undefined, this);
1857
+ })
1858
+ }, undefined, false, undefined, this);
1859
+ }
1860
+
1692
1861
  // src/ui/hooks/useDealList.ts
1693
- import { useCallback, useEffect, useMemo, useState } from "react";
1694
1862
  import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
1863
+ import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
1695
1864
  "use client";
1696
1865
  function useDealList(options = {}) {
1697
1866
  const { handlers, projectId } = useTemplateRuntime();
1698
1867
  const { crm: crm2 } = handlers;
1699
- const [data, setData] = useState(null);
1700
- const [dealsByStage, setDealsByStage] = useState({});
1701
- const [stages, setStages] = useState([]);
1702
- const [loading, setLoading] = useState(true);
1703
- const [error, setError] = useState(null);
1704
- const [page, setPage] = useState(1);
1868
+ const [data, setData] = useState2(null);
1869
+ const [dealsByStage, setDealsByStage] = useState2({});
1870
+ const [stages, setStages] = useState2([]);
1871
+ const [loading, setLoading] = useState2(true);
1872
+ const [error, setError] = useState2(null);
1873
+ const [internalPage, setInternalPage] = useState2(0);
1705
1874
  const pipelineId = options.pipelineId ?? "pipeline-1";
1875
+ const pageIndex = options.pageIndex ?? internalPage;
1876
+ const pageSize = options.pageSize ?? options.limit ?? 50;
1877
+ const [sort] = options.sorting ?? [];
1878
+ const sortBy = sort?.id;
1879
+ const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
1706
1880
  const fetchData = useCallback(async () => {
1707
1881
  setLoading(true);
1708
1882
  setError(null);
@@ -1714,8 +1888,10 @@ function useDealList(options = {}) {
1714
1888
  stageId: options.stageId,
1715
1889
  status: options.status === "all" ? undefined : options.status,
1716
1890
  search: options.search,
1717
- limit: options.limit ?? 50,
1718
- offset: (page - 1) * (options.limit ?? 50)
1891
+ limit: pageSize,
1892
+ offset: pageIndex * pageSize,
1893
+ sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
1894
+ sortDirection
1719
1895
  }),
1720
1896
  crm2.getDealsByStage({ projectId, pipelineId }),
1721
1897
  crm2.getPipelineStages({ pipelineId })
@@ -1735,8 +1911,10 @@ function useDealList(options = {}) {
1735
1911
  options.stageId,
1736
1912
  options.status,
1737
1913
  options.search,
1738
- options.limit,
1739
- page
1914
+ pageIndex,
1915
+ pageSize,
1916
+ sortBy,
1917
+ sortDirection
1740
1918
  ]);
1741
1919
  useEffect(() => {
1742
1920
  fetchData();
@@ -1764,35 +1942,37 @@ function useDealList(options = {}) {
1764
1942
  loading,
1765
1943
  error,
1766
1944
  stats,
1767
- page,
1945
+ page: pageIndex + 1,
1946
+ pageIndex,
1947
+ pageSize,
1768
1948
  refetch: fetchData,
1769
- nextPage: () => setPage((p) => p + 1),
1770
- prevPage: () => page > 1 && setPage((p) => p - 1)
1949
+ nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
1950
+ prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
1771
1951
  };
1772
1952
  }
1773
1953
 
1774
1954
  // src/ui/hooks/useDealMutations.ts
1775
- import { useCallback as useCallback2, useState as useState2 } from "react";
1776
1955
  import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
1956
+ import { useCallback as useCallback2, useState as useState3 } from "react";
1777
1957
  function useDealMutations(options = {}) {
1778
1958
  const { handlers, projectId } = useTemplateRuntime2();
1779
1959
  const { crm: crm2 } = handlers;
1780
- const [createState, setCreateState] = useState2({
1960
+ const [createState, setCreateState] = useState3({
1781
1961
  loading: false,
1782
1962
  error: null,
1783
1963
  data: null
1784
1964
  });
1785
- const [moveState, setMoveState] = useState2({
1965
+ const [moveState, setMoveState] = useState3({
1786
1966
  loading: false,
1787
1967
  error: null,
1788
1968
  data: null
1789
1969
  });
1790
- const [winState, setWinState] = useState2({
1970
+ const [winState, setWinState] = useState3({
1791
1971
  loading: false,
1792
1972
  error: null,
1793
1973
  data: null
1794
1974
  });
1795
- const [loseState, setLoseState] = useState2({
1975
+ const [loseState, setLoseState] = useState3({
1796
1976
  loading: false,
1797
1977
  error: null,
1798
1978
  data: null
@@ -1856,180 +2036,22 @@ function useDealMutations(options = {}) {
1856
2036
  return null;
1857
2037
  }
1858
2038
  }, [crm2, options]);
1859
- return {
1860
- createDeal,
1861
- moveDeal,
1862
- winDeal,
1863
- loseDeal,
1864
- createState,
1865
- moveState,
1866
- winState,
1867
- loseState,
1868
- isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
1869
- };
1870
- }
1871
-
1872
- // src/ui/CrmDealCard.tsx
1873
- import { jsxDEV } from "react/jsx-dev-runtime";
1874
- "use client";
1875
- function formatCurrency(value, currency) {
1876
- return new Intl.NumberFormat("en-US", {
1877
- style: "currency",
1878
- currency,
1879
- minimumFractionDigits: 0,
1880
- maximumFractionDigits: 0
1881
- }).format(value);
1882
- }
1883
- function CrmDealCard({ deal: deal3, onClick }) {
1884
- const daysUntilClose = deal3.expectedCloseDate ? Math.ceil((deal3.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
1885
- return /* @__PURE__ */ jsxDEV("div", {
1886
- onClick,
1887
- className: "border-border bg-card cursor-pointer rounded-lg border p-3 shadow-sm transition-shadow hover:shadow-md",
1888
- role: "button",
1889
- tabIndex: 0,
1890
- onKeyDown: (e) => {
1891
- if (e.key === "Enter" || e.key === " ")
1892
- onClick?.();
1893
- },
1894
- children: [
1895
- /* @__PURE__ */ jsxDEV("h4", {
1896
- className: "leading-snug font-medium",
1897
- children: deal3.name
1898
- }, undefined, false, undefined, this),
1899
- /* @__PURE__ */ jsxDEV("div", {
1900
- className: "text-primary mt-2 text-lg font-semibold",
1901
- children: formatCurrency(deal3.value, deal3.currency)
1902
- }, undefined, false, undefined, this),
1903
- /* @__PURE__ */ jsxDEV("div", {
1904
- className: "text-muted-foreground mt-3 flex items-center justify-between text-xs",
1905
- children: [
1906
- daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
1907
- className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
1908
- children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
1909
- }, undefined, false, undefined, this),
1910
- /* @__PURE__ */ jsxDEV("span", {
1911
- className: `rounded px-1.5 py-0.5 text-xs font-medium ${deal3.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal3.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
1912
- children: deal3.status
1913
- }, undefined, false, undefined, this)
1914
- ]
1915
- }, undefined, true, undefined, this)
1916
- ]
1917
- }, undefined, true, undefined, this);
1918
- }
1919
-
1920
- // src/ui/CrmPipelineBoard.tsx
1921
- import { useState as useState3 } from "react";
1922
- import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
1923
- "use client";
1924
- function formatCurrency2(value) {
1925
- if (value >= 1e6)
1926
- return `$${(value / 1e6).toFixed(1)}M`;
1927
- if (value >= 1000)
1928
- return `$${(value / 1000).toFixed(0)}K`;
1929
- return `$${value}`;
1930
- }
1931
- function CrmPipelineBoard({
1932
- dealsByStage,
1933
- stages,
1934
- onDealClick,
1935
- onDealMove
1936
- }) {
1937
- const [quickMoveOpen, setQuickMoveOpen] = useState3(null);
1938
- const sortedStages = [...stages].sort((a, b) => a.position - b.position);
1939
- const handleQuickMove = (dealId, toStageId) => {
1940
- onDealMove?.(dealId, toStageId);
1941
- setQuickMoveOpen(null);
1942
- };
1943
- return /* @__PURE__ */ jsxDEV2("div", {
1944
- className: "flex gap-4 overflow-x-auto pb-4",
1945
- children: sortedStages.map((stage) => {
1946
- const deals = dealsByStage[stage.id] ?? [];
1947
- const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
1948
- return /* @__PURE__ */ jsxDEV2("div", {
1949
- className: "bg-muted/30 flex w-72 flex-shrink-0 flex-col rounded-lg",
1950
- children: [
1951
- /* @__PURE__ */ jsxDEV2("div", {
1952
- className: "border-border flex items-center justify-between border-b px-3 py-2",
1953
- children: [
1954
- /* @__PURE__ */ jsxDEV2("div", {
1955
- children: [
1956
- /* @__PURE__ */ jsxDEV2("h3", {
1957
- className: "font-medium",
1958
- children: stage.name
1959
- }, undefined, false, undefined, this),
1960
- /* @__PURE__ */ jsxDEV2("p", {
1961
- className: "text-muted-foreground text-xs",
1962
- children: [
1963
- deals.length,
1964
- " deals · ",
1965
- formatCurrency2(stageValue)
1966
- ]
1967
- }, undefined, true, undefined, this)
1968
- ]
1969
- }, undefined, true, undefined, this),
1970
- /* @__PURE__ */ jsxDEV2("span", {
1971
- className: "bg-muted flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium",
1972
- children: deals.length
1973
- }, undefined, false, undefined, this)
1974
- ]
1975
- }, undefined, true, undefined, this),
1976
- /* @__PURE__ */ jsxDEV2("div", {
1977
- className: "flex flex-1 flex-col gap-2 p-2",
1978
- children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
1979
- className: "border-muted-foreground/20 text-muted-foreground flex h-24 items-center justify-center rounded-md border-2 border-dashed text-xs",
1980
- children: "No deals"
1981
- }, undefined, false, undefined, this) : deals.map((deal3) => /* @__PURE__ */ jsxDEV2("div", {
1982
- className: "group relative",
1983
- children: [
1984
- /* @__PURE__ */ jsxDEV2(CrmDealCard, {
1985
- deal: deal3,
1986
- onClick: () => onDealClick?.(deal3.id)
1987
- }, undefined, false, undefined, this),
1988
- deal3.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
1989
- className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
1990
- children: [
1991
- /* @__PURE__ */ jsxDEV2("button", {
1992
- type: "button",
1993
- onClick: (e) => {
1994
- e.stopPropagation();
1995
- setQuickMoveOpen(quickMoveOpen === deal3.id ? null : deal3.id);
1996
- },
1997
- className: "bg-background border-border hover:bg-muted flex h-6 w-6 items-center justify-center rounded border text-xs shadow-sm",
1998
- title: "Quick move",
1999
- children: "➡️"
2000
- }, undefined, false, undefined, this),
2001
- quickMoveOpen === deal3.id && /* @__PURE__ */ jsxDEV2("div", {
2002
- className: "bg-card border-border absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border py-1 shadow-lg",
2003
- children: [
2004
- /* @__PURE__ */ jsxDEV2("p", {
2005
- className: "text-muted-foreground px-3 py-1 text-xs font-medium",
2006
- children: "Move to:"
2007
- }, undefined, false, undefined, this),
2008
- sortedStages.filter((s) => s.id !== deal3.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
2009
- type: "button",
2010
- onClick: (e) => {
2011
- e.stopPropagation();
2012
- handleQuickMove(deal3.id, s.id);
2013
- },
2014
- className: "hover:bg-muted w-full px-3 py-1.5 text-left text-sm",
2015
- children: s.name
2016
- }, s.id, false, undefined, this))
2017
- ]
2018
- }, undefined, true, undefined, this)
2019
- ]
2020
- }, undefined, true, undefined, this)
2021
- ]
2022
- }, deal3.id, true, undefined, this))
2023
- }, undefined, false, undefined, this)
2024
- ]
2025
- }, stage.id, true, undefined, this);
2026
- })
2027
- }, undefined, false, undefined, this);
2039
+ return {
2040
+ createDeal,
2041
+ moveDeal,
2042
+ winDeal,
2043
+ loseDeal,
2044
+ createState,
2045
+ moveState,
2046
+ winState,
2047
+ loseState,
2048
+ isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
2049
+ };
2028
2050
  }
2029
2051
 
2030
2052
  // src/ui/modals/CreateDealModal.tsx
2031
- import { useState as useState4 } from "react";
2032
2053
  import { Button, Input } from "@contractspec/lib.design-system";
2054
+ import { useState as useState4 } from "react";
2033
2055
  import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
2034
2056
  "use client";
2035
2057
  var CURRENCIES = ["USD", "EUR", "GBP", "CAD"];
@@ -2088,7 +2110,7 @@ function CreateDealModal({
2088
2110
  className: "fixed inset-0 z-50 flex items-center justify-center",
2089
2111
  children: [
2090
2112
  /* @__PURE__ */ jsxDEV3("div", {
2091
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
2113
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
2092
2114
  onClick: onClose,
2093
2115
  role: "button",
2094
2116
  tabIndex: 0,
@@ -2099,10 +2121,10 @@ function CreateDealModal({
2099
2121
  "aria-label": "Close modal"
2100
2122
  }, undefined, false, undefined, this),
2101
2123
  /* @__PURE__ */ jsxDEV3("div", {
2102
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
2124
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
2103
2125
  children: [
2104
2126
  /* @__PURE__ */ jsxDEV3("h2", {
2105
- className: "mb-4 text-xl font-semibold",
2127
+ className: "mb-4 font-semibold text-xl",
2106
2128
  children: "Create New Deal"
2107
2129
  }, undefined, false, undefined, this),
2108
2130
  /* @__PURE__ */ jsxDEV3("form", {
@@ -2113,7 +2135,7 @@ function CreateDealModal({
2113
2135
  children: [
2114
2136
  /* @__PURE__ */ jsxDEV3("label", {
2115
2137
  htmlFor: "deal-name",
2116
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2138
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2117
2139
  children: "Deal Name *"
2118
2140
  }, undefined, false, undefined, this),
2119
2141
  /* @__PURE__ */ jsxDEV3(Input, {
@@ -2133,7 +2155,7 @@ function CreateDealModal({
2133
2155
  children: [
2134
2156
  /* @__PURE__ */ jsxDEV3("label", {
2135
2157
  htmlFor: "deal-value",
2136
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2158
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2137
2159
  children: "Value *"
2138
2160
  }, undefined, false, undefined, this),
2139
2161
  /* @__PURE__ */ jsxDEV3(Input, {
@@ -2153,7 +2175,7 @@ function CreateDealModal({
2153
2175
  children: [
2154
2176
  /* @__PURE__ */ jsxDEV3("label", {
2155
2177
  htmlFor: "deal-currency",
2156
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2178
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2157
2179
  children: "Currency"
2158
2180
  }, undefined, false, undefined, this),
2159
2181
  /* @__PURE__ */ jsxDEV3("select", {
@@ -2161,7 +2183,7 @@ function CreateDealModal({
2161
2183
  value: currency,
2162
2184
  onChange: (e) => setCurrency(e.target.value),
2163
2185
  disabled: isLoading,
2164
- 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",
2186
+ 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",
2165
2187
  children: CURRENCIES.map((c) => /* @__PURE__ */ jsxDEV3("option", {
2166
2188
  value: c,
2167
2189
  children: c
@@ -2175,7 +2197,7 @@ function CreateDealModal({
2175
2197
  children: [
2176
2198
  /* @__PURE__ */ jsxDEV3("label", {
2177
2199
  htmlFor: "deal-stage",
2178
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2200
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2179
2201
  children: "Pipeline Stage *"
2180
2202
  }, undefined, false, undefined, this),
2181
2203
  /* @__PURE__ */ jsxDEV3("select", {
@@ -2183,7 +2205,7 @@ function CreateDealModal({
2183
2205
  value: stageId,
2184
2206
  onChange: (e) => setStageId(e.target.value),
2185
2207
  disabled: isLoading,
2186
- 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",
2208
+ 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",
2187
2209
  children: stages.map((stage) => /* @__PURE__ */ jsxDEV3("option", {
2188
2210
  value: stage.id,
2189
2211
  children: stage.name
@@ -2195,7 +2217,7 @@ function CreateDealModal({
2195
2217
  children: [
2196
2218
  /* @__PURE__ */ jsxDEV3("label", {
2197
2219
  htmlFor: "deal-close-date",
2198
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2220
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2199
2221
  children: "Expected Close Date"
2200
2222
  }, undefined, false, undefined, this),
2201
2223
  /* @__PURE__ */ jsxDEV3(Input, {
@@ -2208,7 +2230,7 @@ function CreateDealModal({
2208
2230
  ]
2209
2231
  }, undefined, true, undefined, this),
2210
2232
  error && /* @__PURE__ */ jsxDEV3("div", {
2211
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
2233
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
2212
2234
  children: error
2213
2235
  }, undefined, false, undefined, this),
2214
2236
  /* @__PURE__ */ jsxDEV3("div", {
@@ -2237,8 +2259,8 @@ function CreateDealModal({
2237
2259
  }
2238
2260
 
2239
2261
  // src/ui/modals/DealActionsModal.tsx
2240
- import { useState as useState5 } from "react";
2241
2262
  import { Button as Button2 } from "@contractspec/lib.design-system";
2263
+ import { useState as useState5 } from "react";
2242
2264
  import { jsxDEV as jsxDEV4, Fragment } from "react/jsx-dev-runtime";
2243
2265
  "use client";
2244
2266
  function formatCurrency3(value, currency) {
@@ -2339,7 +2361,7 @@ function DealActionsModal({
2339
2361
  className: "fixed inset-0 z-50 flex items-center justify-center",
2340
2362
  children: [
2341
2363
  /* @__PURE__ */ jsxDEV4("div", {
2342
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
2364
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
2343
2365
  onClick: handleClose,
2344
2366
  role: "button",
2345
2367
  tabIndex: 0,
@@ -2350,21 +2372,21 @@ function DealActionsModal({
2350
2372
  "aria-label": "Close modal"
2351
2373
  }, undefined, false, undefined, this),
2352
2374
  /* @__PURE__ */ jsxDEV4("div", {
2353
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
2375
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
2354
2376
  children: [
2355
2377
  /* @__PURE__ */ jsxDEV4("div", {
2356
- className: "border-border mb-4 border-b pb-4",
2378
+ className: "mb-4 border-border border-b pb-4",
2357
2379
  children: [
2358
2380
  /* @__PURE__ */ jsxDEV4("h2", {
2359
- className: "text-xl font-semibold",
2381
+ className: "font-semibold text-xl",
2360
2382
  children: deal3.name
2361
2383
  }, undefined, false, undefined, this),
2362
2384
  /* @__PURE__ */ jsxDEV4("p", {
2363
- className: "text-primary text-lg font-medium",
2385
+ className: "font-medium text-lg text-primary",
2364
2386
  children: formatCurrency3(deal3.value, deal3.currency)
2365
2387
  }, undefined, false, undefined, this),
2366
2388
  /* @__PURE__ */ jsxDEV4("span", {
2367
- className: `mt-2 inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${deal3.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal3.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
2389
+ className: `mt-2 inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${deal3.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal3.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
2368
2390
  children: deal3.status
2369
2391
  }, undefined, false, undefined, this)
2370
2392
  ]
@@ -2416,7 +2438,7 @@ function DealActionsModal({
2416
2438
  ]
2417
2439
  }, undefined, true, undefined, this),
2418
2440
  deal3.status !== "OPEN" && /* @__PURE__ */ jsxDEV4("p", {
2419
- className: "text-muted-foreground py-4 text-center",
2441
+ className: "py-4 text-center text-muted-foreground",
2420
2442
  children: [
2421
2443
  "This deal is already ",
2422
2444
  deal3.status.toLowerCase(),
@@ -2441,14 +2463,14 @@ function DealActionsModal({
2441
2463
  children: [
2442
2464
  /* @__PURE__ */ jsxDEV4("label", {
2443
2465
  htmlFor: "won-source",
2444
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2466
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2445
2467
  children: "How did you win this deal?"
2446
2468
  }, undefined, false, undefined, this),
2447
2469
  /* @__PURE__ */ jsxDEV4("select", {
2448
2470
  id: "won-source",
2449
2471
  value: wonSource,
2450
2472
  onChange: (e) => setWonSource(e.target.value),
2451
- 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",
2473
+ 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",
2452
2474
  children: [
2453
2475
  /* @__PURE__ */ jsxDEV4("option", {
2454
2476
  value: "",
@@ -2482,7 +2504,7 @@ function DealActionsModal({
2482
2504
  children: [
2483
2505
  /* @__PURE__ */ jsxDEV4("label", {
2484
2506
  htmlFor: "win-notes",
2485
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2507
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2486
2508
  children: "Notes (optional)"
2487
2509
  }, undefined, false, undefined, this),
2488
2510
  /* @__PURE__ */ jsxDEV4("textarea", {
@@ -2491,12 +2513,12 @@ function DealActionsModal({
2491
2513
  onChange: (e) => setNotes(e.target.value),
2492
2514
  placeholder: "Any additional notes about the win...",
2493
2515
  rows: 3,
2494
- className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
2516
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
2495
2517
  }, undefined, false, undefined, this)
2496
2518
  ]
2497
2519
  }, undefined, true, undefined, this),
2498
2520
  error && /* @__PURE__ */ jsxDEV4("div", {
2499
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
2521
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
2500
2522
  children: error
2501
2523
  }, undefined, false, undefined, this),
2502
2524
  /* @__PURE__ */ jsxDEV4("div", {
@@ -2524,14 +2546,14 @@ function DealActionsModal({
2524
2546
  children: [
2525
2547
  /* @__PURE__ */ jsxDEV4("label", {
2526
2548
  htmlFor: "lost-reason",
2527
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2549
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2528
2550
  children: "Why was this deal lost? *"
2529
2551
  }, undefined, false, undefined, this),
2530
2552
  /* @__PURE__ */ jsxDEV4("select", {
2531
2553
  id: "lost-reason",
2532
2554
  value: lostReason,
2533
2555
  onChange: (e) => setLostReason(e.target.value),
2534
- 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",
2556
+ 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",
2535
2557
  children: [
2536
2558
  /* @__PURE__ */ jsxDEV4("option", {
2537
2559
  value: "",
@@ -2573,7 +2595,7 @@ function DealActionsModal({
2573
2595
  children: [
2574
2596
  /* @__PURE__ */ jsxDEV4("label", {
2575
2597
  htmlFor: "lose-notes",
2576
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2598
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2577
2599
  children: "Notes (optional)"
2578
2600
  }, undefined, false, undefined, this),
2579
2601
  /* @__PURE__ */ jsxDEV4("textarea", {
@@ -2582,12 +2604,12 @@ function DealActionsModal({
2582
2604
  onChange: (e) => setNotes(e.target.value),
2583
2605
  placeholder: "Any additional details...",
2584
2606
  rows: 3,
2585
- className: "border-input bg-background focus:ring-ring w-full rounded-md border px-3 py-2 text-sm focus:ring-2 focus:outline-none"
2607
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
2586
2608
  }, undefined, false, undefined, this)
2587
2609
  ]
2588
2610
  }, undefined, true, undefined, this),
2589
2611
  error && /* @__PURE__ */ jsxDEV4("div", {
2590
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
2612
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
2591
2613
  children: error
2592
2614
  }, undefined, false, undefined, this),
2593
2615
  /* @__PURE__ */ jsxDEV4("div", {
@@ -2616,14 +2638,14 @@ function DealActionsModal({
2616
2638
  children: [
2617
2639
  /* @__PURE__ */ jsxDEV4("label", {
2618
2640
  htmlFor: "move-stage",
2619
- className: "text-muted-foreground mb-1 block text-sm font-medium",
2641
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
2620
2642
  children: "Move to Stage"
2621
2643
  }, undefined, false, undefined, this),
2622
2644
  /* @__PURE__ */ jsxDEV4("select", {
2623
2645
  id: "move-stage",
2624
2646
  value: selectedStageId,
2625
2647
  onChange: (e) => setSelectedStageId(e.target.value),
2626
- 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",
2648
+ 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",
2627
2649
  children: stages.map((stage) => /* @__PURE__ */ jsxDEV4("option", {
2628
2650
  value: stage.id,
2629
2651
  children: [
@@ -2635,7 +2657,7 @@ function DealActionsModal({
2635
2657
  ]
2636
2658
  }, undefined, true, undefined, this),
2637
2659
  error && /* @__PURE__ */ jsxDEV4("div", {
2638
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
2660
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
2639
2661
  children: error
2640
2662
  }, undefined, false, undefined, this),
2641
2663
  /* @__PURE__ */ jsxDEV4("div", {
@@ -2662,12 +2684,305 @@ function DealActionsModal({
2662
2684
  }, undefined, true, undefined, this);
2663
2685
  }
2664
2686
 
2665
- // src/ui/CrmDashboard.tsx
2666
- import { useCallback as useCallback3, useState as useState6 } from "react";
2687
+ // src/ui/tables/DealListTab.tsx
2667
2688
  import {
2668
2689
  Button as Button3,
2690
+ DataTable,
2691
+ LoaderBlock
2692
+ } from "@contractspec/lib.design-system";
2693
+ import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
2694
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
2695
+ import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
2696
+ import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
2697
+ import * as React from "react";
2698
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
2699
+ "use client";
2700
+ function formatCurrency4(value, currency = "USD") {
2701
+ return new Intl.NumberFormat("en-US", {
2702
+ style: "currency",
2703
+ currency,
2704
+ minimumFractionDigits: 0,
2705
+ maximumFractionDigits: 0
2706
+ }).format(value);
2707
+ }
2708
+ function statusVariant(status) {
2709
+ switch (status) {
2710
+ case "WON":
2711
+ return "default";
2712
+ case "LOST":
2713
+ return "destructive";
2714
+ case "STALE":
2715
+ return "outline";
2716
+ default:
2717
+ return "secondary";
2718
+ }
2719
+ }
2720
+ function DealListDataTable({
2721
+ deals,
2722
+ totalItems,
2723
+ pageIndex,
2724
+ pageSize,
2725
+ sorting,
2726
+ loading,
2727
+ onSortingChange,
2728
+ onPaginationChange,
2729
+ onDealClick
2730
+ }) {
2731
+ const controller = useContractTable({
2732
+ data: deals,
2733
+ columns: [
2734
+ {
2735
+ id: "deal",
2736
+ header: "Deal",
2737
+ label: "Deal",
2738
+ accessor: (deal3) => deal3.name,
2739
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV5(VStack, {
2740
+ gap: "xs",
2741
+ children: [
2742
+ /* @__PURE__ */ jsxDEV5(Text, {
2743
+ className: "font-medium text-sm",
2744
+ children: item.name
2745
+ }, undefined, false, undefined, this),
2746
+ /* @__PURE__ */ jsxDEV5(Text, {
2747
+ className: "text-muted-foreground text-xs",
2748
+ children: item.companyId ?? "Unassigned company"
2749
+ }, undefined, false, undefined, this)
2750
+ ]
2751
+ }, undefined, true, undefined, this),
2752
+ size: 240,
2753
+ minSize: 180,
2754
+ canSort: true,
2755
+ canPin: true,
2756
+ canResize: true
2757
+ },
2758
+ {
2759
+ id: "value",
2760
+ header: "Value",
2761
+ label: "Value",
2762
+ accessorKey: "value",
2763
+ cell: ({ item }) => formatCurrency4(item.value, item.currency),
2764
+ align: "right",
2765
+ size: 140,
2766
+ canSort: true,
2767
+ canResize: true
2768
+ },
2769
+ {
2770
+ id: "status",
2771
+ header: "Status",
2772
+ label: "Status",
2773
+ accessorKey: "status",
2774
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV5(Badge, {
2775
+ variant: statusVariant(value),
2776
+ children: String(value)
2777
+ }, undefined, false, undefined, this),
2778
+ size: 130,
2779
+ canSort: true,
2780
+ canHide: true,
2781
+ canPin: true,
2782
+ canResize: true
2783
+ },
2784
+ {
2785
+ id: "expectedCloseDate",
2786
+ header: "Expected Close",
2787
+ label: "Expected Close",
2788
+ accessor: (deal3) => deal3.expectedCloseDate?.toISOString() ?? "",
2789
+ cell: ({ item }) => item.expectedCloseDate?.toLocaleDateString() ?? "Not scheduled",
2790
+ size: 170,
2791
+ canSort: true,
2792
+ canHide: true,
2793
+ canResize: true
2794
+ },
2795
+ {
2796
+ id: "updatedAt",
2797
+ header: "Updated",
2798
+ label: "Updated",
2799
+ accessor: (deal3) => deal3.updatedAt.toISOString(),
2800
+ cell: ({ item }) => item.updatedAt.toLocaleDateString(),
2801
+ size: 140,
2802
+ canSort: true,
2803
+ canHide: true,
2804
+ canResize: true
2805
+ },
2806
+ {
2807
+ id: "actions",
2808
+ header: "Actions",
2809
+ label: "Actions",
2810
+ accessor: (deal3) => deal3.id,
2811
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV5(Button3, {
2812
+ variant: "ghost",
2813
+ size: "sm",
2814
+ onPress: () => onDealClick?.(item.id),
2815
+ children: "Actions"
2816
+ }, undefined, false, undefined, this),
2817
+ size: 120,
2818
+ canSort: false,
2819
+ canHide: false,
2820
+ canPin: false,
2821
+ canResize: false
2822
+ }
2823
+ ],
2824
+ executionMode: "server",
2825
+ selectionMode: "multiple",
2826
+ totalItems,
2827
+ state: {
2828
+ sorting,
2829
+ pagination: {
2830
+ pageIndex,
2831
+ pageSize
2832
+ }
2833
+ },
2834
+ onSortingChange,
2835
+ onPaginationChange,
2836
+ initialState: {
2837
+ columnVisibility: { updatedAt: false },
2838
+ columnPinning: { left: ["deal", "status"], right: [] }
2839
+ },
2840
+ renderExpandedContent: (deal3) => /* @__PURE__ */ jsxDEV5(VStack, {
2841
+ gap: "sm",
2842
+ className: "py-2",
2843
+ children: [
2844
+ /* @__PURE__ */ jsxDEV5(HStack, {
2845
+ justify: "between",
2846
+ children: [
2847
+ /* @__PURE__ */ jsxDEV5(Text, {
2848
+ className: "font-medium text-sm",
2849
+ children: "Owner"
2850
+ }, undefined, false, undefined, this),
2851
+ /* @__PURE__ */ jsxDEV5(Text, {
2852
+ className: "text-muted-foreground text-sm",
2853
+ children: deal3.ownerId
2854
+ }, undefined, false, undefined, this)
2855
+ ]
2856
+ }, undefined, true, undefined, this),
2857
+ /* @__PURE__ */ jsxDEV5(HStack, {
2858
+ justify: "between",
2859
+ children: [
2860
+ /* @__PURE__ */ jsxDEV5(Text, {
2861
+ className: "font-medium text-sm",
2862
+ children: "Contact"
2863
+ }, undefined, false, undefined, this),
2864
+ /* @__PURE__ */ jsxDEV5(Text, {
2865
+ className: "text-muted-foreground text-sm",
2866
+ children: deal3.contactId ?? "No linked contact"
2867
+ }, undefined, false, undefined, this)
2868
+ ]
2869
+ }, undefined, true, undefined, this),
2870
+ deal3.wonSource ? /* @__PURE__ */ jsxDEV5(HStack, {
2871
+ justify: "between",
2872
+ children: [
2873
+ /* @__PURE__ */ jsxDEV5(Text, {
2874
+ className: "font-medium text-sm",
2875
+ children: "Won Source"
2876
+ }, undefined, false, undefined, this),
2877
+ /* @__PURE__ */ jsxDEV5(Text, {
2878
+ className: "text-muted-foreground text-sm",
2879
+ children: deal3.wonSource
2880
+ }, undefined, false, undefined, this)
2881
+ ]
2882
+ }, undefined, true, undefined, this) : null,
2883
+ deal3.lostReason ? /* @__PURE__ */ jsxDEV5(HStack, {
2884
+ justify: "between",
2885
+ children: [
2886
+ /* @__PURE__ */ jsxDEV5(Text, {
2887
+ className: "font-medium text-sm",
2888
+ children: "Lost Reason"
2889
+ }, undefined, false, undefined, this),
2890
+ /* @__PURE__ */ jsxDEV5(Text, {
2891
+ className: "text-muted-foreground text-sm",
2892
+ children: deal3.lostReason
2893
+ }, undefined, false, undefined, this)
2894
+ ]
2895
+ }, undefined, true, undefined, this) : null,
2896
+ deal3.notes ? /* @__PURE__ */ jsxDEV5(VStack, {
2897
+ gap: "xs",
2898
+ children: [
2899
+ /* @__PURE__ */ jsxDEV5(Text, {
2900
+ className: "font-medium text-sm",
2901
+ children: "Notes"
2902
+ }, undefined, false, undefined, this),
2903
+ /* @__PURE__ */ jsxDEV5(Text, {
2904
+ className: "text-muted-foreground text-sm",
2905
+ children: deal3.notes
2906
+ }, undefined, false, undefined, this)
2907
+ ]
2908
+ }, undefined, true, undefined, this) : null
2909
+ ]
2910
+ }, undefined, true, undefined, this),
2911
+ getCanExpand: () => true
2912
+ });
2913
+ return /* @__PURE__ */ jsxDEV5(DataTable, {
2914
+ controller,
2915
+ title: "All Deals",
2916
+ description: "Server-mode table using the shared ContractSpec controller.",
2917
+ loading,
2918
+ toolbar: /* @__PURE__ */ jsxDEV5(HStack, {
2919
+ gap: "sm",
2920
+ className: "flex-wrap",
2921
+ children: [
2922
+ /* @__PURE__ */ jsxDEV5(Text, {
2923
+ className: "text-muted-foreground text-sm",
2924
+ children: [
2925
+ "Selected ",
2926
+ controller.selectedRowIds.length
2927
+ ]
2928
+ }, undefined, true, undefined, this),
2929
+ /* @__PURE__ */ jsxDEV5(Text, {
2930
+ className: "text-muted-foreground text-sm",
2931
+ children: [
2932
+ totalItems,
2933
+ " total deals"
2934
+ ]
2935
+ }, undefined, true, undefined, this)
2936
+ ]
2937
+ }, undefined, true, undefined, this),
2938
+ footer: `Page ${controller.pageIndex + 1} of ${controller.pageCount}`,
2939
+ emptyState: /* @__PURE__ */ jsxDEV5("div", {
2940
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
2941
+ children: "No deals found"
2942
+ }, undefined, false, undefined, this)
2943
+ }, undefined, false, undefined, this);
2944
+ }
2945
+ function DealListTab({
2946
+ onDealClick
2947
+ }) {
2948
+ const [sorting, setSorting] = React.useState([
2949
+ { id: "value", desc: true }
2950
+ ]);
2951
+ const [pagination, setPagination] = React.useState({
2952
+ pageIndex: 0,
2953
+ pageSize: 3
2954
+ });
2955
+ const { data, loading } = useDealList({
2956
+ pageIndex: pagination.pageIndex,
2957
+ pageSize: pagination.pageSize,
2958
+ sorting
2959
+ });
2960
+ if (loading && !data) {
2961
+ return /* @__PURE__ */ jsxDEV5(LoaderBlock, {
2962
+ label: "Loading deals..."
2963
+ }, undefined, false, undefined, this);
2964
+ }
2965
+ return /* @__PURE__ */ jsxDEV5(DealListDataTable, {
2966
+ deals: data?.deals ?? [],
2967
+ totalItems: data?.total ?? 0,
2968
+ pageIndex: pagination.pageIndex,
2969
+ pageSize: pagination.pageSize,
2970
+ sorting,
2971
+ loading,
2972
+ onSortingChange: (nextSorting) => {
2973
+ setSorting(nextSorting);
2974
+ setPagination((current) => ({ ...current, pageIndex: 0 }));
2975
+ },
2976
+ onPaginationChange: setPagination,
2977
+ onDealClick
2978
+ }, undefined, false, undefined, this);
2979
+ }
2980
+
2981
+ // src/ui/CrmDashboard.tsx
2982
+ import {
2983
+ Button as Button4,
2669
2984
  ErrorState,
2670
- LoaderBlock,
2985
+ LoaderBlock as LoaderBlock2,
2671
2986
  StatCard,
2672
2987
  StatCardGroup
2673
2988
  } from "@contractspec/lib.design-system";
@@ -2677,9 +2992,10 @@ import {
2677
2992
  TabsList,
2678
2993
  TabsTrigger
2679
2994
  } from "@contractspec/lib.ui-kit-web/ui/tabs";
2680
- import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
2995
+ import { useCallback as useCallback3, useState as useState7 } from "react";
2996
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
2681
2997
  "use client";
2682
- function formatCurrency4(value, currency = "USD") {
2998
+ function formatCurrency5(value, currency = "USD") {
2683
2999
  return new Intl.NumberFormat("en-US", {
2684
3000
  style: "currency",
2685
3001
  currency,
@@ -2688,9 +3004,9 @@ function formatCurrency4(value, currency = "USD") {
2688
3004
  }).format(value);
2689
3005
  }
2690
3006
  function CrmDashboard() {
2691
- const [isCreateModalOpen, setIsCreateModalOpen] = useState6(false);
2692
- const [selectedDeal, setSelectedDeal] = useState6(null);
2693
- const [isDealActionsOpen, setIsDealActionsOpen] = useState6(false);
3007
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState7(false);
3008
+ const [selectedDeal, setSelectedDeal] = useState7(null);
3009
+ const [isDealActionsOpen, setIsDealActionsOpen] = useState7(false);
2694
3010
  const { data, dealsByStage, stages, loading, error, stats, refetch } = useDealList();
2695
3011
  const mutations = useDealMutations({
2696
3012
  onSuccess: () => {
@@ -2708,32 +3024,32 @@ function CrmDashboard() {
2708
3024
  await mutations.moveDeal({ dealId, stageId: toStageId });
2709
3025
  }, [mutations]);
2710
3026
  if (loading && !data) {
2711
- return /* @__PURE__ */ jsxDEV5(LoaderBlock, {
3027
+ return /* @__PURE__ */ jsxDEV6(LoaderBlock2, {
2712
3028
  label: "Loading CRM..."
2713
3029
  }, undefined, false, undefined, this);
2714
3030
  }
2715
3031
  if (error) {
2716
- return /* @__PURE__ */ jsxDEV5(ErrorState, {
3032
+ return /* @__PURE__ */ jsxDEV6(ErrorState, {
2717
3033
  title: "Failed to load CRM",
2718
3034
  description: error.message,
2719
3035
  onRetry: refetch,
2720
3036
  retryLabel: "Retry"
2721
3037
  }, undefined, false, undefined, this);
2722
3038
  }
2723
- return /* @__PURE__ */ jsxDEV5("div", {
3039
+ return /* @__PURE__ */ jsxDEV6("div", {
2724
3040
  className: "space-y-6",
2725
3041
  children: [
2726
- /* @__PURE__ */ jsxDEV5("div", {
3042
+ /* @__PURE__ */ jsxDEV6("div", {
2727
3043
  className: "flex items-center justify-between",
2728
3044
  children: [
2729
- /* @__PURE__ */ jsxDEV5("h2", {
2730
- className: "text-2xl font-bold",
3045
+ /* @__PURE__ */ jsxDEV6("h2", {
3046
+ className: "font-bold text-2xl",
2731
3047
  children: "CRM Pipeline"
2732
3048
  }, undefined, false, undefined, this),
2733
- /* @__PURE__ */ jsxDEV5(Button3, {
3049
+ /* @__PURE__ */ jsxDEV6(Button4, {
2734
3050
  onClick: () => setIsCreateModalOpen(true),
2735
3051
  children: [
2736
- /* @__PURE__ */ jsxDEV5("span", {
3052
+ /* @__PURE__ */ jsxDEV6("span", {
2737
3053
  className: "mr-2",
2738
3054
  children: "+"
2739
3055
  }, undefined, false, undefined, this),
@@ -2742,60 +3058,60 @@ function CrmDashboard() {
2742
3058
  }, undefined, true, undefined, this)
2743
3059
  ]
2744
3060
  }, undefined, true, undefined, this),
2745
- stats && /* @__PURE__ */ jsxDEV5(StatCardGroup, {
3061
+ stats && /* @__PURE__ */ jsxDEV6(StatCardGroup, {
2746
3062
  children: [
2747
- /* @__PURE__ */ jsxDEV5(StatCard, {
3063
+ /* @__PURE__ */ jsxDEV6(StatCard, {
2748
3064
  label: "Total Pipeline",
2749
- value: formatCurrency4(stats.totalValue),
3065
+ value: formatCurrency5(stats.totalValue),
2750
3066
  hint: `${stats.total} deals`
2751
3067
  }, undefined, false, undefined, this),
2752
- /* @__PURE__ */ jsxDEV5(StatCard, {
3068
+ /* @__PURE__ */ jsxDEV6(StatCard, {
2753
3069
  label: "Open Deals",
2754
- value: formatCurrency4(stats.openValue),
3070
+ value: formatCurrency5(stats.openValue),
2755
3071
  hint: `${stats.openCount} active`
2756
3072
  }, undefined, false, undefined, this),
2757
- /* @__PURE__ */ jsxDEV5(StatCard, {
3073
+ /* @__PURE__ */ jsxDEV6(StatCard, {
2758
3074
  label: "Won",
2759
- value: formatCurrency4(stats.wonValue),
3075
+ value: formatCurrency5(stats.wonValue),
2760
3076
  hint: `${stats.wonCount} closed`
2761
3077
  }, undefined, false, undefined, this),
2762
- /* @__PURE__ */ jsxDEV5(StatCard, {
3078
+ /* @__PURE__ */ jsxDEV6(StatCard, {
2763
3079
  label: "Lost",
2764
3080
  value: stats.lostCount,
2765
3081
  hint: "deals lost"
2766
3082
  }, undefined, false, undefined, this)
2767
3083
  ]
2768
3084
  }, undefined, true, undefined, this),
2769
- /* @__PURE__ */ jsxDEV5(Tabs, {
3085
+ /* @__PURE__ */ jsxDEV6(Tabs, {
2770
3086
  defaultValue: "pipeline",
2771
3087
  className: "w-full",
2772
3088
  children: [
2773
- /* @__PURE__ */ jsxDEV5(TabsList, {
3089
+ /* @__PURE__ */ jsxDEV6(TabsList, {
2774
3090
  children: [
2775
- /* @__PURE__ */ jsxDEV5(TabsTrigger, {
3091
+ /* @__PURE__ */ jsxDEV6(TabsTrigger, {
2776
3092
  value: "pipeline",
2777
3093
  children: [
2778
- /* @__PURE__ */ jsxDEV5("span", {
3094
+ /* @__PURE__ */ jsxDEV6("span", {
2779
3095
  className: "mr-2",
2780
3096
  children: "\uD83D\uDCCA"
2781
3097
  }, undefined, false, undefined, this),
2782
3098
  "Pipeline"
2783
3099
  ]
2784
3100
  }, undefined, true, undefined, this),
2785
- /* @__PURE__ */ jsxDEV5(TabsTrigger, {
3101
+ /* @__PURE__ */ jsxDEV6(TabsTrigger, {
2786
3102
  value: "list",
2787
3103
  children: [
2788
- /* @__PURE__ */ jsxDEV5("span", {
3104
+ /* @__PURE__ */ jsxDEV6("span", {
2789
3105
  className: "mr-2",
2790
3106
  children: "\uD83D\uDCCB"
2791
3107
  }, undefined, false, undefined, this),
2792
3108
  "All Deals"
2793
3109
  ]
2794
3110
  }, undefined, true, undefined, this),
2795
- /* @__PURE__ */ jsxDEV5(TabsTrigger, {
3111
+ /* @__PURE__ */ jsxDEV6(TabsTrigger, {
2796
3112
  value: "metrics",
2797
3113
  children: [
2798
- /* @__PURE__ */ jsxDEV5("span", {
3114
+ /* @__PURE__ */ jsxDEV6("span", {
2799
3115
  className: "mr-2",
2800
3116
  children: "\uD83D\uDCC8"
2801
3117
  }, undefined, false, undefined, this),
@@ -2804,34 +3120,33 @@ function CrmDashboard() {
2804
3120
  }, undefined, true, undefined, this)
2805
3121
  ]
2806
3122
  }, undefined, true, undefined, this),
2807
- /* @__PURE__ */ jsxDEV5(TabsContent, {
3123
+ /* @__PURE__ */ jsxDEV6(TabsContent, {
2808
3124
  value: "pipeline",
2809
3125
  className: "min-h-[400px]",
2810
- children: /* @__PURE__ */ jsxDEV5(CrmPipelineBoard, {
3126
+ children: /* @__PURE__ */ jsxDEV6(CrmPipelineBoard, {
2811
3127
  dealsByStage,
2812
3128
  stages,
2813
3129
  onDealClick: handleDealClick,
2814
3130
  onDealMove: handleDealMove
2815
3131
  }, undefined, false, undefined, this)
2816
3132
  }, undefined, false, undefined, this),
2817
- /* @__PURE__ */ jsxDEV5(TabsContent, {
3133
+ /* @__PURE__ */ jsxDEV6(TabsContent, {
2818
3134
  value: "list",
2819
3135
  className: "min-h-[400px]",
2820
- children: /* @__PURE__ */ jsxDEV5(DealListTab, {
2821
- data,
3136
+ children: /* @__PURE__ */ jsxDEV6(DealListTab, {
2822
3137
  onDealClick: handleDealClick
2823
3138
  }, undefined, false, undefined, this)
2824
3139
  }, undefined, false, undefined, this),
2825
- /* @__PURE__ */ jsxDEV5(TabsContent, {
3140
+ /* @__PURE__ */ jsxDEV6(TabsContent, {
2826
3141
  value: "metrics",
2827
3142
  className: "min-h-[400px]",
2828
- children: /* @__PURE__ */ jsxDEV5(MetricsTab, {
3143
+ children: /* @__PURE__ */ jsxDEV6(MetricsTab, {
2829
3144
  stats
2830
3145
  }, undefined, false, undefined, this)
2831
3146
  }, undefined, false, undefined, this)
2832
3147
  ]
2833
3148
  }, undefined, true, undefined, this),
2834
- /* @__PURE__ */ jsxDEV5(CreateDealModal, {
3149
+ /* @__PURE__ */ jsxDEV6(CreateDealModal, {
2835
3150
  isOpen: isCreateModalOpen,
2836
3151
  onClose: () => setIsCreateModalOpen(false),
2837
3152
  onSubmit: async (input) => {
@@ -2840,7 +3155,7 @@ function CrmDashboard() {
2840
3155
  stages,
2841
3156
  isLoading: mutations.createState.loading
2842
3157
  }, undefined, false, undefined, this),
2843
- /* @__PURE__ */ jsxDEV5(DealActionsModal, {
3158
+ /* @__PURE__ */ jsxDEV6(DealActionsModal, {
2844
3159
  isOpen: isDealActionsOpen,
2845
3160
  deal: selectedDeal,
2846
3161
  stages,
@@ -2863,113 +3178,31 @@ function CrmDashboard() {
2863
3178
  ]
2864
3179
  }, undefined, true, undefined, this);
2865
3180
  }
2866
- function DealListTab({ data, onDealClick }) {
2867
- if (!data?.deals.length) {
2868
- return /* @__PURE__ */ jsxDEV5("div", {
2869
- className: "text-muted-foreground flex h-64 items-center justify-center",
2870
- children: "No deals found"
2871
- }, undefined, false, undefined, this);
2872
- }
2873
- return /* @__PURE__ */ jsxDEV5("div", {
2874
- className: "border-border rounded-lg border",
2875
- children: /* @__PURE__ */ jsxDEV5("table", {
2876
- className: "w-full",
2877
- children: [
2878
- /* @__PURE__ */ jsxDEV5("thead", {
2879
- className: "border-border bg-muted/30 border-b",
2880
- children: /* @__PURE__ */ jsxDEV5("tr", {
2881
- children: [
2882
- /* @__PURE__ */ jsxDEV5("th", {
2883
- className: "px-4 py-3 text-left text-sm font-medium",
2884
- children: "Deal"
2885
- }, undefined, false, undefined, this),
2886
- /* @__PURE__ */ jsxDEV5("th", {
2887
- className: "px-4 py-3 text-left text-sm font-medium",
2888
- children: "Value"
2889
- }, undefined, false, undefined, this),
2890
- /* @__PURE__ */ jsxDEV5("th", {
2891
- className: "px-4 py-3 text-left text-sm font-medium",
2892
- children: "Status"
2893
- }, undefined, false, undefined, this),
2894
- /* @__PURE__ */ jsxDEV5("th", {
2895
- className: "px-4 py-3 text-left text-sm font-medium",
2896
- children: "Expected Close"
2897
- }, undefined, false, undefined, this),
2898
- /* @__PURE__ */ jsxDEV5("th", {
2899
- className: "px-4 py-3 text-left text-sm font-medium",
2900
- children: "Actions"
2901
- }, undefined, false, undefined, this)
2902
- ]
2903
- }, undefined, true, undefined, this)
2904
- }, undefined, false, undefined, this),
2905
- /* @__PURE__ */ jsxDEV5("tbody", {
2906
- className: "divide-border divide-y",
2907
- children: data.deals.map((deal3) => /* @__PURE__ */ jsxDEV5("tr", {
2908
- className: "hover:bg-muted/50",
2909
- children: [
2910
- /* @__PURE__ */ jsxDEV5("td", {
2911
- className: "px-4 py-3",
2912
- children: /* @__PURE__ */ jsxDEV5("div", {
2913
- className: "font-medium",
2914
- children: deal3.name
2915
- }, undefined, false, undefined, this)
2916
- }, undefined, false, undefined, this),
2917
- /* @__PURE__ */ jsxDEV5("td", {
2918
- className: "px-4 py-3 font-mono",
2919
- children: formatCurrency4(deal3.value, deal3.currency)
2920
- }, undefined, false, undefined, this),
2921
- /* @__PURE__ */ jsxDEV5("td", {
2922
- className: "px-4 py-3",
2923
- children: /* @__PURE__ */ jsxDEV5("span", {
2924
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${deal3.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal3.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
2925
- children: deal3.status
2926
- }, undefined, false, undefined, this)
2927
- }, undefined, false, undefined, this),
2928
- /* @__PURE__ */ jsxDEV5("td", {
2929
- className: "text-muted-foreground px-4 py-3",
2930
- children: deal3.expectedCloseDate?.toLocaleDateString() ?? "-"
2931
- }, undefined, false, undefined, this),
2932
- /* @__PURE__ */ jsxDEV5("td", {
2933
- className: "px-4 py-3",
2934
- children: /* @__PURE__ */ jsxDEV5(Button3, {
2935
- variant: "ghost",
2936
- size: "sm",
2937
- onPress: () => onDealClick?.(deal3.id),
2938
- children: "Actions"
2939
- }, undefined, false, undefined, this)
2940
- }, undefined, false, undefined, this)
2941
- ]
2942
- }, deal3.id, true, undefined, this))
2943
- }, undefined, false, undefined, this)
2944
- ]
2945
- }, undefined, true, undefined, this)
2946
- }, undefined, false, undefined, this);
2947
- }
2948
3181
  function MetricsTab({
2949
3182
  stats
2950
3183
  }) {
2951
3184
  if (!stats)
2952
3185
  return null;
2953
- return /* @__PURE__ */ jsxDEV5("div", {
3186
+ return /* @__PURE__ */ jsxDEV6("div", {
2954
3187
  className: "space-y-6",
2955
- children: /* @__PURE__ */ jsxDEV5("div", {
2956
- className: "border-border bg-card rounded-xl border p-6",
3188
+ children: /* @__PURE__ */ jsxDEV6("div", {
3189
+ className: "rounded-xl border border-border bg-card p-6",
2957
3190
  children: [
2958
- /* @__PURE__ */ jsxDEV5("h3", {
2959
- className: "mb-4 text-lg font-semibold",
3191
+ /* @__PURE__ */ jsxDEV6("h3", {
3192
+ className: "mb-4 font-semibold text-lg",
2960
3193
  children: "Pipeline Overview"
2961
3194
  }, undefined, false, undefined, this),
2962
- /* @__PURE__ */ jsxDEV5("dl", {
3195
+ /* @__PURE__ */ jsxDEV6("dl", {
2963
3196
  className: "grid gap-4 sm:grid-cols-3",
2964
3197
  children: [
2965
- /* @__PURE__ */ jsxDEV5("div", {
3198
+ /* @__PURE__ */ jsxDEV6("div", {
2966
3199
  children: [
2967
- /* @__PURE__ */ jsxDEV5("dt", {
3200
+ /* @__PURE__ */ jsxDEV6("dt", {
2968
3201
  className: "text-muted-foreground text-sm",
2969
3202
  children: "Win Rate"
2970
3203
  }, undefined, false, undefined, this),
2971
- /* @__PURE__ */ jsxDEV5("dd", {
2972
- className: "text-2xl font-semibold",
3204
+ /* @__PURE__ */ jsxDEV6("dd", {
3205
+ className: "font-semibold text-2xl",
2973
3206
  children: [
2974
3207
  stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0,
2975
3208
  "%"
@@ -2977,26 +3210,26 @@ function MetricsTab({
2977
3210
  }, undefined, true, undefined, this)
2978
3211
  ]
2979
3212
  }, undefined, true, undefined, this),
2980
- /* @__PURE__ */ jsxDEV5("div", {
3213
+ /* @__PURE__ */ jsxDEV6("div", {
2981
3214
  children: [
2982
- /* @__PURE__ */ jsxDEV5("dt", {
3215
+ /* @__PURE__ */ jsxDEV6("dt", {
2983
3216
  className: "text-muted-foreground text-sm",
2984
3217
  children: "Avg Deal Size"
2985
3218
  }, undefined, false, undefined, this),
2986
- /* @__PURE__ */ jsxDEV5("dd", {
2987
- className: "text-2xl font-semibold",
2988
- children: formatCurrency4(stats.total > 0 ? stats.totalValue / stats.total : 0)
3219
+ /* @__PURE__ */ jsxDEV6("dd", {
3220
+ className: "font-semibold text-2xl",
3221
+ children: formatCurrency5(stats.total > 0 ? stats.totalValue / stats.total : 0)
2989
3222
  }, undefined, false, undefined, this)
2990
3223
  ]
2991
3224
  }, undefined, true, undefined, this),
2992
- /* @__PURE__ */ jsxDEV5("div", {
3225
+ /* @__PURE__ */ jsxDEV6("div", {
2993
3226
  children: [
2994
- /* @__PURE__ */ jsxDEV5("dt", {
3227
+ /* @__PURE__ */ jsxDEV6("dt", {
2995
3228
  className: "text-muted-foreground text-sm",
2996
3229
  children: "Conversion"
2997
3230
  }, undefined, false, undefined, this),
2998
- /* @__PURE__ */ jsxDEV5("dd", {
2999
- className: "text-2xl font-semibold",
3231
+ /* @__PURE__ */ jsxDEV6("dd", {
3232
+ className: "font-semibold text-2xl",
3000
3233
  children: [
3001
3234
  stats.wonCount,
3002
3235
  " / ",
@@ -3011,33 +3244,61 @@ function MetricsTab({
3011
3244
  }, undefined, true, undefined, this)
3012
3245
  }, undefined, false, undefined, this);
3013
3246
  }
3247
+
3014
3248
  // src/ui/hooks/index.ts
3015
3249
  "use client";
3016
-
3017
- // src/ui/renderers/pipeline.renderer.tsx
3018
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
3019
- function CrmPipelineBoardWrapper() {
3020
- const { dealsByStage, stages } = useDealList();
3021
- return /* @__PURE__ */ jsxDEV6(CrmPipelineBoard, {
3022
- dealsByStage,
3023
- stages
3024
- }, undefined, false, undefined, this);
3025
- }
3026
- var crmPipelineReactRenderer = {
3027
- target: "react",
3028
- render: async (desc, _ctx) => {
3029
- if (desc.source.type !== "component") {
3030
- throw new Error("Invalid source type");
3031
- }
3032
- if (desc.source.componentKey !== "CrmPipelineView") {
3033
- throw new Error(`Unknown component: ${desc.source.componentKey}`);
3250
+ // src/ui/overlays/demo-overlays.ts
3251
+ var crmDemoOverlay = {
3252
+ overlayId: "crm-pipeline.demo-user",
3253
+ version: "1.0.0",
3254
+ description: "Demo mode with sample data",
3255
+ appliesTo: {
3256
+ feature: "crm-pipeline",
3257
+ role: "demo"
3258
+ },
3259
+ modifications: [
3260
+ {
3261
+ type: "hideField",
3262
+ field: "importButton",
3263
+ reason: "Not available in demo"
3264
+ },
3265
+ {
3266
+ type: "hideField",
3267
+ field: "exportButton",
3268
+ reason: "Not available in demo"
3269
+ },
3270
+ {
3271
+ type: "addBadge",
3272
+ position: "header",
3273
+ label: "Demo Mode",
3274
+ variant: "warning"
3034
3275
  }
3035
- return /* @__PURE__ */ jsxDEV6(CrmPipelineBoardWrapper, {}, undefined, false, undefined, this);
3036
- }
3276
+ ]
3037
3277
  };
3038
-
3278
+ var crmSalesRepOverlay = {
3279
+ overlayId: "crm-pipeline.sales-rep",
3280
+ version: "1.0.0",
3281
+ description: "Sales rep focused view",
3282
+ appliesTo: {
3283
+ feature: "crm-pipeline",
3284
+ role: "sales-rep"
3285
+ },
3286
+ modifications: [
3287
+ {
3288
+ type: "hideField",
3289
+ field: "teamMetrics",
3290
+ reason: "Team metrics for managers only"
3291
+ },
3292
+ { type: "hideField", field: "pipelineSettings", reason: "Admin only" },
3293
+ { type: "renameLabel", field: "deals", newLabel: "My Deals" }
3294
+ ]
3295
+ };
3296
+ var crmOverlays = [
3297
+ crmDemoOverlay,
3298
+ crmSalesRepOverlay
3299
+ ];
3039
3300
  // src/ui/renderers/pipeline.markdown.ts
3040
- function formatCurrency5(value, currency = "USD") {
3301
+ function formatCurrency6(value, currency = "USD") {
3041
3302
  return new Intl.NumberFormat("en-US", {
3042
3303
  style: "currency",
3043
3304
  currency,
@@ -3064,7 +3325,7 @@ var crmPipelineMarkdownRenderer = {
3064
3325
  const lines = [
3065
3326
  "# CRM Pipeline",
3066
3327
  "",
3067
- `**Total Value**: ${formatCurrency5(dealsResult.totalValue)}`,
3328
+ `**Total Value**: ${formatCurrency6(dealsResult.totalValue)}`,
3068
3329
  `**Total Deals**: ${dealsResult.total}`,
3069
3330
  ""
3070
3331
  ];
@@ -3072,13 +3333,13 @@ var crmPipelineMarkdownRenderer = {
3072
3333
  const stageDeals = dealsByStage[stage.id] ?? [];
3073
3334
  const stageValue = stageDeals.reduce((sum, d) => sum + d.value, 0);
3074
3335
  lines.push(`## ${stage.name}`);
3075
- lines.push(`_${stageDeals.length} deals · ${formatCurrency5(stageValue)}_`);
3336
+ lines.push(`_${stageDeals.length} deals · ${formatCurrency6(stageValue)}_`);
3076
3337
  lines.push("");
3077
3338
  if (stageDeals.length === 0) {
3078
3339
  lines.push("_No deals_");
3079
3340
  } else {
3080
3341
  for (const deal3 of stageDeals) {
3081
- lines.push(`- **${deal3.name}** - ${formatCurrency5(deal3.value, deal3.currency)}`);
3342
+ lines.push(`- **${deal3.name}** - ${formatCurrency6(deal3.value, deal3.currency)}`);
3082
3343
  }
3083
3344
  }
3084
3345
  lines.push("");
@@ -3118,9 +3379,9 @@ var crmDashboardMarkdownRenderer = {
3118
3379
  "| Metric | Value |",
3119
3380
  "|--------|-------|",
3120
3381
  `| Total Deals | ${dealsResult.total} |`,
3121
- `| Pipeline Value | ${formatCurrency5(dealsResult.totalValue)} |`,
3122
- `| Open Deals | ${openDeals.length} (${formatCurrency5(openValue)}) |`,
3123
- `| Won Deals | ${wonDeals.length} (${formatCurrency5(wonValue)}) |`,
3382
+ `| Pipeline Value | ${formatCurrency6(dealsResult.totalValue)} |`,
3383
+ `| Open Deals | ${openDeals.length} (${formatCurrency6(openValue)}) |`,
3384
+ `| Won Deals | ${wonDeals.length} (${formatCurrency6(wonValue)}) |`,
3124
3385
  `| Lost Deals | ${lostDeals.length} |`,
3125
3386
  "",
3126
3387
  "## Pipeline Stages",
@@ -3131,7 +3392,7 @@ var crmDashboardMarkdownRenderer = {
3131
3392
  for (const stage of stageList.sort((a, b) => a.position - b.position)) {
3132
3393
  const stageDeals = openDeals.filter((d) => d.stageId === stage.id);
3133
3394
  const stageValue = stageDeals.reduce((sum, d) => sum + d.value, 0);
3134
- lines.push(`| ${stage.name} | ${stageDeals.length} | ${formatCurrency5(stageValue)} |`);
3395
+ lines.push(`| ${stage.name} | ${stageDeals.length} | ${formatCurrency6(stageValue)} |`);
3135
3396
  }
3136
3397
  lines.push("");
3137
3398
  lines.push("## Recent Deals");
@@ -3144,7 +3405,7 @@ var crmDashboardMarkdownRenderer = {
3144
3405
  lines.push("|------|-------|-------|--------|");
3145
3406
  for (const deal3 of recentDeals) {
3146
3407
  const stage = stageList.find((s) => s.id === deal3.stageId);
3147
- lines.push(`| ${deal3.name} | ${formatCurrency5(deal3.value, deal3.currency)} | ${stage?.name ?? "-"} | ${deal3.status} |`);
3408
+ lines.push(`| ${deal3.name} | ${formatCurrency6(deal3.value, deal3.currency)} | ${stage?.name ?? "-"} | ${deal3.status} |`);
3148
3409
  }
3149
3410
  }
3150
3411
  return {
@@ -3154,56 +3415,28 @@ var crmDashboardMarkdownRenderer = {
3154
3415
  };
3155
3416
  }
3156
3417
  };
3157
- // src/ui/overlays/demo-overlays.ts
3158
- var crmDemoOverlay = {
3159
- overlayId: "crm-pipeline.demo-user",
3160
- version: "1.0.0",
3161
- description: "Demo mode with sample data",
3162
- appliesTo: {
3163
- feature: "crm-pipeline",
3164
- role: "demo"
3165
- },
3166
- modifications: [
3167
- {
3168
- type: "hideField",
3169
- field: "importButton",
3170
- reason: "Not available in demo"
3171
- },
3172
- {
3173
- type: "hideField",
3174
- field: "exportButton",
3175
- reason: "Not available in demo"
3176
- },
3177
- {
3178
- type: "addBadge",
3179
- position: "header",
3180
- label: "Demo Mode",
3181
- variant: "warning"
3418
+
3419
+ // src/ui/renderers/pipeline.renderer.tsx
3420
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
3421
+ function CrmPipelineBoardWrapper() {
3422
+ const { dealsByStage, stages } = useDealList();
3423
+ return /* @__PURE__ */ jsxDEV7(CrmPipelineBoard, {
3424
+ dealsByStage,
3425
+ stages
3426
+ }, undefined, false, undefined, this);
3427
+ }
3428
+ var crmPipelineReactRenderer = {
3429
+ target: "react",
3430
+ render: async (desc, _ctx) => {
3431
+ if (desc.source.type !== "component") {
3432
+ throw new Error("Invalid source type");
3182
3433
  }
3183
- ]
3184
- };
3185
- var crmSalesRepOverlay = {
3186
- overlayId: "crm-pipeline.sales-rep",
3187
- version: "1.0.0",
3188
- description: "Sales rep focused view",
3189
- appliesTo: {
3190
- feature: "crm-pipeline",
3191
- role: "sales-rep"
3192
- },
3193
- modifications: [
3194
- {
3195
- type: "hideField",
3196
- field: "teamMetrics",
3197
- reason: "Team metrics for managers only"
3198
- },
3199
- { type: "hideField", field: "pipelineSettings", reason: "Admin only" },
3200
- { type: "renameLabel", field: "deals", newLabel: "My Deals" }
3201
- ]
3434
+ if (desc.source.componentKey !== "CrmPipelineView") {
3435
+ throw new Error(`Unknown component: ${desc.source.componentKey}`);
3436
+ }
3437
+ return /* @__PURE__ */ jsxDEV7(CrmPipelineBoardWrapper, {}, undefined, false, undefined, this);
3438
+ }
3202
3439
  };
3203
- var crmOverlays = [
3204
- crmDemoOverlay,
3205
- crmSalesRepOverlay
3206
- ];
3207
3440
  // src/index.ts
3208
3441
  import { identityRbacSchemaContribution } from "@contractspec/lib.identity-rbac";
3209
3442
  import { auditTrailSchemaContribution } from "@contractspec/module.audit-trail";