@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,83 @@
1
+ "use client";
2
+ import { jsx } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
5
+ import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
6
+ function LaneResizeHandle({ onResize, onResizeEnd, onReset }) {
7
+ const t = useT();
8
+ const dragStateRef = React.useRef(null);
9
+ const [isActive, setIsActive] = React.useState(false);
10
+ const stopDrag = React.useCallback(
11
+ (handlers) => {
12
+ window.removeEventListener("pointermove", handlers.move);
13
+ window.removeEventListener("pointerup", handlers.up);
14
+ window.removeEventListener("pointercancel", handlers.up);
15
+ document.body.style.cursor = "";
16
+ document.body.style.userSelect = "";
17
+ setIsActive(false);
18
+ onResizeEnd?.();
19
+ dragStateRef.current = null;
20
+ },
21
+ [onResizeEnd]
22
+ );
23
+ const handlePointerDown = React.useCallback(
24
+ (event) => {
25
+ if (event.button !== 0 && event.pointerType === "mouse") return;
26
+ event.preventDefault();
27
+ event.stopPropagation();
28
+ setIsActive(true);
29
+ document.body.style.cursor = "col-resize";
30
+ document.body.style.userSelect = "none";
31
+ dragStateRef.current = { lastX: event.clientX };
32
+ const handlers = {
33
+ move: (e) => {
34
+ if (!dragStateRef.current) return;
35
+ const delta = e.clientX - dragStateRef.current.lastX;
36
+ if (delta !== 0) {
37
+ dragStateRef.current.lastX = e.clientX;
38
+ onResize(delta);
39
+ }
40
+ },
41
+ up: () => stopDrag(handlers)
42
+ };
43
+ window.addEventListener("pointermove", handlers.move);
44
+ window.addEventListener("pointerup", handlers.up);
45
+ window.addEventListener("pointercancel", handlers.up);
46
+ },
47
+ [onResize, stopDrag]
48
+ );
49
+ const handleDoubleClick = React.useCallback(
50
+ (event) => {
51
+ event.preventDefault();
52
+ event.stopPropagation();
53
+ onReset?.();
54
+ },
55
+ [onReset]
56
+ );
57
+ return /* @__PURE__ */ jsx(
58
+ "div",
59
+ {
60
+ role: "separator",
61
+ "aria-orientation": "vertical",
62
+ "aria-label": translateWithFallback(
63
+ t,
64
+ "customers.deals.kanban.lane.resizeHandle",
65
+ "Drag to resize column. Double-click to reset width."
66
+ ),
67
+ onPointerDown: handlePointerDown,
68
+ onDoubleClick: handleDoubleClick,
69
+ title: translateWithFallback(
70
+ t,
71
+ "customers.deals.kanban.lane.resizeHandle",
72
+ "Drag to resize \xB7 double-click to reset"
73
+ ),
74
+ className: `absolute -right-0.5 top-0 z-20 h-full w-1.5 cursor-col-resize touch-none select-none ${isActive ? "bg-accent-indigo/40" : "bg-transparent hover:bg-accent-indigo/20"}`
75
+ }
76
+ );
77
+ }
78
+ var LaneResizeHandle_default = LaneResizeHandle;
79
+ export {
80
+ LaneResizeHandle,
81
+ LaneResizeHandle_default as default
82
+ };
83
+ //# sourceMappingURL=LaneResizeHandle.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/LaneResizeHandle.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\n\ntype LaneResizeHandleProps = {\n /** Called with the cumulative delta (px) as the user drags. */\n onResize: (deltaPx: number) => void\n /** Called once when the drag ends. */\n onResizeEnd?: () => void\n /** Called on double-click \u2014 convention is \"reset this lane to default width\". */\n onReset?: () => void\n}\n\n/**\n * Thin vertical drag handle that sits on the right edge of a kanban lane.\n *\n * UX (matches Monday / Asana / ClickUp):\n * - Hover: 4 px column highlights, cursor flips to `col-resize`\n * - Drag: lane width updates live via `onResize`\n * - Double-click: emits `onReset` (the lane resets to its default width)\n *\n * The pointer listeners attach to `window` during the drag so the handle keeps tracking\n * even when the cursor moves off the handle's narrow hit area.\n */\nexport function LaneResizeHandle({ onResize, onResizeEnd, onReset }: LaneResizeHandleProps): React.ReactElement {\n const t = useT()\n const dragStateRef = React.useRef<{ lastX: number } | null>(null)\n const [isActive, setIsActive] = React.useState(false)\n\n const stopDrag = React.useCallback(\n (handlers: { move: (e: PointerEvent) => void; up: () => void }) => {\n window.removeEventListener('pointermove', handlers.move)\n window.removeEventListener('pointerup', handlers.up)\n window.removeEventListener('pointercancel', handlers.up)\n document.body.style.cursor = ''\n document.body.style.userSelect = ''\n setIsActive(false)\n onResizeEnd?.()\n dragStateRef.current = null\n },\n [onResizeEnd],\n )\n\n const handlePointerDown = React.useCallback(\n (event: React.PointerEvent) => {\n // Only respond to primary (left) mouse button / touch / pen\n if (event.button !== 0 && event.pointerType === 'mouse') return\n event.preventDefault()\n event.stopPropagation()\n setIsActive(true)\n document.body.style.cursor = 'col-resize'\n document.body.style.userSelect = 'none'\n dragStateRef.current = { lastX: event.clientX }\n\n const handlers = {\n move: (e: PointerEvent) => {\n if (!dragStateRef.current) return\n const delta = e.clientX - dragStateRef.current.lastX\n if (delta !== 0) {\n dragStateRef.current.lastX = e.clientX\n onResize(delta)\n }\n },\n up: () => stopDrag(handlers),\n }\n window.addEventListener('pointermove', handlers.move)\n window.addEventListener('pointerup', handlers.up)\n window.addEventListener('pointercancel', handlers.up)\n },\n [onResize, stopDrag],\n )\n\n const handleDoubleClick = React.useCallback(\n (event: React.MouseEvent) => {\n event.preventDefault()\n event.stopPropagation()\n onReset?.()\n },\n [onReset],\n )\n\n return (\n <div\n role=\"separator\"\n aria-orientation=\"vertical\"\n aria-label={translateWithFallback(\n t,\n 'customers.deals.kanban.lane.resizeHandle',\n 'Drag to resize column. Double-click to reset width.',\n )}\n onPointerDown={handlePointerDown}\n onDoubleClick={handleDoubleClick}\n title={translateWithFallback(\n t,\n 'customers.deals.kanban.lane.resizeHandle',\n 'Drag to resize \u00B7 double-click to reset',\n )}\n className={`absolute -right-0.5 top-0 z-20 h-full w-1.5 cursor-col-resize touch-none select-none ${\n isActive ? 'bg-accent-indigo/40' : 'bg-transparent hover:bg-accent-indigo/20'\n }`}\n />\n )\n}\n\nexport default LaneResizeHandle\n"],
5
+ "mappings": ";AAoFI;AAlFJ,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,6BAA6B;AAsB/B,SAAS,iBAAiB,EAAE,UAAU,aAAa,QAAQ,GAA8C;AAC9G,QAAM,IAAI,KAAK;AACf,QAAM,eAAe,MAAM,OAAiC,IAAI;AAChE,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAAS,KAAK;AAEpD,QAAM,WAAW,MAAM;AAAA,IACrB,CAAC,aAAkE;AACjE,aAAO,oBAAoB,eAAe,SAAS,IAAI;AACvD,aAAO,oBAAoB,aAAa,SAAS,EAAE;AACnD,aAAO,oBAAoB,iBAAiB,SAAS,EAAE;AACvD,eAAS,KAAK,MAAM,SAAS;AAC7B,eAAS,KAAK,MAAM,aAAa;AACjC,kBAAY,KAAK;AACjB,oBAAc;AACd,mBAAa,UAAU;AAAA,IACzB;AAAA,IACA,CAAC,WAAW;AAAA,EACd;AAEA,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,UAA8B;AAE7B,UAAI,MAAM,WAAW,KAAK,MAAM,gBAAgB,QAAS;AACzD,YAAM,eAAe;AACrB,YAAM,gBAAgB;AACtB,kBAAY,IAAI;AAChB,eAAS,KAAK,MAAM,SAAS;AAC7B,eAAS,KAAK,MAAM,aAAa;AACjC,mBAAa,UAAU,EAAE,OAAO,MAAM,QAAQ;AAE9C,YAAM,WAAW;AAAA,QACf,MAAM,CAAC,MAAoB;AACzB,cAAI,CAAC,aAAa,QAAS;AAC3B,gBAAM,QAAQ,EAAE,UAAU,aAAa,QAAQ;AAC/C,cAAI,UAAU,GAAG;AACf,yBAAa,QAAQ,QAAQ,EAAE;AAC/B,qBAAS,KAAK;AAAA,UAChB;AAAA,QACF;AAAA,QACA,IAAI,MAAM,SAAS,QAAQ;AAAA,MAC7B;AACA,aAAO,iBAAiB,eAAe,SAAS,IAAI;AACpD,aAAO,iBAAiB,aAAa,SAAS,EAAE;AAChD,aAAO,iBAAiB,iBAAiB,SAAS,EAAE;AAAA,IACtD;AAAA,IACA,CAAC,UAAU,QAAQ;AAAA,EACrB;AAEA,QAAM,oBAAoB,MAAM;AAAA,IAC9B,CAAC,UAA4B;AAC3B,YAAM,eAAe;AACrB,YAAM,gBAAgB;AACtB,gBAAU;AAAA,IACZ;AAAA,IACA,CAAC,OAAO;AAAA,EACV;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,oBAAiB;AAAA,MACjB,cAAY;AAAA,QACV;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,eAAe;AAAA,MACf,eAAe;AAAA,MACf,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,WAAW,wFACT,WAAW,wBAAwB,0CACrC;AAAA;AAAA,EACF;AAEJ;AAEA,IAAO,2BAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,155 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import {
5
+ Popover,
6
+ PopoverContent,
7
+ PopoverTrigger
8
+ } from "@open-mercato/ui/primitives/popover";
9
+ import { Button } from "@open-mercato/ui/primitives/button";
10
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
11
+ import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
12
+ import { ChipButton } from "./ChipButton.js";
13
+ import { FilterPopoverShell } from "./FilterPopoverShell.js";
14
+ function RadioDot({ selected }) {
15
+ return /* @__PURE__ */ jsx(
16
+ "span",
17
+ {
18
+ className: `flex size-4 shrink-0 items-center justify-center rounded-full border ${selected ? "border-accent-indigo" : "border-input bg-card"}`,
19
+ "aria-hidden": "true",
20
+ children: selected ? /* @__PURE__ */ jsx("span", { className: "size-2 rounded-full bg-accent-indigo" }) : null
21
+ }
22
+ );
23
+ }
24
+ function PipelineFilterPopover({
25
+ pipelines,
26
+ selectedPipelineId,
27
+ onApply
28
+ }) {
29
+ const t = useT();
30
+ const [open, setOpen] = React.useState(false);
31
+ const [draft, setDraft] = React.useState(selectedPipelineId);
32
+ React.useEffect(() => {
33
+ if (open) setDraft(selectedPipelineId);
34
+ }, [open, selectedPipelineId]);
35
+ const activePipeline = pipelines.find((pipeline) => pipeline.id === selectedPipelineId);
36
+ const totalCount = pipelines.reduce((sum, pipeline) => sum + (pipeline.dealCount ?? 0), 0);
37
+ const chipLabel = translateWithFallback(t, "customers.deals.kanban.filter.pipeline", "Pipeline");
38
+ const chipValue = activePipeline ? activePipeline.name : translateWithFallback(t, "customers.deals.kanban.filter.allCount", "All ({count})", {
39
+ count: pipelines.length
40
+ });
41
+ const handleApply = () => {
42
+ onApply(draft);
43
+ setOpen(false);
44
+ };
45
+ const handleKeyDown = (event) => {
46
+ if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
47
+ event.preventDefault();
48
+ handleApply();
49
+ }
50
+ };
51
+ const allSelected = draft === null;
52
+ const allRowClass = allSelected ? "bg-muted" : "bg-card";
53
+ const footerLeft = draft ? /* @__PURE__ */ jsx("span", { children: translateWithFallback(
54
+ t,
55
+ "customers.deals.kanban.filter.pipeline.selectedOne",
56
+ "1 selected \xB7 {name}",
57
+ { name: pipelines.find((pipeline) => pipeline.id === draft)?.name ?? "" }
58
+ ) }) : /* @__PURE__ */ jsx("span", { children: translateWithFallback(
59
+ t,
60
+ "customers.deals.kanban.filter.pipeline.selectedNone",
61
+ "No pipeline filter"
62
+ ) });
63
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
64
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(ChipButton, { label: chipLabel, value: chipValue, active: !!selectedPipelineId }) }),
65
+ /* @__PURE__ */ jsx(
66
+ PopoverContent,
67
+ {
68
+ className: "w-96 rounded-2xl border-border bg-transparent p-0 shadow-xl",
69
+ align: "start",
70
+ onKeyDown: handleKeyDown,
71
+ children: /* @__PURE__ */ jsxs(
72
+ FilterPopoverShell,
73
+ {
74
+ title: translateWithFallback(t, "customers.deals.kanban.filter.pipeline.title", "Filter \xB7 Pipeline"),
75
+ onClose: () => setOpen(false),
76
+ onCancel: () => setOpen(false),
77
+ onApply: handleApply,
78
+ footerLeft,
79
+ children: [
80
+ /* @__PURE__ */ jsxs(
81
+ Button,
82
+ {
83
+ type: "button",
84
+ variant: "ghost",
85
+ role: "radio",
86
+ "aria-checked": allSelected,
87
+ onClick: () => setDraft(null),
88
+ className: `h-auto w-full justify-start gap-3 px-4 py-2.5 text-left font-normal ${allRowClass}`,
89
+ children: [
90
+ /* @__PURE__ */ jsx(RadioDot, { selected: allSelected }),
91
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-px", children: [
92
+ /* @__PURE__ */ jsx(
93
+ "span",
94
+ {
95
+ className: `text-sm leading-normal text-foreground ${allSelected ? "font-semibold" : "font-normal"}`,
96
+ children: translateWithFallback(t, "customers.deals.kanban.filter.pipeline.all", "All pipelines")
97
+ }
98
+ ),
99
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-normal leading-normal text-muted-foreground", children: translateWithFallback(
100
+ t,
101
+ "customers.deals.kanban.filter.pipeline.allHelper",
102
+ "{count} pipelines",
103
+ { count: pipelines.length }
104
+ ) })
105
+ ] }),
106
+ /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold leading-normal text-accent-indigo", children: totalCount })
107
+ ]
108
+ }
109
+ ),
110
+ pipelines.map((pipeline) => {
111
+ const isSelected = draft === pipeline.id;
112
+ const rowClass = isSelected ? "bg-muted" : "bg-card";
113
+ return /* @__PURE__ */ jsxs(
114
+ Button,
115
+ {
116
+ type: "button",
117
+ variant: "ghost",
118
+ role: "radio",
119
+ "aria-checked": isSelected,
120
+ onClick: () => setDraft(pipeline.id),
121
+ className: `h-auto w-full justify-start gap-3 px-4 py-2.5 text-left font-normal ${rowClass}`,
122
+ children: [
123
+ /* @__PURE__ */ jsx(RadioDot, { selected: isSelected }),
124
+ /* @__PURE__ */ jsx(
125
+ "span",
126
+ {
127
+ className: `flex-1 truncate text-sm leading-normal text-foreground ${isSelected ? "font-semibold" : "font-normal"}`,
128
+ children: pipeline.name
129
+ }
130
+ ),
131
+ typeof pipeline.dealCount === "number" ? /* @__PURE__ */ jsx(
132
+ "span",
133
+ {
134
+ className: `text-xs leading-normal ${isSelected ? "font-semibold text-accent-indigo" : "font-normal text-muted-foreground"}`,
135
+ children: pipeline.dealCount
136
+ }
137
+ ) : null
138
+ ]
139
+ },
140
+ pipeline.id
141
+ );
142
+ })
143
+ ]
144
+ }
145
+ )
146
+ }
147
+ )
148
+ ] });
149
+ }
150
+ var PipelineFilterPopover_default = PipelineFilterPopover;
151
+ export {
152
+ PipelineFilterPopover,
153
+ PipelineFilterPopover_default as default
154
+ };
155
+ //# sourceMappingURL=PipelineFilterPopover.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/PipelineFilterPopover.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport {\n Popover,\n PopoverContent,\n PopoverTrigger,\n} from '@open-mercato/ui/primitives/popover'\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'\nimport { ChipButton } from './ChipButton'\nimport { FilterPopoverShell } from './FilterPopoverShell'\n\nexport type PipelineFilterOption = {\n id: string\n name: string\n dealCount?: number\n}\n\ntype PipelineFilterPopoverProps = {\n pipelines: PipelineFilterOption[]\n selectedPipelineId: string | null\n onApply: (pipelineId: string | null) => void\n}\n\n/**\n * Single radio button matching the Figma spec at 1045:11917 \u2014 an outlined 16px circle that\n * fills with the accent-indigo when selected. Reused for every row + the \"All pipelines\"\n * sentinel so the visual is consistent.\n */\nfunction RadioDot({ selected }: { selected: boolean }): React.ReactElement {\n return (\n <span\n className={`flex size-4 shrink-0 items-center justify-center rounded-full border ${\n selected ? 'border-accent-indigo' : 'border-input bg-card'\n }`}\n aria-hidden=\"true\"\n >\n {selected ? <span className=\"size-2 rounded-full bg-accent-indigo\" /> : null}\n </span>\n )\n}\n\nexport function PipelineFilterPopover({\n pipelines,\n selectedPipelineId,\n onApply,\n}: PipelineFilterPopoverProps): React.ReactElement {\n const t = useT()\n const [open, setOpen] = React.useState(false)\n const [draft, setDraft] = React.useState<string | null>(selectedPipelineId)\n\n React.useEffect(() => {\n if (open) setDraft(selectedPipelineId)\n }, [open, selectedPipelineId])\n\n const activePipeline = pipelines.find((pipeline) => pipeline.id === selectedPipelineId)\n const totalCount = pipelines.reduce((sum, pipeline) => sum + (pipeline.dealCount ?? 0), 0)\n const chipLabel = translateWithFallback(t, 'customers.deals.kanban.filter.pipeline', 'Pipeline')\n const chipValue = activePipeline\n ? activePipeline.name\n : translateWithFallback(t, 'customers.deals.kanban.filter.allCount', 'All ({count})', {\n count: pipelines.length,\n })\n\n const handleApply = () => {\n onApply(draft)\n setOpen(false)\n }\n\n // Cmd/Ctrl+Enter from anywhere inside the popover confirms \u2014 parity with the dialog\n // primary-action shortcut (`AGENTS.md` UI Interaction rules).\n const handleKeyDown = (event: React.KeyboardEvent) => {\n if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {\n event.preventDefault()\n handleApply()\n }\n }\n\n const allSelected = draft === null\n const allRowClass = allSelected ? 'bg-muted' : 'bg-card'\n\n const footerLeft = draft ? (\n <span>\n {translateWithFallback(\n t,\n 'customers.deals.kanban.filter.pipeline.selectedOne',\n '1 selected \u00B7 {name}',\n { name: pipelines.find((pipeline) => pipeline.id === draft)?.name ?? '' },\n )}\n </span>\n ) : (\n <span>\n {translateWithFallback(\n t,\n 'customers.deals.kanban.filter.pipeline.selectedNone',\n 'No pipeline filter',\n )}\n </span>\n )\n\n return (\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <ChipButton label={chipLabel} value={chipValue} active={!!selectedPipelineId} />\n </PopoverTrigger>\n <PopoverContent\n className=\"w-96 rounded-2xl border-border bg-transparent p-0 shadow-xl\"\n align=\"start\"\n onKeyDown={handleKeyDown}\n >\n <FilterPopoverShell\n title={translateWithFallback(t, 'customers.deals.kanban.filter.pipeline.title', 'Filter \u00B7 Pipeline')}\n onClose={() => setOpen(false)}\n onCancel={() => setOpen(false)}\n onApply={handleApply}\n footerLeft={footerLeft}\n >\n {/*\n \"All pipelines\" sentinel row. Per Figma it shows the total deal count in\n accent-indigo (active emphasis), regardless of whether it's selected.\n */}\n <Button\n type=\"button\"\n variant=\"ghost\"\n role=\"radio\"\n aria-checked={allSelected}\n onClick={() => setDraft(null)}\n className={`h-auto w-full justify-start gap-3 px-4 py-2.5 text-left font-normal ${allRowClass}`}\n >\n <RadioDot selected={allSelected} />\n <div className=\"flex min-w-0 flex-1 flex-col gap-px\">\n <span\n className={`text-sm leading-normal text-foreground ${\n allSelected ? 'font-semibold' : 'font-normal'\n }`}\n >\n {translateWithFallback(t, 'customers.deals.kanban.filter.pipeline.all', 'All pipelines')}\n </span>\n <span className=\"text-xs font-normal leading-normal text-muted-foreground\">\n {translateWithFallback(\n t,\n 'customers.deals.kanban.filter.pipeline.allHelper',\n '{count} pipelines',\n { count: pipelines.length },\n )}\n </span>\n </div>\n <span className=\"text-xs font-semibold leading-normal text-accent-indigo\">\n {totalCount}\n </span>\n </Button>\n\n {pipelines.map((pipeline) => {\n const isSelected = draft === pipeline.id\n const rowClass = isSelected ? 'bg-muted' : 'bg-card'\n return (\n <Button\n key={pipeline.id}\n type=\"button\"\n variant=\"ghost\"\n role=\"radio\"\n aria-checked={isSelected}\n onClick={() => setDraft(pipeline.id)}\n className={`h-auto w-full justify-start gap-3 px-4 py-2.5 text-left font-normal ${rowClass}`}\n >\n <RadioDot selected={isSelected} />\n <span\n className={`flex-1 truncate text-sm leading-normal text-foreground ${\n isSelected ? 'font-semibold' : 'font-normal'\n }`}\n >\n {pipeline.name}\n </span>\n {typeof pipeline.dealCount === 'number' ? (\n <span\n className={`text-xs leading-normal ${\n isSelected\n ? 'font-semibold text-accent-indigo'\n : 'font-normal text-muted-foreground'\n }`}\n >\n {pipeline.dealCount}\n </span>\n ) : null}\n </Button>\n )\n })}\n </FilterPopoverShell>\n </PopoverContent>\n </Popover>\n )\n}\n\nexport default PipelineFilterPopover\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,318 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
4
+ import { z } from "zod";
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogDescription,
9
+ DialogHeader,
10
+ DialogTitle
11
+ } from "@open-mercato/ui/primitives/dialog";
12
+ import { Avatar } from "@open-mercato/ui/primitives/avatar";
13
+ import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
14
+ import { ComboboxInput } from "@open-mercato/ui/backend/inputs/ComboboxInput";
15
+ import { createCrud } from "@open-mercato/ui/backend/utils/crud";
16
+ import { createCrudFormError } from "@open-mercato/ui/backend/utils/serverErrors";
17
+ import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
18
+ import { flash } from "@open-mercato/ui/backend/FlashMessages";
19
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
20
+ import { translateWithFallback } from "@open-mercato/shared/lib/i18n/translate";
21
+ const STATUS_OPTIONS = ["open", "in_progress", "win", "loose"];
22
+ const FALLBACK_CURRENCIES = [
23
+ { code: "USD", isBase: true },
24
+ { code: "EUR" },
25
+ { code: "GBP" },
26
+ { code: "PLN" }
27
+ ];
28
+ const QUICK_DEAL_CONTEXT_ID = "customers-deals-kanban:quick-deal";
29
+ function QuickDealDialog({
30
+ open,
31
+ context,
32
+ onClose,
33
+ onCreated,
34
+ currentUserId,
35
+ currentUserLabel,
36
+ companies = [],
37
+ currencies
38
+ }) {
39
+ const t = useT();
40
+ const [formInstanceKey, setFormInstanceKey] = React.useState(0);
41
+ React.useEffect(() => {
42
+ if (open) setFormInstanceKey((current) => current + 1);
43
+ }, [open]);
44
+ const { runMutation, retryLastMutation } = useGuardedMutation({
45
+ contextId: QUICK_DEAL_CONTEXT_ID,
46
+ blockedMessage: translateWithFallback(t, "ui.forms.flash.saveBlocked", "Save blocked by validation")
47
+ });
48
+ const titleRequiredMessage = translateWithFallback(
49
+ t,
50
+ "customers.deals.kanban.quickDeal.title.required",
51
+ "Title is required."
52
+ );
53
+ const resolvedCurrencies = React.useMemo(
54
+ () => currencies && currencies.length > 0 ? currencies : FALLBACK_CURRENCIES,
55
+ [currencies]
56
+ );
57
+ const defaultCurrencyCode = React.useMemo(() => {
58
+ const base = resolvedCurrencies.find((currency) => currency.isBase);
59
+ return base?.code ?? resolvedCurrencies[0]?.code ?? "USD";
60
+ }, [resolvedCurrencies]);
61
+ const formSchema = React.useMemo(
62
+ () => z.object({
63
+ title: z.string().trim().min(1, titleRequiredMessage),
64
+ valueAmount: z.number().min(0).nullable().optional(),
65
+ // The set of valid currencies is tenant-defined and resolved at runtime; we accept
66
+ // any non-empty string here and let the server (which knows the canonical list)
67
+ // reject anything bogus.
68
+ valueCurrency: z.string().min(1),
69
+ companyId: z.string().optional(),
70
+ status: z.enum(STATUS_OPTIONS),
71
+ probability: z.number().min(0).max(100).nullable().optional(),
72
+ expectedCloseAt: z.string().optional(),
73
+ description: z.string().optional()
74
+ }),
75
+ // Cast widens to QuickDealFormValues so CrudForm's TValues generic stays exact.
76
+ [titleRequiredMessage]
77
+ );
78
+ const initialValues = React.useMemo(
79
+ () => ({
80
+ title: "",
81
+ valueAmount: null,
82
+ valueCurrency: defaultCurrencyCode,
83
+ companyId: "",
84
+ status: "open",
85
+ probability: 25,
86
+ expectedCloseAt: "",
87
+ description: ""
88
+ }),
89
+ [defaultCurrencyCode]
90
+ );
91
+ const fields = React.useMemo(
92
+ () => {
93
+ const list = [
94
+ {
95
+ // CrudForm auto-focuses the first field of the first group on mount; we don't
96
+ // need (and CrudField doesn't accept) an explicit `autoFocus` prop here.
97
+ id: "title",
98
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.title.label", "Deal title"),
99
+ type: "text",
100
+ required: true,
101
+ placeholder: translateWithFallback(
102
+ t,
103
+ "customers.deals.kanban.quickDeal.title.placeholder",
104
+ "e.g. Q3 Expansion \u2014 Lighting Package"
105
+ )
106
+ },
107
+ {
108
+ id: "valueAmount",
109
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.value", "Value"),
110
+ type: "number",
111
+ layout: "half",
112
+ placeholder: "0"
113
+ },
114
+ {
115
+ id: "valueCurrency",
116
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.currency", "Currency"),
117
+ type: "select",
118
+ layout: "half",
119
+ options: resolvedCurrencies.map((currency) => ({
120
+ value: currency.code,
121
+ label: currency.label && currency.label !== currency.code ? `${currency.code} \u2014 ${currency.label}` : currency.code
122
+ }))
123
+ },
124
+ {
125
+ // Company is a typeahead (ComboboxInput) rather than a plain <select>: tenants
126
+ // with hundreds of companies get an unusable dropdown otherwise. The `seedOptions`
127
+ // carry the most-recent companies the page already loaded; an async loader could
128
+ // be added later to hit the companies search endpoint for full coverage.
129
+ id: "companyId",
130
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.company", "Company"),
131
+ type: "custom",
132
+ component: ({ value, setValue }) => /* @__PURE__ */ jsx(
133
+ ComboboxInput,
134
+ {
135
+ value: typeof value === "string" ? value : "",
136
+ onChange: (next) => setValue(next),
137
+ seedOptions: companies.map((company) => ({ value: company.id, label: company.label })),
138
+ placeholder: translateWithFallback(
139
+ t,
140
+ "customers.deals.kanban.quickDeal.companyPh",
141
+ "Pick a company or add new\u2026"
142
+ ),
143
+ allowCustomValues: false
144
+ }
145
+ )
146
+ }
147
+ ];
148
+ if (currentUserLabel) {
149
+ list.push({
150
+ id: "owner",
151
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.owner", "Owner"),
152
+ type: "custom",
153
+ readOnly: true,
154
+ component: () => /* @__PURE__ */ jsxs("div", { className: "flex h-9 items-center gap-2 rounded-md border border-input bg-card px-3 text-sm", children: [
155
+ /* @__PURE__ */ jsx(Avatar, { label: currentUserLabel, size: "sm" }),
156
+ /* @__PURE__ */ jsx("span", { children: currentUserLabel })
157
+ ] })
158
+ });
159
+ }
160
+ list.push(
161
+ {
162
+ id: "status",
163
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.status", "Status"),
164
+ type: "select",
165
+ // Storage value is preserved (`loose` etc.) — only the displayed label is
166
+ // translated, so we never write a localised string back to the DB.
167
+ options: STATUS_OPTIONS.map((statusValue) => ({
168
+ value: statusValue,
169
+ label: translateWithFallback(
170
+ t,
171
+ `customers.deals.kanban.quickDeal.status.${statusValue}`,
172
+ statusValue
173
+ )
174
+ }))
175
+ },
176
+ {
177
+ id: "probability",
178
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.probability", "Probability"),
179
+ type: "number",
180
+ layout: "half",
181
+ placeholder: "25"
182
+ },
183
+ {
184
+ id: "expectedCloseAt",
185
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.expectedClose", "Expected close"),
186
+ type: "date",
187
+ layout: "half"
188
+ },
189
+ {
190
+ id: "description",
191
+ label: translateWithFallback(t, "customers.deals.kanban.quickDeal.description", "Description"),
192
+ type: "textarea",
193
+ rows: 3,
194
+ placeholder: translateWithFallback(
195
+ t,
196
+ "customers.deals.kanban.quickDeal.description.placeholder",
197
+ "Short context, decision makers or next steps. Markdown supported."
198
+ )
199
+ }
200
+ );
201
+ return list;
202
+ },
203
+ [companies, currentUserLabel, resolvedCurrencies, t]
204
+ );
205
+ const groups = React.useMemo(
206
+ () => [
207
+ {
208
+ id: "basic",
209
+ // No title — keep the basic section flush with the dialog header.
210
+ fields: ["title", "valueAmount", "valueCurrency", "companyId", ...currentUserLabel ? ["owner"] : []]
211
+ },
212
+ {
213
+ id: "more",
214
+ title: translateWithFallback(
215
+ t,
216
+ "customers.deals.kanban.quickDeal.more",
217
+ "More details (probability, close date, description)"
218
+ ),
219
+ fields: ["status", "probability", "expectedCloseAt", "description"]
220
+ }
221
+ ],
222
+ [currentUserLabel, t]
223
+ );
224
+ const handleSubmit = React.useCallback(
225
+ async (values) => {
226
+ if (!context) return;
227
+ const trimmedTitle = values.title.trim();
228
+ if (!trimmedTitle.length) {
229
+ throw createCrudFormError(titleRequiredMessage, { title: titleRequiredMessage });
230
+ }
231
+ const payload = {
232
+ title: trimmedTitle,
233
+ pipelineId: context.pipelineId,
234
+ pipelineStageId: context.pipelineStageId,
235
+ valueCurrency: values.valueCurrency
236
+ };
237
+ if (typeof values.valueAmount === "number" && Number.isFinite(values.valueAmount)) {
238
+ payload.valueAmount = values.valueAmount;
239
+ }
240
+ if (currentUserId) payload.ownerUserId = currentUserId;
241
+ if (values.companyId) payload.companyIds = [values.companyId];
242
+ if (values.status) payload.status = values.status;
243
+ if (typeof values.probability === "number" && Number.isFinite(values.probability)) {
244
+ payload.probability = values.probability;
245
+ }
246
+ if (values.expectedCloseAt && values.expectedCloseAt.trim().length) {
247
+ payload.expectedCloseAt = values.expectedCloseAt;
248
+ }
249
+ if (values.description && values.description.trim().length) {
250
+ payload.description = values.description;
251
+ }
252
+ const operation = () => createCrud("customers/deals", payload, {
253
+ errorMessage: translateWithFallback(t, "customers.deals.create.error", "Failed to create deal.")
254
+ });
255
+ await runMutation({
256
+ operation,
257
+ context: {
258
+ formId: QUICK_DEAL_CONTEXT_ID,
259
+ resourceKind: "customers.deal",
260
+ retryLastMutation
261
+ }
262
+ });
263
+ flash(
264
+ translateWithFallback(
265
+ t,
266
+ "customers.deals.kanban.quickDeal.success",
267
+ "Deal added to {stage}.",
268
+ { stage: context.pipelineStageLabel }
269
+ ),
270
+ "success"
271
+ );
272
+ onCreated();
273
+ onClose();
274
+ },
275
+ [context, currentUserId, onClose, onCreated, retryLastMutation, runMutation, t, titleRequiredMessage]
276
+ );
277
+ if (!context) return null;
278
+ return /* @__PURE__ */ jsx(
279
+ Dialog,
280
+ {
281
+ open,
282
+ onOpenChange: (next) => {
283
+ if (!next) onClose();
284
+ },
285
+ children: /* @__PURE__ */ jsxs(DialogContent, { children: [
286
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
287
+ /* @__PURE__ */ jsx(DialogTitle, { children: translateWithFallback(t, "customers.deals.kanban.quickDeal.title", "Quick deal") }),
288
+ /* @__PURE__ */ jsx(DialogDescription, { children: translateWithFallback(
289
+ t,
290
+ "customers.deals.kanban.quickDeal.context",
291
+ "Pipeline: {pipeline} \xB7 Stage: {stage}",
292
+ { pipeline: context.pipelineName, stage: context.pipelineStageLabel }
293
+ ) })
294
+ ] }),
295
+ /* @__PURE__ */ jsx(
296
+ CrudForm,
297
+ {
298
+ embedded: true,
299
+ fields,
300
+ groups,
301
+ collapsibleGroups: { pageType: "customers.deals.kanban.quickDeal" },
302
+ initialValues,
303
+ schema: formSchema,
304
+ submitLabel: translateWithFallback(t, "customers.deals.kanban.quickDeal.submit", "Add deal"),
305
+ onSubmit: handleSubmit
306
+ },
307
+ `${context.pipelineStageId}:${formInstanceKey}`
308
+ )
309
+ ] })
310
+ }
311
+ );
312
+ }
313
+ var QuickDealDialog_default = QuickDealDialog;
314
+ export {
315
+ QuickDealDialog,
316
+ QuickDealDialog_default as default
317
+ };
318
+ //# sourceMappingURL=QuickDealDialog.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/pipeline/components/QuickDealDialog.tsx"],
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { z } from 'zod'\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '@open-mercato/ui/primitives/dialog'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { ComboboxInput } from '@open-mercato/ui/backend/inputs/ComboboxInput'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\n\n// Deal status values written by the kanban flow. These intentionally do NOT include 'closed'\n// because the rest of the app only persists 'loose' for lost deals (see StatusFilterPopover\n// notes). 'in_progress' is kept for backwards compatibility with quick-deals created from\n// the previous version of this dialog. The labels are looked up via i18n at render time so\n// the historic typo 'loose' doesn't leak to operators \u2014 the storage value stays unchanged.\nconst STATUS_OPTIONS = ['open', 'in_progress', 'win', 'loose'] as const\n\n/**\n * Tenant currency option. The page resolves these from the currencies module (see\n * `useCurrencyDictionary`) and passes them in; we no longer hardcode the 4-currency\n * list that previously locked tenants on CZK/SEK/HUF/etc. out of quick-add.\n */\nexport type QuickDealCurrencyOption = {\n code: string\n label?: string\n isBase?: boolean\n}\n\nconst FALLBACK_CURRENCIES: QuickDealCurrencyOption[] = [\n { code: 'USD', isBase: true },\n { code: 'EUR' },\n { code: 'GBP' },\n { code: 'PLN' },\n]\n\nexport type QuickDealContext = {\n pipelineId: string\n pipelineName: string\n pipelineStageId: string\n pipelineStageLabel: string\n}\n\nexport type QuickDealCompanyOption = {\n id: string\n label: string\n}\n\ntype QuickDealDialogProps = {\n open: boolean\n context: QuickDealContext | null\n onClose: () => void\n onCreated: () => void\n currentUserId?: string\n currentUserLabel?: string\n companies?: QuickDealCompanyOption[]\n /**\n * Tenant currency list. When omitted (e.g. dictionary not yet loaded), we render a\n * conservative fallback so the dialog still works end-to-end. The default selection is\n * always the entry with `isBase: true`, falling back to the first item.\n */\n currencies?: QuickDealCurrencyOption[]\n}\n\ntype QuickDealFormValues = {\n title: string\n valueAmount: number | null\n valueCurrency: string\n companyId: string\n status: (typeof STATUS_OPTIONS)[number]\n probability: number | null\n expectedCloseAt: string\n description: string\n}\n\nconst QUICK_DEAL_CONTEXT_ID = 'customers-deals-kanban:quick-deal'\n\nexport function QuickDealDialog({\n open,\n context,\n onClose,\n onCreated,\n currentUserId,\n currentUserLabel,\n companies = [],\n currencies,\n}: QuickDealDialogProps): React.ReactElement | null {\n const t = useT()\n // Re-mount CrudForm whenever the dialog opens so cleared state is consistently fresh.\n // We also key on `context?.pipelineStageId` so reopening from a different stage doesn't\n // reuse stale field values (the page passes a fresh stage in `context` each time).\n const [formInstanceKey, setFormInstanceKey] = React.useState(0)\n React.useEffect(() => {\n if (open) setFormInstanceKey((current) => current + 1)\n }, [open])\n\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: QUICK_DEAL_CONTEXT_ID,\n blockedMessage: translateWithFallback(t, 'ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n\n const titleRequiredMessage = translateWithFallback(\n t,\n 'customers.deals.kanban.quickDeal.title.required',\n 'Title is required.',\n )\n\n // Default currency selection: tenant base currency, else first available, else 'USD' as\n // a last-resort literal that satisfies the form validator (which now accepts any 3-char\n // code rather than a hardcoded enum).\n const resolvedCurrencies = React.useMemo(\n () => (currencies && currencies.length > 0 ? currencies : FALLBACK_CURRENCIES),\n [currencies],\n )\n const defaultCurrencyCode = React.useMemo(() => {\n const base = resolvedCurrencies.find((currency) => currency.isBase)\n return base?.code ?? resolvedCurrencies[0]?.code ?? 'USD'\n }, [resolvedCurrencies])\n\n const formSchema = React.useMemo(\n () =>\n z.object({\n title: z.string().trim().min(1, titleRequiredMessage),\n valueAmount: z.number().min(0).nullable().optional(),\n // The set of valid currencies is tenant-defined and resolved at runtime; we accept\n // any non-empty string here and let the server (which knows the canonical list)\n // reject anything bogus.\n valueCurrency: z.string().min(1),\n companyId: z.string().optional(),\n status: z.enum(STATUS_OPTIONS),\n probability: z.number().min(0).max(100).nullable().optional(),\n expectedCloseAt: z.string().optional(),\n description: z.string().optional(),\n }),\n // Cast widens to QuickDealFormValues so CrudForm's TValues generic stays exact.\n [titleRequiredMessage],\n ) as unknown as z.ZodType<QuickDealFormValues>\n\n const initialValues = React.useMemo<Partial<QuickDealFormValues>>(\n () => ({\n title: '',\n valueAmount: null,\n valueCurrency: defaultCurrencyCode,\n companyId: '',\n status: 'open',\n probability: 25,\n expectedCloseAt: '',\n description: '',\n }),\n [defaultCurrencyCode],\n )\n\n const fields = React.useMemo<CrudField[]>(\n () => {\n const list: CrudField[] = [\n {\n // CrudForm auto-focuses the first field of the first group on mount; we don't\n // need (and CrudField doesn't accept) an explicit `autoFocus` prop here.\n id: 'title',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.title.label', 'Deal title'),\n type: 'text',\n required: true,\n placeholder: translateWithFallback(\n t,\n 'customers.deals.kanban.quickDeal.title.placeholder',\n 'e.g. Q3 Expansion \u2014 Lighting Package',\n ),\n },\n {\n id: 'valueAmount',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.value', 'Value'),\n type: 'number',\n layout: 'half',\n placeholder: '0',\n },\n {\n id: 'valueCurrency',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.currency', 'Currency'),\n type: 'select',\n layout: 'half',\n options: resolvedCurrencies.map((currency) => ({\n value: currency.code,\n label: currency.label && currency.label !== currency.code\n ? `${currency.code} \u2014 ${currency.label}`\n : currency.code,\n })),\n },\n {\n // Company is a typeahead (ComboboxInput) rather than a plain <select>: tenants\n // with hundreds of companies get an unusable dropdown otherwise. The `seedOptions`\n // carry the most-recent companies the page already loaded; an async loader could\n // be added later to hit the companies search endpoint for full coverage.\n id: 'companyId',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.company', 'Company'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <ComboboxInput\n value={typeof value === 'string' ? value : ''}\n onChange={(next) => setValue(next)}\n seedOptions={companies.map((company) => ({ value: company.id, label: company.label }))}\n placeholder={translateWithFallback(\n t,\n 'customers.deals.kanban.quickDeal.companyPh',\n 'Pick a company or add new\u2026',\n )}\n allowCustomValues={false}\n />\n ),\n },\n ]\n // Owner is read-only display of the current user (mirrors original UX). Hidden when\n // we don't know the user \u2014 avoids confusing empty avatar cell.\n if (currentUserLabel) {\n list.push({\n id: 'owner',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.owner', 'Owner'),\n type: 'custom',\n readOnly: true,\n component: () => (\n <div className=\"flex h-9 items-center gap-2 rounded-md border border-input bg-card px-3 text-sm\">\n <Avatar label={currentUserLabel} size=\"sm\" />\n <span>{currentUserLabel}</span>\n </div>\n ),\n })\n }\n // Progressive disclosure: status + probability + close date + description live in\n // a separate \"More details\" group with `defaultExpanded: false` (configured via\n // collapsibleGroups on the host CrudForm).\n list.push(\n {\n id: 'status',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.status', 'Status'),\n type: 'select',\n // Storage value is preserved (`loose` etc.) \u2014 only the displayed label is\n // translated, so we never write a localised string back to the DB.\n options: STATUS_OPTIONS.map((statusValue) => ({\n value: statusValue,\n label: translateWithFallback(\n t,\n `customers.deals.kanban.quickDeal.status.${statusValue}`,\n statusValue,\n ),\n })),\n },\n {\n id: 'probability',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.probability', 'Probability'),\n type: 'number',\n layout: 'half',\n placeholder: '25',\n },\n {\n id: 'expectedCloseAt',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.expectedClose', 'Expected close'),\n type: 'date',\n layout: 'half',\n },\n {\n id: 'description',\n label: translateWithFallback(t, 'customers.deals.kanban.quickDeal.description', 'Description'),\n type: 'textarea',\n rows: 3,\n placeholder: translateWithFallback(\n t,\n 'customers.deals.kanban.quickDeal.description.placeholder',\n 'Short context, decision makers or next steps. Markdown supported.',\n ),\n },\n )\n return list\n },\n [companies, currentUserLabel, resolvedCurrencies, t],\n )\n\n const groups = React.useMemo<CrudFormGroup[]>(\n () => [\n {\n id: 'basic',\n // No title \u2014 keep the basic section flush with the dialog header.\n fields: ['title', 'valueAmount', 'valueCurrency', 'companyId', ...(currentUserLabel ? ['owner'] : [])],\n },\n {\n id: 'more',\n title: translateWithFallback(\n t,\n 'customers.deals.kanban.quickDeal.more',\n 'More details (probability, close date, description)',\n ),\n fields: ['status', 'probability', 'expectedCloseAt', 'description'],\n },\n ],\n [currentUserLabel, t],\n )\n\n const handleSubmit = React.useCallback(\n async (values: QuickDealFormValues) => {\n if (!context) return\n const trimmedTitle = values.title.trim()\n if (!trimmedTitle.length) {\n throw createCrudFormError(titleRequiredMessage, { title: titleRequiredMessage })\n }\n const payload: Record<string, unknown> = {\n title: trimmedTitle,\n pipelineId: context.pipelineId,\n pipelineStageId: context.pipelineStageId,\n valueCurrency: values.valueCurrency,\n }\n if (typeof values.valueAmount === 'number' && Number.isFinite(values.valueAmount)) {\n payload.valueAmount = values.valueAmount\n }\n if (currentUserId) payload.ownerUserId = currentUserId\n if (values.companyId) payload.companyIds = [values.companyId]\n if (values.status) payload.status = values.status\n if (typeof values.probability === 'number' && Number.isFinite(values.probability)) {\n payload.probability = values.probability\n }\n if (values.expectedCloseAt && values.expectedCloseAt.trim().length) {\n payload.expectedCloseAt = values.expectedCloseAt\n }\n if (values.description && values.description.trim().length) {\n payload.description = values.description\n }\n const operation = () =>\n createCrud('customers/deals', payload, {\n errorMessage: translateWithFallback(t, 'customers.deals.create.error', 'Failed to create deal.'),\n })\n await runMutation({\n operation,\n context: {\n formId: QUICK_DEAL_CONTEXT_ID,\n resourceKind: 'customers.deal',\n retryLastMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.quickDeal.success',\n 'Deal added to {stage}.',\n { stage: context.pipelineStageLabel },\n ),\n 'success',\n )\n onCreated()\n onClose()\n },\n [context, currentUserId, onClose, onCreated, retryLastMutation, runMutation, t, titleRequiredMessage],\n )\n\n if (!context) return null\n\n return (\n <Dialog\n open={open}\n onOpenChange={(next) => {\n if (!next) onClose()\n }}\n >\n <DialogContent>\n <DialogHeader>\n <DialogTitle>\n {translateWithFallback(t, 'customers.deals.kanban.quickDeal.title', 'Quick deal')}\n </DialogTitle>\n <DialogDescription>\n {translateWithFallback(\n t,\n 'customers.deals.kanban.quickDeal.context',\n 'Pipeline: {pipeline} \u00B7 Stage: {stage}',\n { pipeline: context.pipelineName, stage: context.pipelineStageLabel },\n )}\n </DialogDescription>\n </DialogHeader>\n\n <CrudForm<QuickDealFormValues>\n key={`${context.pipelineStageId}:${formInstanceKey}`}\n embedded\n fields={fields}\n groups={groups}\n // The \"More details\" group starts collapsed; users can expand to set\n // status / probability / close date / description. Mirrors the previous\n // \"+ More details\" toggle.\n collapsibleGroups={{ pageType: 'customers.deals.kanban.quickDeal' }}\n initialValues={initialValues}\n schema={formSchema}\n submitLabel={translateWithFallback(t, 'customers.deals.kanban.quickDeal.submit', 'Add deal')}\n onSubmit={handleSubmit}\n />\n </DialogContent>\n </Dialog>\n )\n}\n\nexport default QuickDealDialog\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }