@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
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/customers/api/deals/summary/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager as CoreEntityManager } from '@mikro-orm/core'\nimport type { EntityManager as PgEntityManager } from '@mikro-orm/postgresql'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { ExchangeRateService, RateResult } from '@open-mercato/core/modules/currencies/services/exchangeRateService'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { fetchStuckDealIds } from '../../../lib/stuckDeals'\nimport {\n computeDelta,\n convertSumsToBase,\n getPreviousQuarterWindow,\n getQuarterWindow,\n getTrailingMonths,\n type CurrencySum,\n type Delta,\n} from '../../../lib/dealsMetrics'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.deals.view'] },\n}\n\nconst OPEN_STATUSES = ['open', 'in_progress'] as const\nconst TRAILING_MONTHS = 6\nconst TOP_OWNERS = 5\n\nconst deltaSchema = z.object({\n value: z.number(),\n direction: z.enum(['up', 'down', 'unchanged']),\n})\n\nconst stageBreakdownSchema = z.object({\n stage: z.string().nullable(),\n count: z.number(),\n value: z.number(),\n})\n\nconst ownerCountSchema = z.object({\n id: z.string(),\n count: z.number(),\n})\n\nconst winRatePointSchema = z.object({\n period: z.string(),\n rate: z.number(),\n})\n\nconst summaryResponseSchema = z.object({\n baseCurrencyCode: z.string().nullable(),\n convertedAll: z.boolean(),\n missingRateCurrencies: z.array(z.string()),\n pipelineValue: z.object({\n value: z.number(),\n delta: deltaSchema,\n stages: z.array(stageBreakdownSchema),\n }),\n activeDeals: z.object({\n value: z.number(),\n delta: deltaSchema,\n ownersCount: z.number(),\n needAttention: z.number(),\n owners: z.array(ownerCountSchema),\n ownersOverflow: z.number(),\n }),\n wonThisQuarter: z.object({\n value: z.number(),\n delta: deltaSchema,\n dealsClosed: z.number(),\n avgDeal: z.number(),\n }),\n winRate: z.object({\n value: z.number(),\n deltaPp: z.number(),\n direction: z.enum(['up', 'down', 'unchanged']),\n previousValue: z.number(),\n series: z.array(winRatePointSchema),\n }),\n})\n\nexport type DealsSummaryResponse = z.infer<typeof summaryResponseSchema>\n\nconst summaryErrorSchema = z.object({\n error: z.string(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Customers',\n summary: 'Deals KPI summary',\n methods: {\n GET: {\n summary: 'Pipeline KPI metrics with period-over-period deltas for the deals list',\n description:\n 'Returns the four list-level KPI cards (pipeline value, active deals, won this quarter, win rate) with quarter-over-quarter deltas, per-stage open-pipeline breakdown, top owners, and a 6-month win-rate series. Values are converted to the tenant base currency where rates are available; partial conversions are disclosed via convertedAll/missingRateCurrencies.',\n responses: [\n { status: 200, description: 'Deals KPI summary payload', schema: summaryResponseSchema },\n ],\n errors: [\n { status: 401, description: 'Unauthorized', schema: summaryErrorSchema },\n ],\n },\n },\n}\n\ntype OpenPipelineRow = {\n stage: string | null\n currency: string | null\n total: string | number | null\n count: string | number\n owner_user_id: string | null\n}\n\ntype WindowSumRow = {\n currency: string | null\n current_total: string | number | null\n current_count: string | number\n previous_total: string | number | null\n previous_count: string | number\n}\n\ntype WinLossRow = {\n current_won: string | number\n current_lost: string | number\n previous_won: string | number\n previous_lost: string | number\n}\n\ntype WinRateMonthRow = {\n period: string\n won: string | number\n lost: string | number\n}\n\ntype OwnerCountRow = {\n owner_user_id: string | null\n count: string | number\n}\n\nfunction toNumber(value: string | number | null | undefined): number {\n const parsed = Number(value ?? 0)\n return Number.isFinite(parsed) ? parsed : 0\n}\n\nfunction winRate(won: number, lost: number): number {\n const denom = won + lost\n if (denom <= 0) return 0\n return Math.round((100 * won) / denom)\n}\n\nfunction sumsByCurrency(entries: Array<{ currency: string | null; total: number }>): CurrencySum[] {\n const byCurrency = new Map<string, number>()\n for (const entry of entries) {\n const currency = (entry.currency ?? '').toString().trim().toUpperCase()\n if (!currency) continue\n byCurrency.set(currency, (byCurrency.get(currency) ?? 0) + entry.total)\n }\n return Array.from(byCurrency.entries()).map(([currency, total]) => ({ currency, total }))\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth?.tenantId || !auth.orgId) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const container = await createRequestContainer()\n const em = container.resolve<CoreEntityManager>('em')\n\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const effectiveTenantId = scope.tenantId ?? auth.tenantId\n const orgFilterIds = Array.isArray(scope.filterIds) && scope.filterIds.length > 0\n ? scope.filterIds.filter((id) => typeof id === 'string' && id.length > 0)\n : auth.orgId\n ? [auth.orgId]\n : []\n if (!effectiveTenantId || orgFilterIds.length === 0) {\n return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n }\n\n const today = new Date()\n const currentQuarter = getQuarterWindow(today)\n const previousQuarter = getPreviousQuarterWindow(today)\n const trailingMonths = getTrailingMonths(today, TRAILING_MONTHS)\n const seriesStart = trailingMonths[0]?.start ?? currentQuarter.start\n\n const connection = em.getConnection()\n\n const baseCurrency: Array<{ code: string }> = await connection.execute<Array<{ code: string }>>(\n `SELECT code FROM currencies WHERE tenant_id = ? AND organization_id = ? AND is_base = true AND deleted_at IS NULL LIMIT 1`,\n [effectiveTenantId, orgFilterIds[0]],\n )\n const baseCurrencyCode = baseCurrency[0]?.code ?? null\n\n const orgPlaceholders = orgFilterIds.map(() => '?').join(',')\n const scopeWhere = `tenant_id = ? AND organization_id IN (${orgPlaceholders}) AND deleted_at IS NULL`\n const scopeValues: Array<string | number | null> = [effectiveTenantId, ...orgFilterIds]\n const openPlaceholders = OPEN_STATUSES.map(() => '?').join(',')\n\n // 1) Open pipeline: per (stage, currency) sums + open-deal owner per row, so we can\n // derive pipeline value (per stage + converted total) and the open owner set in one pass.\n const openRows: OpenPipelineRow[] = await connection.execute<OpenPipelineRow[]>(\n `SELECT\n pipeline_stage AS stage,\n UPPER(COALESCE(value_currency, '')) AS currency,\n COALESCE(SUM(value_amount), 0) AS total,\n COUNT(*) AS count,\n owner_user_id\n FROM customer_deals\n WHERE ${scopeWhere} AND status IN (${openPlaceholders})\n GROUP BY pipeline_stage, UPPER(COALESCE(value_currency, '')), owner_user_id`,\n [...scopeValues, ...OPEN_STATUSES],\n )\n\n // 2) Open-deal value created in the current vs previous quarter (pipeline inflow delta).\n const inflowRows: WindowSumRow[] = await connection.execute<WindowSumRow[]>(\n `SELECT\n UPPER(COALESCE(value_currency, '')) AS currency,\n COALESCE(SUM(value_amount) FILTER (WHERE created_at >= ? AND created_at < ?), 0) AS current_total,\n COUNT(*) FILTER (WHERE created_at >= ? AND created_at < ?) AS current_count,\n COALESCE(SUM(value_amount) FILTER (WHERE created_at >= ? AND created_at < ?), 0) AS previous_total,\n COUNT(*) FILTER (WHERE created_at >= ? AND created_at < ?) AS previous_count\n FROM customer_deals\n WHERE ${scopeWhere} AND status IN (${openPlaceholders})\n GROUP BY UPPER(COALESCE(value_currency, ''))`,\n [\n currentQuarter.start.toISOString(), currentQuarter.end.toISOString(),\n currentQuarter.start.toISOString(), currentQuarter.end.toISOString(),\n previousQuarter.start.toISOString(), previousQuarter.end.toISOString(),\n previousQuarter.start.toISOString(), previousQuarter.end.toISOString(),\n ...scopeValues, ...OPEN_STATUSES,\n ],\n )\n\n // 3) Won value per currency for the current vs previous quarter (updated_at in window).\n const wonRows: WindowSumRow[] = await connection.execute<WindowSumRow[]>(\n `SELECT\n UPPER(COALESCE(value_currency, '')) AS currency,\n COALESCE(SUM(value_amount) FILTER (WHERE updated_at >= ? AND updated_at < ?), 0) AS current_total,\n COUNT(*) FILTER (WHERE updated_at >= ? AND updated_at < ?) AS current_count,\n COALESCE(SUM(value_amount) FILTER (WHERE updated_at >= ? AND updated_at < ?), 0) AS previous_total,\n COUNT(*) FILTER (WHERE updated_at >= ? AND updated_at < ?) AS previous_count\n FROM customer_deals\n WHERE ${scopeWhere} AND (status = 'win' OR closure_outcome = 'won')\n GROUP BY UPPER(COALESCE(value_currency, ''))`,\n [\n currentQuarter.start.toISOString(), currentQuarter.end.toISOString(),\n currentQuarter.start.toISOString(), currentQuarter.end.toISOString(),\n previousQuarter.start.toISOString(), previousQuarter.end.toISOString(),\n previousQuarter.start.toISOString(), previousQuarter.end.toISOString(),\n ...scopeValues,\n ],\n )\n\n // 4) Win/lost counts for the current vs previous quarter (win rate + delta-pp).\n const winLossRows: WinLossRow[] = await connection.execute<WinLossRow[]>(\n `SELECT\n COUNT(*) FILTER (WHERE (status = 'win' OR closure_outcome = 'won') AND updated_at >= ? AND updated_at < ?) AS current_won,\n COUNT(*) FILTER (WHERE (status = 'loose' OR closure_outcome = 'lost') AND updated_at >= ? AND updated_at < ?) AS current_lost,\n COUNT(*) FILTER (WHERE (status = 'win' OR closure_outcome = 'won') AND updated_at >= ? AND updated_at < ?) AS previous_won,\n COUNT(*) FILTER (WHERE (status = 'loose' OR closure_outcome = 'lost') AND updated_at >= ? AND updated_at < ?) AS previous_lost\n FROM customer_deals\n WHERE ${scopeWhere}`,\n [\n currentQuarter.start.toISOString(), currentQuarter.end.toISOString(),\n currentQuarter.start.toISOString(), currentQuarter.end.toISOString(),\n previousQuarter.start.toISOString(), previousQuarter.end.toISOString(),\n previousQuarter.start.toISOString(), previousQuarter.end.toISOString(),\n ...scopeValues,\n ],\n )\n\n // 5) Win-rate series over the trailing months (won/lost grouped by updated_at month).\n const seriesRows: WinRateMonthRow[] = await connection.execute<WinRateMonthRow[]>(\n `SELECT\n to_char(date_trunc('month', updated_at AT TIME ZONE 'UTC'), 'YYYY-MM') AS period,\n COUNT(*) FILTER (WHERE status = 'win' OR closure_outcome = 'won') AS won,\n COUNT(*) FILTER (WHERE status = 'loose' OR closure_outcome = 'lost') AS lost\n FROM customer_deals\n WHERE ${scopeWhere} AND updated_at >= ?\n GROUP BY 1`,\n [...scopeValues, seriesStart.toISOString()],\n )\n\n // Overdue open deals (id set) + stuck deals (id set) \u2192 union count for \"need attention\".\n const overdueRows: Array<{ id: string }> = await connection.execute<Array<{ id: string }>>(\n `SELECT id FROM customer_deals\n WHERE ${scopeWhere} AND status = 'open' AND expected_close_at IS NOT NULL AND expected_close_at < CURRENT_DATE`,\n [...scopeValues],\n )\n // `fetchStuckDealIds` is single-org; run it for every org in scope so multi-org callers don't\n // undercount stuck deals (the aggregates above already span every org in `orgFilterIds`).\n const stuckIdLists = await Promise.all(\n orgFilterIds.map((orgId) =>\n fetchStuckDealIds(em as unknown as PgEntityManager, orgId, effectiveTenantId)),\n )\n const stuckIdSet = new Set<string>()\n for (const list of stuckIdLists) for (const id of list) stuckIdSet.add(id)\n\n // The stuck-deal query does not filter status, so a stuck id can be a won/lost/closed deal.\n // \"Need attention\" is an active-deal metric \u2014 intersect with the open (OPEN_STATUSES) set so\n // terminal deals never inflate the count.\n let openStuckIds: string[] = []\n if (stuckIdSet.size > 0) {\n const stuckIdValues = Array.from(stuckIdSet)\n const stuckPlaceholders = stuckIdValues.map(() => '?').join(',')\n const openStuckRows: Array<{ id: string }> = await connection.execute<Array<{ id: string }>>(\n `SELECT id FROM customer_deals\n WHERE ${scopeWhere} AND status IN (${openPlaceholders}) AND id IN (${stuckPlaceholders})`,\n [...scopeValues, ...OPEN_STATUSES, ...stuckIdValues],\n )\n openStuckIds = openStuckRows.map((row) => row.id)\n }\n\n const attentionIds = new Set<string>()\n for (const row of overdueRows) attentionIds.add(row.id)\n for (const id of openStuckIds) attentionIds.add(id)\n\n // Reduce open rows: per-stage sums, distinct owners, owner counts, and a flat\n // per-currency list for the converted pipeline total.\n const stageMap = new Map<string, { stage: string | null; count: number; byCurrency: CurrencySum[] }>()\n const openOwnerCounts = new Map<string, number>()\n const openSums: Array<{ currency: string | null; total: number }> = []\n for (const row of openRows) {\n const stageKey = row.stage ?? '__null__'\n const total = toNumber(row.total)\n const count = toNumber(row.count)\n const currency = (row.currency ?? '').toString().trim().toUpperCase()\n if (!stageMap.has(stageKey)) {\n stageMap.set(stageKey, { stage: row.stage ?? null, count: 0, byCurrency: [] })\n }\n const stageAgg = stageMap.get(stageKey)!\n stageAgg.count += count\n if (currency) stageAgg.byCurrency.push({ currency, total })\n openSums.push({ currency, total })\n if (row.owner_user_id) {\n openOwnerCounts.set(row.owner_user_id, (openOwnerCounts.get(row.owner_user_id) ?? 0) + count)\n }\n }\n\n // Collect every distinct non-base currency across all metrics and fetch rates ONCE.\n const distinctCurrencies = new Set<string>()\n const collect = (entries: Array<{ currency: string | null }>) => {\n for (const entry of entries) {\n const currency = (entry.currency ?? '').toString().trim().toUpperCase()\n if (currency && currency !== baseCurrencyCode) distinctCurrencies.add(currency)\n }\n }\n collect(openSums)\n collect(inflowRows)\n collect(wonRows)\n\n let rates = new Map<string, RateResult>()\n if (baseCurrencyCode && distinctCurrencies.size > 0) {\n const exchange = container.resolve('exchangeRateService') as ExchangeRateService | undefined\n if (exchange) {\n const pairs = Array.from(distinctCurrencies).map((code) => ({\n fromCurrencyCode: code,\n toCurrencyCode: baseCurrencyCode,\n }))\n try {\n rates = await exchange.getRates({\n pairs,\n date: today,\n scope: { tenantId: effectiveTenantId, organizationId: orgFilterIds[0] },\n options: { maxDaysBack: 60, autoFetch: false },\n })\n } catch (err) {\n console.warn('[customers.deals.summary] exchange-rate lookup failed; falling back to per-currency totals', err)\n }\n }\n }\n\n const missingRateCurrencies = new Set<string>()\n const trackMissing = (missing: string[]) => {\n for (const code of missing) missingRateCurrencies.add(code)\n }\n let convertedAll = true\n\n // Degraded path: when there is no base currency, fall back to the dominant currency's\n // raw sum so the cards still show a number (mirrors the aggregate route's disclosure).\n const dominantCurrencyTotal = (entries: Array<{ currency: string | null; total: number }>): number => {\n const byCurrency = new Map<string, number>()\n for (const entry of entries) {\n const currency = (entry.currency ?? '').toString().trim().toUpperCase()\n if (!currency) continue\n byCurrency.set(currency, (byCurrency.get(currency) ?? 0) + entry.total)\n }\n let best = 0\n for (const total of byCurrency.values()) {\n if (Math.abs(total) > Math.abs(best)) best = total\n }\n return Math.round(best)\n }\n\n const convert = (entries: Array<{ currency: string | null; total: number }>): number => {\n if (!baseCurrencyCode) {\n convertedAll = false\n trackMissing(sumsByCurrency(entries).map((entry) => entry.currency))\n return dominantCurrencyTotal(entries)\n }\n const result = convertSumsToBase(sumsByCurrency(entries), baseCurrencyCode, rates)\n if (!result.convertedAll) convertedAll = false\n trackMissing(result.missingRateCurrencies)\n return result.total\n }\n\n // Pipeline value (open deals, converted) + per-stage converted breakdown.\n const pipelineValueTotal = convert(openSums)\n const stages = Array.from(stageMap.values()).map((stageAgg) => ({\n stage: stageAgg.stage,\n count: stageAgg.count,\n value: convert(stageAgg.byCurrency),\n }))\n\n // Pipeline inflow delta (open value created this vs previous quarter).\n const inflowCurrent = convert(inflowRows.map((row) => ({ currency: row.currency, total: toNumber(row.current_total) })))\n const inflowPrevious = convert(inflowRows.map((row) => ({ currency: row.currency, total: toNumber(row.previous_total) })))\n const pipelineDelta: Delta = computeDelta(inflowCurrent, inflowPrevious)\n\n // Active deals: count of open deals, owners, need-attention, top owners.\n const activeDealsCount = openRows.reduce((sum, row) => sum + toNumber(row.count), 0)\n const ownersCount = openOwnerCounts.size\n const inflowCurrentCount = inflowRows.reduce((sum, row) => sum + toNumber(row.current_count), 0)\n const inflowPreviousCount = inflowRows.reduce((sum, row) => sum + toNumber(row.previous_count), 0)\n const activeDelta: Delta = computeDelta(inflowCurrentCount, inflowPreviousCount)\n const sortedOwners = Array.from(openOwnerCounts.entries())\n .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))\n const owners = sortedOwners.slice(0, TOP_OWNERS).map(([id, count]) => ({ id, count }))\n const ownersOverflow = Math.max(0, ownersCount - owners.length)\n\n // Won this quarter.\n const wonCurrent = convert(wonRows.map((row) => ({ currency: row.currency, total: toNumber(row.current_total) })))\n const wonPrevious = convert(wonRows.map((row) => ({ currency: row.currency, total: toNumber(row.previous_total) })))\n const dealsClosed = wonRows.reduce((sum, row) => sum + toNumber(row.current_count), 0)\n const wonDelta: Delta = computeDelta(wonCurrent, wonPrevious)\n const avgDeal = dealsClosed > 0 ? Math.round(wonCurrent / dealsClosed) : 0\n\n // Win rate (current + previous quarter) and pp delta.\n const winLoss = winLossRows[0]\n const currentWon = toNumber(winLoss?.current_won)\n const currentLost = toNumber(winLoss?.current_lost)\n const previousWon = toNumber(winLoss?.previous_won)\n const previousLost = toNumber(winLoss?.previous_lost)\n const winRateValue = winRate(currentWon, currentLost)\n const winRatePrevious = winRate(previousWon, previousLost)\n const deltaPp = winRateValue - winRatePrevious\n const winRateDirection = deltaPp > 0 ? 'up' : deltaPp < 0 ? 'down' : 'unchanged'\n\n // Win-rate series over trailing months (fill missing months with 0).\n const seriesByPeriod = new Map<string, { won: number; lost: number }>()\n for (const row of seriesRows) {\n seriesByPeriod.set(row.period, { won: toNumber(row.won), lost: toNumber(row.lost) })\n }\n const series = trailingMonths.map((month) => {\n const point = seriesByPeriod.get(month.label)\n const won = point?.won ?? 0\n const lost = point?.lost ?? 0\n const denom = won + lost\n return { period: month.label, rate: denom > 0 ? won / denom : 0 }\n })\n\n const response: DealsSummaryResponse = {\n baseCurrencyCode,\n convertedAll,\n missingRateCurrencies: Array.from(missingRateCurrencies),\n pipelineValue: {\n value: pipelineValueTotal,\n delta: pipelineDelta,\n stages,\n },\n activeDeals: {\n value: activeDealsCount,\n delta: activeDelta,\n ownersCount,\n needAttention: attentionIds.size,\n owners,\n ownersOverflow,\n },\n wonThisQuarter: {\n value: wonCurrent,\n delta: wonDelta,\n dealsClosed,\n avgDeal,\n },\n winRate: {\n value: winRateValue,\n deltaPp,\n direction: winRateDirection,\n previousValue: winRatePrevious,\n series,\n },\n }\n\n return NextResponse.json(response)\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,19 +1,30 @@
1
1
  import * as React from "react";
