@open-mercato/core 0.6.5-develop.5337.1.534b781eac → 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 (350) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +1 -1
  3. package/dist/bootstrap.js +46 -6
  4. package/dist/bootstrap.js.map +2 -2
  5. package/dist/generated/entities/organization/index.js +2 -0
  6. package/dist/generated/entities/organization/index.js.map +2 -2
  7. package/dist/generated/entity-fields-registry.js +1 -0
  8. package/dist/generated/entity-fields-registry.js.map +2 -2
  9. package/dist/helpers/integration/crmFixtures.js +4 -0
  10. package/dist/helpers/integration/crmFixtures.js.map +2 -2
  11. package/dist/modules/attachments/api/library/route.js +2 -2
  12. package/dist/modules/attachments/api/library/route.js.map +2 -2
  13. package/dist/modules/attachments/api/route.js +2 -0
  14. package/dist/modules/attachments/api/route.js.map +2 -2
  15. package/dist/modules/attachments/components/AttachmentContentPreview.js +9 -5
  16. package/dist/modules/attachments/components/AttachmentContentPreview.js.map +2 -2
  17. package/dist/modules/attachments/lib/access.js +18 -0
  18. package/dist/modules/attachments/lib/access.js.map +2 -2
  19. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +3 -2
  20. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
  21. package/dist/modules/audit_logs/data/entities.js +2 -1
  22. package/dist/modules/audit_logs/data/entities.js.map +2 -2
  23. package/dist/modules/audit_logs/migrations/Migration20260611104500.js +13 -0
  24. package/dist/modules/audit_logs/migrations/Migration20260611104500.js.map +7 -0
  25. package/dist/modules/audit_logs/services/accessLogService.js +10 -0
  26. package/dist/modules/audit_logs/services/accessLogService.js.map +2 -2
  27. package/dist/modules/auth/api/admin/nav.js +9 -0
  28. package/dist/modules/auth/api/admin/nav.js.map +2 -2
  29. package/dist/modules/auth/api/login.js +4 -13
  30. package/dist/modules/auth/api/login.js.map +2 -2
  31. package/dist/modules/auth/commands/users.js +20 -14
  32. package/dist/modules/auth/commands/users.js.map +2 -2
  33. package/dist/modules/auth/data/entities.js +4 -2
  34. package/dist/modules/auth/data/entities.js.map +2 -2
  35. package/dist/modules/auth/lib/backendChrome.js +35 -2
  36. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  37. package/dist/modules/auth/lib/consentIntegrity.js +3 -3
  38. package/dist/modules/auth/lib/consentIntegrity.js.map +2 -2
  39. package/dist/modules/auth/migrations/Migration20260610120000.js +30 -0
  40. package/dist/modules/auth/migrations/Migration20260610120000.js.map +7 -0
  41. package/dist/modules/auth/migrations/Migration20260611103000.js +15 -0
  42. package/dist/modules/auth/migrations/Migration20260611103000.js.map +7 -0
  43. package/dist/modules/auth/services/authService.js +5 -3
  44. package/dist/modules/auth/services/authService.js.map +2 -2
  45. package/dist/modules/auth/services/rbacService.js +3 -2
  46. package/dist/modules/auth/services/rbacService.js.map +2 -2
  47. package/dist/modules/catalog/ai-tools/configuration-pack.js.map +1 -1
  48. package/dist/modules/catalog/ai-tools/prices-offers-pack.js.map +1 -1
  49. package/dist/modules/catalog/ai-tools/products-pack.js.map +1 -1
  50. package/dist/modules/catalog/ai-tools/variants-pack.js.map +1 -1
  51. package/dist/modules/communication_channels/data/entities.js.map +1 -1
  52. package/dist/modules/communication_channels/encryption.js.map +1 -1
  53. package/dist/modules/communication_channels/lib/thread-matcher.js.map +1 -1
  54. package/dist/modules/communication_channels/lib/thread-token.js.map +1 -1
  55. package/dist/modules/currencies/api/currencies/route.js +4 -3
  56. package/dist/modules/currencies/api/currencies/route.js.map +2 -2
  57. package/dist/modules/customer_accounts/api/admin/roles.js +2 -1
  58. package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
  59. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js +0 -3
  60. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.js.map +2 -2
  61. package/dist/modules/customer_accounts/events.js +1 -1
  62. package/dist/modules/customer_accounts/events.js.map +1 -1
  63. package/dist/modules/customer_accounts/lib/resolveTenantContext.js.map +1 -1
  64. package/dist/modules/customers/acl.js +1 -1
  65. package/dist/modules/customers/acl.js.map +1 -1
  66. package/dist/modules/customers/ai-tools/companies-pack.js.map +1 -1
  67. package/dist/modules/customers/ai-tools/deals-pack.js.map +1 -1
  68. package/dist/modules/customers/ai-tools/people-pack.js.map +1 -1
  69. package/dist/modules/customers/api/companies/route.js +4 -4
  70. package/dist/modules/customers/api/companies/route.js.map +2 -2
  71. package/dist/modules/customers/api/deals/route.js +43 -2
  72. package/dist/modules/customers/api/deals/route.js.map +2 -2
  73. package/dist/modules/customers/api/deals/summary/route.js +402 -0
  74. package/dist/modules/customers/api/deals/summary/route.js.map +7 -0
  75. package/dist/modules/customers/api/people/route.js +4 -4
  76. package/dist/modules/customers/api/people/route.js.map +2 -2
  77. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +16 -5
  78. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
  79. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +22 -5
  80. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +2 -2
  81. package/dist/modules/customers/backend/customers/deals/[id]/page.js +12 -2
  82. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  83. package/dist/modules/customers/backend/customers/deals/page.js +221 -56
  84. package/dist/modules/customers/backend/customers/deals/page.js.map +3 -3
  85. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +1 -1
  86. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  87. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +18 -0
  88. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  89. package/dist/modules/customers/cli.js +15 -9
  90. package/dist/modules/customers/cli.js.map +2 -2
  91. package/dist/modules/customers/commands/addresses.js +5 -5
  92. package/dist/modules/customers/commands/addresses.js.map +2 -2
  93. package/dist/modules/customers/commands/comments.js +5 -5
  94. package/dist/modules/customers/commands/comments.js.map +2 -2
  95. package/dist/modules/customers/commands/deals.js +2 -2
  96. package/dist/modules/customers/commands/deals.js.map +2 -2
  97. package/dist/modules/customers/commands/entity-roles.js +2 -1
  98. package/dist/modules/customers/commands/entity-roles.js.map +2 -2
  99. package/dist/modules/customers/commands/interactions.js +8 -5
  100. package/dist/modules/customers/commands/interactions.js.map +2 -2
  101. package/dist/modules/customers/commands/shared.js +21 -6
  102. package/dist/modules/customers/commands/shared.js.map +2 -2
  103. package/dist/modules/customers/commands/tags.js +3 -3
  104. package/dist/modules/customers/commands/tags.js.map +2 -2
  105. package/dist/modules/customers/components/DealsKpiStrip.js +282 -0
  106. package/dist/modules/customers/components/DealsKpiStrip.js.map +7 -0
  107. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +0 -1
  108. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
  109. package/dist/modules/customers/components/detail/DealForm.js +100 -17
  110. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  111. package/dist/modules/customers/components/detail/PersonDetailTabs.js +11 -3
  112. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +2 -2
  113. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +1 -2
  114. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  115. package/dist/modules/customers/components/detail/assignableStaff.js +21 -8
  116. package/dist/modules/customers/components/detail/assignableStaff.js.map +2 -2
  117. package/dist/modules/customers/components/kpi/PipelineStageBar.js +63 -0
  118. package/dist/modules/customers/components/kpi/PipelineStageBar.js.map +7 -0
  119. package/dist/modules/customers/lib/dealsMetrics.js +82 -0
  120. package/dist/modules/customers/lib/dealsMetrics.js.map +7 -0
  121. package/dist/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.js.map +1 -1
  122. package/dist/modules/data_sync/api/run.js +1 -1
  123. package/dist/modules/data_sync/api/run.js.map +2 -2
  124. package/dist/modules/directory/api/organization-branding/route.js +214 -0
  125. package/dist/modules/directory/api/organization-branding/route.js.map +7 -0
  126. package/dist/modules/directory/api/organizations/route.js +7 -0
  127. package/dist/modules/directory/api/organizations/route.js.map +3 -3
  128. package/dist/modules/directory/backend/directory/branding/page.js +214 -0
  129. package/dist/modules/directory/backend/directory/branding/page.js.map +7 -0
  130. package/dist/modules/directory/backend/directory/branding/page.meta.js +26 -0
  131. package/dist/modules/directory/backend/directory/branding/page.meta.js.map +7 -0
  132. package/dist/modules/directory/commands/organizations.js +8 -1
  133. package/dist/modules/directory/commands/organizations.js.map +2 -2
  134. package/dist/modules/directory/data/entities.js +3 -0
  135. package/dist/modules/directory/data/entities.js.map +2 -2
  136. package/dist/modules/directory/data/validators.js +9 -0
  137. package/dist/modules/directory/data/validators.js.map +2 -2
  138. package/dist/modules/directory/migrations/Migration20260607222259_directory.js +13 -0
  139. package/dist/modules/directory/migrations/Migration20260607222259_directory.js.map +7 -0
  140. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js +2 -1
  141. package/dist/modules/directory/subscribers/invalidateOrgScopeCache.js.map +2 -2
  142. package/dist/modules/directory/utils/organizationScope.js +59 -27
  143. package/dist/modules/directory/utils/organizationScope.js.map +2 -2
  144. package/dist/modules/entities/api/definitions.batch.js +2 -1
  145. package/dist/modules/entities/api/definitions.batch.js.map +2 -2
  146. package/dist/modules/entities/api/entities.js +7 -0
  147. package/dist/modules/entities/api/entities.js.map +2 -2
  148. package/dist/modules/entities/api/records.js +26 -15
  149. package/dist/modules/entities/api/records.js.map +2 -2
  150. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +14 -0
  151. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
  152. package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js +14 -0
  153. package/dist/modules/entities/backend/entities/user/[entityId]/records/create/page.js.map +2 -2
  154. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +12 -0
  155. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  156. package/dist/modules/entities/components/useRecordsEntityGuard.js +30 -0
  157. package/dist/modules/entities/components/useRecordsEntityGuard.js.map +7 -0
  158. package/dist/modules/payment_gateways/api/transactions/route.js +2 -4
  159. package/dist/modules/payment_gateways/api/transactions/route.js.map +2 -2
  160. package/dist/modules/progress/api/jobs/[id]/route.js +7 -2
  161. package/dist/modules/progress/api/jobs/[id]/route.js.map +2 -2
  162. package/dist/modules/progress/api/jobs/route.js +1 -1
  163. package/dist/modules/progress/api/jobs/route.js.map +2 -2
  164. package/dist/modules/progress/lib/progressServiceImpl.js +8 -2
  165. package/dist/modules/progress/lib/progressServiceImpl.js.map +2 -2
  166. package/dist/modules/query_index/data/entities.js +2 -1
  167. package/dist/modules/query_index/data/entities.js.map +2 -2
  168. package/dist/modules/query_index/lib/engine.js +4 -2
  169. package/dist/modules/query_index/lib/engine.js.map +2 -2
  170. package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js +16 -0
  171. package/dist/modules/query_index/migrations/Migration20260611103000_query_index.js.map +7 -0
  172. package/dist/modules/resources/api/resources.js +2 -3
  173. package/dist/modules/resources/api/resources.js.map +2 -2
  174. package/dist/modules/sales/api/documents/factory.js +2 -2
  175. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  176. package/dist/modules/sales/commands/documents.js +7 -5
  177. package/dist/modules/sales/commands/documents.js.map +2 -2
  178. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +2 -1
  179. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  180. package/dist/modules/sales/components/documents/salesDocumentsColumns.js +10 -0
  181. package/dist/modules/sales/components/documents/salesDocumentsColumns.js.map +7 -0
  182. package/dist/modules/staff/api/team-members.js +9 -2
  183. package/dist/modules/staff/api/team-members.js.map +2 -2
  184. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +24 -1
  185. package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
  186. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +11 -6
  187. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  188. package/dist/modules/staff/commands/team-members.js +1 -1
  189. package/dist/modules/staff/commands/team-members.js.map +2 -2
  190. package/dist/modules/staff/components/TeamMemberForm.js +1 -1
  191. package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
  192. package/dist/modules/staff/lib/scheduleSwitch.js +23 -0
  193. package/dist/modules/staff/lib/scheduleSwitch.js.map +7 -0
  194. package/dist/modules/sync_excel/api/import/route.js +1 -1
  195. package/dist/modules/sync_excel/api/import/route.js.map +2 -2
  196. package/dist/modules/workflows/api/definitions/route.js +3 -2
  197. package/dist/modules/workflows/api/definitions/route.js.map +2 -2
  198. package/dist/modules/workflows/backend/definitions/create/page.js +1 -2
  199. package/dist/modules/workflows/backend/definitions/create/page.js.map +2 -2
  200. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +1 -2
  201. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  202. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -2
  203. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  204. package/dist/modules/workflows/components/NodeEditDialog.js +4 -13
  205. package/dist/modules/workflows/components/NodeEditDialog.js.map +2 -2
  206. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js +4 -13
  207. package/dist/modules/workflows/components/NodeEditDialogCrudForm.js.map +2 -2
  208. package/dist/modules/workflows/components/WorkflowGraphImpl.js +1 -4
  209. package/dist/modules/workflows/components/WorkflowGraphImpl.js.map +2 -2
  210. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js +2 -5
  211. package/dist/modules/workflows/components/fields/FormFieldArrayEditor.js.map +2 -2
  212. package/generated/entities/organization/index.ts +1 -0
  213. package/generated/entity-fields-registry.ts +1 -0
  214. package/package.json +11 -12
  215. package/src/bootstrap.ts +65 -7
  216. package/src/helpers/integration/crmFixtures.ts +21 -1
  217. package/src/modules/attachments/AGENTS.md +79 -0
  218. package/src/modules/attachments/api/library/route.ts +2 -2
  219. package/src/modules/attachments/api/route.ts +2 -0
  220. package/src/modules/attachments/components/AttachmentContentPreview.tsx +6 -6
  221. package/src/modules/attachments/lib/access.ts +36 -0
  222. package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +14 -2
  223. package/src/modules/audit_logs/data/entities.ts +1 -0
  224. package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +10 -0
  225. package/src/modules/audit_logs/migrations/Migration20260611104500.ts +13 -0
  226. package/src/modules/audit_logs/services/accessLogService.ts +15 -0
  227. package/src/modules/auth/api/admin/nav.ts +9 -0
  228. package/src/modules/auth/api/login.ts +13 -13
  229. package/src/modules/auth/commands/users.ts +32 -15
  230. package/src/modules/auth/data/entities.ts +13 -1
  231. package/src/modules/auth/i18n/de.json +0 -1
  232. package/src/modules/auth/i18n/en.json +0 -1
  233. package/src/modules/auth/i18n/es.json +0 -1
  234. package/src/modules/auth/i18n/pl.json +0 -1
  235. package/src/modules/auth/lib/backendChrome.tsx +37 -1
  236. package/src/modules/auth/lib/consentIntegrity.ts +6 -3
  237. package/src/modules/auth/migrations/.snapshot-open-mercato.json +20 -10
  238. package/src/modules/auth/migrations/Migration20260610120000.ts +53 -0
  239. package/src/modules/auth/migrations/Migration20260611103000.ts +21 -0
  240. package/src/modules/auth/services/authService.ts +24 -4
  241. package/src/modules/auth/services/rbacService.ts +11 -2
  242. package/src/modules/catalog/ai-tools/configuration-pack.ts +1 -1
  243. package/src/modules/catalog/ai-tools/prices-offers-pack.ts +1 -1
  244. package/src/modules/catalog/ai-tools/products-pack.ts +1 -1
  245. package/src/modules/catalog/ai-tools/variants-pack.ts +1 -1
  246. package/src/modules/communication_channels/data/entities.ts +2 -2
  247. package/src/modules/communication_channels/encryption.ts +1 -1
  248. package/src/modules/communication_channels/lib/adapter.ts +1 -1
  249. package/src/modules/communication_channels/lib/thread-matcher.ts +1 -1
  250. package/src/modules/communication_channels/lib/thread-token.ts +1 -1
  251. package/src/modules/currencies/api/currencies/route.ts +4 -3
  252. package/src/modules/customer_accounts/api/admin/roles.ts +2 -1
  253. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/components/Diagnostics.tsx +0 -3
  254. package/src/modules/customer_accounts/events.ts +1 -1
  255. package/src/modules/customer_accounts/lib/resolveTenantContext.ts +2 -2
  256. package/src/modules/customers/acl.ts +1 -1
  257. package/src/modules/customers/ai-tools/companies-pack.ts +1 -1
  258. package/src/modules/customers/ai-tools/deals-pack.ts +1 -1
  259. package/src/modules/customers/ai-tools/people-pack.ts +1 -1
  260. package/src/modules/customers/api/companies/route.ts +4 -4
  261. package/src/modules/customers/api/deals/route.ts +51 -2
  262. package/src/modules/customers/api/deals/summary/route.ts +496 -0
  263. package/src/modules/customers/api/people/route.ts +4 -4
  264. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +28 -6
  265. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +33 -6
  266. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +17 -2
  267. package/src/modules/customers/backend/customers/deals/page.tsx +254 -66
  268. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +1 -2
  269. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +18 -0
  270. package/src/modules/customers/cli.ts +15 -15
  271. package/src/modules/customers/commands/addresses.ts +5 -5
  272. package/src/modules/customers/commands/comments.ts +5 -5
  273. package/src/modules/customers/commands/deals.ts +2 -2
  274. package/src/modules/customers/commands/entity-roles.ts +2 -1
  275. package/src/modules/customers/commands/interactions.ts +8 -5
  276. package/src/modules/customers/commands/shared.ts +26 -4
  277. package/src/modules/customers/commands/tags.ts +3 -3
  278. package/src/modules/customers/components/DealsKpiStrip.tsx +389 -0
  279. package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +0 -1
  280. package/src/modules/customers/components/detail/DealForm.tsx +121 -19
  281. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +12 -2
  282. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +1 -2
  283. package/src/modules/customers/components/detail/assignableStaff.ts +32 -8
  284. package/src/modules/customers/components/kpi/PipelineStageBar.tsx +77 -0
  285. package/src/modules/customers/i18n/de.json +43 -0
  286. package/src/modules/customers/i18n/en.json +43 -0
  287. package/src/modules/customers/i18n/es.json +43 -0
  288. package/src/modules/customers/i18n/pl.json +43 -0
  289. package/src/modules/customers/lib/dealsMetrics.ts +159 -0
  290. package/src/modules/customers/migrations/Migration20260519120000_pipeline_stage_color_tones.ts +1 -1
  291. package/src/modules/data_sync/api/run.ts +1 -1
  292. package/src/modules/directory/api/organization-branding/route.ts +238 -0
  293. package/src/modules/directory/api/organizations/route.ts +7 -0
  294. package/src/modules/directory/backend/directory/branding/page.meta.ts +24 -0
  295. package/src/modules/directory/backend/directory/branding/page.tsx +248 -0
  296. package/src/modules/directory/commands/organizations.ts +9 -1
  297. package/src/modules/directory/data/entities.ts +3 -0
  298. package/src/modules/directory/data/validators.ts +12 -0
  299. package/src/modules/directory/i18n/de.json +21 -0
  300. package/src/modules/directory/i18n/en.json +21 -0
  301. package/src/modules/directory/i18n/es.json +21 -0
  302. package/src/modules/directory/i18n/pl.json +21 -0
  303. package/src/modules/directory/migrations/.snapshot-open-mercato.json +40 -0
  304. package/src/modules/directory/migrations/Migration20260607222259_directory.ts +13 -0
  305. package/src/modules/directory/subscribers/invalidateOrgScopeCache.ts +3 -1
  306. package/src/modules/directory/utils/organizationScope.ts +85 -30
  307. package/src/modules/entities/api/definitions.batch.ts +11 -7
  308. package/src/modules/entities/api/entities.ts +11 -0
  309. package/src/modules/entities/api/records.ts +46 -25
  310. package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +15 -0
  311. package/src/modules/entities/backend/entities/user/[entityId]/records/create/page.tsx +15 -0
  312. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +23 -0
  313. package/src/modules/entities/components/useRecordsEntityGuard.ts +41 -0
  314. package/src/modules/entities/i18n/de.json +1 -0
  315. package/src/modules/entities/i18n/en.json +1 -0
  316. package/src/modules/entities/i18n/es.json +1 -0
  317. package/src/modules/entities/i18n/pl.json +1 -0
  318. package/src/modules/payment_gateways/api/transactions/route.ts +2 -5
  319. package/src/modules/progress/api/jobs/[id]/route.ts +6 -1
  320. package/src/modules/progress/api/jobs/route.ts +1 -1
  321. package/src/modules/progress/lib/progressServiceImpl.ts +7 -1
  322. package/src/modules/query_index/data/entities.ts +1 -0
  323. package/src/modules/query_index/lib/engine.ts +11 -5
  324. package/src/modules/query_index/migrations/.snapshot-open-mercato.json +11 -0
  325. package/src/modules/query_index/migrations/Migration20260611103000_query_index.ts +29 -0
  326. package/src/modules/resources/api/resources.ts +2 -3
  327. package/src/modules/sales/api/documents/factory.ts +2 -2
  328. package/src/modules/sales/commands/documents.ts +7 -5
  329. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +2 -1
  330. package/src/modules/sales/components/documents/salesDocumentsColumns.ts +6 -0
  331. package/src/modules/staff/AGENTS.md +1 -1
  332. package/src/modules/staff/api/team-members.ts +9 -2
  333. package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +31 -1
  334. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +18 -8
  335. package/src/modules/staff/commands/team-members.ts +5 -2
  336. package/src/modules/staff/components/TeamMemberForm.tsx +4 -1
  337. package/src/modules/staff/i18n/de.json +1 -0
  338. package/src/modules/staff/i18n/en.json +1 -0
  339. package/src/modules/staff/i18n/es.json +1 -0
  340. package/src/modules/staff/i18n/pl.json +1 -0
  341. package/src/modules/staff/lib/scheduleSwitch.ts +46 -0
  342. package/src/modules/sync_excel/api/import/route.ts +1 -1
  343. package/src/modules/workflows/api/definitions/route.ts +3 -2
  344. package/src/modules/workflows/backend/definitions/create/page.tsx +1 -2
  345. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +1 -2
  346. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -2
  347. package/src/modules/workflows/components/NodeEditDialog.tsx +1 -4
  348. package/src/modules/workflows/components/NodeEditDialogCrudForm.tsx +4 -7
  349. package/src/modules/workflows/components/WorkflowGraphImpl.tsx +1 -2
  350. 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
