@open-mercato/core 0.6.3-develop.3651.1.d3a548a5f1 → 0.6.3-develop.3653.1.b8558737ae

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 (160) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/customer_settings/index.js +2 -0
  3. package/dist/generated/entities/customer_settings/index.js.map +2 -2
  4. package/dist/generated/entity-fields-registry.js +1 -0
  5. package/dist/generated/entity-fields-registry.js.map +2 -2
  6. package/dist/modules/customers/api/deals/aggregate/route.js +332 -0
  7. package/dist/modules/customers/api/deals/aggregate/route.js.map +7 -0
  8. package/dist/modules/customers/api/deals/bulk-update-owner/route.js +188 -0
  9. package/dist/modules/customers/api/deals/bulk-update-owner/route.js.map +7 -0
  10. package/dist/modules/customers/api/deals/bulk-update-stage/route.js +188 -0
  11. package/dist/modules/customers/api/deals/bulk-update-stage/route.js.map +7 -0
  12. package/dist/modules/customers/api/deals/route.js +255 -122
  13. package/dist/modules/customers/api/deals/route.js.map +2 -2
  14. package/dist/modules/customers/api/settings/stuck-threshold/route.js +169 -0
  15. package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +7 -0
  16. package/dist/modules/customers/backend/config/customers/deals/page.js +181 -0
  17. package/dist/modules/customers/backend/config/customers/deals/page.js.map +7 -0
  18. package/dist/modules/customers/backend/config/customers/deals/page.meta.js +38 -0
  19. package/dist/modules/customers/backend/config/customers/deals/page.meta.js.map +7 -0
  20. package/dist/modules/customers/backend/customers/deals/create/page.js +17 -6
  21. package/dist/modules/customers/backend/customers/deals/create/page.js.map +2 -2
  22. package/dist/modules/customers/backend/customers/deals/page.js +2 -0
  23. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  24. package/dist/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.js +217 -0
  25. package/dist/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.js.map +7 -0
  26. package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.js +335 -0
  27. package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.js.map +7 -0
  28. package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.js +29 -0
  29. package/dist/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.js.map +7 -0
  30. package/dist/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.js +160 -0
  31. package/dist/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.js.map +7 -0
  32. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js +157 -0
  33. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js.map +7 -0
  34. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.js +114 -0
  35. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.js.map +7 -0
  36. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChipButton.js +36 -0
  37. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChipButton.js.map +7 -0
  38. package/dist/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.js +199 -0
  39. package/dist/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.js.map +7 -0
  40. package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.js +71 -0
  41. package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.js.map +7 -0
  42. package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.js +233 -0
  43. package/dist/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.js.map +7 -0
  44. package/dist/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.js +126 -0
  45. package/dist/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.js.map +7 -0
  46. package/dist/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.js +31 -0
  47. package/dist/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.js.map +7 -0
  48. package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCard.js +389 -0
  49. package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCard.js.map +7 -0
  50. package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.js +181 -0
  51. package/dist/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.js.map +7 -0
  52. package/dist/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.js +199 -0
  53. package/dist/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.js.map +7 -0
  54. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js +44 -0
  55. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.js.map +7 -0
  56. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.js +70 -0
  57. package/dist/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.js.map +7 -0
  58. package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +278 -0
  59. package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +7 -0
  60. package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.js +135 -0
  61. package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.js.map +7 -0
  62. package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.js +83 -0
  63. package/dist/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.js.map +7 -0
  64. package/dist/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.js +155 -0
  65. package/dist/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.js.map +7 -0
  66. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js +318 -0
  67. package/dist/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.js.map +7 -0
  68. package/dist/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.js +138 -0
  69. package/dist/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.js.map +7 -0
  70. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +126 -0
  71. package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +7 -0
  72. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js +71 -0
  73. package/dist/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.js.map +7 -0
  74. package/dist/modules/customers/backend/customers/deals/pipeline/components/constants.js +7 -0
  75. package/dist/modules/customers/backend/customers/deals/pipeline/components/constants.js.map +7 -0
  76. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +2330 -415
  77. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +3 -3
  78. package/dist/modules/customers/commands/deals.js +6 -0
  79. package/dist/modules/customers/commands/deals.js.map +2 -2
  80. package/dist/modules/customers/commands/pipeline-stages.js +48 -13
  81. package/dist/modules/customers/commands/pipeline-stages.js.map +3 -3
  82. package/dist/modules/customers/commands/settings.js +45 -5
  83. package/dist/modules/customers/commands/settings.js.map +2 -2
  84. package/dist/modules/customers/data/enrichers.js +130 -1
  85. package/dist/modules/customers/data/enrichers.js.map +2 -2
  86. package/dist/modules/customers/data/entities.js +4 -0
  87. package/dist/modules/customers/data/entities.js.map +2 -2
  88. package/dist/modules/customers/data/validators.js +29 -2
  89. package/dist/modules/customers/data/validators.js.map +2 -2
  90. package/dist/modules/customers/lib/bulkDeals.js +150 -0
  91. package/dist/modules/customers/lib/bulkDeals.js.map +7 -0
  92. package/dist/modules/customers/lib/kysely.js +12 -0
  93. package/dist/modules/customers/lib/kysely.js.map +7 -0
  94. package/dist/modules/customers/lib/stuckDeals.js +32 -0
  95. package/dist/modules/customers/lib/stuckDeals.js.map +7 -0
  96. package/dist/modules/customers/migrations/Migration20260513203311_customers.js +13 -0
  97. package/dist/modules/customers/migrations/Migration20260513203311_customers.js.map +7 -0
  98. package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js +52 -0
  99. package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js.map +7 -0
  100. package/dist/modules/customers/workers/deals-bulk-update-owner.js +41 -0
  101. package/dist/modules/customers/workers/deals-bulk-update-owner.js.map +7 -0
  102. package/dist/modules/customers/workers/deals-bulk-update-stage.js +41 -0
  103. package/dist/modules/customers/workers/deals-bulk-update-stage.js.map +7 -0
  104. package/generated/entities/customer_settings/index.ts +1 -0
  105. package/generated/entity-fields-registry.ts +1 -0
  106. package/package.json +7 -7
  107. package/src/modules/customers/api/deals/aggregate/route.ts +438 -0
  108. package/src/modules/customers/api/deals/bulk-update-owner/route.ts +216 -0
  109. package/src/modules/customers/api/deals/bulk-update-stage/route.ts +228 -0
  110. package/src/modules/customers/api/deals/route.ts +330 -135
  111. package/src/modules/customers/api/settings/stuck-threshold/route.ts +195 -0
  112. package/src/modules/customers/backend/config/customers/deals/page.meta.ts +36 -0
  113. package/src/modules/customers/backend/config/customers/deals/page.tsx +205 -0
  114. package/src/modules/customers/backend/customers/deals/create/page.tsx +25 -6
  115. package/src/modules/customers/backend/customers/deals/page.tsx +2 -0
  116. package/src/modules/customers/backend/customers/deals/pipeline/components/ActivityComposerDialog.tsx +277 -0
  117. package/src/modules/customers/backend/customers/deals/pipeline/components/AddStageDialog.tsx +425 -0
  118. package/src/modules/customers/backend/customers/deals/pipeline/components/AddStageLane.tsx +31 -0
  119. package/src/modules/customers/backend/customers/deals/pipeline/components/BulkActionsBar.tsx +171 -0
  120. package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.tsx +196 -0
  121. package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeStageDialog.tsx +138 -0
  122. package/src/modules/customers/backend/customers/deals/pipeline/components/ChipButton.tsx +47 -0
  123. package/src/modules/customers/backend/customers/deals/pipeline/components/CloseDateFilterPopover.tsx +232 -0
  124. package/src/modules/customers/backend/customers/deals/pipeline/components/CurrencyBreakdownTable.tsx +110 -0
  125. package/src/modules/customers/backend/customers/deals/pipeline/components/CurrencyFilterPopover.tsx +310 -0
  126. package/src/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.tsx +169 -0
  127. package/src/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.tsx +54 -0
  128. package/src/modules/customers/backend/customers/deals/pipeline/components/DealCard.tsx +562 -0
  129. package/src/modules/customers/backend/customers/deals/pipeline/components/DealCardMenu.tsx +214 -0
  130. package/src/modules/customers/backend/customers/deals/pipeline/components/EntityFilterPopover.tsx +257 -0
  131. package/src/modules/customers/backend/customers/deals/pipeline/components/FilterBarRow.tsx +62 -0
  132. package/src/modules/customers/backend/customers/deals/pipeline/components/FilterPopoverShell.tsx +101 -0
  133. package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +378 -0
  134. package/src/modules/customers/backend/customers/deals/pipeline/components/LaneCurrencyBreakdown.tsx +192 -0
  135. package/src/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.tsx +107 -0
  136. package/src/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.tsx +196 -0
  137. package/src/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.tsx +408 -0
  138. package/src/modules/customers/backend/customers/deals/pipeline/components/SortByPopover.tsx +164 -0
  139. package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +171 -0
  140. package/src/modules/customers/backend/customers/deals/pipeline/components/ViewTabsRow.tsx +84 -0
  141. package/src/modules/customers/backend/customers/deals/pipeline/components/constants.ts +16 -0
  142. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +2841 -550
  143. package/src/modules/customers/commands/deals.ts +19 -0
  144. package/src/modules/customers/commands/pipeline-stages.ts +68 -13
  145. package/src/modules/customers/commands/settings.ts +64 -5
  146. package/src/modules/customers/data/enrichers.ts +209 -2
  147. package/src/modules/customers/data/entities.ts +4 -1
  148. package/src/modules/customers/data/validators.ts +36 -2
  149. package/src/modules/customers/i18n/de.json +244 -0
  150. package/src/modules/customers/i18n/en.json +244 -0
  151. package/src/modules/customers/i18n/es.json +244 -0
  152. package/src/modules/customers/i18n/pl.json +244 -0
  153. package/src/modules/customers/lib/bulkDeals.ts +231 -0
  154. package/src/modules/customers/lib/kysely.ts +60 -0
  155. package/src/modules/customers/lib/stuckDeals.ts +88 -0
  156. package/src/modules/customers/migrations/.snapshot-open-mercato.json +16 -0
  157. package/src/modules/customers/migrations/Migration20260513203311_customers.ts +13 -0
  158. package/src/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.ts +71 -0
  159. package/src/modules/customers/workers/deals-bulk-update-owner.ts +46 -0
  160. package/src/modules/customers/workers/deals-bulk-update-stage.ts +46 -0