2
2
  import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
3
3
  import { useInteractionMutations } from "../../../../../components/detail/hooks/useInteractionMutations.js";
4
+ const plannedActivitiesCache = /* @__PURE__ */ new Map();
5
+ function fetchPlannedActivities(dealId, useCache) {
6
+ const url = `/api/customers/interactions?dealId=${encodeURIComponent(dealId)}&status=planned&excludeInteractionType=task&limit=100&sortField=scheduledAt&sortDir=asc`;
7
+ const cached = plannedActivitiesCache.get(url);
8
+ if (useCache && cached) return cached.promise;
9
+ const entry = {
10
+ promise: readApiResultOrThrow(url).then((result) => Array.isArray(result.items) ? result.items : [])
11
+ };
12
+ if (useCache) plannedActivitiesCache.set(url, entry);
13
+ return entry.promise.finally(() => {
14
+ if (plannedActivitiesCache.get(url) === entry) plannedActivitiesCache.delete(url);
15
+ });
16
+ }
4
17
  function useDealActivities({
5
18
  dealId,
6
19
  runMutationWithContext
7
20
  }) {
8
21
  const [plannedActivities, setPlannedActivities] = React.useState([]);
9
22
  const [activityRefreshKey, setActivityRefreshKey] = React.useState(0);
10
- const loadPlannedActivities = React.useCallback(async () => {
23
+ const loadPlannedActivities = React.useCallback(async (options = {}) => {
11
24
  if (!dealId) return;
12
25
  try {
13
- const result = await readApiResultOrThrow(
14
- `/api/customers/interactions?dealId=${encodeURIComponent(dealId)}&status=planned&excludeInteractionType=task&limit=100&sortField=scheduledAt&sortDir=asc`
15
- );
16
- setPlannedActivities(Array.isArray(result.items) ? result.items : []);
26
+ const items = await fetchPlannedActivities(dealId, options.cache === true);
27
+ setPlannedActivities(items);
17
28
  } catch (err) {
18
29
  console.warn("[customers.deals.detail] load planned activities failed", err);
19
30
  setPlannedActivities([]);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/%5Bid%5D/hooks/useDealActivities.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { InteractionSummary } from '../../../../../components/detail/types'\nimport { useInteractionMutations } from '../../../../../components/detail/hooks/useInteractionMutations'\nimport type { GuardedMutationRunner } from './types'\n\ntype UseDealActivitiesOptions = {\n dealId: string\n runMutationWithContext: GuardedMutationRunner\n}\n\ntype UseDealActivitiesResult = {\n plannedActivities: InteractionSummary[]\n activityRefreshKey: number\n loadPlannedActivities: () => Promise<void>\n handleActivityCreated: () => Promise<void>\n handleMarkDone: (interactionId: string) => Promise<void>\n handleCancelActivity: (interactionId: string) => Promise<void>\n}\n\nexport function useDealActivities({\n dealId,\n runMutationWithContext,\n}: UseDealActivitiesOptions): UseDealActivitiesResult {\n const [plannedActivities, setPlannedActivities] = React.useState<InteractionSummary[]>([])\n const [activityRefreshKey, setActivityRefreshKey] = React.useState(0)\n\n const loadPlannedActivities = React.useCallback(async () => {\n if (!dealId) return\n try {\n const result = await readApiResultOrThrow<{ items?: InteractionSummary[] }>(\n `/api/customers/interactions?dealId=${encodeURIComponent(dealId)}&status=planned&excludeInteractionType=task&limit=100&sortField=scheduledAt&sortDir=asc`,\n )\n setPlannedActivities(Array.isArray(result.items) ? result.items : [])\n } catch (err) {\n console.warn('[customers.deals.detail] load planned activities failed', err)\n setPlannedActivities([])\n }\n }, [dealId])\n\n const handleActivityCreated = React.useCallback(async () => {\n setActivityRefreshKey((value) => value + 1)\n await loadPlannedActivities()\n }, [loadPlannedActivities])\n\n const { completeInteraction, cancelInteraction } = useInteractionMutations({\n runMutationWithContext,\n onAfterChange: handleActivityCreated,\n logContext: 'customers.deals.detail',\n })\n\n return {\n plannedActivities,\n activityRefreshKey,\n loadPlannedActivities,\n handleActivityCreated,\n handleMarkDone: completeInteraction,\n handleCancelActivity: cancelInteraction,\n }\n}\n"],
5
- "mappings": "AAAA,YAAY,WAAW;AACvB,SAAS,4BAA4B;AAErC,SAAS,+BAA+B;AAiBjC,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AACF,GAAsD;AACpD,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAA+B,CAAC,CAAC;AACzF,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAS,CAAC;AAEpE,QAAM,wBAAwB,MAAM,YAAY,YAAY;AAC1D,QAAI,CAAC,OAAQ;AACb,QAAI;AACF,YAAM,SAAS,MAAM;AAAA,QACnB,sCAAsC,mBAAmB,MAAM,CAAC;AAAA,MAClE;AACA,2BAAqB,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAAQ,CAAC,CAAC;AAAA,IACtE,SAAS,KAAK;AACZ,cAAQ,KAAK,2DAA2D,GAAG;AAC3E,2BAAqB,CAAC,CAAC;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,wBAAwB,MAAM,YAAY,YAAY;AAC1D,0BAAsB,CAAC,UAAU,QAAQ,CAAC;AAC1C,UAAM,sBAAsB;AAAA,EAC9B,GAAG,CAAC,qBAAqB,CAAC;AAE1B,QAAM,EAAE,qBAAqB,kBAAkB,IAAI,wBAAwB;AAAA,IACzE;AAAA,IACA,eAAe;AAAA,IACf,YAAY;AAAA,EACd,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,sBAAsB;AAAA,EACxB;AACF;",
4
+ "sourcesContent": ["import * as React from 'react'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport type { InteractionSummary } from '../../../../../components/detail/types'\nimport { useInteractionMutations } from '../../../../../components/detail/hooks/useInteractionMutations'\nimport type { GuardedMutationRunner } from './types'\n\ntype LoadPlannedActivitiesOptions = {\n cache?: boolean\n}\n\ntype UseDealActivitiesOptions = {\n dealId: string\n runMutationWithContext: GuardedMutationRunner\n}\n\ntype UseDealActivitiesResult = {\n plannedActivities: InteractionSummary[]\n activityRefreshKey: number\n loadPlannedActivities: (options?: LoadPlannedActivitiesOptions) => Promise<void>\n handleActivityCreated: () => Promise<void>\n handleMarkDone: (interactionId: string) => Promise<void>\n handleCancelActivity: (interactionId: string) => Promise<void>\n}\n\ntype PlannedActivitiesCacheEntry = {\n promise: Promise<InteractionSummary[]>\n}\n\nconst plannedActivitiesCache = new Map<string, PlannedActivitiesCacheEntry>()\n\nfunction fetchPlannedActivities(dealId: string, useCache: boolean): Promise<InteractionSummary[]> {\n const url = `/api/customers/interactions?dealId=${encodeURIComponent(dealId)}&status=planned&excludeInteractionType=task&limit=100&sortField=scheduledAt&sortDir=asc`\n const cached = plannedActivitiesCache.get(url)\n if (useCache && cached) return cached.promise\n const entry: PlannedActivitiesCacheEntry = {\n promise: readApiResultOrThrow<{ items?: InteractionSummary[] }>(url)\n .then((result) => (Array.isArray(result.items) ? result.items : [])),\n }\n if (useCache) plannedActivitiesCache.set(url, entry)\n return entry.promise.finally(() => {\n if (plannedActivitiesCache.get(url) === entry) plannedActivitiesCache.delete(url)\n })\n}\n\nexport function useDealActivities({\n dealId,\n runMutationWithContext,\n}: UseDealActivitiesOptions): UseDealActivitiesResult {\n const [plannedActivities, setPlannedActivities] = React.useState<InteractionSummary[]>([])\n const [activityRefreshKey, setActivityRefreshKey] = React.useState(0)\n\n const loadPlannedActivities = React.useCallback(async (options: LoadPlannedActivitiesOptions = {}) => {\n if (!dealId) return\n try {\n const items = await fetchPlannedActivities(dealId, options.cache === true)\n setPlannedActivities(items)\n } catch (err) {\n console.warn('[customers.deals.detail] load planned activities failed', err)\n setPlannedActivities([])\n }\n }, [dealId])\n\n const handleActivityCreated = React.useCallback(async () => {\n setActivityRefreshKey((value) => value + 1)\n await loadPlannedActivities()\n }, [loadPlannedActivities])\n\n const { completeInteraction, cancelInteraction } = useInteractionMutations({\n runMutationWithContext,\n onAfterChange: handleActivityCreated,\n logContext: 'customers.deals.detail',\n })\n\n return {\n plannedActivities,\n activityRefreshKey,\n loadPlannedActivities,\n handleActivityCreated,\n handleMarkDone: completeInteraction,\n handleCancelActivity: cancelInteraction,\n }\n}\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AACvB,SAAS,4BAA4B;AAErC,SAAS,+BAA+B;AAyBxC,MAAM,yBAAyB,oBAAI,IAAyC;AAE5E,SAAS,uBAAuB,QAAgB,UAAkD;AAChG,QAAM,MAAM,sCAAsC,mBAAmB,MAAM,CAAC;AAC5E,QAAM,SAAS,uBAAuB,IAAI,GAAG;AAC7C,MAAI,YAAY,OAAQ,QAAO,OAAO;AACtC,QAAM,QAAqC;AAAA,IACzC,SAAS,qBAAuD,GAAG,EAChE,KAAK,CAAC,WAAY,MAAM,QAAQ,OAAO,KAAK,IAAI,OAAO,QAAQ,CAAC,CAAE;AAAA,EACvE;AACA,MAAI,SAAU,wBAAuB,IAAI,KAAK,KAAK;AACnD,SAAO,MAAM,QAAQ,QAAQ,MAAM;AACjC,QAAI,uBAAuB,IAAI,GAAG,MAAM,MAAO,wBAAuB,OAAO,GAAG;AAAA,EAClF,CAAC;AACH;AAEO,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AACF,GAAsD;AACpD,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,MAAM,SAA+B,CAAC,CAAC;AACzF,QAAM,CAAC,oBAAoB,qBAAqB,IAAI,MAAM,SAAS,CAAC;AAEpE,QAAM,wBAAwB,MAAM,YAAY,OAAO,UAAwC,CAAC,MAAM;AACpG,QAAI,CAAC,OAAQ;AACb,QAAI;AACF,YAAM,QAAQ,MAAM,uBAAuB,QAAQ,QAAQ,UAAU,IAAI;AACzE,2BAAqB,KAAK;AAAA,IAC5B,SAAS,KAAK;AACZ,cAAQ,KAAK,2DAA2D,GAAG;AAC3E,2BAAqB,CAAC,CAAC;AAAA,IACzB;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,wBAAwB,MAAM,YAAY,YAAY;AAC1D,0BAAsB,CAAC,UAAU,QAAQ,CAAC;AAC1C,UAAM,sBAAsB;AAAA,EAC9B,GAAG,CAAC,qBAAqB,CAAC;AAE1B,QAAM,EAAE,qBAAqB,kBAAkB,IAAI,wBAAwB;AAAA,IACzE;AAAA,IACA,eAAe;AAAA,IACf,YAAY;AAAA,EACd,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,sBAAsB;AAAA,EACxB;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,6 +1,23 @@
1
1
  import * as React from "react";
2
2
  import { readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
3
3
  import { useT } from "@open-mercato/shared/lib/i18n/context";
4
+ const dealDataCache = /* @__PURE__ */ new Map();
5
+ function fetchDealData(id, errorMessage, useCache) {
6
+ const url = `/api/customers/deals/${encodeURIComponent(id)}?include=stages&view=lite`;
7
+ const cached = dealDataCache.get(url);
8
+ if (useCache && cached) return cached.promise;
9
+ const entry = {
10
+ promise: readApiResultOrThrow(
11
+ url,
12
+ void 0,
13
+ { errorMessage }
14
+ )
15
+ };
16
+ if (useCache) dealDataCache.set(url, entry);
17
+ return entry.promise.finally(() => {
18
+ if (dealDataCache.get(url) === entry) dealDataCache.delete(url);
19
+ });
20
+ }
4
21
  function useDealData(id) {
5
22
  const t = useT();
6
23
  const [data, setData] = React.useState(null);
@@ -8,7 +25,7 @@ function useDealData(id) {
8
25
  const [error, setError] = React.useState(null);
9
26
  const [isNotFound, setIsNotFound] = React.useState(false);
10
27
  const initialLoadDoneRef = React.useRef(false);
11
- const loadData = React.useCallback(async () => {
28
+ const loadData = React.useCallback(async (options = {}) => {
12
29
  if (!id) {
13
30
  setIsNotFound(true);
14
31
  setIsLoading(false);
@@ -19,10 +36,10 @@ function useDealData(id) {
19
36
  }
20
37
  setError(null);
21
38
  try {
22
- const payload = await readApiResultOrThrow(
23
- `/api/customers/deals/${encodeURIComponent(id)}?include=stages&view=lite`,
24
- void 0,
25
- { errorMessage: t("customers.deals.detail.error.load", "Failed to load deal.") }
39
+ const payload = await fetchDealData(
40
+ id,
41
+ t("customers.deals.detail.error.load", "Failed to load deal."),
42
+ options.cache === true
26
43
  );
27
44
  setData(payload);
28
45
  } catch (loadError) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../../src/modules/customers/backend/customers/deals/%5Bid%5D/hooks/useDealData.ts"],
4
- "sourcesContent": ["import * as React from 'react'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { DealDetailPayload } from './types'\n\ntype UseDealDataResult = {\n data: DealDetailPayload | null\n setData: React.Dispatch<React.SetStateAction<DealDetailPayload | null>>\n isLoading: boolean\n error: string | null\n isNotFound: boolean\n loadData: () => Promise<void>\n}\n\nexport function useDealData(id: string): UseDealDataResult {\n const t = useT()\n const [data, setData] = React.useState<DealDetailPayload | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n const initialLoadDoneRef = React.useRef(false)\n\n const loadData = React.useCallback(async () => {\n if (!id) {\n setIsNotFound(true)\n setIsLoading(false)\n return\n }\n if (!initialLoadDoneRef.current) {\n setIsLoading(true)\n }\n setError(null)\n try {\n const payload = await readApiResultOrThrow<DealDetailPayload>(\n `/api/customers/deals/${encodeURIComponent(id)}?include=stages&view=lite`,\n undefined,\n { errorMessage: t('customers.deals.detail.error.load', 'Failed to load deal.') },\n )\n setData(payload)\n } catch (loadError) {\n if ((loadError as { status?: number }).status === 404) {\n setIsNotFound(true)\n } else {\n const message =\n loadError instanceof Error\n ? loadError.message\n : t('customers.deals.detail.error.load', 'Failed to load deal.')\n setError(message)\n }\n if (!initialLoadDoneRef.current) setData(null)\n } finally {\n setIsLoading(false)\n initialLoadDoneRef.current = true\n }\n }, [id, t])\n\n return { data, setData, isLoading, error, isNotFound, loadData }\n}\n"],
5
- "mappings": "AAAA,YAAY,WAAW;AACvB,SAAS,4BAA4B;AACrC,SAAS,YAAY;AAYd,SAAS,YAAY,IAA+B;AACzD,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAmC,IAAI;AACrE,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,qBAAqB,MAAM,OAAO,KAAK;AAE7C,QAAM,WAAW,MAAM,YAAY,YAAY;AAC7C,QAAI,CAAC,IAAI;AACP,oBAAc,IAAI;AAClB,mBAAa,KAAK;AAClB;AAAA,IACF;AACA,QAAI,CAAC,mBAAmB,SAAS;AAC/B,mBAAa,IAAI;AAAA,IACnB;AACA,aAAS,IAAI;AACb,QAAI;AACF,YAAM,UAAU,MAAM;AAAA,QACpB,wBAAwB,mBAAmB,EAAE,CAAC;AAAA,QAC9C;AAAA,QACA,EAAE,cAAc,EAAE,qCAAqC,sBAAsB,EAAE;AAAA,MACjF;AACA,cAAQ,OAAO;AAAA,IACjB,SAAS,WAAW;AAClB,UAAK,UAAkC,WAAW,KAAK;AACrD,sBAAc,IAAI;AAAA,MACpB,OAAO;AACL,cAAM,UACJ,qBAAqB,QACjB,UAAU,UACV,EAAE,qCAAqC,sBAAsB;AACnE,iBAAS,OAAO;AAAA,MAClB;AACA,UAAI,CAAC,mBAAmB,QAAS,SAAQ,IAAI;AAAA,IAC/C,UAAE;AACA,mBAAa,KAAK;AAClB,yBAAmB,UAAU;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,IAAI,CAAC,CAAC;AAEV,SAAO,EAAE,MAAM,SAAS,WAAW,OAAO,YAAY,SAAS;AACjE;",
4
+ "sourcesContent": ["import * as React from 'react'\nimport { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport type { DealDetailPayload } from './types'\n\ntype LoadDataOptions = {\n cache?: boolean\n}\n\ntype UseDealDataResult = {\n data: DealDetailPayload | null\n setData: React.Dispatch<React.SetStateAction<DealDetailPayload | null>>\n isLoading: boolean\n error: string | null\n isNotFound: boolean\n loadData: (options?: LoadDataOptions) => Promise<void>\n}\n\ntype DealDataCacheEntry = {\n promise: Promise<DealDetailPayload>\n}\n\nconst dealDataCache = new Map<string, DealDataCacheEntry>()\n\nfunction fetchDealData(id: string, errorMessage: string, useCache: boolean): Promise<DealDetailPayload> {\n const url = `/api/customers/deals/${encodeURIComponent(id)}?include=stages&view=lite`\n const cached = dealDataCache.get(url)\n if (useCache && cached) return cached.promise\n const entry: DealDataCacheEntry = {\n promise: readApiResultOrThrow<DealDetailPayload>(\n url,\n undefined,\n { errorMessage },\n ),\n }\n if (useCache) dealDataCache.set(url, entry)\n return entry.promise.finally(() => {\n if (dealDataCache.get(url) === entry) dealDataCache.delete(url)\n })\n}\n\nexport function useDealData(id: string): UseDealDataResult {\n const t = useT()\n const [data, setData] = React.useState<DealDetailPayload | null>(null)\n const [isLoading, setIsLoading] = React.useState(true)\n const [error, setError] = React.useState<string | null>(null)\n const [isNotFound, setIsNotFound] = React.useState(false)\n const initialLoadDoneRef = React.useRef(false)\n\n const loadData = React.useCallback(async (options: LoadDataOptions = {}) => {\n if (!id) {\n setIsNotFound(true)\n setIsLoading(false)\n return\n }\n if (!initialLoadDoneRef.current) {\n setIsLoading(true)\n }\n setError(null)\n try {\n const payload = await fetchDealData(\n id,\n t('customers.deals.detail.error.load', 'Failed to load deal.'),\n options.cache === true,\n )\n setData(payload)\n } catch (loadError) {\n if ((loadError as { status?: number }).status === 404) {\n setIsNotFound(true)\n } else {\n const message =\n loadError instanceof Error\n ? loadError.message\n : t('customers.deals.detail.error.load', 'Failed to load deal.')\n setError(message)\n }\n if (!initialLoadDoneRef.current) setData(null)\n } finally {\n setIsLoading(false)\n initialLoadDoneRef.current = true\n }\n }, [id, t])\n\n return { data, setData, isLoading, error, isNotFound, loadData }\n}\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;AACvB,SAAS,4BAA4B;AACrC,SAAS,YAAY;AAoBrB,MAAM,gBAAgB,oBAAI,IAAgC;AAE1D,SAAS,cAAc,IAAY,cAAsB,UAA+C;AACtG,QAAM,MAAM,wBAAwB,mBAAmB,EAAE,CAAC;AAC1D,QAAM,SAAS,cAAc,IAAI,GAAG;AACpC,MAAI,YAAY,OAAQ,QAAO,OAAO;AACtC,QAAM,QAA4B;AAAA,IAChC,SAAS;AAAA,MACP;AAAA,MACA;AAAA,MACA,EAAE,aAAa;AAAA,IACjB;AAAA,EACF;AACA,MAAI,SAAU,eAAc,IAAI,KAAK,KAAK;AAC1C,SAAO,MAAM,QAAQ,QAAQ,MAAM;AACjC,QAAI,cAAc,IAAI,GAAG,MAAM,MAAO,eAAc,OAAO,GAAG;AAAA,EAChE,CAAC;AACH;AAEO,SAAS,YAAY,IAA+B;AACzD,QAAM,IAAI,KAAK;AACf,QAAM,CAAC,MAAM,OAAO,IAAI,MAAM,SAAmC,IAAI;AACrE,QAAM,CAAC,WAAW,YAAY,IAAI,MAAM,SAAS,IAAI;AACrD,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,IAAI;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAI,MAAM,SAAS,KAAK;AACxD,QAAM,qBAAqB,MAAM,OAAO,KAAK;AAE7C,QAAM,WAAW,MAAM,YAAY,OAAO,UAA2B,CAAC,MAAM;AAC1E,QAAI,CAAC,IAAI;AACP,oBAAc,IAAI;AAClB,mBAAa,KAAK;AAClB;AAAA,IACF;AACA,QAAI,CAAC,mBAAmB,SAAS;AAC/B,mBAAa,IAAI;AAAA,IACnB;AACA,aAAS,IAAI;AACb,QAAI;AACF,YAAM,UAAU,MAAM;AAAA,QACpB;AAAA,QACA,EAAE,qCAAqC,sBAAsB;AAAA,QAC7D,QAAQ,UAAU;AAAA,MACpB;AACA,cAAQ,OAAO;AAAA,IACjB,SAAS,WAAW;AAClB,UAAK,UAAkC,WAAW,KAAK;AACrD,sBAAc,IAAI;AAAA,MACpB,OAAO;AACL,cAAM,UACJ,qBAAqB,QACjB,UAAU,UACV,EAAE,qCAAqC,sBAAsB;AACnE,iBAAS,OAAO;AAAA,MAClB;AACA,UAAI,CAAC,mBAAmB,QAAS,SAAQ,IAAI;AAAA,IAC/C,UAAE;AACA,mBAAa,KAAK;AAClB,yBAAmB,UAAU;AAAA,IAC/B;AAAA,EACF,GAAG,CAAC,IAAI,CAAC,CAAC;AAEV,SAAO,EAAE,MAAM,SAAS,WAAW,OAAO,YAAY,SAAS;AACjE;",
6
6
  "names": []
7
7
  }
@@ -91,7 +91,7 @@ function DealDetailPage({ params }) {
91
91
  handleCancelActivity
92
92
  } = useDealActivities({ dealId: id, runMutationWithContext });
93
93
  React.useEffect(() => {
94
- void Promise.all([loadData(), loadPlannedActivities()]);
94
+ void Promise.all([loadData({ cache: true }), loadPlannedActivities({ cache: true })]);
95
95
  }, [loadData, loadPlannedActivities]);
96
96
  const activityEntities = React.useMemo(
97
97
  () => data ? [...data.people, ...data.companies].map((entry) => ({
@@ -252,6 +252,15 @@ function DealDetailPage({ params }) {
252
252
  ]
253
253
  });
254
254
  }, [closeLostPopup, data, openScheduleEdit, selectedActivityEntity, t]);
255
+ const currentPipelineName = data ? data.pipelineName ?? wonStats?.pipelineName ?? lostStats?.pipelineName ?? null : wonStats?.pipelineName ?? lostStats?.pipelineName ?? null;
256
+ const formPipelineOptions = React.useMemo(
257
+ () => data?.deal.pipelineId ? [{
258
+ id: data.deal.pipelineId,
259
+ name: currentPipelineName ?? t("customers.deals.detail.pipeline.defaultName", "Current pipeline"),
260
+ isDefault: false
261
+ }] : [],
262
+ [currentPipelineName, data?.deal.pipelineId, t]
263
+ );
255
264
  if (isLoading) {
256
265
  return /* @__PURE__ */ jsx(Page, { children: /* @__PURE__ */ jsx(PageBody, { children: /* @__PURE__ */ jsx(LoadingMessage, { label: t("customers.deals.detail.loading", "Loading deal\u2026") }) }) });
257
266
  }
@@ -275,7 +284,6 @@ function DealDetailPage({ params }) {
275
284
  ) }) });
276
285
  }
277
286
  const amountLabel = formatCurrency(data.deal.valueAmount, data.deal.valueCurrency);
278
- const currentPipelineName = data.pipelineName ?? wonStats?.pipelineName ?? lostStats?.pipelineName ?? null;
279
287
  const dealName = data.deal.title || t("customers.deals.detail.untitled", "Untitled deal");
280
288
  const zone1Content = /* @__PURE__ */ jsx("div", { ref: formWrapperRef, children: /* @__PURE__ */ jsx(
281
289
  DealForm,
@@ -289,6 +297,8 @@ function DealDetailPage({ params }) {
289
297
  showVersionHistory: false,
290
298
  showCancelAction: false,
291
299
  onDirtyChange: setIsDirty,
300
+ initialPipelineOptions: formPipelineOptions,
301
+ initialPipelineStageOptions: data.pipelineStages,
292
302
  collapsibleGroups: { pageType: "deal-detail-v3", chevronPosition: "right" },
293
303
  sortableGroups: { pageType: "deal-detail-v3" },
294
304
  initialValues: {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/customers/backend/customers/deals/%5Bid%5D/page.tsx"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport Link from 'next/link'\nimport { Building2, UserSearch, Users } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { AttachmentsSection, ErrorMessage, LoadingMessage, NotesSection, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { CollapsibleZoneLayout } from '@open-mercato/ui/backend/crud/CollapsibleZoneLayout'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { E } from '#generated/entities.ids.generated'\n\nimport { ActivitiesSection } from '../../../../components/detail/ActivitiesSection'\nimport { ChangelogTab } from '../../../../components/detail/ChangelogTab'\nimport { DealClosureActionBar } from '../../../../components/detail/DealClosureActionBar'\nimport { DealDetailHeader } from '../../../../components/detail/DealDetailHeader'\nimport { DealDetailTabs, resolveLegacyTab, type DealTabId } from '../../../../components/detail/DealDetailTabs'\nimport { DealForm, useDealAssociationLookups } from '../../../../components/detail/DealForm'\nimport { DealLinkedEntitiesTab } from '../../../../components/detail/DealLinkedEntitiesTab'\nimport { ConfirmDealLostDialog } from '../../../../components/detail/ConfirmDealLostDialog'\nimport { DealLostSummaryDialog } from '../../../../components/detail/DealLostSummaryDialog'\nimport { DealWonPopup } from '../../../../components/detail/DealWonPopup'\nimport { InlineActivityComposer } from '../../../../components/detail/InlineActivityComposer'\nimport { PipelineStepper } from '../../../../components/detail/PipelineStepper'\nimport { PlannedActivitiesSection } from '../../../../components/detail/PlannedActivitiesSection'\nimport { ScheduleActivityDialog, type ScheduleActivityEditData } from '../../../../components/detail/ScheduleActivityDialog'\nimport { createCustomerNotesAdapter } from '../../../../components/detail/notesAdapter'\nimport type { InteractionSummary } from '../../../../components/detail/types'\nimport { readMarkdownPreferenceCookie, writeMarkdownPreferenceCookie } from '../../../../lib/markdownPreference'\nimport { ICON_SUGGESTIONS } from '../../../../lib/dictionaries'\nimport { renderDictionaryColor, renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\n\nimport { formatCurrency, startOfNextQuarter } from './hooks/formatters'\nimport type { DealDetailPayload } from './hooks/types'\nimport { useDealActivities } from './hooks/useDealActivities'\nimport { useDealAssociations } from './hooks/useDealAssociations'\nimport { useDealClosure } from './hooks/useDealClosure'\nimport { useDealData } from './hooks/useDealData'\nimport { useDealFormHandlers } from './hooks/useDealFormHandlers'\nimport { useDealInjectedTabs } from './hooks/useDealInjectedTabs'\nimport { useDealMutationContext } from './hooks/useDealMutationContext'\nimport { useDealPipeline } from './hooks/useDealPipeline'\nimport { useScheduleDialog } from './hooks/useScheduleDialog'\n\nexport default function DealDetailPage({ params }: { params?: { id?: string } }) {\n const id = params?.id ?? ''\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n\n const { data, setData, isLoading, error, isNotFound, loadData } = useDealData(id)\n const [isDirty, setIsDirty] = React.useState(false)\n const {\n scheduleDialogOpen,\n scheduleEditData,\n openSchedule,\n openEdit: openScheduleEdit,\n closeSchedule,\n } = useScheduleDialog()\n const formWrapperRef = React.useRef<HTMLDivElement>(null)\n\n const initialTab = React.useMemo(() => resolveLegacyTab(searchParams?.get('tab')), [searchParams])\n const [activeTab, setActiveTab] = React.useState<DealTabId>(initialTab)\n\n React.useEffect(() => {\n setActiveTab(initialTab)\n }, [initialTab])\n\n const currentDealId = data?.deal.id ?? id\n const { injectionContext, runMutationWithContext } = useDealMutationContext({\n currentDealId,\n fallbackId: id,\n data,\n })\n\n const notesAdapter = React.useMemo(\n () => createCustomerNotesAdapter(detailTranslator, { runMutation: runMutationWithContext }),\n [detailTranslator, runMutationWithContext],\n )\n\n const { injectedTabs, injectedTabMap } = useDealInjectedTabs({\n injectionContext,\n data,\n setData,\n })\n\n const { searchPeoplePage, fetchPeopleByIds, searchCompaniesPage, fetchCompaniesByIds } = useDealAssociationLookups({\n excludeLinkedDealId: data?.deal.id ?? null,\n })\n\n const {\n plannedActivities,\n activityRefreshKey,\n loadPlannedActivities,\n handleActivityCreated,\n handleMarkDone,\n handleCancelActivity,\n } = useDealActivities({ dealId: id, runMutationWithContext })\n\n React.useEffect(() => {\n void Promise.all([loadData(), loadPlannedActivities()])\n }, [loadData, loadPlannedActivities])\n\n const activityEntities = React.useMemo(\n () => (data\n ? [...data.people, ...data.companies].map((entry) => ({\n id: entry.id,\n label: entry.subtitle ? `${entry.label} \u00B7 ${entry.subtitle}` : entry.label,\n kind: entry.kind,\n }))\n : []),\n [data],\n )\n const [selectedActivityEntityId, setSelectedActivityEntityId] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n setSelectedActivityEntityId((current) => {\n if (activityEntities.length === 1) return activityEntities[0].id\n if (current && activityEntities.some((entry) => entry.id === current)) return current\n return null\n })\n }, [activityEntities])\n\n const selectedActivityEntity = React.useMemo(\n () => activityEntities.find((entry) => entry.id === selectedActivityEntityId) ?? null,\n [activityEntities, selectedActivityEntityId],\n )\n\n const dealOptions = React.useMemo(\n () => data ? [{ id: data.deal.id, label: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal') }] : [],\n [data, t],\n )\n\n const entityOptions = React.useMemo(\n () => activityEntities.map(({ id, label }) => ({ id, label })),\n [activityEntities],\n )\n\n const confirmDiscardIfDirty = React.useCallback(async () => {\n if (!isDirty) return true\n return confirm({\n title: t('customers.deals.detail.unsavedTitle', 'Discard unsaved changes?'),\n description: t(\n 'customers.deals.detail.unsavedDescription',\n 'You have unsaved edits in this deal. Save them first or continue to discard them.',\n ),\n confirmText: t('customers.deals.detail.unsavedConfirm', 'Discard changes'),\n cancelText: t('customers.deals.detail.unsavedCancel', 'Keep editing'),\n variant: 'destructive',\n })\n }, [confirm, isDirty, t])\n\n const {\n peopleEditorIds,\n companiesEditorIds,\n peopleSaving,\n companiesSaving,\n handlePeopleAssociationsChange,\n handleCompaniesAssociationsChange,\n loadLinkedPeoplePage,\n loadLinkedCompaniesPage,\n } = useDealAssociations({\n currentDealId,\n data,\n setData,\n runMutationWithContext,\n })\n\n const { isStageSaving, handleStageChange } = useDealPipeline({\n currentDealId,\n data,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onStageChanged: loadData,\n })\n\n const {\n lostDialogOpen,\n wonPopupOpen,\n lostPopupOpen,\n wonStats,\n lostStats,\n openLostDialog,\n closeLostDialog,\n closeWonPopup,\n closeLostPopup,\n handleWon,\n handleLostConfirm,\n } = useDealClosure({\n currentDealId,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onClosed: loadData,\n })\n\n const handleTabChange = React.useCallback(async (tab: DealTabId) => {\n if (!(await confirmDiscardIfDirty())) return\n setActiveTab(tab)\n const nextParams = new URLSearchParams(searchParams?.toString() ?? '')\n nextParams.set('tab', tab)\n router.replace(`/backend/customers/deals/${encodeURIComponent(id)}?${nextParams.toString()}`, { scroll: false })\n }, [confirmDiscardIfDirty, id, router, searchParams])\n\n const { isSaving, handleFormSubmit, handleDelete, handleHeaderSave } = useDealFormHandlers({\n data,\n currentDealId,\n loadData,\n runMutationWithContext,\n formWrapperRef,\n confirm,\n })\n\n const handleEditActivity = React.useCallback((activity: InteractionSummary) => {\n if (activity.entityId && activityEntities.some((entry) => entry.id === activity.entityId)) {\n setSelectedActivityEntityId(activity.entityId)\n }\n // Forward `customValues` so per-type chip state (callPhoneNumber,\n // callDirection, taskPriority) round-trips on edit (#1808 phone persistence).\n // Forward `occurredAt` so historical activity edits prefill from the\n // original moment instead of \"today\" (#1807 prefill).\n const rawActivity = activity as unknown as Record<string, unknown>\n openScheduleEdit({\n id: activity.id,\n updatedAt: typeof rawActivity.updatedAt === 'string' ? rawActivity.updatedAt as string : typeof rawActivity.updated_at === 'string' ? rawActivity.updated_at as string : null,\n interactionType: activity.interactionType,\n title: activity.title ?? null,\n body: activity.body ?? null,\n scheduledAt: activity.scheduledAt ?? null,\n occurredAt: activity.occurredAt ?? null,\n durationMinutes: activity.duration ?? null,\n location: activity.location ?? null,\n allDay: activity.allDay ?? null,\n recurrenceRule: activity.recurrenceRule ?? null,\n recurrenceEnd: activity.recurrenceEnd ?? null,\n participants: activity.participants ?? null,\n reminderMinutes: activity.reminderMinutes ?? null,\n visibility: activity.visibility ?? null,\n linkedEntities: activity.linkedEntities ?? null,\n guestPermissions: activity.guestPermissions ?? null,\n ...(rawActivity.customValues && typeof rawActivity.customValues === 'object'\n ? { customValues: rawActivity.customValues as Record<string, unknown> }\n : {}),\n ...(typeof rawActivity.phoneNumber === 'string'\n ? { phoneNumber: rawActivity.phoneNumber as string }\n : {}),\n } as ScheduleActivityEditData & { customValues?: Record<string, unknown> | null; phoneNumber?: string | null })\n }, [activityEntities, openScheduleEdit])\n\n const handleViewDashboard = React.useCallback(() => {\n closeWonPopup()\n router.push('/backend')\n }, [closeWonPopup, router])\n\n const handleBackToPipeline = React.useCallback(() => {\n closeWonPopup()\n closeLostPopup()\n router.push('/backend/customers/deals/pipeline')\n }, [closeLostPopup, closeWonPopup, router])\n\n const handleScheduleLostFollowUp = React.useCallback(() => {\n if (!data || !selectedActivityEntity) return\n const nextQuarterDate = startOfNextQuarter(new Date())\n closeLostPopup()\n openScheduleEdit({\n id: '',\n interactionType: 'task',\n title: data.deal.title\n ? t('customers.deals.detail.lost.followUpTitle', 'Revisit {{title}}', { title: data.deal.title })\n : t('customers.deals.detail.lost.followUpFallbackTitle', 'Revisit closed deal'),\n body: data.deal.lossNotes ?? null,\n scheduledAt: nextQuarterDate.toISOString(),\n durationMinutes: 30,\n location: null,\n allDay: false,\n recurrenceRule: null,\n recurrenceEnd: null,\n participants: null,\n reminderMinutes: 1440,\n visibility: 'team',\n linkedEntities: [\n {\n id: data.deal.id,\n type: 'deal',\n label: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal'),\n },\n ],\n })\n }, [closeLostPopup, data, openScheduleEdit, selectedActivityEntity, t])\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('customers.deals.detail.loading', 'Loading deal\u2026')} />\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('customers.deals.detail.error.notFound', 'Deal not found.')}\n backHref=\"/backend/customers/deals\"\n backLabel={t('customers.deals.detail.actions.backToList', 'Back to deals')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !data) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={error ?? t('customers.deals.detail.error.load', 'Failed to load deal.')}\n action={\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href=\"/backend/customers/deals\">\n {t('customers.deals.detail.actions.backToList', 'Back to deals')}\n </Link>\n </Button>\n }\n />\n </PageBody>\n </Page>\n )\n }\n\n const amountLabel = formatCurrency(data.deal.valueAmount, data.deal.valueCurrency)\n const currentPipelineName = data.pipelineName ?? wonStats?.pipelineName ?? lostStats?.pipelineName ?? null\n const dealName = data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')\n\n const zone1Content = (\n <div ref={formWrapperRef}>\n <DealForm\n mode=\"edit\"\n embedded\n trackDirtyWhenEmbedded\n hideFooterActions\n singleColumnGroups\n showAssociationsGroup={false}\n showVersionHistory={false}\n showCancelAction={false}\n onDirtyChange={setIsDirty}\n collapsibleGroups={{ pageType: 'deal-detail-v3', chevronPosition: 'right' }}\n sortableGroups={{ pageType: 'deal-detail-v3' }}\n initialValues={{\n ...data.deal,\n valueAmount:\n typeof data.deal.valueAmount === 'string' && data.deal.valueAmount.trim().length\n ? Number(data.deal.valueAmount)\n : null,\n personIds: data.linkedPersonIds,\n companyIds: data.linkedCompanyIds,\n customFields: data.customFields,\n ...Object.fromEntries(Object.entries(data.customFields ?? {}).map(([key, value]) => [`cf_${key}`, value])),\n }}\n onSubmit={handleFormSubmit}\n onCancel={() => { void loadData() }}\n onDelete={handleDelete}\n />\n </div>\n )\n\n const zone2Content = (\n <div className=\"rounded-[10px] border border-border bg-card px-5 py-5\">\n {(() => {\n const injected = injectedTabMap.get(activeTab)\n if (injected) return injected()\n\n if (activeTab === 'activities') {\n const activityEntitySelection = activityEntities.length > 1 ? (\n <div className=\"rounded-[10px] border border-border bg-muted/20 px-5 py-5\">\n <label htmlFor=\"deal-activity-entity\" className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.selectEntityLabel', 'Choose customer record')}\n </label>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t(\n 'customers.deals.detail.activities.selectEntityDescription',\n 'Pick the person or company that should own new deal activities and follow-ups.',\n )}\n </div>\n <select\n id=\"deal-activity-entity\"\n aria-label={t('customers.deals.detail.activities.selectEntityLabel', 'Choose customer record')}\n className=\"mt-4 h-9 w-full rounded border border-muted-foreground/40 bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n value={selectedActivityEntityId ?? ''}\n onChange={(event) => setSelectedActivityEntityId(event.target.value || null)}\n >\n <option value=\"\">\n {t('customers.deals.detail.activities.selectEntityPlaceholder', 'Select a person or company')}\n </option>\n {activityEntities.map((entry) => (\n <option key={entry.id} value={entry.id}>\n {entry.label}\n </option>\n ))}\n </select>\n </div>\n ) : null\n\n return (\n <div className=\"space-y-4\">\n {activityEntities.length > 1 ? activityEntitySelection : null}\n {activityEntities.length === 0 ? (\n <div className=\"rounded-[10px] border border-border bg-muted/20 px-5 py-5\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.linkEntityTitle', 'Link a person or company first')}\n </div>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t('customers.deals.detail.activities.linkEntityDescription', 'Activities on a deal still need a customer record for timeline ownership.')}\n </div>\n <div className=\"mt-4 flex gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => handleTabChange('people')}>\n {t('customers.deals.detail.tabs.people', 'People')}\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => handleTabChange('companies')}>\n {t('customers.deals.detail.tabs.companies', 'Companies')}\n </Button>\n </div>\n </div>\n ) : selectedActivityEntity ? (\n <InlineActivityComposer\n entityType={selectedActivityEntity.kind}\n entityId={selectedActivityEntity.id}\n dealId={data.deal.id}\n onActivityCreated={() => { void handleActivityCreated() }}\n runGuardedMutation={runMutationWithContext}\n onScheduleRequested={openSchedule}\n />\n ) : (\n <EmptyState\n size=\"sm\"\n icon={<UserSearch className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={t('customers.deals.detail.activities.selectEntityRequiredTitle', 'Choose a person or company to continue')}\n description={t(\n 'customers.deals.detail.activities.selectEntityRequiredDescription',\n 'Select the customer record that should receive new deal activities before logging or scheduling anything.',\n )}\n />\n )}\n <PlannedActivitiesSection\n activities={plannedActivities}\n onComplete={(interactionId) => { void handleMarkDone(interactionId) }}\n onSchedule={selectedActivityEntity ? openSchedule : undefined}\n onEdit={handleEditActivity}\n onCancel={(interactionId) => { void handleCancelActivity(interactionId) }}\n />\n {selectedActivityEntity ? (\n <ActivitiesSection\n entityId={selectedActivityEntity.id}\n entityName={selectedActivityEntity.label}\n dealId={data.deal.id}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n defaultEntityId={selectedActivityEntity.id}\n addActionLabel={t('customers.deals.detail.activitiesAdd', 'Log activity')}\n emptyState={{\n title: t('customers.deals.detail.activitiesEmptyTitle', 'No activities yet'),\n actionLabel: t('customers.deals.detail.activitiesEmptyAction', 'Log activity'),\n }}\n runGuardedMutation={runMutationWithContext}\n onDataRefresh={() => { void handleActivityCreated() }}\n refreshKey={activityRefreshKey}\n onEditActivity={handleEditActivity}\n />\n ) : null}\n </div>\n )\n }\n\n if (activeTab === 'people') {\n return (\n <DealLinkedEntitiesTab\n entityLabel={t('customers.deals.detail.tabs.peopleSingular', 'Person')}\n entityLabelPlural={t('customers.deals.detail.tabs.people', 'People')}\n manageLabel={t('customers.deals.detail.peopleEditorTitle', 'Manage linked people')}\n searchPlaceholder={t('customers.deals.detail.peopleSearch', 'Search linked people\u2026')}\n linkedItems={data.people}\n linkedCount={data.counts.people}\n selectedIds={peopleEditorIds}\n disabled={peopleSaving || isSaving}\n savePending={peopleSaving}\n hrefBuilder={(personId) => `/backend/customers/people-v2/${encodeURIComponent(personId)}`}\n onSaveSelection={(next) => handlePeopleAssociationsChange(next)}\n loadLinkedPage={loadLinkedPeoplePage}\n searchEntities={searchPeoplePage}\n fetchEntitiesByIds={fetchPeopleByIds}\n icon={<Users className=\"size-4\" />}\n />\n )\n }\n\n if (activeTab === 'companies') {\n return (\n <DealLinkedEntitiesTab\n entityLabel={t('customers.deals.detail.tabs.companySingular', 'Company')}\n entityLabelPlural={t('customers.deals.detail.tabs.companies', 'Companies')}\n manageLabel={t('customers.deals.detail.companiesEditorTitle', 'Manage linked companies')}\n searchPlaceholder={t('customers.deals.detail.companiesSearch', 'Search linked companies\u2026')}\n linkedItems={data.companies}\n linkedCount={data.counts.companies}\n selectedIds={companiesEditorIds}\n disabled={companiesSaving || isSaving}\n savePending={companiesSaving}\n hrefBuilder={(companyId) => `/backend/customers/companies-v2/${encodeURIComponent(companyId)}`}\n onSaveSelection={(next) => handleCompaniesAssociationsChange(next)}\n loadLinkedPage={loadLinkedCompaniesPage}\n searchEntities={searchCompaniesPage}\n fetchEntitiesByIds={fetchCompaniesByIds}\n icon={<Building2 className=\"size-4\" />}\n />\n )\n }\n\n if (activeTab === 'notes') {\n return (\n <NotesSection\n entityId={null}\n dealId={data.deal.id}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n emptyLabel={t('customers.deals.detail.notesEmpty', 'No notes yet.')}\n viewerUserId={data.viewer?.userId ?? null}\n viewerName={data.viewer?.name ?? null}\n viewerEmail={data.viewer?.email ?? null}\n addActionLabel={t('customers.deals.detail.notesAdd', 'Add note')}\n emptyState={{\n title: t('customers.deals.detail.notesEmptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('customers.deals.detail.notesEmptyAction', 'Add a note'),\n }}\n translator={detailTranslator}\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n readMarkdownPreference={readMarkdownPreferenceCookie}\n writeMarkdownPreference={writeMarkdownPreferenceCookie}\n />\n )\n }\n\n if (activeTab === 'files') {\n return (\n <AttachmentsSection\n entityId={E.customers.customer_deal}\n recordId={data.deal.id}\n title={t('customers.deals.detail.tabs.files', 'Files')}\n description={t('customers.deals.detail.files.subtitle', 'Upload and manage files linked to this deal.')}\n />\n )\n }\n\n if (activeTab === 'changelog') {\n return <ChangelogTab entityId={data.deal.id} entityType=\"deal\" />\n }\n\n return null\n })()}\n </div>\n )\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <InjectionSpot spotId=\"detail:customers.deal:header\" context={injectionContext} data={data} />\n\n <DealDetailHeader\n deal={data.deal}\n owner={data.owner}\n people={data.people}\n companies={data.companies}\n pipelineName={currentPipelineName}\n stageOptions={data.pipelineStages}\n currentStageId={data.deal.pipelineStageId}\n onStageChange={handleStageChange}\n isStageSaving={isStageSaving}\n onSave={handleHeaderSave}\n onDelete={handleDelete}\n isDirty={isDirty}\n isSaving={isSaving}\n />\n\n <InjectionSpot spotId=\"detail:customers.deal:status-badges\" context={injectionContext} data={data} />\n\n <PipelineStepper\n stages={data.pipelineStages}\n transitions={data.stageTransitions}\n currentStageId={data.deal.pipelineStageId}\n pipelineName={currentPipelineName}\n closureOutcome={data.deal.closureOutcome}\n footer={data.deal.closureOutcome ? null : (\n <DealClosureActionBar\n embedded\n closureOutcome={data.deal.closureOutcome}\n onWon={() => { void handleWon() }}\n onLost={openLostDialog}\n />\n )}\n />\n\n <DealDetailTabs\n activeTab={activeTab}\n onTabChange={handleTabChange}\n injectedTabs={injectedTabs.map((tab) => ({ id: tab.id, label: tab.label }))}\n peopleCount={data.counts.people}\n companiesCount={data.counts.companies}\n >\n <CollapsibleZoneLayout\n pageType=\"deal-detail-v3\"\n entityName={dealName}\n isDirty={isDirty}\n zone1DefaultWidth=\"540px\"\n zone1={zone1Content}\n zone2={zone2Content}\n />\n </DealDetailTabs>\n\n <InjectionSpot spotId=\"detail:customers.deal:footer\" context={injectionContext} data={data} />\n </div>\n\n {ConfirmDialogElement}\n\n {selectedActivityEntity ? (\n <ScheduleActivityDialog\n open={scheduleDialogOpen}\n onClose={closeSchedule}\n entityId={selectedActivityEntity.id}\n dealId={data.deal.id}\n entityType={selectedActivityEntity.kind}\n entityName={selectedActivityEntity.label}\n companyName={selectedActivityEntity.kind === 'company' ? selectedActivityEntity.label : data.companies[0]?.label ?? null}\n onActivityCreated={() => { void handleActivityCreated() }}\n editData={scheduleEditData}\n />\n ) : null}\n\n <ConfirmDealLostDialog\n open={lostDialogOpen}\n onClose={closeLostDialog}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n dealValue={amountLabel}\n companyName={data.companies[0]?.label ?? null}\n onConfirm={handleLostConfirm}\n />\n\n <DealWonPopup\n open={wonPopupOpen}\n onClose={closeWonPopup}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n stats={wonStats}\n onViewDashboard={handleViewDashboard}\n onBackToPipeline={handleBackToPipeline}\n />\n\n <DealLostSummaryDialog\n open={lostPopupOpen}\n onClose={closeLostPopup}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n lossNotes={data.deal.lossNotes}\n stats={lostStats}\n onBackToPipeline={handleBackToPipeline}\n onScheduleFollowUp={selectedActivityEntity ? handleScheduleLostFollowUp : undefined}\n />\n </PageBody>\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 { Building2, UserSearch, Users } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { useRouter, useSearchParams } from 'next/navigation'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { AttachmentsSection, ErrorMessage, LoadingMessage, NotesSection, RecordNotFoundState } from '@open-mercato/ui/backend/detail'\nimport { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { CollapsibleZoneLayout } from '@open-mercato/ui/backend/crud/CollapsibleZoneLayout'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'\nimport { E } from '#generated/entities.ids.generated'\n\nimport { ActivitiesSection } from '../../../../components/detail/ActivitiesSection'\nimport { ChangelogTab } from '../../../../components/detail/ChangelogTab'\nimport { DealClosureActionBar } from '../../../../components/detail/DealClosureActionBar'\nimport { DealDetailHeader } from '../../../../components/detail/DealDetailHeader'\nimport { DealDetailTabs, resolveLegacyTab, type DealTabId } from '../../../../components/detail/DealDetailTabs'\nimport { DealForm, useDealAssociationLookups } from '../../../../components/detail/DealForm'\nimport { DealLinkedEntitiesTab } from '../../../../components/detail/DealLinkedEntitiesTab'\nimport { ConfirmDealLostDialog } from '../../../../components/detail/ConfirmDealLostDialog'\nimport { DealLostSummaryDialog } from '../../../../components/detail/DealLostSummaryDialog'\nimport { DealWonPopup } from '../../../../components/detail/DealWonPopup'\nimport { InlineActivityComposer } from '../../../../components/detail/InlineActivityComposer'\nimport { PipelineStepper } from '../../../../components/detail/PipelineStepper'\nimport { PlannedActivitiesSection } from '../../../../components/detail/PlannedActivitiesSection'\nimport { ScheduleActivityDialog, type ScheduleActivityEditData } from '../../../../components/detail/ScheduleActivityDialog'\nimport { createCustomerNotesAdapter } from '../../../../components/detail/notesAdapter'\nimport type { InteractionSummary } from '../../../../components/detail/types'\nimport { readMarkdownPreferenceCookie, writeMarkdownPreferenceCookie } from '../../../../lib/markdownPreference'\nimport { ICON_SUGGESTIONS } from '../../../../lib/dictionaries'\nimport { renderDictionaryColor, renderDictionaryIcon } from '@open-mercato/core/modules/dictionaries/components/dictionaryAppearance'\n\nimport { formatCurrency, startOfNextQuarter } from './hooks/formatters'\nimport type { DealDetailPayload } from './hooks/types'\nimport { useDealActivities } from './hooks/useDealActivities'\nimport { useDealAssociations } from './hooks/useDealAssociations'\nimport { useDealClosure } from './hooks/useDealClosure'\nimport { useDealData } from './hooks/useDealData'\nimport { useDealFormHandlers } from './hooks/useDealFormHandlers'\nimport { useDealInjectedTabs } from './hooks/useDealInjectedTabs'\nimport { useDealMutationContext } from './hooks/useDealMutationContext'\nimport { useDealPipeline } from './hooks/useDealPipeline'\nimport { useScheduleDialog } from './hooks/useScheduleDialog'\n\nexport default function DealDetailPage({ params }: { params?: { id?: string } }) {\n const id = params?.id ?? ''\n const t = useT()\n const router = useRouter()\n const searchParams = useSearchParams()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])\n\n const { data, setData, isLoading, error, isNotFound, loadData } = useDealData(id)\n const [isDirty, setIsDirty] = React.useState(false)\n const {\n scheduleDialogOpen,\n scheduleEditData,\n openSchedule,\n openEdit: openScheduleEdit,\n closeSchedule,\n } = useScheduleDialog()\n const formWrapperRef = React.useRef<HTMLDivElement>(null)\n\n const initialTab = React.useMemo(() => resolveLegacyTab(searchParams?.get('tab')), [searchParams])\n const [activeTab, setActiveTab] = React.useState<DealTabId>(initialTab)\n\n React.useEffect(() => {\n setActiveTab(initialTab)\n }, [initialTab])\n\n const currentDealId = data?.deal.id ?? id\n const { injectionContext, runMutationWithContext } = useDealMutationContext({\n currentDealId,\n fallbackId: id,\n data,\n })\n\n const notesAdapter = React.useMemo(\n () => createCustomerNotesAdapter(detailTranslator, { runMutation: runMutationWithContext }),\n [detailTranslator, runMutationWithContext],\n )\n\n const { injectedTabs, injectedTabMap } = useDealInjectedTabs({\n injectionContext,\n data,\n setData,\n })\n\n const { searchPeoplePage, fetchPeopleByIds, searchCompaniesPage, fetchCompaniesByIds } = useDealAssociationLookups({\n excludeLinkedDealId: data?.deal.id ?? null,\n })\n\n const {\n plannedActivities,\n activityRefreshKey,\n loadPlannedActivities,\n handleActivityCreated,\n handleMarkDone,\n handleCancelActivity,\n } = useDealActivities({ dealId: id, runMutationWithContext })\n\n React.useEffect(() => {\n void Promise.all([loadData({ cache: true }), loadPlannedActivities({ cache: true })])\n }, [loadData, loadPlannedActivities])\n\n const activityEntities = React.useMemo(\n () => (data\n ? [...data.people, ...data.companies].map((entry) => ({\n id: entry.id,\n label: entry.subtitle ? `${entry.label} \u00B7 ${entry.subtitle}` : entry.label,\n kind: entry.kind,\n }))\n : []),\n [data],\n )\n const [selectedActivityEntityId, setSelectedActivityEntityId] = React.useState<string | null>(null)\n\n React.useEffect(() => {\n setSelectedActivityEntityId((current) => {\n if (activityEntities.length === 1) return activityEntities[0].id\n if (current && activityEntities.some((entry) => entry.id === current)) return current\n return null\n })\n }, [activityEntities])\n\n const selectedActivityEntity = React.useMemo(\n () => activityEntities.find((entry) => entry.id === selectedActivityEntityId) ?? null,\n [activityEntities, selectedActivityEntityId],\n )\n\n const dealOptions = React.useMemo(\n () => data ? [{ id: data.deal.id, label: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal') }] : [],\n [data, t],\n )\n\n const entityOptions = React.useMemo(\n () => activityEntities.map(({ id, label }) => ({ id, label })),\n [activityEntities],\n )\n\n const confirmDiscardIfDirty = React.useCallback(async () => {\n if (!isDirty) return true\n return confirm({\n title: t('customers.deals.detail.unsavedTitle', 'Discard unsaved changes?'),\n description: t(\n 'customers.deals.detail.unsavedDescription',\n 'You have unsaved edits in this deal. Save them first or continue to discard them.',\n ),\n confirmText: t('customers.deals.detail.unsavedConfirm', 'Discard changes'),\n cancelText: t('customers.deals.detail.unsavedCancel', 'Keep editing'),\n variant: 'destructive',\n })\n }, [confirm, isDirty, t])\n\n const {\n peopleEditorIds,\n companiesEditorIds,\n peopleSaving,\n companiesSaving,\n handlePeopleAssociationsChange,\n handleCompaniesAssociationsChange,\n loadLinkedPeoplePage,\n loadLinkedCompaniesPage,\n } = useDealAssociations({\n currentDealId,\n data,\n setData,\n runMutationWithContext,\n })\n\n const { isStageSaving, handleStageChange } = useDealPipeline({\n currentDealId,\n data,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onStageChanged: loadData,\n })\n\n const {\n lostDialogOpen,\n wonPopupOpen,\n lostPopupOpen,\n wonStats,\n lostStats,\n openLostDialog,\n closeLostDialog,\n closeWonPopup,\n closeLostPopup,\n handleWon,\n handleLostConfirm,\n } = useDealClosure({\n currentDealId,\n runMutationWithContext,\n confirmDiscardIfDirty,\n onClosed: loadData,\n })\n\n const handleTabChange = React.useCallback(async (tab: DealTabId) => {\n if (!(await confirmDiscardIfDirty())) return\n setActiveTab(tab)\n const nextParams = new URLSearchParams(searchParams?.toString() ?? '')\n nextParams.set('tab', tab)\n router.replace(`/backend/customers/deals/${encodeURIComponent(id)}?${nextParams.toString()}`, { scroll: false })\n }, [confirmDiscardIfDirty, id, router, searchParams])\n\n const { isSaving, handleFormSubmit, handleDelete, handleHeaderSave } = useDealFormHandlers({\n data,\n currentDealId,\n loadData,\n runMutationWithContext,\n formWrapperRef,\n confirm,\n })\n\n const handleEditActivity = React.useCallback((activity: InteractionSummary) => {\n if (activity.entityId && activityEntities.some((entry) => entry.id === activity.entityId)) {\n setSelectedActivityEntityId(activity.entityId)\n }\n // Forward `customValues` so per-type chip state (callPhoneNumber,\n // callDirection, taskPriority) round-trips on edit (#1808 phone persistence).\n // Forward `occurredAt` so historical activity edits prefill from the\n // original moment instead of \"today\" (#1807 prefill).\n const rawActivity = activity as unknown as Record<string, unknown>\n openScheduleEdit({\n id: activity.id,\n updatedAt: typeof rawActivity.updatedAt === 'string' ? rawActivity.updatedAt as string : typeof rawActivity.updated_at === 'string' ? rawActivity.updated_at as string : null,\n interactionType: activity.interactionType,\n title: activity.title ?? null,\n body: activity.body ?? null,\n scheduledAt: activity.scheduledAt ?? null,\n occurredAt: activity.occurredAt ?? null,\n durationMinutes: activity.duration ?? null,\n location: activity.location ?? null,\n allDay: activity.allDay ?? null,\n recurrenceRule: activity.recurrenceRule ?? null,\n recurrenceEnd: activity.recurrenceEnd ?? null,\n participants: activity.participants ?? null,\n reminderMinutes: activity.reminderMinutes ?? null,\n visibility: activity.visibility ?? null,\n linkedEntities: activity.linkedEntities ?? null,\n guestPermissions: activity.guestPermissions ?? null,\n ...(rawActivity.customValues && typeof rawActivity.customValues === 'object'\n ? { customValues: rawActivity.customValues as Record<string, unknown> }\n : {}),\n ...(typeof rawActivity.phoneNumber === 'string'\n ? { phoneNumber: rawActivity.phoneNumber as string }\n : {}),\n } as ScheduleActivityEditData & { customValues?: Record<string, unknown> | null; phoneNumber?: string | null })\n }, [activityEntities, openScheduleEdit])\n\n const handleViewDashboard = React.useCallback(() => {\n closeWonPopup()\n router.push('/backend')\n }, [closeWonPopup, router])\n\n const handleBackToPipeline = React.useCallback(() => {\n closeWonPopup()\n closeLostPopup()\n router.push('/backend/customers/deals/pipeline')\n }, [closeLostPopup, closeWonPopup, router])\n\n const handleScheduleLostFollowUp = React.useCallback(() => {\n if (!data || !selectedActivityEntity) return\n const nextQuarterDate = startOfNextQuarter(new Date())\n closeLostPopup()\n openScheduleEdit({\n id: '',\n interactionType: 'task',\n title: data.deal.title\n ? t('customers.deals.detail.lost.followUpTitle', 'Revisit {{title}}', { title: data.deal.title })\n : t('customers.deals.detail.lost.followUpFallbackTitle', 'Revisit closed deal'),\n body: data.deal.lossNotes ?? null,\n scheduledAt: nextQuarterDate.toISOString(),\n durationMinutes: 30,\n location: null,\n allDay: false,\n recurrenceRule: null,\n recurrenceEnd: null,\n participants: null,\n reminderMinutes: 1440,\n visibility: 'team',\n linkedEntities: [\n {\n id: data.deal.id,\n type: 'deal',\n label: data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal'),\n },\n ],\n })\n }, [closeLostPopup, data, openScheduleEdit, selectedActivityEntity, t])\n\n const currentPipelineName = data\n ? data.pipelineName ?? wonStats?.pipelineName ?? lostStats?.pipelineName ?? null\n : wonStats?.pipelineName ?? lostStats?.pipelineName ?? null\n const formPipelineOptions = React.useMemo(\n () => data?.deal.pipelineId\n ? [{\n id: data.deal.pipelineId,\n name: currentPipelineName ?? t('customers.deals.detail.pipeline.defaultName', 'Current pipeline'),\n isDefault: false,\n }]\n : [],\n [currentPipelineName, data?.deal.pipelineId, t],\n )\n\n if (isLoading) {\n return (\n <Page>\n <PageBody>\n <LoadingMessage label={t('customers.deals.detail.loading', 'Loading deal\u2026')} />\n </PageBody>\n </Page>\n )\n }\n\n if (isNotFound) {\n return (\n <Page>\n <PageBody>\n <RecordNotFoundState\n label={t('customers.deals.detail.error.notFound', 'Deal not found.')}\n backHref=\"/backend/customers/deals\"\n backLabel={t('customers.deals.detail.actions.backToList', 'Back to deals')}\n />\n </PageBody>\n </Page>\n )\n }\n\n if (error || !data) {\n return (\n <Page>\n <PageBody>\n <ErrorMessage\n label={error ?? t('customers.deals.detail.error.load', 'Failed to load deal.')}\n action={\n <Button asChild variant=\"outline\" size=\"sm\">\n <Link href=\"/backend/customers/deals\">\n {t('customers.deals.detail.actions.backToList', 'Back to deals')}\n </Link>\n </Button>\n }\n />\n </PageBody>\n </Page>\n )\n }\n\n const amountLabel = formatCurrency(data.deal.valueAmount, data.deal.valueCurrency)\n const dealName = data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')\n\n const zone1Content = (\n <div ref={formWrapperRef}>\n <DealForm\n mode=\"edit\"\n embedded\n trackDirtyWhenEmbedded\n hideFooterActions\n singleColumnGroups\n showAssociationsGroup={false}\n showVersionHistory={false}\n showCancelAction={false}\n onDirtyChange={setIsDirty}\n initialPipelineOptions={formPipelineOptions}\n initialPipelineStageOptions={data.pipelineStages}\n collapsibleGroups={{ pageType: 'deal-detail-v3', chevronPosition: 'right' }}\n sortableGroups={{ pageType: 'deal-detail-v3' }}\n initialValues={{\n ...data.deal,\n valueAmount:\n typeof data.deal.valueAmount === 'string' && data.deal.valueAmount.trim().length\n ? Number(data.deal.valueAmount)\n : null,\n personIds: data.linkedPersonIds,\n companyIds: data.linkedCompanyIds,\n customFields: data.customFields,\n ...Object.fromEntries(Object.entries(data.customFields ?? {}).map(([key, value]) => [`cf_${key}`, value])),\n }}\n onSubmit={handleFormSubmit}\n onCancel={() => { void loadData() }}\n onDelete={handleDelete}\n />\n </div>\n )\n\n const zone2Content = (\n <div className=\"rounded-[10px] border border-border bg-card px-5 py-5\">\n {(() => {\n const injected = injectedTabMap.get(activeTab)\n if (injected) return injected()\n\n if (activeTab === 'activities') {\n const activityEntitySelection = activityEntities.length > 1 ? (\n <div className=\"rounded-[10px] border border-border bg-muted/20 px-5 py-5\">\n <label htmlFor=\"deal-activity-entity\" className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.selectEntityLabel', 'Choose customer record')}\n </label>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t(\n 'customers.deals.detail.activities.selectEntityDescription',\n 'Pick the person or company that should own new deal activities and follow-ups.',\n )}\n </div>\n <select\n id=\"deal-activity-entity\"\n aria-label={t('customers.deals.detail.activities.selectEntityLabel', 'Choose customer record')}\n className=\"mt-4 h-9 w-full rounded border border-muted-foreground/40 bg-background px-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary\"\n value={selectedActivityEntityId ?? ''}\n onChange={(event) => setSelectedActivityEntityId(event.target.value || null)}\n >\n <option value=\"\">\n {t('customers.deals.detail.activities.selectEntityPlaceholder', 'Select a person or company')}\n </option>\n {activityEntities.map((entry) => (\n <option key={entry.id} value={entry.id}>\n {entry.label}\n </option>\n ))}\n </select>\n </div>\n ) : null\n\n return (\n <div className=\"space-y-4\">\n {activityEntities.length > 1 ? activityEntitySelection : null}\n {activityEntities.length === 0 ? (\n <div className=\"rounded-[10px] border border-border bg-muted/20 px-5 py-5\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.deals.detail.activities.linkEntityTitle', 'Link a person or company first')}\n </div>\n <div className=\"mt-1 text-sm text-muted-foreground\">\n {t('customers.deals.detail.activities.linkEntityDescription', 'Activities on a deal still need a customer record for timeline ownership.')}\n </div>\n <div className=\"mt-4 flex gap-2\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => handleTabChange('people')}>\n {t('customers.deals.detail.tabs.people', 'People')}\n </Button>\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={() => handleTabChange('companies')}>\n {t('customers.deals.detail.tabs.companies', 'Companies')}\n </Button>\n </div>\n </div>\n ) : selectedActivityEntity ? (\n <InlineActivityComposer\n entityType={selectedActivityEntity.kind}\n entityId={selectedActivityEntity.id}\n dealId={data.deal.id}\n onActivityCreated={() => { void handleActivityCreated() }}\n runGuardedMutation={runMutationWithContext}\n onScheduleRequested={openSchedule}\n />\n ) : (\n <EmptyState\n size=\"sm\"\n icon={<UserSearch className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={t('customers.deals.detail.activities.selectEntityRequiredTitle', 'Choose a person or company to continue')}\n description={t(\n 'customers.deals.detail.activities.selectEntityRequiredDescription',\n 'Select the customer record that should receive new deal activities before logging or scheduling anything.',\n )}\n />\n )}\n <PlannedActivitiesSection\n activities={plannedActivities}\n onComplete={(interactionId) => { void handleMarkDone(interactionId) }}\n onSchedule={selectedActivityEntity ? openSchedule : undefined}\n onEdit={handleEditActivity}\n onCancel={(interactionId) => { void handleCancelActivity(interactionId) }}\n />\n {selectedActivityEntity ? (\n <ActivitiesSection\n entityId={selectedActivityEntity.id}\n entityName={selectedActivityEntity.label}\n dealId={data.deal.id}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n defaultEntityId={selectedActivityEntity.id}\n addActionLabel={t('customers.deals.detail.activitiesAdd', 'Log activity')}\n emptyState={{\n title: t('customers.deals.detail.activitiesEmptyTitle', 'No activities yet'),\n actionLabel: t('customers.deals.detail.activitiesEmptyAction', 'Log activity'),\n }}\n runGuardedMutation={runMutationWithContext}\n onDataRefresh={() => { void handleActivityCreated() }}\n refreshKey={activityRefreshKey}\n onEditActivity={handleEditActivity}\n />\n ) : null}\n </div>\n )\n }\n\n if (activeTab === 'people') {\n return (\n <DealLinkedEntitiesTab\n entityLabel={t('customers.deals.detail.tabs.peopleSingular', 'Person')}\n entityLabelPlural={t('customers.deals.detail.tabs.people', 'People')}\n manageLabel={t('customers.deals.detail.peopleEditorTitle', 'Manage linked people')}\n searchPlaceholder={t('customers.deals.detail.peopleSearch', 'Search linked people\u2026')}\n linkedItems={data.people}\n linkedCount={data.counts.people}\n selectedIds={peopleEditorIds}\n disabled={peopleSaving || isSaving}\n savePending={peopleSaving}\n hrefBuilder={(personId) => `/backend/customers/people-v2/${encodeURIComponent(personId)}`}\n onSaveSelection={(next) => handlePeopleAssociationsChange(next)}\n loadLinkedPage={loadLinkedPeoplePage}\n searchEntities={searchPeoplePage}\n fetchEntitiesByIds={fetchPeopleByIds}\n icon={<Users className=\"size-4\" />}\n />\n )\n }\n\n if (activeTab === 'companies') {\n return (\n <DealLinkedEntitiesTab\n entityLabel={t('customers.deals.detail.tabs.companySingular', 'Company')}\n entityLabelPlural={t('customers.deals.detail.tabs.companies', 'Companies')}\n manageLabel={t('customers.deals.detail.companiesEditorTitle', 'Manage linked companies')}\n searchPlaceholder={t('customers.deals.detail.companiesSearch', 'Search linked companies\u2026')}\n linkedItems={data.companies}\n linkedCount={data.counts.companies}\n selectedIds={companiesEditorIds}\n disabled={companiesSaving || isSaving}\n savePending={companiesSaving}\n hrefBuilder={(companyId) => `/backend/customers/companies-v2/${encodeURIComponent(companyId)}`}\n onSaveSelection={(next) => handleCompaniesAssociationsChange(next)}\n loadLinkedPage={loadLinkedCompaniesPage}\n searchEntities={searchCompaniesPage}\n fetchEntitiesByIds={fetchCompaniesByIds}\n icon={<Building2 className=\"size-4\" />}\n />\n )\n }\n\n if (activeTab === 'notes') {\n return (\n <NotesSection\n entityId={null}\n dealId={data.deal.id}\n dealOptions={dealOptions}\n entityOptions={entityOptions}\n emptyLabel={t('customers.deals.detail.notesEmpty', 'No notes yet.')}\n viewerUserId={data.viewer?.userId ?? null}\n viewerName={data.viewer?.name ?? null}\n viewerEmail={data.viewer?.email ?? null}\n addActionLabel={t('customers.deals.detail.notesAdd', 'Add note')}\n emptyState={{\n title: t('customers.deals.detail.notesEmptyTitle', 'Keep everyone in the loop'),\n actionLabel: t('customers.deals.detail.notesEmptyAction', 'Add a note'),\n }}\n translator={detailTranslator}\n dataAdapter={notesAdapter}\n renderIcon={renderDictionaryIcon}\n renderColor={renderDictionaryColor}\n iconSuggestions={ICON_SUGGESTIONS}\n readMarkdownPreference={readMarkdownPreferenceCookie}\n writeMarkdownPreference={writeMarkdownPreferenceCookie}\n />\n )\n }\n\n if (activeTab === 'files') {\n return (\n <AttachmentsSection\n entityId={E.customers.customer_deal}\n recordId={data.deal.id}\n title={t('customers.deals.detail.tabs.files', 'Files')}\n description={t('customers.deals.detail.files.subtitle', 'Upload and manage files linked to this deal.')}\n />\n )\n }\n\n if (activeTab === 'changelog') {\n return <ChangelogTab entityId={data.deal.id} entityType=\"deal\" />\n }\n\n return null\n })()}\n </div>\n )\n\n return (\n <Page>\n <PageBody>\n <div className=\"space-y-6\">\n <InjectionSpot spotId=\"detail:customers.deal:header\" context={injectionContext} data={data} />\n\n <DealDetailHeader\n deal={data.deal}\n owner={data.owner}\n people={data.people}\n companies={data.companies}\n pipelineName={currentPipelineName}\n stageOptions={data.pipelineStages}\n currentStageId={data.deal.pipelineStageId}\n onStageChange={handleStageChange}\n isStageSaving={isStageSaving}\n onSave={handleHeaderSave}\n onDelete={handleDelete}\n isDirty={isDirty}\n isSaving={isSaving}\n />\n\n <InjectionSpot spotId=\"detail:customers.deal:status-badges\" context={injectionContext} data={data} />\n\n <PipelineStepper\n stages={data.pipelineStages}\n transitions={data.stageTransitions}\n currentStageId={data.deal.pipelineStageId}\n pipelineName={currentPipelineName}\n closureOutcome={data.deal.closureOutcome}\n footer={data.deal.closureOutcome ? null : (\n <DealClosureActionBar\n embedded\n closureOutcome={data.deal.closureOutcome}\n onWon={() => { void handleWon() }}\n onLost={openLostDialog}\n />\n )}\n />\n\n <DealDetailTabs\n activeTab={activeTab}\n onTabChange={handleTabChange}\n injectedTabs={injectedTabs.map((tab) => ({ id: tab.id, label: tab.label }))}\n peopleCount={data.counts.people}\n companiesCount={data.counts.companies}\n >\n <CollapsibleZoneLayout\n pageType=\"deal-detail-v3\"\n entityName={dealName}\n isDirty={isDirty}\n zone1DefaultWidth=\"540px\"\n zone1={zone1Content}\n zone2={zone2Content}\n />\n </DealDetailTabs>\n\n <InjectionSpot spotId=\"detail:customers.deal:footer\" context={injectionContext} data={data} />\n </div>\n\n {ConfirmDialogElement}\n\n {selectedActivityEntity ? (\n <ScheduleActivityDialog\n open={scheduleDialogOpen}\n onClose={closeSchedule}\n entityId={selectedActivityEntity.id}\n dealId={data.deal.id}\n entityType={selectedActivityEntity.kind}\n entityName={selectedActivityEntity.label}\n companyName={selectedActivityEntity.kind === 'company' ? selectedActivityEntity.label : data.companies[0]?.label ?? null}\n onActivityCreated={() => { void handleActivityCreated() }}\n editData={scheduleEditData}\n />\n ) : null}\n\n <ConfirmDealLostDialog\n open={lostDialogOpen}\n onClose={closeLostDialog}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n dealValue={amountLabel}\n companyName={data.companies[0]?.label ?? null}\n onConfirm={handleLostConfirm}\n />\n\n <DealWonPopup\n open={wonPopupOpen}\n onClose={closeWonPopup}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n stats={wonStats}\n onViewDashboard={handleViewDashboard}\n onBackToPipeline={handleBackToPipeline}\n />\n\n <DealLostSummaryDialog\n open={lostPopupOpen}\n onClose={closeLostPopup}\n dealTitle={data.deal.title || t('customers.deals.detail.untitled', 'Untitled deal')}\n lossNotes={data.deal.lossNotes}\n stats={lostStats}\n onBackToPipeline={handleBackToPipeline}\n onScheduleFollowUp={selectedActivityEntity ? handleScheduleLostFollowUp : undefined}\n />\n </PageBody>\n </Page>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["id"]
7
7
  }