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