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