+ }
@@ -18,7 +18,7 @@ import {
18
18
  withScopedPayload
19
19
  } from "../utils.js";
20
20
  import { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries, splitCustomFieldPayload } from "@open-mercato/shared/lib/crud/custom-fields";
21
- import { escapeLikePattern } from "@open-mercato/shared/lib/db/escapeLikePattern";
21
+ import { buildIlikeTerm } from "@open-mercato/shared/lib/db/buildIlikeTerm";
22
22
  import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
23
23
  import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
24
24
  import { consumeAdvancedFilterState, mergeAdvancedFilterTree } from "@open-mercato/shared/lib/crud/advanced-filter-integration";
@@ -163,7 +163,7 @@ const crud = makeCrudRoute({
163
163
  if (matchingIds !== null && matchingIds.length > 0) {
164
164
  applyEntityIdRestriction(filters, matchingIds);
165
165
  } else {
166
- const searchPattern = `%${escapeLikePattern(query.search)}%`;
166
+ const searchPattern = buildIlikeTerm(query.search);
167
167
  filters.$or = [
168
168
  { display_name: { $ilike: searchPattern } },
169
169
  { primary_email: { $ilike: searchPattern } },
@@ -179,9 +179,9 @@ const crud = makeCrudRoute({
179
179
  if (email) {
180
180
  filters.primary_email = { $eq: email };
181
181
  } else if (emailStartsWith) {
182
- filters.primary_email = { $ilike: `${escapeLikePattern(emailStartsWith)}%` };
182
+ filters.primary_email = { $ilike: buildIlikeTerm(emailStartsWith, "startsWith") };
183
183
  } else if (emailContains) {
184
- filters.primary_email = { $ilike: `%${escapeLikePattern(emailContains)}%` };
184
+ filters.primary_email = { $ilike: buildIlikeTerm(emailContains) };
185
185
  }
186
186
  if (query.status) {
187
187
  filters.status = { $eq: query.status };
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/api/people/route.ts"],
4
- "sourcesContent": ["import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n CustomerDealPersonLink,\n CustomerEntity,\n CustomerPersonCompanyLink,\n CustomerPersonProfile,\n} from '../../data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { personCreateSchema, personUpdateSchema } from '../../data/validators'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n applyEntityIdExclusion,\n applyEntityIdRestriction,\n findMatchingEntityIdsWithQueryEngine,\n findMatchingEntityIdsBySearchTokensAcrossSources,\n withScopedPayload,\n} from '../utils'\nimport { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries, splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { escapeLikePattern } from '@open-mercato/shared/lib/db/escapeLikePattern'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { consumeAdvancedFilterState, mergeAdvancedFilterTree } from '@open-mercato/shared/lib/crud/advanced-filter-integration'\nimport {\n createCustomersCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport {\n filterActivePersonCompanyLinks,\n withActiveCustomerPersonCompanyLinkFilter,\n withCustomerPersonCompanyLinkScope,\n withScopedCustomerDealLinkWhere,\n} from '../../lib/personCompanyLinkTable'\nimport { normalizeProfilePayload } from './payload'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n email: z.string().optional(),\n emailStartsWith: z.string().optional(),\n emailContains: z.string().optional(),\n status: z.string().optional(),\n lifecycleStage: z.string().optional(),\n source: z.string().optional(),\n hasEmail: z.string().optional(),\n hasPhone: z.string().optional(),\n hasNextInteraction: z.string().optional(),\n createdFrom: z.string().optional(),\n createdTo: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n id: z.string().uuid().optional(),\n tagIds: z.string().optional(),\n tagIdsEmpty: z.string().optional(),\n excludeIds: z.string().optional(),\n excludeLinkedCompanyId: z.string().uuid().optional(),\n excludeLinkedDealId: z.string().uuid().optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.people.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: CustomerEntity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n enrichers: { entityId: 'customers.person' },\n indexer: { entityType: E.customers.customer_entity },\n list: {\n schema: listSchema,\n entityId: E.customers.customer_entity,\n fields: [\n 'id',\n 'display_name',\n 'description',\n 'owner_user_id',\n 'primary_email',\n 'primary_phone',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'next_interaction_at',\n 'next_interaction_name',\n 'next_interaction_ref_id',\n 'next_interaction_icon',\n 'next_interaction_color',\n 'organization_id',\n 'tenant_id',\n 'kind',\n 'created_at',\n 'updated_at',\n ],\n sortFieldMap: {\n name: 'display_name',\n email: 'primary_email',\n primaryEmail: 'primary_email',\n status: 'status',\n lifecycleStage: 'lifecycle_stage',\n source: 'source',\n nextInteractionAt: 'next_interaction_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n buildFilters: async (query, ctx) => {\n const advancedFilterTree = consumeAdvancedFilterState(query)\n const filters: Record<string, unknown> = { kind: { $eq: 'person' } }\n if (query.id) filters.id = { $eq: query.id }\n if (query.search) {\n const matchingIds = ctx\n ? await findMatchingEntityIdsBySearchTokensAcrossSources({\n ctx,\n query: query.search,\n sources: [\n {\n entityType: E.customers.customer_entity,\n fields: [\n 'display_name',\n 'primary_email',\n 'primary_phone',\n 'description',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'next_interaction_name',\n ],\n },\n {\n entityType: E.customers.customer_person_profile,\n fields: [\n 'display_name',\n 'primary_email',\n 'primary_phone',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'first_name',\n 'last_name',\n 'preferred_name',\n 'job_title',\n 'department',\n 'seniority',\n 'timezone',\n 'linked_in_url',\n 'twitter_url',\n ],\n mapToEntityIds: {\n table: 'customer_people',\n targetColumn: 'entity_id',\n },\n },\n ],\n })\n : null\n if (matchingIds !== null && matchingIds.length > 0) {\n applyEntityIdRestriction(filters, matchingIds)\n } else {\n const searchPattern = `%${escapeLikePattern(query.search)}%`\n filters.$or = [\n { display_name: { $ilike: searchPattern } },\n { primary_email: { $ilike: searchPattern } },\n { primary_phone: { $ilike: searchPattern } },\n { description: { $ilike: searchPattern } },\n { next_interaction_name: { $ilike: searchPattern } },\n ]\n }\n }\n const email = typeof query.email === 'string' ? query.email.trim().toLowerCase() : ''\n const emailStartsWith = typeof query.emailStartsWith === 'string' ? query.emailStartsWith.trim().toLowerCase() : ''\n const emailContains = typeof query.emailContains === 'string' ? query.emailContains.trim().toLowerCase() : ''\n if (email) {\n filters.primary_email = { $eq: email }\n } else if (emailStartsWith) {\n filters.primary_email = { $ilike: `${escapeLikePattern(emailStartsWith)}%` }\n } else if (emailContains) {\n filters.primary_email = { $ilike: `%${escapeLikePattern(emailContains)}%` }\n }\n if (query.status) {\n filters.status = { $eq: query.status }\n }\n if (query.lifecycleStage) {\n filters.lifecycle_stage = { $eq: query.lifecycleStage }\n }\n if (query.source) {\n filters.source = { $eq: query.source }\n }\n const tagIdsRaw = typeof query.tagIds === 'string' ? query.tagIds : ''\n const tagIds = tagIdsRaw\n .split(',')\n .map((value: string) => value.trim())\n .filter((value: string) => value.length > 0)\n const tagIdsEmpty = parseBooleanToken(query.tagIdsEmpty) === true\n if (tagIdsEmpty) {\n filters.id = { $eq: '00000000-0000-0000-0000-000000000000' }\n } else if (tagIds.length > 0) {\n filters['tag_assignments.tag_id'] = { $in: tagIds }\n }\n const excludedIds = new Set<string>()\n const excludeIdsRaw = typeof query.excludeIds === 'string' ? query.excludeIds : ''\n excludeIdsRaw\n .split(',')\n .map((value: string) => value.trim())\n .filter((value: string) => value.length > 0)\n .forEach((value: string) => excludedIds.add(value))\n if (ctx && query.excludeLinkedCompanyId) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const linkWhere = await withActiveCustomerPersonCompanyLinkFilter(\n em,\n withCustomerPersonCompanyLinkScope({ company: query.excludeLinkedCompanyId }, decryptionScope),\n 'customers.people.GET',\n )\n const links = filterActivePersonCompanyLinks(\n await findWithDecryption(\n em,\n CustomerPersonCompanyLink,\n linkWhere,\n { populate: ['person'] },\n decryptionScope,\n ),\n )\n links.forEach((link) => {\n const personId = link.person?.id\n if (typeof personId === 'string' && personId.length > 0) excludedIds.add(personId)\n })\n } catch (err) {\n console.warn('[customers.people.list] exclusion lookup failed; falling back to base result set', err)\n }\n }\n if (ctx && query.excludeLinkedDealId) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const links = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n withScopedCustomerDealLinkWhere(query.excludeLinkedDealId, decryptionScope),\n { populate: ['person'] },\n decryptionScope,\n )\n links.forEach((link) => {\n const personId = link.person?.id\n if (typeof personId === 'string' && personId.length > 0) excludedIds.add(personId)\n })\n } catch (err) {\n console.warn('[customers.people.list] exclusion lookup failed; falling back to base result set', err)\n }\n }\n applyEntityIdExclusion(filters, Array.from(excludedIds))\n const hasEmail = parseBooleanToken(query.hasEmail)\n if (!email && !emailStartsWith && !emailContains && hasEmail !== null) {\n filters.primary_email = { $exists: hasEmail }\n }\n const hasPhone = parseBooleanToken(query.hasPhone)\n if (hasPhone !== null) {\n filters.primary_phone = { $exists: hasPhone }\n }\n const hasNextInteraction = parseBooleanToken(query.hasNextInteraction)\n if (hasNextInteraction !== null) {\n filters.next_interaction_at = { $exists: hasNextInteraction }\n }\n const createdRange: Record<string, Date> = {}\n if (query.createdFrom) {\n const from = new Date(query.createdFrom)\n if (!Number.isNaN(from.getTime())) createdRange.$gte = from\n }\n if (query.createdTo) {\n const to = new Date(query.createdTo)\n if (!Number.isNaN(to.getTime())) createdRange.$lte = to\n }\n if (Object.keys(createdRange).length) {\n filters.created_at = createdRange\n }\n if (ctx) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityIds: [E.customers.customer_entity, E.customers.customer_person_profile],\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch (err) {\n console.warn('[customers.people.list] custom field filter resolution failed; falling back to base filters', err)\n }\n }\n if (ctx && advancedFilterTree) {\n const advancedFilters = mergeAdvancedFilterTree({ ...filters }, advancedFilterTree)\n const matchedIds = await findMatchingEntityIdsWithQueryEngine({\n ctx,\n entityId: E.customers.customer_entity,\n filters: advancedFilters,\n customFieldSources: [\n {\n entityId: E.customers.customer_person_profile,\n table: 'customer_people',\n alias: 'person_profile',\n recordIdColumn: 'id',\n join: { fromField: 'id', toField: 'entity_id' },\n },\n ],\n joins: [\n {\n alias: 'tag_assignments',\n table: 'customer_tag_assignments',\n from: { field: 'id' },\n to: { field: 'entity_id' },\n type: 'left',\n },\n ],\n })\n applyEntityIdRestriction(filters, matchedIds)\n }\n return filters\n },\n customFieldSources: [\n {\n entityId: E.customers.customer_person_profile,\n table: 'customer_people',\n alias: 'person_profile',\n recordIdColumn: 'id',\n join: { fromField: 'id', toField: 'entity_id' },\n },\n ],\n joins: [\n {\n alias: 'tag_assignments',\n table: 'customer_tag_assignments',\n from: { field: 'id' },\n to: { field: 'entity_id' },\n type: 'left',\n },\n ],\n transformItem: (item) => {\n if (!item || typeof item !== 'object') return item\n const record = item as Record<string, unknown>\n const normalized: Record<string, unknown> = { ...record }\n delete normalized.kind\n const cfEntries = extractAllCustomFieldEntries(record)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) {\n delete normalized[key]\n }\n }\n return { ...normalized, ...cfEntries }\n },\n },\n actions: {\n create: {\n commandId: 'customers.people.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const { base, custom } = splitCustomFieldPayload(scoped)\n const parsed = personCreateSchema.parse(base)\n return Object.keys(custom).length ? { ...parsed, customFields: custom } : parsed\n },\n response: ({ result }) => ({\n id: result?.entityId ?? result?.id ?? null,\n personId: result?.personId ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: 'customers.people.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const normalized = normalizeProfilePayload(scoped, translate)\n const { base, custom } = splitCustomFieldPayload(normalized)\n const parsed = personUpdateSchema.parse(base)\n return Object.keys(custom).length ? { ...parsed, customFields: custom } : parsed\n },\n // Return the freshly-bumped updatedAt so inline-edit detail pages can refresh\n // their optimistic-lock token between sequential saves (#2055).\n response: (arg: { result?: unknown; updatedAt?: Date | string | null }) => {\n const raw = arg?.updatedAt\n ?? (arg?.result as { updatedAt?: Date | string | null } | null | undefined)?.updatedAt\n ?? null\n return {\n ok: true,\n updatedAt: raw instanceof Date ? raw.toISOString() : (typeof raw === 'string' ? raw : null),\n }\n },\n },\n delete: {\n commandId: 'customers.people.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n if (!id) throw new CrudHttpError(400, { error: translate('customers.errors.person_required', 'Person id is required') })\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload?.items) ? payload.items : []\n const ids = items\n .map((item: unknown) => (\n item && typeof item === 'object' && typeof (item as Record<string, unknown>).id === 'string'\n ? (item as Record<string, unknown>).id as string\n : null\n ))\n .filter((id: string | null): id is string => typeof id === 'string' && id.length > 0)\n if (!ids.length) return\n\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const profileWhere: Record<string, unknown> = {\n entity: { $in: ids },\n tenantId: ctx.auth?.tenantId ?? null,\n }\n if (ctx.selectedOrganizationId) {\n profileWhere.organizationId = ctx.selectedOrganizationId\n }\n\n const [entities, profiles] = await Promise.all([\n findWithDecryption(\n em,\n CustomerEntity,\n {\n id: { $in: ids },\n deletedAt: null,\n kind: 'person',\n } as FilterQuery<CustomerEntity>,\n undefined,\n decryptionScope,\n ),\n findWithDecryption(\n em,\n CustomerPersonProfile,\n profileWhere as FilterQuery<CustomerPersonProfile>,\n { populate: ['entity', 'company'] },\n decryptionScope,\n ),\n ])\n\n const entitiesById = new Map<string, CustomerEntity>()\n for (const entity of entities) {\n entitiesById.set(entity.id, entity)\n }\n\n const profilesByEntityId = new Map<string, CustomerPersonProfile>()\n for (const profile of profiles) {\n const profileEntity = (profile as { entity?: { id?: unknown } }).entity\n const entityId = typeof profileEntity?.id === 'string' ? profileEntity.id : null\n if (entityId) profilesByEntityId.set(entityId, profile)\n }\n\n payload.items = items.map((item: unknown) => {\n if (!item || typeof item !== 'object') return item\n const record = item as Record<string, unknown>\n const entity = typeof record.id === 'string' ? entitiesById.get(record.id) : undefined\n const profile = typeof record.id === 'string' ? profilesByEntityId.get(record.id) : undefined\n if (!entity && !profile) return item\n return {\n ...record,\n display_name: entity?.displayName ?? record.display_name ?? null,\n description: entity?.description ?? record.description ?? null,\n owner_user_id: entity?.ownerUserId ?? record.owner_user_id ?? null,\n primary_email: entity?.primaryEmail ?? record.primary_email ?? null,\n primary_phone: entity?.primaryPhone ?? record.primary_phone ?? null,\n status: entity?.status ?? record.status ?? null,\n lifecycle_stage: entity?.lifecycleStage ?? record.lifecycle_stage ?? null,\n source: entity?.source ?? record.source ?? null,\n next_interaction_at: entity?.nextInteractionAt ? entity.nextInteractionAt.toISOString() : record.next_interaction_at ?? null,\n next_interaction_name: entity?.nextInteractionName ?? record.next_interaction_name ?? null,\n next_interaction_ref_id: entity?.nextInteractionRefId ?? record.next_interaction_ref_id ?? null,\n next_interaction_icon: entity?.nextInteractionIcon ?? record.next_interaction_icon ?? null,\n next_interaction_color: entity?.nextInteractionColor ?? record.next_interaction_color ?? null,\n first_name: profile?.firstName ?? null,\n last_name: profile?.lastName ?? null,\n preferred_name: profile?.preferredName ?? null,\n job_title: profile?.jobTitle ?? null,\n department: profile?.department ?? null,\n seniority: profile?.seniority ?? null,\n timezone: profile?.timezone ?? null,\n linked_in_url: profile?.linkedInUrl ?? null,\n twitter_url: profile?.twitterUrl ?? null,\n company_entity_id:\n profile?.company && typeof profile.company === 'object'\n ? profile.company.id\n : profile?.company ?? null,\n }\n })\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\n\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nconst personListItemSchema = z.object({\n id: z.string().uuid(),\n display_name: z.string().optional(),\n description: z.string().nullable().optional(),\n owner_user_id: z.string().uuid().nullable().optional(),\n primary_email: z.string().nullable().optional(),\n primary_phone: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n lifecycle_stage: z.string().nullable().optional(),\n source: z.string().nullable().optional(),\n next_interaction_at: z.string().nullable().optional(),\n next_interaction_name: z.string().nullable().optional(),\n next_interaction_ref_id: z.string().nullable().optional(),\n next_interaction_icon: z.string().nullable().optional(),\n next_interaction_color: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n})\n\nconst personCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n personId: z.string().uuid().nullable(),\n})\n\nexport const openApi = createCustomersCrudOpenApi({\n resourceName: 'Person',\n pluralName: 'People',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(personListItemSchema),\n create: {\n schema: personCreateSchema,\n responseSchema: personCreateResponseSchema,\n description: 'Creates a person contact using scoped organization and tenant identifiers.',\n },\n update: {\n schema: personUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates contact details or custom fields for a person.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a person by id. Request body or query may provide the identifier.',\n errors: [\n {\n status: 422,\n description: 'Person has dependent records (e.g. linked deals); unlink or reassign before delete.',\n schema: z.object({\n error: z.string(),\n code: z.literal('PERSON_HAS_DEPENDENTS'),\n }),\n },\n ],\n },\n})\n"],
5
- "mappings": "AACA,SAAS,SAAS;AAClB,SAAS,qBAAqB;AAC9B,SAAS,qBAAqB;AAC9B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,SAAS;AAClB,SAAS,oBAAoB,0BAA0B;AACvD,SAAS,2BAA2B;AACpC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,kCAAkC,8BAA8B,+BAA+B;AACxG,SAAS,yBAAyB;AAClC,SAAS,yBAAyB;AAClC,SAAS,0BAA0B;AACnC,SAAS,4BAA4B,+BAA+B;AACpE;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,+BAA+B;AAExC,MAAM,gBAAgB,EAAE,OAAO,CAAC,CAAC,EAAE,YAAY;AAE/C,MAAM,aAAa,EAChB,OAAO;AAAA,EACN,MAAM,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,CAAC;AAAA,EACxC,UAAU,EAAE,OAAO,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,GAAG,EAAE,QAAQ,EAAE;AAAA,EACtD,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,EACrC,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,EACnC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,EACpC,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,UAAU,EAAE,OAAO,EAAE,SAAS;AAAA,EAC9B,oBAAoB,EAAE,OAAO,EAAE,SAAS;AAAA,EACxC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,SAAS,EAAE,KAAK,CAAC,OAAO,MAAM,CAAC,EAAE,SAAS;AAAA,EAC1C,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC/B,QAAQ,EAAE,OAAO,EAAE,SAAS;AAAA,EAC5B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,EAChC,wBAAwB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EACnD,qBAAqB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAClD,CAAC,EACA,YAAY;AAEf,MAAM,gBAAgB;AAAA,EACpB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,uBAAuB,EAAE;AAAA,EACrE,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAAA,EACxE,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAAA,EACvE,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,yBAAyB,EAAE;AAC5E;AAEO,MAAM,WAAW;AAExB,MAAM,OAAO,cAAc;AAAA,EACzB,UAAU;AAAA,EACV,KAAK;AAAA,IACH,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,UAAU;AAAA,IACV,aAAa;AAAA,IACb,iBAAiB;AAAA,EACnB;AAAA,EACA,WAAW,EAAE,UAAU,mBAAmB;AAAA,EAC1C,SAAS,EAAE,YAAY,EAAE,UAAU,gBAAgB;AAAA,EACnD,MAAM;AAAA,IACJ,QAAQ;AAAA,IACR,UAAU,EAAE,UAAU;AAAA,IACtB,QAAQ;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,cAAc;AAAA,MACZ,MAAM;AAAA,MACN,OAAO;AAAA,MACP,cAAc;AAAA,MACd,QAAQ;AAAA,MACR,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,mBAAmB;AAAA,MACnB,WAAW;AAAA,MACX,WAAW;AAAA,IACb;AAAA,IACA,cAAc,OAAO,OAAO,QAAQ;AAClC,YAAM,qBAAqB,2BAA2B,KAAK;AAC3D,YAAM,UAAmC,EAAE,MAAM,EAAE,KAAK,SAAS,EAAE;AACnE,UAAI,MAAM,GAAI,SAAQ,KAAK,EAAE,KAAK,MAAM,GAAG;AAC3C,UAAI,MAAM,QAAQ;AAChB,cAAM,cAAc,MAChB,MAAM,iDAAiD;AAAA,UACrD;AAAA,UACA,OAAO,MAAM;AAAA,UACb,SAAS;AAAA,YACP;AAAA,cACE,YAAY,EAAE,UAAU;AAAA,cACxB,QAAQ;AAAA,gBACN;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,YACF;AAAA,YACA;AAAA,cACE,YAAY,EAAE,UAAU;AAAA,cACxB,QAAQ;AAAA,gBACN;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAAA,cACA,gBAAgB;AAAA,gBACd,OAAO;AAAA,gBACP,cAAc;AAAA,cAChB;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC,IACD;AACJ,YAAI,gBAAgB,QAAQ,YAAY,SAAS,GAAG;AAClD,mCAAyB,SAAS,WAAW;AAAA,QAC/C,OAAO;AACL,gBAAM,gBAAgB,IAAI,kBAAkB,MAAM,MAAM,CAAC;AACzD,kBAAQ,MAAM;AAAA,YACZ,EAAE,cAAc,EAAE,QAAQ,cAAc,EAAE;AAAA,YAC1C,EAAE,eAAe,EAAE,QAAQ,cAAc,EAAE;AAAA,YAC3C,EAAE,eAAe,EAAE,QAAQ,cAAc,EAAE;AAAA,YAC3C,EAAE,aAAa,EAAE,QAAQ,cAAc,EAAE;AAAA,YACzC,EAAE,uBAAuB,EAAE,QAAQ,cAAc,EAAE;AAAA,UACrD;AAAA,QACF;AAAA,MACF;AACA,YAAM,QAAQ,OAAO,MAAM,UAAU,WAAW,MAAM,MAAM,KAAK,EAAE,YAAY,IAAI;AACnF,YAAM,kBAAkB,OAAO,MAAM,oBAAoB,WAAW,MAAM,gBAAgB,KAAK,EAAE,YAAY,IAAI;AACjH,YAAM,gBAAgB,OAAO,MAAM,kBAAkB,WAAW,MAAM,cAAc,KAAK,EAAE,YAAY,IAAI;AAC3G,UAAI,OAAO;AACT,gBAAQ,gBAAgB,EAAE,KAAK,MAAM;AAAA,MACvC,WAAW,iBAAiB;AAC1B,gBAAQ,gBAAgB,EAAE,QAAQ,GAAG,kBAAkB,eAAe,CAAC,IAAI;AAAA,MAC7E,WAAW,eAAe;AACxB,gBAAQ,gBAAgB,EAAE,QAAQ,IAAI,kBAAkB,aAAa,CAAC,IAAI;AAAA,MAC5E;AACA,UAAI,MAAM,QAAQ;AAChB,gBAAQ,SAAS,EAAE,KAAK,MAAM,OAAO;AAAA,MACvC;AACA,UAAI,MAAM,gBAAgB;AACxB,gBAAQ,kBAAkB,EAAE,KAAK,MAAM,eAAe;AAAA,MACxD;AACA,UAAI,MAAM,QAAQ;AAChB,gBAAQ,SAAS,EAAE,KAAK,MAAM,OAAO;AAAA,MACvC;AACA,YAAM,YAAY,OAAO,MAAM,WAAW,WAAW,MAAM,SAAS;AACpE,YAAM,SAAS,UACZ,MAAM,GAAG,EACT,IAAI,CAAC,UAAkB,MAAM,KAAK,CAAC,EACnC,OAAO,CAAC,UAAkB,MAAM,SAAS,CAAC;AAC7C,YAAM,cAAc,kBAAkB,MAAM,WAAW,MAAM;AAC7D,UAAI,aAAa;AACf,gBAAQ,KAAK,EAAE,KAAK,uCAAuC;AAAA,MAC7D,WAAW,OAAO,SAAS,GAAG;AAC5B,gBAAQ,wBAAwB,IAAI,EAAE,KAAK,OAAO;AAAA,MACpD;AACA,YAAM,cAAc,oBAAI,IAAY;AACpC,YAAM,gBAAgB,OAAO,MAAM,eAAe,WAAW,MAAM,aAAa;AAChF,oBACG,MAAM,GAAG,EACT,IAAI,CAAC,UAAkB,MAAM,KAAK,CAAC,EACnC,OAAO,CAAC,UAAkB,MAAM,SAAS,CAAC,EAC1C,QAAQ,CAAC,UAAkB,YAAY,IAAI,KAAK,CAAC;AACpD,UAAI,OAAO,MAAM,wBAAwB;AACvC,YAAI;AACF,gBAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,gBAAM,kBAAkB;AAAA,YACtB,UAAU,IAAI,MAAM,YAAY;AAAA,YAChC,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,UACnE;AACA,gBAAM,YAAY,MAAM;AAAA,YACtB;AAAA,YACA,mCAAmC,EAAE,SAAS,MAAM,uBAAuB,GAAG,eAAe;AAAA,YAC7F;AAAA,UACF;AACA,gBAAM,QAAQ;AAAA,YACZ,MAAM;AAAA,cACJ;AAAA,cACA;AAAA,cACA;AAAA,cACA,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,cACvB;AAAA,YACF;AAAA,UACF;AACA,gBAAM,QAAQ,CAAC,SAAS;AACtB,kBAAM,WAAW,KAAK,QAAQ;AAC9B,gBAAI,OAAO,aAAa,YAAY,SAAS,SAAS,EAAG,aAAY,IAAI,QAAQ;AAAA,UACnF,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,kBAAQ,KAAK,oFAAoF,GAAG;AAAA,QACtG;AAAA,MACF;AACA,UAAI,OAAO,MAAM,qBAAqB;AACpC,YAAI;AACF,gBAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,gBAAM,kBAAkB;AAAA,YACtB,UAAU,IAAI,MAAM,YAAY;AAAA,YAChC,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,UACnE;AACA,gBAAM,QAAQ,MAAM;AAAA,YAClB;AAAA,YACA;AAAA,YACA,gCAAgC,MAAM,qBAAqB,eAAe;AAAA,YAC1E,EAAE,UAAU,CAAC,QAAQ,EAAE;AAAA,YACvB;AAAA,UACF;AACA,gBAAM,QAAQ,CAAC,SAAS;AACtB,kBAAM,WAAW,KAAK,QAAQ;AAC9B,gBAAI,OAAO,aAAa,YAAY,SAAS,SAAS,EAAG,aAAY,IAAI,QAAQ;AAAA,UACnF,CAAC;AAAA,QACH,SAAS,KAAK;AACZ,kBAAQ,KAAK,oFAAoF,GAAG;AAAA,QACtG;AAAA,MACF;AACA,6BAAuB,SAAS,MAAM,KAAK,WAAW,CAAC;AACvD,YAAM,WAAW,kBAAkB,MAAM,QAAQ;AACjD,UAAI,CAAC,SAAS,CAAC,mBAAmB,CAAC,iBAAiB,aAAa,MAAM;AACrE,gBAAQ,gBAAgB,EAAE,SAAS,SAAS;AAAA,MAC9C;AACA,YAAM,WAAW,kBAAkB,MAAM,QAAQ;AACjD,UAAI,aAAa,MAAM;AACrB,gBAAQ,gBAAgB,EAAE,SAAS,SAAS;AAAA,MAC9C;AACA,YAAM,qBAAqB,kBAAkB,MAAM,kBAAkB;AACrE,UAAI,uBAAuB,MAAM;AAC/B,gBAAQ,sBAAsB,EAAE,SAAS,mBAAmB;AAAA,MAC9D;AACA,YAAM,eAAqC,CAAC;AAC5C,UAAI,MAAM,aAAa;AACrB,cAAM,OAAO,IAAI,KAAK,MAAM,WAAW;AACvC,YAAI,CAAC,OAAO,MAAM,KAAK,QAAQ,CAAC,EAAG,cAAa,OAAO;AAAA,MACzD;AACA,UAAI,MAAM,WAAW;AACnB,cAAM,KAAK,IAAI,KAAK,MAAM,SAAS;AACnC,YAAI,CAAC,OAAO,MAAM,GAAG,QAAQ,CAAC,EAAG,cAAa,OAAO;AAAA,MACvD;AACA,UAAI,OAAO,KAAK,YAAY,EAAE,QAAQ;AACpC,gBAAQ,aAAa;AAAA,MACvB;AACA,UAAI,KAAK;AACP,YAAI;AACF,gBAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,gBAAM,YAAY,MAAM,iCAAiC;AAAA,YACvD,WAAW,CAAC,EAAE,UAAU,iBAAiB,EAAE,UAAU,uBAAuB;AAAA,YAC5E;AAAA,YACA;AAAA,YACA,UAAU,IAAI,MAAM,YAAY;AAAA,UAClC,CAAC;AACD,iBAAO,OAAO,SAAS,SAAS;AAAA,QAClC,SAAS,KAAK;AACZ,kBAAQ,KAAK,+FAA+F,GAAG;AAAA,QACjH;AAAA,MACF;AACA,UAAI,OAAO,oBAAoB;AAC7B,cAAM,kBAAkB,wBAAwB,EAAE,GAAG,QAAQ,GAAG,kBAAkB;AAClF,cAAM,aAAa,MAAM,qCAAqC;AAAA,UAC5D;AAAA,UACA,UAAU,EAAE,UAAU;AAAA,UACtB,SAAS;AAAA,UACT,oBAAoB;AAAA,YAClB;AAAA,cACE,UAAU,EAAE,UAAU;AAAA,cACtB,OAAO;AAAA,cACP,OAAO;AAAA,cACP,gBAAgB;AAAA,cAChB,MAAM,EAAE,WAAW,MAAM,SAAS,YAAY;AAAA,YAChD;AAAA,UACF;AAAA,UACA,OAAO;AAAA,YACL;AAAA,cACE,OAAO;AAAA,cACP,OAAO;AAAA,cACP,MAAM,EAAE,OAAO,KAAK;AAAA,cACpB,IAAI,EAAE,OAAO,YAAY;AAAA,cACzB,MAAM;AAAA,YACR;AAAA,UACF;AAAA,QACF,CAAC;AACD,iCAAyB,SAAS,UAAU;AAAA,MAC9C;AACA,aAAO;AAAA,IACT;AAAA,IACA,oBAAoB;AAAA,MAClB;AAAA,QACE,UAAU,EAAE,UAAU;AAAA,QACtB,OAAO;AAAA,QACP,OAAO;AAAA,QACP,gBAAgB;AAAA,QAChB,MAAM,EAAE,WAAW,MAAM,SAAS,YAAY;AAAA,MAChD;AAAA,IACF;AAAA,IACA,OAAO;AAAA,MACL;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,QACP,MAAM,EAAE,OAAO,KAAK;AAAA,QACpB,IAAI,EAAE,OAAO,YAAY;AAAA,QACzB,MAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,eAAe,CAAC,SAAS;AACvB,UAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,YAAM,SAAS;AACf,YAAM,aAAsC,EAAE,GAAG,OAAO;AACxD,aAAO,WAAW;AAClB,YAAM,YAAY,6BAA6B,MAAM;AACrD,iBAAW,OAAO,OAAO,KAAK,UAAU,GAAG;AACzC,YAAI,IAAI,WAAW,KAAK,GAAG;AACzB,iBAAO,WAAW,GAAG;AAAA,QACvB;AAAA,MACF;AACA,aAAO,EAAE,GAAG,YAAY,GAAG,UAAU;AAAA,IACvC;AAAA,EACF;AAAA,EACA,SAAS;AAAA,IACP,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,cAAM,EAAE,MAAM,OAAO,IAAI,wBAAwB,MAAM;AACvD,cAAM,SAAS,mBAAmB,MAAM,IAAI;AAC5C,eAAO,OAAO,KAAK,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,cAAc,OAAO,IAAI;AAAA,MAC5E;AAAA,MACA,UAAU,CAAC,EAAE,OAAO,OAAO;AAAA,QACzB,IAAI,QAAQ,YAAY,QAAQ,MAAM;AAAA,QACtC,UAAU,QAAQ,YAAY;AAAA,MAChC;AAAA,MACA,QAAQ;AAAA,IACV;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,KAAK,IAAI,MAAM;AAChC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,SAAS,kBAAkB,OAAO,CAAC,GAAG,KAAK,SAAS;AAC1D,cAAM,aAAa,wBAAwB,QAAQ,SAAS;AAC5D,cAAM,EAAE,MAAM,OAAO,IAAI,wBAAwB,UAAU;AAC3D,cAAM,SAAS,mBAAmB,MAAM,IAAI;AAC5C,eAAO,OAAO,KAAK,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,cAAc,OAAO,IAAI;AAAA,MAC5E;AAAA;AAAA;AAAA,MAGA,UAAU,CAAC,QAAgE;AACzE,cAAM,MAAM,KAAK,aACX,KAAK,QAAoE,aAC1E;AACL,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,WAAW,eAAe,OAAO,IAAI,YAAY,IAAK,OAAO,QAAQ,WAAW,MAAM;AAAA,QACxF;AAAA,MACF;AAAA,IACF;AAAA,IACA,QAAQ;AAAA,MACN,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,UAAU,OAAO,EAAE,QAAQ,IAAI,MAAM;AACnC,cAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,cAAM,KACJ,QAAQ,MAAM,MACd,QAAQ,MACR,QAAQ,OAAO,OACd,IAAI,UAAU,IAAI,IAAI,IAAI,QAAQ,GAAG,EAAE,aAAa,IAAI,IAAI,IAAI;AACnE,YAAI,CAAC,GAAI,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,oCAAoC,uBAAuB,EAAE,CAAC;AACvH,eAAO,EAAE,GAAG;AAAA,MACd;AAAA,MACA,UAAU,OAAO,EAAE,IAAI,KAAK;AAAA,IAC9B;AAAA,EACF;AAAA,EACA,OAAO;AAAA,IACL,WAAW,OAAO,SAAS,QAAQ;AACjC,YAAM,QAAQ,MAAM,QAAQ,SAAS,KAAK,IAAI,QAAQ,QAAQ,CAAC;AAC/D,YAAM,MAAM,MACT,IAAI,CAAC,SACJ,QAAQ,OAAO,SAAS,YAAY,OAAQ,KAAiC,OAAO,WAC/E,KAAiC,KAClC,IACL,EACA,OAAO,CAAC,OAAoC,OAAO,OAAO,YAAY,GAAG,SAAS,CAAC;AACtF,UAAI,CAAC,IAAI,OAAQ;AAEjB,YAAM,KAAK,IAAI,UAAU,QAAQ,IAAI;AACrC,YAAM,kBAAkB;AAAA,QACtB,UAAU,IAAI,MAAM,YAAY;AAAA,QAChC,gBAAgB,IAAI,0BAA0B,IAAI,MAAM,SAAS;AAAA,MACnE;AACA,YAAM,eAAwC;AAAA,QAC5C,QAAQ,EAAE,KAAK,IAAI;AAAA,QACnB,UAAU,IAAI,MAAM,YAAY;AAAA,MAClC;AACA,UAAI,IAAI,wBAAwB;AAC9B,qBAAa,iBAAiB,IAAI;AAAA,MACpC;AAEA,YAAM,CAAC,UAAU,QAAQ,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC7C;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,YACE,IAAI,EAAE,KAAK,IAAI;AAAA,YACf,WAAW;AAAA,YACX,MAAM;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA;AAAA,UACE;AAAA,UACA;AAAA,UACA;AAAA,UACA,EAAE,UAAU,CAAC,UAAU,SAAS,EAAE;AAAA,UAClC;AAAA,QACF;AAAA,MACF,CAAC;AAED,YAAM,eAAe,oBAAI,IAA4B;AACrD,iBAAW,UAAU,UAAU;AAC7B,qBAAa,IAAI,OAAO,IAAI,MAAM;AAAA,MACpC;AAEA,YAAM,qBAAqB,oBAAI,IAAmC;AAClE,iBAAW,WAAW,UAAU;AAC9B,cAAM,gBAAiB,QAA0C;AACjE,cAAM,WAAW,OAAO,eAAe,OAAO,WAAW,cAAc,KAAK;AAC5E,YAAI,SAAU,oBAAmB,IAAI,UAAU,OAAO;AAAA,MACxD;AAEA,cAAQ,QAAQ,MAAM,IAAI,CAAC,SAAkB;AAC3C,YAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC9C,cAAM,SAAS;AACf,cAAM,SAAS,OAAO,OAAO,OAAO,WAAW,aAAa,IAAI,OAAO,EAAE,IAAI;AAC7E,cAAM,UAAU,OAAO,OAAO,OAAO,WAAW,mBAAmB,IAAI,OAAO,EAAE,IAAI;AACpF,YAAI,CAAC,UAAU,CAAC,QAAS,QAAO;AAChC,eAAO;AAAA,UACL,GAAG;AAAA,UACH,cAAc,QAAQ,eAAe,OAAO,gBAAgB;AAAA,UAC5D,aAAa,QAAQ,eAAe,OAAO,eAAe;AAAA,UAC1D,eAAe,QAAQ,eAAe,OAAO,iBAAiB;AAAA,UAC9D,eAAe,QAAQ,gBAAgB,OAAO,iBAAiB;AAAA,UAC/D,eAAe,QAAQ,gBAAgB,OAAO,iBAAiB;AAAA,UAC/D,QAAQ,QAAQ,UAAU,OAAO,UAAU;AAAA,UAC3C,iBAAiB,QAAQ,kBAAkB,OAAO,mBAAmB;AAAA,UACrE,QAAQ,QAAQ,UAAU,OAAO,UAAU;AAAA,UAC3C,qBAAqB,QAAQ,oBAAoB,OAAO,kBAAkB,YAAY,IAAI,OAAO,uBAAuB;AAAA,UACxH,uBAAuB,QAAQ,uBAAuB,OAAO,yBAAyB;AAAA,UACtF,yBAAyB,QAAQ,wBAAwB,OAAO,2BAA2B;AAAA,UAC3F,uBAAuB,QAAQ,uBAAuB,OAAO,yBAAyB;AAAA,UACtF,wBAAwB,QAAQ,wBAAwB,OAAO,0BAA0B;AAAA,UACzF,YAAY,SAAS,aAAa;AAAA,UAClC,WAAW,SAAS,YAAY;AAAA,UAChC,gBAAgB,SAAS,iBAAiB;AAAA,UAC1C,WAAW,SAAS,YAAY;AAAA,UAChC,YAAY,SAAS,cAAc;AAAA,UACnC,WAAW,SAAS,aAAa;AAAA,UACjC,UAAU,SAAS,YAAY;AAAA,UAC/B,eAAe,SAAS,eAAe;AAAA,UACvC,aAAa,SAAS,cAAc;AAAA,UACpC,mBACE,SAAS,WAAW,OAAO,QAAQ,YAAY,WAC3C,QAAQ,QAAQ,KAChB,SAAS,WAAW;AAAA,QAC5B;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAED,MAAM,EAAE,MAAM,KAAK,OAAO,IAAI;AAGvB,MAAM,MAAM,KAAK;AAExB,MAAM,uBAAuB,EAAE,OAAO;AAAA,EACpC,IAAI,EAAE,OAAO,EAAE,KAAK;AAAA,EACpB,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,EAClC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC5C,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACrD,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAC9C,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,iBAAiB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EAChD,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvC,qBAAqB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACpD,uBAAuB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtD,yBAAyB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACxD,uBAAuB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACtD,wBAAwB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,iBAAiB,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACvD,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS;AAAA,EACjD,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS;AAC7C,CAAC;AAED,MAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AAAA,EAC/B,UAAU,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS;AACvC,CAAC;AAEM,MAAM,UAAU,2BAA2B;AAAA,EAChD,cAAc;AAAA,EACd,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,oBAAoB,8BAA8B,oBAAoB;AAAA,EACtE,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,QAAQ;AAAA,IACN,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,aAAa;AAAA,EACf;AAAA,EACA,KAAK;AAAA,IACH,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAAA,IAC1C,gBAAgB;AAAA,IAChB,aAAa;AAAA,IACb,QAAQ;AAAA,MACN;AAAA,QACE,QAAQ;AAAA,QACR,aAAa;AAAA,QACb,QAAQ,EAAE,OAAO;AAAA,UACf,OAAO,EAAE,OAAO;AAAA,UAChB,MAAM,EAAE,QAAQ,uBAAuB;AAAA,QACzC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF,CAAC;",
4
+ "sourcesContent": ["import type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { makeCrudRoute } from '@open-mercato/shared/lib/crud/factory'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n CustomerDealPersonLink,\n CustomerEntity,\n CustomerPersonCompanyLink,\n CustomerPersonProfile,\n} from '../../data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { personCreateSchema, personUpdateSchema } from '../../data/validators'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport {\n applyEntityIdExclusion,\n applyEntityIdRestriction,\n findMatchingEntityIdsWithQueryEngine,\n findMatchingEntityIdsBySearchTokensAcrossSources,\n withScopedPayload,\n} from '../utils'\nimport { buildCustomFieldFiltersFromQuery, extractAllCustomFieldEntries, splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { buildIlikeTerm } from '@open-mercato/shared/lib/db/buildIlikeTerm'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { consumeAdvancedFilterState, mergeAdvancedFilterTree } from '@open-mercato/shared/lib/crud/advanced-filter-integration'\nimport {\n createCustomersCrudOpenApi,\n createPagedListResponseSchema,\n defaultOkResponseSchema,\n} from '../openapi'\nimport {\n filterActivePersonCompanyLinks,\n withActiveCustomerPersonCompanyLinkFilter,\n withCustomerPersonCompanyLinkScope,\n withScopedCustomerDealLinkWhere,\n} from '../../lib/personCompanyLinkTable'\nimport { normalizeProfilePayload } from './payload'\n\nconst rawBodySchema = z.object({}).passthrough()\n\nconst listSchema = z\n .object({\n page: z.coerce.number().min(1).default(1),\n pageSize: z.coerce.number().min(1).max(100).default(50),\n search: z.string().optional(),\n email: z.string().optional(),\n emailStartsWith: z.string().optional(),\n emailContains: z.string().optional(),\n status: z.string().optional(),\n lifecycleStage: z.string().optional(),\n source: z.string().optional(),\n hasEmail: z.string().optional(),\n hasPhone: z.string().optional(),\n hasNextInteraction: z.string().optional(),\n createdFrom: z.string().optional(),\n createdTo: z.string().optional(),\n sortField: z.string().optional(),\n sortDir: z.enum(['asc', 'desc']).optional(),\n id: z.string().uuid().optional(),\n tagIds: z.string().optional(),\n tagIdsEmpty: z.string().optional(),\n excludeIds: z.string().optional(),\n excludeLinkedCompanyId: z.string().uuid().optional(),\n excludeLinkedDealId: z.string().uuid().optional(),\n })\n .passthrough()\n\nconst routeMetadata = {\n GET: { requireAuth: true, requireFeatures: ['customers.people.view'] },\n POST: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n PUT: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['customers.people.manage'] },\n}\n\nexport const metadata = routeMetadata\n\nconst crud = makeCrudRoute({\n metadata: routeMetadata,\n orm: {\n entity: CustomerEntity,\n idField: 'id',\n orgField: 'organizationId',\n tenantField: 'tenantId',\n softDeleteField: 'deletedAt',\n },\n enrichers: { entityId: 'customers.person' },\n indexer: { entityType: E.customers.customer_entity },\n list: {\n schema: listSchema,\n entityId: E.customers.customer_entity,\n fields: [\n 'id',\n 'display_name',\n 'description',\n 'owner_user_id',\n 'primary_email',\n 'primary_phone',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'next_interaction_at',\n 'next_interaction_name',\n 'next_interaction_ref_id',\n 'next_interaction_icon',\n 'next_interaction_color',\n 'organization_id',\n 'tenant_id',\n 'kind',\n 'created_at',\n 'updated_at',\n ],\n sortFieldMap: {\n name: 'display_name',\n email: 'primary_email',\n primaryEmail: 'primary_email',\n status: 'status',\n lifecycleStage: 'lifecycle_stage',\n source: 'source',\n nextInteractionAt: 'next_interaction_at',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n },\n buildFilters: async (query, ctx) => {\n const advancedFilterTree = consumeAdvancedFilterState(query)\n const filters: Record<string, unknown> = { kind: { $eq: 'person' } }\n if (query.id) filters.id = { $eq: query.id }\n if (query.search) {\n const matchingIds = ctx\n ? await findMatchingEntityIdsBySearchTokensAcrossSources({\n ctx,\n query: query.search,\n sources: [\n {\n entityType: E.customers.customer_entity,\n fields: [\n 'display_name',\n 'primary_email',\n 'primary_phone',\n 'description',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'next_interaction_name',\n ],\n },\n {\n entityType: E.customers.customer_person_profile,\n fields: [\n 'display_name',\n 'primary_email',\n 'primary_phone',\n 'status',\n 'lifecycle_stage',\n 'source',\n 'first_name',\n 'last_name',\n 'preferred_name',\n 'job_title',\n 'department',\n 'seniority',\n 'timezone',\n 'linked_in_url',\n 'twitter_url',\n ],\n mapToEntityIds: {\n table: 'customer_people',\n targetColumn: 'entity_id',\n },\n },\n ],\n })\n : null\n if (matchingIds !== null && matchingIds.length > 0) {\n applyEntityIdRestriction(filters, matchingIds)\n } else {\n const searchPattern = buildIlikeTerm(query.search)\n filters.$or = [\n { display_name: { $ilike: searchPattern } },\n { primary_email: { $ilike: searchPattern } },\n { primary_phone: { $ilike: searchPattern } },\n { description: { $ilike: searchPattern } },\n { next_interaction_name: { $ilike: searchPattern } },\n ]\n }\n }\n const email = typeof query.email === 'string' ? query.email.trim().toLowerCase() : ''\n const emailStartsWith = typeof query.emailStartsWith === 'string' ? query.emailStartsWith.trim().toLowerCase() : ''\n const emailContains = typeof query.emailContains === 'string' ? query.emailContains.trim().toLowerCase() : ''\n if (email) {\n filters.primary_email = { $eq: email }\n } else if (emailStartsWith) {\n filters.primary_email = { $ilike: buildIlikeTerm(emailStartsWith, 'startsWith') }\n } else if (emailContains) {\n filters.primary_email = { $ilike: buildIlikeTerm(emailContains) }\n }\n if (query.status) {\n filters.status = { $eq: query.status }\n }\n if (query.lifecycleStage) {\n filters.lifecycle_stage = { $eq: query.lifecycleStage }\n }\n if (query.source) {\n filters.source = { $eq: query.source }\n }\n const tagIdsRaw = typeof query.tagIds === 'string' ? query.tagIds : ''\n const tagIds = tagIdsRaw\n .split(',')\n .map((value: string) => value.trim())\n .filter((value: string) => value.length > 0)\n const tagIdsEmpty = parseBooleanToken(query.tagIdsEmpty) === true\n if (tagIdsEmpty) {\n filters.id = { $eq: '00000000-0000-0000-0000-000000000000' }\n } else if (tagIds.length > 0) {\n filters['tag_assignments.tag_id'] = { $in: tagIds }\n }\n const excludedIds = new Set<string>()\n const excludeIdsRaw = typeof query.excludeIds === 'string' ? query.excludeIds : ''\n excludeIdsRaw\n .split(',')\n .map((value: string) => value.trim())\n .filter((value: string) => value.length > 0)\n .forEach((value: string) => excludedIds.add(value))\n if (ctx && query.excludeLinkedCompanyId) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const linkWhere = await withActiveCustomerPersonCompanyLinkFilter(\n em,\n withCustomerPersonCompanyLinkScope({ company: query.excludeLinkedCompanyId }, decryptionScope),\n 'customers.people.GET',\n )\n const links = filterActivePersonCompanyLinks(\n await findWithDecryption(\n em,\n CustomerPersonCompanyLink,\n linkWhere,\n { populate: ['person'] },\n decryptionScope,\n ),\n )\n links.forEach((link) => {\n const personId = link.person?.id\n if (typeof personId === 'string' && personId.length > 0) excludedIds.add(personId)\n })\n } catch (err) {\n console.warn('[customers.people.list] exclusion lookup failed; falling back to base result set', err)\n }\n }\n if (ctx && query.excludeLinkedDealId) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const links = await findWithDecryption(\n em,\n CustomerDealPersonLink,\n withScopedCustomerDealLinkWhere(query.excludeLinkedDealId, decryptionScope),\n { populate: ['person'] },\n decryptionScope,\n )\n links.forEach((link) => {\n const personId = link.person?.id\n if (typeof personId === 'string' && personId.length > 0) excludedIds.add(personId)\n })\n } catch (err) {\n console.warn('[customers.people.list] exclusion lookup failed; falling back to base result set', err)\n }\n }\n applyEntityIdExclusion(filters, Array.from(excludedIds))\n const hasEmail = parseBooleanToken(query.hasEmail)\n if (!email && !emailStartsWith && !emailContains && hasEmail !== null) {\n filters.primary_email = { $exists: hasEmail }\n }\n const hasPhone = parseBooleanToken(query.hasPhone)\n if (hasPhone !== null) {\n filters.primary_phone = { $exists: hasPhone }\n }\n const hasNextInteraction = parseBooleanToken(query.hasNextInteraction)\n if (hasNextInteraction !== null) {\n filters.next_interaction_at = { $exists: hasNextInteraction }\n }\n const createdRange: Record<string, Date> = {}\n if (query.createdFrom) {\n const from = new Date(query.createdFrom)\n if (!Number.isNaN(from.getTime())) createdRange.$gte = from\n }\n if (query.createdTo) {\n const to = new Date(query.createdTo)\n if (!Number.isNaN(to.getTime())) createdRange.$lte = to\n }\n if (Object.keys(createdRange).length) {\n filters.created_at = createdRange\n }\n if (ctx) {\n try {\n const em = ctx.container.resolve('em') as EntityManager\n const cfFilters = await buildCustomFieldFiltersFromQuery({\n entityIds: [E.customers.customer_entity, E.customers.customer_person_profile],\n query,\n em,\n tenantId: ctx.auth?.tenantId ?? null,\n })\n Object.assign(filters, cfFilters)\n } catch (err) {\n console.warn('[customers.people.list] custom field filter resolution failed; falling back to base filters', err)\n }\n }\n if (ctx && advancedFilterTree) {\n const advancedFilters = mergeAdvancedFilterTree({ ...filters }, advancedFilterTree)\n const matchedIds = await findMatchingEntityIdsWithQueryEngine({\n ctx,\n entityId: E.customers.customer_entity,\n filters: advancedFilters,\n customFieldSources: [\n {\n entityId: E.customers.customer_person_profile,\n table: 'customer_people',\n alias: 'person_profile',\n recordIdColumn: 'id',\n join: { fromField: 'id', toField: 'entity_id' },\n },\n ],\n joins: [\n {\n alias: 'tag_assignments',\n table: 'customer_tag_assignments',\n from: { field: 'id' },\n to: { field: 'entity_id' },\n type: 'left',\n },\n ],\n })\n applyEntityIdRestriction(filters, matchedIds)\n }\n return filters\n },\n customFieldSources: [\n {\n entityId: E.customers.customer_person_profile,\n table: 'customer_people',\n alias: 'person_profile',\n recordIdColumn: 'id',\n join: { fromField: 'id', toField: 'entity_id' },\n },\n ],\n joins: [\n {\n alias: 'tag_assignments',\n table: 'customer_tag_assignments',\n from: { field: 'id' },\n to: { field: 'entity_id' },\n type: 'left',\n },\n ],\n transformItem: (item) => {\n if (!item || typeof item !== 'object') return item\n const record = item as Record<string, unknown>\n const normalized: Record<string, unknown> = { ...record }\n delete normalized.kind\n const cfEntries = extractAllCustomFieldEntries(record)\n for (const key of Object.keys(normalized)) {\n if (key.startsWith('cf:')) {\n delete normalized[key]\n }\n }\n return { ...normalized, ...cfEntries }\n },\n },\n actions: {\n create: {\n commandId: 'customers.people.create',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const { base, custom } = splitCustomFieldPayload(scoped)\n const parsed = personCreateSchema.parse(base)\n return Object.keys(custom).length ? { ...parsed, customFields: custom } : parsed\n },\n response: ({ result }) => ({\n id: result?.entityId ?? result?.id ?? null,\n personId: result?.personId ?? null,\n }),\n status: 201,\n },\n update: {\n commandId: 'customers.people.update',\n schema: rawBodySchema,\n mapInput: async ({ raw, ctx }) => {\n const { translate } = await resolveTranslations()\n const scoped = withScopedPayload(raw ?? {}, ctx, translate)\n const normalized = normalizeProfilePayload(scoped, translate)\n const { base, custom } = splitCustomFieldPayload(normalized)\n const parsed = personUpdateSchema.parse(base)\n return Object.keys(custom).length ? { ...parsed, customFields: custom } : parsed\n },\n // Return the freshly-bumped updatedAt so inline-edit detail pages can refresh\n // their optimistic-lock token between sequential saves (#2055).\n response: (arg: { result?: unknown; updatedAt?: Date | string | null }) => {\n const raw = arg?.updatedAt\n ?? (arg?.result as { updatedAt?: Date | string | null } | null | undefined)?.updatedAt\n ?? null\n return {\n ok: true,\n updatedAt: raw instanceof Date ? raw.toISOString() : (typeof raw === 'string' ? raw : null),\n }\n },\n },\n delete: {\n commandId: 'customers.people.delete',\n schema: rawBodySchema,\n mapInput: async ({ parsed, ctx }) => {\n const { translate } = await resolveTranslations()\n const id =\n parsed?.body?.id ??\n parsed?.id ??\n parsed?.query?.id ??\n (ctx.request ? new URL(ctx.request.url).searchParams.get('id') : null)\n if (!id) throw new CrudHttpError(400, { error: translate('customers.errors.person_required', 'Person id is required') })\n return { id }\n },\n response: () => ({ ok: true }),\n },\n },\n hooks: {\n afterList: async (payload, ctx) => {\n const items = Array.isArray(payload?.items) ? payload.items : []\n const ids = items\n .map((item: unknown) => (\n item && typeof item === 'object' && typeof (item as Record<string, unknown>).id === 'string'\n ? (item as Record<string, unknown>).id as string\n : null\n ))\n .filter((id: string | null): id is string => typeof id === 'string' && id.length > 0)\n if (!ids.length) return\n\n const em = ctx.container.resolve('em') as EntityManager\n const decryptionScope = {\n tenantId: ctx.auth?.tenantId ?? null,\n organizationId: ctx.selectedOrganizationId ?? ctx.auth?.orgId ?? null,\n }\n const profileWhere: Record<string, unknown> = {\n entity: { $in: ids },\n tenantId: ctx.auth?.tenantId ?? null,\n }\n if (ctx.selectedOrganizationId) {\n profileWhere.organizationId = ctx.selectedOrganizationId\n }\n\n const [entities, profiles] = await Promise.all([\n findWithDecryption(\n em,\n CustomerEntity,\n {\n id: { $in: ids },\n deletedAt: null,\n kind: 'person',\n } as FilterQuery<CustomerEntity>,\n undefined,\n decryptionScope,\n ),\n findWithDecryption(\n em,\n CustomerPersonProfile,\n profileWhere as FilterQuery<CustomerPersonProfile>,\n { populate: ['entity', 'company'] },\n decryptionScope,\n ),\n ])\n\n const entitiesById = new Map<string, CustomerEntity>()\n for (const entity of entities) {\n entitiesById.set(entity.id, entity)\n }\n\n const profilesByEntityId = new Map<string, CustomerPersonProfile>()\n for (const profile of profiles) {\n const profileEntity = (profile as { entity?: { id?: unknown } }).entity\n const entityId = typeof profileEntity?.id === 'string' ? profileEntity.id : null\n if (entityId) profilesByEntityId.set(entityId, profile)\n }\n\n payload.items = items.map((item: unknown) => {\n if (!item || typeof item !== 'object') return item\n const record = item as Record<string, unknown>\n const entity = typeof record.id === 'string' ? entitiesById.get(record.id) : undefined\n const profile = typeof record.id === 'string' ? profilesByEntityId.get(record.id) : undefined\n if (!entity && !profile) return item\n return {\n ...record,\n display_name: entity?.displayName ?? record.display_name ?? null,\n description: entity?.description ?? record.description ?? null,\n owner_user_id: entity?.ownerUserId ?? record.owner_user_id ?? null,\n primary_email: entity?.primaryEmail ?? record.primary_email ?? null,\n primary_phone: entity?.primaryPhone ?? record.primary_phone ?? null,\n status: entity?.status ?? record.status ?? null,\n lifecycle_stage: entity?.lifecycleStage ?? record.lifecycle_stage ?? null,\n source: entity?.source ?? record.source ?? null,\n next_interaction_at: entity?.nextInteractionAt ? entity.nextInteractionAt.toISOString() : record.next_interaction_at ?? null,\n next_interaction_name: entity?.nextInteractionName ?? record.next_interaction_name ?? null,\n next_interaction_ref_id: entity?.nextInteractionRefId ?? record.next_interaction_ref_id ?? null,\n next_interaction_icon: entity?.nextInteractionIcon ?? record.next_interaction_icon ?? null,\n next_interaction_color: entity?.nextInteractionColor ?? record.next_interaction_color ?? null,\n first_name: profile?.firstName ?? null,\n last_name: profile?.lastName ?? null,\n preferred_name: profile?.preferredName ?? null,\n job_title: profile?.jobTitle ?? null,\n department: profile?.department ?? null,\n seniority: profile?.seniority ?? null,\n timezone: profile?.timezone ?? null,\n linked_in_url: profile?.linkedInUrl ?? null,\n twitter_url: profile?.twitterUrl ?? null,\n company_entity_id:\n profile?.company && typeof profile.company === 'object'\n ? profile.company.id\n : profile?.company ?? null,\n }\n })\n },\n },\n})\n\nconst { POST, PUT, DELETE } = crud\n\nexport { POST, PUT, DELETE }\nexport const GET = crud.GET\n\nconst personListItemSchema = z.object({\n id: z.string().uuid(),\n display_name: z.string().optional(),\n description: z.string().nullable().optional(),\n owner_user_id: z.string().uuid().nullable().optional(),\n primary_email: z.string().nullable().optional(),\n primary_phone: z.string().nullable().optional(),\n status: z.string().nullable().optional(),\n lifecycle_stage: z.string().nullable().optional(),\n source: z.string().nullable().optional(),\n next_interaction_at: z.string().nullable().optional(),\n next_interaction_name: z.string().nullable().optional(),\n next_interaction_ref_id: z.string().nullable().optional(),\n next_interaction_icon: z.string().nullable().optional(),\n next_interaction_color: z.string().nullable().optional(),\n organization_id: z.string().uuid().nullable().optional(),\n tenant_id: z.string().uuid().nullable().optional(),\n created_at: z.string().nullable().optional(),\n})\n\nconst personCreateResponseSchema = z.object({\n id: z.string().uuid().nullable(),\n personId: z.string().uuid().nullable(),\n})\n\nexport const openApi = createCustomersCrudOpenApi({\n resourceName: 'Person',\n pluralName: 'People',\n querySchema: listSchema,\n listResponseSchema: createPagedListResponseSchema(personListItemSchema),\n create: {\n schema: personCreateSchema,\n responseSchema: personCreateResponseSchema,\n description: 'Creates a person contact using scoped organization and tenant identifiers.',\n },\n update: {\n schema: personUpdateSchema,\n responseSchema: defaultOkResponseSchema,\n description: 'Updates contact details or custom fields for a person.',\n },\n del: {\n schema: z.object({ id: z.string().uuid() }),\n responseSchema: defaultOkResponseSchema,\n description: 'Deletes a person by id. Request body or query may provide the identifier.',\n errors: [\n {\n status: 422,\n description: 'Person has dependent records (e.g. linked deals); unlink or reassign before delete.',\n schema: z.object({\n error: z.string(),\n code: z.literal('PERSON_HAS_DEPENDENTS'),\n }),\n },\n ],\n },\n})\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
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: {