@open-mercato/core 0.6.5-develop.5382.1.f542de69af → 0.6.5

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 (237) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/bootstrap.js +46 -6
  3. package/dist/bootstrap.js.map +2 -2
  4. package/dist/generated/entities/organization/index.js +2 -0
  5. package/dist/generated/entities/organization/index.js.map +2 -2
  6. package/dist/generated/entity-fields-registry.js +1 -0
  7. package/dist/generated/entity-fields-registry.js.map +2 -2
  8. package/dist/helpers/integration/crmFixtures.js +4 -0
  9. package/dist/helpers/integration/crmFixtures.js.map +2 -2
  10. package/dist/modules/attachments/api/route.js +2 -0
  11. package/dist/modules/attachments/api/route.js.map +2 -2
  12. package/dist/modules/attachments/lib/access.js +18 -0
  13. package/dist/modules/attachments/lib/access.js.map +2 -2
  14. package/dist/modules/audit_logs/data/entities.js +2 -1
  15. package/dist/modules/audit_logs/data/entities.js.map +2 -2
  16. package/dist/modules/audit_logs/migrations/Migration20260611104500.js +13 -0
  17. package/dist/modules/audit_logs/migrations/Migration20260611104500.js.map +7 -0
  18. package/dist/modules/audit_logs/services/accessLogService.js +10 -0
  19. package/dist/modules/audit_logs/services/accessLogService.js.map +2 -2
  20. package/dist/modules/auth/api/admin/nav.js +9 -0
  21. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  22. package/dist/modules/auth/api/login.js +4 -13
  23. package/dist/modules/auth/api/login.js.map +2 -2
  24. package/dist/modules/auth/data/entities.js +3 -1
  25. package/dist/modules/auth/data/entities.js.map +2 -2
  26. package/dist/modules/auth/lib/backendChrome.js +35 -2
  27. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  28. package/dist/modules/auth/lib/consentIntegrity.js +3 -3
  29. package/dist/modules/auth/lib/consentIntegrity.js.map +2 -2
  30. package/dist/modules/auth/migrations/Migration20260611103000.js +15 -0
  31. package/dist/modules/auth/migrations/Migration20260611103000.js.map +7 -0
  32. package/dist/modules/auth/services/authService.js +5 -3
  33. package/dist/modules/auth/services/authService.js.map +2 -2
  34. package/dist/modules/auth/services/rbacService.js +3 -2
  35. package/dist/modules/auth/services/rbacService.js.map +2 -2
  36. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
  37. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
  38. package/dist/modules/customers/api/deals/route.js +43 -2
  39. package/dist/modules/customers/api/deals/route.js.map +2 -2
  40. package/dist/modules/customers/api/deals/summary/route.js +402 -0
  41. package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
  42. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
  43. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
  44. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
  45. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
  46. package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
  47. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  48. package/dist/modules/customers/backend/customers/deals/page.js +221 -56
  49. package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
  50. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
  51. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  52. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +18 -0
  53. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  54. package/dist/modules/customers/cli.js +15 -9
  55. package/dist/modules/customers/cli.js.map +2 -2
  56. package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
  57. package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
  58. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
  59. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
  60. package/dist/modules/customers/components/detail/DealForm.js +100 -17
  61. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  62. package/dist/modules/customers/components/detail/PersonDetailTabs.js +11 -3
  63. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  64. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
  65. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  66. package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
  67. package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
  68. package/dist/modules/customers/lib/dealsMetrics.js +82 -0
  69. package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
  70. package/dist/modules/directory/api/organization-branding/route.js +214 -0
  71. package/dist/modules/directory/api/organization-branding/route.js.map +7 -0
  72. package/dist/modules/directory/api/organizations/route.js +7 -0
  73. package/dist/modules/directory/api/organizations/route.js.map +3 -3
  74. package/dist/modules/directory/backend/directory/branding/page.js +214 -0
  75. package/dist/modules/directory/backend/directory/branding/page.js.map +7 -0
  76. package/dist/modules/directory/backend/directory/branding/page.meta.js +26 -0
  77. package/dist/modules/directory/backend/directory/branding/page.meta.js.map +7 -0
  78. package/dist/modules/directory/commands/organizations.js +8 -1
  79. package/dist/modules/directory/commands/organizations.js.map +2 -2
  80. package/dist/modules/directory/data/entities.js +3 -0
  81. package/dist/modules/directory/data/entities.js.map +2 -2
  82. package/dist/modules/directory/data/validators.js +9 -0
  83. package/dist/modules/directory/data/validators.js.map +2 -2
  84. package/dist/modules/directory/migrations/Migration20260607222259_directory.js +13 -0
  85. package/dist/modules/directory/migrations/Migration20260607222259_directory.js.map +7 -0
  86. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
  87. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
  88. package/dist/modules/directory/utils/organizationScope.js +59 -27
  89. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  90. package/dist/modules/entities/api/definitions.batch.js +2 -1
  91. package/dist/modules/entities/api/definitions.batch.js.map +2 -2
  92. package/dist/modules/entities/api/entities.js +7 -0
  93. package/dist/modules/entities/api/entities.js.map +2 -2
  94. package/dist/modules/entities/api/records.js +26 -15
  95. package/dist/modules/entities/api/records.js.map +2 -2
  96. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
  97. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
  98. package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
  99. package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
  100. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
  101. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  102. package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
  103. package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
  104. package/dist/modules/query_index/data/entities.js +2 -1
  105. package/dist/modules/query_index/data/entities.js.map +2 -2
  106. package/dist/modules/query_index/lib/engine.js +4 -2
  107. package/dist/modules/query_index/lib/engine.js.map +2 -2
  108. package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js +16 -0
  109. package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js.map +7 -0
  110. package/dist/modules/sales/commands/documents.js +7 -5
  111. package/dist/modules/sales/commands/documents.js.map +2 -2
  112. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -1
  113. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  114. package/dist/modules/sales/components/documents/salesDocumentsColumns.js +10 -0
  115. package/dist/modules/sales/components/documents/salesDocumentsColumns.js.map +7 -0
  116. package/dist/modules/staff/api/team-members.js +9 -2
  117. package/dist/modules/staff/api/team-members.js.map +2 -2
  118. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
  119. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  120. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
  121. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  122. package/dist/modules/staff/commands/team-members.js +1 -1
  123. package/dist/modules/staff/commands/team-members.js.map +2 -2
  124. package/dist/modules/staff/components/TeamMemberForm.js +1 -1
  125. package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
  126. package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
  127. package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
  128. package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
  129. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  130. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
  131. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  132. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
  133. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  134. package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
  135. package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
  136. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
  137. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
  138. package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
  139. package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
  140. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
  141. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
  142. package/generated/entities/organization/index.ts +1 -0
  143. package/generated/entity-fields-registry.ts +1 -0
  144. package/package.json +11 -12
  145. package/src/bootstrap.ts +65 -7
  146. package/src/helpers/integration/crmFixtures.ts +21 -1
  147. package/src/modules/attachments/AGENTS.md +79 -0
  148. package/src/modules/attachments/api/route.ts +2 -0
  149. package/src/modules/attachments/lib/access.ts +36 -0
  150. package/src/modules/audit_logs/data/entities.ts +1 -0
  151. package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +10 -0
  152. package/src/modules/audit_logs/migrations/Migration20260611104500.ts +13 -0
  153. package/src/modules/audit_logs/services/accessLogService.ts +15 -0
  154. package/src/modules/auth/api/admin/nav.ts +9 -0
  155. package/src/modules/auth/api/login.ts +13 -13
  156. package/src/modules/auth/data/entities.ts +2 -0
  157. package/src/modules/auth/i18n/de.json +0 -1
  158. package/src/modules/auth/i18n/en.json +0 -1
  159. package/src/modules/auth/i18n/es.json +0 -1
  160. package/src/modules/auth/i18n/pl.json +0 -1
  161. package/src/modules/auth/lib/backendChrome.tsx +37 -1
  162. package/src/modules/auth/lib/consentIntegrity.ts +6 -3
  163. package/src/modules/auth/migrations/.snapshot-open-mercato.json +20 -0
  164. package/src/modules/auth/migrations/Migration20260611103000.ts +21 -0
  165. package/src/modules/auth/services/authService.ts +24 -4
  166. package/src/modules/auth/services/rbacService.ts +11 -2
  167. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
  168. package/src/modules/customers/api/deals/route.ts +51 -2
  169. package/src/modules/customers/api/deals/summary/route.ts +496 -0
  170. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
  171. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
  172. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
  173. package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
  174. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
  175. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +18 -0
  176. package/src/modules/customers/cli.ts +15 -15
  177. package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
  178. package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
  179. package/src/modules/customers/components/detail/DealForm.tsx +121 -19
  180. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +12 -2
  181. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
  182. package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
  183. package/src/modules/customers/i18n/de.json +43 -0
  184. package/src/modules/customers/i18n/en.json +43 -0
  185. package/src/modules/customers/i18n/es.json +43 -0
  186. package/src/modules/customers/i18n/pl.json +43 -0
  187. package/src/modules/customers/lib/dealsMetrics.ts +159 -0
  188. package/src/modules/directory/api/organization-branding/route.ts +238 -0
  189. package/src/modules/directory/api/organizations/route.ts +7 -0
  190. package/src/modules/directory/backend/directory/branding/page.meta.ts +24 -0
  191. package/src/modules/directory/backend/directory/branding/page.tsx +248 -0
  192. package/src/modules/directory/commands/organizations.ts +9 -1
  193. package/src/modules/directory/data/entities.ts +3 -0
  194. package/src/modules/directory/data/validators.ts +12 -0
  195. package/src/modules/directory/i18n/de.json +21 -0
  196. package/src/modules/directory/i18n/en.json +21 -0
  197. package/src/modules/directory/i18n/es.json +21 -0
  198. package/src/modules/directory/i18n/pl.json +21 -0
  199. package/src/modules/directory/migrations/.snapshot-open-mercato.json +40 -0
  200. package/src/modules/directory/migrations/Migration20260607222259_directory.ts +13 -0
  201. package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
  202. package/src/modules/directory/utils/organizationScope.ts +85 -30
  203. package/src/modules/entities/api/definitions.batch.ts +11 -7
  204. package/src/modules/entities/api/entities.ts +11 -0
  205. package/src/modules/entities/api/records.ts +46 -25
  206. package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
  207. package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
  208. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
  209. package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
  210. package/src/modules/entities/i18n/de.json +1 -0
  211. package/src/modules/entities/i18n/en.json +1 -0
  212. package/src/modules/entities/i18n/es.json +1 -0
  213. package/src/modules/entities/i18n/pl.json +1 -0
  214. package/src/modules/query_index/data/entities.ts +1 -0
  215. package/src/modules/query_index/lib/engine.ts +11 -5
  216. package/src/modules/query_index/migrations/.snapshot-open-mercato.json +11 -0
  217. package/src/modules/query_index/migrations/Migration20260611103000_query_index.ts +29 -0
  218. package/src/modules/sales/commands/documents.ts +7 -5
  219. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -1
  220. package/src/modules/sales/components/documents/salesDocumentsColumns.ts +6 -0
  221. package/src/modules/staff/api/team-members.ts +9 -2
  222. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
  223. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
  224. package/src/modules/staff/commands/team-members.ts +5 -2
  225. package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
  226. package/src/modules/staff/i18n/de.json +1 -0
  227. package/src/modules/staff/i18n/en.json +1 -0
  228. package/src/modules/staff/i18n/es.json +1 -0
  229. package/src/modules/staff/i18n/pl.json +1 -0
  230. package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
  231. package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
  232. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
  233. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
  234. package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
  235. package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
  236. package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
  237. package/src/modules/workflows/components/fields/FormFieldArrayEditor.tsx +2 -3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/customers/backend/customers/deals/pipeline/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { useQueries, useQuery, useQueryClient } from '@tanstack/react-query'\nimport {\n DndContext,\n DragOverlay,\n KeyboardSensor,\n MeasuringStrategy,\n PointerSensor,\n pointerWithin,\n useSensor,\n useSensors,\n type CollisionDetection,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core'\nimport { sortableKeyboardCoordinates } from '@dnd-kit/sortable'\nimport { ChevronLeft, ChevronRight, Layers, Plus, SlidersHorizontal, Workflow } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@open-mercato/ui/primitives/breadcrumb'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useCurrentUserId } from '@open-mercato/ui/backend/utils/useCurrentUserId'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport type { RowActionItem } from '@open-mercato/ui/backend/RowActions'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport type { FilterOptionTone } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { ViewTabsRow } from './components/ViewTabsRow'\nimport { LANE_WIDTH_CLASS } from './components/constants'\nimport { useCurrencyDictionary } from '../../../../components/detail/hooks/useCurrencyDictionary'\nimport { FilterBarRow, type KanbanFilterChip } from './components/FilterBarRow'\nimport { Lane, type LaneStage } from './components/Lane'\nimport { LaneCurrencyBreakdown } from './components/LaneCurrencyBreakdown'\nimport { CurrencyFilterPopover } from './components/CurrencyFilterPopover'\nimport { AddStageLane } from './components/AddStageLane'\nimport type { DealCardData } from './components/DealCard'\nimport {\n QuickDealDialog,\n type QuickDealContext,\n type QuickDealCompanyOption,\n} from './components/QuickDealDialog'\nimport { AddStageDialog, type AddStageContext } from './components/AddStageDialog'\nimport { StatusFilterPopover } from './components/StatusFilterPopover'\nimport { PipelineFilterPopover } from './components/PipelineFilterPopover'\nimport { SortByPopover, type SortOption } from './components/SortByPopover'\nimport { EntityFilterPopover, type EntityFilterOption } from './components/EntityFilterPopover'\nimport { CloseDateFilterPopover, type CloseDateRange } from './components/CloseDateFilterPopover'\nimport { CustomizeViewDialog } from './components/CustomizeViewDialog'\nimport {\n ActivityComposerDialog,\n type ActivityComposerContext,\n} from './components/ActivityComposerDialog'\nimport { BulkActionsBar } from './components/BulkActionsBar'\nimport { ChangeStageDialog } from './components/ChangeStageDialog'\nimport { ChangeOwnerDialog } from './components/ChangeOwnerDialog'\nimport { buildCrudExportUrl, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { runBulkDelete, groupBulkDeleteFailures } from '@open-mercato/ui/backend/utils/bulkDelete'\nimport {\n fetchAssignableStaffMembers,\n type AssignableStaffMember,\n} from '../../../../components/detail/assignableStaff'\n\ntype PipelineRecord = { id: string; name: string; isDefault: boolean }\n\ntype StageAggregateRow = {\n stageId: string\n count: number\n openCount: number\n totalInBaseCurrency: number\n byCurrency: Array<{ currency: string; total: number; count: number }>\n convertedAll?: boolean\n missingRateCurrencies?: string[]\n}\n\ntype StageAggregateResponse = {\n baseCurrencyCode: string | null\n perStage: StageAggregateRow[]\n}\n\ntype BulkJobResponse = {\n ok: boolean\n progressJobId: string | null\n message?: string\n}\n\nexport type LaneAggregate = {\n count: number\n totalInBaseCurrency: number\n byCurrency: Array<{ currency: string; total: number; count: number }>\n baseCurrencyCode: string | null\n // Both fields default to \"best case\" so an older aggregate response (without these\n // fields) doesn't trigger spurious \"partial conversion\" warnings in the UI.\n convertedAll: boolean\n missingRateCurrencies: string[]\n}\ntype PipelineStageRecord = {\n id: string\n label: string\n order: number\n pipelineId: string\n // `color` is sourced from the `customer_dictionary_entries` row that backs each stage\n // (kind = 'pipeline_stage'). After the 2026-05-19 hex\u2192tone migration this stores a\n // canonical tone identifier ('success', 'warning', etc.) rather than a hex string.\n color?: string | null\n}\n\ntype DealApiRecord = Record<string, unknown> & {\n id: string\n title?: string | null\n status?: string | null\n pipeline_stage?: string | null\n pipeline_id?: string | null\n pipeline_stage_id?: string | null\n value_amount?: number | string | null\n value_currency?: string | null\n probability?: number | string | null\n expected_close_at?: string | null\n created_at?: string | null\n updated_at?: string | null\n owner_user_id?: string | null\n companies?: Array<{ id?: unknown; label?: unknown } | null> | null\n people?: Array<{ id?: unknown; label?: unknown } | null> | null\n _pipeline?: {\n openActivitiesCount?: number\n daysInCurrentStage?: number\n isStuck?: boolean\n isOverdue?: boolean\n } | null\n}\n\nconst DEFAULT_SORT: SortOption = 'updated_desc'\nconst LANE_PAGE_SIZE = 25\nconst FALLBACK_TONES: FilterOptionTone[] = ['success', 'warning', 'error', 'info', 'neutral', 'brand']\n// Module-level singleton so empty lanes get the same array reference every render \u2014\n// React.memo's identity check on the `deals` prop holds across renders.\nconst EMPTY_DEAL_ARRAY: DealCardData[] = []\n\n// Lane width / resize configuration (~20% larger than original Figma spec for readability)\nconst DEFAULT_LANE_WIDTH = 308\nconst MIN_LANE_WIDTH = 240\nconst MAX_LANE_WIDTH = 576\nconst LANE_GAP = 14\nconst LANE_WIDTHS_STORAGE_KEY_PREFIX = 'kanban-lane-widths-v2'\n\nfunction loadLaneWidths(scopeKey: string): Record<string, number> {\n if (typeof window === 'undefined') return {}\n try {\n const raw = window.localStorage.getItem(`${LANE_WIDTHS_STORAGE_KEY_PREFIX}:${scopeKey}`)\n if (!raw) return {}\n const parsed = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object') return {}\n const out: Record<string, number> = {}\n for (const [k, v] of Object.entries(parsed)) {\n if (typeof v === 'number' && Number.isFinite(v) && v >= MIN_LANE_WIDTH && v <= MAX_LANE_WIDTH) {\n out[k] = v\n }\n }\n return out\n } catch {\n return {}\n }\n}\n\nfunction saveLaneWidths(scopeKey: string, widths: Record<string, number>) {\n if (typeof window === 'undefined') return\n try {\n if (Object.keys(widths).length === 0) {\n window.localStorage.removeItem(`${LANE_WIDTHS_STORAGE_KEY_PREFIX}:${scopeKey}`)\n } else {\n window.localStorage.setItem(`${LANE_WIDTHS_STORAGE_KEY_PREFIX}:${scopeKey}`, JSON.stringify(widths))\n }\n } catch {}\n}\n\nfunction normalizeAmount(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\nfunction normalizeProbability(value: unknown): number | null {\n const parsed = normalizeAmount(value)\n if (parsed === null) return null\n return Math.min(Math.max(Math.round(parsed), 0), 100)\n}\n\nfunction normalizeIso(value: unknown): string | null {\n if (typeof value !== 'string' || !value.trim().length) return null\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? null : date.toISOString()\n}\n\nfunction normalizeAssociation(entry: unknown): { id: string; label: string } | null {\n if (!entry || typeof entry !== 'object') return null\n const ref = entry as Record<string, unknown>\n const id = typeof ref.id === 'string' ? ref.id : null\n if (!id) return null\n const label =\n typeof ref.label === 'string' && ref.label.trim().length ? ref.label.trim() : id\n return { id, label }\n}\n\nfunction mapDealRecord(item: DealApiRecord, fallbackTitle: string): DealCardData | null {\n const id = typeof item.id === 'string' ? item.id : null\n if (!id) return null\n const title =\n typeof item.title === 'string' && item.title.trim().length ? item.title.trim() : fallbackTitle\n const status =\n typeof item.status === 'string' && item.status.trim().length ? item.status.trim() : null\n const valueAmount = normalizeAmount(item.value_amount)\n const valueCurrency =\n typeof item.value_currency === 'string' && item.value_currency.trim().length\n ? item.value_currency.trim().toUpperCase()\n : null\n const probability = normalizeProbability(item.probability)\n const expectedCloseAt = normalizeIso(item.expected_close_at)\n const createdAt = normalizeIso(item.created_at)\n const updatedAt = normalizeIso(item.updated_at)\n const ownerUserId =\n typeof item.owner_user_id === 'string' && item.owner_user_id.trim().length\n ? item.owner_user_id\n : null\n const companies = Array.isArray(item.companies)\n ? (item.companies.map(normalizeAssociation).filter(Boolean) as { id: string; label: string }[])\n : []\n const primaryCompany = companies[0] ?? null\n const pipelineState = {\n openActivitiesCount:\n typeof item._pipeline?.openActivitiesCount === 'number' ? item._pipeline.openActivitiesCount : 0,\n daysInCurrentStage:\n typeof item._pipeline?.daysInCurrentStage === 'number' ? item._pipeline.daysInCurrentStage : 0,\n isStuck: !!item._pipeline?.isStuck,\n isOverdue: !!item._pipeline?.isOverdue,\n }\n return {\n id,\n title,\n status,\n valueAmount,\n valueCurrency,\n probability,\n expectedCloseAt,\n createdAt,\n updatedAt,\n owner: ownerUserId ? { userId: ownerUserId, label: '' } : null,\n primaryCompany,\n pipelineState,\n }\n}\n\n// Canonical tone identifiers stored in `customer_dictionary_entries.color` after the\n// 2026-05-19 hex\u2192tone migration. Keep in sync with `TONE_OPTIONS` in `AddStageDialog.tsx`.\nconst KNOWN_STAGE_TONES: ReadonlySet<string> = new Set([\n 'success',\n 'warning',\n 'info',\n 'error',\n 'neutral',\n 'brand',\n 'pink',\n])\n\nfunction buildLaneStages(\n stages: PipelineStageRecord[],\n unassignedLabel: string,\n hasUnassigned: boolean,\n): LaneStage[] {\n const sorted = stages.slice().sort((a, b) => a.order - b.order)\n const lanes: LaneStage[] = sorted.map((stage, index) => ({\n id: stage.id,\n label: stage.label,\n // Prefer the stage's persisted tone (now stored as a canonical identifier post-migration)\n // so colors picked by operators in AddStageDialog actually show up on the kanban. Falls\n // back to the position-based rotation for stages with no color set or unmapped legacy\n // values that survived the migration's neutral-fallback arm.\n tone:\n stage.color && KNOWN_STAGE_TONES.has(stage.color)\n ? (stage.color as FilterOptionTone)\n : FALLBACK_TONES[index % FALLBACK_TONES.length],\n }))\n if (hasUnassigned) {\n lanes.push({ id: '__unassigned', label: unassignedLabel, tone: 'neutral' })\n }\n return lanes\n}\n\nfunction groupDealsByStageId(deals: DealCardData[], stageIdByDealId: Map<string, string>): Map<string, DealCardData[]> {\n const grouped = new Map<string, DealCardData[]>()\n for (const deal of deals) {\n const stageKey = stageIdByDealId.get(deal.id) ?? '__unassigned'\n const bucket = grouped.get(stageKey) ?? []\n bucket.push(deal)\n grouped.set(stageKey, bucket)\n }\n return grouped\n}\n\n/**\n * Translate the UI SortOption into the deals CRUD API's `sortField` / `sortDir` query params.\n *\n * Returns `null` for `owner_asc` because the deals table only stores `owner_user_id` (a UUID);\n * the UI displays a resolved owner name, so a UUID-alphabetical server sort would be misleading.\n * For that case the caller falls back to a sensible default sort server-side and re-sorts the\n * page client-side via `sortDeals`. All other options sort server-side over the full result set,\n * so paging (25/lane \u2192 Show more) keeps a globally correct order.\n */\nfunction mapSortOptionToApi(option: SortOption): { sortField: string; sortDir: 'asc' | 'desc' } | null {\n switch (option) {\n case 'updated_desc':\n return { sortField: 'updatedAt', sortDir: 'desc' }\n case 'updated_asc':\n return { sortField: 'updatedAt', sortDir: 'asc' }\n case 'created_desc':\n return { sortField: 'createdAt', sortDir: 'desc' }\n case 'value_desc':\n return { sortField: 'value', sortDir: 'desc' }\n case 'value_asc':\n return { sortField: 'value', sortDir: 'asc' }\n case 'probability_desc':\n return { sortField: 'probability', sortDir: 'desc' }\n case 'close_asc':\n return { sortField: 'expectedCloseAt', sortDir: 'asc' }\n case 'owner_asc':\n default:\n return null\n }\n}\n\nfunction sortDeals(deals: DealCardData[], option: SortOption): DealCardData[] {\n const sorted = deals.slice()\n sorted.sort((a, b) => {\n switch (option) {\n case 'value_desc':\n case 'value_asc': {\n const aValue = typeof a.valueAmount === 'number' ? a.valueAmount : Number.NEGATIVE_INFINITY\n const bValue = typeof b.valueAmount === 'number' ? b.valueAmount : Number.NEGATIVE_INFINITY\n return option === 'value_desc' ? bValue - aValue : aValue - bValue\n }\n case 'probability_desc': {\n const aProb = typeof a.probability === 'number' ? a.probability : -1\n const bProb = typeof b.probability === 'number' ? b.probability : -1\n return bProb - aProb\n }\n case 'close_asc': {\n const aTs = a.expectedCloseAt ? new Date(a.expectedCloseAt).getTime() : Number.POSITIVE_INFINITY\n const bTs = b.expectedCloseAt ? new Date(b.expectedCloseAt).getTime() : Number.POSITIVE_INFINITY\n return aTs - bTs\n }\n case 'created_desc': {\n const aTs = a.createdAt ? new Date(a.createdAt).getTime() : 0\n const bTs = b.createdAt ? new Date(b.createdAt).getTime() : 0\n return bTs - aTs\n }\n case 'owner_asc': {\n // Owner sort is client-side only: deals carry `owner.userId` (UUID), and the human-readable\n // label is resolved separately via ownerNamesById. Compare on the resolved label that was\n // already merged into deal.owner.label by `dealsByStage`'s ownerNamesById pass.\n const aLabel = a.owner?.label ?? a.owner?.userId ?? ''\n const bLabel = b.owner?.label ?? b.owner?.userId ?? ''\n return aLabel.localeCompare(bLabel)\n }\n case 'updated_asc':\n return (a.updatedAt ?? a.createdAt ?? '').localeCompare(b.updatedAt ?? b.createdAt ?? '')\n case 'updated_desc':\n default:\n return (b.updatedAt ?? b.createdAt ?? '').localeCompare(a.updatedAt ?? a.createdAt ?? '')\n }\n })\n return sorted\n}\n\nexport default function DealsKanbanPage(): React.ReactElement {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const [selectedPipelineId, setSelectedPipelineId] = React.useState<string | null>(null)\n const [search, setSearch] = React.useState('')\n const [sortBy, setSortBy] = React.useState<SortOption>(DEFAULT_SORT)\n const [statusFilters, setStatusFilters] = React.useState<string[]>([])\n const [ownerFilters, setOwnerFilters] = React.useState<string[]>([])\n const [peopleFilters, setPeopleFilters] = React.useState<string[]>([])\n const [companyFilters, setCompanyFilters] = React.useState<string[]>([])\n const [closeDateFilter, setCloseDateFilter] = React.useState<CloseDateRange>({ from: null, to: null })\n // Currency filter narrows visible deal cards (list query) but DOES NOT narrow the aggregate\n // query \u2014 lane headers + the Currency popover keep the full breakdown so the operator can\n // see what they're filtering for and change selection without losing context. `null` = \"All\n // currencies\" sentinel from the Figma popover (1251:699).\n const [currencyFilter, setCurrencyFilter] = React.useState<string | null>(null)\n // Cache labels for selected ids so the chip can show readable text without re-fetching\n const [ownerLabels, setOwnerLabels] = React.useState<Record<string, string>>({})\n const [peopleLabels, setPeopleLabels] = React.useState<Record<string, string>>({})\n const [companyLabels, setCompanyLabels] = React.useState<Record<string, string>>({})\n const [selectedDealIds, setSelectedDealIds] = React.useState<Set<string>>(new Set())\n const [pendingDealId, setPendingDealId] = React.useState<string | null>(null)\n const [quickDealContext, setQuickDealContext] = React.useState<QuickDealContext | null>(null)\n const [addStageContext, setAddStageContext] = React.useState<AddStageContext | null>(null)\n const [customizeOpen, setCustomizeOpen] = React.useState(false)\n const [activityContext, setActivityContext] = React.useState<ActivityComposerContext | null>(null)\n const [changeStageOpen, setChangeStageOpen] = React.useState(false)\n const [changeOwnerOpen, setChangeOwnerOpen] = React.useState(false)\n const [isBulkMutating, setIsBulkMutating] = React.useState(false)\n const [singleMoveStageContext, setSingleMoveStageContext] = React.useState<\n { dealId: string; currentStageId: string | null } | null\n >(null)\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const fallbackTitle = translateWithFallback(\n t,\n 'customers.deals.pipeline.untitled',\n 'Untitled deal',\n )\n const unassignedLabel = translateWithFallback(\n t,\n 'customers.deals.pipeline.unassigned',\n 'No stage',\n )\n\n const pipelinesQuery = useQuery<PipelineRecord[]>({\n queryKey: ['customers', 'pipelines', `scope:${scopeVersion}`],\n staleTime: 60_000,\n queryFn: async () => {\n const payload = await readApiResultOrThrow<{ items: PipelineRecord[] }>(\n '/api/customers/pipelines',\n undefined,\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.pipeline.loadError',\n 'Failed to load pipelines.',\n ),\n },\n )\n return payload?.items ?? []\n },\n })\n\n React.useEffect(() => {\n if (selectedPipelineId) return\n const pipelines = pipelinesQuery.data\n if (!pipelines || !pipelines.length) return\n const defaultPipeline = pipelines.find((pipeline) => pipeline.isDefault) ?? pipelines[0]\n if (defaultPipeline) setSelectedPipelineId(defaultPipeline.id)\n }, [pipelinesQuery.data, selectedPipelineId])\n\n const staffQuery = useQuery<AssignableStaffMember[]>({\n queryKey: ['customers', 'deals', 'kanban', 'staff', `scope:${scopeVersion}`],\n staleTime: 300_000,\n queryFn: async () => fetchAssignableStaffMembers('', { pageSize: 100 }),\n })\n\n const ownerNamesById = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const member of staffQuery.data ?? []) {\n if (member.userId && member.displayName) map.set(member.userId, member.displayName)\n }\n return map\n }, [staffQuery.data])\n\n const currentUserId = useCurrentUserId()\n const currentUserLabel = React.useMemo(() => {\n if (!currentUserId) return undefined\n return ownerNamesById.get(currentUserId)\n }, [currentUserId, ownerNamesById])\n\n // Per-user, per-pipeline persisted lane widths. localStorage is per-browser per-user \u2014 exactly\n // matches the \"remember my preferred column widths\" UX from Monday / Asana / ClickUp.\n const laneWidthsScopeKey = React.useMemo(\n () => `${currentUserId ?? 'anon'}:${selectedPipelineId ?? 'none'}`,\n [currentUserId, selectedPipelineId],\n )\n const [laneWidths, setLaneWidths] = React.useState<Record<string, number>>({})\n // Hydrate from localStorage when the scope (user / pipeline) changes\n React.useEffect(() => {\n setLaneWidths(loadLaneWidths(laneWidthsScopeKey))\n }, [laneWidthsScopeKey])\n // Persist on change (debounced via setTimeout to coalesce drag-frame updates)\n React.useEffect(() => {\n const handle = window.setTimeout(() => saveLaneWidths(laneWidthsScopeKey, laneWidths), 250)\n return () => window.clearTimeout(handle)\n }, [laneWidthsScopeKey, laneWidths])\n\n const handleLaneResize = React.useCallback(\n (stageId: string, deltaPx: number) => {\n setLaneWidths((prev) => {\n const current = prev[stageId] ?? DEFAULT_LANE_WIDTH\n const next = Math.max(MIN_LANE_WIDTH, Math.min(MAX_LANE_WIDTH, current + deltaPx))\n if (next === current) return prev\n return { ...prev, [stageId]: next }\n })\n },\n [],\n )\n const handleResetLaneWidth = React.useCallback((stageId: string) => {\n setLaneWidths((prev) => {\n if (!(stageId in prev)) return prev\n const { [stageId]: _omit, ...rest } = prev\n return rest\n })\n }, [])\n const handleResetAllLaneWidths = React.useCallback(() => {\n setLaneWidths({})\n }, [])\n\n // Tenant currency list for the Quick deal dialog. The currencies module is the source\n // of truth (it backs `LaneAggregateProp.baseCurrencyCode` already); we used to hardcode\n // `['PLN','EUR','USD','GBP']` in the dialog which excluded any tenant operating in CZK,\n // SEK, HUF, BRL, etc. from quick-add.\n const currencyDictionary = useCurrencyDictionary()\n\n const companiesQuery = useQuery<QuickDealCompanyOption[]>({\n queryKey: ['customers', 'companies', 'kanban-quick-deal', `scope:${scopeVersion}`],\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<{ items?: Array<{ id?: unknown; display_name?: unknown }> }>(\n '/api/customers/companies?page=1&pageSize=100&sortField=display_name&sortDir=asc',\n )\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result!.items! : []\n const options: QuickDealCompanyOption[] = []\n for (const item of items) {\n const id = typeof item.id === 'string' ? item.id : null\n const displayName =\n typeof item.display_name === 'string' && item.display_name.trim().length\n ? item.display_name.trim()\n : null\n if (id && displayName) options.push({ id, label: displayName })\n }\n return options\n },\n })\n\n const stagesQuery = useQuery<PipelineStageRecord[]>({\n queryKey: ['customers', 'pipeline-stages', `scope:${scopeVersion}`, `pipeline:${selectedPipelineId}`],\n enabled: !!selectedPipelineId,\n staleTime: 30_000,\n queryFn: async () => {\n if (!selectedPipelineId) return []\n const payload = await readApiResultOrThrow<{ items: PipelineStageRecord[] }>(\n `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(selectedPipelineId)}`,\n undefined,\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.pipeline.loadError',\n 'Failed to load stages.',\n ),\n },\n )\n return payload?.items ?? []\n },\n })\n\n // Per-lane \"Show more\" state. Each lane's first 25 are loaded via useQueries; subsequent pages are\n // fetched on demand and appended here, so the cached page-1 cards stay in place (no full lane refetch).\n const [extraCardsByStage, setExtraCardsByStage] = React.useState<Record<string, DealApiRecord[]>>({})\n const [loadingMoreByStage, setLoadingMoreByStage] = React.useState<Record<string, boolean>>({})\n // Reset extra-cards + loading state when any filter changes\n React.useEffect(() => {\n setExtraCardsByStage({})\n setLoadingMoreByStage({})\n }, [selectedPipelineId, search, statusFilters, ownerFilters, peopleFilters, companyFilters, closeDateFilter, currencyFilter])\n\n // Filter signature shared by every per-lane query so all lanes invalidate together when filters change\n const filterSignature = React.useMemo(\n () => ({\n pipelineId: selectedPipelineId,\n search: search.trim(),\n status: statusFilters.slice().sort().join(','),\n owners: ownerFilters.slice().sort().join(','),\n people: peopleFilters.slice().sort().join(','),\n companies: companyFilters.slice().sort().join(','),\n closeFrom: closeDateFilter.from ?? '',\n closeTo: closeDateFilter.to ?? '',\n currency: currencyFilter ?? '',\n }),\n [selectedPipelineId, search, statusFilters, ownerFilters, peopleFilters, companyFilters, closeDateFilter, currencyFilter],\n )\n\n // Aggregate query \u2014 gives accurate per-stage counts + totals (in base currency) across the entire pipeline,\n // not just the loaded slice. Drives both lane headers and pagination decisions.\n const aggregateQuery = useQuery<StageAggregateResponse | null>({\n queryKey: [\n 'customers',\n 'deals',\n 'kanban-aggregate',\n `scope:${scopeVersion}`,\n `pipeline:${selectedPipelineId ?? 'none'}`,\n `search:${filterSignature.search}`,\n `status:${filterSignature.status}`,\n `owners:${filterSignature.owners}`,\n `people:${filterSignature.people}`,\n `companies:${filterSignature.companies}`,\n `close:${filterSignature.closeFrom}-${filterSignature.closeTo}`,\n ],\n enabled: !!selectedPipelineId,\n staleTime: 30_000,\n queryFn: async () => {\n if (!selectedPipelineId) return null\n const params = new URLSearchParams()\n params.set('pipelineId', selectedPipelineId)\n if (filterSignature.search.length) params.set('search', filterSignature.search)\n for (const status of statusFilters) params.append('status', status)\n for (const ownerId of ownerFilters) params.append('ownerUserId', ownerId)\n for (const personId of peopleFilters) params.append('personId', personId)\n for (const companyId of companyFilters) params.append('companyId', companyId)\n if (closeDateFilter.from) params.set('expectedCloseAtFrom', closeDateFilter.from)\n if (closeDateFilter.to) params.set('expectedCloseAtTo', closeDateFilter.to)\n const call = await apiCall<StageAggregateResponse>(\n `/api/customers/deals/aggregate?${params.toString()}`,\n )\n return call.ok ? call.result ?? null : null\n },\n })\n\n const aggregateByStage = React.useMemo(() => {\n const map = new Map<string, LaneAggregate>()\n const data = aggregateQuery.data\n if (!data) return map\n for (const row of data.perStage) {\n map.set(row.stageId, {\n count: row.count,\n totalInBaseCurrency: row.totalInBaseCurrency,\n byCurrency: row.byCurrency,\n baseCurrencyCode: data.baseCurrencyCode,\n // Default to \"complete conversion\" when the field is absent (older response, or no\n // base currency case where the route still returns the field). The aggregate route\n // explicitly sets `convertedAll: false` when at least one currency was excluded.\n convertedAll: row.convertedAll ?? true,\n missingRateCurrencies: row.missingRateCurrencies ?? [],\n })\n }\n return map\n }, [aggregateQuery.data])\n\n const stagesData = stagesQuery.data ?? []\n const aggregateData = aggregateQuery.data\n\n // Pre-compute the lane shape from stages + aggregate so unassigned cards still show up if any\n const lanes = React.useMemo<LaneStage[]>(() => {\n const knownStageIds = new Set(stagesData.map((stage) => stage.id))\n const hasUnassigned = (aggregateData?.perStage ?? []).some(\n (row) => row.stageId === '__unassigned' || (!!row.stageId && !knownStageIds.has(row.stageId)),\n )\n return buildLaneStages(stagesData, unassignedLabel, hasUnassigned)\n }, [stagesData, aggregateData, unassignedLabel])\n\n // Resolve the active SortOption to an API sort once per change. Owner sort has no server\n // representation (only owner_user_id exists in the deals table; the displayed name is resolved\n // client-side), so it falls back to `updatedAt desc` server-side and is re-sorted client-side.\n const apiSort = React.useMemo(() => {\n const mapped = mapSortOptionToApi(sortBy)\n return mapped ?? { sortField: 'updatedAt' as const, sortDir: 'desc' as const }\n }, [sortBy])\n\n const laneQueries = useQueries({\n queries: lanes.map((stage) => {\n return {\n queryKey: [\n 'customers',\n 'deals',\n 'kanban-lane',\n `scope:${scopeVersion}`,\n `pipeline:${filterSignature.pipelineId ?? 'none'}`,\n `search:${filterSignature.search}`,\n `status:${filterSignature.status}`,\n `owners:${filterSignature.owners}`,\n `people:${filterSignature.people}`,\n `companies:${filterSignature.companies}`,\n `close:${filterSignature.closeFrom}-${filterSignature.closeTo}`,\n // Sort affects which 25 deals come back on page 1, so it MUST be in the key \u2014\n // otherwise switching from \"Value (high to low)\" to \"Close (soonest)\" reuses\n // the old (wrong-sort) cache and silently keeps the wrong slice on screen.\n `sort:${apiSort.sortField}:${apiSort.sortDir}`,\n `currency:${filterSignature.currency}`,\n `stage:${stage.id}`,\n ],\n enabled: !!selectedPipelineId,\n staleTime: 30_000,\n queryFn: async () => {\n if (!selectedPipelineId) return { items: [] as DealApiRecord[], total: 0 }\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', String(LANE_PAGE_SIZE))\n params.set('pipelineId', selectedPipelineId)\n params.set('pipelineStageId', stage.id)\n params.set('sortField', apiSort.sortField)\n params.set('sortDir', apiSort.sortDir)\n if (filterSignature.search.length) params.set('search', filterSignature.search)\n for (const status of statusFilters) params.append('status', status)\n for (const ownerId of ownerFilters) params.append('ownerUserId', ownerId)\n for (const personId of peopleFilters) params.append('personId', personId)\n for (const companyId of companyFilters) params.append('companyId', companyId)\n if (closeDateFilter.from) params.set('expectedCloseAtFrom', closeDateFilter.from)\n if (closeDateFilter.to) params.set('expectedCloseAtTo', closeDateFilter.to)\n if (currencyFilter) params.set('valueCurrency', currencyFilter)\n const payload = await readApiResultOrThrow<{ items?: DealApiRecord[]; total?: number }>(\n `/api/customers/deals?${params.toString()}`,\n undefined,\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.pipeline.loadError',\n 'Failed to load deals.',\n ),\n },\n )\n const items = Array.isArray(payload?.items) ? (payload!.items as DealApiRecord[]) : []\n return { items, total: typeof payload?.total === 'number' ? payload.total : items.length }\n },\n }\n }),\n })\n\n const laneState = React.useMemo(() => {\n const dealsByStage = new Map<string, DealCardData[]>()\n const stageIdByDealId = new Map<string, string>()\n const allDeals: DealCardData[] = []\n let total = 0\n laneQueries.forEach((query, idx) => {\n const stage = lanes[idx]\n if (!stage) return\n const firstPage = query.data?.items ?? []\n const extra = extraCardsByStage[stage.id] ?? []\n // Merge first page + appended pages, deduping by id (in case of races/optimistic moves)\n const seen = new Set<string>()\n const merged: DealApiRecord[] = []\n for (const item of [...firstPage, ...extra]) {\n if (!item.id || seen.has(item.id)) continue\n seen.add(item.id)\n merged.push(item)\n }\n const list: DealCardData[] = []\n for (const item of merged) {\n const mapped = mapDealRecord(item, fallbackTitle)\n if (!mapped) continue\n list.push(mapped)\n const stageId =\n typeof item.pipeline_stage_id === 'string' && item.pipeline_stage_id.trim().length\n ? item.pipeline_stage_id\n : '__unassigned'\n stageIdByDealId.set(mapped.id, stageId)\n allDeals.push(mapped)\n }\n dealsByStage.set(stage.id, list)\n total += list.length\n })\n return { dealsByStage, stageIdByDealId, allDeals, total }\n }, [laneQueries, lanes, fallbackTitle, extraCardsByStage])\n\n const rawDeals = laneState.allDeals\n const stageIdByDealId = laneState.stageIdByDealId\n const total = aggregateData?.perStage?.reduce((sum, row) => sum + row.count, 0) ?? laneState.total\n\n /**\n * Sum of `totalInBaseCurrency` across every visible lane \u2014 gives the operator one\n * authoritative \"what's this pipeline worth?\" number at the top of the kanban. Also\n * aggregates the per-currency breakdown so the same `LaneCurrencyBreakdown` popover\n * can show the board-wide split. We compute `convertedAll` as the AND of every lane's\n * flag: any single missing-rate currency drops the board to \"partial\" so the operator\n * can't read the headline as authoritative.\n */\n const boardSummary = React.useMemo(() => {\n const perStage = aggregateData?.perStage ?? []\n if (perStage.length === 0) {\n return null\n }\n const totalsByCurrency = new Map<string, { total: number; count: number }>()\n let totalInBaseCurrency = 0\n let convertedAll = true\n const missingRateCurrencies = new Set<string>()\n for (const row of perStage) {\n totalInBaseCurrency += row.totalInBaseCurrency\n if (!(row.convertedAll ?? true)) convertedAll = false\n for (const c of row.missingRateCurrencies ?? []) missingRateCurrencies.add(c)\n for (const cur of row.byCurrency) {\n const entry = totalsByCurrency.get(cur.currency) ?? { total: 0, count: 0 }\n entry.total += cur.total\n entry.count += cur.count\n totalsByCurrency.set(cur.currency, entry)\n }\n }\n const rows = Array.from(totalsByCurrency.entries())\n .map(([currency, value]) => ({ currency, total: value.total, count: value.count }))\n .sort((a, b) => b.total - a.total)\n return {\n baseCurrencyCode: aggregateData?.baseCurrencyCode ?? null,\n totalInBaseCurrency,\n convertedAll,\n missingRateCurrencies: Array.from(missingRateCurrencies),\n rows,\n }\n }, [aggregateData])\n\n const deals = React.useMemo(() => {\n if (ownerNamesById.size === 0) return rawDeals\n return rawDeals.map((deal) => {\n if (!deal.owner) return deal\n const resolvedLabel = ownerNamesById.get(deal.owner.userId)\n if (!resolvedLabel || resolvedLabel === deal.owner.label) return deal\n return { ...deal, owner: { ...deal.owner, label: resolvedLabel } }\n })\n }, [ownerNamesById, rawDeals])\n\n const dealsByStage = React.useMemo(() => {\n const map = new Map<string, DealCardData[]>()\n if (ownerNamesById.size === 0) {\n for (const [stageId, list] of laneState.dealsByStage) map.set(stageId, list)\n return map\n }\n for (const [stageId, list] of laneState.dealsByStage) {\n map.set(\n stageId,\n list.map((deal) => {\n if (!deal.owner) return deal\n const resolvedLabel = ownerNamesById.get(deal.owner.userId)\n if (!resolvedLabel || resolvedLabel === deal.owner.label) return deal\n return { ...deal, owner: { ...deal.owner, label: resolvedLabel } }\n }),\n )\n }\n return map\n }, [laneState.dealsByStage, ownerNamesById])\n\n // Pre-sort each lane's deals once per data/sort change. This keeps the array reference\n // stable across page re-renders (e.g. drag-start, filter changes that don't touch this\n // lane), so Lane.memo's `prev.deals !== next.deals` check stays cheap \u2014 without this\n // the inline `sortDeals(...)` in the JSX allocated a new array every render and busted\n // every Lane's memo, cascading reconciliation through every memoized DealCard.\n const sortedDealsByStage = React.useMemo(() => {\n const map = new Map<string, DealCardData[]>()\n for (const [stageId, list] of dealsByStage) {\n map.set(stageId, sortDeals(list, sortBy))\n }\n return map\n }, [dealsByStage, sortBy])\n\n const handleLoadMoreInLane = React.useCallback(\n async (stageId: string) => {\n if (!selectedPipelineId) return\n if (loadingMoreByStage[stageId]) return\n // Page 1 = first 25 (initial useQueries fetch). Subsequent pages come from extraCardsByStage.\n const already = extraCardsByStage[stageId]?.length ?? 0\n const nextPage = Math.floor((LANE_PAGE_SIZE + already) / LANE_PAGE_SIZE) + 1\n setLoadingMoreByStage((prev) => ({ ...prev, [stageId]: true }))\n try {\n const params = new URLSearchParams()\n params.set('page', String(nextPage))\n params.set('pageSize', String(LANE_PAGE_SIZE))\n params.set('pipelineId', selectedPipelineId)\n params.set('pipelineStageId', stageId)\n // Show-more MUST request the same sort as page 1; otherwise the appended cards land\n // in an arbitrary order relative to the cards already on screen.\n params.set('sortField', apiSort.sortField)\n params.set('sortDir', apiSort.sortDir)\n if (filterSignature.search.length) params.set('search', filterSignature.search)\n for (const status of statusFilters) params.append('status', status)\n for (const ownerId of ownerFilters) params.append('ownerUserId', ownerId)\n for (const personId of peopleFilters) params.append('personId', personId)\n for (const companyId of companyFilters) params.append('companyId', companyId)\n if (closeDateFilter.from) params.set('expectedCloseAtFrom', closeDateFilter.from)\n if (closeDateFilter.to) params.set('expectedCloseAtTo', closeDateFilter.to)\n if (currencyFilter) params.set('valueCurrency', currencyFilter)\n const call = await apiCall<{ items?: DealApiRecord[] }>(\n `/api/customers/deals?${params.toString()}`,\n )\n if (!call.ok) return\n const items = Array.isArray(call.result?.items) ? (call.result!.items as DealApiRecord[]) : []\n setExtraCardsByStage((prev) => ({\n ...prev,\n [stageId]: [...(prev[stageId] ?? []), ...items],\n }))\n } finally {\n setLoadingMoreByStage((prev) => {\n const next = { ...prev }\n delete next[stageId]\n return next\n })\n }\n },\n [\n selectedPipelineId,\n loadingMoreByStage,\n extraCardsByStage,\n filterSignature.search,\n statusFilters,\n ownerFilters,\n peopleFilters,\n companyFilters,\n closeDateFilter.from,\n closeDateFilter.to,\n apiSort,\n ],\n )\n\n // Page chrome loading/error indicators derived from per-lane queries\n const isInitialLoading =\n !!selectedPipelineId && lanes.length === 0\n ? stagesQuery.isLoading\n : laneQueries.some((laneQuery) => laneQuery.isLoading && !laneQuery.data)\n const firstError: unknown = laneQueries.find((laneQuery) => laneQuery.isError)?.error ?? null\n\n // Centralized invalidation so writes refresh per-lane card queries, the aggregate, and drop stale extras\n const invalidateKanbanData = React.useCallback(() => {\n queryClient\n .invalidateQueries({ queryKey: ['customers', 'deals', 'kanban-lane'] })\n .catch(() => {})\n queryClient\n .invalidateQueries({ queryKey: ['customers', 'deals', 'kanban-aggregate'] })\n .catch(() => {})\n // Clear appended pages \u2014 they'll be re-fetched on demand by the user\n setExtraCardsByStage({})\n }, [queryClient])\n\n const trackedBulkProgressJobIdsRef = React.useRef(new Set<string>())\n\n const trackBulkProgressJob = React.useCallback((jobId: string | null | undefined) => {\n if (!jobId) return\n trackedBulkProgressJobIdsRef.current.add(jobId)\n }, [])\n\n const clearTrackedBulkProgressJob = React.useCallback((jobId: string | null): boolean => {\n if (!jobId) return false\n return trackedBulkProgressJobIdsRef.current.delete(jobId)\n }, [])\n\n useAppEvent(\n 'progress.job.completed',\n (event) => {\n const payload = event.payload as { jobId?: unknown } | null | undefined\n const jobId = typeof payload?.jobId === 'string' ? payload.jobId : null\n if (!clearTrackedBulkProgressJob(jobId)) return\n invalidateKanbanData()\n },\n [clearTrackedBulkProgressJob, invalidateKanbanData],\n )\n\n useAppEvent(\n 'progress.job.failed',\n (event) => {\n const payload = event.payload as { jobId?: unknown } | null | undefined\n const jobId = typeof payload?.jobId === 'string' ? payload.jobId : null\n clearTrackedBulkProgressJob(jobId)\n },\n [clearTrackedBulkProgressJob],\n )\n\n useAppEvent(\n 'progress.job.cancelled',\n (event) => {\n const payload = event.payload as { jobId?: unknown } | null | undefined\n const jobId = typeof payload?.jobId === 'string' ? payload.jobId : null\n clearTrackedBulkProgressJob(jobId)\n },\n [clearTrackedBulkProgressJob],\n )\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),\n )\n\n const [activeDragDealId, setActiveDragDealId] = React.useState<string | null>(null)\n const activeDragDeal = React.useMemo(\n () => (activeDragDealId ? deals.find((deal) => deal.id === activeDragDealId) ?? null : null),\n [activeDragDealId, deals],\n )\n\n // Cards are draggable but NOT droppable; only lanes are droppable. That means collision\n // detection iterates only ~7 lane rects per pointer move. `pointerWithin` is the right\n // semantic \u2014 return the lane whose rect contains the pointer. We DROPPED the\n // `rectIntersection` fallback because it was running on every move alongside `pointerWithin`\n // (2\u00D7 the rect math), and the only case it covered (cursor outside every lane rect) is\n // already handled by dnd-kit returning empty collisions \u2192 no drop target shown.\n const collisionDetection = React.useCallback<CollisionDetection>((args) => pointerWithin(args), [])\n\n // Measure droppables once at drag start (not on every pointer move). Kanban lanes don't\n // resize during a drag, so re-measuring on every move is pure overhead \u2014 this single line\n // is the biggest single-shot perf win for dnd-kit kanbans.\n // dnd-kit will still automatically re-measure if a layout-affecting change happens (e.g.\n // the scroller scrolls or a new lane appears).\n const measuringConfig = React.useMemo(\n () => ({ droppable: { strategy: MeasuringStrategy.BeforeDragging } }),\n [],\n )\n\n const moveMutationContextId = 'customers-deals-kanban:stage-move'\n const { runMutation: runMoveMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: moveMutationContextId,\n blockedMessage: translateWithFallback(\n t,\n 'ui.forms.flash.saveBlocked',\n 'Save blocked by validation',\n ),\n })\n\n // Deal mutation context is declared here (not next to the dialog/bulk handlers further\n // down) because `bulkMoveDealsToStage` \u2014 declared right after `moveDealToStage` so\n // `handleDragEnd` can dispatch to it \u2014 depends on `runDealMutation`/`retryDealMutation`.\n // Hoisting the hook keeps the lexical ordering valid for `useCallback` dependency arrays.\n const dealMutationContextId = 'customers-deals-kanban:deal-mutation'\n const { runMutation: runDealMutation, retryLastMutation: retryDealMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: dealMutationContextId,\n blockedMessage: translateWithFallback(\n t,\n 'ui.forms.flash.saveBlocked',\n 'Save blocked by validation',\n ),\n })\n\n const moveDealToStage = React.useCallback(\n (dealId: string, targetStageId: string) => {\n if (!targetStageId || targetStageId === '__unassigned') return\n const currentStageId = stageIdByDealId.get(dealId)\n if (currentStageId === targetStageId) return\n\n // Optimistic update: move the card between the per-lane query caches\n const laneCachePredicate = { queryKey: ['customers', 'deals', 'kanban-lane'] as const }\n const snapshot = queryClient.getQueriesData<{ items: DealApiRecord[]; total: number }>(laneCachePredicate)\n const extraSnapshot = extraCardsByStage\n let movingItem: DealApiRecord | null = null\n for (const [key, data] of snapshot) {\n if (!data) continue\n const idx = data.items.findIndex((it) => it.id === dealId)\n if (idx < 0) continue\n const stageKey = (key as readonly unknown[]).find((part) => typeof part === 'string' && (part as string).startsWith('stage:'))\n if (typeof stageKey !== 'string') continue\n const stageId = stageKey.slice('stage:'.length)\n // remove from source lane's page 1 cache\n if (stageId === currentStageId) {\n queryClient.setQueryData(key, {\n ...data,\n items: data.items.filter((it) => it.id !== dealId),\n total: Math.max(0, data.total - 1),\n })\n movingItem = { ...data.items[idx], pipeline_stage_id: targetStageId }\n }\n }\n // Also check extra pages for the card if it wasn't on page 1\n if (!movingItem && currentStageId) {\n const extras = extraCardsByStage[currentStageId] ?? []\n const found = extras.find((it) => it.id === dealId)\n if (found) movingItem = { ...found, pipeline_stage_id: targetStageId }\n }\n // Remove from source lane's extra-pages cache\n if (currentStageId && extraCardsByStage[currentStageId]?.some((it) => it.id === dealId)) {\n setExtraCardsByStage((prev) => {\n const list = prev[currentStageId] ?? []\n return { ...prev, [currentStageId]: list.filter((it) => it.id !== dealId) }\n })\n }\n if (movingItem) {\n // add to target lane caches (if they are loaded)\n for (const [key, data] of snapshot) {\n if (!data) continue\n const stageKey = (key as readonly unknown[]).find((part) => typeof part === 'string' && (part as string).startsWith('stage:'))\n if (typeof stageKey !== 'string') continue\n const stageId = stageKey.slice('stage:'.length)\n if (stageId !== targetStageId) continue\n queryClient.setQueryData(key, {\n ...data,\n items: [movingItem!, ...data.items],\n total: data.total + 1,\n })\n }\n }\n setPendingDealId(dealId)\n\n // The moved deal's version, so a concurrent edit/move from another tab is\n // refused instead of silently overwritten (#2055).\n const dealVersion = typeof movingItem?.updated_at === 'string' ? movingItem.updated_at : null\n\n runMoveMutation({\n operation: async () => {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dealVersion),\n () => apiCallOrThrow(\n '/api/customers/deals',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: dealId, pipelineStageId: targetStageId }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.pipeline.moveError',\n 'Failed to update deal stage.',\n ),\n },\n ),\n )\n },\n context: {\n formId: moveMutationContextId,\n resourceKind: 'customers.deal',\n resourceId: dealId,\n retryLastMutation,\n },\n })\n .then(() => {\n flash(\n translateWithFallback(t, 'customers.deals.pipeline.moveSuccess', 'Deal updated.'),\n 'success',\n )\n })\n .catch((error: unknown) => {\n // Roll back optimistic update by restoring the prior cache state\n for (const [key, data] of snapshot) {\n queryClient.setQueryData(key, data)\n }\n // Restore extra-pages cache to pre-move snapshot\n setExtraCardsByStage(extraSnapshot)\n // A stale move surfaces the unified conflict bar \u2014 skip the generic flash.\n if (surfaceRecordConflict(error, t)) return\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.pipeline.moveError',\n 'Failed to update deal stage.',\n )\n flash(message, 'error')\n })\n .finally(() => {\n setPendingDealId(null)\n invalidateKanbanData()\n })\n },\n [invalidateKanbanData, queryClient, retryLastMutation, runMoveMutation, stageIdByDealId, t],\n )\n\n // Round-2 UX review item 32: when the operator drags a card that is part of a multi-card\n // selection, every selected card must travel with it. We mirror `moveDealToStage`'s\n // optimistic-update pattern but apply it across every id in `dealIds`, capture a single\n // pre-batch snapshot of every lane cache (so rollback restores the full pre-batch state\n // atomically on failure), and dispatch the same `/api/customers/deals/bulk-update-stage`\n // endpoint that powers the `BulkActionsBar` \"Change stage\" menu. The worker is async, so\n // we intentionally do NOT call `invalidateKanbanData()` after a successful POST \u2014 that\n // would refetch the pre-move server state and flicker the optimistic UI back. The\n // `progress.job.completed` listener already triggers an invalidate when the worker is\n // actually done, which is what we want.\n const bulkMoveDealsToStage = React.useCallback(\n async (dealIds: string[], targetStageId: string) => {\n if (!targetStageId || targetStageId === '__unassigned' || dealIds.length === 0) return\n const uniqueIds = Array.from(new Set(dealIds))\n const idsToMove = uniqueIds.filter((id) => stageIdByDealId.get(id) !== targetStageId)\n if (idsToMove.length === 0) return\n\n const laneCachePredicate = { queryKey: ['customers', 'deals', 'kanban-lane'] as const }\n const snapshot = queryClient.getQueriesData<{ items: DealApiRecord[]; total: number }>(laneCachePredicate)\n const extraSnapshot = extraCardsByStage\n const newExtras: Record<string, DealApiRecord[]> = { ...extraCardsByStage }\n\n for (const dealId of idsToMove) {\n const currentStageId = stageIdByDealId.get(dealId)\n if (!currentStageId) continue\n let movingItem: DealApiRecord | null = null\n for (const [key, data] of snapshot) {\n if (!data) continue\n const stageKey = (key as readonly unknown[]).find(\n (part) => typeof part === 'string' && (part as string).startsWith('stage:'),\n )\n if (typeof stageKey !== 'string') continue\n const stageId = stageKey.slice('stage:'.length)\n if (stageId !== currentStageId) continue\n const current = queryClient.getQueryData<{ items: DealApiRecord[]; total: number }>(\n key as readonly unknown[],\n )\n if (!current) continue\n const idx = current.items.findIndex((it) => it.id === dealId)\n if (idx < 0) continue\n movingItem = { ...current.items[idx], pipeline_stage_id: targetStageId }\n queryClient.setQueryData(key, {\n ...current,\n items: current.items.filter((it) => it.id !== dealId),\n total: Math.max(0, current.total - 1),\n })\n break\n }\n if (!movingItem) {\n const extras = newExtras[currentStageId] ?? []\n const found = extras.find((it) => it.id === dealId)\n if (found) {\n movingItem = { ...found, pipeline_stage_id: targetStageId }\n newExtras[currentStageId] = extras.filter((it) => it.id !== dealId)\n }\n } else if (newExtras[currentStageId]?.some((it) => it.id === dealId)) {\n newExtras[currentStageId] = (newExtras[currentStageId] ?? []).filter((it) => it.id !== dealId)\n }\n if (!movingItem) continue\n for (const [key, data] of snapshot) {\n if (!data) continue\n const stageKey = (key as readonly unknown[]).find(\n (part) => typeof part === 'string' && (part as string).startsWith('stage:'),\n )\n if (typeof stageKey !== 'string') continue\n const stageId = stageKey.slice('stage:'.length)\n if (stageId !== targetStageId) continue\n const current = queryClient.getQueryData<{ items: DealApiRecord[]; total: number }>(\n key as readonly unknown[],\n )\n if (!current) continue\n queryClient.setQueryData(key, {\n ...current,\n items: [movingItem, ...current.items],\n total: current.total + 1,\n })\n }\n }\n setExtraCardsByStage(newExtras)\n\n setIsBulkMutating(true)\n let progressJobId: string | null = null\n try {\n await runDealMutation({\n operation: async () => {\n const call = await apiCallOrThrow<BulkJobResponse>(\n '/api/customers/deals/bulk-update-stage',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n ids: idsToMove,\n pipelineStageId: targetStageId,\n }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.error',\n 'Failed to start bulk stage update.',\n ),\n },\n )\n progressJobId = call.result?.progressJobId ?? null\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deals.bulk_stage',\n resourceId: idsToMove.join(','),\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.queued',\n 'Bulk stage update started ({count} deals).',\n { count: idsToMove.length },\n ),\n 'success',\n )\n if (progressJobId) {\n trackBulkProgressJob(progressJobId)\n }\n // Selection persists after a successful bulk drag \u2014 Asana convention; lets the\n // operator drag the same set into another stage without re-selecting.\n } catch (error) {\n for (const [key, data] of snapshot) {\n queryClient.setQueryData(key, data)\n }\n setExtraCardsByStage(extraSnapshot)\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.error',\n 'Failed to start bulk stage update.',\n )\n flash(message, 'error')\n } finally {\n setIsBulkMutating(false)\n }\n },\n [\n dealMutationContextId,\n extraCardsByStage,\n queryClient,\n retryDealMutation,\n runDealMutation,\n stageIdByDealId,\n t,\n trackBulkProgressJob,\n ],\n )\n\n // Horizontal scroll arrows \u2014 let the user step lane-by-lane instead of using only the bottom slider.\n const boardScrollerRef = React.useRef<HTMLDivElement | null>(null)\n const LANE_STEP = 322 // 308px lane width + 14px gap (scaled 1.2x for readability)\n const [scrollEdges, setScrollEdges] = React.useState<{ atStart: boolean; atEnd: boolean }>({\n atStart: true,\n atEnd: false,\n })\n\n // Edge-detection tolerance in CSS px. Chrome may report fractional scrollLeft / scrollWidth\n // values when the page DPI is non-integer or the layout has sub-pixel sizing; a too-tight\n // threshold here disables the arrow before the user actually reaches the end.\n const EDGE_TOLERANCE = 4\n\n const updateScrollEdges = React.useCallback(() => {\n const el = boardScrollerRef.current\n if (!el) return\n const maxScroll = el.scrollWidth - el.clientWidth\n const nextAtStart = el.scrollLeft <= EDGE_TOLERANCE\n const nextAtEnd = maxScroll <= EDGE_TOLERANCE || el.scrollLeft >= maxScroll - EDGE_TOLERANCE\n // Functional update with equality check: avoids a full page re-render every frame of the\n // scroll animation when atStart/atEnd haven't actually flipped. Without this, the entire\n // 125+ card tree reconciled on every tick \u2192 effective frame rate collapsed.\n setScrollEdges((prev) =>\n prev.atStart === nextAtStart && prev.atEnd === nextAtEnd ? prev : { atStart: nextAtStart, atEnd: nextAtEnd },\n )\n }, [])\n\n // Combined ref + listener attachment. Using a callback ref guarantees we wire scroll/resize\n // listeners the moment the scroller div mounts (even when it's conditionally rendered behind\n // a loading guard), and cleanly tears them down when it unmounts.\n const cleanupScrollerRef = React.useRef<(() => void) | null>(null)\n const setBoardScroller = React.useCallback(\n (el: HTMLDivElement | null) => {\n cleanupScrollerRef.current?.()\n cleanupScrollerRef.current = null\n boardScrollerRef.current = el\n if (!el) return\n updateScrollEdges()\n el.addEventListener('scroll', updateScrollEdges, { passive: true })\n const ro = new ResizeObserver(updateScrollEdges)\n ro.observe(el)\n cleanupScrollerRef.current = () => {\n el.removeEventListener('scroll', updateScrollEdges)\n ro.disconnect()\n }\n },\n [updateScrollEdges],\n )\n\n // Also recompute when the lane set changes (cards loaded \u2192 scrollWidth grows)\n React.useEffect(() => {\n updateScrollEdges()\n }, [updateScrollEdges, lanes.length])\n\n // Smooth-scroll helper: animate scrollLeft over ~200ms with cubic easing.\n // Uses requestAnimationFrame so the animation is synced to the display refresh rate\n // (handles 60Hz, 120Hz, 144Hz, ProMotion etc.). performance.now() gives sub-ms timing.\n // We do NOT call updateScrollEdges() inside the step \u2014 programmatic scrollLeft writes\n // DO fire a `scroll` event (which the listener we attached in setBoardScroller handles).\n // Skipping the redundant per-frame call avoids cascading the whole page tree to re-render.\n const activeAnimRef = React.useRef<{ cancel: () => void } | null>(null)\n const animateScrollTo = React.useCallback(\n (el: HTMLDivElement, target: number) => {\n activeAnimRef.current?.cancel()\n const startLeft = el.scrollLeft\n const clampedTarget = Math.max(0, Math.min(el.scrollWidth - el.clientWidth, target))\n const distance = clampedTarget - startLeft\n if (Math.abs(distance) < 1) {\n updateScrollEdges()\n return\n }\n const duration = 200\n const hasRaf = typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function'\n const now = () =>\n typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now()\n const startTime = now()\n let handle = 0\n let cancelled = false\n const step = () => {\n if (cancelled) return\n const t = Math.min(1, (now() - startTime) / duration)\n const eased = 1 - Math.pow(1 - t, 3)\n el.scrollLeft = startLeft + distance * eased\n if (t < 1) {\n handle = hasRaf\n ? window.requestAnimationFrame(step)\n : (window.setTimeout(step, 16) as unknown as number)\n } else {\n // Final edge-sync once the animation lands \u2014 the scroll listener also fires,\n // but on some browsers the last `scroll` event is debounced behind the final write.\n updateScrollEdges()\n }\n }\n handle = hasRaf\n ? window.requestAnimationFrame(step)\n : (window.setTimeout(step, 16) as unknown as number)\n activeAnimRef.current = {\n cancel: () => {\n cancelled = true\n if (hasRaf) window.cancelAnimationFrame(handle)\n else window.clearTimeout(handle)\n },\n }\n },\n [updateScrollEdges],\n )\n\n // Pre-compute lane left-edge offsets (in scrollLeft coords) so resize-aware \"step one lane\"\n // works even when lanes have different widths.\n const laneOffsets = React.useMemo(() => {\n const offsets: number[] = []\n let left = 0\n for (const stage of lanes) {\n offsets.push(left)\n const w = laneWidths[stage.id] ?? DEFAULT_LANE_WIDTH\n left += w + LANE_GAP\n }\n return offsets\n }, [lanes, laneWidths])\n\n // Per-direction step: pick the next lane boundary based on the live scrollLeft.\n // Variable-width-aware: uses laneOffsets instead of a fixed LANE_STEP so resized columns still\n // snap cleanly. Chrome's scrollLeft can be fractional; we add EDGE_TOLERANCE to dodge that.\n const stepScroll = React.useCallback(\n (direction: -1 | 1) => {\n const el = boardScrollerRef.current\n if (!el) return false\n const sl = el.scrollLeft\n const maxScroll = Math.max(0, el.scrollWidth - el.clientWidth)\n if (maxScroll <= EDGE_TOLERANCE) return false\n if (direction < 0 && sl <= EDGE_TOLERANCE) return false\n if (direction > 0 && sl >= maxScroll - EDGE_TOLERANCE) return false\n\n let target: number\n if (direction > 0) {\n // First lane offset that is meaningfully ahead of the current scroll position\n const next = laneOffsets.find((off) => off > sl + EDGE_TOLERANCE)\n target = next ?? maxScroll\n } else {\n // Last lane offset that is meaningfully behind the current scroll position\n let prev = 0\n for (const off of laneOffsets) {\n if (off >= sl - EDGE_TOLERANCE) break\n prev = off\n }\n target = prev\n }\n if (target > maxScroll - EDGE_TOLERANCE) target = maxScroll\n if (target < EDGE_TOLERANCE) target = 0\n\n // No-op safety: if target is essentially the current position, do nothing\n if (Math.abs(target - sl) < 1) return false\n\n animateScrollTo(el, target)\n // Belt-and-suspenders: if the animation library couldn't progress in this browser, force\n // the target after the animation duration so we never \"stick\" mid-scroll.\n window.setTimeout(() => {\n if (!boardScrollerRef.current) return\n const cur = boardScrollerRef.current.scrollLeft\n if (Math.abs(cur - target) > EDGE_TOLERANCE) {\n boardScrollerRef.current.scrollLeft = target\n }\n updateScrollEdges()\n }, 240)\n return true\n },\n [animateScrollTo, updateScrollEdges, laneOffsets],\n )\n\n // Press-and-hold continuous scroll. First step fires immediately on press; if the user keeps\n // the button held, additional steps fire every ~280ms (matched to animation duration) until they\n // release or the scroller reaches the edge.\n const holdTimerRef = React.useRef<number | null>(null)\n const stopContinuousScroll = React.useCallback(() => {\n if (holdTimerRef.current !== null) {\n window.clearInterval(holdTimerRef.current)\n holdTimerRef.current = null\n }\n }, [])\n const startContinuousScroll = React.useCallback(\n (direction: -1 | 1) => {\n stopContinuousScroll()\n const moved = stepScroll(direction)\n if (!moved) return\n const HOLD_DELAY = 320\n const REPEAT_INTERVAL = 240\n holdTimerRef.current = window.setTimeout(function repeat() {\n const more = stepScroll(direction)\n if (!more) {\n stopContinuousScroll()\n return\n }\n holdTimerRef.current = window.setTimeout(repeat, REPEAT_INTERVAL) as unknown as number\n }, HOLD_DELAY) as unknown as number\n },\n [stepScroll, stopContinuousScroll],\n )\n\n React.useEffect(() => {\n // Safety: stop any running hold timer on unmount\n return () => stopContinuousScroll()\n }, [stopContinuousScroll])\n\n // Dedup pointerdown + click: real browsers fire both when the user clicks a button.\n // We start the hold sequence on pointerdown (so press-and-hold feels immediate). The click\n // event that follows would otherwise scroll an EXTRA lane \u2014 we suppress it here.\n // Keyboard activation (Enter/Space) only fires click, no pointerdown, so it still works.\n const lastPointerDownAtRef = React.useRef(0)\n const handleScrollPrev = React.useCallback(() => {\n if (Date.now() - lastPointerDownAtRef.current < 500) return\n stepScroll(-1)\n }, [stepScroll])\n const handleScrollNext = React.useCallback(() => {\n if (Date.now() - lastPointerDownAtRef.current < 500) return\n stepScroll(1)\n }, [stepScroll])\n const handleHoldPrevStart = React.useCallback(() => {\n lastPointerDownAtRef.current = Date.now()\n startContinuousScroll(-1)\n }, [startContinuousScroll])\n const handleHoldNextStart = React.useCallback(() => {\n lastPointerDownAtRef.current = Date.now()\n startContinuousScroll(1)\n }, [startContinuousScroll])\n\n // Keyboard navigation: ArrowLeft / ArrowRight scroll one lane at a time, mirroring the\n // on-screen arrow buttons. We intentionally:\n // - skip when the user is typing inside an input / textarea / select / contenteditable\n // - skip when modifier keys are held (Cmd/Ctrl/Alt/Shift) so we don't fight browser shortcuts\n // - skip when any dialog or popover (Radix `[role=\"dialog\"]` / `[data-state=\"open\"]` portals)\n // is open, so popover keyboard nav (Apply, Tab, Escape, etc.) keeps working\n // - skip when no kanban scroller is mounted yet (initial loading state)\n React.useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return\n if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return\n const target = event.target as HTMLElement | null\n if (target) {\n const tag = target.tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return\n if (target.isContentEditable) return\n }\n // Don't steal arrow keys while a dialog/popover is open \u2014 they own keyboard nav.\n if (typeof document !== 'undefined') {\n if (document.querySelector('[role=\"dialog\"][data-state=\"open\"], [role=\"alertdialog\"][data-state=\"open\"]')) return\n if (document.querySelector('[data-radix-popper-content-wrapper] [data-state=\"open\"]')) return\n }\n if (!boardScrollerRef.current) return\n event.preventDefault()\n stepScroll(event.key === 'ArrowRight' ? 1 : -1)\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [stepScroll])\n\n const handleDragStart = React.useCallback((event: DragStartEvent) => {\n const id = typeof event.active.id === 'string' ? event.active.id : null\n setActiveDragDealId(id)\n }, [])\n\n const handleDragCancel = React.useCallback(() => {\n setActiveDragDealId(null)\n }, [])\n\n const handleDragEnd = React.useCallback(\n (event: DragEndEvent) => {\n setActiveDragDealId(null)\n const dealId = typeof event.active.id === 'string' ? event.active.id : null\n if (!dealId) return\n const overId = event.over?.id\n if (typeof overId !== 'string') return\n\n let targetStageId: string | null = null\n if (overId.startsWith('lane:')) {\n targetStageId = overId.slice('lane:'.length)\n } else {\n targetStageId = stageIdByDealId.get(overId) ?? null\n }\n if (!targetStageId) return\n\n // Round-2 UX review item 32: bulk-drag dispatch. If the dragged card is part of a\n // multi-card selection, route through `bulkMoveDealsToStage` so every selected card\n // travels with it. Dragging a NON-selected card is treated as a single-card move\n // and leaves the existing selection intact (the operator may have selected cards\n // intentionally and just be moving an unrelated one).\n const isBulkDrag = selectedDealIds.has(dealId) && selectedDealIds.size > 1\n if (isBulkDrag) {\n void bulkMoveDealsToStage(Array.from(selectedDealIds), targetStageId)\n } else {\n moveDealToStage(dealId, targetStageId)\n }\n },\n [bulkMoveDealsToStage, moveDealToStage, selectedDealIds, stageIdByDealId],\n )\n\n const bulkDragActive = React.useMemo(\n () => activeDragDealId !== null && selectedDealIds.has(activeDragDealId) && selectedDealIds.size > 1,\n [activeDragDealId, selectedDealIds],\n )\n\n const handleToggleSelect = React.useCallback((dealId: string) => {\n setSelectedDealIds((prev) => {\n const next = new Set(prev)\n if (next.has(dealId)) next.delete(dealId)\n else next.add(dealId)\n return next\n })\n }, [])\n\n const handleOpenDetail = React.useCallback(\n (dealId: string) => {\n router.push(`/backend/customers/deals/${dealId}`)\n },\n [router],\n )\n\n const handleComingSoon = React.useCallback(\n (label: string) => {\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.comingSoon',\n '{feature} arrives in the next iteration.',\n { feature: label },\n ),\n 'info',\n )\n },\n [t],\n )\n\n const handleChipClick = React.useCallback(\n (chipId: KanbanFilterChip['id']) => {\n handleComingSoon(\n translateWithFallback(\n t,\n 'customers.deals.kanban.filter.aria.chip',\n 'Filter by {label}',\n { label: chipId },\n ),\n )\n },\n [handleComingSoon, t],\n )\n\n const handleCustomizeView = React.useCallback(() => {\n setCustomizeOpen(true)\n }, [])\n\n // \"Reset to default\" must clear every filter chip the operator may have set, otherwise\n // the menu copy (\"Clear filters and restore columns\") is a lie. Previously only the\n // status filter and sort were reset, which left the operator confused when other chips\n // (pipeline, owner, people, companies, close date, currency, search) stayed populated.\n const handleResetView = React.useCallback(() => {\n setStatusFilters([])\n setOwnerFilters([])\n setPeopleFilters([])\n setCompanyFilters([])\n setCloseDateFilter({ from: null, to: null })\n setCurrencyFilter(null)\n setSearch('')\n setSortBy(DEFAULT_SORT)\n }, [])\n\n const activePipelineName = React.useMemo(() => {\n if (!selectedPipelineId) return ''\n return pipelinesQuery.data?.find((pipeline) => pipeline.id === selectedPipelineId)?.name ?? ''\n }, [pipelinesQuery.data, selectedPipelineId])\n\n const stageLabelById = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const lane of lanes) map.set(lane.id, lane.label)\n return map\n }, [lanes])\n\n const handleQuickAdd = React.useCallback(\n (stageId: string) => {\n if (!selectedPipelineId) return\n const stageLabel = stageLabelById.get(stageId)\n if (!stageLabel || stageId === '__unassigned') return\n setQuickDealContext({\n pipelineId: selectedPipelineId,\n pipelineName: activePipelineName,\n pipelineStageId: stageId,\n pipelineStageLabel: stageLabel,\n })\n },\n [activePipelineName, selectedPipelineId, stageLabelById],\n )\n\n const handleAddStage = React.useCallback(() => {\n if (!selectedPipelineId) return\n // Snapshot the current pipeline's stages (sorted by `order`) so the position picker\n // can offer \"After {label}\" entries. We sort here rather than relying on stagesData's\n // ordering because that array's order is technically determined by the API response \u2014\n // a defensive in-place sort is cheap and survives any future API change.\n const orderedStages = stagesData\n .filter((stage) => stage.pipelineId === selectedPipelineId)\n .slice()\n .sort((a, b) => a.order - b.order)\n .map((stage) => ({ id: stage.id, label: stage.label, order: stage.order }))\n setAddStageContext({\n pipelineId: selectedPipelineId,\n pipelineName: activePipelineName,\n existingStages: orderedStages,\n })\n }, [activePipelineName, selectedPipelineId, stagesData])\n\n const handleDialogCreated = React.useCallback(() => {\n invalidateKanbanData()\n queryClient\n .invalidateQueries({\n queryKey: ['customers', 'pipeline-stages', `scope:${scopeVersion}`, `pipeline:${selectedPipelineId}`],\n })\n .catch(() => {})\n }, [invalidateKanbanData, scopeVersion, selectedPipelineId])\n\n const updateDealStatus = React.useCallback(\n async (dealId: string, status: 'win' | 'loose') => {\n const dealVersion = deals.find((deal) => deal.id === dealId)?.updatedAt ?? null\n setPendingDealId(dealId)\n try {\n await runDealMutation({\n operation: async () => {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dealVersion),\n () => apiCallOrThrow(\n '/api/customers/deals',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: dealId, status }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.error.status',\n 'Failed to update deal status.',\n ),\n },\n ),\n )\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deal',\n resourceId: dealId,\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n status === 'win'\n ? translateWithFallback(\n t,\n 'customers.deals.kanban.menu.markWon.success',\n 'Deal marked as won.',\n )\n : translateWithFallback(\n t,\n 'customers.deals.kanban.menu.markLost.success',\n 'Deal marked as lost.',\n ),\n 'success',\n )\n invalidateKanbanData()\n } catch (error) {\n if (surfaceRecordConflict(error, t)) { invalidateKanbanData(); return }\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.menu.error.status',\n 'Failed to update deal status.',\n )\n flash(message, 'error')\n } finally {\n setPendingDealId(null)\n }\n },\n [deals, invalidateKanbanData, retryDealMutation, runDealMutation, t],\n )\n\n const deleteDeal = React.useCallback(\n async (dealId: string, dealTitle: string) => {\n const confirmed = await confirm({\n title: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.confirmTitle',\n 'Delete deal \u201C{title}\u201D?',\n { title: dealTitle },\n ),\n text: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.confirmText',\n 'This action cannot be undone.',\n ),\n variant: 'destructive',\n confirmText: translateWithFallback(t, 'customers.deals.kanban.menu.delete', 'Delete'),\n })\n if (!confirmed) return\n\n const lockVersion = deals.find((deal) => deal.id === dealId)?.updatedAt ?? null\n setPendingDealId(dealId)\n try {\n await runDealMutation({\n operation: async () => {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(lockVersion),\n () => deleteCrud('customers/deals', {\n body: { id: dealId },\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.error',\n 'Failed to delete deal.',\n ),\n }),\n )\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deal',\n resourceId: dealId,\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(t, 'customers.deals.kanban.menu.delete.success', 'Deal deleted.'),\n 'success',\n )\n invalidateKanbanData()\n } catch (error) {\n // A stale delete surfaces the unified conflict bar \u2014 skip the generic flash (#2332).\n if (surfaceRecordConflict(error, t)) {\n invalidateKanbanData()\n return\n }\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.error',\n 'Failed to delete deal.',\n )\n flash(message, 'error')\n } finally {\n setPendingDealId(null)\n }\n },\n [confirm, deals, invalidateKanbanData, retryDealMutation, runDealMutation, t],\n )\n\n const bulkSelectionSummary = React.useMemo(() => {\n if (selectedDealIds.size === 0) return { count: 0, totalLabel: null, currency: null as string | null, ids: [] as string[] }\n // Group totals by currency so the label is HONEST when the selection mixes currencies.\n // The previous implementation summed `valueAmount` across every selected deal and labeled\n // the result with whichever currency happened to appear first \u2014 e.g. selecting \u20AC100k + $50k\n // displayed as \"\u20AC150k\", which is meaningless. We now keep one bucket per currency.\n const totalsByCurrency = new Map<string, number>()\n const ids: string[] = []\n for (const deal of deals) {\n if (!selectedDealIds.has(deal.id)) continue\n ids.push(deal.id)\n if (typeof deal.valueAmount === 'number' && Number.isFinite(deal.valueAmount) && deal.valueAmount > 0) {\n const code = deal.valueCurrency && deal.valueCurrency.length === 3\n ? deal.valueCurrency.toUpperCase()\n : 'USD'\n totalsByCurrency.set(code, (totalsByCurrency.get(code) ?? 0) + deal.valueAmount)\n }\n }\n const rows = Array.from(totalsByCurrency.entries())\n .map(([code, amount]) => ({ code, amount }))\n .sort((a, b) => b.amount - a.amount)\n const formatOne = (code: string, amount: number) => {\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: code,\n maximumFractionDigits: 0,\n }).format(amount)\n } catch {\n return `${code} ${Math.round(amount)}`\n }\n }\n let totalLabel: string | null = null\n if (rows.length === 1) {\n totalLabel = formatOne(rows[0].code, rows[0].amount)\n } else if (rows.length >= 2) {\n // Concatenate per-currency totals with \" + \" so multi-currency selections stay readable.\n // Capping at 3 visible rows keeps the bar narrow; the rest collapses into \"+N more\"\n // so the operator sees the dominant currencies and an explicit \"mixed\" hint without\n // misleading aggregation across rates.\n const visible = rows.slice(0, 3).map((row) => formatOne(row.code, row.amount))\n const overflow = rows.length - visible.length\n totalLabel = overflow > 0 ? `${visible.join(' + ')} +${overflow}` : visible.join(' + ')\n }\n // `currency` (singular) is intentionally null when 2+ currencies are present so downstream\n // consumers (CSV export, change-stage dialog) don't assume a single canonical currency.\n const currency = rows.length === 1 ? rows[0].code : null\n return { count: ids.length, totalLabel, currency, ids }\n }, [deals, selectedDealIds])\n\n const handleBulkClear = React.useCallback(() => {\n setSelectedDealIds(new Set())\n }, [])\n\n const stageOptions = React.useMemo(\n () =>\n (stagesQuery.data ?? [])\n .slice()\n .sort((a, b) => a.order - b.order)\n .map((stage) => ({ id: stage.id, label: stage.label })),\n [stagesQuery.data],\n )\n\n const handleBulkChangeStage = React.useCallback(\n async (stageId: string) => {\n if (bulkSelectionSummary.ids.length === 0) return\n setIsBulkMutating(true)\n let progressJobId: string | null = null\n try {\n // Bulk writes MUST go through useGuardedMutation so injection modules (record-lock\n // conflict handling, retry chains, scoped headers) run the same `onBeforeSave`/\n // `onAfterSave` lifecycle as single-deal writes \u2014 see UI AGENTS.md and customers\n // module MUST rules. `runMutation` throws on guard rejection; the catch block below\n // surfaces the error message via flash().\n await runDealMutation({\n operation: async () => {\n const call = await apiCallOrThrow<BulkJobResponse>(\n '/api/customers/deals/bulk-update-stage',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n ids: bulkSelectionSummary.ids,\n pipelineStageId: stageId,\n }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.error',\n 'Failed to start bulk stage update.',\n ),\n },\n )\n progressJobId = call.result?.progressJobId ?? null\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deals.bulk_stage',\n resourceId: bulkSelectionSummary.ids.join(','),\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.queued',\n 'Bulk stage update started ({count} deals).',\n { count: bulkSelectionSummary.count },\n ),\n 'success',\n )\n setChangeStageOpen(false)\n setSelectedDealIds(new Set())\n if (progressJobId) {\n trackBulkProgressJob(progressJobId)\n }\n invalidateKanbanData()\n } catch (error) {\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.error',\n 'Failed to start bulk stage update.',\n )\n flash(message, 'error')\n } finally {\n setIsBulkMutating(false)\n }\n },\n [\n bulkSelectionSummary.count,\n bulkSelectionSummary.ids,\n dealMutationContextId,\n invalidateKanbanData,\n retryDealMutation,\n runDealMutation,\n t,\n trackBulkProgressJob,\n ],\n )\n\n const handleBulkChangeOwner = React.useCallback(\n async (ownerUserId: string | null) => {\n if (bulkSelectionSummary.ids.length === 0) return\n setIsBulkMutating(true)\n let progressJobId: string | null = null\n try {\n await runDealMutation({\n operation: async () => {\n const call = await apiCallOrThrow<BulkJobResponse>(\n '/api/customers/deals/bulk-update-owner',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n ids: bulkSelectionSummary.ids,\n ownerUserId,\n }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeOwner.error',\n 'Failed to start bulk owner update.',\n ),\n },\n )\n progressJobId = call.result?.progressJobId ?? null\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deals.bulk_owner',\n resourceId: bulkSelectionSummary.ids.join(','),\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeOwner.queued',\n 'Bulk owner update started ({count} deals).',\n { count: bulkSelectionSummary.count },\n ),\n 'success',\n )\n setChangeOwnerOpen(false)\n setSelectedDealIds(new Set())\n if (progressJobId) {\n trackBulkProgressJob(progressJobId)\n }\n invalidateKanbanData()\n } catch (error) {\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeOwner.error',\n 'Failed to start bulk owner update.',\n )\n flash(message, 'error')\n } finally {\n setIsBulkMutating(false)\n }\n },\n [\n bulkSelectionSummary.count,\n bulkSelectionSummary.ids,\n dealMutationContextId,\n invalidateKanbanData,\n retryDealMutation,\n runDealMutation,\n t,\n trackBulkProgressJob,\n ],\n )\n\n const handleBulkExport = React.useCallback(() => {\n if (bulkSelectionSummary.ids.length === 0) return\n const url = buildCrudExportUrl(\n 'customers/deals',\n {\n ids: bulkSelectionSummary.ids.join(','),\n exportScope: 'view',\n },\n 'csv',\n )\n if (typeof window !== 'undefined') {\n window.open(url, '_blank', 'noopener')\n }\n }, [bulkSelectionSummary.ids])\n\n const handleBulkDelete = React.useCallback(async () => {\n if (bulkSelectionSummary.ids.length === 0) return\n const confirmed = await confirm({\n title: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.title',\n 'Delete {count} deals?',\n { count: bulkSelectionSummary.count },\n ),\n text: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.text',\n 'This action cannot be undone.',\n ),\n variant: 'destructive',\n confirmText: translateWithFallback(t, 'customers.deals.kanban.bulk.delete', 'Delete'),\n })\n if (!confirmed) return\n\n const rows = deals.filter((deal) => selectedDealIds.has(deal.id)).map((deal) => ({ id: deal.id }))\n\n setIsBulkMutating(true)\n try {\n const { succeeded, failures } = await runBulkDelete(\n rows,\n async (row) => {\n await deleteCrud('customers/deals', {\n body: { id: row.id },\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.error',\n 'Failed to delete deal.',\n ),\n })\n },\n {\n fallbackErrorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.error',\n 'Failed to delete deal.',\n ),\n logTag: 'customers.deals.kanban',\n progress: {\n jobType: 'customers.deals.bulk_delete',\n name: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.progress.name',\n 'Delete selected deals',\n ),\n description: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.progress.description',\n '{count} deals queued for deletion',\n { count: rows.length },\n ),\n meta: { source: 'customers.deals.kanban' },\n },\n },\n )\n\n if (succeeded.length > 0) {\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.success',\n '{count} deals deleted.',\n { count: succeeded.length },\n ),\n failures.length === 0 ? 'success' : 'warning',\n )\n }\n for (const group of groupBulkDeleteFailures(failures)) {\n const message =\n group.count === 1\n ? group.sampleMessage\n : translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.failedGroup',\n '{count} deals could not be deleted: {message}',\n { count: group.count, message: group.sampleMessage },\n )\n flash(message, 'error')\n }\n setSelectedDealIds(new Set())\n invalidateKanbanData()\n } finally {\n setIsBulkMutating(false)\n }\n }, [\n bulkSelectionSummary.count,\n bulkSelectionSummary.ids,\n confirm,\n deals,\n queryClient,\n selectedDealIds,\n t,\n ])\n\n const duplicateDeal = React.useCallback(\n async (deal: DealCardData) => {\n setPendingDealId(deal.id)\n try {\n const stageId = stageIdByDealId.get(deal.id) ?? null\n const payload: Record<string, unknown> = {\n title: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.duplicate.titleSuffix',\n '{title} (copy)',\n { title: deal.title },\n ),\n status: 'open',\n }\n if (selectedPipelineId) payload.pipelineId = selectedPipelineId\n if (stageId && stageId !== '__unassigned') payload.pipelineStageId = stageId\n if (typeof deal.valueAmount === 'number') payload.valueAmount = deal.valueAmount\n if (deal.valueCurrency) payload.valueCurrency = deal.valueCurrency\n if (typeof deal.probability === 'number') payload.probability = deal.probability\n if (deal.expectedCloseAt) payload.expectedCloseAt = deal.expectedCloseAt\n if (deal.owner?.userId) payload.ownerUserId = deal.owner.userId\n\n await runDealMutation({\n operation: async () => {\n await apiCallOrThrow(\n '/api/customers/deals',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.duplicate.error',\n 'Failed to duplicate deal.',\n ),\n },\n )\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deal',\n resourceId: deal.id,\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.menu.duplicate.success',\n 'Deal duplicated.',\n ),\n 'success',\n )\n invalidateKanbanData()\n } catch (error) {\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.menu.duplicate.error',\n 'Failed to duplicate deal.',\n )\n flash(message, 'error')\n } finally {\n setPendingDealId(null)\n }\n },\n [\n queryClient,\n retryDealMutation,\n runDealMutation,\n selectedPipelineId,\n stageIdByDealId,\n t,\n ],\n )\n\n const buildMenuItems = React.useCallback(\n (deal: DealCardData): RowActionItem[] => [\n {\n id: 'open',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.open', 'Open deal'),\n onSelect: () => handleOpenDetail(deal.id),\n },\n {\n id: 'edit',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.edit', 'Edit'),\n onSelect: () => handleOpenDetail(deal.id),\n },\n {\n id: 'duplicate',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.duplicate', 'Duplicate'),\n onSelect: () => void duplicateDeal(deal),\n },\n {\n id: 'move-stage',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.moveStage', 'Move stage\u2026'),\n onSelect: () =>\n setSingleMoveStageContext({\n dealId: deal.id,\n currentStageId: stageIdByDealId.get(deal.id) ?? null,\n }),\n },\n {\n id: 'mark-won',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.markWon', 'Mark as Won'),\n onSelect: () => void updateDealStatus(deal.id, 'win'),\n },\n {\n id: 'mark-lost',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.markLost', 'Mark as Lost'),\n onSelect: () => void updateDealStatus(deal.id, 'loose'),\n },\n {\n id: 'delete',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.delete', 'Delete'),\n destructive: true,\n onSelect: () => void deleteDeal(deal.id, deal.title),\n },\n ],\n [\n deleteDeal,\n duplicateDeal,\n handleOpenDetail,\n stageIdByDealId,\n t,\n updateDealStatus,\n ],\n )\n\n const handleComposeActivity = React.useCallback(\n (dealId: string, type: 'call' | 'email' | 'note') => {\n const deal = deals.find((entry) => entry.id === dealId)\n if (!deal) return\n const entityId = deal.primaryCompany?.id\n if (!entityId) {\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.activityComposer.noEntity',\n 'Link a company to this deal before logging activities here.',\n ),\n 'info',\n )\n return\n }\n setActivityContext({\n dealId,\n dealTitle: deal.title,\n type,\n entityId,\n })\n },\n [deals, t],\n )\n\n // The placeholder \"stub\" chips have been replaced by real popovers; pass an empty list.\n const filterChips = React.useMemo<KanbanFilterChip[]>(() => [], [])\n\n const pipelineFilterOptions = React.useMemo(\n () =>\n (pipelinesQuery.data ?? []).map((pipeline) => ({\n id: pipeline.id,\n name: pipeline.name,\n })),\n [pipelinesQuery.data],\n )\n\n // Async loaders for entity-filter popovers (Owner / People / Companies)\n const loadOwnerOptions = React.useCallback(\n async (query: string, _signal: AbortSignal): Promise<EntityFilterOption[]> => {\n const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100 })\n const opts: EntityFilterOption[] = items\n .filter((user) => !!user.userId && !!user.displayName)\n .map((user) => ({ value: user.userId!, label: user.displayName! }))\n // Cache labels so chip can display readable text for already-selected ids\n setOwnerLabels((prev) => {\n const next: Record<string, string> = { ...prev }\n for (const option of opts) next[option.value] = option.label\n return next\n })\n return opts\n },\n [],\n )\n const loadPeopleOptions = React.useCallback(\n async (query: string, signal: AbortSignal): Promise<EntityFilterOption[]> => {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '50')\n if (query) params.set('search', query)\n params.set('sortField', 'displayName')\n params.set('sortDir', 'asc')\n // Route the request through `apiCall` so scoped tenant/org headers are attached and the\n // JSON parser uses the shared safe-read helper. AbortSignal is forwarded through init.\n const call = await apiCall<{\n items?: Array<{ id?: string; display_name?: string; first_name?: string; last_name?: string }>\n }>(`/api/customers/people?${params.toString()}`, { signal })\n if (!call.ok) return []\n const items = call.result?.items ?? []\n const opts: EntityFilterOption[] = []\n for (const it of items) {\n if (!it.id) continue\n const label = (it.display_name && it.display_name.trim().length)\n ? it.display_name.trim()\n : [it.first_name, it.last_name].filter(Boolean).join(' ').trim() || it.id.slice(0, 8)\n opts.push({ value: it.id, label })\n }\n setPeopleLabels((prev) => {\n const next: Record<string, string> = { ...prev }\n for (const o of opts) next[o.value] = o.label\n return next\n })\n return opts\n },\n [],\n )\n const loadCompanyOptions = React.useCallback(\n async (query: string, signal: AbortSignal): Promise<EntityFilterOption[]> => {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '50')\n if (query) params.set('search', query)\n params.set('sortField', 'display_name')\n params.set('sortDir', 'asc')\n const call = await apiCall<{ items?: Array<{ id?: string; display_name?: string }> }>(\n `/api/customers/companies?${params.toString()}`,\n { signal },\n )\n if (!call.ok) return []\n const items = call.result?.items ?? []\n const opts: EntityFilterOption[] = []\n for (const it of items) {\n if (!it.id || !it.display_name) continue\n opts.push({ value: it.id, label: it.display_name })\n }\n setCompanyLabels((prev) => {\n const next: Record<string, string> = { ...prev }\n for (const o of opts) next[o.value] = o.label\n return next\n })\n return opts\n },\n [],\n )\n\n const leadingChipsNode = (\n <>\n <StatusFilterPopover values={statusFilters} onApply={setStatusFilters} />\n <PipelineFilterPopover\n pipelines={pipelineFilterOptions}\n selectedPipelineId={selectedPipelineId}\n onApply={setSelectedPipelineId}\n />\n {boardSummary && boardSummary.rows.length > 0 ? (\n <CurrencyFilterPopover\n rows={boardSummary.rows}\n baseCurrencyCode={boardSummary.baseCurrencyCode}\n totalInBaseCurrency={boardSummary.totalInBaseCurrency}\n headingLabel={translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.headingLabel',\n 'PIPELINE',\n )}\n headingCount={total}\n selectedCurrency={currencyFilter}\n onApply={setCurrencyFilter}\n />\n ) : null}\n <EntityFilterPopover\n label={translateWithFallback(t, 'customers.deals.kanban.filter.owner', 'Owner')}\n anyLabel={translateWithFallback(t, 'customers.deals.kanban.filter.all', 'All')}\n values={ownerFilters}\n onApply={setOwnerFilters}\n loadOptions={loadOwnerOptions}\n labelById={ownerLabels}\n />\n <EntityFilterPopover\n label={translateWithFallback(t, 'customers.deals.kanban.filter.people', 'People')}\n values={peopleFilters}\n onApply={setPeopleFilters}\n loadOptions={loadPeopleOptions}\n labelById={peopleLabels}\n />\n <EntityFilterPopover\n label={translateWithFallback(t, 'customers.deals.kanban.filter.companies', 'Companies')}\n values={companyFilters}\n onApply={setCompanyFilters}\n loadOptions={loadCompanyOptions}\n labelById={companyLabels}\n />\n <CloseDateFilterPopover value={closeDateFilter} onApply={setCloseDateFilter} />\n </>\n )\n\n const sortNode = <SortByPopover value={sortBy} onApply={setSortBy} />\n\n const activePipeline = React.useMemo(() => {\n if (!selectedPipelineId) return null\n return pipelinesQuery.data?.find((pipeline) => pipeline.id === selectedPipelineId) ?? null\n }, [pipelinesQuery.data, selectedPipelineId])\n\n const dragHint = translateWithFallback(\n t,\n 'customers.deals.kanban.helper.dragHint',\n 'Drag cards between lanes to update stage',\n )\n const footerCount = translateWithFallback(\n t,\n 'customers.deals.kanban.footer.count',\n 'Showing {visible} of {total} deals in {pipeline} pipeline',\n {\n visible: deals.length,\n total,\n pipeline: activePipeline?.name ?? '\u2014',\n },\n )\n\n return (\n <Page>\n <PageBody>\n <div className=\"flex flex-col gap-2\">\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink asChild>\n <Link href=\"/backend\">\n {translateWithFallback(t, 'customers.deals.kanban.breadcrumb.dashboard', 'Dashboard')}\n </Link>\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage>\n {translateWithFallback(t, 'customers.deals.kanban.breadcrumb.deals', 'Deals')}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col gap-1\">\n {/*\n Heading typography mirrors DS PageHeader (text-xl sm:text-2xl font-semibold\n leading-tight) so the kanban title matches /backend/customers/people and\n other backoffice pages. We don't use the PageHeader component itself because\n it can't host the rich board-summary content (LaneCurrencyBreakdown popover)\n that lives directly under the title.\n */}\n <h1 className=\"text-xl font-semibold leading-tight text-foreground sm:text-2xl\">\n {translateWithFallback(t, 'customers.deals.kanban.pageTitle', 'Deals')}\n </h1>\n {boardSummary && boardSummary.rows.length > 0 ? (\n /*\n * Pipeline-wide value summary. The headline number is the converted total in\n * the tenant's base currency, paired with the deal count for context. When at\n * least one currency lacks an FX rate, we show a `~` prefix and a \"partial\"\n * caveat \u2014 the `LaneCurrencyBreakdown` popover (anchored on the `+N` chip)\n * surfaces the full breakdown and missing-rate disclosure. Clicking the\n * popover trigger here gives the operator the same board-level detail view.\n */\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <span>\n {translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.count',\n '{count} deals',\n { count: total },\n )}\n </span>\n {boardSummary.baseCurrencyCode && boardSummary.totalInBaseCurrency > 0 ? (\n <>\n <span aria-hidden=\"true\">\u00B7</span>\n <span className=\"flex items-baseline gap-1 font-semibold text-foreground\">\n {boardSummary.convertedAll ? null : (\n <span className=\"text-muted-foreground\" aria-hidden=\"true\">\n ~\n </span>\n )}\n <span>\n {new Intl.NumberFormat(undefined, {\n style: 'decimal',\n maximumFractionDigits: 0,\n }).format(boardSummary.totalInBaseCurrency)}\n </span>\n <span className=\"text-xs font-medium text-muted-foreground\">\n {boardSummary.baseCurrencyCode}\n </span>\n </span>\n {!boardSummary.convertedAll ? (\n <span\n className=\"text-overline uppercase tracking-wide text-status-warning-text\"\n title={translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.partialHint',\n 'Missing FX rates for {currencies} \u2014 excluded from total',\n { currencies: boardSummary.missingRateCurrencies.join(', ') },\n )}\n >\n {translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.partial',\n 'partial',\n )}\n </span>\n ) : null}\n </>\n ) : null}\n {boardSummary.rows.length > 1 ? (\n <LaneCurrencyBreakdown\n rows={boardSummary.rows}\n baseCurrencyCode={boardSummary.baseCurrencyCode}\n totalInBaseCurrency={boardSummary.totalInBaseCurrency}\n convertedAll={boardSummary.convertedAll}\n missingRateCurrencies={boardSummary.missingRateCurrencies}\n headingLabel={translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.headingLabel',\n 'PIPELINE',\n )}\n headingCount={total}\n triggerLabel={translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.breakdownTrigger',\n 'Breakdown',\n )}\n triggerClassName=\"inline-flex items-center rounded-md border border-border bg-card px-2 py-0.5 text-overline font-medium uppercase tracking-wide text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n />\n ) : null}\n </div>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2.5\">\n <SearchInput\n value={search}\n onChange={setSearch}\n placeholder={translateWithFallback(\n t,\n 'customers.deals.kanban.search.placeholder',\n 'Search deals\u2026',\n )}\n className=\"w-64\"\n />\n <Button variant=\"outline\" type=\"button\" onClick={handleCustomizeView}>\n <SlidersHorizontal className=\"size-4\" aria-hidden=\"true\" />\n {translateWithFallback(t, 'customers.deals.kanban.cta.customize', 'Customize view')}\n </Button>\n <Button\n variant=\"outline\"\n type=\"button\"\n onClick={handleAddStage}\n disabled={!selectedPipelineId}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {translateWithFallback(t, 'customers.deals.kanban.cta.newStage', 'New stage')}\n </Button>\n <Button asChild>\n <Link href=\"/backend/customers/deals/create?returnTo=/backend/customers/deals/pipeline\">\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {translateWithFallback(t, 'customers.deals.kanban.cta.newDeal', 'New deal')}\n </Link>\n </Button>\n </div>\n </div>\n\n {pipelinesQuery.data && pipelinesQuery.data.length > 1 ? (\n <div className=\"flex items-center gap-2 pb-1 text-sm\">\n <span className=\"text-muted-foreground\">\n {translateWithFallback(t, 'customers.deals.pipeline.switch.label', 'Pipeline')}\n </span>\n <Select\n value={selectedPipelineId ?? undefined}\n onValueChange={(value) => setSelectedPipelineId(value || null)}\n >\n <SelectTrigger className=\"w-auto min-w-48\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pipelinesQuery.data.map((pipeline) => (\n <SelectItem key={pipeline.id} value={pipeline.id}>\n {pipeline.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n </div>\n\n <ViewTabsRow active=\"kanban\" className=\"mt-4\" />\n\n <FilterBarRow\n leadingChips={leadingChipsNode}\n chips={filterChips}\n sortNode={sortNode}\n onChipClick={handleChipClick}\n />\n\n {!selectedPipelineId ? (\n <EmptyState\n icon={<Workflow className=\"size-8\" aria-hidden=\"true\" />}\n title={translateWithFallback(\n t,\n 'customers.deals.pipeline.noPipeline',\n 'No pipeline selected. Create a pipeline in settings.',\n )}\n className=\"h-[50vh] w-full\"\n />\n ) : isInitialLoading ? (\n <div className=\"flex h-[50vh] items-center justify-center\">\n <Spinner />\n </div>\n ) : firstError ? (\n <div className=\"max-w-xl\">\n <ErrorNotice\n message={\n firstError instanceof Error\n ? firstError.message\n : translateWithFallback(\n t,\n 'customers.deals.pipeline.loadError',\n 'Failed to load deals.',\n )\n }\n />\n </div>\n ) : (\n <DndContext\n sensors={sensors}\n collisionDetection={collisionDetection}\n measuring={measuringConfig}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n {/* Three-column flex: [36px gutter] [scroller flex-1] [36px gutter]\n Each gutter sized exactly to the 36px arrow button \u2014 no extra padding/gap so the\n scroller keeps as much horizontal room as possible (= more lanes fit). The buttons\n are absolute inside the gutter so they never overlap card content. */}\n <div className=\"flex items-stretch\">\n {/*\n Lane rail reads as an elevated tab over the kanban scroller (round-2 UX item 31,\n revised): `bg-card shadow-lg` gives it a real surface and a soft drop-shadow;\n `z-10` keeps it above the scroller. `mr-2` adds an 8px gap between the rail and\n the first lane so card edges don't touch the rail (UX-designer round-3 feedback \u2014\n the prior `-mr-2` tuck-under overlap was reverted; a small positive gap reads\n cleaner than the overlap).\n\n `shadow-lg` glows in all four directions by default, but the LEFT side of the\n rail sits flush with the page chrome \u2014 without clipping, that shadow half spills\n onto the page background and reads as a halo (UX-designer feedback from Zielivia).\n `clip-path:inset(-40px -40px -40px 0)` extends the visible region 40px beyond\n top/right/bottom (so the soft drop-shadow can render in full on those three sides)\n and clips flush at the LEFT edge (the page-chrome side). Right rail mirrors with\n `inset(-40px 0 -40px -40px)`.\n */}\n <div className=\"relative z-10 flex w-11 mr-2 shrink-0 items-center justify-center bg-card shadow-lg [clip-path:inset(-40px_-40px_-40px_0)]\">\n <IconButton\n variant=\"outline\"\n size=\"lg\"\n fullRadius\n onClick={handleScrollPrev}\n onPointerDown={handleHoldPrevStart}\n onPointerUp={stopContinuousScroll}\n onPointerLeave={stopContinuousScroll}\n onPointerCancel={stopContinuousScroll}\n aria-label={translateWithFallback(t, 'customers.deals.kanban.board.scrollPrev', 'Scroll to previous stage')}\n aria-disabled={scrollEdges.atStart}\n className={`absolute left-0 top-28 flex size-9 items-center justify-center rounded-full border border-border bg-card text-foreground shadow-lg transition-opacity hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n scrollEdges.atStart ? 'opacity-30' : ''\n }`}\n >\n <ChevronLeft className=\"size-5\" aria-hidden=\"true\" />\n </IconButton>\n </div>\n <div\n ref={setBoardScroller}\n data-kanban-scroller\n className={`flex min-w-0 flex-1 gap-3.5 overflow-x-auto pb-6 ${\n activeDragDealId ? 'cursor-grabbing select-none' : ''\n }`}\n >\n {lanes.length === 0 ? (\n <EmptyState\n icon={<Layers className=\"size-8\" aria-hidden=\"true\" />}\n title={translateWithFallback(\n t,\n 'customers.deals.pipeline.noStages',\n 'Define pipeline stages to start tracking deals.',\n )}\n className=\"h-[50vh] w-full\"\n />\n ) : (\n <>\n {lanes.map((stage) => {\n const laneDeals = sortedDealsByStage.get(stage.id) ?? EMPTY_DEAL_ARRAY\n return (\n <Lane\n key={stage.id}\n stage={stage}\n deals={laneDeals}\n aggregate={aggregateByStage.get(stage.id) ?? null}\n selectedDealIds={selectedDealIds}\n buildMenuItems={buildMenuItems}\n activeDragDealId={activeDragDealId}\n isLoadingMore={!!loadingMoreByStage[stage.id]}\n width={laneWidths[stage.id] ?? null}\n onToggleSelect={handleToggleSelect}\n onComposeActivity={handleComposeActivity}\n onOpenDetail={handleOpenDetail}\n onQuickAddClick={handleQuickAdd}\n onLoadMore={handleLoadMoreInLane}\n onResize={handleLaneResize}\n onResetWidth={handleResetLaneWidth}\n />\n )\n })}\n {/*\n Trailing \"Add stage\" tile (round-2 UX review item 30, revised). Coexists\n with the page-header toolbar button by explicit design: the toolbar button\n is the always-visible primary CTA; this tile is the in-context affordance\n at the end of the stage row that operators have used to map onto from\n Trello/Asana muscle memory.\n */}\n <AddStageLane onClick={handleAddStage} />\n </>\n )}\n </div>\n {/* Mirror of the left rail; `ml-3.5` adds a 14px gap between the last lane and the\n right rail. The right gap is intentionally wider than the left (`mr-2` = 8px)\n because the trailing tile here is `AddStageLane` \u2014 its dashed border reads more\n subtly than a regular lane's saturated color bar, so the same 8px on the right\n perceptually feels like a \"tucked under\" overlap (round-3 UX-designer feedback).\n `ml-3.5` matches the scroller's inter-lane `gap-3.5` so the visual rhythm stays\n consistent. `clip-path:inset(-40px 0 -40px -40px)` is the right-rail mirror of the\n left rail's clip \u2014 keeps the shadow on top/left/bottom (40px extend) and clips\n flush at the RIGHT edge so it doesn't spill onto the page background past the\n page chrome. */}\n <div className=\"relative z-10 flex w-11 ml-3.5 shrink-0 items-center justify-center bg-card shadow-lg [clip-path:inset(-40px_0_-40px_-40px)]\">\n <IconButton\n variant=\"outline\"\n size=\"lg\"\n fullRadius\n onClick={handleScrollNext}\n onPointerDown={handleHoldNextStart}\n onPointerUp={stopContinuousScroll}\n onPointerLeave={stopContinuousScroll}\n onPointerCancel={stopContinuousScroll}\n aria-label={translateWithFallback(t, 'customers.deals.kanban.board.scrollNext', 'Scroll to next stage')}\n aria-disabled={scrollEdges.atEnd}\n className={`absolute right-0 top-28 flex size-9 items-center justify-center rounded-full border border-border bg-card text-foreground shadow-lg transition-opacity hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n scrollEdges.atEnd ? 'opacity-30' : ''\n }`}\n >\n <ChevronRight className=\"size-5\" aria-hidden=\"true\" />\n </IconButton>\n </div>\n </div>\n <DragOverlay dropAnimation={null}>\n {activeDragDeal ? (\n <div className=\"relative\">\n <div className={`pointer-events-none ${LANE_WIDTH_CLASS} rotate-2 cursor-grabbing select-none rounded-lg border border-border bg-card px-4 py-3.5 shadow-xl ring-2 ring-accent-indigo/40`}>\n <div className=\"flex flex-col gap-2\">\n <h3 className=\"line-clamp-2 text-base font-semibold leading-normal text-foreground\">\n {activeDragDeal.title}\n </h3>\n {typeof activeDragDeal.valueAmount === 'number' ? (\n <div className=\"flex items-baseline gap-1.5\">\n <span className=\"text-lg font-bold leading-normal text-foreground\">\n {new Intl.NumberFormat(undefined, {\n style: 'decimal',\n maximumFractionDigits: 0,\n useGrouping: true,\n }).format(activeDragDeal.valueAmount)}\n </span>\n {activeDragDeal.valueCurrency ? (\n <span className=\"text-sm font-semibold leading-normal text-muted-foreground\">\n {activeDragDeal.valueCurrency.toUpperCase()}\n </span>\n ) : null}\n </div>\n ) : null}\n {activeDragDeal.primaryCompany ? (\n <span className=\"inline-flex w-fit 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\">\n <span className=\"truncate\">{activeDragDeal.primaryCompany.label}</span>\n </span>\n ) : null}\n </div>\n </div>\n {bulkDragActive ? (\n // +N badge tells the operator how many cards are travelling with the dragged\n // one \u2014 round-2 UX review item 32. N excludes the dragged card itself, so a\n // selection of 5 shows \"+4\" floating above the dragged surface.\n <span\n aria-label={translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.dragOverlayBadge',\n '{count} more selected',\n { count: selectedDealIds.size - 1 },\n )}\n className=\"pointer-events-none absolute -right-3 -top-3 inline-flex h-7 min-w-7 rotate-2 items-center justify-center rounded-full border-2 border-card bg-foreground px-2 text-xs font-bold tabular-nums text-background shadow-lg\"\n >\n +{selectedDealIds.size - 1}\n </span>\n ) : null}\n </div>\n ) : null}\n </DragOverlay>\n </DndContext>\n )}\n\n {selectedPipelineId && !isInitialLoading ? (\n <div className=\"flex flex-col gap-1 border-t border-border pt-3 text-xs text-muted-foreground sm:flex-row sm:items-center sm:justify-between\">\n <span>{footerCount}</span>\n <span>{dragHint}</span>\n {pendingDealId ? <Spinner className=\"size-3\" /> : null}\n </div>\n ) : null}\n </PageBody>\n\n <QuickDealDialog\n open={!!quickDealContext}\n context={quickDealContext}\n onClose={() => setQuickDealContext(null)}\n onCreated={handleDialogCreated}\n currentUserId={currentUserId || undefined}\n currentUserLabel={currentUserLabel}\n companies={companiesQuery.data ?? []}\n currencies={\n currencyDictionary.data\n ? currencyDictionary.data.entries.map((entry) => ({\n code: entry.value,\n label: entry.label,\n isBase: boardSummary?.baseCurrencyCode\n ? entry.value === boardSummary.baseCurrencyCode\n : false,\n }))\n : undefined\n }\n />\n <AddStageDialog\n open={!!addStageContext}\n context={addStageContext}\n onClose={() => setAddStageContext(null)}\n onCreated={handleDialogCreated}\n />\n <CustomizeViewDialog\n open={customizeOpen}\n resizedLanesCount={Object.keys(laneWidths).length}\n onClose={() => setCustomizeOpen(false)}\n onResetToDefault={handleResetView}\n onResetColumnWidths={handleResetAllLaneWidths}\n />\n <ActivityComposerDialog\n open={!!activityContext}\n context={activityContext}\n onClose={() => setActivityContext(null)}\n onCreated={handleDialogCreated}\n />\n <ChangeStageDialog\n open={changeStageOpen}\n selectedCount={bulkSelectionSummary.count}\n pipelineName={activePipelineName}\n stages={stageOptions}\n isSubmitting={isBulkMutating}\n onClose={() => setChangeStageOpen(false)}\n onConfirm={(stageId) => void handleBulkChangeStage(stageId)}\n />\n <ChangeStageDialog\n open={!!singleMoveStageContext}\n selectedCount={1}\n pipelineName={activePipelineName}\n stages={stageOptions}\n isSubmitting={false}\n onClose={() => setSingleMoveStageContext(null)}\n onConfirm={(stageId) => {\n if (singleMoveStageContext) {\n moveDealToStage(singleMoveStageContext.dealId, stageId)\n }\n setSingleMoveStageContext(null)\n }}\n />\n <ChangeOwnerDialog\n open={changeOwnerOpen}\n selectedCount={bulkSelectionSummary.count}\n isSubmitting={isBulkMutating}\n onClose={() => setChangeOwnerOpen(false)}\n onConfirm={(userId) => void handleBulkChangeOwner(userId)}\n />\n <BulkActionsBar\n count={bulkSelectionSummary.count}\n totalLabel={bulkSelectionSummary.totalLabel}\n onChangeStage={() => setChangeStageOpen(true)}\n onChangeOwner={() => setChangeOwnerOpen(true)}\n onExportCsv={handleBulkExport}\n onDelete={() => void handleBulkDelete()}\n onClear={handleBulkClear}\n />\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { useRouter } from 'next/navigation'\nimport { useQueries, useQuery, useQueryClient } from '@tanstack/react-query'\nimport {\n DndContext,\n DragOverlay,\n KeyboardSensor,\n MeasuringStrategy,\n PointerSensor,\n pointerWithin,\n useSensor,\n useSensors,\n type CollisionDetection,\n type DragEndEvent,\n type DragStartEvent,\n} from '@dnd-kit/core'\nimport { sortableKeyboardCoordinates } from '@dnd-kit/sortable'\nimport { ChevronLeft, ChevronRight, Layers, Plus, SlidersHorizontal, Workflow } from 'lucide-react'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbPage,\n BreadcrumbSeparator,\n} from '@open-mercato/ui/primitives/breadcrumb'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { SearchInput } from '@open-mercato/ui/primitives/search-input'\nimport { Spinner } from '@open-mercato/ui/primitives/spinner'\nimport { ErrorNotice } from '@open-mercato/ui/primitives/ErrorNotice'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@open-mercato/ui/primitives/select'\nimport { apiCall, apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'\nimport { useCurrentUserId } from '@open-mercato/ui/backend/utils/useCurrentUserId'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport type { RowActionItem } from '@open-mercato/ui/backend/RowActions'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport type { FilterOptionTone } from '@open-mercato/shared/lib/query/advanced-filter'\nimport { ViewTabsRow } from './components/ViewTabsRow'\nimport { LANE_WIDTH_CLASS } from './components/constants'\nimport { useCurrencyDictionary } from '../../../../components/detail/hooks/useCurrencyDictionary'\nimport { FilterBarRow, type KanbanFilterChip } from './components/FilterBarRow'\nimport { Lane, type LaneStage } from './components/Lane'\nimport { LaneCurrencyBreakdown } from './components/LaneCurrencyBreakdown'\nimport { CurrencyFilterPopover } from './components/CurrencyFilterPopover'\nimport { AddStageLane } from './components/AddStageLane'\nimport type { DealCardData } from './components/DealCard'\nimport {\n QuickDealDialog,\n type QuickDealContext,\n type QuickDealCompanyOption,\n} from './components/QuickDealDialog'\nimport { AddStageDialog, type AddStageContext } from './components/AddStageDialog'\nimport { StatusFilterPopover } from './components/StatusFilterPopover'\nimport { PipelineFilterPopover } from './components/PipelineFilterPopover'\nimport { SortByPopover, type SortOption } from './components/SortByPopover'\nimport { EntityFilterPopover, type EntityFilterOption } from './components/EntityFilterPopover'\nimport { CloseDateFilterPopover, type CloseDateRange } from './components/CloseDateFilterPopover'\nimport { CustomizeViewDialog } from './components/CustomizeViewDialog'\nimport {\n ActivityComposerDialog,\n type ActivityComposerContext,\n} from './components/ActivityComposerDialog'\nimport { BulkActionsBar } from './components/BulkActionsBar'\nimport { ChangeStageDialog } from './components/ChangeStageDialog'\nimport { ChangeOwnerDialog } from './components/ChangeOwnerDialog'\nimport { buildCrudExportUrl, deleteCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { runBulkDelete, groupBulkDeleteFailures } from '@open-mercato/ui/backend/utils/bulkDelete'\nimport {\n fetchAssignableStaffMembers,\n type AssignableStaffMember,\n} from '../../../../components/detail/assignableStaff'\n\ntype PipelineRecord = { id: string; name: string; isDefault: boolean }\n\ntype StageAggregateRow = {\n stageId: string\n count: number\n openCount: number\n totalInBaseCurrency: number\n byCurrency: Array<{ currency: string; total: number; count: number }>\n convertedAll?: boolean\n missingRateCurrencies?: string[]\n}\n\ntype StageAggregateResponse = {\n baseCurrencyCode: string | null\n perStage: StageAggregateRow[]\n}\n\ntype BulkJobResponse = {\n ok: boolean\n progressJobId: string | null\n message?: string\n}\n\nexport type LaneAggregate = {\n count: number\n totalInBaseCurrency: number\n byCurrency: Array<{ currency: string; total: number; count: number }>\n baseCurrencyCode: string | null\n // Both fields default to \"best case\" so an older aggregate response (without these\n // fields) doesn't trigger spurious \"partial conversion\" warnings in the UI.\n convertedAll: boolean\n missingRateCurrencies: string[]\n}\ntype PipelineStageRecord = {\n id: string\n label: string\n order: number\n pipelineId: string\n // `color` is sourced from the `customer_dictionary_entries` row that backs each stage\n // (kind = 'pipeline_stage'). After the 2026-05-19 hex\u2192tone migration this stores a\n // canonical tone identifier ('success', 'warning', etc.) rather than a hex string.\n color?: string | null\n}\n\ntype DealApiRecord = Record<string, unknown> & {\n id: string\n title?: string | null\n status?: string | null\n pipeline_stage?: string | null\n pipeline_id?: string | null\n pipeline_stage_id?: string | null\n value_amount?: number | string | null\n value_currency?: string | null\n probability?: number | string | null\n expected_close_at?: string | null\n created_at?: string | null\n updated_at?: string | null\n owner_user_id?: string | null\n companies?: Array<{ id?: unknown; label?: unknown } | null> | null\n people?: Array<{ id?: unknown; label?: unknown } | null> | null\n _pipeline?: {\n openActivitiesCount?: number\n daysInCurrentStage?: number\n isStuck?: boolean\n isOverdue?: boolean\n } | null\n}\n\nconst DEFAULT_SORT: SortOption = 'updated_desc'\nconst LANE_PAGE_SIZE = 25\nconst FALLBACK_TONES: FilterOptionTone[] = ['success', 'warning', 'error', 'info', 'neutral', 'brand']\n// Module-level singleton so empty lanes get the same array reference every render \u2014\n// React.memo's identity check on the `deals` prop holds across renders.\nconst EMPTY_DEAL_ARRAY: DealCardData[] = []\n\n// Lane width / resize configuration (~20% larger than original Figma spec for readability)\nconst DEFAULT_LANE_WIDTH = 308\nconst MIN_LANE_WIDTH = 240\nconst MAX_LANE_WIDTH = 576\nconst LANE_GAP = 14\nconst LANE_WIDTHS_STORAGE_KEY_PREFIX = 'kanban-lane-widths-v2'\n\nfunction loadLaneWidths(scopeKey: string): Record<string, number> {\n if (typeof window === 'undefined') return {}\n try {\n const raw = window.localStorage.getItem(`${LANE_WIDTHS_STORAGE_KEY_PREFIX}:${scopeKey}`)\n if (!raw) return {}\n const parsed = JSON.parse(raw)\n if (!parsed || typeof parsed !== 'object') return {}\n const out: Record<string, number> = {}\n for (const [k, v] of Object.entries(parsed)) {\n if (typeof v === 'number' && Number.isFinite(v) && v >= MIN_LANE_WIDTH && v <= MAX_LANE_WIDTH) {\n out[k] = v\n }\n }\n return out\n } catch {\n return {}\n }\n}\n\nfunction saveLaneWidths(scopeKey: string, widths: Record<string, number>) {\n if (typeof window === 'undefined') return\n try {\n if (Object.keys(widths).length === 0) {\n window.localStorage.removeItem(`${LANE_WIDTHS_STORAGE_KEY_PREFIX}:${scopeKey}`)\n } else {\n window.localStorage.setItem(`${LANE_WIDTHS_STORAGE_KEY_PREFIX}:${scopeKey}`, JSON.stringify(widths))\n }\n } catch {}\n}\n\nfunction normalizeAmount(value: unknown): number | null {\n if (typeof value === 'number' && Number.isFinite(value)) return value\n if (typeof value === 'string' && value.trim().length) {\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n }\n return null\n}\n\nfunction normalizeProbability(value: unknown): number | null {\n const parsed = normalizeAmount(value)\n if (parsed === null) return null\n return Math.min(Math.max(Math.round(parsed), 0), 100)\n}\n\nfunction normalizeIso(value: unknown): string | null {\n if (typeof value !== 'string' || !value.trim().length) return null\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? null : date.toISOString()\n}\n\nfunction normalizeAssociation(entry: unknown): { id: string; label: string } | null {\n if (!entry || typeof entry !== 'object') return null\n const ref = entry as Record<string, unknown>\n const id = typeof ref.id === 'string' ? ref.id : null\n if (!id) return null\n const label =\n typeof ref.label === 'string' && ref.label.trim().length ? ref.label.trim() : id\n return { id, label }\n}\n\nfunction mapDealRecord(item: DealApiRecord, fallbackTitle: string): DealCardData | null {\n const id = typeof item.id === 'string' ? item.id : null\n if (!id) return null\n const title =\n typeof item.title === 'string' && item.title.trim().length ? item.title.trim() : fallbackTitle\n const status =\n typeof item.status === 'string' && item.status.trim().length ? item.status.trim() : null\n const valueAmount = normalizeAmount(item.value_amount)\n const valueCurrency =\n typeof item.value_currency === 'string' && item.value_currency.trim().length\n ? item.value_currency.trim().toUpperCase()\n : null\n const probability = normalizeProbability(item.probability)\n const expectedCloseAt = normalizeIso(item.expected_close_at)\n const createdAt = normalizeIso(item.created_at)\n const updatedAt = normalizeIso(item.updated_at)\n const ownerUserId =\n typeof item.owner_user_id === 'string' && item.owner_user_id.trim().length\n ? item.owner_user_id\n : null\n const companies = Array.isArray(item.companies)\n ? (item.companies.map(normalizeAssociation).filter(Boolean) as { id: string; label: string }[])\n : []\n const primaryCompany = companies[0] ?? null\n const pipelineState = {\n openActivitiesCount:\n typeof item._pipeline?.openActivitiesCount === 'number' ? item._pipeline.openActivitiesCount : 0,\n daysInCurrentStage:\n typeof item._pipeline?.daysInCurrentStage === 'number' ? item._pipeline.daysInCurrentStage : 0,\n isStuck: !!item._pipeline?.isStuck,\n isOverdue: !!item._pipeline?.isOverdue,\n }\n return {\n id,\n title,\n status,\n valueAmount,\n valueCurrency,\n probability,\n expectedCloseAt,\n createdAt,\n updatedAt,\n owner: ownerUserId ? { userId: ownerUserId, label: '' } : null,\n primaryCompany,\n pipelineState,\n }\n}\n\n// Canonical tone identifiers stored in `customer_dictionary_entries.color` after the\n// 2026-05-19 hex\u2192tone migration. Keep in sync with `TONE_OPTIONS` in `AddStageDialog.tsx`.\nconst KNOWN_STAGE_TONES: ReadonlySet<string> = new Set([\n 'success',\n 'warning',\n 'info',\n 'error',\n 'neutral',\n 'brand',\n 'pink',\n])\n\nfunction buildLaneStages(\n stages: PipelineStageRecord[],\n unassignedLabel: string,\n hasUnassigned: boolean,\n): LaneStage[] {\n const sorted = stages.slice().sort((a, b) => a.order - b.order)\n const lanes: LaneStage[] = sorted.map((stage, index) => ({\n id: stage.id,\n label: stage.label,\n // Prefer the stage's persisted tone (now stored as a canonical identifier post-migration)\n // so colors picked by operators in AddStageDialog actually show up on the kanban. Falls\n // back to the position-based rotation for stages with no color set or unmapped legacy\n // values that survived the migration's neutral-fallback arm.\n tone:\n stage.color && KNOWN_STAGE_TONES.has(stage.color)\n ? (stage.color as FilterOptionTone)\n : FALLBACK_TONES[index % FALLBACK_TONES.length],\n }))\n if (hasUnassigned) {\n lanes.push({ id: '__unassigned', label: unassignedLabel, tone: 'neutral' })\n }\n return lanes\n}\n\nfunction groupDealsByStageId(deals: DealCardData[], stageIdByDealId: Map<string, string>): Map<string, DealCardData[]> {\n const grouped = new Map<string, DealCardData[]>()\n for (const deal of deals) {\n const stageKey = stageIdByDealId.get(deal.id) ?? '__unassigned'\n const bucket = grouped.get(stageKey) ?? []\n bucket.push(deal)\n grouped.set(stageKey, bucket)\n }\n return grouped\n}\n\n/**\n * Translate the UI SortOption into the deals CRUD API's `sortField` / `sortDir` query params.\n *\n * Returns `null` for `owner_asc` because the deals table only stores `owner_user_id` (a UUID);\n * the UI displays a resolved owner name, so a UUID-alphabetical server sort would be misleading.\n * For that case the caller falls back to a sensible default sort server-side and re-sorts the\n * page client-side via `sortDeals`. All other options sort server-side over the full result set,\n * so paging (25/lane \u2192 Show more) keeps a globally correct order.\n */\nfunction mapSortOptionToApi(option: SortOption): { sortField: string; sortDir: 'asc' | 'desc' } | null {\n switch (option) {\n case 'updated_desc':\n return { sortField: 'updatedAt', sortDir: 'desc' }\n case 'updated_asc':\n return { sortField: 'updatedAt', sortDir: 'asc' }\n case 'created_desc':\n return { sortField: 'createdAt', sortDir: 'desc' }\n case 'value_desc':\n return { sortField: 'value', sortDir: 'desc' }\n case 'value_asc':\n return { sortField: 'value', sortDir: 'asc' }\n case 'probability_desc':\n return { sortField: 'probability', sortDir: 'desc' }\n case 'close_asc':\n return { sortField: 'expectedCloseAt', sortDir: 'asc' }\n case 'owner_asc':\n default:\n return null\n }\n}\n\nfunction sortDeals(deals: DealCardData[], option: SortOption): DealCardData[] {\n const sorted = deals.slice()\n sorted.sort((a, b) => {\n switch (option) {\n case 'value_desc':\n case 'value_asc': {\n const aValue = typeof a.valueAmount === 'number' ? a.valueAmount : Number.NEGATIVE_INFINITY\n const bValue = typeof b.valueAmount === 'number' ? b.valueAmount : Number.NEGATIVE_INFINITY\n return option === 'value_desc' ? bValue - aValue : aValue - bValue\n }\n case 'probability_desc': {\n const aProb = typeof a.probability === 'number' ? a.probability : -1\n const bProb = typeof b.probability === 'number' ? b.probability : -1\n return bProb - aProb\n }\n case 'close_asc': {\n const aTs = a.expectedCloseAt ? new Date(a.expectedCloseAt).getTime() : Number.POSITIVE_INFINITY\n const bTs = b.expectedCloseAt ? new Date(b.expectedCloseAt).getTime() : Number.POSITIVE_INFINITY\n return aTs - bTs\n }\n case 'created_desc': {\n const aTs = a.createdAt ? new Date(a.createdAt).getTime() : 0\n const bTs = b.createdAt ? new Date(b.createdAt).getTime() : 0\n return bTs - aTs\n }\n case 'owner_asc': {\n // Owner sort is client-side only: deals carry `owner.userId` (UUID), and the human-readable\n // label is resolved separately via ownerNamesById. Compare on the resolved label that was\n // already merged into deal.owner.label by `dealsByStage`'s ownerNamesById pass.\n const aLabel = a.owner?.label ?? a.owner?.userId ?? ''\n const bLabel = b.owner?.label ?? b.owner?.userId ?? ''\n return aLabel.localeCompare(bLabel)\n }\n case 'updated_asc':\n return (a.updatedAt ?? a.createdAt ?? '').localeCompare(b.updatedAt ?? b.createdAt ?? '')\n case 'updated_desc':\n default:\n return (b.updatedAt ?? b.createdAt ?? '').localeCompare(a.updatedAt ?? a.createdAt ?? '')\n }\n })\n return sorted\n}\n\nexport default function DealsKanbanPage(): React.ReactElement {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const [selectedPipelineId, setSelectedPipelineId] = React.useState<string | null>(null)\n const [search, setSearch] = React.useState('')\n const [sortBy, setSortBy] = React.useState<SortOption>(DEFAULT_SORT)\n const [statusFilters, setStatusFilters] = React.useState<string[]>([])\n const [ownerFilters, setOwnerFilters] = React.useState<string[]>([])\n const [peopleFilters, setPeopleFilters] = React.useState<string[]>([])\n const [companyFilters, setCompanyFilters] = React.useState<string[]>([])\n const [closeDateFilter, setCloseDateFilter] = React.useState<CloseDateRange>({ from: null, to: null })\n // Currency filter narrows visible deal cards (list query) but DOES NOT narrow the aggregate\n // query \u2014 lane headers + the Currency popover keep the full breakdown so the operator can\n // see what they're filtering for and change selection without losing context. `null` = \"All\n // currencies\" sentinel from the Figma popover (1251:699).\n const [currencyFilter, setCurrencyFilter] = React.useState<string | null>(null)\n // Cache labels for selected ids so the chip can show readable text without re-fetching\n const [ownerLabels, setOwnerLabels] = React.useState<Record<string, string>>({})\n const [peopleLabels, setPeopleLabels] = React.useState<Record<string, string>>({})\n const [companyLabels, setCompanyLabels] = React.useState<Record<string, string>>({})\n const [selectedDealIds, setSelectedDealIds] = React.useState<Set<string>>(new Set())\n const [pendingDealId, setPendingDealId] = React.useState<string | null>(null)\n const [quickDealContext, setQuickDealContext] = React.useState<QuickDealContext | null>(null)\n const [addStageContext, setAddStageContext] = React.useState<AddStageContext | null>(null)\n const [customizeOpen, setCustomizeOpen] = React.useState(false)\n const [activityContext, setActivityContext] = React.useState<ActivityComposerContext | null>(null)\n const [changeStageOpen, setChangeStageOpen] = React.useState(false)\n const [changeOwnerOpen, setChangeOwnerOpen] = React.useState(false)\n const [isBulkMutating, setIsBulkMutating] = React.useState(false)\n const [singleMoveStageContext, setSingleMoveStageContext] = React.useState<\n { dealId: string; currentStageId: string | null } | null\n >(null)\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const fallbackTitle = translateWithFallback(\n t,\n 'customers.deals.pipeline.untitled',\n 'Untitled deal',\n )\n const unassignedLabel = translateWithFallback(\n t,\n 'customers.deals.pipeline.unassigned',\n 'No stage',\n )\n\n const pipelinesQuery = useQuery<PipelineRecord[]>({\n queryKey: ['customers', 'pipelines', `scope:${scopeVersion}`],\n staleTime: 60_000,\n queryFn: async () => {\n const payload = await readApiResultOrThrow<{ items: PipelineRecord[] }>(\n '/api/customers/pipelines',\n undefined,\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.pipeline.loadError',\n 'Failed to load pipelines.',\n ),\n },\n )\n return payload?.items ?? []\n },\n })\n\n React.useEffect(() => {\n if (selectedPipelineId) return\n const pipelines = pipelinesQuery.data\n if (!pipelines || !pipelines.length) return\n const defaultPipeline = pipelines.find((pipeline) => pipeline.isDefault) ?? pipelines[0]\n if (defaultPipeline) setSelectedPipelineId(defaultPipeline.id)\n }, [pipelinesQuery.data, selectedPipelineId])\n\n const staffQuery = useQuery<AssignableStaffMember[]>({\n queryKey: ['customers', 'deals', 'kanban', 'staff', `scope:${scopeVersion}`],\n staleTime: 300_000,\n queryFn: async () => fetchAssignableStaffMembers('', { pageSize: 100 }),\n })\n\n const ownerNamesById = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const member of staffQuery.data ?? []) {\n if (member.userId && member.displayName) map.set(member.userId, member.displayName)\n }\n return map\n }, [staffQuery.data])\n\n const currentUserId = useCurrentUserId()\n const currentUserLabel = React.useMemo(() => {\n if (!currentUserId) return undefined\n return ownerNamesById.get(currentUserId)\n }, [currentUserId, ownerNamesById])\n\n // Per-user, per-pipeline persisted lane widths. localStorage is per-browser per-user \u2014 exactly\n // matches the \"remember my preferred column widths\" UX from Monday / Asana / ClickUp.\n const laneWidthsScopeKey = React.useMemo(\n () => `${currentUserId ?? 'anon'}:${selectedPipelineId ?? 'none'}`,\n [currentUserId, selectedPipelineId],\n )\n const [laneWidths, setLaneWidths] = React.useState<Record<string, number>>({})\n // Hydrate from localStorage when the scope (user / pipeline) changes\n React.useEffect(() => {\n setLaneWidths(loadLaneWidths(laneWidthsScopeKey))\n }, [laneWidthsScopeKey])\n // Persist on change (debounced via setTimeout to coalesce drag-frame updates)\n React.useEffect(() => {\n const handle = window.setTimeout(() => saveLaneWidths(laneWidthsScopeKey, laneWidths), 250)\n return () => window.clearTimeout(handle)\n }, [laneWidthsScopeKey, laneWidths])\n\n const handleLaneResize = React.useCallback(\n (stageId: string, deltaPx: number) => {\n setLaneWidths((prev) => {\n const current = prev[stageId] ?? DEFAULT_LANE_WIDTH\n const next = Math.max(MIN_LANE_WIDTH, Math.min(MAX_LANE_WIDTH, current + deltaPx))\n if (next === current) return prev\n return { ...prev, [stageId]: next }\n })\n },\n [],\n )\n const handleResetLaneWidth = React.useCallback((stageId: string) => {\n setLaneWidths((prev) => {\n if (!(stageId in prev)) return prev\n const { [stageId]: _omit, ...rest } = prev\n return rest\n })\n }, [])\n const handleResetAllLaneWidths = React.useCallback(() => {\n setLaneWidths({})\n }, [])\n\n // Tenant currency list for the Quick deal dialog. The currencies module is the source\n // of truth (it backs `LaneAggregateProp.baseCurrencyCode` already); we used to hardcode\n // `['PLN','EUR','USD','GBP']` in the dialog which excluded any tenant operating in CZK,\n // SEK, HUF, BRL, etc. from quick-add.\n const currencyDictionary = useCurrencyDictionary()\n\n const companiesQuery = useQuery<QuickDealCompanyOption[]>({\n queryKey: ['customers', 'companies', 'kanban-quick-deal', `scope:${scopeVersion}`],\n staleTime: 300_000,\n queryFn: async () => {\n const call = await apiCall<{ items?: Array<{ id?: unknown; display_name?: unknown }> }>(\n '/api/customers/companies?page=1&pageSize=100&sortField=display_name&sortDir=asc',\n )\n if (!call.ok) return []\n const items = Array.isArray(call.result?.items) ? call.result!.items! : []\n const options: QuickDealCompanyOption[] = []\n for (const item of items) {\n const id = typeof item.id === 'string' ? item.id : null\n const displayName =\n typeof item.display_name === 'string' && item.display_name.trim().length\n ? item.display_name.trim()\n : null\n if (id && displayName) options.push({ id, label: displayName })\n }\n return options\n },\n })\n\n const stagesQuery = useQuery<PipelineStageRecord[]>({\n queryKey: ['customers', 'pipeline-stages', `scope:${scopeVersion}`, `pipeline:${selectedPipelineId}`],\n enabled: !!selectedPipelineId,\n staleTime: 30_000,\n queryFn: async () => {\n if (!selectedPipelineId) return []\n const payload = await readApiResultOrThrow<{ items: PipelineStageRecord[] }>(\n `/api/customers/pipeline-stages?pipelineId=${encodeURIComponent(selectedPipelineId)}`,\n undefined,\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.pipeline.loadError',\n 'Failed to load stages.',\n ),\n },\n )\n return payload?.items ?? []\n },\n })\n\n // Per-lane \"Show more\" state. Each lane's first 25 are loaded via useQueries; subsequent pages are\n // fetched on demand and appended here, so the cached page-1 cards stay in place (no full lane refetch).\n const [extraCardsByStage, setExtraCardsByStage] = React.useState<Record<string, DealApiRecord[]>>({})\n const [loadingMoreByStage, setLoadingMoreByStage] = React.useState<Record<string, boolean>>({})\n // Reset extra-cards + loading state when any filter changes\n React.useEffect(() => {\n setExtraCardsByStage({})\n setLoadingMoreByStage({})\n }, [selectedPipelineId, search, statusFilters, ownerFilters, peopleFilters, companyFilters, closeDateFilter, currencyFilter])\n\n // Filter signature shared by every per-lane query so all lanes invalidate together when filters change\n const filterSignature = React.useMemo(\n () => ({\n pipelineId: selectedPipelineId,\n search: search.trim(),\n status: statusFilters.slice().sort().join(','),\n owners: ownerFilters.slice().sort().join(','),\n people: peopleFilters.slice().sort().join(','),\n companies: companyFilters.slice().sort().join(','),\n closeFrom: closeDateFilter.from ?? '',\n closeTo: closeDateFilter.to ?? '',\n currency: currencyFilter ?? '',\n }),\n [selectedPipelineId, search, statusFilters, ownerFilters, peopleFilters, companyFilters, closeDateFilter, currencyFilter],\n )\n\n // Aggregate query \u2014 gives accurate per-stage counts + totals (in base currency) across the entire pipeline,\n // not just the loaded slice. Drives both lane headers and pagination decisions.\n const aggregateQuery = useQuery<StageAggregateResponse | null>({\n queryKey: [\n 'customers',\n 'deals',\n 'kanban-aggregate',\n `scope:${scopeVersion}`,\n `pipeline:${selectedPipelineId ?? 'none'}`,\n `search:${filterSignature.search}`,\n `status:${filterSignature.status}`,\n `owners:${filterSignature.owners}`,\n `people:${filterSignature.people}`,\n `companies:${filterSignature.companies}`,\n `close:${filterSignature.closeFrom}-${filterSignature.closeTo}`,\n ],\n enabled: !!selectedPipelineId,\n staleTime: 30_000,\n queryFn: async () => {\n if (!selectedPipelineId) return null\n const params = new URLSearchParams()\n params.set('pipelineId', selectedPipelineId)\n if (filterSignature.search.length) params.set('search', filterSignature.search)\n for (const status of statusFilters) params.append('status', status)\n for (const ownerId of ownerFilters) params.append('ownerUserId', ownerId)\n for (const personId of peopleFilters) params.append('personId', personId)\n for (const companyId of companyFilters) params.append('companyId', companyId)\n if (closeDateFilter.from) params.set('expectedCloseAtFrom', closeDateFilter.from)\n if (closeDateFilter.to) params.set('expectedCloseAtTo', closeDateFilter.to)\n const call = await apiCall<StageAggregateResponse>(\n `/api/customers/deals/aggregate?${params.toString()}`,\n )\n return call.ok ? call.result ?? null : null\n },\n })\n\n const aggregateByStage = React.useMemo(() => {\n const map = new Map<string, LaneAggregate>()\n const data = aggregateQuery.data\n if (!data) return map\n for (const row of data.perStage) {\n map.set(row.stageId, {\n count: row.count,\n totalInBaseCurrency: row.totalInBaseCurrency,\n byCurrency: row.byCurrency,\n baseCurrencyCode: data.baseCurrencyCode,\n // Default to \"complete conversion\" when the field is absent (older response, or no\n // base currency case where the route still returns the field). The aggregate route\n // explicitly sets `convertedAll: false` when at least one currency was excluded.\n convertedAll: row.convertedAll ?? true,\n missingRateCurrencies: row.missingRateCurrencies ?? [],\n })\n }\n return map\n }, [aggregateQuery.data])\n\n const stagesData = stagesQuery.data ?? []\n const aggregateData = aggregateQuery.data\n\n // Pre-compute the lane shape from stages + aggregate so unassigned cards still show up if any\n const lanes = React.useMemo<LaneStage[]>(() => {\n const knownStageIds = new Set(stagesData.map((stage) => stage.id))\n const hasUnassigned = (aggregateData?.perStage ?? []).some(\n (row) => row.stageId === '__unassigned' || (!!row.stageId && !knownStageIds.has(row.stageId)),\n )\n return buildLaneStages(stagesData, unassignedLabel, hasUnassigned)\n }, [stagesData, aggregateData, unassignedLabel])\n\n // Resolve the active SortOption to an API sort once per change. Owner sort has no server\n // representation (only owner_user_id exists in the deals table; the displayed name is resolved\n // client-side), so it falls back to `updatedAt desc` server-side and is re-sorted client-side.\n const apiSort = React.useMemo(() => {\n const mapped = mapSortOptionToApi(sortBy)\n return mapped ?? { sortField: 'updatedAt' as const, sortDir: 'desc' as const }\n }, [sortBy])\n\n const laneQueries = useQueries({\n queries: lanes.map((stage) => {\n return {\n queryKey: [\n 'customers',\n 'deals',\n 'kanban-lane',\n `scope:${scopeVersion}`,\n `pipeline:${filterSignature.pipelineId ?? 'none'}`,\n `search:${filterSignature.search}`,\n `status:${filterSignature.status}`,\n `owners:${filterSignature.owners}`,\n `people:${filterSignature.people}`,\n `companies:${filterSignature.companies}`,\n `close:${filterSignature.closeFrom}-${filterSignature.closeTo}`,\n // Sort affects which 25 deals come back on page 1, so it MUST be in the key \u2014\n // otherwise switching from \"Value (high to low)\" to \"Close (soonest)\" reuses\n // the old (wrong-sort) cache and silently keeps the wrong slice on screen.\n `sort:${apiSort.sortField}:${apiSort.sortDir}`,\n `currency:${filterSignature.currency}`,\n `stage:${stage.id}`,\n ],\n enabled: !!selectedPipelineId,\n staleTime: 30_000,\n queryFn: async () => {\n if (!selectedPipelineId) return { items: [] as DealApiRecord[], total: 0 }\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', String(LANE_PAGE_SIZE))\n params.set('pipelineId', selectedPipelineId)\n params.set('pipelineStageId', stage.id)\n params.set('sortField', apiSort.sortField)\n params.set('sortDir', apiSort.sortDir)\n if (filterSignature.search.length) params.set('search', filterSignature.search)\n for (const status of statusFilters) params.append('status', status)\n for (const ownerId of ownerFilters) params.append('ownerUserId', ownerId)\n for (const personId of peopleFilters) params.append('personId', personId)\n for (const companyId of companyFilters) params.append('companyId', companyId)\n if (closeDateFilter.from) params.set('expectedCloseAtFrom', closeDateFilter.from)\n if (closeDateFilter.to) params.set('expectedCloseAtTo', closeDateFilter.to)\n if (currencyFilter) params.set('valueCurrency', currencyFilter)\n const payload = await readApiResultOrThrow<{ items?: DealApiRecord[]; total?: number }>(\n `/api/customers/deals?${params.toString()}`,\n undefined,\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.pipeline.loadError',\n 'Failed to load deals.',\n ),\n },\n )\n const items = Array.isArray(payload?.items) ? (payload!.items as DealApiRecord[]) : []\n return { items, total: typeof payload?.total === 'number' ? payload.total : items.length }\n },\n }\n }),\n })\n\n const laneState = React.useMemo(() => {\n const dealsByStage = new Map<string, DealCardData[]>()\n const stageIdByDealId = new Map<string, string>()\n const allDeals: DealCardData[] = []\n let total = 0\n laneQueries.forEach((query, idx) => {\n const stage = lanes[idx]\n if (!stage) return\n const firstPage = query.data?.items ?? []\n const extra = extraCardsByStage[stage.id] ?? []\n // Merge first page + appended pages, deduping by id (in case of races/optimistic moves)\n const seen = new Set<string>()\n const merged: DealApiRecord[] = []\n for (const item of [...firstPage, ...extra]) {\n if (!item.id || seen.has(item.id)) continue\n seen.add(item.id)\n merged.push(item)\n }\n const list: DealCardData[] = []\n for (const item of merged) {\n const mapped = mapDealRecord(item, fallbackTitle)\n if (!mapped) continue\n list.push(mapped)\n const stageId =\n typeof item.pipeline_stage_id === 'string' && item.pipeline_stage_id.trim().length\n ? item.pipeline_stage_id\n : '__unassigned'\n stageIdByDealId.set(mapped.id, stageId)\n allDeals.push(mapped)\n }\n dealsByStage.set(stage.id, list)\n total += list.length\n })\n return { dealsByStage, stageIdByDealId, allDeals, total }\n }, [laneQueries, lanes, fallbackTitle, extraCardsByStage])\n\n const rawDeals = laneState.allDeals\n const stageIdByDealId = laneState.stageIdByDealId\n const total = aggregateData?.perStage?.reduce((sum, row) => sum + row.count, 0) ?? laneState.total\n\n /**\n * Sum of `totalInBaseCurrency` across every visible lane \u2014 gives the operator one\n * authoritative \"what's this pipeline worth?\" number at the top of the kanban. Also\n * aggregates the per-currency breakdown so the same `LaneCurrencyBreakdown` popover\n * can show the board-wide split. We compute `convertedAll` as the AND of every lane's\n * flag: any single missing-rate currency drops the board to \"partial\" so the operator\n * can't read the headline as authoritative.\n */\n const boardSummary = React.useMemo(() => {\n const perStage = aggregateData?.perStage ?? []\n if (perStage.length === 0) {\n return null\n }\n const totalsByCurrency = new Map<string, { total: number; count: number }>()\n let totalInBaseCurrency = 0\n let convertedAll = true\n const missingRateCurrencies = new Set<string>()\n for (const row of perStage) {\n totalInBaseCurrency += row.totalInBaseCurrency\n if (!(row.convertedAll ?? true)) convertedAll = false\n for (const c of row.missingRateCurrencies ?? []) missingRateCurrencies.add(c)\n for (const cur of row.byCurrency) {\n const entry = totalsByCurrency.get(cur.currency) ?? { total: 0, count: 0 }\n entry.total += cur.total\n entry.count += cur.count\n totalsByCurrency.set(cur.currency, entry)\n }\n }\n const rows = Array.from(totalsByCurrency.entries())\n .map(([currency, value]) => ({ currency, total: value.total, count: value.count }))\n .sort((a, b) => b.total - a.total)\n return {\n baseCurrencyCode: aggregateData?.baseCurrencyCode ?? null,\n totalInBaseCurrency,\n convertedAll,\n missingRateCurrencies: Array.from(missingRateCurrencies),\n rows,\n }\n }, [aggregateData])\n\n const deals = React.useMemo(() => {\n if (ownerNamesById.size === 0) return rawDeals\n return rawDeals.map((deal) => {\n if (!deal.owner) return deal\n const resolvedLabel = ownerNamesById.get(deal.owner.userId)\n if (!resolvedLabel || resolvedLabel === deal.owner.label) return deal\n return { ...deal, owner: { ...deal.owner, label: resolvedLabel } }\n })\n }, [ownerNamesById, rawDeals])\n\n const dealsByStage = React.useMemo(() => {\n const map = new Map<string, DealCardData[]>()\n if (ownerNamesById.size === 0) {\n for (const [stageId, list] of laneState.dealsByStage) map.set(stageId, list)\n return map\n }\n for (const [stageId, list] of laneState.dealsByStage) {\n map.set(\n stageId,\n list.map((deal) => {\n if (!deal.owner) return deal\n const resolvedLabel = ownerNamesById.get(deal.owner.userId)\n if (!resolvedLabel || resolvedLabel === deal.owner.label) return deal\n return { ...deal, owner: { ...deal.owner, label: resolvedLabel } }\n }),\n )\n }\n return map\n }, [laneState.dealsByStage, ownerNamesById])\n\n // Pre-sort each lane's deals once per data/sort change. This keeps the array reference\n // stable across page re-renders (e.g. drag-start, filter changes that don't touch this\n // lane), so Lane.memo's `prev.deals !== next.deals` check stays cheap \u2014 without this\n // the inline `sortDeals(...)` in the JSX allocated a new array every render and busted\n // every Lane's memo, cascading reconciliation through every memoized DealCard.\n const sortedDealsByStage = React.useMemo(() => {\n const map = new Map<string, DealCardData[]>()\n for (const [stageId, list] of dealsByStage) {\n map.set(stageId, sortDeals(list, sortBy))\n }\n return map\n }, [dealsByStage, sortBy])\n\n const handleLoadMoreInLane = React.useCallback(\n async (stageId: string) => {\n if (!selectedPipelineId) return\n if (loadingMoreByStage[stageId]) return\n // Page 1 = first 25 (initial useQueries fetch). Subsequent pages come from extraCardsByStage.\n const already = extraCardsByStage[stageId]?.length ?? 0\n const nextPage = Math.floor((LANE_PAGE_SIZE + already) / LANE_PAGE_SIZE) + 1\n setLoadingMoreByStage((prev) => ({ ...prev, [stageId]: true }))\n try {\n const params = new URLSearchParams()\n params.set('page', String(nextPage))\n params.set('pageSize', String(LANE_PAGE_SIZE))\n params.set('pipelineId', selectedPipelineId)\n params.set('pipelineStageId', stageId)\n // Show-more MUST request the same sort as page 1; otherwise the appended cards land\n // in an arbitrary order relative to the cards already on screen.\n params.set('sortField', apiSort.sortField)\n params.set('sortDir', apiSort.sortDir)\n if (filterSignature.search.length) params.set('search', filterSignature.search)\n for (const status of statusFilters) params.append('status', status)\n for (const ownerId of ownerFilters) params.append('ownerUserId', ownerId)\n for (const personId of peopleFilters) params.append('personId', personId)\n for (const companyId of companyFilters) params.append('companyId', companyId)\n if (closeDateFilter.from) params.set('expectedCloseAtFrom', closeDateFilter.from)\n if (closeDateFilter.to) params.set('expectedCloseAtTo', closeDateFilter.to)\n if (currencyFilter) params.set('valueCurrency', currencyFilter)\n const call = await apiCall<{ items?: DealApiRecord[] }>(\n `/api/customers/deals?${params.toString()}`,\n )\n if (!call.ok) return\n const items = Array.isArray(call.result?.items) ? (call.result!.items as DealApiRecord[]) : []\n setExtraCardsByStage((prev) => ({\n ...prev,\n [stageId]: [...(prev[stageId] ?? []), ...items],\n }))\n } finally {\n setLoadingMoreByStage((prev) => {\n const next = { ...prev }\n delete next[stageId]\n return next\n })\n }\n },\n [\n selectedPipelineId,\n loadingMoreByStage,\n extraCardsByStage,\n filterSignature.search,\n statusFilters,\n ownerFilters,\n peopleFilters,\n companyFilters,\n closeDateFilter.from,\n closeDateFilter.to,\n apiSort,\n ],\n )\n\n // Page chrome loading/error indicators derived from per-lane queries\n const isInitialLoading =\n !!selectedPipelineId && lanes.length === 0\n ? stagesQuery.isLoading\n : laneQueries.some((laneQuery) => laneQuery.isLoading && !laneQuery.data)\n const firstError: unknown = laneQueries.find((laneQuery) => laneQuery.isError)?.error ?? null\n\n // Centralized invalidation so writes refresh per-lane card queries, the aggregate, and drop stale extras\n const invalidateKanbanData = React.useCallback(() => {\n queryClient\n .invalidateQueries({ queryKey: ['customers', 'deals', 'kanban-lane'] })\n .catch(() => {})\n queryClient\n .invalidateQueries({ queryKey: ['customers', 'deals', 'kanban-aggregate'] })\n .catch(() => {})\n // Clear appended pages \u2014 they'll be re-fetched on demand by the user\n setExtraCardsByStage({})\n }, [queryClient])\n\n const trackedBulkProgressJobIdsRef = React.useRef(new Set<string>())\n\n const trackBulkProgressJob = React.useCallback((jobId: string | null | undefined) => {\n if (!jobId) return\n trackedBulkProgressJobIdsRef.current.add(jobId)\n }, [])\n\n const clearTrackedBulkProgressJob = React.useCallback((jobId: string | null): boolean => {\n if (!jobId) return false\n return trackedBulkProgressJobIdsRef.current.delete(jobId)\n }, [])\n\n useAppEvent(\n 'progress.job.completed',\n (event) => {\n const payload = event.payload as { jobId?: unknown } | null | undefined\n const jobId = typeof payload?.jobId === 'string' ? payload.jobId : null\n if (!clearTrackedBulkProgressJob(jobId)) return\n invalidateKanbanData()\n },\n [clearTrackedBulkProgressJob, invalidateKanbanData],\n )\n\n useAppEvent(\n 'progress.job.failed',\n (event) => {\n const payload = event.payload as { jobId?: unknown } | null | undefined\n const jobId = typeof payload?.jobId === 'string' ? payload.jobId : null\n clearTrackedBulkProgressJob(jobId)\n },\n [clearTrackedBulkProgressJob],\n )\n\n useAppEvent(\n 'progress.job.cancelled',\n (event) => {\n const payload = event.payload as { jobId?: unknown } | null | undefined\n const jobId = typeof payload?.jobId === 'string' ? payload.jobId : null\n clearTrackedBulkProgressJob(jobId)\n },\n [clearTrackedBulkProgressJob],\n )\n\n const sensors = useSensors(\n useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),\n useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),\n )\n\n const [activeDragDealId, setActiveDragDealId] = React.useState<string | null>(null)\n const activeDragDeal = React.useMemo(\n () => (activeDragDealId ? deals.find((deal) => deal.id === activeDragDealId) ?? null : null),\n [activeDragDealId, deals],\n )\n\n // Cards are draggable but NOT droppable; only lanes are droppable. That means collision\n // detection iterates only ~7 lane rects per pointer move. `pointerWithin` is the right\n // semantic \u2014 return the lane whose rect contains the pointer. We DROPPED the\n // `rectIntersection` fallback because it was running on every move alongside `pointerWithin`\n // (2\u00D7 the rect math), and the only case it covered (cursor outside every lane rect) is\n // already handled by dnd-kit returning empty collisions \u2192 no drop target shown.\n const collisionDetection = React.useCallback<CollisionDetection>((args) => pointerWithin(args), [])\n\n // Measure droppables once at drag start (not on every pointer move). Kanban lanes don't\n // resize during a drag, so re-measuring on every move is pure overhead \u2014 this single line\n // is the biggest single-shot perf win for dnd-kit kanbans.\n // dnd-kit will still automatically re-measure if a layout-affecting change happens (e.g.\n // the scroller scrolls or a new lane appears).\n const measuringConfig = React.useMemo(\n () => ({ droppable: { strategy: MeasuringStrategy.BeforeDragging } }),\n [],\n )\n\n const moveMutationContextId = 'customers-deals-kanban:stage-move'\n const { runMutation: runMoveMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: moveMutationContextId,\n blockedMessage: translateWithFallback(\n t,\n 'ui.forms.flash.saveBlocked',\n 'Save blocked by validation',\n ),\n })\n\n // Deal mutation context is declared here (not next to the dialog/bulk handlers further\n // down) because `bulkMoveDealsToStage` \u2014 declared right after `moveDealToStage` so\n // `handleDragEnd` can dispatch to it \u2014 depends on `runDealMutation`/`retryDealMutation`.\n // Hoisting the hook keeps the lexical ordering valid for `useCallback` dependency arrays.\n const dealMutationContextId = 'customers-deals-kanban:deal-mutation'\n const { runMutation: runDealMutation, retryLastMutation: retryDealMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: dealMutationContextId,\n blockedMessage: translateWithFallback(\n t,\n 'ui.forms.flash.saveBlocked',\n 'Save blocked by validation',\n ),\n })\n\n const moveDealToStage = React.useCallback(\n (dealId: string, targetStageId: string) => {\n if (!targetStageId || targetStageId === '__unassigned') return\n const currentStageId = stageIdByDealId.get(dealId)\n if (currentStageId === targetStageId) return\n\n // Optimistic update: move the card between the per-lane query caches\n const laneCachePredicate = { queryKey: ['customers', 'deals', 'kanban-lane'] as const }\n const snapshot = queryClient.getQueriesData<{ items: DealApiRecord[]; total: number }>(laneCachePredicate)\n const extraSnapshot = extraCardsByStage\n let movingItem: DealApiRecord | null = null\n for (const [key, data] of snapshot) {\n if (!data) continue\n const idx = data.items.findIndex((it) => it.id === dealId)\n if (idx < 0) continue\n const stageKey = (key as readonly unknown[]).find((part) => typeof part === 'string' && (part as string).startsWith('stage:'))\n if (typeof stageKey !== 'string') continue\n const stageId = stageKey.slice('stage:'.length)\n // remove from source lane's page 1 cache\n if (stageId === currentStageId) {\n queryClient.setQueryData(key, {\n ...data,\n items: data.items.filter((it) => it.id !== dealId),\n total: Math.max(0, data.total - 1),\n })\n movingItem = { ...data.items[idx], pipeline_stage_id: targetStageId }\n }\n }\n // Also check extra pages for the card if it wasn't on page 1\n if (!movingItem && currentStageId) {\n const extras = extraCardsByStage[currentStageId] ?? []\n const found = extras.find((it) => it.id === dealId)\n if (found) movingItem = { ...found, pipeline_stage_id: targetStageId }\n }\n // Remove from source lane's extra-pages cache\n if (currentStageId && extraCardsByStage[currentStageId]?.some((it) => it.id === dealId)) {\n setExtraCardsByStage((prev) => {\n const list = prev[currentStageId] ?? []\n return { ...prev, [currentStageId]: list.filter((it) => it.id !== dealId) }\n })\n }\n if (movingItem) {\n // add to target lane caches (if they are loaded)\n for (const [key, data] of snapshot) {\n if (!data) continue\n const stageKey = (key as readonly unknown[]).find((part) => typeof part === 'string' && (part as string).startsWith('stage:'))\n if (typeof stageKey !== 'string') continue\n const stageId = stageKey.slice('stage:'.length)\n if (stageId !== targetStageId) continue\n queryClient.setQueryData(key, {\n ...data,\n items: [movingItem!, ...data.items],\n total: data.total + 1,\n })\n }\n }\n setPendingDealId(dealId)\n\n // The moved deal's version, so a concurrent edit/move from another tab is\n // refused instead of silently overwritten (#2055).\n const dealVersion = typeof movingItem?.updated_at === 'string' ? movingItem.updated_at : null\n\n runMoveMutation({\n operation: async () => {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dealVersion),\n () => apiCallOrThrow(\n '/api/customers/deals',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: dealId, pipelineStageId: targetStageId }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.pipeline.moveError',\n 'Failed to update deal stage.',\n ),\n },\n ),\n )\n },\n context: {\n formId: moveMutationContextId,\n resourceKind: 'customers.deal',\n resourceId: dealId,\n retryLastMutation,\n },\n })\n .then(() => {\n flash(\n translateWithFallback(t, 'customers.deals.pipeline.moveSuccess', 'Deal updated.'),\n 'success',\n )\n })\n .catch((error: unknown) => {\n // Roll back optimistic update by restoring the prior cache state\n for (const [key, data] of snapshot) {\n queryClient.setQueryData(key, data)\n }\n // Restore extra-pages cache to pre-move snapshot\n setExtraCardsByStage(extraSnapshot)\n // A stale move surfaces the unified conflict bar \u2014 skip the generic flash.\n if (surfaceRecordConflict(error, t)) return\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.pipeline.moveError',\n 'Failed to update deal stage.',\n )\n flash(message, 'error')\n })\n .finally(() => {\n setPendingDealId(null)\n invalidateKanbanData()\n })\n },\n [invalidateKanbanData, queryClient, retryLastMutation, runMoveMutation, stageIdByDealId, t],\n )\n\n // Round-2 UX review item 32: when the operator drags a card that is part of a multi-card\n // selection, every selected card must travel with it. We mirror `moveDealToStage`'s\n // optimistic-update pattern but apply it across every id in `dealIds`, capture a single\n // pre-batch snapshot of every lane cache (so rollback restores the full pre-batch state\n // atomically on failure), and dispatch the same `/api/customers/deals/bulk-update-stage`\n // endpoint that powers the `BulkActionsBar` \"Change stage\" menu. The worker is async, so\n // we intentionally do NOT call `invalidateKanbanData()` after a successful POST \u2014 that\n // would refetch the pre-move server state and flicker the optimistic UI back. The\n // `progress.job.completed` listener already triggers an invalidate when the worker is\n // actually done, which is what we want.\n const bulkMoveDealsToStage = React.useCallback(\n async (dealIds: string[], targetStageId: string) => {\n if (!targetStageId || targetStageId === '__unassigned' || dealIds.length === 0) return\n const uniqueIds = Array.from(new Set(dealIds))\n const idsToMove = uniqueIds.filter((id) => stageIdByDealId.get(id) !== targetStageId)\n if (idsToMove.length === 0) return\n\n const laneCachePredicate = { queryKey: ['customers', 'deals', 'kanban-lane'] as const }\n const snapshot = queryClient.getQueriesData<{ items: DealApiRecord[]; total: number }>(laneCachePredicate)\n const extraSnapshot = extraCardsByStage\n const newExtras: Record<string, DealApiRecord[]> = { ...extraCardsByStage }\n\n for (const dealId of idsToMove) {\n const currentStageId = stageIdByDealId.get(dealId)\n if (!currentStageId) continue\n let movingItem: DealApiRecord | null = null\n for (const [key, data] of snapshot) {\n if (!data) continue\n const stageKey = (key as readonly unknown[]).find(\n (part) => typeof part === 'string' && (part as string).startsWith('stage:'),\n )\n if (typeof stageKey !== 'string') continue\n const stageId = stageKey.slice('stage:'.length)\n if (stageId !== currentStageId) continue\n const current = queryClient.getQueryData<{ items: DealApiRecord[]; total: number }>(\n key as readonly unknown[],\n )\n if (!current) continue\n const idx = current.items.findIndex((it) => it.id === dealId)\n if (idx < 0) continue\n movingItem = { ...current.items[idx], pipeline_stage_id: targetStageId }\n queryClient.setQueryData(key, {\n ...current,\n items: current.items.filter((it) => it.id !== dealId),\n total: Math.max(0, current.total - 1),\n })\n break\n }\n if (!movingItem) {\n const extras = newExtras[currentStageId] ?? []\n const found = extras.find((it) => it.id === dealId)\n if (found) {\n movingItem = { ...found, pipeline_stage_id: targetStageId }\n newExtras[currentStageId] = extras.filter((it) => it.id !== dealId)\n }\n } else if (newExtras[currentStageId]?.some((it) => it.id === dealId)) {\n newExtras[currentStageId] = (newExtras[currentStageId] ?? []).filter((it) => it.id !== dealId)\n }\n if (!movingItem) continue\n for (const [key, data] of snapshot) {\n if (!data) continue\n const stageKey = (key as readonly unknown[]).find(\n (part) => typeof part === 'string' && (part as string).startsWith('stage:'),\n )\n if (typeof stageKey !== 'string') continue\n const stageId = stageKey.slice('stage:'.length)\n if (stageId !== targetStageId) continue\n const current = queryClient.getQueryData<{ items: DealApiRecord[]; total: number }>(\n key as readonly unknown[],\n )\n if (!current) continue\n queryClient.setQueryData(key, {\n ...current,\n items: [movingItem, ...current.items],\n total: current.total + 1,\n })\n }\n }\n setExtraCardsByStage(newExtras)\n\n setIsBulkMutating(true)\n let progressJobId: string | null = null\n try {\n await runDealMutation({\n operation: async () => {\n const call = await apiCallOrThrow<BulkJobResponse>(\n '/api/customers/deals/bulk-update-stage',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n ids: idsToMove,\n pipelineStageId: targetStageId,\n }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.error',\n 'Failed to start bulk stage update.',\n ),\n },\n )\n progressJobId = call.result?.progressJobId ?? null\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deals.bulk_stage',\n resourceId: idsToMove.join(','),\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.queued',\n 'Bulk stage update started ({count} deals).',\n { count: idsToMove.length },\n ),\n 'success',\n )\n if (progressJobId) {\n trackBulkProgressJob(progressJobId)\n }\n // Selection persists after a successful bulk drag \u2014 Asana convention; lets the\n // operator drag the same set into another stage without re-selecting.\n } catch (error) {\n for (const [key, data] of snapshot) {\n queryClient.setQueryData(key, data)\n }\n setExtraCardsByStage(extraSnapshot)\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.error',\n 'Failed to start bulk stage update.',\n )\n flash(message, 'error')\n } finally {\n setIsBulkMutating(false)\n }\n },\n [\n dealMutationContextId,\n extraCardsByStage,\n queryClient,\n retryDealMutation,\n runDealMutation,\n stageIdByDealId,\n t,\n trackBulkProgressJob,\n ],\n )\n\n // Horizontal scroll arrows \u2014 let the user step lane-by-lane instead of using only the bottom slider.\n const boardScrollerRef = React.useRef<HTMLDivElement | null>(null)\n const LANE_STEP = 322 // 308px lane width + 14px gap (scaled 1.2x for readability)\n const [scrollEdges, setScrollEdges] = React.useState<{ atStart: boolean; atEnd: boolean }>({\n atStart: true,\n atEnd: false,\n })\n\n // Edge-detection tolerance in CSS px. Chrome may report fractional scrollLeft / scrollWidth\n // values when the page DPI is non-integer or the layout has sub-pixel sizing; a too-tight\n // threshold here disables the arrow before the user actually reaches the end.\n const EDGE_TOLERANCE = 4\n\n const updateScrollEdges = React.useCallback(() => {\n const el = boardScrollerRef.current\n if (!el) return\n const maxScroll = el.scrollWidth - el.clientWidth\n const nextAtStart = el.scrollLeft <= EDGE_TOLERANCE\n const nextAtEnd = maxScroll <= EDGE_TOLERANCE || el.scrollLeft >= maxScroll - EDGE_TOLERANCE\n // Functional update with equality check: avoids a full page re-render every frame of the\n // scroll animation when atStart/atEnd haven't actually flipped. Without this, the entire\n // 125+ card tree reconciled on every tick \u2192 effective frame rate collapsed.\n setScrollEdges((prev) =>\n prev.atStart === nextAtStart && prev.atEnd === nextAtEnd ? prev : { atStart: nextAtStart, atEnd: nextAtEnd },\n )\n }, [])\n\n // Combined ref + listener attachment. Using a callback ref guarantees we wire scroll/resize\n // listeners the moment the scroller div mounts (even when it's conditionally rendered behind\n // a loading guard), and cleanly tears them down when it unmounts.\n const cleanupScrollerRef = React.useRef<(() => void) | null>(null)\n const setBoardScroller = React.useCallback(\n (el: HTMLDivElement | null) => {\n cleanupScrollerRef.current?.()\n cleanupScrollerRef.current = null\n boardScrollerRef.current = el\n if (!el) return\n updateScrollEdges()\n el.addEventListener('scroll', updateScrollEdges, { passive: true })\n const ro = new ResizeObserver(updateScrollEdges)\n ro.observe(el)\n cleanupScrollerRef.current = () => {\n el.removeEventListener('scroll', updateScrollEdges)\n ro.disconnect()\n }\n },\n [updateScrollEdges],\n )\n\n // Also recompute when the lane set changes (cards loaded \u2192 scrollWidth grows)\n React.useEffect(() => {\n updateScrollEdges()\n }, [updateScrollEdges, lanes.length])\n\n // Smooth-scroll helper: animate scrollLeft over ~200ms with cubic easing.\n // Uses requestAnimationFrame so the animation is synced to the display refresh rate\n // (handles 60Hz, 120Hz, 144Hz, ProMotion etc.). performance.now() gives sub-ms timing.\n // We do NOT call updateScrollEdges() inside the step \u2014 programmatic scrollLeft writes\n // DO fire a `scroll` event (which the listener we attached in setBoardScroller handles).\n // Skipping the redundant per-frame call avoids cascading the whole page tree to re-render.\n const activeAnimRef = React.useRef<{ cancel: () => void } | null>(null)\n const animateScrollTo = React.useCallback(\n (el: HTMLDivElement, target: number) => {\n activeAnimRef.current?.cancel()\n const startLeft = el.scrollLeft\n const clampedTarget = Math.max(0, Math.min(el.scrollWidth - el.clientWidth, target))\n const distance = clampedTarget - startLeft\n if (Math.abs(distance) < 1) {\n updateScrollEdges()\n return\n }\n const duration = 200\n const hasRaf = typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function'\n const now = () =>\n typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now()\n const startTime = now()\n let handle = 0\n let cancelled = false\n const step = () => {\n if (cancelled) return\n const t = Math.min(1, (now() - startTime) / duration)\n const eased = 1 - Math.pow(1 - t, 3)\n el.scrollLeft = startLeft + distance * eased\n if (t < 1) {\n handle = hasRaf\n ? window.requestAnimationFrame(step)\n : (window.setTimeout(step, 16) as unknown as number)\n } else {\n // Final edge-sync once the animation lands \u2014 the scroll listener also fires,\n // but on some browsers the last `scroll` event is debounced behind the final write.\n updateScrollEdges()\n }\n }\n handle = hasRaf\n ? window.requestAnimationFrame(step)\n : (window.setTimeout(step, 16) as unknown as number)\n activeAnimRef.current = {\n cancel: () => {\n cancelled = true\n if (hasRaf) window.cancelAnimationFrame(handle)\n else window.clearTimeout(handle)\n },\n }\n },\n [updateScrollEdges],\n )\n\n // Pre-compute lane left-edge offsets (in scrollLeft coords) so resize-aware \"step one lane\"\n // works even when lanes have different widths.\n const laneOffsets = React.useMemo(() => {\n const offsets: number[] = []\n let left = 0\n for (const stage of lanes) {\n offsets.push(left)\n const w = laneWidths[stage.id] ?? DEFAULT_LANE_WIDTH\n left += w + LANE_GAP\n }\n return offsets\n }, [lanes, laneWidths])\n\n // Per-direction step: pick the next lane boundary based on the live scrollLeft.\n // Variable-width-aware: uses laneOffsets instead of a fixed LANE_STEP so resized columns still\n // snap cleanly. Chrome's scrollLeft can be fractional; we add EDGE_TOLERANCE to dodge that.\n const stepScroll = React.useCallback(\n (direction: -1 | 1) => {\n const el = boardScrollerRef.current\n if (!el) return false\n const sl = el.scrollLeft\n const maxScroll = Math.max(0, el.scrollWidth - el.clientWidth)\n if (maxScroll <= EDGE_TOLERANCE) return false\n if (direction < 0 && sl <= EDGE_TOLERANCE) return false\n if (direction > 0 && sl >= maxScroll - EDGE_TOLERANCE) return false\n\n let target: number\n if (direction > 0) {\n // First lane offset that is meaningfully ahead of the current scroll position\n const next = laneOffsets.find((off) => off > sl + EDGE_TOLERANCE)\n target = next ?? maxScroll\n } else {\n // Last lane offset that is meaningfully behind the current scroll position\n let prev = 0\n for (const off of laneOffsets) {\n if (off >= sl - EDGE_TOLERANCE) break\n prev = off\n }\n target = prev\n }\n if (target > maxScroll - EDGE_TOLERANCE) target = maxScroll\n if (target < EDGE_TOLERANCE) target = 0\n\n // No-op safety: if target is essentially the current position, do nothing\n if (Math.abs(target - sl) < 1) return false\n\n animateScrollTo(el, target)\n // Belt-and-suspenders: if the animation library couldn't progress in this browser, force\n // the target after the animation duration so we never \"stick\" mid-scroll.\n window.setTimeout(() => {\n if (!boardScrollerRef.current) return\n const cur = boardScrollerRef.current.scrollLeft\n if (Math.abs(cur - target) > EDGE_TOLERANCE) {\n boardScrollerRef.current.scrollLeft = target\n }\n updateScrollEdges()\n }, 240)\n return true\n },\n [animateScrollTo, updateScrollEdges, laneOffsets],\n )\n\n // Press-and-hold continuous scroll. First step fires immediately on press; if the user keeps\n // the button held, additional steps fire every ~280ms (matched to animation duration) until they\n // release or the scroller reaches the edge.\n const holdTimerRef = React.useRef<number | null>(null)\n const stopContinuousScroll = React.useCallback(() => {\n if (holdTimerRef.current !== null) {\n window.clearInterval(holdTimerRef.current)\n holdTimerRef.current = null\n }\n }, [])\n const startContinuousScroll = React.useCallback(\n (direction: -1 | 1) => {\n stopContinuousScroll()\n const moved = stepScroll(direction)\n if (!moved) return\n const HOLD_DELAY = 320\n const REPEAT_INTERVAL = 240\n holdTimerRef.current = window.setTimeout(function repeat() {\n const more = stepScroll(direction)\n if (!more) {\n stopContinuousScroll()\n return\n }\n holdTimerRef.current = window.setTimeout(repeat, REPEAT_INTERVAL) as unknown as number\n }, HOLD_DELAY) as unknown as number\n },\n [stepScroll, stopContinuousScroll],\n )\n\n React.useEffect(() => {\n // Safety: stop any running hold timer on unmount\n return () => stopContinuousScroll()\n }, [stopContinuousScroll])\n\n // Dedup pointerdown + click: real browsers fire both when the user clicks a button.\n // We start the hold sequence on pointerdown (so press-and-hold feels immediate). The click\n // event that follows would otherwise scroll an EXTRA lane \u2014 we suppress it here.\n // Keyboard activation (Enter/Space) only fires click, no pointerdown, so it still works.\n const lastPointerDownAtRef = React.useRef(0)\n const handleScrollPrev = React.useCallback(() => {\n if (Date.now() - lastPointerDownAtRef.current < 500) return\n stepScroll(-1)\n }, [stepScroll])\n const handleScrollNext = React.useCallback(() => {\n if (Date.now() - lastPointerDownAtRef.current < 500) return\n stepScroll(1)\n }, [stepScroll])\n const handleHoldPrevStart = React.useCallback(() => {\n lastPointerDownAtRef.current = Date.now()\n startContinuousScroll(-1)\n }, [startContinuousScroll])\n const handleHoldNextStart = React.useCallback(() => {\n lastPointerDownAtRef.current = Date.now()\n startContinuousScroll(1)\n }, [startContinuousScroll])\n\n // Keyboard navigation: ArrowLeft / ArrowRight scroll one lane at a time, mirroring the\n // on-screen arrow buttons. We intentionally:\n // - skip when the user is typing inside an input / textarea / select / contenteditable\n // - skip when modifier keys are held (Cmd/Ctrl/Alt/Shift) so we don't fight browser shortcuts\n // - skip when any dialog or popover (Radix `[role=\"dialog\"]` / `[data-state=\"open\"]` portals)\n // is open, so popover keyboard nav (Apply, Tab, Escape, etc.) keeps working\n // - skip when no kanban scroller is mounted yet (initial loading state)\n React.useEffect(() => {\n const onKey = (event: KeyboardEvent) => {\n if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) return\n if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight') return\n const target = event.target as HTMLElement | null\n if (target) {\n const tag = target.tagName\n if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT') return\n if (target.isContentEditable) return\n }\n // Don't steal arrow keys while a dialog/popover is open \u2014 they own keyboard nav.\n if (typeof document !== 'undefined') {\n if (document.querySelector('[role=\"dialog\"][data-state=\"open\"], [role=\"alertdialog\"][data-state=\"open\"]')) return\n if (document.querySelector('[data-radix-popper-content-wrapper] [data-state=\"open\"]')) return\n }\n if (!boardScrollerRef.current) return\n event.preventDefault()\n stepScroll(event.key === 'ArrowRight' ? 1 : -1)\n }\n window.addEventListener('keydown', onKey)\n return () => window.removeEventListener('keydown', onKey)\n }, [stepScroll])\n\n const handleDragStart = React.useCallback((event: DragStartEvent) => {\n const id = typeof event.active.id === 'string' ? event.active.id : null\n setActiveDragDealId(id)\n }, [])\n\n const handleDragCancel = React.useCallback(() => {\n setActiveDragDealId(null)\n }, [])\n\n const handleDragEnd = React.useCallback(\n (event: DragEndEvent) => {\n setActiveDragDealId(null)\n const dealId = typeof event.active.id === 'string' ? event.active.id : null\n if (!dealId) return\n const overId = event.over?.id\n if (typeof overId !== 'string') return\n\n let targetStageId: string | null = null\n if (overId.startsWith('lane:')) {\n targetStageId = overId.slice('lane:'.length)\n } else {\n targetStageId = stageIdByDealId.get(overId) ?? null\n }\n if (!targetStageId) return\n\n // Round-2 UX review item 32: bulk-drag dispatch. If the dragged card is part of a\n // multi-card selection, route through `bulkMoveDealsToStage` so every selected card\n // travels with it. Dragging a NON-selected card is treated as a single-card move\n // and leaves the existing selection intact (the operator may have selected cards\n // intentionally and just be moving an unrelated one).\n const isBulkDrag = selectedDealIds.has(dealId) && selectedDealIds.size > 1\n if (isBulkDrag) {\n void bulkMoveDealsToStage(Array.from(selectedDealIds), targetStageId)\n } else {\n moveDealToStage(dealId, targetStageId)\n }\n },\n [bulkMoveDealsToStage, moveDealToStage, selectedDealIds, stageIdByDealId],\n )\n\n const bulkDragActive = React.useMemo(\n () => activeDragDealId !== null && selectedDealIds.has(activeDragDealId) && selectedDealIds.size > 1,\n [activeDragDealId, selectedDealIds],\n )\n\n const handleToggleSelect = React.useCallback((dealId: string) => {\n setSelectedDealIds((prev) => {\n const next = new Set(prev)\n if (next.has(dealId)) next.delete(dealId)\n else next.add(dealId)\n return next\n })\n }, [])\n\n const handleOpenDetail = React.useCallback(\n (dealId: string) => {\n router.push(`/backend/customers/deals/${dealId}`)\n },\n [router],\n )\n\n const handleComingSoon = React.useCallback(\n (label: string) => {\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.comingSoon',\n '{feature} arrives in the next iteration.',\n { feature: label },\n ),\n 'info',\n )\n },\n [t],\n )\n\n const handleChipClick = React.useCallback(\n (chipId: KanbanFilterChip['id']) => {\n handleComingSoon(\n translateWithFallback(\n t,\n 'customers.deals.kanban.filter.aria.chip',\n 'Filter by {label}',\n { label: chipId },\n ),\n )\n },\n [handleComingSoon, t],\n )\n\n const handleCustomizeView = React.useCallback(() => {\n setCustomizeOpen(true)\n }, [])\n\n // \"Reset to default\" must clear every filter chip the operator may have set, otherwise\n // the menu copy (\"Clear filters and restore columns\") is a lie. Previously only the\n // status filter and sort were reset, which left the operator confused when other chips\n // (pipeline, owner, people, companies, close date, currency, search) stayed populated.\n const handleResetView = React.useCallback(() => {\n setStatusFilters([])\n setOwnerFilters([])\n setPeopleFilters([])\n setCompanyFilters([])\n setCloseDateFilter({ from: null, to: null })\n setCurrencyFilter(null)\n setSearch('')\n setSortBy(DEFAULT_SORT)\n }, [])\n\n const activePipelineName = React.useMemo(() => {\n if (!selectedPipelineId) return ''\n return pipelinesQuery.data?.find((pipeline) => pipeline.id === selectedPipelineId)?.name ?? ''\n }, [pipelinesQuery.data, selectedPipelineId])\n\n const stageLabelById = React.useMemo(() => {\n const map = new Map<string, string>()\n for (const lane of lanes) map.set(lane.id, lane.label)\n return map\n }, [lanes])\n\n const handleQuickAdd = React.useCallback(\n (stageId: string) => {\n if (!selectedPipelineId) return\n const stageLabel = stageLabelById.get(stageId)\n if (!stageLabel || stageId === '__unassigned') return\n setQuickDealContext({\n pipelineId: selectedPipelineId,\n pipelineName: activePipelineName,\n pipelineStageId: stageId,\n pipelineStageLabel: stageLabel,\n })\n },\n [activePipelineName, selectedPipelineId, stageLabelById],\n )\n\n const handleAddStage = React.useCallback(() => {\n if (!selectedPipelineId) return\n // Snapshot the current pipeline's stages (sorted by `order`) so the position picker\n // can offer \"After {label}\" entries. We sort here rather than relying on stagesData's\n // ordering because that array's order is technically determined by the API response \u2014\n // a defensive in-place sort is cheap and survives any future API change.\n const orderedStages = stagesData\n .filter((stage) => stage.pipelineId === selectedPipelineId)\n .slice()\n .sort((a, b) => a.order - b.order)\n .map((stage) => ({ id: stage.id, label: stage.label, order: stage.order }))\n setAddStageContext({\n pipelineId: selectedPipelineId,\n pipelineName: activePipelineName,\n existingStages: orderedStages,\n })\n }, [activePipelineName, selectedPipelineId, stagesData])\n\n const handleDialogCreated = React.useCallback(() => {\n invalidateKanbanData()\n queryClient\n .invalidateQueries({\n queryKey: ['customers', 'pipeline-stages', `scope:${scopeVersion}`, `pipeline:${selectedPipelineId}`],\n })\n .catch(() => {})\n }, [invalidateKanbanData, scopeVersion, selectedPipelineId])\n\n const updateDealStatus = React.useCallback(\n async (dealId: string, status: 'win' | 'loose') => {\n const dealVersion = deals.find((deal) => deal.id === dealId)?.updatedAt ?? null\n setPendingDealId(dealId)\n try {\n await runDealMutation({\n operation: async () => {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(dealVersion),\n () => apiCallOrThrow(\n '/api/customers/deals',\n {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: dealId, status }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.error.status',\n 'Failed to update deal status.',\n ),\n },\n ),\n )\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deal',\n resourceId: dealId,\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n status === 'win'\n ? translateWithFallback(\n t,\n 'customers.deals.kanban.menu.markWon.success',\n 'Deal marked as won.',\n )\n : translateWithFallback(\n t,\n 'customers.deals.kanban.menu.markLost.success',\n 'Deal marked as lost.',\n ),\n 'success',\n )\n invalidateKanbanData()\n } catch (error) {\n if (surfaceRecordConflict(error, t)) { invalidateKanbanData(); return }\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.menu.error.status',\n 'Failed to update deal status.',\n )\n flash(message, 'error')\n } finally {\n setPendingDealId(null)\n }\n },\n [deals, invalidateKanbanData, retryDealMutation, runDealMutation, t],\n )\n\n const deleteDeal = React.useCallback(\n async (dealId: string, dealTitle: string) => {\n const confirmed = await confirm({\n title: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.confirmTitle',\n 'Delete deal \u201C{title}\u201D?',\n { title: dealTitle },\n ),\n text: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.confirmText',\n 'This action cannot be undone.',\n ),\n variant: 'destructive',\n confirmText: translateWithFallback(t, 'customers.deals.kanban.menu.delete', 'Delete'),\n })\n if (!confirmed) return\n\n const lockVersion = deals.find((deal) => deal.id === dealId)?.updatedAt ?? null\n setPendingDealId(dealId)\n try {\n await runDealMutation({\n operation: async () => {\n await withScopedApiRequestHeaders(\n buildOptimisticLockHeader(lockVersion),\n () => deleteCrud('customers/deals', {\n body: { id: dealId },\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.error',\n 'Failed to delete deal.',\n ),\n }),\n )\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deal',\n resourceId: dealId,\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(t, 'customers.deals.kanban.menu.delete.success', 'Deal deleted.'),\n 'success',\n )\n invalidateKanbanData()\n } catch (error) {\n // A stale delete surfaces the unified conflict bar \u2014 skip the generic flash (#2332).\n if (surfaceRecordConflict(error, t)) {\n invalidateKanbanData()\n return\n }\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.error',\n 'Failed to delete deal.',\n )\n flash(message, 'error')\n } finally {\n setPendingDealId(null)\n }\n },\n [confirm, deals, invalidateKanbanData, retryDealMutation, runDealMutation, t],\n )\n\n const bulkSelectionSummary = React.useMemo(() => {\n if (selectedDealIds.size === 0) return { count: 0, totalLabel: null, currency: null as string | null, ids: [] as string[] }\n // Group totals by currency so the label is HONEST when the selection mixes currencies.\n // The previous implementation summed `valueAmount` across every selected deal and labeled\n // the result with whichever currency happened to appear first \u2014 e.g. selecting \u20AC100k + $50k\n // displayed as \"\u20AC150k\", which is meaningless. We now keep one bucket per currency.\n const totalsByCurrency = new Map<string, number>()\n const ids: string[] = []\n for (const deal of deals) {\n if (!selectedDealIds.has(deal.id)) continue\n ids.push(deal.id)\n if (typeof deal.valueAmount === 'number' && Number.isFinite(deal.valueAmount) && deal.valueAmount > 0) {\n const code = deal.valueCurrency && deal.valueCurrency.length === 3\n ? deal.valueCurrency.toUpperCase()\n : 'USD'\n totalsByCurrency.set(code, (totalsByCurrency.get(code) ?? 0) + deal.valueAmount)\n }\n }\n const rows = Array.from(totalsByCurrency.entries())\n .map(([code, amount]) => ({ code, amount }))\n .sort((a, b) => b.amount - a.amount)\n const formatOne = (code: string, amount: number) => {\n try {\n return new Intl.NumberFormat(undefined, {\n style: 'currency',\n currency: code,\n maximumFractionDigits: 0,\n }).format(amount)\n } catch {\n return `${code} ${Math.round(amount)}`\n }\n }\n let totalLabel: string | null = null\n if (rows.length === 1) {\n totalLabel = formatOne(rows[0].code, rows[0].amount)\n } else if (rows.length >= 2) {\n // Concatenate per-currency totals with \" + \" so multi-currency selections stay readable.\n // Capping at 3 visible rows keeps the bar narrow; the rest collapses into \"+N more\"\n // so the operator sees the dominant currencies and an explicit \"mixed\" hint without\n // misleading aggregation across rates.\n const visible = rows.slice(0, 3).map((row) => formatOne(row.code, row.amount))\n const overflow = rows.length - visible.length\n totalLabel = overflow > 0 ? `${visible.join(' + ')} +${overflow}` : visible.join(' + ')\n }\n // `currency` (singular) is intentionally null when 2+ currencies are present so downstream\n // consumers (CSV export, change-stage dialog) don't assume a single canonical currency.\n const currency = rows.length === 1 ? rows[0].code : null\n return { count: ids.length, totalLabel, currency, ids }\n }, [deals, selectedDealIds])\n\n const handleBulkClear = React.useCallback(() => {\n setSelectedDealIds(new Set())\n }, [])\n\n const stageOptions = React.useMemo(\n () =>\n (stagesQuery.data ?? [])\n .slice()\n .sort((a, b) => a.order - b.order)\n .map((stage) => ({ id: stage.id, label: stage.label })),\n [stagesQuery.data],\n )\n\n const handleBulkChangeStage = React.useCallback(\n async (stageId: string) => {\n if (bulkSelectionSummary.ids.length === 0) return\n setIsBulkMutating(true)\n let progressJobId: string | null = null\n try {\n // Bulk writes MUST go through useGuardedMutation so injection modules (record-lock\n // conflict handling, retry chains, scoped headers) run the same `onBeforeSave`/\n // `onAfterSave` lifecycle as single-deal writes \u2014 see UI AGENTS.md and customers\n // module MUST rules. `runMutation` throws on guard rejection; the catch block below\n // surfaces the error message via flash().\n await runDealMutation({\n operation: async () => {\n const call = await apiCallOrThrow<BulkJobResponse>(\n '/api/customers/deals/bulk-update-stage',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n ids: bulkSelectionSummary.ids,\n pipelineStageId: stageId,\n }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.error',\n 'Failed to start bulk stage update.',\n ),\n },\n )\n progressJobId = call.result?.progressJobId ?? null\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deals.bulk_stage',\n resourceId: bulkSelectionSummary.ids.join(','),\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.queued',\n 'Bulk stage update started ({count} deals).',\n { count: bulkSelectionSummary.count },\n ),\n 'success',\n )\n setChangeStageOpen(false)\n setSelectedDealIds(new Set())\n if (progressJobId) {\n trackBulkProgressJob(progressJobId)\n }\n invalidateKanbanData()\n } catch (error) {\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeStage.error',\n 'Failed to start bulk stage update.',\n )\n flash(message, 'error')\n } finally {\n setIsBulkMutating(false)\n }\n },\n [\n bulkSelectionSummary.count,\n bulkSelectionSummary.ids,\n dealMutationContextId,\n invalidateKanbanData,\n retryDealMutation,\n runDealMutation,\n t,\n trackBulkProgressJob,\n ],\n )\n\n const handleBulkChangeOwner = React.useCallback(\n async (ownerUserId: string | null) => {\n if (bulkSelectionSummary.ids.length === 0) return\n setIsBulkMutating(true)\n let progressJobId: string | null = null\n try {\n await runDealMutation({\n operation: async () => {\n const call = await apiCallOrThrow<BulkJobResponse>(\n '/api/customers/deals/bulk-update-owner',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n ids: bulkSelectionSummary.ids,\n ownerUserId,\n }),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeOwner.error',\n 'Failed to start bulk owner update.',\n ),\n },\n )\n progressJobId = call.result?.progressJobId ?? null\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deals.bulk_owner',\n resourceId: bulkSelectionSummary.ids.join(','),\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeOwner.queued',\n 'Bulk owner update started ({count} deals).',\n { count: bulkSelectionSummary.count },\n ),\n 'success',\n )\n setChangeOwnerOpen(false)\n setSelectedDealIds(new Set())\n if (progressJobId) {\n trackBulkProgressJob(progressJobId)\n }\n invalidateKanbanData()\n } catch (error) {\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.changeOwner.error',\n 'Failed to start bulk owner update.',\n )\n flash(message, 'error')\n } finally {\n setIsBulkMutating(false)\n }\n },\n [\n bulkSelectionSummary.count,\n bulkSelectionSummary.ids,\n dealMutationContextId,\n invalidateKanbanData,\n retryDealMutation,\n runDealMutation,\n t,\n trackBulkProgressJob,\n ],\n )\n\n const handleBulkExport = React.useCallback(() => {\n if (bulkSelectionSummary.ids.length === 0) return\n const url = buildCrudExportUrl(\n 'customers/deals',\n {\n ids: bulkSelectionSummary.ids.join(','),\n exportScope: 'view',\n },\n 'csv',\n )\n if (typeof window !== 'undefined') {\n window.open(url, '_blank', 'noopener')\n }\n }, [bulkSelectionSummary.ids])\n\n const handleBulkDelete = React.useCallback(async () => {\n if (bulkSelectionSummary.ids.length === 0) return\n const confirmed = await confirm({\n title: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.title',\n 'Delete {count} deals?',\n { count: bulkSelectionSummary.count },\n ),\n text: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.text',\n 'This action cannot be undone.',\n ),\n variant: 'destructive',\n confirmText: translateWithFallback(t, 'customers.deals.kanban.bulk.delete', 'Delete'),\n })\n if (!confirmed) return\n\n const rows = deals.filter((deal) => selectedDealIds.has(deal.id)).map((deal) => ({ id: deal.id }))\n\n setIsBulkMutating(true)\n try {\n const { succeeded, failures } = await runBulkDelete(\n rows,\n async (row) => {\n await deleteCrud('customers/deals', {\n body: { id: row.id },\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.error',\n 'Failed to delete deal.',\n ),\n })\n },\n {\n fallbackErrorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.delete.error',\n 'Failed to delete deal.',\n ),\n logTag: 'customers.deals.kanban',\n progress: {\n jobType: 'customers.deals.bulk_delete',\n name: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.progress.name',\n 'Delete selected deals',\n ),\n description: translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.progress.description',\n '{count} deals queued for deletion',\n { count: rows.length },\n ),\n meta: { source: 'customers.deals.kanban' },\n },\n },\n )\n\n if (succeeded.length > 0) {\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.success',\n '{count} deals deleted.',\n { count: succeeded.length },\n ),\n failures.length === 0 ? 'success' : 'warning',\n )\n }\n for (const group of groupBulkDeleteFailures(failures)) {\n const message =\n group.count === 1\n ? group.sampleMessage\n : translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.delete.failedGroup',\n '{count} deals could not be deleted: {message}',\n { count: group.count, message: group.sampleMessage },\n )\n flash(message, 'error')\n }\n setSelectedDealIds(new Set())\n invalidateKanbanData()\n } finally {\n setIsBulkMutating(false)\n }\n }, [\n bulkSelectionSummary.count,\n bulkSelectionSummary.ids,\n confirm,\n deals,\n queryClient,\n selectedDealIds,\n t,\n ])\n\n const duplicateDeal = React.useCallback(\n async (deal: DealCardData) => {\n setPendingDealId(deal.id)\n try {\n const stageId = stageIdByDealId.get(deal.id) ?? null\n const payload: Record<string, unknown> = {\n title: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.duplicate.titleSuffix',\n '{title} (copy)',\n { title: deal.title },\n ),\n status: 'open',\n }\n if (selectedPipelineId) payload.pipelineId = selectedPipelineId\n if (stageId && stageId !== '__unassigned') payload.pipelineStageId = stageId\n if (typeof deal.valueAmount === 'number') payload.valueAmount = deal.valueAmount\n if (deal.valueCurrency) payload.valueCurrency = deal.valueCurrency\n if (typeof deal.probability === 'number') payload.probability = deal.probability\n if (deal.expectedCloseAt) payload.expectedCloseAt = deal.expectedCloseAt\n if (deal.owner?.userId) payload.ownerUserId = deal.owner.userId\n\n await runDealMutation({\n operation: async () => {\n await apiCallOrThrow(\n '/api/customers/deals',\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n },\n {\n errorMessage: translateWithFallback(\n t,\n 'customers.deals.kanban.menu.duplicate.error',\n 'Failed to duplicate deal.',\n ),\n },\n )\n },\n context: {\n formId: dealMutationContextId,\n resourceKind: 'customers.deal',\n resourceId: deal.id,\n retryLastMutation: retryDealMutation,\n },\n })\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.menu.duplicate.success',\n 'Deal duplicated.',\n ),\n 'success',\n )\n invalidateKanbanData()\n } catch (error) {\n const message =\n error instanceof Error && error.message\n ? error.message\n : translateWithFallback(\n t,\n 'customers.deals.kanban.menu.duplicate.error',\n 'Failed to duplicate deal.',\n )\n flash(message, 'error')\n } finally {\n setPendingDealId(null)\n }\n },\n [\n queryClient,\n retryDealMutation,\n runDealMutation,\n selectedPipelineId,\n stageIdByDealId,\n t,\n ],\n )\n\n const buildMenuItems = React.useCallback(\n (deal: DealCardData): RowActionItem[] => [\n {\n id: 'open',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.open', 'Open deal'),\n onSelect: () => handleOpenDetail(deal.id),\n },\n {\n id: 'edit',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.edit', 'Edit'),\n onSelect: () => handleOpenDetail(deal.id),\n },\n {\n id: 'duplicate',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.duplicate', 'Duplicate'),\n onSelect: () => void duplicateDeal(deal),\n },\n {\n id: 'move-stage',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.moveStage', 'Move stage\u2026'),\n onSelect: () =>\n setSingleMoveStageContext({\n dealId: deal.id,\n currentStageId: stageIdByDealId.get(deal.id) ?? null,\n }),\n },\n {\n id: 'mark-won',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.markWon', 'Mark as Won'),\n onSelect: () => void updateDealStatus(deal.id, 'win'),\n },\n {\n id: 'mark-lost',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.markLost', 'Mark as Lost'),\n onSelect: () => void updateDealStatus(deal.id, 'loose'),\n },\n {\n id: 'delete',\n label: translateWithFallback(t, 'customers.deals.kanban.menu.delete', 'Delete'),\n destructive: true,\n onSelect: () => void deleteDeal(deal.id, deal.title),\n },\n ],\n [\n deleteDeal,\n duplicateDeal,\n handleOpenDetail,\n stageIdByDealId,\n t,\n updateDealStatus,\n ],\n )\n\n const handleComposeActivity = React.useCallback(\n (dealId: string, type: 'call' | 'email' | 'note') => {\n const deal = deals.find((entry) => entry.id === dealId)\n if (!deal) return\n const entityId = deal.primaryCompany?.id\n if (!entityId) {\n flash(\n translateWithFallback(\n t,\n 'customers.deals.kanban.activityComposer.noEntity',\n 'Link a company to this deal before logging activities here.',\n ),\n 'info',\n )\n return\n }\n setActivityContext({\n dealId,\n dealTitle: deal.title,\n type,\n entityId,\n })\n },\n [deals, t],\n )\n\n // The placeholder \"stub\" chips have been replaced by real popovers; pass an empty list.\n const filterChips = React.useMemo<KanbanFilterChip[]>(() => [], [])\n\n const pipelineFilterOptions = React.useMemo(\n () =>\n (pipelinesQuery.data ?? []).map((pipeline) => ({\n id: pipeline.id,\n name: pipeline.name,\n })),\n [pipelinesQuery.data],\n )\n\n // Async loaders for entity-filter popovers (Owner / People / Companies)\n const loadOwnerOptions = React.useCallback(\n async (query: string, _signal: AbortSignal): Promise<EntityFilterOption[]> => {\n const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100 })\n const opts: EntityFilterOption[] = items\n .filter((user) => !!user.userId && !!user.displayName)\n .map((user) => ({ value: user.userId!, label: user.displayName! }))\n // Cache labels so chip can display readable text for already-selected ids\n setOwnerLabels((prev) => {\n const next: Record<string, string> = { ...prev }\n for (const option of opts) next[option.value] = option.label\n return next\n })\n return opts\n },\n [],\n )\n const loadPeopleOptions = React.useCallback(\n async (query: string, signal: AbortSignal): Promise<EntityFilterOption[]> => {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '50')\n if (query) params.set('search', query)\n params.set('sortField', 'displayName')\n params.set('sortDir', 'asc')\n // Route the request through `apiCall` so scoped tenant/org headers are attached and the\n // JSON parser uses the shared safe-read helper. AbortSignal is forwarded through init.\n const call = await apiCall<{\n items?: Array<{ id?: string; display_name?: string; first_name?: string; last_name?: string }>\n }>(`/api/customers/people?${params.toString()}`, { signal })\n if (!call.ok) return []\n const items = call.result?.items ?? []\n const opts: EntityFilterOption[] = []\n for (const it of items) {\n if (!it.id) continue\n const label = (it.display_name && it.display_name.trim().length)\n ? it.display_name.trim()\n : [it.first_name, it.last_name].filter(Boolean).join(' ').trim() || it.id.slice(0, 8)\n opts.push({ value: it.id, label })\n }\n setPeopleLabels((prev) => {\n const next: Record<string, string> = { ...prev }\n for (const o of opts) next[o.value] = o.label\n return next\n })\n return opts\n },\n [],\n )\n const loadCompanyOptions = React.useCallback(\n async (query: string, signal: AbortSignal): Promise<EntityFilterOption[]> => {\n const params = new URLSearchParams()\n params.set('page', '1')\n params.set('pageSize', '50')\n if (query) params.set('search', query)\n params.set('sortField', 'display_name')\n params.set('sortDir', 'asc')\n const call = await apiCall<{ items?: Array<{ id?: string; display_name?: string }> }>(\n `/api/customers/companies?${params.toString()}`,\n { signal },\n )\n if (!call.ok) return []\n const items = call.result?.items ?? []\n const opts: EntityFilterOption[] = []\n for (const it of items) {\n if (!it.id || !it.display_name) continue\n opts.push({ value: it.id, label: it.display_name })\n }\n setCompanyLabels((prev) => {\n const next: Record<string, string> = { ...prev }\n for (const o of opts) next[o.value] = o.label\n return next\n })\n return opts\n },\n [],\n )\n\n const leadingChipsNode = (\n <>\n <StatusFilterPopover values={statusFilters} onApply={setStatusFilters} />\n <PipelineFilterPopover\n pipelines={pipelineFilterOptions}\n selectedPipelineId={selectedPipelineId}\n onApply={setSelectedPipelineId}\n />\n {boardSummary && boardSummary.rows.length > 0 ? (\n <CurrencyFilterPopover\n rows={boardSummary.rows}\n baseCurrencyCode={boardSummary.baseCurrencyCode}\n totalInBaseCurrency={boardSummary.totalInBaseCurrency}\n headingLabel={translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.headingLabel',\n 'PIPELINE',\n )}\n headingCount={total}\n selectedCurrency={currencyFilter}\n onApply={setCurrencyFilter}\n />\n ) : null}\n <EntityFilterPopover\n label={translateWithFallback(t, 'customers.deals.kanban.filter.owner', 'Owner')}\n anyLabel={translateWithFallback(t, 'customers.deals.kanban.filter.all', 'All')}\n values={ownerFilters}\n onApply={setOwnerFilters}\n loadOptions={loadOwnerOptions}\n labelById={ownerLabels}\n />\n <EntityFilterPopover\n label={translateWithFallback(t, 'customers.deals.kanban.filter.people', 'People')}\n values={peopleFilters}\n onApply={setPeopleFilters}\n loadOptions={loadPeopleOptions}\n labelById={peopleLabels}\n />\n <EntityFilterPopover\n label={translateWithFallback(t, 'customers.deals.kanban.filter.companies', 'Companies')}\n values={companyFilters}\n onApply={setCompanyFilters}\n loadOptions={loadCompanyOptions}\n labelById={companyLabels}\n />\n <CloseDateFilterPopover value={closeDateFilter} onApply={setCloseDateFilter} />\n </>\n )\n\n const sortNode = <SortByPopover value={sortBy} onApply={setSortBy} />\n\n const activePipeline = React.useMemo(() => {\n if (!selectedPipelineId) return null\n return pipelinesQuery.data?.find((pipeline) => pipeline.id === selectedPipelineId) ?? null\n }, [pipelinesQuery.data, selectedPipelineId])\n\n const dragHint = translateWithFallback(\n t,\n 'customers.deals.kanban.helper.dragHint',\n 'Drag cards between lanes to update stage',\n )\n const footerCount = translateWithFallback(\n t,\n 'customers.deals.kanban.footer.count',\n 'Showing {visible} of {total} deals in {pipeline} pipeline',\n {\n visible: deals.length,\n total,\n pipeline: activePipeline?.name ?? '\u2014',\n },\n )\n\n return (\n <Page>\n <PageBody>\n <ViewTabsRow active=\"kanban\" className=\"mb-4\" />\n <div className=\"flex flex-col gap-2\">\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink asChild>\n <Link href=\"/backend\">\n {translateWithFallback(t, 'customers.deals.kanban.breadcrumb.dashboard', 'Dashboard')}\n </Link>\n </BreadcrumbLink>\n </BreadcrumbItem>\n <BreadcrumbSeparator />\n <BreadcrumbItem>\n <BreadcrumbPage>\n {translateWithFallback(t, 'customers.deals.kanban.breadcrumb.deals', 'Deals')}\n </BreadcrumbPage>\n </BreadcrumbItem>\n </BreadcrumbList>\n </Breadcrumb>\n\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex flex-col gap-1\">\n {/*\n Heading typography mirrors DS PageHeader (text-xl sm:text-2xl font-semibold\n leading-tight) so the kanban title matches /backend/customers/people and\n other backoffice pages. We don't use the PageHeader component itself because\n it can't host the rich board-summary content (LaneCurrencyBreakdown popover)\n that lives directly under the title.\n */}\n <h1 className=\"text-xl font-semibold leading-tight text-foreground sm:text-2xl\">\n {translateWithFallback(t, 'customers.deals.kanban.pageTitle', 'Deals')}\n </h1>\n {boardSummary && boardSummary.rows.length > 0 ? (\n /*\n * Pipeline-wide value summary. The headline number is the converted total in\n * the tenant's base currency, paired with the deal count for context. When at\n * least one currency lacks an FX rate, we show a `~` prefix and a \"partial\"\n * caveat \u2014 the `LaneCurrencyBreakdown` popover (anchored on the `+N` chip)\n * surfaces the full breakdown and missing-rate disclosure. Clicking the\n * popover trigger here gives the operator the same board-level detail view.\n */\n <div className=\"flex items-center gap-2 text-sm text-muted-foreground\">\n <span>\n {translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.count',\n '{count} deals',\n { count: total },\n )}\n </span>\n {boardSummary.baseCurrencyCode && boardSummary.totalInBaseCurrency > 0 ? (\n <>\n <span aria-hidden=\"true\">\u00B7</span>\n <span className=\"flex items-baseline gap-1 font-semibold text-foreground\">\n {boardSummary.convertedAll ? null : (\n <span className=\"text-muted-foreground\" aria-hidden=\"true\">\n ~\n </span>\n )}\n <span>\n {new Intl.NumberFormat(undefined, {\n style: 'decimal',\n maximumFractionDigits: 0,\n }).format(boardSummary.totalInBaseCurrency)}\n </span>\n <span className=\"text-xs font-medium text-muted-foreground\">\n {boardSummary.baseCurrencyCode}\n </span>\n </span>\n {!boardSummary.convertedAll ? (\n <span\n className=\"text-overline uppercase tracking-wide text-status-warning-text\"\n title={translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.partialHint',\n 'Missing FX rates for {currencies} \u2014 excluded from total',\n { currencies: boardSummary.missingRateCurrencies.join(', ') },\n )}\n >\n {translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.partial',\n 'partial',\n )}\n </span>\n ) : null}\n </>\n ) : null}\n {boardSummary.rows.length > 1 ? (\n <LaneCurrencyBreakdown\n rows={boardSummary.rows}\n baseCurrencyCode={boardSummary.baseCurrencyCode}\n totalInBaseCurrency={boardSummary.totalInBaseCurrency}\n convertedAll={boardSummary.convertedAll}\n missingRateCurrencies={boardSummary.missingRateCurrencies}\n headingLabel={translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.headingLabel',\n 'PIPELINE',\n )}\n headingCount={total}\n triggerLabel={translateWithFallback(\n t,\n 'customers.deals.kanban.boardSummary.breakdownTrigger',\n 'Breakdown',\n )}\n triggerClassName=\"inline-flex items-center rounded-md border border-border bg-card px-2 py-0.5 text-overline font-medium uppercase tracking-wide text-muted-foreground transition-colors hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n />\n ) : null}\n </div>\n ) : null}\n </div>\n <div className=\"flex flex-wrap items-center gap-2.5\">\n <SearchInput\n value={search}\n onChange={setSearch}\n placeholder={translateWithFallback(\n t,\n 'customers.deals.kanban.search.placeholder',\n 'Search deals\u2026',\n )}\n className=\"w-64\"\n />\n <Button variant=\"outline\" type=\"button\" onClick={handleCustomizeView}>\n <SlidersHorizontal className=\"size-4\" aria-hidden=\"true\" />\n {translateWithFallback(t, 'customers.deals.kanban.cta.customize', 'Customize view')}\n </Button>\n <Button\n variant=\"outline\"\n type=\"button\"\n onClick={handleAddStage}\n disabled={!selectedPipelineId}\n >\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {translateWithFallback(t, 'customers.deals.kanban.cta.newStage', 'New stage')}\n </Button>\n <Button asChild>\n <Link href=\"/backend/customers/deals/create?returnTo=/backend/customers/deals/pipeline\">\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n {translateWithFallback(t, 'customers.deals.kanban.cta.newDeal', 'New deal')}\n </Link>\n </Button>\n </div>\n </div>\n\n {pipelinesQuery.data && pipelinesQuery.data.length > 1 ? (\n <div className=\"flex items-center gap-2 pb-1 text-sm\">\n <span className=\"text-muted-foreground\">\n {translateWithFallback(t, 'customers.deals.pipeline.switch.label', 'Pipeline')}\n </span>\n <Select\n value={selectedPipelineId ?? undefined}\n onValueChange={(value) => setSelectedPipelineId(value || null)}\n >\n <SelectTrigger className=\"w-auto min-w-48\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n {pipelinesQuery.data.map((pipeline) => (\n <SelectItem key={pipeline.id} value={pipeline.id}>\n {pipeline.name}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n ) : null}\n </div>\n\n <FilterBarRow\n leadingChips={leadingChipsNode}\n chips={filterChips}\n sortNode={sortNode}\n onChipClick={handleChipClick}\n />\n\n {!selectedPipelineId ? (\n <EmptyState\n icon={<Workflow className=\"size-8\" aria-hidden=\"true\" />}\n title={translateWithFallback(\n t,\n 'customers.deals.pipeline.noPipeline',\n 'No pipeline selected. Create a pipeline in settings.',\n )}\n className=\"h-[50vh] w-full\"\n />\n ) : isInitialLoading ? (\n <div className=\"flex h-[50vh] items-center justify-center\">\n <Spinner />\n </div>\n ) : firstError ? (\n <div className=\"max-w-xl\">\n <ErrorNotice\n message={\n firstError instanceof Error\n ? firstError.message\n : translateWithFallback(\n t,\n 'customers.deals.pipeline.loadError',\n 'Failed to load deals.',\n )\n }\n />\n </div>\n ) : (\n <DndContext\n sensors={sensors}\n collisionDetection={collisionDetection}\n measuring={measuringConfig}\n onDragStart={handleDragStart}\n onDragEnd={handleDragEnd}\n onDragCancel={handleDragCancel}\n >\n {/* Three-column flex: [36px gutter] [scroller flex-1] [36px gutter]\n Each gutter sized exactly to the 36px arrow button \u2014 no extra padding/gap so the\n scroller keeps as much horizontal room as possible (= more lanes fit). The buttons\n are absolute inside the gutter so they never overlap card content. */}\n <div className=\"flex items-stretch\">\n {/*\n Lane rail reads as an elevated tab over the kanban scroller (round-2 UX item 31,\n revised): `bg-card shadow-lg` gives it a real surface and a soft drop-shadow;\n `z-10` keeps it above the scroller. `mr-2` adds an 8px gap between the rail and\n the first lane so card edges don't touch the rail (UX-designer round-3 feedback \u2014\n the prior `-mr-2` tuck-under overlap was reverted; a small positive gap reads\n cleaner than the overlap).\n\n `shadow-lg` glows in all four directions by default, but the LEFT side of the\n rail sits flush with the page chrome \u2014 without clipping, that shadow half spills\n onto the page background and reads as a halo (UX-designer feedback from Zielivia).\n `clip-path:inset(-40px -40px -40px 0)` extends the visible region 40px beyond\n top/right/bottom (so the soft drop-shadow can render in full on those three sides)\n and clips flush at the LEFT edge (the page-chrome side). Right rail mirrors with\n `inset(-40px 0 -40px -40px)`.\n */}\n <div className=\"relative z-10 flex w-11 mr-2 shrink-0 items-center justify-center bg-card shadow-lg [clip-path:inset(-40px_-40px_-40px_0)]\">\n <IconButton\n variant=\"outline\"\n size=\"lg\"\n fullRadius\n onClick={handleScrollPrev}\n onPointerDown={handleHoldPrevStart}\n onPointerUp={stopContinuousScroll}\n onPointerLeave={stopContinuousScroll}\n onPointerCancel={stopContinuousScroll}\n aria-label={translateWithFallback(t, 'customers.deals.kanban.board.scrollPrev', 'Scroll to previous stage')}\n aria-disabled={scrollEdges.atStart}\n className={`absolute left-0 top-28 flex size-9 items-center justify-center rounded-full border border-border bg-card text-foreground shadow-lg transition-opacity hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n scrollEdges.atStart ? 'opacity-30' : ''\n }`}\n >\n <ChevronLeft className=\"size-5\" aria-hidden=\"true\" />\n </IconButton>\n </div>\n <div\n ref={setBoardScroller}\n data-kanban-scroller\n className={`flex min-w-0 flex-1 gap-3.5 overflow-x-auto pb-6 ${\n activeDragDealId ? 'cursor-grabbing select-none' : ''\n }`}\n >\n {lanes.length === 0 ? (\n <EmptyState\n icon={<Layers className=\"size-8\" aria-hidden=\"true\" />}\n title={translateWithFallback(\n t,\n 'customers.deals.pipeline.noStages',\n 'Define pipeline stages to start tracking deals.',\n )}\n className=\"h-[50vh] w-full\"\n />\n ) : (\n <>\n {lanes.map((stage) => {\n const laneDeals = sortedDealsByStage.get(stage.id) ?? EMPTY_DEAL_ARRAY\n return (\n <Lane\n key={stage.id}\n stage={stage}\n deals={laneDeals}\n aggregate={aggregateByStage.get(stage.id) ?? null}\n selectedDealIds={selectedDealIds}\n buildMenuItems={buildMenuItems}\n activeDragDealId={activeDragDealId}\n isLoadingMore={!!loadingMoreByStage[stage.id]}\n width={laneWidths[stage.id] ?? null}\n onToggleSelect={handleToggleSelect}\n onComposeActivity={handleComposeActivity}\n onOpenDetail={handleOpenDetail}\n onQuickAddClick={handleQuickAdd}\n onLoadMore={handleLoadMoreInLane}\n onResize={handleLaneResize}\n onResetWidth={handleResetLaneWidth}\n />\n )\n })}\n {/*\n Trailing \"Add stage\" tile (round-2 UX review item 30, revised). Coexists\n with the page-header toolbar button by explicit design: the toolbar button\n is the always-visible primary CTA; this tile is the in-context affordance\n at the end of the stage row that operators have used to map onto from\n Trello/Asana muscle memory.\n */}\n <AddStageLane onClick={handleAddStage} />\n </>\n )}\n </div>\n {/* Mirror of the left rail; `ml-3.5` adds a 14px gap between the last lane and the\n right rail. The right gap is intentionally wider than the left (`mr-2` = 8px)\n because the trailing tile here is `AddStageLane` \u2014 its dashed border reads more\n subtly than a regular lane's saturated color bar, so the same 8px on the right\n perceptually feels like a \"tucked under\" overlap (round-3 UX-designer feedback).\n `ml-3.5` matches the scroller's inter-lane `gap-3.5` so the visual rhythm stays\n consistent. `clip-path:inset(-40px 0 -40px -40px)` is the right-rail mirror of the\n left rail's clip \u2014 keeps the shadow on top/left/bottom (40px extend) and clips\n flush at the RIGHT edge so it doesn't spill onto the page background past the\n page chrome. */}\n <div className=\"relative z-10 flex w-11 ml-3.5 shrink-0 items-center justify-center bg-card shadow-lg [clip-path:inset(-40px_0_-40px_-40px)]\">\n <IconButton\n variant=\"outline\"\n size=\"lg\"\n fullRadius\n onClick={handleScrollNext}\n onPointerDown={handleHoldNextStart}\n onPointerUp={stopContinuousScroll}\n onPointerLeave={stopContinuousScroll}\n onPointerCancel={stopContinuousScroll}\n aria-label={translateWithFallback(t, 'customers.deals.kanban.board.scrollNext', 'Scroll to next stage')}\n aria-disabled={scrollEdges.atEnd}\n className={`absolute right-0 top-28 flex size-9 items-center justify-center rounded-full border border-border bg-card text-foreground shadow-lg transition-opacity hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 ${\n scrollEdges.atEnd ? 'opacity-30' : ''\n }`}\n >\n <ChevronRight className=\"size-5\" aria-hidden=\"true\" />\n </IconButton>\n </div>\n </div>\n <DragOverlay dropAnimation={null}>\n {activeDragDeal ? (\n <div className=\"relative\">\n <div className={`pointer-events-none ${LANE_WIDTH_CLASS} rotate-2 cursor-grabbing select-none rounded-lg border border-border bg-card px-4 py-3.5 shadow-xl ring-2 ring-accent-indigo/40`}>\n <div className=\"flex flex-col gap-2\">\n <h3 className=\"line-clamp-2 text-base font-semibold leading-normal text-foreground\">\n {activeDragDeal.title}\n </h3>\n {typeof activeDragDeal.valueAmount === 'number' ? (\n <div className=\"flex items-baseline gap-1.5\">\n <span className=\"text-lg font-bold leading-normal text-foreground\">\n {new Intl.NumberFormat(undefined, {\n style: 'decimal',\n maximumFractionDigits: 0,\n useGrouping: true,\n }).format(activeDragDeal.valueAmount)}\n </span>\n {activeDragDeal.valueCurrency ? (\n <span className=\"text-sm font-semibold leading-normal text-muted-foreground\">\n {activeDragDeal.valueCurrency.toUpperCase()}\n </span>\n ) : null}\n </div>\n ) : null}\n {activeDragDeal.primaryCompany ? (\n <span className=\"inline-flex w-fit 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\">\n <span className=\"truncate\">{activeDragDeal.primaryCompany.label}</span>\n </span>\n ) : null}\n </div>\n </div>\n {bulkDragActive ? (\n // +N badge tells the operator how many cards are travelling with the dragged\n // one \u2014 round-2 UX review item 32. N excludes the dragged card itself, so a\n // selection of 5 shows \"+4\" floating above the dragged surface.\n <span\n aria-label={translateWithFallback(\n t,\n 'customers.deals.kanban.bulk.dragOverlayBadge',\n '{count} more selected',\n { count: selectedDealIds.size - 1 },\n )}\n className=\"pointer-events-none absolute -right-3 -top-3 inline-flex h-7 min-w-7 rotate-2 items-center justify-center rounded-full border-2 border-card bg-foreground px-2 text-xs font-bold tabular-nums text-background shadow-lg\"\n >\n +{selectedDealIds.size - 1}\n </span>\n ) : null}\n </div>\n ) : null}\n </DragOverlay>\n </DndContext>\n )}\n\n {selectedPipelineId && !isInitialLoading ? (\n <div className=\"flex flex-col gap-1 border-t border-border pt-3 text-xs text-muted-foreground sm:flex-row sm:items-center sm:justify-between\">\n <span>{footerCount}</span>\n <span>{dragHint}</span>\n {pendingDealId ? <Spinner className=\"size-3\" /> : null}\n </div>\n ) : null}\n </PageBody>\n\n <QuickDealDialog\n open={!!quickDealContext}\n context={quickDealContext}\n onClose={() => setQuickDealContext(null)}\n onCreated={handleDialogCreated}\n currentUserId={currentUserId || undefined}\n currentUserLabel={currentUserLabel}\n companies={companiesQuery.data ?? []}\n currencies={\n currencyDictionary.data\n ? currencyDictionary.data.entries.map((entry) => ({\n code: entry.value,\n label: entry.label,\n isBase: boardSummary?.baseCurrencyCode\n ? entry.value === boardSummary.baseCurrencyCode\n : false,\n }))\n : undefined\n }\n />\n <AddStageDialog\n open={!!addStageContext}\n context={addStageContext}\n onClose={() => setAddStageContext(null)}\n onCreated={handleDialogCreated}\n />\n <CustomizeViewDialog\n open={customizeOpen}\n resizedLanesCount={Object.keys(laneWidths).length}\n onClose={() => setCustomizeOpen(false)}\n onResetToDefault={handleResetView}\n onResetColumnWidths={handleResetAllLaneWidths}\n />\n <ActivityComposerDialog\n open={!!activityContext}\n context={activityContext}\n onClose={() => setActivityContext(null)}\n onCreated={handleDialogCreated}\n />\n <ChangeStageDialog\n open={changeStageOpen}\n selectedCount={bulkSelectionSummary.count}\n pipelineName={activePipelineName}\n stages={stageOptions}\n isSubmitting={isBulkMutating}\n onClose={() => setChangeStageOpen(false)}\n onConfirm={(stageId) => void handleBulkChangeStage(stageId)}\n />\n <ChangeStageDialog\n open={!!singleMoveStageContext}\n selectedCount={1}\n pipelineName={activePipelineName}\n stages={stageOptions}\n isSubmitting={false}\n onClose={() => setSingleMoveStageContext(null)}\n onConfirm={(stageId) => {\n if (singleMoveStageContext) {\n moveDealToStage(singleMoveStageContext.dealId, stageId)\n }\n setSingleMoveStageContext(null)\n }}\n />\n <ChangeOwnerDialog\n open={changeOwnerOpen}\n selectedCount={bulkSelectionSummary.count}\n isSubmitting={isBulkMutating}\n onClose={() => setChangeOwnerOpen(false)}\n onConfirm={(userId) => void handleBulkChangeOwner(userId)}\n />\n <BulkActionsBar\n count={bulkSelectionSummary.count}\n totalLabel={bulkSelectionSummary.totalLabel}\n onChangeStage={() => setChangeStageOpen(true)}\n onChangeOwner={() => setChangeOwnerOpen(true)}\n onExportCsv={handleBulkExport}\n onDelete={() => void handleBulkDelete()}\n onClear={handleBulkClear}\n />\n {ConfirmDialogElement}\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["dealsByStage", "stageIdByDealId", "total", "t"]
7
7
  }