@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
@@ -22,6 +22,13 @@ function rowToDeal(row) {
22
22
  updatedAt: new Date(row.updatedAt)
23
23
  };
24
24
  }
25
+ var DEAL_SORT_COLUMNS = {
26
+ name: "name",
27
+ value: "value",
28
+ status: "status",
29
+ expectedCloseDate: "expectedCloseDate",
30
+ updatedAt: "updatedAt"
31
+ };
25
32
  function createCrmHandlers(db) {
26
33
  async function listDeals(input) {
27
34
  const {
@@ -32,7 +39,9 @@ function createCrmHandlers(db) {
32
39
  ownerId,
33
40
  search,
34
41
  limit = 20,
35
- offset = 0
42
+ offset = 0,
43
+ sortBy = "value",
44
+ sortDirection = "desc"
36
45
  } = input;
37
46
  let whereClause = "WHERE projectId = ?";
38
47
  const params = [projectId];
@@ -60,7 +69,9 @@ function createCrmHandlers(db) {
60
69
  const total = countResult[0]?.count ?? 0;
61
70
  const valueResult = (await db.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${whereClause}`, params)).rows;
62
71
  const totalValue = valueResult[0]?.total ?? 0;
63
- const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY value DESC LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
72
+ const orderByColumn = DEAL_SORT_COLUMNS[sortBy] ?? DEAL_SORT_COLUMNS.value;
73
+ const orderByDirection = sortDirection === "asc" ? "ASC" : "DESC";
74
+ const dealRows = (await db.query(`SELECT * FROM crm_deal ${whereClause} ORDER BY ${orderByColumn} ${orderByDirection} LIMIT ? OFFSET ?`, [...params, limit, offset])).rows;
64
75
  return {
65
76
  deals: dealRows.map(rowToDeal),
66
77
  total,
@@ -440,186 +451,6 @@ async function mockGetDealsByStageHandler(input) {
440
451
  async function mockGetPipelineStagesHandler(input) {
441
452
  return MOCK_STAGES.filter((s) => s.pipelineId === input.pipelineId);
442
453
  }
443
- // src/ui/hooks/useDealList.ts
444
- import { useCallback, useEffect, useMemo, useState } from "react";
445
- import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
446
- "use client";
447
- function useDealList(options = {}) {
448
- const { handlers, projectId } = useTemplateRuntime();
449
- const { crm: crm2 } = handlers;
450
- const [data, setData] = useState(null);
451
- const [dealsByStage, setDealsByStage] = useState({});
452
- const [stages, setStages] = useState([]);
453
- const [loading, setLoading] = useState(true);
454
- const [error, setError] = useState(null);
455
- const [page, setPage] = useState(1);
456
- const pipelineId = options.pipelineId ?? "pipeline-1";
457
- const fetchData = useCallback(async () => {
458
- setLoading(true);
459
- setError(null);
460
- try {
461
- const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
462
- crm2.listDeals({
463
- projectId,
464
- pipelineId,
465
- stageId: options.stageId,
466
- status: options.status === "all" ? undefined : options.status,
467
- search: options.search,
468
- limit: options.limit ?? 50,
469
- offset: (page - 1) * (options.limit ?? 50)
470
- }),
471
- crm2.getDealsByStage({ projectId, pipelineId }),
472
- crm2.getPipelineStages({ pipelineId })
473
- ]);
474
- setData(dealsResult);
475
- setDealsByStage(stageDealsResult);
476
- setStages(stagesResult);
477
- } catch (err) {
478
- setError(err instanceof Error ? err : new Error("Unknown error"));
479
- } finally {
480
- setLoading(false);
481
- }
482
- }, [
483
- crm2,
484
- projectId,
485
- pipelineId,
486
- options.stageId,
487
- options.status,
488
- options.search,
489
- options.limit,
490
- page
491
- ]);
492
- useEffect(() => {
493
- fetchData();
494
- }, [fetchData]);
495
- const stats = useMemo(() => {
496
- if (!data)
497
- return null;
498
- const open = data.deals.filter((d) => d.status === "OPEN");
499
- const won = data.deals.filter((d) => d.status === "WON");
500
- const lost = data.deals.filter((d) => d.status === "LOST");
501
- return {
502
- total: data.total,
503
- totalValue: data.totalValue,
504
- openCount: open.length,
505
- openValue: open.reduce((sum, d) => sum + d.value, 0),
506
- wonCount: won.length,
507
- wonValue: won.reduce((sum, d) => sum + d.value, 0),
508
- lostCount: lost.length
509
- };
510
- }, [data]);
511
- return {
512
- data,
513
- dealsByStage,
514
- stages,
515
- loading,
516
- error,
517
- stats,
518
- page,
519
- refetch: fetchData,
520
- nextPage: () => setPage((p) => p + 1),
521
- prevPage: () => page > 1 && setPage((p) => p - 1)
522
- };
523
- }
524
-
525
- // src/ui/hooks/useDealMutations.ts
526
- import { useCallback as useCallback2, useState as useState2 } from "react";
527
- import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
528
- function useDealMutations(options = {}) {
529
- const { handlers, projectId } = useTemplateRuntime2();
530
- const { crm: crm2 } = handlers;
531
- const [createState, setCreateState] = useState2({
532
- loading: false,
533
- error: null,
534
- data: null
535
- });
536
- const [moveState, setMoveState] = useState2({
537
- loading: false,
538
- error: null,
539
- data: null
540
- });
541
- const [winState, setWinState] = useState2({
542
- loading: false,
543
- error: null,
544
- data: null
545
- });
546
- const [loseState, setLoseState] = useState2({
547
- loading: false,
548
- error: null,
549
- data: null
550
- });
551
- const createDeal = useCallback2(async (input) => {
552
- setCreateState({ loading: true, error: null, data: null });
553
- try {
554
- const result = await crm2.createDeal(input, {
555
- projectId,
556
- ownerId: "user-1"
557
- });
558
- setCreateState({ loading: false, error: null, data: result });
559
- options.onSuccess?.();
560
- return result;
561
- } catch (err) {
562
- const error = err instanceof Error ? err : new Error("Failed to create deal");
563
- setCreateState({ loading: false, error, data: null });
564
- options.onError?.(error);
565
- return null;
566
- }
567
- }, [crm2, projectId, options]);
568
- const moveDeal = useCallback2(async (input) => {
569
- setMoveState({ loading: true, error: null, data: null });
570
- try {
571
- const result = await crm2.moveDeal(input);
572
- setMoveState({ loading: false, error: null, data: result });
573
- options.onSuccess?.();
574
- return result;
575
- } catch (err) {
576
- const error = err instanceof Error ? err : new Error("Failed to move deal");
577
- setMoveState({ loading: false, error, data: null });
578
- options.onError?.(error);
579
- return null;
580
- }
581
- }, [crm2, options]);
582
- const winDeal = useCallback2(async (input) => {
583
- setWinState({ loading: true, error: null, data: null });
584
- try {
585
- const result = await crm2.winDeal(input);
586
- setWinState({ loading: false, error: null, data: result });
587
- options.onSuccess?.();
588
- return result;
589
- } catch (err) {
590
- const error = err instanceof Error ? err : new Error("Failed to mark deal as won");
591
- setWinState({ loading: false, error, data: null });
592
- options.onError?.(error);
593
- return null;
594
- }
595
- }, [crm2, options]);
596
- const loseDeal = useCallback2(async (input) => {
597
- setLoseState({ loading: true, error: null, data: null });
598
- try {
599
- const result = await crm2.loseDeal(input);
600
- setLoseState({ loading: false, error: null, data: result });
601
- options.onSuccess?.();
602
- return result;
603
- } catch (err) {
604
- const error = err instanceof Error ? err : new Error("Failed to mark deal as lost");
605
- setLoseState({ loading: false, error, data: null });
606
- options.onError?.(error);
607
- return null;
608
- }
609
- }, [crm2, options]);
610
- return {
611
- createDeal,
612
- moveDeal,
613
- winDeal,
614
- loseDeal,
615
- createState,
616
- moveState,
617
- winState,
618
- loseState,
619
- isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
620
- };
621
- }
622
-
623
454
  // src/ui/CrmDealCard.tsx
624
455
  import { jsxDEV } from "react/jsx-dev-runtime";
625
456
  "use client";
@@ -635,7 +466,7 @@ function CrmDealCard({ deal, onClick }) {
635
466
  const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
636
467
  return /* @__PURE__ */ jsxDEV("div", {
637
468
  onClick,
638
- className: "border-border bg-card cursor-pointer rounded-lg border p-3 shadow-sm transition-shadow hover:shadow-md",
469
+ className: "cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",
639
470
  role: "button",
640
471
  tabIndex: 0,
641
472
  onKeyDown: (e) => {
@@ -644,22 +475,22 @@ function CrmDealCard({ deal, onClick }) {
644
475
  },
645
476
  children: [
646
477
  /* @__PURE__ */ jsxDEV("h4", {
647
- className: "leading-snug font-medium",
478
+ className: "font-medium leading-snug",
648
479
  children: deal.name
649
480
  }, undefined, false, undefined, this),
650
481
  /* @__PURE__ */ jsxDEV("div", {
651
- className: "text-primary mt-2 text-lg font-semibold",
482
+ className: "mt-2 font-semibold text-lg text-primary",
652
483
  children: formatCurrency(deal.value, deal.currency)
653
484
  }, undefined, false, undefined, this),
654
485
  /* @__PURE__ */ jsxDEV("div", {
655
- className: "text-muted-foreground mt-3 flex items-center justify-between text-xs",
486
+ className: "mt-3 flex items-center justify-between text-muted-foreground text-xs",
656
487
  children: [
657
488
  daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
658
489
  className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
659
490
  children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
660
491
  }, undefined, false, undefined, this),
661
492
  /* @__PURE__ */ jsxDEV("span", {
662
- className: `rounded px-1.5 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.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"}`,
493
+ className: `rounded px-1.5 py-0.5 font-medium text-xs ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.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"}`,
663
494
  children: deal.status
664
495
  }, undefined, false, undefined, this)
665
496
  ]
@@ -669,7 +500,7 @@ function CrmDealCard({ deal, onClick }) {
669
500
  }
670
501
 
671
502
  // src/ui/CrmPipelineBoard.tsx
672
- import { useState as useState3 } from "react";
503
+ import { useState } from "react";
673
504
  import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
674
505
  "use client";
675
506
  function formatCurrency2(value) {
@@ -685,7 +516,7 @@ function CrmPipelineBoard({
685
516
  onDealClick,
686
517
  onDealMove
687
518
  }) {
688
- const [quickMoveOpen, setQuickMoveOpen] = useState3(null);
519
+ const [quickMoveOpen, setQuickMoveOpen] = useState(null);
689
520
  const sortedStages = [...stages].sort((a, b) => a.position - b.position);
690
521
  const handleQuickMove = (dealId, toStageId) => {
691
522
  onDealMove?.(dealId, toStageId);
@@ -697,10 +528,10 @@ function CrmPipelineBoard({
697
528
  const deals = dealsByStage[stage.id] ?? [];
698
529
  const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
699
530
  return /* @__PURE__ */ jsxDEV2("div", {
700
- className: "bg-muted/30 flex w-72 flex-shrink-0 flex-col rounded-lg",
531
+ className: "flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",
701
532
  children: [
702
533
  /* @__PURE__ */ jsxDEV2("div", {
703
- className: "border-border flex items-center justify-between border-b px-3 py-2",
534
+ className: "flex items-center justify-between border-border border-b px-3 py-2",
704
535
  children: [
705
536
  /* @__PURE__ */ jsxDEV2("div", {
706
537
  children: [
@@ -719,7 +550,7 @@ function CrmPipelineBoard({
719
550
  ]
720
551
  }, undefined, true, undefined, this),
721
552
  /* @__PURE__ */ jsxDEV2("span", {
722
- className: "bg-muted flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium",
553
+ className: "flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",
723
554
  children: deals.length
724
555
  }, undefined, false, undefined, this)
725
556
  ]
@@ -727,7 +558,7 @@ function CrmPipelineBoard({
727
558
  /* @__PURE__ */ jsxDEV2("div", {
728
559
  className: "flex flex-1 flex-col gap-2 p-2",
729
560
  children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
730
- className: "border-muted-foreground/20 text-muted-foreground flex h-24 items-center justify-center rounded-md border-2 border-dashed text-xs",
561
+ className: "flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",
731
562
  children: "No deals"
732
563
  }, undefined, false, undefined, this) : deals.map((deal) => /* @__PURE__ */ jsxDEV2("div", {
733
564
  className: "group relative",
@@ -745,15 +576,15 @@ function CrmPipelineBoard({
745
576
  e.stopPropagation();
746
577
  setQuickMoveOpen(quickMoveOpen === deal.id ? null : deal.id);
747
578
  },
748
- className: "bg-background border-border hover:bg-muted flex h-6 w-6 items-center justify-center rounded border text-xs shadow-sm",
579
+ className: "flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",
749
580
  title: "Quick move",
750
581
  children: "➡️"
751
582
  }, undefined, false, undefined, this),
752
583
  quickMoveOpen === deal.id && /* @__PURE__ */ jsxDEV2("div", {
753
- className: "bg-card border-border absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border py-1 shadow-lg",
584
+ className: "absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",
754
585
  children: [
755
586
  /* @__PURE__ */ jsxDEV2("p", {
756
- className: "text-muted-foreground px-3 py-1 text-xs font-medium",
587
+ className: "px-3 py-1 font-medium text-muted-foreground text-xs",
757
588
  children: "Move to:"
758
589
  }, undefined, false, undefined, this),
759
590
  sortedStages.filter((s) => s.id !== deal.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
@@ -762,7 +593,7 @@ function CrmPipelineBoard({
762
593
  e.stopPropagation();
763
594
  handleQuickMove(deal.id, s.id);
764
595
  },
765
- className: "hover:bg-muted w-full px-3 py-1.5 text-left text-sm",
596
+ className: "w-full px-3 py-1.5 text-left text-sm hover:bg-muted",
766
597
  children: s.name
767
598
  }, s.id, false, undefined, this))
768
599
  ]
@@ -778,9 +609,200 @@ function CrmPipelineBoard({
778
609
  }, undefined, false, undefined, this);
779
610
  }
780
611
 
612
+ // src/ui/hooks/useDealList.ts
613
+ import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
614
+ import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
615
+ "use client";
616
+ function useDealList(options = {}) {
617
+ const { handlers, projectId } = useTemplateRuntime();
618
+ const { crm: crm2 } = handlers;
619
+ const [data, setData] = useState2(null);
620
+ const [dealsByStage, setDealsByStage] = useState2({});
621
+ const [stages, setStages] = useState2([]);
622
+ const [loading, setLoading] = useState2(true);
623
+ const [error, setError] = useState2(null);
624
+ const [internalPage, setInternalPage] = useState2(0);
625
+ const pipelineId = options.pipelineId ?? "pipeline-1";
626
+ const pageIndex = options.pageIndex ?? internalPage;
627
+ const pageSize = options.pageSize ?? options.limit ?? 50;
628
+ const [sort] = options.sorting ?? [];
629
+ const sortBy = sort?.id;
630
+ const sortDirection = sort ? sort.desc ? "desc" : "asc" : undefined;
631
+ const fetchData = useCallback(async () => {
632
+ setLoading(true);
633
+ setError(null);
634
+ try {
635
+ const [dealsResult, stageDealsResult, stagesResult] = await Promise.all([
636
+ crm2.listDeals({
637
+ projectId,
638
+ pipelineId,
639
+ stageId: options.stageId,
640
+ status: options.status === "all" ? undefined : options.status,
641
+ search: options.search,
642
+ limit: pageSize,
643
+ offset: pageIndex * pageSize,
644
+ sortBy: sortBy === "name" || sortBy === "value" || sortBy === "status" || sortBy === "expectedCloseDate" || sortBy === "updatedAt" ? sortBy : undefined,
645
+ sortDirection
646
+ }),
647
+ crm2.getDealsByStage({ projectId, pipelineId }),
648
+ crm2.getPipelineStages({ pipelineId })
649
+ ]);
650
+ setData(dealsResult);
651
+ setDealsByStage(stageDealsResult);
652
+ setStages(stagesResult);
653
+ } catch (err) {
654
+ setError(err instanceof Error ? err : new Error("Unknown error"));
655
+ } finally {
656
+ setLoading(false);
657
+ }
658
+ }, [
659
+ crm2,
660
+ projectId,
661
+ pipelineId,
662
+ options.stageId,
663
+ options.status,
664
+ options.search,
665
+ pageIndex,
666
+ pageSize,
667
+ sortBy,
668
+ sortDirection
669
+ ]);
670
+ useEffect(() => {
671
+ fetchData();
672
+ }, [fetchData]);
673
+ const stats = useMemo(() => {
674
+ if (!data)
675
+ return null;
676
+ const open = data.deals.filter((d) => d.status === "OPEN");
677
+ const won = data.deals.filter((d) => d.status === "WON");
678
+ const lost = data.deals.filter((d) => d.status === "LOST");
679
+ return {
680
+ total: data.total,
681
+ totalValue: data.totalValue,
682
+ openCount: open.length,
683
+ openValue: open.reduce((sum, d) => sum + d.value, 0),
684
+ wonCount: won.length,
685
+ wonValue: won.reduce((sum, d) => sum + d.value, 0),
686
+ lostCount: lost.length
687
+ };
688
+ }, [data]);
689
+ return {
690
+ data,
691
+ dealsByStage,
692
+ stages,
693
+ loading,
694
+ error,
695
+ stats,
696
+ page: pageIndex + 1,
697
+ pageIndex,
698
+ pageSize,
699
+ refetch: fetchData,
700
+ nextPage: options.pageIndex === undefined ? () => setInternalPage((page) => page + 1) : undefined,
701
+ prevPage: options.pageIndex === undefined ? () => pageIndex > 0 && setInternalPage((page) => page - 1) : undefined
702
+ };
703
+ }
704
+
705
+ // src/ui/hooks/useDealMutations.ts
706
+ import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
707
+ import { useCallback as useCallback2, useState as useState3 } from "react";
708
+ function useDealMutations(options = {}) {
709
+ const { handlers, projectId } = useTemplateRuntime2();
710
+ const { crm: crm2 } = handlers;
711
+ const [createState, setCreateState] = useState3({
712
+ loading: false,
713
+ error: null,
714
+ data: null
715
+ });
716
+ const [moveState, setMoveState] = useState3({
717
+ loading: false,
718
+ error: null,
719
+ data: null
720
+ });
721
+ const [winState, setWinState] = useState3({
722
+ loading: false,
723
+ error: null,
724
+ data: null
725
+ });
726
+ const [loseState, setLoseState] = useState3({
727
+ loading: false,
728
+ error: null,
729
+ data: null
730
+ });
731
+ const createDeal = useCallback2(async (input) => {
732
+ setCreateState({ loading: true, error: null, data: null });
733
+ try {
734
+ const result = await crm2.createDeal(input, {
735
+ projectId,
736
+ ownerId: "user-1"
737
+ });
738
+ setCreateState({ loading: false, error: null, data: result });
739
+ options.onSuccess?.();
740
+ return result;
741
+ } catch (err) {
742
+ const error = err instanceof Error ? err : new Error("Failed to create deal");
743
+ setCreateState({ loading: false, error, data: null });
744
+ options.onError?.(error);
745
+ return null;
746
+ }
747
+ }, [crm2, projectId, options]);
748
+ const moveDeal = useCallback2(async (input) => {
749
+ setMoveState({ loading: true, error: null, data: null });
750
+ try {
751
+ const result = await crm2.moveDeal(input);
752
+ setMoveState({ loading: false, error: null, data: result });
753
+ options.onSuccess?.();
754
+ return result;
755
+ } catch (err) {
756
+ const error = err instanceof Error ? err : new Error("Failed to move deal");
757
+ setMoveState({ loading: false, error, data: null });
758
+ options.onError?.(error);
759
+ return null;
760
+ }
761
+ }, [crm2, options]);
762
+ const winDeal = useCallback2(async (input) => {
763
+ setWinState({ loading: true, error: null, data: null });
764
+ try {
765
+ const result = await crm2.winDeal(input);
766
+ setWinState({ loading: false, error: null, data: result });
767
+ options.onSuccess?.();
768
+ return result;
769
+ } catch (err) {
770
+ const error = err instanceof Error ? err : new Error("Failed to mark deal as won");
771
+ setWinState({ loading: false, error, data: null });
772
+ options.onError?.(error);
773
+ return null;
774
+ }
775
+ }, [crm2, options]);
776
+ const loseDeal = useCallback2(async (input) => {
777
+ setLoseState({ loading: true, error: null, data: null });
778
+ try {
779
+ const result = await crm2.loseDeal(input);
780
+ setLoseState({ loading: false, error: null, data: result });
781
+ options.onSuccess?.();
782
+ return result;
783
+ } catch (err) {
784
+ const error = err instanceof Error ? err : new Error("Failed to mark deal as lost");
785
+ setLoseState({ loading: false, error, data: null });
786
+ options.onError?.(error);
787
+ return null;
788
+ }
789
+ }, [crm2, options]);
790
+ return {
791
+ createDeal,
792
+ moveDeal,
793
+ winDeal,
794
+ loseDeal,
795
+ createState,
796
+ moveState,
797
+ winState,
798
+ loseState,
799
+ isLoading: createState.loading || moveState.loading || winState.loading || loseState.loading
800
+ };
801
+ }
802
+
781
803
  // src/ui/modals/CreateDealModal.tsx
782
- import { useState as useState4 } from "react";
783
804
  import { Button, Input } from "@contractspec/lib.design-system";
805
+ import { useState as useState4 } from "react";
784
806
  import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
785
807
  "use client";
786
808
  var CURRENCIES = ["USD", "EUR", "GBP", "CAD"];
@@ -839,7 +861,7 @@ function CreateDealModal({
839
861
  className: "fixed inset-0 z-50 flex items-center justify-center",
840
862
  children: [
841
863
  /* @__PURE__ */ jsxDEV3("div", {
842
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
864
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
843
865
  onClick: onClose,
844
866
  role: "button",
845
867
  tabIndex: 0,
@@ -850,10 +872,10 @@ function CreateDealModal({
850
872
  "aria-label": "Close modal"
851
873
  }, undefined, false, undefined, this),
852
874
  /* @__PURE__ */ jsxDEV3("div", {
853
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
875
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
854
876
  children: [
855
877
  /* @__PURE__ */ jsxDEV3("h2", {
856
- className: "mb-4 text-xl font-semibold",
878
+ className: "mb-4 font-semibold text-xl",
857
879
  children: "Create New Deal"
858
880
  }, undefined, false, undefined, this),
859
881
  /* @__PURE__ */ jsxDEV3("form", {
@@ -864,7 +886,7 @@ function CreateDealModal({
864
886
  children: [
865
887
  /* @__PURE__ */ jsxDEV3("label", {
866
888
  htmlFor: "deal-name",
867
- className: "text-muted-foreground mb-1 block text-sm font-medium",
889
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
868
890
  children: "Deal Name *"
869
891
  }, undefined, false, undefined, this),
870
892
  /* @__PURE__ */ jsxDEV3(Input, {
@@ -884,7 +906,7 @@ function CreateDealModal({
884
906
  children: [
885
907
  /* @__PURE__ */ jsxDEV3("label", {
886
908
  htmlFor: "deal-value",
887
- className: "text-muted-foreground mb-1 block text-sm font-medium",
909
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
888
910
  children: "Value *"
889
911
  }, undefined, false, undefined, this),
890
912
  /* @__PURE__ */ jsxDEV3(Input, {
@@ -904,7 +926,7 @@ function CreateDealModal({
904
926
  children: [
905
927
  /* @__PURE__ */ jsxDEV3("label", {
906
928
  htmlFor: "deal-currency",
907
- className: "text-muted-foreground mb-1 block text-sm font-medium",
929
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
908
930
  children: "Currency"
909
931
  }, undefined, false, undefined, this),
910
932
  /* @__PURE__ */ jsxDEV3("select", {
@@ -912,7 +934,7 @@ function CreateDealModal({
912
934
  value: currency,
913
935
  onChange: (e) => setCurrency(e.target.value),
914
936
  disabled: isLoading,
915
- 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",
937
+ 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",
916
938
  children: CURRENCIES.map((c) => /* @__PURE__ */ jsxDEV3("option", {
917
939
  value: c,
918
940
  children: c
@@ -926,7 +948,7 @@ function CreateDealModal({
926
948
  children: [
927
949
  /* @__PURE__ */ jsxDEV3("label", {
928
950
  htmlFor: "deal-stage",
929
- className: "text-muted-foreground mb-1 block text-sm font-medium",
951
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
930
952
  children: "Pipeline Stage *"
931
953
  }, undefined, false, undefined, this),
932
954
  /* @__PURE__ */ jsxDEV3("select", {
@@ -934,7 +956,7 @@ function CreateDealModal({
934
956
  value: stageId,
935
957
  onChange: (e) => setStageId(e.target.value),
936
958
  disabled: isLoading,
937
- 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",
959
+ 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",
938
960
  children: stages.map((stage) => /* @__PURE__ */ jsxDEV3("option", {
939
961
  value: stage.id,
940
962
  children: stage.name
@@ -946,7 +968,7 @@ function CreateDealModal({
946
968
  children: [
947
969
  /* @__PURE__ */ jsxDEV3("label", {
948
970
  htmlFor: "deal-close-date",
949
- className: "text-muted-foreground mb-1 block text-sm font-medium",
971
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
950
972
  children: "Expected Close Date"
951
973
  }, undefined, false, undefined, this),
952
974
  /* @__PURE__ */ jsxDEV3(Input, {
@@ -959,7 +981,7 @@ function CreateDealModal({
959
981
  ]
960
982
  }, undefined, true, undefined, this),
961
983
  error && /* @__PURE__ */ jsxDEV3("div", {
962
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
984
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
963
985
  children: error
964
986
  }, undefined, false, undefined, this),
965
987
  /* @__PURE__ */ jsxDEV3("div", {
@@ -988,8 +1010,8 @@ function CreateDealModal({
988
1010
  }
989
1011
 
990
1012
  // src/ui/modals/DealActionsModal.tsx
991
- import { useState as useState5 } from "react";
992
1013
  import { Button as Button2 } from "@contractspec/lib.design-system";
1014
+ import { useState as useState5 } from "react";
993
1015
  import { jsxDEV as jsxDEV4, Fragment } from "react/jsx-dev-runtime";
994
1016
  "use client";
995
1017
  function formatCurrency3(value, currency) {
@@ -1090,7 +1112,7 @@ function DealActionsModal({
1090
1112
  className: "fixed inset-0 z-50 flex items-center justify-center",
1091
1113
  children: [
1092
1114
  /* @__PURE__ */ jsxDEV4("div", {
1093
- className: "bg-background/80 absolute inset-0 backdrop-blur-sm",
1115
+ className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
1094
1116
  onClick: handleClose,
1095
1117
  role: "button",
1096
1118
  tabIndex: 0,
@@ -1101,21 +1123,21 @@ function DealActionsModal({
1101
1123
  "aria-label": "Close modal"
1102
1124
  }, undefined, false, undefined, this),
1103
1125
  /* @__PURE__ */ jsxDEV4("div", {
1104
- className: "bg-card border-border relative z-10 w-full max-w-md rounded-xl border p-6 shadow-xl",
1126
+ className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
1105
1127
  children: [
1106
1128
  /* @__PURE__ */ jsxDEV4("div", {
1107
- className: "border-border mb-4 border-b pb-4",
1129
+ className: "mb-4 border-border border-b pb-4",
1108
1130
  children: [
1109
1131
  /* @__PURE__ */ jsxDEV4("h2", {
1110
- className: "text-xl font-semibold",
1132
+ className: "font-semibold text-xl",
1111
1133
  children: deal.name
1112
1134
  }, undefined, false, undefined, this),
1113
1135
  /* @__PURE__ */ jsxDEV4("p", {
1114
- className: "text-primary text-lg font-medium",
1136
+ className: "font-medium text-lg text-primary",
1115
1137
  children: formatCurrency3(deal.value, deal.currency)
1116
1138
  }, undefined, false, undefined, this),
1117
1139
  /* @__PURE__ */ jsxDEV4("span", {
1118
- className: `mt-2 inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.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"}`,
1140
+ className: `mt-2 inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.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"}`,
1119
1141
  children: deal.status
1120
1142
  }, undefined, false, undefined, this)
1121
1143
  ]
@@ -1167,7 +1189,7 @@ function DealActionsModal({
1167
1189
  ]
1168
1190
  }, undefined, true, undefined, this),
1169
1191
  deal.status !== "OPEN" && /* @__PURE__ */ jsxDEV4("p", {
1170
- className: "text-muted-foreground py-4 text-center",
1192
+ className: "py-4 text-center text-muted-foreground",
1171
1193
  children: [
1172
1194
  "This deal is already ",
1173
1195
  deal.status.toLowerCase(),
@@ -1192,14 +1214,14 @@ function DealActionsModal({
1192
1214
  children: [
1193
1215
  /* @__PURE__ */ jsxDEV4("label", {
1194
1216
  htmlFor: "won-source",
1195
- className: "text-muted-foreground mb-1 block text-sm font-medium",
1217
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
1196
1218
  children: "How did you win this deal?"
1197
1219
  }, undefined, false, undefined, this),
1198
1220
  /* @__PURE__ */ jsxDEV4("select", {
1199
1221
  id: "won-source",
1200
1222
  value: wonSource,
1201
1223
  onChange: (e) => setWonSource(e.target.value),
1202
- 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",
1224
+ 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",
1203
1225
  children: [
1204
1226
  /* @__PURE__ */ jsxDEV4("option", {
1205
1227
  value: "",
@@ -1233,7 +1255,7 @@ function DealActionsModal({
1233
1255
  children: [
1234
1256
  /* @__PURE__ */ jsxDEV4("label", {
1235
1257
  htmlFor: "win-notes",
1236
- className: "text-muted-foreground mb-1 block text-sm font-medium",
1258
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
1237
1259
  children: "Notes (optional)"
1238
1260
  }, undefined, false, undefined, this),
1239
1261
  /* @__PURE__ */ jsxDEV4("textarea", {
@@ -1242,12 +1264,12 @@ function DealActionsModal({
1242
1264
  onChange: (e) => setNotes(e.target.value),
1243
1265
  placeholder: "Any additional notes about the win...",
1244
1266
  rows: 3,
1245
- 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"
1267
+ 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"
1246
1268
  }, undefined, false, undefined, this)
1247
1269
  ]
1248
1270
  }, undefined, true, undefined, this),
1249
1271
  error && /* @__PURE__ */ jsxDEV4("div", {
1250
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
1272
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
1251
1273
  children: error
1252
1274
  }, undefined, false, undefined, this),
1253
1275
  /* @__PURE__ */ jsxDEV4("div", {
@@ -1275,14 +1297,14 @@ function DealActionsModal({
1275
1297
  children: [
1276
1298
  /* @__PURE__ */ jsxDEV4("label", {
1277
1299
  htmlFor: "lost-reason",
1278
- className: "text-muted-foreground mb-1 block text-sm font-medium",
1300
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
1279
1301
  children: "Why was this deal lost? *"
1280
1302
  }, undefined, false, undefined, this),
1281
1303
  /* @__PURE__ */ jsxDEV4("select", {
1282
1304
  id: "lost-reason",
1283
1305
  value: lostReason,
1284
1306
  onChange: (e) => setLostReason(e.target.value),
1285
- 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",
1307
+ 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",
1286
1308
  children: [
1287
1309
  /* @__PURE__ */ jsxDEV4("option", {
1288
1310
  value: "",
@@ -1324,7 +1346,7 @@ function DealActionsModal({
1324
1346
  children: [
1325
1347
  /* @__PURE__ */ jsxDEV4("label", {
1326
1348
  htmlFor: "lose-notes",
1327
- className: "text-muted-foreground mb-1 block text-sm font-medium",
1349
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
1328
1350
  children: "Notes (optional)"
1329
1351
  }, undefined, false, undefined, this),
1330
1352
  /* @__PURE__ */ jsxDEV4("textarea", {
@@ -1333,12 +1355,12 @@ function DealActionsModal({
1333
1355
  onChange: (e) => setNotes(e.target.value),
1334
1356
  placeholder: "Any additional details...",
1335
1357
  rows: 3,
1336
- 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"
1358
+ 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"
1337
1359
  }, undefined, false, undefined, this)
1338
1360
  ]
1339
1361
  }, undefined, true, undefined, this),
1340
1362
  error && /* @__PURE__ */ jsxDEV4("div", {
1341
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
1363
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
1342
1364
  children: error
1343
1365
  }, undefined, false, undefined, this),
1344
1366
  /* @__PURE__ */ jsxDEV4("div", {
@@ -1367,14 +1389,14 @@ function DealActionsModal({
1367
1389
  children: [
1368
1390
  /* @__PURE__ */ jsxDEV4("label", {
1369
1391
  htmlFor: "move-stage",
1370
- className: "text-muted-foreground mb-1 block text-sm font-medium",
1392
+ className: "mb-1 block font-medium text-muted-foreground text-sm",
1371
1393
  children: "Move to Stage"
1372
1394
  }, undefined, false, undefined, this),
1373
1395
  /* @__PURE__ */ jsxDEV4("select", {
1374
1396
  id: "move-stage",
1375
1397
  value: selectedStageId,
1376
1398
  onChange: (e) => setSelectedStageId(e.target.value),
1377
- 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",
1399
+ 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",
1378
1400
  children: stages.map((stage) => /* @__PURE__ */ jsxDEV4("option", {
1379
1401
  value: stage.id,
1380
1402
  children: [
@@ -1386,7 +1408,7 @@ function DealActionsModal({
1386
1408
  ]
1387
1409
  }, undefined, true, undefined, this),
1388
1410
  error && /* @__PURE__ */ jsxDEV4("div", {
1389
- className: "bg-destructive/10 text-destructive rounded-md p-3 text-sm",
1411
+ className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
1390
1412
  children: error
1391
1413
  }, undefined, false, undefined, this),
1392
1414
  /* @__PURE__ */ jsxDEV4("div", {
@@ -1413,12 +1435,305 @@ function DealActionsModal({
1413
1435
  }, undefined, true, undefined, this);
1414
1436
  }
1415
1437
 
1416
- // src/ui/CrmDashboard.tsx
1417
- import { useCallback as useCallback3, useState as useState6 } from "react";
1438
+ // src/ui/tables/DealListTab.tsx
1418
1439
  import {
1419
1440
  Button as Button3,
1441
+ DataTable,
1442
+ LoaderBlock
1443
+ } from "@contractspec/lib.design-system";
1444
+ import { useContractTable } from "@contractspec/lib.presentation-runtime-react";
1445
+ import { Badge } from "@contractspec/lib.ui-kit-web/ui/badge";
1446
+ import { HStack, VStack } from "@contractspec/lib.ui-kit-web/ui/stack";
1447
+ import { Text } from "@contractspec/lib.ui-kit-web/ui/text";
1448
+ import * as React from "react";
1449
+ import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
1450
+ "use client";
1451
+ function formatCurrency4(value, currency = "USD") {
1452
+ return new Intl.NumberFormat("en-US", {
1453
+ style: "currency",
1454
+ currency,
1455
+ minimumFractionDigits: 0,
1456
+ maximumFractionDigits: 0
1457
+ }).format(value);
1458
+ }
1459
+ function statusVariant(status) {
1460
+ switch (status) {
1461
+ case "WON":
1462
+ return "default";
1463
+ case "LOST":
1464
+ return "destructive";
1465
+ case "STALE":
1466
+ return "outline";
1467
+ default:
1468
+ return "secondary";
1469
+ }
1470
+ }
1471
+ function DealListDataTable({
1472
+ deals,
1473
+ totalItems,
1474
+ pageIndex,
1475
+ pageSize,
1476
+ sorting,
1477
+ loading,
1478
+ onSortingChange,
1479
+ onPaginationChange,
1480
+ onDealClick
1481
+ }) {
1482
+ const controller = useContractTable({
1483
+ data: deals,
1484
+ columns: [
1485
+ {
1486
+ id: "deal",
1487
+ header: "Deal",
1488
+ label: "Deal",
1489
+ accessor: (deal) => deal.name,
1490
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV5(VStack, {
1491
+ gap: "xs",
1492
+ children: [
1493
+ /* @__PURE__ */ jsxDEV5(Text, {
1494
+ className: "font-medium text-sm",
1495
+ children: item.name
1496
+ }, undefined, false, undefined, this),
1497
+ /* @__PURE__ */ jsxDEV5(Text, {
1498
+ className: "text-muted-foreground text-xs",
1499
+ children: item.companyId ?? "Unassigned company"
1500
+ }, undefined, false, undefined, this)
1501
+ ]
1502
+ }, undefined, true, undefined, this),
1503
+ size: 240,
1504
+ minSize: 180,
1505
+ canSort: true,
1506
+ canPin: true,
1507
+ canResize: true
1508
+ },
1509
+ {
1510
+ id: "value",
1511
+ header: "Value",
1512
+ label: "Value",
1513
+ accessorKey: "value",
1514
+ cell: ({ item }) => formatCurrency4(item.value, item.currency),
1515
+ align: "right",
1516
+ size: 140,
1517
+ canSort: true,
1518
+ canResize: true
1519
+ },
1520
+ {
1521
+ id: "status",
1522
+ header: "Status",
1523
+ label: "Status",
1524
+ accessorKey: "status",
1525
+ cell: ({ value }) => /* @__PURE__ */ jsxDEV5(Badge, {
1526
+ variant: statusVariant(value),
1527
+ children: String(value)
1528
+ }, undefined, false, undefined, this),
1529
+ size: 130,
1530
+ canSort: true,
1531
+ canHide: true,
1532
+ canPin: true,
1533
+ canResize: true
1534
+ },
1535
+ {
1536
+ id: "expectedCloseDate",
1537
+ header: "Expected Close",
1538
+ label: "Expected Close",
1539
+ accessor: (deal) => deal.expectedCloseDate?.toISOString() ?? "",
1540
+ cell: ({ item }) => item.expectedCloseDate?.toLocaleDateString() ?? "Not scheduled",
1541
+ size: 170,
1542
+ canSort: true,
1543
+ canHide: true,
1544
+ canResize: true
1545
+ },
1546
+ {
1547
+ id: "updatedAt",
1548
+ header: "Updated",
1549
+ label: "Updated",
1550
+ accessor: (deal) => deal.updatedAt.toISOString(),
1551
+ cell: ({ item }) => item.updatedAt.toLocaleDateString(),
1552
+ size: 140,
1553
+ canSort: true,
1554
+ canHide: true,
1555
+ canResize: true
1556
+ },
1557
+ {
1558
+ id: "actions",
1559
+ header: "Actions",
1560
+ label: "Actions",
1561
+ accessor: (deal) => deal.id,
1562
+ cell: ({ item }) => /* @__PURE__ */ jsxDEV5(Button3, {
1563
+ variant: "ghost",
1564
+ size: "sm",
1565
+ onPress: () => onDealClick?.(item.id),
1566
+ children: "Actions"
1567
+ }, undefined, false, undefined, this),
1568
+ size: 120,
1569
+ canSort: false,
1570
+ canHide: false,
1571
+ canPin: false,
1572
+ canResize: false
1573
+ }
1574
+ ],
1575
+ executionMode: "server",
1576
+ selectionMode: "multiple",
1577
+ totalItems,
1578
+ state: {
1579
+ sorting,
1580
+ pagination: {
1581
+ pageIndex,
1582
+ pageSize
1583
+ }
1584
+ },
1585
+ onSortingChange,
1586
+ onPaginationChange,
1587
+ initialState: {
1588
+ columnVisibility: { updatedAt: false },
1589
+ columnPinning: { left: ["deal", "status"], right: [] }
1590
+ },
1591
+ renderExpandedContent: (deal) => /* @__PURE__ */ jsxDEV5(VStack, {
1592
+ gap: "sm",
1593
+ className: "py-2",
1594
+ children: [
1595
+ /* @__PURE__ */ jsxDEV5(HStack, {
1596
+ justify: "between",
1597
+ children: [
1598
+ /* @__PURE__ */ jsxDEV5(Text, {
1599
+ className: "font-medium text-sm",
1600
+ children: "Owner"
1601
+ }, undefined, false, undefined, this),
1602
+ /* @__PURE__ */ jsxDEV5(Text, {
1603
+ className: "text-muted-foreground text-sm",
1604
+ children: deal.ownerId
1605
+ }, undefined, false, undefined, this)
1606
+ ]
1607
+ }, undefined, true, undefined, this),
1608
+ /* @__PURE__ */ jsxDEV5(HStack, {
1609
+ justify: "between",
1610
+ children: [
1611
+ /* @__PURE__ */ jsxDEV5(Text, {
1612
+ className: "font-medium text-sm",
1613
+ children: "Contact"
1614
+ }, undefined, false, undefined, this),
1615
+ /* @__PURE__ */ jsxDEV5(Text, {
1616
+ className: "text-muted-foreground text-sm",
1617
+ children: deal.contactId ?? "No linked contact"
1618
+ }, undefined, false, undefined, this)
1619
+ ]
1620
+ }, undefined, true, undefined, this),
1621
+ deal.wonSource ? /* @__PURE__ */ jsxDEV5(HStack, {
1622
+ justify: "between",
1623
+ children: [
1624
+ /* @__PURE__ */ jsxDEV5(Text, {
1625
+ className: "font-medium text-sm",
1626
+ children: "Won Source"
1627
+ }, undefined, false, undefined, this),
1628
+ /* @__PURE__ */ jsxDEV5(Text, {
1629
+ className: "text-muted-foreground text-sm",
1630
+ children: deal.wonSource
1631
+ }, undefined, false, undefined, this)
1632
+ ]
1633
+ }, undefined, true, undefined, this) : null,
1634
+ deal.lostReason ? /* @__PURE__ */ jsxDEV5(HStack, {
1635
+ justify: "between",
1636
+ children: [
1637
+ /* @__PURE__ */ jsxDEV5(Text, {
1638
+ className: "font-medium text-sm",
1639
+ children: "Lost Reason"
1640
+ }, undefined, false, undefined, this),
1641
+ /* @__PURE__ */ jsxDEV5(Text, {
1642
+ className: "text-muted-foreground text-sm",
1643
+ children: deal.lostReason
1644
+ }, undefined, false, undefined, this)
1645
+ ]
1646
+ }, undefined, true, undefined, this) : null,
1647
+ deal.notes ? /* @__PURE__ */ jsxDEV5(VStack, {
1648
+ gap: "xs",
1649
+ children: [
1650
+ /* @__PURE__ */ jsxDEV5(Text, {
1651
+ className: "font-medium text-sm",
1652
+ children: "Notes"
1653
+ }, undefined, false, undefined, this),
1654
+ /* @__PURE__ */ jsxDEV5(Text, {
1655
+ className: "text-muted-foreground text-sm",
1656
+ children: deal.notes
1657
+ }, undefined, false, undefined, this)
1658
+ ]
1659
+ }, undefined, true, undefined, this) : null
1660
+ ]
1661
+ }, undefined, true, undefined, this),
1662
+ getCanExpand: () => true
1663
+ });
1664
+ return /* @__PURE__ */ jsxDEV5(DataTable, {
1665
+ controller,
1666
+ title: "All Deals",
1667
+ description: "Server-mode table using the shared ContractSpec controller.",
1668
+ loading,
1669
+ toolbar: /* @__PURE__ */ jsxDEV5(HStack, {
1670
+ gap: "sm",
1671
+ className: "flex-wrap",
1672
+ children: [
1673
+ /* @__PURE__ */ jsxDEV5(Text, {
1674
+ className: "text-muted-foreground text-sm",
1675
+ children: [
1676
+ "Selected ",
1677
+ controller.selectedRowIds.length
1678
+ ]
1679
+ }, undefined, true, undefined, this),
1680
+ /* @__PURE__ */ jsxDEV5(Text, {
1681
+ className: "text-muted-foreground text-sm",
1682
+ children: [
1683
+ totalItems,
1684
+ " total deals"
1685
+ ]
1686
+ }, undefined, true, undefined, this)
1687
+ ]
1688
+ }, undefined, true, undefined, this),
1689
+ footer: `Page ${controller.pageIndex + 1} of ${controller.pageCount}`,
1690
+ emptyState: /* @__PURE__ */ jsxDEV5("div", {
1691
+ className: "rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",
1692
+ children: "No deals found"
1693
+ }, undefined, false, undefined, this)
1694
+ }, undefined, false, undefined, this);
1695
+ }
1696
+ function DealListTab({
1697
+ onDealClick
1698
+ }) {
1699
+ const [sorting, setSorting] = React.useState([
1700
+ { id: "value", desc: true }
1701
+ ]);
1702
+ const [pagination, setPagination] = React.useState({
1703
+ pageIndex: 0,
1704
+ pageSize: 3
1705
+ });
1706
+ const { data, loading } = useDealList({
1707
+ pageIndex: pagination.pageIndex,
1708
+ pageSize: pagination.pageSize,
1709
+ sorting
1710
+ });
1711
+ if (loading && !data) {
1712
+ return /* @__PURE__ */ jsxDEV5(LoaderBlock, {
1713
+ label: "Loading deals..."
1714
+ }, undefined, false, undefined, this);
1715
+ }
1716
+ return /* @__PURE__ */ jsxDEV5(DealListDataTable, {
1717
+ deals: data?.deals ?? [],
1718
+ totalItems: data?.total ?? 0,
1719
+ pageIndex: pagination.pageIndex,
1720
+ pageSize: pagination.pageSize,
1721
+ sorting,
1722
+ loading,
1723
+ onSortingChange: (nextSorting) => {
1724
+ setSorting(nextSorting);
1725
+ setPagination((current) => ({ ...current, pageIndex: 0 }));
1726
+ },
1727
+ onPaginationChange: setPagination,
1728
+ onDealClick
1729
+ }, undefined, false, undefined, this);
1730
+ }
1731
+
1732
+ // src/ui/CrmDashboard.tsx
1733
+ import {
1734
+ Button as Button4,
1420
1735
  ErrorState,
1421
- LoaderBlock,
1736
+ LoaderBlock as LoaderBlock2,
1422
1737
  StatCard,
1423
1738
  StatCardGroup
1424
1739
  } from "@contractspec/lib.design-system";
@@ -1428,9 +1743,10 @@ import {
1428
1743
  TabsList,
1429
1744
  TabsTrigger
1430
1745
  } from "@contractspec/lib.ui-kit-web/ui/tabs";
1431
- import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
1746
+ import { useCallback as useCallback3, useState as useState7 } from "react";
1747
+ import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1432
1748
  "use client";
1433
- function formatCurrency4(value, currency = "USD") {
1749
+ function formatCurrency5(value, currency = "USD") {
1434
1750
  return new Intl.NumberFormat("en-US", {
1435
1751
  style: "currency",
1436
1752
  currency,
@@ -1439,9 +1755,9 @@ function formatCurrency4(value, currency = "USD") {
1439
1755
  }).format(value);
1440
1756
  }
1441
1757
  function CrmDashboard() {
1442
- const [isCreateModalOpen, setIsCreateModalOpen] = useState6(false);
1443
- const [selectedDeal, setSelectedDeal] = useState6(null);
1444
- const [isDealActionsOpen, setIsDealActionsOpen] = useState6(false);
1758
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState7(false);
1759
+ const [selectedDeal, setSelectedDeal] = useState7(null);
1760
+ const [isDealActionsOpen, setIsDealActionsOpen] = useState7(false);
1445
1761
  const { data, dealsByStage, stages, loading, error, stats, refetch } = useDealList();
1446
1762
  const mutations = useDealMutations({
1447
1763
  onSuccess: () => {
@@ -1459,32 +1775,32 @@ function CrmDashboard() {
1459
1775
  await mutations.moveDeal({ dealId, stageId: toStageId });
1460
1776
  }, [mutations]);
1461
1777
  if (loading && !data) {
1462
- return /* @__PURE__ */ jsxDEV5(LoaderBlock, {
1778
+ return /* @__PURE__ */ jsxDEV6(LoaderBlock2, {
1463
1779
  label: "Loading CRM..."
1464
1780
  }, undefined, false, undefined, this);
1465
1781
  }
1466
1782
  if (error) {
1467
- return /* @__PURE__ */ jsxDEV5(ErrorState, {
1783
+ return /* @__PURE__ */ jsxDEV6(ErrorState, {
1468
1784
  title: "Failed to load CRM",
1469
1785
  description: error.message,
1470
1786
  onRetry: refetch,
1471
1787
  retryLabel: "Retry"
1472
1788
  }, undefined, false, undefined, this);
1473
1789
  }
1474
- return /* @__PURE__ */ jsxDEV5("div", {
1790
+ return /* @__PURE__ */ jsxDEV6("div", {
1475
1791
  className: "space-y-6",
1476
1792
  children: [
1477
- /* @__PURE__ */ jsxDEV5("div", {
1793
+ /* @__PURE__ */ jsxDEV6("div", {
1478
1794
  className: "flex items-center justify-between",
1479
1795
  children: [
1480
- /* @__PURE__ */ jsxDEV5("h2", {
1481
- className: "text-2xl font-bold",
1796
+ /* @__PURE__ */ jsxDEV6("h2", {
1797
+ className: "font-bold text-2xl",
1482
1798
  children: "CRM Pipeline"
1483
1799
  }, undefined, false, undefined, this),
1484
- /* @__PURE__ */ jsxDEV5(Button3, {
1800
+ /* @__PURE__ */ jsxDEV6(Button4, {
1485
1801
  onClick: () => setIsCreateModalOpen(true),
1486
1802
  children: [
1487
- /* @__PURE__ */ jsxDEV5("span", {
1803
+ /* @__PURE__ */ jsxDEV6("span", {
1488
1804
  className: "mr-2",
1489
1805
  children: "+"
1490
1806
  }, undefined, false, undefined, this),
@@ -1493,60 +1809,60 @@ function CrmDashboard() {
1493
1809
  }, undefined, true, undefined, this)
1494
1810
  ]
1495
1811
  }, undefined, true, undefined, this),
1496
- stats && /* @__PURE__ */ jsxDEV5(StatCardGroup, {
1812
+ stats && /* @__PURE__ */ jsxDEV6(StatCardGroup, {
1497
1813
  children: [
1498
- /* @__PURE__ */ jsxDEV5(StatCard, {
1814
+ /* @__PURE__ */ jsxDEV6(StatCard, {
1499
1815
  label: "Total Pipeline",
1500
- value: formatCurrency4(stats.totalValue),
1816
+ value: formatCurrency5(stats.totalValue),
1501
1817
  hint: `${stats.total} deals`
1502
1818
  }, undefined, false, undefined, this),
1503
- /* @__PURE__ */ jsxDEV5(StatCard, {
1819
+ /* @__PURE__ */ jsxDEV6(StatCard, {
1504
1820
  label: "Open Deals",
1505
- value: formatCurrency4(stats.openValue),
1821
+ value: formatCurrency5(stats.openValue),
1506
1822
  hint: `${stats.openCount} active`
1507
1823
  }, undefined, false, undefined, this),
1508
- /* @__PURE__ */ jsxDEV5(StatCard, {
1824
+ /* @__PURE__ */ jsxDEV6(StatCard, {
1509
1825
  label: "Won",
1510
- value: formatCurrency4(stats.wonValue),
1826
+ value: formatCurrency5(stats.wonValue),
1511
1827
  hint: `${stats.wonCount} closed`
1512
1828
  }, undefined, false, undefined, this),
1513
- /* @__PURE__ */ jsxDEV5(StatCard, {
1829
+ /* @__PURE__ */ jsxDEV6(StatCard, {
1514
1830
  label: "Lost",
1515
1831
  value: stats.lostCount,
1516
1832
  hint: "deals lost"
1517
1833
  }, undefined, false, undefined, this)
1518
1834
  ]
1519
1835
  }, undefined, true, undefined, this),
1520
- /* @__PURE__ */ jsxDEV5(Tabs, {
1836
+ /* @__PURE__ */ jsxDEV6(Tabs, {
1521
1837
  defaultValue: "pipeline",
1522
1838
  className: "w-full",
1523
1839
  children: [
1524
- /* @__PURE__ */ jsxDEV5(TabsList, {
1840
+ /* @__PURE__ */ jsxDEV6(TabsList, {
1525
1841
  children: [
1526
- /* @__PURE__ */ jsxDEV5(TabsTrigger, {
1842
+ /* @__PURE__ */ jsxDEV6(TabsTrigger, {
1527
1843
  value: "pipeline",
1528
1844
  children: [
1529
- /* @__PURE__ */ jsxDEV5("span", {
1845
+ /* @__PURE__ */ jsxDEV6("span", {
1530
1846
  className: "mr-2",
1531
1847
  children: "\uD83D\uDCCA"
1532
1848
  }, undefined, false, undefined, this),
1533
1849
  "Pipeline"
1534
1850
  ]
1535
1851
  }, undefined, true, undefined, this),
1536
- /* @__PURE__ */ jsxDEV5(TabsTrigger, {
1852
+ /* @__PURE__ */ jsxDEV6(TabsTrigger, {
1537
1853
  value: "list",
1538
1854
  children: [
1539
- /* @__PURE__ */ jsxDEV5("span", {
1855
+ /* @__PURE__ */ jsxDEV6("span", {
1540
1856
  className: "mr-2",
1541
1857
  children: "\uD83D\uDCCB"
1542
1858
  }, undefined, false, undefined, this),
1543
1859
  "All Deals"
1544
1860
  ]
1545
1861
  }, undefined, true, undefined, this),
1546
- /* @__PURE__ */ jsxDEV5(TabsTrigger, {
1862
+ /* @__PURE__ */ jsxDEV6(TabsTrigger, {
1547
1863
  value: "metrics",
1548
1864
  children: [
1549
- /* @__PURE__ */ jsxDEV5("span", {
1865
+ /* @__PURE__ */ jsxDEV6("span", {
1550
1866
  className: "mr-2",
1551
1867
  children: "\uD83D\uDCC8"
1552
1868
  }, undefined, false, undefined, this),
@@ -1555,34 +1871,33 @@ function CrmDashboard() {
1555
1871
  }, undefined, true, undefined, this)
1556
1872
  ]
1557
1873
  }, undefined, true, undefined, this),
1558
- /* @__PURE__ */ jsxDEV5(TabsContent, {
1874
+ /* @__PURE__ */ jsxDEV6(TabsContent, {
1559
1875
  value: "pipeline",
1560
1876
  className: "min-h-[400px]",
1561
- children: /* @__PURE__ */ jsxDEV5(CrmPipelineBoard, {
1877
+ children: /* @__PURE__ */ jsxDEV6(CrmPipelineBoard, {
1562
1878
  dealsByStage,
1563
1879
  stages,
1564
1880
  onDealClick: handleDealClick,
1565
1881
  onDealMove: handleDealMove
1566
1882
  }, undefined, false, undefined, this)
1567
1883
  }, undefined, false, undefined, this),
1568
- /* @__PURE__ */ jsxDEV5(TabsContent, {
1884
+ /* @__PURE__ */ jsxDEV6(TabsContent, {
1569
1885
  value: "list",
1570
1886
  className: "min-h-[400px]",
1571
- children: /* @__PURE__ */ jsxDEV5(DealListTab, {
1572
- data,
1887
+ children: /* @__PURE__ */ jsxDEV6(DealListTab, {
1573
1888
  onDealClick: handleDealClick
1574
1889
  }, undefined, false, undefined, this)
1575
1890
  }, undefined, false, undefined, this),
1576
- /* @__PURE__ */ jsxDEV5(TabsContent, {
1891
+ /* @__PURE__ */ jsxDEV6(TabsContent, {
1577
1892
  value: "metrics",
1578
1893
  className: "min-h-[400px]",
1579
- children: /* @__PURE__ */ jsxDEV5(MetricsTab, {
1894
+ children: /* @__PURE__ */ jsxDEV6(MetricsTab, {
1580
1895
  stats
1581
1896
  }, undefined, false, undefined, this)
1582
1897
  }, undefined, false, undefined, this)
1583
1898
  ]
1584
1899
  }, undefined, true, undefined, this),
1585
- /* @__PURE__ */ jsxDEV5(CreateDealModal, {
1900
+ /* @__PURE__ */ jsxDEV6(CreateDealModal, {
1586
1901
  isOpen: isCreateModalOpen,
1587
1902
  onClose: () => setIsCreateModalOpen(false),
1588
1903
  onSubmit: async (input) => {
@@ -1591,7 +1906,7 @@ function CrmDashboard() {
1591
1906
  stages,
1592
1907
  isLoading: mutations.createState.loading
1593
1908
  }, undefined, false, undefined, this),
1594
- /* @__PURE__ */ jsxDEV5(DealActionsModal, {
1909
+ /* @__PURE__ */ jsxDEV6(DealActionsModal, {
1595
1910
  isOpen: isDealActionsOpen,
1596
1911
  deal: selectedDeal,
1597
1912
  stages,
@@ -1614,113 +1929,31 @@ function CrmDashboard() {
1614
1929
  ]
1615
1930
  }, undefined, true, undefined, this);
1616
1931
  }
1617
- function DealListTab({ data, onDealClick }) {
1618
- if (!data?.deals.length) {
1619
- return /* @__PURE__ */ jsxDEV5("div", {
1620
- className: "text-muted-foreground flex h-64 items-center justify-center",
1621
- children: "No deals found"
1622
- }, undefined, false, undefined, this);
1623
- }
1624
- return /* @__PURE__ */ jsxDEV5("div", {
1625
- className: "border-border rounded-lg border",
1626
- children: /* @__PURE__ */ jsxDEV5("table", {
1627
- className: "w-full",
1628
- children: [
1629
- /* @__PURE__ */ jsxDEV5("thead", {
1630
- className: "border-border bg-muted/30 border-b",
1631
- children: /* @__PURE__ */ jsxDEV5("tr", {
1632
- children: [
1633
- /* @__PURE__ */ jsxDEV5("th", {
1634
- className: "px-4 py-3 text-left text-sm font-medium",
1635
- children: "Deal"
1636
- }, undefined, false, undefined, this),
1637
- /* @__PURE__ */ jsxDEV5("th", {
1638
- className: "px-4 py-3 text-left text-sm font-medium",
1639
- children: "Value"
1640
- }, undefined, false, undefined, this),
1641
- /* @__PURE__ */ jsxDEV5("th", {
1642
- className: "px-4 py-3 text-left text-sm font-medium",
1643
- children: "Status"
1644
- }, undefined, false, undefined, this),
1645
- /* @__PURE__ */ jsxDEV5("th", {
1646
- className: "px-4 py-3 text-left text-sm font-medium",
1647
- children: "Expected Close"
1648
- }, undefined, false, undefined, this),
1649
- /* @__PURE__ */ jsxDEV5("th", {
1650
- className: "px-4 py-3 text-left text-sm font-medium",
1651
- children: "Actions"
1652
- }, undefined, false, undefined, this)
1653
- ]
1654
- }, undefined, true, undefined, this)
1655
- }, undefined, false, undefined, this),
1656
- /* @__PURE__ */ jsxDEV5("tbody", {
1657
- className: "divide-border divide-y",
1658
- children: data.deals.map((deal) => /* @__PURE__ */ jsxDEV5("tr", {
1659
- className: "hover:bg-muted/50",
1660
- children: [
1661
- /* @__PURE__ */ jsxDEV5("td", {
1662
- className: "px-4 py-3",
1663
- children: /* @__PURE__ */ jsxDEV5("div", {
1664
- className: "font-medium",
1665
- children: deal.name
1666
- }, undefined, false, undefined, this)
1667
- }, undefined, false, undefined, this),
1668
- /* @__PURE__ */ jsxDEV5("td", {
1669
- className: "px-4 py-3 font-mono",
1670
- children: formatCurrency4(deal.value, deal.currency)
1671
- }, undefined, false, undefined, this),
1672
- /* @__PURE__ */ jsxDEV5("td", {
1673
- className: "px-4 py-3",
1674
- children: /* @__PURE__ */ jsxDEV5("span", {
1675
- className: `inline-flex rounded-full px-2 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.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"}`,
1676
- children: deal.status
1677
- }, undefined, false, undefined, this)
1678
- }, undefined, false, undefined, this),
1679
- /* @__PURE__ */ jsxDEV5("td", {
1680
- className: "text-muted-foreground px-4 py-3",
1681
- children: deal.expectedCloseDate?.toLocaleDateString() ?? "-"
1682
- }, undefined, false, undefined, this),
1683
- /* @__PURE__ */ jsxDEV5("td", {
1684
- className: "px-4 py-3",
1685
- children: /* @__PURE__ */ jsxDEV5(Button3, {
1686
- variant: "ghost",
1687
- size: "sm",
1688
- onPress: () => onDealClick?.(deal.id),
1689
- children: "Actions"
1690
- }, undefined, false, undefined, this)
1691
- }, undefined, false, undefined, this)
1692
- ]
1693
- }, deal.id, true, undefined, this))
1694
- }, undefined, false, undefined, this)
1695
- ]
1696
- }, undefined, true, undefined, this)
1697
- }, undefined, false, undefined, this);
1698
- }
1699
1932
  function MetricsTab({
1700
1933
  stats
1701
1934
  }) {
1702
1935
  if (!stats)
1703
1936
  return null;
1704
- return /* @__PURE__ */ jsxDEV5("div", {
1937
+ return /* @__PURE__ */ jsxDEV6("div", {
1705
1938
  className: "space-y-6",
1706
- children: /* @__PURE__ */ jsxDEV5("div", {
1707
- className: "border-border bg-card rounded-xl border p-6",
1939
+ children: /* @__PURE__ */ jsxDEV6("div", {
1940
+ className: "rounded-xl border border-border bg-card p-6",
1708
1941
  children: [
1709
- /* @__PURE__ */ jsxDEV5("h3", {
1710
- className: "mb-4 text-lg font-semibold",
1942
+ /* @__PURE__ */ jsxDEV6("h3", {
1943
+ className: "mb-4 font-semibold text-lg",
1711
1944
  children: "Pipeline Overview"
1712
1945
  }, undefined, false, undefined, this),
1713
- /* @__PURE__ */ jsxDEV5("dl", {
1946
+ /* @__PURE__ */ jsxDEV6("dl", {
1714
1947
  className: "grid gap-4 sm:grid-cols-3",
1715
1948
  children: [
1716
- /* @__PURE__ */ jsxDEV5("div", {
1949
+ /* @__PURE__ */ jsxDEV6("div", {
1717
1950
  children: [
1718
- /* @__PURE__ */ jsxDEV5("dt", {
1951
+ /* @__PURE__ */ jsxDEV6("dt", {
1719
1952
  className: "text-muted-foreground text-sm",
1720
1953
  children: "Win Rate"
1721
1954
  }, undefined, false, undefined, this),
1722
- /* @__PURE__ */ jsxDEV5("dd", {
1723
- className: "text-2xl font-semibold",
1955
+ /* @__PURE__ */ jsxDEV6("dd", {
1956
+ className: "font-semibold text-2xl",
1724
1957
  children: [
1725
1958
  stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0,
1726
1959
  "%"
@@ -1728,26 +1961,26 @@ function MetricsTab({
1728
1961
  }, undefined, true, undefined, this)
1729
1962
  ]
1730
1963
  }, undefined, true, undefined, this),
1731
- /* @__PURE__ */ jsxDEV5("div", {
1964
+ /* @__PURE__ */ jsxDEV6("div", {
1732
1965
  children: [
1733
- /* @__PURE__ */ jsxDEV5("dt", {
1966
+ /* @__PURE__ */ jsxDEV6("dt", {
1734
1967
  className: "text-muted-foreground text-sm",
1735
1968
  children: "Avg Deal Size"
1736
1969
  }, undefined, false, undefined, this),
1737
- /* @__PURE__ */ jsxDEV5("dd", {
1738
- className: "text-2xl font-semibold",
1739
- children: formatCurrency4(stats.total > 0 ? stats.totalValue / stats.total : 0)
1970
+ /* @__PURE__ */ jsxDEV6("dd", {
1971
+ className: "font-semibold text-2xl",
1972
+ children: formatCurrency5(stats.total > 0 ? stats.totalValue / stats.total : 0)
1740
1973
  }, undefined, false, undefined, this)
1741
1974
  ]
1742
1975
  }, undefined, true, undefined, this),
1743
- /* @__PURE__ */ jsxDEV5("div", {
1976
+ /* @__PURE__ */ jsxDEV6("div", {
1744
1977
  children: [
1745
- /* @__PURE__ */ jsxDEV5("dt", {
1978
+ /* @__PURE__ */ jsxDEV6("dt", {
1746
1979
  className: "text-muted-foreground text-sm",
1747
1980
  children: "Conversion"
1748
1981
  }, undefined, false, undefined, this),
1749
- /* @__PURE__ */ jsxDEV5("dd", {
1750
- className: "text-2xl font-semibold",
1982
+ /* @__PURE__ */ jsxDEV6("dd", {
1983
+ className: "font-semibold text-2xl",
1751
1984
  children: [
1752
1985
  stats.wonCount,
1753
1986
  " / ",
@@ -1762,33 +1995,61 @@ function MetricsTab({
1762
1995
  }, undefined, true, undefined, this)
1763
1996
  }, undefined, false, undefined, this);
1764
1997
  }
1998
+
1765
1999
  // src/ui/hooks/index.ts
1766
2000
  "use client";
1767
-
1768
- // src/ui/renderers/pipeline.renderer.tsx
1769
- import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
1770
- function CrmPipelineBoardWrapper() {
1771
- const { dealsByStage, stages } = useDealList();
1772
- return /* @__PURE__ */ jsxDEV6(CrmPipelineBoard, {
1773
- dealsByStage,
1774
- stages
1775
- }, undefined, false, undefined, this);
1776
- }
1777
- var crmPipelineReactRenderer = {
1778
- target: "react",
1779
- render: async (desc, _ctx) => {
1780
- if (desc.source.type !== "component") {
1781
- throw new Error("Invalid source type");
1782
- }
1783
- if (desc.source.componentKey !== "CrmPipelineView") {
1784
- throw new Error(`Unknown component: ${desc.source.componentKey}`);
2001
+ // src/ui/overlays/demo-overlays.ts
2002
+ var crmDemoOverlay = {
2003
+ overlayId: "crm-pipeline.demo-user",
2004
+ version: "1.0.0",
2005
+ description: "Demo mode with sample data",
2006
+ appliesTo: {
2007
+ feature: "crm-pipeline",
2008
+ role: "demo"
2009
+ },
2010
+ modifications: [
2011
+ {
2012
+ type: "hideField",
2013
+ field: "importButton",
2014
+ reason: "Not available in demo"
2015
+ },
2016
+ {
2017
+ type: "hideField",
2018
+ field: "exportButton",
2019
+ reason: "Not available in demo"
2020
+ },
2021
+ {
2022
+ type: "addBadge",
2023
+ position: "header",
2024
+ label: "Demo Mode",
2025
+ variant: "warning"
1785
2026
  }
1786
- return /* @__PURE__ */ jsxDEV6(CrmPipelineBoardWrapper, {}, undefined, false, undefined, this);
1787
- }
2027
+ ]
1788
2028
  };
1789
-
2029
+ var crmSalesRepOverlay = {
2030
+ overlayId: "crm-pipeline.sales-rep",
2031
+ version: "1.0.0",
2032
+ description: "Sales rep focused view",
2033
+ appliesTo: {
2034
+ feature: "crm-pipeline",
2035
+ role: "sales-rep"
2036
+ },
2037
+ modifications: [
2038
+ {
2039
+ type: "hideField",
2040
+ field: "teamMetrics",
2041
+ reason: "Team metrics for managers only"
2042
+ },
2043
+ { type: "hideField", field: "pipelineSettings", reason: "Admin only" },
2044
+ { type: "renameLabel", field: "deals", newLabel: "My Deals" }
2045
+ ]
2046
+ };
2047
+ var crmOverlays = [
2048
+ crmDemoOverlay,
2049
+ crmSalesRepOverlay
2050
+ ];
1790
2051
  // src/ui/renderers/pipeline.markdown.ts
1791
- function formatCurrency5(value, currency = "USD") {
2052
+ function formatCurrency6(value, currency = "USD") {
1792
2053
  return new Intl.NumberFormat("en-US", {
1793
2054
  style: "currency",
1794
2055
  currency,
@@ -1815,7 +2076,7 @@ var crmPipelineMarkdownRenderer = {
1815
2076
  const lines = [
1816
2077
  "# CRM Pipeline",
1817
2078
  "",
1818
- `**Total Value**: ${formatCurrency5(dealsResult.totalValue)}`,
2079
+ `**Total Value**: ${formatCurrency6(dealsResult.totalValue)}`,
1819
2080
  `**Total Deals**: ${dealsResult.total}`,
1820
2081
  ""
1821
2082
  ];
@@ -1823,13 +2084,13 @@ var crmPipelineMarkdownRenderer = {
1823
2084
  const stageDeals = dealsByStage[stage.id] ?? [];
1824
2085
  const stageValue = stageDeals.reduce((sum, d) => sum + d.value, 0);
1825
2086
  lines.push(`## ${stage.name}`);
1826
- lines.push(`_${stageDeals.length} deals · ${formatCurrency5(stageValue)}_`);
2087
+ lines.push(`_${stageDeals.length} deals · ${formatCurrency6(stageValue)}_`);
1827
2088
  lines.push("");
1828
2089
  if (stageDeals.length === 0) {
1829
2090
  lines.push("_No deals_");
1830
2091
  } else {
1831
2092
  for (const deal of stageDeals) {
1832
- lines.push(`- **${deal.name}** - ${formatCurrency5(deal.value, deal.currency)}`);
2093
+ lines.push(`- **${deal.name}** - ${formatCurrency6(deal.value, deal.currency)}`);
1833
2094
  }
1834
2095
  }
1835
2096
  lines.push("");
@@ -1869,9 +2130,9 @@ var crmDashboardMarkdownRenderer = {
1869
2130
  "| Metric | Value |",
1870
2131
  "|--------|-------|",
1871
2132
  `| Total Deals | ${dealsResult.total} |`,
1872
- `| Pipeline Value | ${formatCurrency5(dealsResult.totalValue)} |`,
1873
- `| Open Deals | ${openDeals.length} (${formatCurrency5(openValue)}) |`,
1874
- `| Won Deals | ${wonDeals.length} (${formatCurrency5(wonValue)}) |`,
2133
+ `| Pipeline Value | ${formatCurrency6(dealsResult.totalValue)} |`,
2134
+ `| Open Deals | ${openDeals.length} (${formatCurrency6(openValue)}) |`,
2135
+ `| Won Deals | ${wonDeals.length} (${formatCurrency6(wonValue)}) |`,
1875
2136
  `| Lost Deals | ${lostDeals.length} |`,
1876
2137
  "",
1877
2138
  "## Pipeline Stages",
@@ -1882,7 +2143,7 @@ var crmDashboardMarkdownRenderer = {
1882
2143
  for (const stage of stageList.sort((a, b) => a.position - b.position)) {
1883
2144
  const stageDeals = openDeals.filter((d) => d.stageId === stage.id);
1884
2145
  const stageValue = stageDeals.reduce((sum, d) => sum + d.value, 0);
1885
- lines.push(`| ${stage.name} | ${stageDeals.length} | ${formatCurrency5(stageValue)} |`);
2146
+ lines.push(`| ${stage.name} | ${stageDeals.length} | ${formatCurrency6(stageValue)} |`);
1886
2147
  }
1887
2148
  lines.push("");
1888
2149
  lines.push("## Recent Deals");
@@ -1895,7 +2156,7 @@ var crmDashboardMarkdownRenderer = {
1895
2156
  lines.push("|------|-------|-------|--------|");
1896
2157
  for (const deal of recentDeals) {
1897
2158
  const stage = stageList.find((s) => s.id === deal.stageId);
1898
- lines.push(`| ${deal.name} | ${formatCurrency5(deal.value, deal.currency)} | ${stage?.name ?? "-"} | ${deal.status} |`);
2159
+ lines.push(`| ${deal.name} | ${formatCurrency6(deal.value, deal.currency)} | ${stage?.name ?? "-"} | ${deal.status} |`);
1899
2160
  }
1900
2161
  }
1901
2162
  return {
@@ -1905,56 +2166,28 @@ var crmDashboardMarkdownRenderer = {
1905
2166
  };
1906
2167
  }
1907
2168
  };
1908
- // src/ui/overlays/demo-overlays.ts
1909
- var crmDemoOverlay = {
1910
- overlayId: "crm-pipeline.demo-user",
1911
- version: "1.0.0",
1912
- description: "Demo mode with sample data",
1913
- appliesTo: {
1914
- feature: "crm-pipeline",
1915
- role: "demo"
1916
- },
1917
- modifications: [
1918
- {
1919
- type: "hideField",
1920
- field: "importButton",
1921
- reason: "Not available in demo"
1922
- },
1923
- {
1924
- type: "hideField",
1925
- field: "exportButton",
1926
- reason: "Not available in demo"
1927
- },
1928
- {
1929
- type: "addBadge",
1930
- position: "header",
1931
- label: "Demo Mode",
1932
- variant: "warning"
2169
+
2170
+ // src/ui/renderers/pipeline.renderer.tsx
2171
+ import { jsxDEV as jsxDEV7 } from "react/jsx-dev-runtime";
2172
+ function CrmPipelineBoardWrapper() {
2173
+ const { dealsByStage, stages } = useDealList();
2174
+ return /* @__PURE__ */ jsxDEV7(CrmPipelineBoard, {
2175
+ dealsByStage,
2176
+ stages
2177
+ }, undefined, false, undefined, this);
2178
+ }
2179
+ var crmPipelineReactRenderer = {
2180
+ target: "react",
2181
+ render: async (desc, _ctx) => {
2182
+ if (desc.source.type !== "component") {
2183
+ throw new Error("Invalid source type");
1933
2184
  }
1934
- ]
1935
- };
1936
- var crmSalesRepOverlay = {
1937
- overlayId: "crm-pipeline.sales-rep",
1938
- version: "1.0.0",
1939
- description: "Sales rep focused view",
1940
- appliesTo: {
1941
- feature: "crm-pipeline",
1942
- role: "sales-rep"
1943
- },
1944
- modifications: [
1945
- {
1946
- type: "hideField",
1947
- field: "teamMetrics",
1948
- reason: "Team metrics for managers only"
1949
- },
1950
- { type: "hideField", field: "pipelineSettings", reason: "Admin only" },
1951
- { type: "renameLabel", field: "deals", newLabel: "My Deals" }
1952
- ]
2185
+ if (desc.source.componentKey !== "CrmPipelineView") {
2186
+ throw new Error(`Unknown component: ${desc.source.componentKey}`);
2187
+ }
2188
+ return /* @__PURE__ */ jsxDEV7(CrmPipelineBoardWrapper, {}, undefined, false, undefined, this);
2189
+ }
1953
2190
  };
1954
- var crmOverlays = [
1955
- crmDemoOverlay,
1956
- crmSalesRepOverlay
1957
- ];
1958
2191
  export {
1959
2192
  useDealMutations,
1960
2193
  useDealList,