@@ -0,0 +1,126 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import Link from "next/link";
4
+ import { ChevronRight } from "lucide-react";
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogHeader,
9
+ DialogTitle
10
+ } from "@open-mercato/ui/primitives/dialog";
11
+ import { Button } from "@open-mercato/ui/primitives/button";
12
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
13
+ import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
14
+ function CustomizeViewDialog({
15
+ open,
16
+ resizedLanesCount = 0,
17
+ onClose,
18
+ onResetToDefault,
19
+ onResetColumnWidths
20
+ }) {
21
+ const t = useT();
22
+ return /* @__PURE__ */ jsx(
23
+ Dialog,
24
+ {
25
+ open,
26
+ onOpenChange: (next) => {
27
+ if (!next) onClose();
28
+ },
29
+ children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
30
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: translateWithFallback(t, "customers.deals.kanban.customize.title", "Customize view") }) }),
31
+ /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-4", children: /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-1", children: [
32
+ /* @__PURE__ */ jsx("span", { className: "text-overline font-semibold uppercase tracking-wider text-muted-foreground", children: translateWithFallback(t, "customers.deals.kanban.customize.actions", "Actions") }),
33
+ /* @__PURE__ */ jsxs(
34
+ Link,
35
+ {
36
+ href: "/backend/config/customers/pipeline-stages",
37
+ className: "flex items-center justify-between gap-2 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
38
+ onClick: () => onClose(),
39
+ children: [
40
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-col", children: [
41
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: translateWithFallback(
42
+ t,
43
+ "customers.deals.kanban.customize.manageStages",
44
+ "Manage pipeline stages"
45
+ ) }),
46
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: translateWithFallback(
47
+ t,
48
+ "customers.deals.kanban.customize.manageStages.help",
49
+ "Edit names, colors, required fields"
50
+ ) })
51
+ ] }),
52
+ /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 text-muted-foreground", "aria-hidden": "true" })
53
+ ]
54
+ }
55
+ ),
56
+ onResetColumnWidths ? /* @__PURE__ */ jsxs(
57
+ Button,
58
+ {
59
+ variant: "ghost",
60
+ type: "button",
61
+ disabled: resizedLanesCount === 0,
62
+ onClick: () => {
63
+ onResetColumnWidths();
64
+ onClose();
65
+ },
66
+ className: "flex items-center justify-between gap-2 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
67
+ children: [
68
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-col", children: [
69
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: translateWithFallback(
70
+ t,
71
+ "customers.deals.kanban.customize.resetWidths",
72
+ "Reset column widths"
73
+ ) }),
74
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: resizedLanesCount > 0 ? translateWithFallback(
75
+ t,
76
+ "customers.deals.kanban.customize.resetWidths.helpCount",
77
+ "Restore the default width on {count} resized column(s)",
78
+ { count: resizedLanesCount }
79
+ ) : translateWithFallback(
80
+ t,
81
+ "customers.deals.kanban.customize.resetWidths.helpEmpty",
82
+ "No columns have been resized yet"
83
+ ) })
84
+ ] }),
85
+ /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 text-muted-foreground", "aria-hidden": "true" })
86
+ ]
87
+ }
88
+ ) : null,
89
+ /* @__PURE__ */ jsxs(
90
+ Button,
91
+ {
92
+ variant: "ghost",
93
+ type: "button",
94
+ onClick: () => {
95
+ onResetToDefault();
96
+ onClose();
97
+ },
98
+ className: "flex items-center justify-between gap-2 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
99
+ children: [
100
+ /* @__PURE__ */ jsxs("span", { className: "flex flex-col", children: [
101
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: translateWithFallback(
102
+ t,
103
+ "customers.deals.kanban.customize.resetDefault",
104
+ "Reset to default"
105
+ ) }),
106
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: translateWithFallback(
107
+ t,
108
+ "customers.deals.kanban.customize.resetDefault.help",
109
+ "Clear filters and restore columns"
110
+ ) })
111
+ ] }),
112
+ /* @__PURE__ */ jsx(ChevronRight, { className: "size-4 text-muted-foreground", "aria-hidden": "true" })
113
+ ]
114
+ }
115
+ )
116
+ ] }) })
117
+ ] })
118
+ }
119
+ );
120
+ }
121
+ var CustomizeViewDialog_default = CustomizeViewDialog;
122
+ export {
123
+ CustomizeViewDialog,
124
+ CustomizeViewDialog_default as default
125
+ };
126
+ //# sourceMappingURL=CustomizeViewDialog.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/CustomizeViewDialog.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { ChevronRight } from 'lucide-react'\nimport {\n Dialog,\n DialogContent,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\n\n/**\n * Action-launcher dialog for the kanban board:\n * - \"Configure card fields\"\n * - \"Manage pipeline stages\"\n * - \"Reset column widths\" (only enabled when at least one lane has been resized)\n * - \"Reset to default\" (clears filters, restores columns)\n *\n * NOTE: a hardcoded \"Saved views\" radio group used to live here (Domy\u015Blny widok / M\u00F3j\n * pipeline / Closing this month / Stuck deals). Selecting an item only updated a local\n * activeViewId \u2014 no filters changed, nothing persisted. The reviewer flagged it as\n * misleading, and the perspectives-backed implementation is not in scope for this PR.\n * We removed the section entirely; once a real saved-view system lands, restore it with\n * actual filter application and persistence.\n */\ntype CustomizeViewDialogProps = {\n open: boolean\n /** Number of lanes the user has manually resized \u2014 displayed alongside the reset action */\n resizedLanesCount?: number\n onClose: () => void\n onResetToDefault: () => void\n /** Clear all per-stage width overrides so every lane falls back to its default width */\n onResetColumnWidths?: () => void\n}\n\nexport function CustomizeViewDialog({\n open,\n resizedLanesCount = 0,\n onClose,\n onResetToDefault,\n onResetColumnWidths,\n}: CustomizeViewDialogProps): React.ReactElement {\n const t = useT()\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) onClose()\n }}\n >\n <DialogContent className=\"sm:max-w-md\">\n <DialogHeader>\n {/*\n * DS Dialog renders the close affordance (X) automatically; we previously rendered\n * a second manual IconButton X here AND a header-level \"Reset to default\" button\n * that duplicated the row-level entry below. Both were removed per the SPEC-048\n * UX review (item 12).\n */}\n <DialogTitle>\n {translateWithFallback(t, 'customers.deals.kanban.customize.title', 'Customize view')}\n </DialogTitle>\n </DialogHeader>\n\n <div className=\"flex flex-col gap-4\">\n <section className=\"flex flex-col gap-1\">\n <span className=\"text-overline font-semibold uppercase tracking-wider text-muted-foreground\">\n {translateWithFallback(t, 'customers.deals.kanban.customize.actions', 'Actions')}\n </span>\n <Link\n href=\"/backend/config/customers/pipeline-stages\"\n className=\"flex items-center justify-between gap-2 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n onClick={() => onClose()}\n >\n <span className=\"flex flex-col\">\n <span className=\"text-sm font-medium text-foreground\">\n {translateWithFallback(\n t,\n 'customers.deals.kanban.customize.manageStages',\n 'Manage pipeline stages',\n )}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n {translateWithFallback(\n t,\n 'customers.deals.kanban.customize.manageStages.help',\n 'Edit names, colors, required fields',\n )}\n </span>\n </span>\n <ChevronRight className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </Link>\n {onResetColumnWidths ? (\n <Button\n variant=\"ghost\"\n type=\"button\"\n disabled={resizedLanesCount === 0}\n onClick={() => {\n onResetColumnWidths()\n onClose()\n }}\n className=\"flex items-center justify-between gap-2 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted disabled:cursor-not-allowed disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n >\n <span className=\"flex flex-col\">\n <span className=\"text-sm font-medium text-foreground\">\n {translateWithFallback(\n t,\n 'customers.deals.kanban.customize.resetWidths',\n 'Reset column widths',\n )}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n {resizedLanesCount > 0\n ? translateWithFallback(\n t,\n 'customers.deals.kanban.customize.resetWidths.helpCount',\n 'Restore the default width on {count} resized column(s)',\n { count: resizedLanesCount },\n )\n : translateWithFallback(\n t,\n 'customers.deals.kanban.customize.resetWidths.helpEmpty',\n 'No columns have been resized yet',\n )}\n </span>\n </span>\n <ChevronRight className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </Button>\n ) : null}\n <Button\n variant=\"ghost\"\n type=\"button\"\n onClick={() => {\n onResetToDefault()\n onClose()\n }}\n className=\"flex items-center justify-between gap-2 rounded-md px-3 py-2 text-left transition-colors hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n >\n <span className=\"flex flex-col\">\n <span className=\"text-sm font-medium text-foreground\">\n {translateWithFallback(\n t,\n 'customers.deals.kanban.customize.resetDefault',\n 'Reset to default',\n )}\n </span>\n <span className=\"text-xs text-muted-foreground\">\n {translateWithFallback(\n t,\n 'customers.deals.kanban.customize.resetDefault.help',\n 'Clear filters and restore columns',\n )}\n </span>\n </span>\n <ChevronRight className=\"size-4 text-muted-foreground\" aria-hidden=\"true\" />\n </Button>\n </section>\n </div>\n\n </DialogContent>\n </Dialog>\n )\n}\n\nexport default CustomizeViewDialog\n"],
5
+ "mappings": ";AA+DU,cAeI,YAfJ;AA5DV,OAAO,UAAU;AACjB,SAAS,oBAAoB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,cAAc;AACvB,SAAS,YAAY;AACrB,SAAS,6BAA6B;AA0B/B,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA,oBAAoB;AAAA,EACpB;AAAA,EACA;AAAA,EACA;AACF,GAAiD;AAC/C,QAAM,IAAI,KAAK;AAEf,SACE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,cAAc,CAAC,SAAS;AACtB,YAAI,CAAC,KAAM,SAAQ;AAAA,MACrB;AAAA,MAEA,+BAAC,iBAAc,WAAU,eACvB;AAAA,4BAAC,gBAOC,8BAAC,eACE,gCAAsB,GAAG,0CAA0C,gBAAgB,GACtF,GACF;AAAA,QAEA,oBAAC,SAAI,WAAU,uBACb,+BAAC,aAAQ,WAAU,uBACjB;AAAA,8BAAC,UAAK,WAAU,8EACb,gCAAsB,GAAG,4CAA4C,SAAS,GACjF;AAAA,UACA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cACV,SAAS,MAAM,QAAQ;AAAA,cAEvB;AAAA,qCAAC,UAAK,WAAU,iBACd;AAAA,sCAAC,UAAK,WAAU,uCACb;AAAA,oBACC;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,GACF;AAAA,kBACA,oBAAC,UAAK,WAAU,iCACb;AAAA,oBACC;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,GACF;AAAA,mBACF;AAAA,gBACA,oBAAC,gBAAa,WAAU,gCAA+B,eAAY,QAAO;AAAA;AAAA;AAAA,UAC5E;AAAA,UACC,sBACC;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,UAAU,sBAAsB;AAAA,cAChC,SAAS,MAAM;AACb,oCAAoB;AACpB,wBAAQ;AAAA,cACV;AAAA,cACA,WAAU;AAAA,cAEV;AAAA,qCAAC,UAAK,WAAU,iBACd;AAAA,sCAAC,UAAK,WAAU,uCACb;AAAA,oBACC;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,GACF;AAAA,kBACA,oBAAC,UAAK,WAAU,iCACb,8BAAoB,IACjB;AAAA,oBACE;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,EAAE,OAAO,kBAAkB;AAAA,kBAC7B,IACA;AAAA,oBACE;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,GACN;AAAA,mBACF;AAAA,gBACA,oBAAC,gBAAa,WAAU,gCAA+B,eAAY,QAAO;AAAA;AAAA;AAAA,UAC5E,IACE;AAAA,UACJ;AAAA,YAAC;AAAA;AAAA,cACC,SAAQ;AAAA,cACR,MAAK;AAAA,cACL,SAAS,MAAM;AACb,iCAAiB;AACjB,wBAAQ;AAAA,cACV;AAAA,cACA,WAAU;AAAA,cAEV;AAAA,qCAAC,UAAK,WAAU,iBACd;AAAA,sCAAC,UAAK,WAAU,uCACb;AAAA,oBACC;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,GACF;AAAA,kBACA,oBAAC,UAAK,WAAU,iCACb;AAAA,oBACC;AAAA,oBACA;AAAA,oBACA;AAAA,kBACF,GACF;AAAA,mBACF;AAAA,gBACA,oBAAC,gBAAa,WAAU,gCAA+B,eAAY,QAAO;AAAA;AAAA;AAAA,UAC5E;AAAA,WACF,GACF;AAAA,SAEF;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,8BAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,31 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import { Button } from "@open-mercato/ui/primitives/button";
4
+ function DashedTileButton({
5
+ onClick,
6
+ disabled = false,
7
+ ariaLabel,
8
+ title,
9
+ className,
10
+ children
11
+ }) {
12
+ return /* @__PURE__ */ jsx(
13
+ Button,
14
+ {
15
+ variant: "ghost",
16
+ type: "button",
17
+ onClick,
18
+ disabled,
19
+ "aria-label": ariaLabel,
20
+ title,
21
+ className: `group flex items-center justify-center gap-2.5 rounded-lg border border-dashed border-muted-foreground/60 bg-muted/40 text-sm font-semibold leading-normal text-foreground transition-colors hover:bg-muted/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className ?? ""}`.trim(),
22
+ children
23
+ }
24
+ );
25
+ }
26
+ var DashedTileButton_default = DashedTileButton;
27
+ export {
28
+ DashedTileButton,
29
+ DashedTileButton_default as default
30
+ };
31
+ //# sourceMappingURL=DashedTileButton.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/DashedTileButton.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { Button } from '@open-mercato/ui/primitives/button'\n\nexport type DashedTileButtonProps = {\n onClick: () => void\n disabled?: boolean\n ariaLabel?: string\n title?: string\n /**\n * Extra utility classes appended after the shared dashed-tile chrome. Use this for\n * width/height/orientation tweaks that vary per consumer (e.g. AddStageLane needs\n * `min-h-[60vh]` + `flex-col`, quick-add needs `h-11`, show-more needs `px-3 py-2.5`).\n */\n className?: string\n children: React.ReactNode\n}\n\n/**\n * Shared chrome for the three dashed-outline CTAs on the kanban board:\n * - quick-add per lane (Lane.tsx)\n * - show-more per lane (Lane.tsx)\n * - new-stage lane (AddStageLane.tsx)\n *\n * Before extraction each consumer hand-rolled the same 8\u201310 utility classes plus its own\n * focus-ring + dashed-border styling; drift between the three was a Figma compliance risk\n * (and was flagged in the SPEC-048 UX review as item 19). Centralising the styles here\n * keeps the trio in sync.\n */\nexport function DashedTileButton({\n onClick,\n disabled = false,\n ariaLabel,\n title,\n className,\n children,\n}: DashedTileButtonProps): React.ReactElement {\n return (\n <Button\n variant=\"ghost\"\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n aria-label={ariaLabel}\n title={title}\n className={`group flex items-center justify-center gap-2.5 rounded-lg border border-dashed border-muted-foreground/60 bg-muted/40 text-sm font-semibold leading-normal text-foreground transition-colors hover:bg-muted/70 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${className ?? ''}`.trim()}\n >\n {children}\n </Button>\n )\n}\n\nexport default DashedTileButton\n"],
5
+ "mappings": ";AAuCI;AApCJ,SAAS,cAAc;AA2BhB,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8C;AAC5C,SACE;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA,cAAY;AAAA,MACZ;AAAA,MACA,WAAW,sWAAsW,aAAa,EAAE,GAAG,KAAK;AAAA,MAEvY;AAAA;AAAA,EACH;AAEJ;AAEA,IAAO,2BAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,389 @@
1
+ "use client";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useDraggable } from "@dnd-kit/core";
5
+ import { AlertTriangle, Building2, Calendar, Clock, Mail, Phone, StickyNote } from "lucide-react";
6
+ import { Avatar } from "@open-mercato/ui/primitives/avatar";
7
+ import { Button } from "@open-mercato/ui/primitives/button";
8
+ import { Checkbox } from "@open-mercato/ui/primitives/checkbox";
9
+ import { DealCardMenu } from "./DealCardMenu.js";
10
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
11
+ import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
12
+ const ACTIVITY_BADGE_CAP = 9;
13
+ const ACTIVITY_BADGE_WARNING_THRESHOLD = 9;
14
+ const AVATAR_STACK_MAX = 3;
15
+ const AVATAR_ACCENT_CLASSES = [
16
+ "bg-status-info-icon text-white",
17
+ "bg-brand-violet text-brand-violet-foreground",
18
+ "bg-status-warning-icon text-white",
19
+ "bg-status-neutral-icon text-white",
20
+ "bg-status-success-icon text-white",
21
+ "bg-status-pink-icon text-white"
22
+ ];
23
+ function hashAccent(seed) {
24
+ let h = 0;
25
+ for (let i = 0; i < seed.length; i += 1) {
26
+ h = (h << 5) - h + seed.charCodeAt(i) | 0;
27
+ }
28
+ const idx = Math.abs(h) % AVATAR_ACCENT_CLASSES.length;
29
+ return AVATAR_ACCENT_CLASSES[idx];
30
+ }
31
+ function splitCurrencyAmount(amount, currency) {
32
+ const code = currency && currency.length === 3 ? currency.toUpperCase() : null;
33
+ try {
34
+ const formatter = new Intl.NumberFormat(void 0, {
35
+ style: "decimal",
36
+ maximumFractionDigits: 0,
37
+ useGrouping: true
38
+ });
39
+ return { display: formatter.format(amount), code };
40
+ } catch {
41
+ return { display: String(Math.round(amount)), code };
42
+ }
43
+ }
44
+ function formatProbability(value) {
45
+ if (typeof value !== "number" || Number.isNaN(value)) return null;
46
+ return `${Math.min(Math.max(Math.round(value), 0), 100)}%`;
47
+ }
48
+ const shortDateFormatter = new Intl.DateTimeFormat(void 0, {
49
+ month: "short",
50
+ day: "2-digit"
51
+ });
52
+ function formatShortDate(value) {
53
+ if (!value) return null;
54
+ const date = new Date(value);
55
+ if (Number.isNaN(date.getTime())) return null;
56
+ return shortDateFormatter.format(date);
57
+ }
58
+ function shortDealRef(id) {
59
+ return `DEAL-${id.slice(0, 6).toUpperCase()}`;
60
+ }
61
+ function DealCardImpl({
62
+ deal,
63
+ selected,
64
+ bulkSelectionActive = false,
65
+ buildMenuItems,
66
+ extraCompaniesCount = 0,
67
+ extraOwners,
68
+ isActiveDrag = false,
69
+ onToggleSelect,
70
+ onComposeActivity,
71
+ onOpenDetail
72
+ }) {
73
+ const menuItems = React.useMemo(() => buildMenuItems(deal), [buildMenuItems, deal]);
74
+ const t = useT();
75
+ const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
76
+ id: deal.id,
77
+ data: { type: "deal" }
78
+ });
79
+ const dimmed = isActiveDrag || isDragging;
80
+ const style = {};
81
+ const activityCount = deal.pipelineState.openActivitiesCount;
82
+ const activityBadgeLabel = activityCount > 0 ? activityCount > ACTIVITY_BADGE_CAP ? `${ACTIVITY_BADGE_CAP}+` : String(activityCount) : null;
83
+ const activityWarning = activityCount >= ACTIVITY_BADGE_WARNING_THRESHOLD;
84
+ const probabilityLabel = formatProbability(deal.probability);
85
+ const dateLabel = formatShortDate(deal.expectedCloseAt ?? deal.createdAt);
86
+ const stageDaysLabel = deal.pipelineState.isOverdue ? translateWithFallback(t, "customers.deals.kanban.card.overdueLabel", "Overdue") : translateWithFallback(t, "customers.deals.kanban.card.daysInStage", "in {days}d", {
87
+ days: deal.pipelineState.daysInCurrentStage
88
+ });
89
+ const showOverdue = deal.pipelineState.isOverdue;
90
+ const showStuck = !showOverdue && deal.pipelineState.isStuck;
91
+ const handleSelectChange = (next) => {
92
+ if (typeof next === "boolean" && next !== selected) {
93
+ onToggleSelect(deal.id);
94
+ }
95
+ };
96
+ const handleCardClick = (event) => {
97
+ const target = event.target;
98
+ if (target.closest('[data-card-action="true"]')) return;
99
+ if (bulkSelectionActive) {
100
+ onToggleSelect(deal.id);
101
+ return;
102
+ }
103
+ onOpenDetail(deal.id);
104
+ };
105
+ const handleActionClick = (type) => (event) => {
106
+ event.stopPropagation();
107
+ onComposeActivity(deal.id, type);
108
+ };
109
+ const stopPointerDown = React.useCallback((event) => {
110
+ event.stopPropagation();
111
+ }, []);
112
+ const ariaLabel = translateWithFallback(t, "customers.deals.kanban.card.aria", "Deal: {title}", {
113
+ title: deal.title
114
+ });
115
+ const ariaRoleDescription = translateWithFallback(
116
+ t,
117
+ "customers.deals.kanban.card.aria.roledescription",
118
+ "deal card"
119
+ );
120
+ const valuePieces = deal.valueAmount !== null && Number.isFinite(deal.valueAmount) ? splitCurrencyAmount(deal.valueAmount, deal.valueCurrency) : null;
121
+ const probabilityValue = typeof deal.probability === "number" ? deal.probability : null;
122
+ const probabilityPillClass = probabilityValue !== null && probabilityValue >= 70 ? "bg-muted text-foreground" : probabilityValue !== null && probabilityValue >= 40 ? "bg-status-warning-bg text-status-warning-text" : "bg-muted text-muted-foreground";
123
+ const activityBadgeClass = activityWarning ? "bg-status-warning-bg text-status-warning-text" : "bg-muted text-foreground";
124
+ const ownersStack = React.useMemo(() => {
125
+ if (!deal.owner) return extraOwners ?? [];
126
+ return [deal.owner, ...extraOwners ?? []];
127
+ }, [deal.owner, extraOwners]);
128
+ const visibleOwners = ownersStack.slice(0, AVATAR_STACK_MAX);
129
+ const ownerOverflow = ownersStack.length - visibleOwners.length;
130
+ return /* @__PURE__ */ jsxs(
131
+ "div",
132
+ {
133
+ ref: setNodeRef,
134
+ style,
135
+ ...attributes,
136
+ ...listeners,
137
+ "aria-label": ariaLabel,
138
+ "aria-roledescription": ariaRoleDescription,
139
+ onClick: handleCardClick,
140
+ className: `group relative flex w-full flex-col gap-3 rounded-lg border border-border bg-card px-4 py-3.5 shadow-xs transition-shadow ${dimmed ? "cursor-grabbing opacity-30" : bulkSelectionActive ? "cursor-pointer hover:shadow-sm" : "cursor-grab hover:shadow-sm active:cursor-grabbing"} ${selected ? "ring-2 ring-accent-indigo" : ""}`,
141
+ children: [
142
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2.5", children: [
143
+ /* @__PURE__ */ jsx(
144
+ "div",
145
+ {
146
+ "data-card-action": "true",
147
+ className: `mr-0.5 mt-0.5 flex shrink-0 transition-opacity ${selected || bulkSelectionActive ? "opacity-100" : "opacity-0 pointer-events-none group-hover:opacity-100 group-hover:pointer-events-auto focus-within:opacity-100 focus-within:pointer-events-auto [@media(hover:none)]:opacity-100 [@media(hover:none)]:pointer-events-auto"}`,
148
+ onClick: (event) => event.stopPropagation(),
149
+ onPointerDown: stopPointerDown,
150
+ children: /* @__PURE__ */ jsx(
151
+ Checkbox,
152
+ {
153
+ checked: selected,
154
+ onCheckedChange: handleSelectChange,
155
+ "aria-label": translateWithFallback(t, "customers.deals.kanban.card.aria.select", "Select deal")
156
+ }
157
+ )
158
+ }
159
+ ),
160
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [
161
+ /* @__PURE__ */ jsx("h3", { className: "line-clamp-2 text-base font-semibold leading-normal text-foreground", children: deal.title }),
162
+ /* @__PURE__ */ jsx("span", { className: "text-xs leading-normal text-muted-foreground", children: shortDealRef(deal.id) })
163
+ ] }),
164
+ /* @__PURE__ */ jsxs(
165
+ "div",
166
+ {
167
+ className: "flex shrink-0 items-center gap-2",
168
+ "data-card-action": "true",
169
+ onClick: (event) => event.stopPropagation(),
170
+ onPointerDown: stopPointerDown,
171
+ children: [
172
+ activityBadgeLabel ? /* @__PURE__ */ jsx(
173
+ "span",
174
+ {
175
+ className: `inline-flex items-center justify-center rounded-full px-2 py-px text-xs font-bold leading-normal ${activityBadgeClass}`,
176
+ "aria-label": translateWithFallback(
177
+ t,
178
+ "customers.deals.kanban.card.aria.openActivities",
179
+ "{count} open activities",
180
+ { count: activityCount }
181
+ ),
182
+ children: activityBadgeLabel
183
+ }
184
+ ) : null,
185
+ /* @__PURE__ */ jsx(
186
+ DealCardMenu,
187
+ {
188
+ items: menuItems,
189
+ ariaLabel: translateWithFallback(
190
+ t,
191
+ "customers.deals.kanban.card.aria.menu",
192
+ "Deal actions"
193
+ )
194
+ }
195
+ )
196
+ ]
197
+ }
198
+ )
199
+ ] }),
200
+ showOverdue || showStuck ? /* @__PURE__ */ jsx("div", { children: showOverdue ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md bg-status-error-bg px-2.5 py-1 text-xs font-semibold leading-normal text-status-error-text", children: [
201
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "size-3.5", "aria-hidden": "true" }),
202
+ translateWithFallback(t, "customers.deals.kanban.card.statusOverdue", "OVERDUE")
203
+ ] }) : /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md bg-status-warning-bg px-2.5 py-1 text-xs font-semibold leading-normal text-status-warning-text", children: [
204
+ /* @__PURE__ */ jsx(Clock, { className: "size-3.5", "aria-hidden": "true" }),
205
+ translateWithFallback(t, "customers.deals.kanban.card.statusStuck", "STUCK")
206
+ ] }) }) : null,
207
+ /* @__PURE__ */ jsx(
208
+ "div",
209
+ {
210
+ "data-card-action": "true",
211
+ className: "flex items-center gap-1 opacity-0 pointer-events-none transition-opacity group-hover:opacity-100 group-hover:pointer-events-auto focus-within:opacity-100 focus-within:pointer-events-auto [@media(hover:none)]:opacity-100 [@media(hover:none)]:pointer-events-auto",
212
+ onClick: (event) => event.stopPropagation(),
213
+ onPointerDown: stopPointerDown,
214
+ children: (() => {
215
+ const disabled = !deal.primaryCompany;
216
+ const disabledTitle = disabled ? translateWithFallback(
217
+ t,
218
+ "customers.deals.kanban.card.action.disabledNoCompany",
219
+ "Link a company to this deal before logging activities."
220
+ ) : void 0;
221
+ const baseClass = "inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap rounded-md px-2 py-1 text-sm font-medium text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:pointer-events-none disabled:opacity-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2";
222
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
223
+ /* @__PURE__ */ jsxs(
224
+ Button,
225
+ {
226
+ variant: "ghost",
227
+ size: "sm",
228
+ type: "button",
229
+ onClick: handleActionClick("call"),
230
+ disabled,
231
+ title: disabledTitle,
232
+ "aria-disabled": disabled || void 0,
233
+ className: baseClass,
234
+ children: [
235
+ /* @__PURE__ */ jsx(Phone, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
236
+ /* @__PURE__ */ jsx("span", { children: translateWithFallback(t, "customers.deals.kanban.card.action.call", "Call") })
237
+ ]
238
+ }
239
+ ),
240
+ /* @__PURE__ */ jsxs(
241
+ Button,
242
+ {
243
+ variant: "ghost",
244
+ size: "sm",
245
+ type: "button",
246
+ onClick: handleActionClick("email"),
247
+ disabled,
248
+ title: disabledTitle,
249
+ "aria-disabled": disabled || void 0,
250
+ className: baseClass,
251
+ children: [
252
+ /* @__PURE__ */ jsx(Mail, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
253
+ /* @__PURE__ */ jsx("span", { children: translateWithFallback(t, "customers.deals.kanban.card.action.email", "Email") })
254
+ ]
255
+ }
256
+ ),
257
+ /* @__PURE__ */ jsxs(
258
+ Button,
259
+ {
260
+ variant: "ghost",
261
+ size: "sm",
262
+ type: "button",
263
+ onClick: handleActionClick("note"),
264
+ disabled,
265
+ title: disabledTitle,
266
+ "aria-disabled": disabled || void 0,
267
+ className: baseClass,
268
+ children: [
269
+ /* @__PURE__ */ jsx(StickyNote, { className: "size-3.5 shrink-0", "aria-hidden": "true" }),
270
+ /* @__PURE__ */ jsx("span", { children: translateWithFallback(t, "customers.deals.kanban.card.action.note", "Note") })
271
+ ]
272
+ }
273
+ )
274
+ ] });
275
+ })()
276
+ }
277
+ ),
278
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2.5", children: [
279
+ valuePieces ? /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-1.5", children: [
280
+ /* @__PURE__ */ jsx("span", { className: "text-lg font-bold leading-normal text-foreground", children: valuePieces.display }),
281
+ valuePieces.code ? /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold leading-normal text-muted-foreground", children: valuePieces.code }) : null
282
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: translateWithFallback(t, "customers.deals.kanban.card.noValue", "No value") }),
283
+ probabilityLabel ? /* @__PURE__ */ jsx(
284
+ "span",
285
+ {
286
+ className: `inline-flex items-center rounded-md px-2.5 py-1 text-sm font-semibold leading-normal ${probabilityPillClass}`,
287
+ "aria-label": translateWithFallback(
288
+ t,
289
+ "customers.deals.kanban.card.aria.probability",
290
+ "Probability: {value}",
291
+ { value: probabilityLabel }
292
+ ),
293
+ children: probabilityLabel
294
+ }
295
+ ) : null
296
+ ] }),
297
+ deal.primaryCompany || extraCompaniesCount > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5", children: [
298
+ deal.primaryCompany ? /* @__PURE__ */ jsxs("span", { className: "inline-flex max-w-full items-center gap-1.5 overflow-hidden rounded-md bg-muted px-2.5 py-1 text-sm font-semibold leading-normal text-foreground", children: [
299
+ /* @__PURE__ */ jsx(Building2, { className: "size-3.5 shrink-0 text-muted-foreground", "aria-hidden": "true" }),
300
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: deal.primaryCompany.label })
301
+ ] }) : null,
302
+ extraCompaniesCount > 0 ? /* @__PURE__ */ jsxs(
303
+ "span",
304
+ {
305
+ className: "inline-flex shrink-0 items-center rounded-md bg-muted px-2.5 py-1 text-sm font-semibold leading-normal text-muted-foreground",
306
+ "aria-label": translateWithFallback(
307
+ t,
308
+ "customers.deals.kanban.card.aria.moreCompanies",
309
+ "{count} more companies",
310
+ { count: extraCompaniesCount }
311
+ ),
312
+ children: [
313
+ "+",
314
+ extraCompaniesCount
315
+ ]
316
+ }
317
+ ) : null
318
+ ] }) : null,
319
+ /* @__PURE__ */ jsx("div", { className: "h-px w-full bg-border", "aria-hidden": "true" }),
320
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2.5", children: [
321
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-sm leading-normal", children: [
322
+ dateLabel ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-2", children: [
323
+ /* @__PURE__ */ jsx(
324
+ Calendar,
325
+ {
326
+ className: `size-3.5 ${showOverdue ? "text-status-error-icon" : "text-muted-foreground"}`,
327
+ "aria-hidden": "true"
328
+ }
329
+ ),
330
+ /* @__PURE__ */ jsx(
331
+ "span",
332
+ {
333
+ className: showOverdue ? "font-semibold text-status-error-text" : "font-semibold text-foreground",
334
+ children: dateLabel
335
+ }
336
+ )
337
+ ] }) : null,
338
+ /* @__PURE__ */ jsx(
339
+ "span",
340
+ {
341
+ className: showOverdue ? "font-semibold text-status-error-text" : "font-normal text-muted-foreground",
342
+ children: stageDaysLabel
343
+ }
344
+ )
345
+ ] }),
346
+ ownersStack.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "flex items-center", children: [
347
+ visibleOwners.map((owner, idx) => {
348
+ const accentClass = hashAccent(owner.userId);
349
+ return /* @__PURE__ */ jsx(
350
+ Avatar,
351
+ {
352
+ label: owner.label || owner.userId.slice(0, 2).toUpperCase(),
353
+ size: "sm",
354
+ className: `size-7 text-xs font-bold ring-2 ring-card ${accentClass} ${idx > 0 ? "-ml-2.5" : ""}`
355
+ },
356
+ `${owner.userId}-${idx}`
357
+ );
358
+ }),
359
+ ownerOverflow > 0 ? /* @__PURE__ */ jsx(
360
+ Avatar,
361
+ {
362
+ label: `+${ownerOverflow}`,
363
+ size: "sm",
364
+ variant: "monochrome",
365
+ className: "size-7 -ml-2.5 text-xs font-bold ring-2 ring-card"
366
+ }
367
+ ) : null
368
+ ] }) : null
369
+ ] })
370
+ ]
371
+ }
372
+ );
373
+ }
374
+ const DealCard = React.memo(DealCardImpl, (prev, next) => {
375
+ if (prev.deal !== next.deal) return false;
376
+ if (prev.selected !== next.selected) return false;
377
+ if (prev.bulkSelectionActive !== next.bulkSelectionActive) return false;
378
+ if (prev.isActiveDrag !== next.isActiveDrag) return false;
379
+ if (prev.buildMenuItems !== next.buildMenuItems) return false;
380
+ if (prev.extraCompaniesCount !== next.extraCompaniesCount) return false;
381
+ if (prev.extraOwners !== next.extraOwners) return false;
382
+ return true;
383
+ });
384
+ var DealCard_default = DealCard;
385
+ export {
386
+ DealCard,
387
+ DealCard_default as default
388
+ };
389
+ //# sourceMappingURL=DealCard.js.map