@open-mercato/core 0.5.1-develop.2663.2c29774b5b → 0.5.1-develop.2681.c559bb2bc3

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 (687) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/generated/entities/action_log/index.js +8 -0
  3. package/dist/generated/entities/action_log/index.js.map +2 -2
  4. package/dist/generated/entities/customer_company_billing/index.js +23 -0
  5. package/dist/generated/entities/customer_company_billing/index.js.map +7 -0
  6. package/dist/generated/entities/customer_deal/index.js +8 -0
  7. package/dist/generated/entities/customer_deal/index.js.map +2 -2
  8. package/dist/generated/entities/customer_deal_stage_transition/index.js +31 -0
  9. package/dist/generated/entities/customer_deal_stage_transition/index.js.map +7 -0
  10. package/dist/generated/entities/customer_dictionary_kind_setting/index.js +21 -0
  11. package/dist/generated/entities/customer_dictionary_kind_setting/index.js.map +7 -0
  12. package/dist/generated/entities/customer_entity/index.js +8 -0
  13. package/dist/generated/entities/customer_entity/index.js.map +2 -2
  14. package/dist/generated/entities/customer_entity_role/index.js +23 -0
  15. package/dist/generated/entities/customer_entity_role/index.js.map +7 -0
  16. package/dist/generated/entities/customer_interaction/index.js +23 -1
  17. package/dist/generated/entities/customer_interaction/index.js.map +2 -2
  18. package/dist/generated/entities/customer_label/index.js +19 -0
  19. package/dist/generated/entities/customer_label/index.js.map +7 -0
  20. package/dist/generated/entities/customer_label_assignment/index.js +17 -0
  21. package/dist/generated/entities/customer_label_assignment/index.js.map +7 -0
  22. package/dist/generated/entities/customer_person_company_link/index.js +21 -0
  23. package/dist/generated/entities/customer_person_company_link/index.js.map +7 -0
  24. package/dist/generated/entities/customer_person_company_role/index.js +17 -0
  25. package/dist/generated/entities/customer_person_company_role/index.js.map +7 -0
  26. package/dist/generated/entities/dictionary_entry/index.js +4 -0
  27. package/dist/generated/entities/dictionary_entry/index.js.map +2 -2
  28. package/dist/generated/entities.ids.generated.js +9 -1
  29. package/dist/generated/entities.ids.generated.js.map +2 -2
  30. package/dist/generated/entity-fields-registry.js +116 -1
  31. package/dist/generated/entity-fields-registry.js.map +2 -2
  32. package/dist/modules/attachments/api/route.js +46 -8
  33. package/dist/modules/attachments/api/route.js.map +2 -2
  34. package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js +208 -0
  35. package/dist/modules/audit_logs/api/audit-logs/actions/export/route.js.map +7 -0
  36. package/dist/modules/audit_logs/api/audit-logs/actions/route.js +52 -6
  37. package/dist/modules/audit_logs/api/audit-logs/actions/route.js.map +2 -2
  38. package/dist/modules/audit_logs/cli.js +62 -0
  39. package/dist/modules/audit_logs/cli.js.map +7 -0
  40. package/dist/modules/audit_logs/data/entities.js +21 -1
  41. package/dist/modules/audit_logs/data/entities.js.map +2 -2
  42. package/dist/modules/audit_logs/data/validators.js +9 -1
  43. package/dist/modules/audit_logs/data/validators.js.map +2 -2
  44. package/dist/modules/audit_logs/lib/changeRows.js +34 -0
  45. package/dist/modules/audit_logs/lib/changeRows.js.map +7 -0
  46. package/dist/modules/audit_logs/lib/display-helpers.js +2 -20
  47. package/dist/modules/audit_logs/lib/display-helpers.js.map +3 -3
  48. package/dist/modules/audit_logs/lib/projections.js +58 -0
  49. package/dist/modules/audit_logs/lib/projections.js.map +7 -0
  50. package/dist/modules/audit_logs/migrations/Migration20260412160533.js +21 -0
  51. package/dist/modules/audit_logs/migrations/Migration20260412160533.js.map +7 -0
  52. package/dist/modules/audit_logs/services/actionLogService.js +313 -79
  53. package/dist/modules/audit_logs/services/actionLogService.js.map +2 -2
  54. package/dist/modules/customers/acl.js +3 -1
  55. package/dist/modules/customers/acl.js.map +2 -2
  56. package/dist/modules/customers/api/activities/route.js +4 -0
  57. package/dist/modules/customers/api/activities/route.js.map +2 -2
  58. package/dist/modules/customers/api/assignable-staff/route.js +208 -0
  59. package/dist/modules/customers/api/assignable-staff/route.js.map +7 -0
  60. package/dist/modules/customers/api/companies/[id]/people/route.js +205 -0
  61. package/dist/modules/customers/api/companies/[id]/people/route.js.map +7 -0
  62. package/dist/modules/customers/api/companies/[id]/roles/route.js +22 -0
  63. package/dist/modules/customers/api/companies/[id]/roles/route.js.map +7 -0
  64. package/dist/modules/customers/api/companies/[id]/route.js +374 -32
  65. package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
  66. package/dist/modules/customers/api/companies/route.js +82 -7
  67. package/dist/modules/customers/api/companies/route.js.map +2 -2
  68. package/dist/modules/customers/api/deals/[id]/companies/route.js +172 -0
  69. package/dist/modules/customers/api/deals/[id]/companies/route.js.map +7 -0
  70. package/dist/modules/customers/api/deals/[id]/people/route.js +156 -0
  71. package/dist/modules/customers/api/deals/[id]/people/route.js.map +7 -0
  72. package/dist/modules/customers/api/deals/[id]/route.js +459 -53
  73. package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
  74. package/dist/modules/customers/api/deals/[id]/stats/route.js +195 -0
  75. package/dist/modules/customers/api/deals/[id]/stats/route.js.map +7 -0
  76. package/dist/modules/customers/api/deals/route.js +20 -10
  77. package/dist/modules/customers/api/deals/route.js.map +3 -3
  78. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +105 -4
  79. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  80. package/dist/modules/customers/api/dictionaries/[kind]/route.js +118 -42
  81. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  82. package/dist/modules/customers/api/dictionaries/context.js +30 -6
  83. package/dist/modules/customers/api/dictionaries/context.js.map +2 -2
  84. package/dist/modules/customers/api/dictionaries/kind-settings/route.js +207 -0
  85. package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +7 -0
  86. package/dist/modules/customers/api/entity-roles-factory.js +471 -0
  87. package/dist/modules/customers/api/entity-roles-factory.js.map +7 -0
  88. package/dist/modules/customers/api/interactions/conflicts/route.js +158 -0
  89. package/dist/modules/customers/api/interactions/conflicts/route.js.map +7 -0
  90. package/dist/modules/customers/api/interactions/counts/route.js +92 -0
  91. package/dist/modules/customers/api/interactions/counts/route.js.map +7 -0
  92. package/dist/modules/customers/api/interactions/route.js +83 -4
  93. package/dist/modules/customers/api/interactions/route.js.map +2 -2
  94. package/dist/modules/customers/api/labels/assign/route.js +189 -0
  95. package/dist/modules/customers/api/labels/assign/route.js.map +7 -0
  96. package/dist/modules/customers/api/labels/auth.js +17 -0
  97. package/dist/modules/customers/api/labels/auth.js.map +7 -0
  98. package/dist/modules/customers/api/labels/route.js +281 -0
  99. package/dist/modules/customers/api/labels/route.js.map +7 -0
  100. package/dist/modules/customers/api/labels/table-errors.js +38 -0
  101. package/dist/modules/customers/api/labels/table-errors.js.map +7 -0
  102. package/dist/modules/customers/api/labels/unassign/route.js +184 -0
  103. package/dist/modules/customers/api/labels/unassign/route.js.map +7 -0
  104. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +292 -0
  105. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +7 -0
  106. package/dist/modules/customers/api/people/[id]/companies/context.js +66 -0
  107. package/dist/modules/customers/api/people/[id]/companies/context.js.map +7 -0
  108. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +334 -0
  109. package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +7 -0
  110. package/dist/modules/customers/api/people/[id]/companies/route.js +205 -0
  111. package/dist/modules/customers/api/people/[id]/companies/route.js.map +7 -0
  112. package/dist/modules/customers/api/people/[id]/roles/route.js +22 -0
  113. package/dist/modules/customers/api/people/[id]/roles/route.js.map +7 -0
  114. package/dist/modules/customers/api/people/[id]/route.js +134 -21
  115. package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
  116. package/dist/modules/customers/api/people/route.js +122 -23
  117. package/dist/modules/customers/api/people/route.js.map +2 -2
  118. package/dist/modules/customers/api/todos/route.js +4 -0
  119. package/dist/modules/customers/api/todos/route.js.map +2 -2
  120. package/dist/modules/customers/api/utils.js +22 -0
  121. package/dist/modules/customers/api/utils.js.map +2 -2
  122. package/dist/modules/customers/backend/config/customers/page.js +2 -6
  123. package/dist/modules/customers/backend/config/customers/page.js.map +2 -2
  124. package/dist/modules/customers/backend/customers/companies/page.js +37 -26
  125. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  126. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +265 -262
  127. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +3 -3
  128. package/dist/modules/customers/backend/customers/deals/[id]/hooks/formatters.js +23 -0
  129. package/dist/modules/customers/backend/customers/deals/[id]/hooks/formatters.js.map +7 -0
  130. package/dist/modules/customers/backend/customers/deals/[id]/hooks/types.js +1 -0
  131. package/dist/modules/customers/backend/customers/deals/[id]/hooks/types.js.map +7 -0
  132. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +43 -0
  133. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +7 -0
  134. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js +264 -0
  135. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.js.map +7 -0
  136. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js +88 -0
  137. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js.map +7 -0
  138. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js +41 -0
  139. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealData.js.map +7 -0
  140. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js +66 -0
  141. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js.map +7 -0
  142. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealInjectedTabs.js +39 -0
  143. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealInjectedTabs.js.map +7 -0
  144. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealMutationContext.js +49 -0
  145. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealMutationContext.js.map +7 -0
  146. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.js +43 -0
  147. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.js.map +7 -0
  148. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useScheduleDialog.js +28 -0
  149. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useScheduleDialog.js.map +7 -0
  150. package/dist/modules/customers/backend/customers/deals/[id]/page.js +556 -503
  151. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +3 -3
  152. package/dist/modules/customers/backend/customers/deals/page.js +66 -21
  153. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  154. package/dist/modules/customers/backend/customers/people/page.js +36 -28
  155. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  156. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +318 -203
  157. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +3 -3
  158. package/dist/modules/customers/cli.js +105 -13
  159. package/dist/modules/customers/cli.js.map +2 -2
  160. package/dist/modules/customers/commands/activities.js +6 -0
  161. package/dist/modules/customers/commands/activities.js.map +2 -2
  162. package/dist/modules/customers/commands/deals.js +315 -107
  163. package/dist/modules/customers/commands/deals.js.map +2 -2
  164. package/dist/modules/customers/commands/dictionaries.js +166 -32
  165. package/dist/modules/customers/commands/dictionaries.js.map +2 -2
  166. package/dist/modules/customers/commands/dictionaryKindSettings.js +208 -0
  167. package/dist/modules/customers/commands/dictionaryKindSettings.js.map +7 -0
  168. package/dist/modules/customers/commands/entity-roles.js +415 -0
  169. package/dist/modules/customers/commands/entity-roles.js.map +7 -0
  170. package/dist/modules/customers/commands/index.js +4 -0
  171. package/dist/modules/customers/commands/index.js.map +2 -2
  172. package/dist/modules/customers/commands/interactions.js +108 -21
  173. package/dist/modules/customers/commands/interactions.js.map +2 -2
  174. package/dist/modules/customers/commands/labels.js +539 -0
  175. package/dist/modules/customers/commands/labels.js.map +7 -0
  176. package/dist/modules/customers/commands/people.js +560 -463
  177. package/dist/modules/customers/commands/people.js.map +3 -3
  178. package/dist/modules/customers/commands/personCompanyLinks.js +568 -0
  179. package/dist/modules/customers/commands/personCompanyLinks.js.map +7 -0
  180. package/dist/modules/customers/commands/shared.js +12 -4
  181. package/dist/modules/customers/commands/shared.js.map +2 -2
  182. package/dist/modules/customers/commands/todos.js +10 -1
  183. package/dist/modules/customers/commands/todos.js.map +2 -2
  184. package/dist/modules/customers/components/AddressEditor.js +1 -1
  185. package/dist/modules/customers/components/AddressEditor.js.map +2 -2
  186. package/dist/modules/customers/components/CustomersConfigurationSections.js +31 -0
  187. package/dist/modules/customers/components/CustomersConfigurationSections.js.map +7 -0
  188. package/dist/modules/customers/components/DictionarySettings.js +37 -2
  189. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  190. package/dist/modules/customers/components/detail/ActiveDealCard.js +121 -0
  191. package/dist/modules/customers/components/detail/ActiveDealCard.js.map +7 -0
  192. package/dist/modules/customers/components/detail/ActivitiesSection.js +222 -331
  193. package/dist/modules/customers/components/detail/ActivitiesSection.js.map +3 -3
  194. package/dist/modules/customers/components/detail/ActivityAiActions.js +36 -0
  195. package/dist/modules/customers/components/detail/ActivityAiActions.js.map +7 -0
  196. package/dist/modules/customers/components/detail/ActivityCard.js +126 -0
  197. package/dist/modules/customers/components/detail/ActivityCard.js.map +7 -0
  198. package/dist/modules/customers/components/detail/ActivityHistorySection.js +340 -0
  199. package/dist/modules/customers/components/detail/ActivityHistorySection.js.map +7 -0
  200. package/dist/modules/customers/components/detail/ActivityLogTab.js +56 -0
  201. package/dist/modules/customers/components/detail/ActivityLogTab.js.map +7 -0
  202. package/dist/modules/customers/components/detail/ActivityTimeline.js +108 -0
  203. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +7 -0
  204. package/dist/modules/customers/components/detail/ActivityTimelineFilters.js +139 -0
  205. package/dist/modules/customers/components/detail/ActivityTimelineFilters.js.map +7 -0
  206. package/dist/modules/customers/components/detail/ActivityTypeSelector.js +42 -0
  207. package/dist/modules/customers/components/detail/ActivityTypeSelector.js.map +7 -0
  208. package/dist/modules/customers/components/detail/AiActionChips.js +38 -0
  209. package/dist/modules/customers/components/detail/AiActionChips.js.map +7 -0
  210. package/dist/modules/customers/components/detail/AssignRoleDialog.js +534 -0
  211. package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +7 -0
  212. package/dist/modules/customers/components/detail/ChangelogEntryRow.js +79 -0
  213. package/dist/modules/customers/components/detail/ChangelogEntryRow.js.map +7 -0
  214. package/dist/modules/customers/components/detail/ChangelogFilters.js +176 -0
  215. package/dist/modules/customers/components/detail/ChangelogFilters.js.map +7 -0
  216. package/dist/modules/customers/components/detail/ChangelogKpiCards.js +88 -0
  217. package/dist/modules/customers/components/detail/ChangelogKpiCards.js.map +7 -0
  218. package/dist/modules/customers/components/detail/ChangelogTab.js +470 -0
  219. package/dist/modules/customers/components/detail/ChangelogTab.js.map +7 -0
  220. package/dist/modules/customers/components/detail/ComingSoonPlaceholder.js +16 -0
  221. package/dist/modules/customers/components/detail/ComingSoonPlaceholder.js.map +7 -0
  222. package/dist/modules/customers/components/detail/CompanyCard.js +283 -0
  223. package/dist/modules/customers/components/detail/CompanyCard.js.map +7 -0
  224. package/dist/modules/customers/components/detail/CompanyDashboardTab.js +133 -0
  225. package/dist/modules/customers/components/detail/CompanyDashboardTab.js.map +7 -0
  226. package/dist/modules/customers/components/detail/CompanyDetailHeader.js +191 -0
  227. package/dist/modules/customers/components/detail/CompanyDetailHeader.js.map +7 -0
  228. package/dist/modules/customers/components/detail/CompanyDetailTabs.js +123 -0
  229. package/dist/modules/customers/components/detail/CompanyDetailTabs.js.map +7 -0
  230. package/dist/modules/customers/components/detail/CompanyKpiBar.js +174 -0
  231. package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +7 -0
  232. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +514 -230
  233. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  234. package/dist/modules/customers/components/detail/CompanyTagsDialog.js +22 -0
  235. package/dist/modules/customers/components/detail/CompanyTagsDialog.js.map +7 -0
  236. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +159 -0
  237. package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +7 -0
  238. package/dist/modules/customers/components/detail/CreatePersonDialog.js +135 -0
  239. package/dist/modules/customers/components/detail/CreatePersonDialog.js.map +7 -0
  240. package/dist/modules/customers/components/detail/DealClosureActionBar.js +59 -0
  241. package/dist/modules/customers/components/detail/DealClosureActionBar.js.map +7 -0
  242. package/dist/modules/customers/components/detail/DealDetailHeader.js +237 -0
  243. package/dist/modules/customers/components/detail/DealDetailHeader.js.map +7 -0
  244. package/dist/modules/customers/components/detail/DealDetailTabs.js +109 -0
  245. package/dist/modules/customers/components/detail/DealDetailTabs.js.map +7 -0
  246. package/dist/modules/customers/components/detail/DealForm.js +219 -92
  247. package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
  248. package/dist/modules/customers/components/detail/DealLinkedEntitiesTab.js +295 -0
  249. package/dist/modules/customers/components/detail/DealLinkedEntitiesTab.js.map +7 -0
  250. package/dist/modules/customers/components/detail/DealLostSummaryDialog.js +107 -0
  251. package/dist/modules/customers/components/detail/DealLostSummaryDialog.js.map +7 -0
  252. package/dist/modules/customers/components/detail/DealWonPopup.js +113 -0
  253. package/dist/modules/customers/components/detail/DealWonPopup.js.map +7 -0
  254. package/dist/modules/customers/components/detail/DealsSection.js +206 -193
  255. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  256. package/dist/modules/customers/components/detail/DecisionMakersFooter.js +39 -0
  257. package/dist/modules/customers/components/detail/DecisionMakersFooter.js.map +7 -0
  258. package/dist/modules/customers/components/detail/EntityTagsDialog.js +1096 -0
  259. package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +7 -0
  260. package/dist/modules/customers/components/detail/InlineActivityComposer.js +197 -0
  261. package/dist/modules/customers/components/detail/InlineActivityComposer.js.map +7 -0
  262. package/dist/modules/customers/components/detail/ManageTagsDialog.js +1091 -0
  263. package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +7 -0
  264. package/dist/modules/customers/components/detail/MiniWeekCalendar.js +272 -0
  265. package/dist/modules/customers/components/detail/MiniWeekCalendar.js.map +7 -0
  266. package/dist/modules/customers/components/detail/MobilePersonDetail.js +106 -0
  267. package/dist/modules/customers/components/detail/MobilePersonDetail.js.map +7 -0
  268. package/dist/modules/customers/components/detail/NextStepCard.js +72 -0
  269. package/dist/modules/customers/components/detail/NextStepCard.js.map +7 -0
  270. package/dist/modules/customers/components/detail/PersonCard.js +192 -0
  271. package/dist/modules/customers/components/detail/PersonCard.js.map +7 -0
  272. package/dist/modules/customers/components/detail/PersonCompaniesSection.js +345 -0
  273. package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +7 -0
  274. package/dist/modules/customers/components/detail/PersonDetailHeader.js +220 -0
  275. package/dist/modules/customers/components/detail/PersonDetailHeader.js.map +7 -0
  276. package/dist/modules/customers/components/detail/PersonDetailTabs.js +122 -0
  277. package/dist/modules/customers/components/detail/PersonDetailTabs.js.map +7 -0
  278. package/dist/modules/customers/components/detail/PersonTagsDialog.js +24 -0
  279. package/dist/modules/customers/components/detail/PersonTagsDialog.js.map +7 -0
  280. package/dist/modules/customers/components/detail/PipelineStepper.js +191 -0
  281. package/dist/modules/customers/components/detail/PipelineStepper.js.map +7 -0
  282. package/dist/modules/customers/components/detail/PlannedActivitiesSection.js +222 -0
  283. package/dist/modules/customers/components/detail/PlannedActivitiesSection.js.map +7 -0
  284. package/dist/modules/customers/components/detail/RelationshipHealthCard.js +49 -0
  285. package/dist/modules/customers/components/detail/RelationshipHealthCard.js.map +7 -0
  286. package/dist/modules/customers/components/detail/RoleAssignmentRow.js +189 -0
  287. package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +7 -0
  288. package/dist/modules/customers/components/detail/RolesSection.js +234 -0
  289. package/dist/modules/customers/components/detail/RolesSection.js.map +7 -0
  290. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +410 -0
  291. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +7 -0
  292. package/dist/modules/customers/components/detail/aiActionCatalog.js +41 -0
  293. package/dist/modules/customers/components/detail/aiActionCatalog.js.map +7 -0
  294. package/dist/modules/customers/components/detail/assignableStaff.js +48 -0
  295. package/dist/modules/customers/components/detail/assignableStaff.js.map +7 -0
  296. package/dist/modules/customers/components/detail/dashboard/ActiveDealWidget.js +48 -0
  297. package/dist/modules/customers/components/detail/dashboard/ActiveDealWidget.js.map +7 -0
  298. package/dist/modules/customers/components/detail/dashboard/OpenTasksWidget.js +86 -0
  299. package/dist/modules/customers/components/detail/dashboard/OpenTasksWidget.js.map +7 -0
  300. package/dist/modules/customers/components/detail/dashboard/RecentActivityWidget.js +53 -0
  301. package/dist/modules/customers/components/detail/dashboard/RecentActivityWidget.js.map +7 -0
  302. package/dist/modules/customers/components/detail/dashboard/RelationshipHealthWidget.js +30 -0
  303. package/dist/modules/customers/components/detail/dashboard/RelationshipHealthWidget.js.map +7 -0
  304. package/dist/modules/customers/components/detail/dashboard/UpcomingMeetingsWidget.js +43 -0
  305. package/dist/modules/customers/components/detail/dashboard/UpcomingMeetingsWidget.js.map +7 -0
  306. package/dist/modules/customers/components/detail/dashboard/helpers.js +71 -0
  307. package/dist/modules/customers/components/detail/dashboard/helpers.js.map +7 -0
  308. package/dist/modules/customers/components/detail/healthScoreUtils.js +69 -0
  309. package/dist/modules/customers/components/detail/healthScoreUtils.js.map +7 -0
  310. package/dist/modules/customers/components/detail/hooks/useCurrencyDictionary.js +5 -5
  311. package/dist/modules/customers/components/detail/hooks/useCurrencyDictionary.js.map +2 -2
  312. package/dist/modules/customers/components/detail/hooks/useCustomerDictionary.js +9 -8
  313. package/dist/modules/customers/components/detail/hooks/useCustomerDictionary.js.map +3 -3
  314. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js +65 -0
  315. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js.map +7 -0
  316. package/dist/modules/customers/components/detail/notesAdapter.js +70 -30
  317. package/dist/modules/customers/components/detail/notesAdapter.js.map +2 -2
  318. package/dist/modules/customers/components/detail/pipelineStageUtils.js +26 -0
  319. package/dist/modules/customers/components/detail/pipelineStageUtils.js.map +7 -0
  320. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +144 -0
  321. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +7 -0
  322. package/dist/modules/customers/components/detail/schedule/FooterFields.js +60 -0
  323. package/dist/modules/customers/components/detail/schedule/FooterFields.js.map +7 -0
  324. package/dist/modules/customers/components/detail/schedule/LinkedEntitiesField.js +216 -0
  325. package/dist/modules/customers/components/detail/schedule/LinkedEntitiesField.js.map +7 -0
  326. package/dist/modules/customers/components/detail/schedule/LocationField.js +34 -0
  327. package/dist/modules/customers/components/detail/schedule/LocationField.js.map +7 -0
  328. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +226 -0
  329. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +7 -0
  330. package/dist/modules/customers/components/detail/schedule/fieldConfig.js +69 -0
  331. package/dist/modules/customers/components/detail/schedule/fieldConfig.js.map +7 -0
  332. package/dist/modules/customers/components/detail/schedule/index.js +21 -0
  333. package/dist/modules/customers/components/detail/schedule/index.js.map +7 -0
  334. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +172 -0
  335. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +7 -0
  336. package/dist/modules/customers/components/detail/utils.js +23 -0
  337. package/dist/modules/customers/components/detail/utils.js.map +2 -2
  338. package/dist/modules/customers/components/formConfig.js +144 -22
  339. package/dist/modules/customers/components/formConfig.js.map +2 -2
  340. package/dist/modules/customers/components/linking/LinkEntityDialog.js +661 -0
  341. package/dist/modules/customers/components/linking/LinkEntityDialog.js.map +7 -0
  342. package/dist/modules/customers/components/linking/adapters/companyAdapter.js +252 -0
  343. package/dist/modules/customers/components/linking/adapters/companyAdapter.js.map +7 -0
  344. package/dist/modules/customers/components/linking/adapters/dealAdapter.js +384 -0
  345. package/dist/modules/customers/components/linking/adapters/dealAdapter.js.map +7 -0
  346. package/dist/modules/customers/components/linking/adapters/personAdapter.js +324 -0
  347. package/dist/modules/customers/components/linking/adapters/personAdapter.js.map +7 -0
  348. package/dist/modules/customers/components/list/CollectionPreviewCell.js +53 -0
  349. package/dist/modules/customers/components/list/CollectionPreviewCell.js.map +7 -0
  350. package/dist/modules/customers/data/entities.js +407 -1
  351. package/dist/modules/customers/data/entities.js.map +2 -2
  352. package/dist/modules/customers/data/validators.js +139 -21
  353. package/dist/modules/customers/data/validators.js.map +2 -2
  354. package/dist/modules/customers/events.js +19 -1
  355. package/dist/modules/customers/events.js.map +2 -2
  356. package/dist/modules/customers/lib/customerRoleTypes.js +19 -0
  357. package/dist/modules/customers/lib/customerRoleTypes.js.map +7 -0
  358. package/dist/modules/customers/lib/dealClosureNotification.js +39 -0
  359. package/dist/modules/customers/lib/dealClosureNotification.js.map +7 -0
  360. package/dist/modules/customers/lib/dealStageTransitionTable.js +29 -0
  361. package/dist/modules/customers/lib/dealStageTransitionTable.js.map +7 -0
  362. package/dist/modules/customers/lib/dictionaries.js +25 -0
  363. package/dist/modules/customers/lib/dictionaries.js.map +2 -2
  364. package/dist/modules/customers/lib/interactionReadModel.js +10 -0
  365. package/dist/modules/customers/lib/interactionReadModel.js.map +2 -2
  366. package/dist/modules/customers/lib/personCompanies.js +235 -0
  367. package/dist/modules/customers/lib/personCompanies.js.map +7 -0
  368. package/dist/modules/customers/lib/personCompanyLinkTable.js +42 -0
  369. package/dist/modules/customers/lib/personCompanyLinkTable.js.map +7 -0
  370. package/dist/modules/customers/lib/roleTypeUsage.js +104 -0
  371. package/dist/modules/customers/lib/roleTypeUsage.js.map +7 -0
  372. package/dist/modules/customers/migrations/Migration20260406214502.js +18 -0
  373. package/dist/modules/customers/migrations/Migration20260406214502.js.map +7 -0
  374. package/dist/modules/customers/migrations/Migration20260408135736.js +17 -0
  375. package/dist/modules/customers/migrations/Migration20260408135736.js.map +7 -0
  376. package/dist/modules/customers/migrations/Migration20260408225345.js +21 -0
  377. package/dist/modules/customers/migrations/Migration20260408225345.js.map +7 -0
  378. package/dist/modules/customers/migrations/Migration20260411075533.js +27 -0
  379. package/dist/modules/customers/migrations/Migration20260411075533.js.map +7 -0
  380. package/dist/modules/customers/migrations/Migration20260411103551.js +13 -0
  381. package/dist/modules/customers/migrations/Migration20260411103551.js.map +7 -0
  382. package/dist/modules/customers/migrations/Migration20260411130944.js +26 -0
  383. package/dist/modules/customers/migrations/Migration20260411130944.js.map +7 -0
  384. package/dist/modules/customers/migrations/Migration20260415095203.js +13 -0
  385. package/dist/modules/customers/migrations/Migration20260415095203.js.map +7 -0
  386. package/dist/modules/customers/migrations/Migration20260415135056.js +20 -0
  387. package/dist/modules/customers/migrations/Migration20260415135056.js.map +7 -0
  388. package/dist/modules/customers/migrations/Migration20260417140000.js +15 -0
  389. package/dist/modules/customers/migrations/Migration20260417140000.js.map +7 -0
  390. package/dist/modules/customers/migrations/Migration20260417160000.js +17 -0
  391. package/dist/modules/customers/migrations/Migration20260417160000.js.map +7 -0
  392. package/dist/modules/customers/migrations/Migration20260417235407.js +13 -0
  393. package/dist/modules/customers/migrations/Migration20260417235407.js.map +7 -0
  394. package/dist/modules/customers/setup.js +16 -1
  395. package/dist/modules/customers/setup.js.map +2 -2
  396. package/dist/modules/customers/subscribers/deal-closure-notification.js +16 -0
  397. package/dist/modules/customers/subscribers/deal-closure-notification.js.map +7 -0
  398. package/dist/modules/customers/subscribers/deal-lost-notification.js +16 -0
  399. package/dist/modules/customers/subscribers/deal-lost-notification.js.map +7 -0
  400. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +2 -0
  401. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  402. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +154 -0
  403. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +7 -0
  404. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +6 -2
  405. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
  406. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +154 -0
  407. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +7 -0
  408. package/dist/modules/dictionaries/api/context.js +8 -1
  409. package/dist/modules/dictionaries/api/context.js.map +2 -2
  410. package/dist/modules/dictionaries/api/openapi.js +18 -1
  411. package/dist/modules/dictionaries/api/openapi.js.map +2 -2
  412. package/dist/modules/dictionaries/commands/entry-operations.js +388 -0
  413. package/dist/modules/dictionaries/commands/entry-operations.js.map +7 -0
  414. package/dist/modules/dictionaries/commands/factory.js +24 -3
  415. package/dist/modules/dictionaries/commands/factory.js.map +2 -2
  416. package/dist/modules/dictionaries/commands/index.js +1 -0
  417. package/dist/modules/dictionaries/commands/index.js.map +2 -2
  418. package/dist/modules/dictionaries/components/DictionaryTable.js +6 -3
  419. package/dist/modules/dictionaries/components/DictionaryTable.js.map +2 -2
  420. package/dist/modules/dictionaries/data/entities.js +11 -1
  421. package/dist/modules/dictionaries/data/entities.js.map +2 -2
  422. package/dist/modules/dictionaries/data/validators.js +28 -2
  423. package/dist/modules/dictionaries/data/validators.js.map +2 -2
  424. package/dist/modules/dictionaries/events.js +18 -0
  425. package/dist/modules/dictionaries/events.js.map +7 -0
  426. package/dist/modules/dictionaries/lib/clientEntries.js +43 -0
  427. package/dist/modules/dictionaries/lib/clientEntries.js.map +7 -0
  428. package/dist/modules/dictionaries/migrations/Migration20260410171544.js +45 -0
  429. package/dist/modules/dictionaries/migrations/Migration20260410171544.js.map +7 -0
  430. package/dist/modules/inbox_ops/api/proposals/[id]/route.js +4 -1
  431. package/dist/modules/inbox_ops/api/proposals/[id]/route.js.map +2 -2
  432. package/dist/modules/query_index/lib/engine.js +1 -1
  433. package/dist/modules/query_index/lib/engine.js.map +2 -2
  434. package/dist/modules/sales/components/documents/AddressesSection.js +82 -42
  435. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  436. package/dist/modules/sales/lib/dictionaries.js +16 -0
  437. package/dist/modules/sales/lib/dictionaries.js.map +2 -2
  438. package/dist/modules/sales/widgets/injection-table.js +5 -1
  439. package/dist/modules/sales/widgets/injection-table.js.map +2 -2
  440. package/generated/entities/action_log/index.ts +4 -0
  441. package/generated/entities/customer_company_billing/index.ts +10 -0
  442. package/generated/entities/customer_deal/index.ts +4 -0
  443. package/generated/entities/customer_deal_stage_transition/index.ts +14 -0
  444. package/generated/entities/customer_dictionary_kind_setting/index.ts +9 -0
  445. package/generated/entities/customer_entity/index.ts +4 -0
  446. package/generated/entities/customer_entity_role/index.ts +10 -0
  447. package/generated/entities/customer_interaction/index.ts +11 -0
  448. package/generated/entities/customer_label/index.ts +8 -0
  449. package/generated/entities/customer_label_assignment/index.ts +7 -0
  450. package/generated/entities/customer_person_company_link/index.ts +9 -0
  451. package/generated/entities/customer_person_company_role/index.ts +7 -0
  452. package/generated/entities/dictionary_entry/index.ts +2 -0
  453. package/generated/entities.ids.generated.ts +9 -1
  454. package/generated/entity-fields-registry.ts +116 -1
  455. package/package.json +3 -3
  456. package/src/modules/attachments/api/route.ts +48 -6
  457. package/src/modules/attachments/i18n/de.json +4 -0
  458. package/src/modules/attachments/i18n/en.json +4 -0
  459. package/src/modules/attachments/i18n/es.json +4 -0
  460. package/src/modules/attachments/i18n/pl.json +4 -0
  461. package/src/modules/audit_logs/api/audit-logs/actions/export/route.ts +260 -0
  462. package/src/modules/audit_logs/api/audit-logs/actions/route.ts +81 -6
  463. package/src/modules/audit_logs/cli.ts +79 -0
  464. package/src/modules/audit_logs/data/entities.ts +17 -0
  465. package/src/modules/audit_logs/data/validators.ts +9 -1
  466. package/src/modules/audit_logs/lib/changeRows.ts +47 -0
  467. package/src/modules/audit_logs/lib/display-helpers.tsx +4 -30
  468. package/src/modules/audit_logs/lib/projections.ts +110 -0
  469. package/src/modules/audit_logs/migrations/.snapshot-open-mercato.json +325 -2
  470. package/src/modules/audit_logs/migrations/Migration20260412160533.ts +21 -0
  471. package/src/modules/audit_logs/services/actionLogService.ts +455 -85
  472. package/src/modules/catalog/i18n/de.json +1 -0
  473. package/src/modules/catalog/i18n/en.json +1 -0
  474. package/src/modules/catalog/i18n/es.json +1 -0
  475. package/src/modules/catalog/i18n/pl.json +1 -0
  476. package/src/modules/customer_accounts/i18n/de.json +2 -0
  477. package/src/modules/customer_accounts/i18n/en.json +2 -0
  478. package/src/modules/customer_accounts/i18n/es.json +2 -0
  479. package/src/modules/customer_accounts/i18n/pl.json +2 -0
  480. package/src/modules/customers/acl.ts +2 -0
  481. package/src/modules/customers/api/activities/route.ts +4 -0
  482. package/src/modules/customers/api/assignable-staff/route.ts +250 -0
  483. package/src/modules/customers/api/companies/[id]/people/route.ts +244 -0
  484. package/src/modules/customers/api/companies/[id]/roles/route.ts +15 -0
  485. package/src/modules/customers/api/companies/[id]/route.ts +458 -40
  486. package/src/modules/customers/api/companies/route.ts +93 -15
  487. package/src/modules/customers/api/deals/[id]/companies/route.ts +203 -0
  488. package/src/modules/customers/api/deals/[id]/people/route.ts +182 -0
  489. package/src/modules/customers/api/deals/[id]/route.ts +554 -57
  490. package/src/modules/customers/api/deals/[id]/stats/route.ts +221 -0
  491. package/src/modules/customers/api/deals/route.ts +35 -46
  492. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +105 -3
  493. package/src/modules/customers/api/dictionaries/[kind]/route.ts +143 -44
  494. package/src/modules/customers/api/dictionaries/context.ts +45 -16
  495. package/src/modules/customers/api/dictionaries/kind-settings/route.ts +232 -0
  496. package/src/modules/customers/api/entity-roles-factory.ts +520 -0
  497. package/src/modules/customers/api/interactions/conflicts/route.ts +196 -0
  498. package/src/modules/customers/api/interactions/counts/route.ts +112 -0
  499. package/src/modules/customers/api/interactions/route.ts +95 -2
  500. package/src/modules/customers/api/labels/assign/route.ts +202 -0
  501. package/src/modules/customers/api/labels/auth.ts +19 -0
  502. package/src/modules/customers/api/labels/route.ts +310 -0
  503. package/src/modules/customers/api/labels/table-errors.ts +36 -0
  504. package/src/modules/customers/api/labels/unassign/route.ts +197 -0
  505. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +331 -0
  506. package/src/modules/customers/api/people/[id]/companies/context.ts +70 -0
  507. package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +384 -0
  508. package/src/modules/customers/api/people/[id]/companies/route.ts +215 -0
  509. package/src/modules/customers/api/people/[id]/roles/route.ts +15 -0
  510. package/src/modules/customers/api/people/[id]/route.ts +153 -26
  511. package/src/modules/customers/api/people/route.ts +134 -31
  512. package/src/modules/customers/api/todos/route.ts +4 -0
  513. package/src/modules/customers/api/utils.ts +36 -0
  514. package/src/modules/customers/backend/config/customers/page.tsx +2 -6
  515. package/src/modules/customers/backend/customers/companies/page.tsx +36 -26
  516. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +277 -262
  517. package/src/modules/customers/backend/customers/deals/[id]/hooks/formatters.ts +19 -0
  518. package/src/modules/customers/backend/customers/deals/[id]/hooks/types.ts +104 -0
  519. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +60 -0
  520. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealAssociations.ts +362 -0
  521. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.ts +113 -0
  522. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealData.ts +52 -0
  523. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.ts +86 -0
  524. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealInjectedTabs.tsx +60 -0
  525. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealMutationContext.ts +76 -0
  526. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealPipeline.ts +56 -0
  527. package/src/modules/customers/backend/customers/deals/[id]/hooks/useScheduleDialog.ts +38 -0
  528. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +587 -624
  529. package/src/modules/customers/backend/customers/deals/page.tsx +71 -28
  530. package/src/modules/customers/backend/customers/people/page.tsx +35 -29
  531. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +343 -209
  532. package/src/modules/customers/cli.ts +107 -12
  533. package/src/modules/customers/commands/activities.ts +13 -0
  534. package/src/modules/customers/commands/deals.ts +386 -114
  535. package/src/modules/customers/commands/dictionaries.ts +175 -32
  536. package/src/modules/customers/commands/dictionaryKindSettings.ts +268 -0
  537. package/src/modules/customers/commands/entity-roles.ts +494 -0
  538. package/src/modules/customers/commands/index.ts +4 -0
  539. package/src/modules/customers/commands/interactions.ts +125 -21
  540. package/src/modules/customers/commands/labels.ts +626 -0
  541. package/src/modules/customers/commands/people.ts +373 -259
  542. package/src/modules/customers/commands/personCompanyLinks.ts +654 -0
  543. package/src/modules/customers/commands/shared.ts +16 -15
  544. package/src/modules/customers/commands/todos.ts +17 -1
  545. package/src/modules/customers/components/AddressEditor.tsx +1 -1
  546. package/src/modules/customers/components/CustomersConfigurationSections.tsx +36 -0
  547. package/src/modules/customers/components/DictionarySettings.tsx +43 -2
  548. package/src/modules/customers/components/detail/ActiveDealCard.tsx +175 -0
  549. package/src/modules/customers/components/detail/ActivitiesSection.tsx +267 -361
  550. package/src/modules/customers/components/detail/ActivityAiActions.tsx +49 -0
  551. package/src/modules/customers/components/detail/ActivityCard.tsx +154 -0
  552. package/src/modules/customers/components/detail/ActivityHistorySection.tsx +412 -0
  553. package/src/modules/customers/components/detail/ActivityLogTab.tsx +67 -0
  554. package/src/modules/customers/components/detail/ActivityTimeline.tsx +158 -0
  555. package/src/modules/customers/components/detail/ActivityTimelineFilters.tsx +163 -0
  556. package/src/modules/customers/components/detail/ActivityTypeSelector.tsx +53 -0
  557. package/src/modules/customers/components/detail/AiActionChips.tsx +48 -0
  558. package/src/modules/customers/components/detail/AssignRoleDialog.tsx +672 -0
  559. package/src/modules/customers/components/detail/ChangelogEntryRow.tsx +132 -0
  560. package/src/modules/customers/components/detail/ChangelogFilters.tsx +193 -0
  561. package/src/modules/customers/components/detail/ChangelogKpiCards.tsx +107 -0
  562. package/src/modules/customers/components/detail/ChangelogTab.tsx +629 -0
  563. package/src/modules/customers/components/detail/ComingSoonPlaceholder.tsx +21 -0
  564. package/src/modules/customers/components/detail/CompanyCard.tsx +419 -0
  565. package/src/modules/customers/components/detail/CompanyDashboardTab.tsx +161 -0
  566. package/src/modules/customers/components/detail/CompanyDetailHeader.tsx +243 -0
  567. package/src/modules/customers/components/detail/CompanyDetailTabs.tsx +172 -0
  568. package/src/modules/customers/components/detail/CompanyKpiBar.tsx +206 -0
  569. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +582 -288
  570. package/src/modules/customers/components/detail/CompanyTagsDialog.tsx +23 -0
  571. package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +210 -0
  572. package/src/modules/customers/components/detail/CreatePersonDialog.tsx +178 -0
  573. package/src/modules/customers/components/detail/DealClosureActionBar.tsx +63 -0
  574. package/src/modules/customers/components/detail/DealDetailHeader.tsx +335 -0
  575. package/src/modules/customers/components/detail/DealDetailTabs.tsx +154 -0
  576. package/src/modules/customers/components/detail/DealForm.tsx +253 -101
  577. package/src/modules/customers/components/detail/DealLinkedEntitiesTab.tsx +349 -0
  578. package/src/modules/customers/components/detail/DealLostSummaryDialog.tsx +156 -0
  579. package/src/modules/customers/components/detail/DealWonPopup.tsx +164 -0
  580. package/src/modules/customers/components/detail/DealsSection.tsx +276 -221
  581. package/src/modules/customers/components/detail/DecisionMakersFooter.tsx +56 -0
  582. package/src/modules/customers/components/detail/EntityTagsDialog.tsx +1372 -0
  583. package/src/modules/customers/components/detail/InlineActivityComposer.tsx +239 -0
  584. package/src/modules/customers/components/detail/ManageTagsDialog.tsx +1331 -0
  585. package/src/modules/customers/components/detail/MiniWeekCalendar.tsx +338 -0
  586. package/src/modules/customers/components/detail/MobilePersonDetail.tsx +124 -0
  587. package/src/modules/customers/components/detail/NextStepCard.tsx +104 -0
  588. package/src/modules/customers/components/detail/PersonCard.tsx +238 -0
  589. package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +426 -0
  590. package/src/modules/customers/components/detail/PersonDetailHeader.tsx +294 -0
  591. package/src/modules/customers/components/detail/PersonDetailTabs.tsx +172 -0
  592. package/src/modules/customers/components/detail/PersonTagsDialog.tsx +26 -0
  593. package/src/modules/customers/components/detail/PipelineStepper.tsx +245 -0
  594. package/src/modules/customers/components/detail/PlannedActivitiesSection.tsx +255 -0
  595. package/src/modules/customers/components/detail/RelationshipHealthCard.tsx +63 -0
  596. package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +248 -0
  597. package/src/modules/customers/components/detail/RolesSection.tsx +311 -0
  598. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +481 -0
  599. package/src/modules/customers/components/detail/aiActionCatalog.ts +77 -0
  600. package/src/modules/customers/components/detail/assignableStaff.ts +124 -0
  601. package/src/modules/customers/components/detail/dashboard/ActiveDealWidget.tsx +63 -0
  602. package/src/modules/customers/components/detail/dashboard/OpenTasksWidget.tsx +114 -0
  603. package/src/modules/customers/components/detail/dashboard/RecentActivityWidget.tsx +69 -0
  604. package/src/modules/customers/components/detail/dashboard/RelationshipHealthWidget.tsx +40 -0
  605. package/src/modules/customers/components/detail/dashboard/UpcomingMeetingsWidget.tsx +64 -0
  606. package/src/modules/customers/components/detail/dashboard/helpers.ts +78 -0
  607. package/src/modules/customers/components/detail/healthScoreUtils.ts +91 -0
  608. package/src/modules/customers/components/detail/hooks/useCurrencyDictionary.ts +8 -8
  609. package/src/modules/customers/components/detail/hooks/useCustomerDictionary.ts +10 -6
  610. package/src/modules/customers/components/detail/hooks/useInteractionMutations.ts +91 -0
  611. package/src/modules/customers/components/detail/notesAdapter.ts +91 -30
  612. package/src/modules/customers/components/detail/pipelineStageUtils.ts +29 -0
  613. package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +187 -0
  614. package/src/modules/customers/components/detail/schedule/FooterFields.tsx +79 -0
  615. package/src/modules/customers/components/detail/schedule/LinkedEntitiesField.tsx +277 -0
  616. package/src/modules/customers/components/detail/schedule/LocationField.tsx +42 -0
  617. package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +255 -0
  618. package/src/modules/customers/components/detail/schedule/fieldConfig.ts +70 -0
  619. package/src/modules/customers/components/detail/schedule/index.ts +9 -0
  620. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +221 -0
  621. package/src/modules/customers/components/detail/types.ts +16 -0
  622. package/src/modules/customers/components/detail/utils.ts +25 -0
  623. package/src/modules/customers/components/formConfig.tsx +223 -28
  624. package/src/modules/customers/components/linking/LinkEntityDialog.tsx +920 -0
  625. package/src/modules/customers/components/linking/adapters/companyAdapter.tsx +398 -0
  626. package/src/modules/customers/components/linking/adapters/dealAdapter.tsx +578 -0
  627. package/src/modules/customers/components/linking/adapters/personAdapter.tsx +512 -0
  628. package/src/modules/customers/components/list/CollectionPreviewCell.tsx +66 -0
  629. package/src/modules/customers/data/entities.ts +353 -1
  630. package/src/modules/customers/data/validators.ts +170 -19
  631. package/src/modules/customers/events.ts +22 -0
  632. package/src/modules/customers/i18n/de.json +841 -2
  633. package/src/modules/customers/i18n/en.json +841 -2
  634. package/src/modules/customers/i18n/es.json +840 -1
  635. package/src/modules/customers/i18n/pl.json +841 -2
  636. package/src/modules/customers/lib/customerRoleTypes.ts +24 -0
  637. package/src/modules/customers/lib/dealClosureNotification.ts +64 -0
  638. package/src/modules/customers/lib/dealStageTransitionTable.ts +32 -0
  639. package/src/modules/customers/lib/dictionaries.ts +26 -10
  640. package/src/modules/customers/lib/interactionReadModel.ts +10 -0
  641. package/src/modules/customers/lib/personCompanies.ts +317 -0
  642. package/src/modules/customers/lib/personCompanyLinkTable.ts +58 -0
  643. package/src/modules/customers/lib/roleTypeUsage.ts +146 -0
  644. package/src/modules/customers/migrations/.snapshot-open-mercato.json +2747 -798
  645. package/src/modules/customers/migrations/Migration20260406214502.ts +19 -0
  646. package/src/modules/customers/migrations/Migration20260408135736.ts +15 -0
  647. package/src/modules/customers/migrations/Migration20260408225345.ts +23 -0
  648. package/src/modules/customers/migrations/Migration20260411075533.ts +30 -0
  649. package/src/modules/customers/migrations/Migration20260411103551.ts +13 -0
  650. package/src/modules/customers/migrations/Migration20260411130944.ts +30 -0
  651. package/src/modules/customers/migrations/Migration20260415095203.ts +13 -0
  652. package/src/modules/customers/migrations/Migration20260415135056.ts +22 -0
  653. package/src/modules/customers/migrations/Migration20260417140000.ts +15 -0
  654. package/src/modules/customers/migrations/Migration20260417160000.ts +17 -0
  655. package/src/modules/customers/migrations/Migration20260417235407.ts +13 -0
  656. package/src/modules/customers/setup.ts +15 -0
  657. package/src/modules/customers/subscribers/deal-closure-notification.ts +22 -0
  658. package/src/modules/customers/subscribers/deal-lost-notification.ts +22 -0
  659. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +2 -0
  660. package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +162 -0
  661. package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +6 -2
  662. package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +162 -0
  663. package/src/modules/dictionaries/api/context.ts +9 -0
  664. package/src/modules/dictionaries/api/openapi.ts +17 -0
  665. package/src/modules/dictionaries/commands/entry-operations.ts +457 -0
  666. package/src/modules/dictionaries/commands/factory.ts +31 -3
  667. package/src/modules/dictionaries/commands/index.ts +1 -0
  668. package/src/modules/dictionaries/components/DictionaryTable.tsx +15 -6
  669. package/src/modules/dictionaries/data/entities.ts +9 -0
  670. package/src/modules/dictionaries/data/validators.ts +34 -0
  671. package/src/modules/dictionaries/events.ts +20 -0
  672. package/src/modules/dictionaries/i18n/de.json +2 -0
  673. package/src/modules/dictionaries/i18n/en.json +2 -0
  674. package/src/modules/dictionaries/i18n/es.json +2 -0
  675. package/src/modules/dictionaries/i18n/pl.json +2 -0
  676. package/src/modules/dictionaries/lib/clientEntries.ts +66 -0
  677. package/src/modules/dictionaries/migrations/.snapshot-open-mercato.json +185 -3
  678. package/src/modules/dictionaries/migrations/Migration20260410171544.ts +49 -0
  679. package/src/modules/inbox_ops/api/proposals/[id]/route.ts +4 -1
  680. package/src/modules/query_index/lib/engine.ts +9 -1
  681. package/src/modules/sales/components/documents/AddressesSection.tsx +92 -42
  682. package/src/modules/sales/i18n/de.json +28 -0
  683. package/src/modules/sales/i18n/en.json +28 -0
  684. package/src/modules/sales/i18n/es.json +28 -0
  685. package/src/modules/sales/i18n/pl.json +28 -0
  686. package/src/modules/sales/lib/dictionaries.ts +18 -0
  687. package/src/modules/sales/widgets/injection-table.ts +4 -0
@@ -1,25 +1,25 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from 'react'
4
- import { useRouter } from 'next/navigation'
5
- import { Users, Trash2, Loader2, ArrowUpRightSquare, Link2, Plus } from 'lucide-react'
4
+ import { Users, Link2, Plus, Filter } from 'lucide-react'
6
5
  import { EmptyState } from '@open-mercato/ui/backend/EmptyState'
7
6
  import { Button } from '@open-mercato/ui/primitives/button'
8
- import {
9
- Dialog,
10
- DialogContent,
11
- DialogDescription,
12
- DialogFooter,
13
- DialogHeader,
14
- DialogTitle,
15
- } from '@open-mercato/ui/primitives/dialog'
16
- import { LookupSelect, type LookupSelectItem } from '@open-mercato/ui/backend/inputs'
7
+ import { Badge } from '@open-mercato/ui/primitives/badge'
17
8
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
18
9
  import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
19
- import type { SectionAction, TabEmptyStateConfig, Translator } from './types'
10
+ import {
11
+ readJsonFromLocalStorage,
12
+ writeJsonToLocalStorage,
13
+ } from '@open-mercato/shared/lib/browser/safeLocalStorage'
20
14
  import { useT } from '@open-mercato/shared/lib/i18n/context'
21
15
  import { createTranslatorWithFallback } from '@open-mercato/shared/lib/i18n/translate'
22
- import { formatDate } from './utils'
16
+ import type { SectionAction, TabEmptyStateConfig, Translator } from './types'
17
+ import { CreatePersonDialog } from './CreatePersonDialog'
18
+ import { PersonCard } from './PersonCard'
19
+ import { DecisionMakersFooter } from './DecisionMakersFooter'
20
+ import { RolesSection } from './RolesSection'
21
+ import { LinkEntityDialog, type LinkEntityOption } from '../linking/LinkEntityDialog'
22
+ import { createPersonLinkAdapter } from '../linking/adapters/personAdapter'
23
23
 
24
24
  type GuardedMutationRunner = <T>(
25
25
  operation: () => Promise<T>,
@@ -37,10 +37,14 @@ export type CompanyPersonSummary = {
37
37
  department?: string | null
38
38
  createdAt?: string | null
39
39
  organizationId?: string | null
40
+ temperature?: string | null
41
+ source?: string | null
42
+ linkedAt?: string | null
40
43
  }
41
44
 
42
45
  export type CompanyPeopleSectionProps = {
43
46
  companyId: string
47
+ companyName?: string
44
48
  initialPeople: CompanyPersonSummary[]
45
49
  addActionLabel: string
46
50
  emptyLabel: string
@@ -53,10 +57,7 @@ export type CompanyPeopleSectionProps = {
53
57
  runGuardedMutation?: GuardedMutationRunner
54
58
  }
55
59
 
56
- function buildCompanyPersonCreateHref(companyId: string): string {
57
- const returnTo = `/backend/customers/companies-v2/${encodeURIComponent(companyId)}?tab=people`
58
- return `/backend/customers/people/create?companyId=${encodeURIComponent(companyId)}&returnTo=${encodeURIComponent(returnTo)}`
59
- }
60
+ const COMPANY_PEOPLE_PAGE_SIZE = 20
60
61
 
61
62
  function normalizeCompanyPerson(record: Record<string, unknown>): CompanyPersonSummary | null {
62
63
  const id = typeof record.id === 'string' ? record.id : null
@@ -115,23 +116,64 @@ function normalizeCompanyPerson(record: Record<string, unknown>): CompanyPersonS
115
116
  : typeof record.organization_id === 'string'
116
117
  ? record.organization_id
117
118
  : null,
119
+ temperature:
120
+ typeof record.temperature === 'string'
121
+ ? record.temperature
122
+ : null,
123
+ source:
124
+ typeof record.source === 'string'
125
+ ? record.source
126
+ : null,
127
+ linkedAt:
128
+ typeof record.linkedAt === 'string'
129
+ ? record.linkedAt
130
+ : typeof record.linked_at === 'string'
131
+ ? record.linked_at
132
+ : null,
118
133
  }
119
134
  }
120
135
 
121
- function toLookupItem(person: CompanyPersonSummary): LookupSelectItem {
122
- const subtitle = person.primaryEmail || person.primaryPhone || null
123
- const description = person.lifecycleStage || person.jobTitle || null
124
- return {
125
- id: person.id,
126
- title: person.displayName,
127
- subtitle,
128
- description,
129
- rightLabel: person.status || null,
130
- }
136
+ function mergeCompanyPeople(items: CompanyPersonSummary[]): CompanyPersonSummary[] {
137
+ const merged = new Map<string, CompanyPersonSummary>()
138
+ items.forEach((item) => merged.set(item.id, item))
139
+ return Array.from(merged.values())
140
+ }
141
+
142
+ function matchesCompanyPersonSearch(person: CompanyPersonSummary, query: string): boolean {
143
+ const normalizedQuery = query.trim().toLowerCase()
144
+ if (!normalizedQuery.length) return true
145
+ const haystack = [
146
+ person.displayName,
147
+ person.jobTitle ?? '',
148
+ person.primaryEmail ?? '',
149
+ person.primaryPhone ?? '',
150
+ person.department ?? '',
151
+ ]
152
+ .join(' ')
153
+ .toLowerCase()
154
+ return haystack.includes(normalizedQuery)
155
+ }
156
+
157
+ function sortCompanyPeople(
158
+ items: CompanyPersonSummary[],
159
+ sortMode: 'name-asc' | 'name-desc' | 'recent',
160
+ ): CompanyPersonSummary[] {
161
+ return [...items].sort((left, right) => {
162
+ if (sortMode === 'recent') {
163
+ const leftTimestamp = Date.parse(left.linkedAt ?? left.createdAt ?? '') || 0
164
+ const rightTimestamp = Date.parse(right.linkedAt ?? right.createdAt ?? '') || 0
165
+ return rightTimestamp - leftTimestamp
166
+ }
167
+ const leftLabel = left.displayName.trim().toLowerCase()
168
+ const rightLabel = right.displayName.trim().toLowerCase()
169
+ if (sortMode === 'name-desc') return rightLabel.localeCompare(leftLabel)
170
+ return leftLabel.localeCompare(rightLabel)
171
+ })
131
172
  }
132
173
 
133
174
  export function CompanyPeopleSection({
134
175
  companyId,
176
+ companyName,
135
177
  initialPeople,
136
178
  addActionLabel,
137
179
  emptyLabel,
@@ -143,21 +185,34 @@ export function CompanyPeopleSection({
143
185
  onDataRefresh,
144
186
  runGuardedMutation,
145
187
  }: CompanyPeopleSectionProps) {
146
- const router = useRouter()
147
188
  const tHook = useT()
148
- const fallbackTranslator = React.useMemo<Translator>(() => createTranslatorWithFallback(tHook), [tHook])
189
+ const fallbackTranslator = React.useMemo<Translator>(
190
+ () => createTranslatorWithFallback(tHook),
191
+ [tHook],
192
+ )
149
193
  const translate: Translator = translator ?? fallbackTranslator
150
194
  const [people, setPeople] = React.useState<CompanyPersonSummary[]>(initialPeople)
151
195
  const [removingId, setRemovingId] = React.useState<string | null>(null)
152
196
  const [linkDialogOpen, setLinkDialogOpen] = React.useState(false)
153
- const [selectedPersonId, setSelectedPersonId] = React.useState<string | null>(null)
154
- const [linking, setLinking] = React.useState(false)
155
- const candidatePeopleRef = React.useRef<Map<string, CompanyPersonSummary>>(new Map())
197
+ const [createDialogOpen, setCreateDialogOpen] = React.useState(false)
198
+ const [searchQuery, setSearchQuery] = React.useState('')
199
+ const [sortMode, setSortMode] = React.useState<'name-asc' | 'name-desc' | 'recent'>('name-asc')
200
+ const [filtersOpen, setFiltersOpen] = React.useState(true)
201
+ const [visiblePeople, setVisiblePeople] = React.useState<CompanyPersonSummary[]>([])
202
+ const [listPage, setListPage] = React.useState(1)
203
+ const [listTotalPages, setListTotalPages] = React.useState(1)
204
+ const [listTotalCount, setListTotalCount] = React.useState(initialPeople.length)
205
+ const [listLoading, setListLoading] = React.useState(true)
206
+ const [starredIds, setStarredIds] = React.useState<Set<string>>(
207
+ () => new Set(readJsonFromLocalStorage<string[]>(`om:starred-people:${companyId}`, [])),
208
+ )
156
209
  const pendingPeopleChangeRef = React.useRef(false)
157
- const createPersonHref = React.useMemo(() => buildCompanyPersonCreateHref(companyId), [companyId])
158
210
 
159
211
  const runWriteMutation = React.useCallback(
160
- async <T,>(operation: () => Promise<T>, mutationPayload?: Record<string, unknown>): Promise<T> => {
212
+ async <T,>(
213
+ operation: () => Promise<T>,
214
+ mutationPayload?: Record<string, unknown>,
215
+ ): Promise<T> => {
161
216
  if (!runGuardedMutation) {
162
217
  return operation()
163
218
  }
@@ -166,18 +221,44 @@ export function CompanyPeopleSection({
166
221
  [runGuardedMutation],
167
222
  )
168
223
 
224
+ const toggleStar = React.useCallback(
225
+ (personId: string) => {
226
+ setStarredIds((prev) => {
227
+ const next = new Set(prev)
228
+ if (next.has(personId)) next.delete(personId)
229
+ else next.add(personId)
230
+ writeJsonToLocalStorage(`om:starred-people:${companyId}`, [...next])
231
+ return next
232
+ })
233
+ },
234
+ [companyId],
235
+ )
236
+
237
+ const displayedPeople = React.useMemo(
238
+ () => (visiblePeople.length > 0 ? visiblePeople : people),
239
+ [people, visiblePeople],
240
+ )
241
+ const totalLinkedPeople = listTotalCount > 0 ? listTotalCount : displayedPeople.length
242
+ const decisionMakerNames = React.useMemo(
243
+ () =>
244
+ displayedPeople
245
+ .filter((person) => starredIds.has(person.id))
246
+ .map((person) => person.displayName),
247
+ [displayedPeople, starredIds],
248
+ )
249
+
169
250
  React.useEffect(() => {
170
251
  const action: SectionAction = {
171
252
  label: addActionLabel,
172
253
  onClick: () => {
173
- router.push(createPersonHref)
254
+ setCreateDialogOpen(true)
174
255
  },
175
256
  }
176
257
  onActionChange?.(action)
177
258
  return () => {
178
259
  onActionChange?.(null)
179
260
  }
180
- }, [addActionLabel, createPersonHref, onActionChange, router])
261
+ }, [addActionLabel, onActionChange])
181
262
 
182
263
  React.useEffect(() => {
183
264
  pendingPeopleChangeRef.current = false
@@ -190,38 +271,57 @@ export function CompanyPeopleSection({
190
271
  onPeopleChange?.(people)
191
272
  }, [onPeopleChange, people])
192
273
 
193
- const linkedIds = React.useMemo(() => new Set(people.map((person) => person.id)), [people])
194
-
195
- const fetchCandidatePeople = React.useCallback(
196
- async (query?: string): Promise<LookupSelectItem[]> => {
274
+ const loadVisiblePeople = React.useCallback(async () => {
275
+ setListLoading(true)
276
+ try {
197
277
  const params = new URLSearchParams({
198
- pageSize: '20',
199
- sortField: 'name',
200
- sortDir: 'asc',
278
+ page: String(listPage),
279
+ pageSize: String(COMPANY_PEOPLE_PAGE_SIZE),
280
+ sort: sortMode,
201
281
  })
202
- if (typeof query === 'string' && query.trim().length > 0) {
203
- params.set('search', query.trim())
282
+ if (searchQuery.trim().length > 0) {
283
+ params.set('search', searchQuery.trim())
204
284
  }
205
- const payload = await readApiResultOrThrow<Record<string, unknown>>(
206
- `/api/customers/people?${params.toString()}`,
285
+ const payload = await readApiResultOrThrow<{
286
+ items?: CompanyPersonSummary[]
287
+ page?: number
288
+ total?: number
289
+ totalPages?: number
290
+ }>(
291
+ `/api/customers/companies/${encodeURIComponent(companyId)}/people?${params.toString()}`,
207
292
  undefined,
208
- { errorMessage: translate('customers.companies.detail.people.linkLoadError', 'Failed to load people.') },
293
+ {
294
+ errorMessage: translate(
295
+ 'customers.companies.detail.people.loadError',
296
+ 'Failed to load people.',
297
+ ),
298
+ },
209
299
  )
210
- const items = Array.isArray(payload.items) ? payload.items : []
211
- const nextMap = new Map<string, CompanyPersonSummary>()
212
- const options = items
213
- .map((item) => (item && typeof item === 'object' ? normalizeCompanyPerson(item as Record<string, unknown>) : null))
214
- .filter((entry): entry is CompanyPersonSummary => entry !== null)
215
- .filter((entry) => !linkedIds.has(entry.id))
216
- .map((entry) => {
217
- nextMap.set(entry.id, entry)
218
- return toLookupItem(entry)
219
- })
220
- candidatePeopleRef.current = nextMap
221
- return options
222
- },
223
- [linkedIds, translate],
224
- )
300
+ const nextTotalCount = typeof payload.total === 'number' ? payload.total : 0
301
+ setVisiblePeople(Array.isArray(payload.items) ? payload.items : [])
302
+ setListPage(typeof payload.page === 'number' ? payload.page : listPage)
303
+ setListTotalCount((current) =>
304
+ searchQuery.trim().length > 0 ? Math.max(current, nextTotalCount) : nextTotalCount,
305
+ )
306
+ setListTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : 1)
307
+ } catch {
308
+ setVisiblePeople([])
309
+ if (searchQuery.trim().length === 0) {
310
+ setListTotalCount(0)
311
+ }
312
+ setListTotalPages(1)
313
+ } finally {
314
+ setListLoading(false)
315
+ }
316
+ }, [companyId, listPage, searchQuery, sortMode, translate])
317
+
318
+ React.useEffect(() => {
319
+ void loadVisiblePeople()
320
+ }, [loadVisiblePeople])
321
+
322
+ React.useEffect(() => {
323
+ setListPage(1)
324
+ }, [searchQuery, sortMode])
225
325
 
226
326
  const applyPeopleChange = React.useCallback(
227
327
  (updater: (current: CompanyPersonSummary[]) => CompanyPersonSummary[]) => {
@@ -236,61 +336,165 @@ export function CompanyPeopleSection({
236
336
  [],
237
337
  )
238
338
 
239
- const handleLink = React.useCallback(async () => {
240
- if (!selectedPersonId || linking) return
241
- setLinking(true)
242
- onLoadingChange?.(true)
243
- try {
244
- await runWriteMutation(
245
- () =>
246
- apiCallOrThrow(
247
- '/api/customers/people',
339
+ const handleLinkConfirm = React.useCallback(
340
+ async ({
341
+ addedIds,
342
+ optionsById,
343
+ }: {
344
+ addedIds: string[]
345
+ removedIds: string[]
346
+ optionsById: Record<string, LinkEntityOption>
347
+ }) => {
348
+ if (!addedIds.length) return
349
+ onLoadingChange?.(true)
350
+ try {
351
+ for (const personId of addedIds) {
352
+ await runWriteMutation(
353
+ () =>
354
+ apiCallOrThrow(
355
+ `/api/customers/people/${encodeURIComponent(personId)}/companies`,
356
+ {
357
+ method: 'POST',
358
+ headers: { 'content-type': 'application/json' },
359
+ body: JSON.stringify({ companyId }),
360
+ },
361
+ {
362
+ errorMessage: translate(
363
+ 'customers.companies.detail.people.linkError',
364
+ 'Failed to link person to company.',
365
+ ),
366
+ },
367
+ ),
248
368
  {
249
- method: 'PUT',
250
- headers: { 'content-type': 'application/json' },
251
- body: JSON.stringify({ id: selectedPersonId, companyEntityId: companyId }),
369
+ personId,
370
+ companyId,
252
371
  },
253
- { errorMessage: translate('customers.companies.detail.people.linkError', 'Failed to link person to company.') },
254
- ),
255
- {
256
- id: selectedPersonId,
257
- companyEntityId: companyId,
258
- },
259
- )
260
-
261
- const candidate = candidatePeopleRef.current.get(selectedPersonId)
262
- if (candidate) {
263
- applyPeopleChange((current) => {
264
- const withoutSelected = current.filter((entry) => entry.id !== selectedPersonId)
265
- return [...withoutSelected, candidate]
266
- })
267
- }
268
-
269
- flash(translate('customers.companies.detail.people.linkSuccess', 'Person linked to company.'), 'success')
270
- setSelectedPersonId(null)
271
- setLinkDialogOpen(false)
272
- await onDataRefresh?.()
273
- } catch (err) {
274
- const message =
275
- err instanceof Error
276
- ? err.message
277
- : translate('customers.companies.detail.people.linkError', 'Failed to link person to company.')
278
- flash(message, 'error')
279
- } finally {
280
- setLinking(false)
281
- onLoadingChange?.(false)
282
- }
283
- }, [applyPeopleChange, companyId, linking, onDataRefresh, onLoadingChange, runWriteMutation, selectedPersonId, translate])
284
-
285
- const handleLinkDialogKeyDown = React.useCallback(
286
- (event: React.KeyboardEvent<HTMLDivElement>) => {
287
- if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
288
- event.preventDefault()
289
- if (!selectedPersonId || linking) return
290
- void handleLink()
372
+ )
373
+ }
374
+ const optimisticPeople: CompanyPersonSummary[] = addedIds
375
+ .map((personId): CompanyPersonSummary | null => {
376
+ const option = optionsById[personId]
377
+ if (!option) return null
378
+ return {
379
+ id: option.id,
380
+ displayName: option.label,
381
+ primaryEmail: null,
382
+ primaryPhone: null,
383
+ jobTitle: option.subtitle ?? null,
384
+ }
385
+ })
386
+ .filter((entry): entry is CompanyPersonSummary => entry !== null)
387
+ if (optimisticPeople.length > 0) {
388
+ applyPeopleChange((current) => mergeCompanyPeople([...current, ...optimisticPeople]))
389
+ setListTotalCount((current) => current + optimisticPeople.length)
390
+ }
391
+ await loadVisiblePeople()
392
+ flash(
393
+ addedIds.length === 1
394
+ ? translate(
395
+ 'customers.companies.detail.people.linkSuccess',
396
+ 'Person linked to company.',
397
+ )
398
+ : translate(
399
+ 'customers.companies.detail.people.linkSuccessMultiple',
400
+ '{{count}} people linked to company.',
401
+ { count: String(addedIds.length) },
402
+ ),
403
+ 'success',
404
+ )
405
+ } catch (err) {
406
+ try {
407
+ await onDataRefresh?.()
408
+ } catch {
409
+ // preserve original linking error for the user
410
+ }
411
+ const message =
412
+ err instanceof Error
413
+ ? err.message
414
+ : translate(
415
+ 'customers.companies.detail.people.linkError',
416
+ 'Failed to link person to company.',
417
+ )
418
+ flash(message, 'error')
419
+ throw err
420
+ } finally {
421
+ onLoadingChange?.(false)
291
422
  }
292
423
  },
293
- [handleLink, linking, selectedPersonId],
424
+ [
425
+ applyPeopleChange,
426
+ companyId,
427
+ loadVisiblePeople,
428
+ onDataRefresh,
429
+ onLoadingChange,
430
+ runWriteMutation,
431
+ translate,
432
+ ],
433
+ )
434
+
435
+ const personLinkAdapter = React.useMemo(
436
+ () =>
437
+ createPersonLinkAdapter({
438
+ dialogTitle: translate('customers.linking.person.dialogTitle', 'Link person'),
439
+ dialogSubtitle: companyName
440
+ ? translate(
441
+ 'customers.linking.person.dialogSubtitleFor',
442
+ 'Link an existing contact to {{name}}',
443
+ { name: companyName },
444
+ )
445
+ : translate(
446
+ 'customers.linking.person.dialogSubtitle',
447
+ 'Link an existing contact to this company',
448
+ ),
449
+ sectionLabel: translate('customers.linking.person.sectionLabel', 'MATCHING CONTACTS'),
450
+ searchPlaceholder: translate(
451
+ 'customers.linking.person.searchPlaceholder',
452
+ 'Search all people…',
453
+ ),
454
+ searchEmptyHint: translate(
455
+ 'customers.linking.person.searchEmpty',
456
+ 'No matching people found.',
457
+ ),
458
+ selectedEmptyHint: translate(
459
+ 'customers.linking.person.selectedEmpty',
460
+ 'No people selected.',
461
+ ),
462
+ confirmButtonLabel: translate('customers.linking.person.confirmButton', 'Link person'),
463
+ showLinkSettings: true,
464
+ roleOptions: [
465
+ { id: 'decision_maker', label: 'Decision maker' },
466
+ { id: 'budget_holder', label: 'Budget holder' },
467
+ { id: 'stakeholder', label: 'Stakeholder' },
468
+ { id: 'contact', label: 'Contact' },
469
+ ],
470
+ excludeLinkedCompanyId: companyId,
471
+ addNew: {
472
+ title: translate('customers.linking.person.addNew', 'Add new contact'),
473
+ subtitle: translate(
474
+ 'customers.linking.person.addNewSubtitle',
475
+ 'Company will be filled in automatically',
476
+ ),
477
+ render: ({ onCancel }) => (
478
+ <CreatePersonDialog
479
+ open
480
+ onClose={onCancel}
481
+ companyId={companyId}
482
+ companyName={companyName ?? companyId}
483
+ runGuardedMutation={runWriteMutation}
484
+ onPersonCreated={() => {
485
+ // CreatePersonDialog already created and linked the person to this company
486
+ // via the companyEntityId payload field. Refresh the on-page list and close
487
+ // both the nested and outer dialogs so the user can see the new entry.
488
+ void loadVisiblePeople()
489
+ void onDataRefresh?.()
490
+ setLinkDialogOpen(false)
491
+ onCancel()
492
+ }}
493
+ />
494
+ ),
495
+ },
496
+ }),
497
+ [companyId, companyName, loadVisiblePeople, onDataRefresh, runWriteMutation, translate],
294
498
  )
295
499
 
296
500
  const handleRemove = React.useCallback(
@@ -302,233 +506,323 @@ export function CompanyPeopleSection({
302
506
  await runWriteMutation(
303
507
  () =>
304
508
  apiCallOrThrow(
305
- '/api/customers/people',
509
+ `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,
510
+ { method: 'DELETE' },
306
511
  {
307
- method: 'PUT',
308
- headers: { 'content-type': 'application/json' },
309
- body: JSON.stringify({ id: personId, companyEntityId: null }),
512
+ errorMessage: translate(
513
+ 'customers.companies.detail.people.removeError',
514
+ 'Failed to unlink person from company.',
515
+ ),
310
516
  },
311
- { errorMessage: translate('customers.companies.detail.people.removeError', 'Failed to unlink person from company.') },
312
517
  ),
313
518
  {
314
- id: personId,
315
- companyEntityId: null,
519
+ personId,
520
+ companyId,
316
521
  },
317
522
  )
318
523
  applyPeopleChange((current) => current.filter((entry) => entry.id !== personId))
319
- flash(translate('customers.companies.detail.people.removeSuccess', 'Person unlinked from company.'), 'success')
320
- await onDataRefresh?.()
524
+ setVisiblePeople((current) => current.filter((entry) => entry.id !== personId))
525
+ setListTotalCount((current) => Math.max(0, current - 1))
526
+ await loadVisiblePeople()
527
+ flash(
528
+ translate(
529
+ 'customers.companies.detail.people.removeSuccess',
530
+ 'Person unlinked from company.',
531
+ ),
532
+ 'success',
533
+ )
321
534
  } catch (err) {
322
535
  const message =
323
536
  err instanceof Error
324
537
  ? err.message
325
- : translate('customers.companies.detail.people.removeError', 'Failed to unlink person from company.')
538
+ : translate(
539
+ 'customers.companies.detail.people.removeError',
540
+ 'Failed to unlink person from company.',
541
+ )
326
542
  flash(message, 'error')
327
543
  } finally {
328
544
  setRemovingId(null)
329
545
  onLoadingChange?.(false)
330
546
  }
331
547
  },
332
- [applyPeopleChange, onDataRefresh, onLoadingChange, removingId, runWriteMutation, translate],
548
+ [
549
+ applyPeopleChange,
550
+ companyId,
551
+ loadVisiblePeople,
552
+ onLoadingChange,
553
+ removingId,
554
+ runWriteMutation,
555
+ translate,
556
+ ],
333
557
  )
334
558
 
335
559
  const linkAction = (
336
- <Button type="button" variant="outline" size="sm" onClick={() => setLinkDialogOpen(true)} disabled={linking}>
560
+ <Button
561
+ type="button"
562
+ variant="outline"
563
+ size="sm"
564
+ onClick={() => setLinkDialogOpen(true)}
565
+ >
337
566
  <Link2 className="mr-1.5 h-4 w-4" />
338
- {translate('customers.companies.detail.people.linkAction', 'Link existing person')}
567
+ {translate(
568
+ 'customers.companies.detail.people.linkAction',
569
+ 'Link existing person',
570
+ )}
339
571
  </Button>
340
572
  )
341
573
  const addPersonAction = (
342
- <Button type="button" size="sm" onClick={() => router.push(createPersonHref)}>
574
+ <Button type="button" size="sm" onClick={() => setCreateDialogOpen(true)}>
343
575
  <Plus className="mr-1.5 h-4 w-4" />
344
576
  {addActionLabel}
345
577
  </Button>
346
578
  )
347
579
 
348
- if (!people.length) {
580
+ if (!listLoading && totalLinkedPeople === 0) {
349
581
  return (
350
582
  <>
351
583
  <EmptyState
352
584
  icon={<Users className="h-10 w-10 text-muted-foreground" />}
353
585
  title={emptyState.title}
354
586
  actionLabel={emptyState.actionLabel}
355
- onAction={() => router.push(createPersonHref)}
587
+ onAction={() => setCreateDialogOpen(true)}
356
588
  >
357
589
  <p className="text-sm text-muted-foreground">{emptyLabel}</p>
358
590
  <div className="mt-4">{linkAction}</div>
359
591
  </EmptyState>
360
- <Dialog open={linkDialogOpen} onOpenChange={setLinkDialogOpen}>
361
- <DialogContent className="sm:max-w-2xl" onKeyDown={handleLinkDialogKeyDown}>
362
- <DialogHeader>
363
- <DialogTitle>{translate('customers.companies.detail.people.linkDialog.title', 'Link existing person')}</DialogTitle>
364
- <DialogDescription>
365
- {translate(
366
- 'customers.companies.detail.people.linkDialog.description',
367
- 'Search for an existing person and attach them to this company without leaving the page.',
368
- )}
369
- </DialogDescription>
370
- </DialogHeader>
371
- <div className="space-y-3">
372
- <LookupSelect
373
- value={selectedPersonId}
374
- onChange={setSelectedPersonId}
375
- fetchOptions={fetchCandidatePeople}
376
- defaultOpen
377
- searchPlaceholder={translate('customers.companies.detail.people.linkSearchPlaceholder', 'Search people by name or email')}
378
- emptyLabel={translate('customers.companies.detail.people.linkEmpty', 'No matching people found.')}
379
- loadingLabel={translate('customers.companies.detail.people.linkLoading', 'Searching people…')}
380
- selectLabel={translate('customers.companies.detail.people.linkSelect', 'Link')}
381
- selectedLabel={translate('customers.companies.detail.people.linkSelected', 'Selected')}
382
- clearLabel={translate('customers.companies.detail.people.linkClear', 'Clear selection')}
383
- startTypingLabel={translate('customers.companies.detail.people.linkStartTyping', 'Start typing to search for an existing person.')}
384
- />
385
- </div>
386
- <DialogFooter>
387
- <Button type="button" variant="outline" onClick={() => setLinkDialogOpen(false)} disabled={linking}>
388
- {translate('customers.companies.detail.people.linkCancel', 'Cancel')}
389
- </Button>
390
- <Button type="button" onClick={() => void handleLink()} disabled={!selectedPersonId || linking}>
391
- {linking ? (
392
- <>
393
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
394
- {translate('customers.companies.detail.people.linkSubmitting', 'Linking…')}
395
- </>
396
- ) : (
397
- translate('customers.companies.detail.people.linkConfirm', 'Link person')
398
- )}
399
- </Button>
400
- </DialogFooter>
401
- </DialogContent>
402
- </Dialog>
592
+ <LinkEntityDialog
593
+ open={linkDialogOpen}
594
+ onOpenChange={setLinkDialogOpen}
595
+ adapter={personLinkAdapter}
596
+ initialSelectedIds={[]}
597
+ onConfirm={handleLinkConfirm}
598
+ runGuardedMutation={runWriteMutation}
599
+ />
600
+ <CreatePersonDialog
601
+ open={createDialogOpen}
602
+ onClose={() => setCreateDialogOpen(false)}
603
+ companyId={companyId}
604
+ companyName={companyName ?? companyId}
605
+ runGuardedMutation={runWriteMutation}
606
+ onPersonCreated={() => {
607
+ setCreateDialogOpen(false)
608
+ void loadVisiblePeople()
609
+ void onDataRefresh?.()
610
+ }}
611
+ />
403
612
  </>
404
613
  )
405
614
  }
406
615
 
407
616
  return (
408
617
  <>
409
- <div className="space-y-3">
410
- <div className="flex flex-wrap justify-end gap-2">
411
- {linkAction}
412
- </div>
413
- <div className="rounded border bg-muted/20">
414
- {people.map((person) => (
415
- <div
416
- key={person.id}
417
- className="flex flex-col gap-3 border-b px-4 py-3 last:border-b-0 md:flex-row md:items-center md:justify-between"
418
- >
618
+ <div className="space-y-4">
619
+ <RolesSection
620
+ entityType="company"
621
+ entityId={companyId}
622
+ entityName={companyName ?? null}
623
+ />
624
+
625
+ <section className="rounded-lg border bg-card px-4 py-4 sm:px-5">
626
+ <div className="flex flex-col gap-4">
627
+ <div className="flex flex-col gap-3 xl:flex-row xl:items-start xl:justify-between">
419
628
  <div className="space-y-1">
420
- <div className="flex flex-wrap items-center gap-2">
421
- <p className="font-medium text-foreground">{person.displayName}</p>
422
- {person.jobTitle ? (
423
- <span className="rounded-full bg-muted px-2 py-0.5 text-xs text-muted-foreground">
424
- {person.jobTitle}
425
- </span>
426
- ) : null}
427
- {person.status ? (
428
- <span className="rounded-full border px-2 py-0.5 text-xs text-muted-foreground">
429
- {person.status}
430
- </span>
431
- ) : null}
432
- </div>
433
- <div className="flex flex-wrap gap-x-4 gap-y-1 text-xs text-muted-foreground">
434
- {person.primaryEmail ? <span>{person.primaryEmail}</span> : null}
435
- {person.primaryPhone ? <span>{person.primaryPhone}</span> : null}
436
- {person.lifecycleStage ? <span>{person.lifecycleStage}</span> : null}
437
- {(() => {
438
- const linkedDate = formatDate(person.createdAt ?? null)
439
- return linkedDate
440
- ? (
441
- <span>
442
- {translate('customers.companies.detail.people.linkedOn', 'Linked on {{date}}', {
443
- date: linkedDate,
444
- })}
445
- </span>
446
- )
447
- : null
448
- })()}
629
+ <div className="flex items-center gap-2">
630
+ <h3 className="text-base font-semibold">
631
+ {translate(
632
+ 'customers.companies.detail.people.sectionTitle',
633
+ 'People',
634
+ )}
635
+ </h3>
636
+ <Badge
637
+ variant="secondary"
638
+ className="rounded-full px-2 py-0 text-xs font-semibold"
639
+ >
640
+ {totalLinkedPeople}
641
+ </Badge>
449
642
  </div>
450
- </div>
451
- <div className="flex flex-wrap items-center gap-2">
452
- <Button
453
- type="button"
454
- variant="outline"
455
- size="sm"
456
- onClick={() => router.push(`/backend/customers/people-v2/${encodeURIComponent(person.id)}`)}
457
- >
458
- <ArrowUpRightSquare className="mr-1.5 h-4 w-4" />
459
- {translate('customers.companies.detail.people.open', 'Open person')}
460
- </Button>
461
- <Button
462
- type="button"
463
- variant="ghost"
464
- size="sm"
465
- onClick={() => handleRemove(person.id)}
466
- disabled={removingId === person.id}
467
- className="text-destructive hover:bg-destructive/10 hover:text-destructive"
468
- >
469
- {removingId === person.id ? (
470
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
471
- ) : (
472
- <Trash2 className="mr-2 h-4 w-4" />
643
+ <p className="text-xs text-muted-foreground">
644
+ {translate(
645
+ 'customers.companies.detail.people.sectionSubtitle',
646
+ 'Employees and decision makers on the client side',
473
647
  )}
474
- {translate('customers.companies.detail.people.remove', 'Unlink')}
475
- </Button>
648
+ </p>
649
+ </div>
650
+ <div className="flex flex-wrap items-center gap-2 xl:justify-end">
651
+ {linkAction}
652
+ {addPersonAction}
476
653
  </div>
477
654
  </div>
478
- ))}
479
- </div>
480
- <p className="text-xs text-muted-foreground">
481
- {translate(
482
- 'customers.companies.detail.people.helper',
483
- 'People linked to this company appear here and can be created, linked, opened, or unlinked without leaving the page.',
484
- )}
485
- </p>
486
- </div>
487
- <Dialog open={linkDialogOpen} onOpenChange={setLinkDialogOpen}>
488
- <DialogContent className="sm:max-w-2xl" onKeyDown={handleLinkDialogKeyDown}>
489
- <DialogHeader>
490
- <DialogTitle>{translate('customers.companies.detail.people.linkDialog.title', 'Link existing person')}</DialogTitle>
491
- <DialogDescription>
492
- {translate(
493
- 'customers.companies.detail.people.linkDialog.description',
494
- 'Search for an existing person and attach them to this company without leaving the page.',
495
- )}
496
- </DialogDescription>
497
- </DialogHeader>
498
- <div className="space-y-3">
499
- <LookupSelect
500
- value={selectedPersonId}
501
- onChange={setSelectedPersonId}
502
- fetchOptions={fetchCandidatePeople}
503
- defaultOpen
504
- searchPlaceholder={translate('customers.companies.detail.people.linkSearchPlaceholder', 'Search people by name or email')}
505
- emptyLabel={translate('customers.companies.detail.people.linkEmpty', 'No matching people found.')}
506
- loadingLabel={translate('customers.companies.detail.people.linkLoading', 'Searching people…')}
507
- selectLabel={translate('customers.companies.detail.people.linkSelect', 'Link')}
508
- selectedLabel={translate('customers.companies.detail.people.linkSelected', 'Selected')}
509
- clearLabel={translate('customers.companies.detail.people.linkClear', 'Clear selection')}
510
- startTypingLabel={translate('customers.companies.detail.people.linkStartTyping', 'Start typing to search for an existing person.')}
511
- />
655
+
656
+ {totalLinkedPeople > 0 ? (
657
+ <div className="flex flex-col gap-3 lg:flex-row lg:items-center">
658
+ {filtersOpen ? (
659
+ <div className="min-w-0 flex-1">
660
+ <input
661
+ type="text"
662
+ value={searchQuery}
663
+ onChange={(event) => setSearchQuery(event.target.value)}
664
+ placeholder={translate(
665
+ 'customers.companies.detail.people.searchPlaceholder',
666
+ 'Search by name, role, email...',
667
+ )}
668
+ className="h-10 w-full rounded-md border bg-background px-3 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring"
669
+ />
670
+ </div>
671
+ ) : null}
672
+ <div className="flex flex-wrap items-center gap-2 lg:justify-end">
673
+ <Button
674
+ type="button"
675
+ variant="outline"
676
+ size="sm"
677
+ onClick={() => setFiltersOpen((current) => !current)}
678
+ className="h-10"
679
+ >
680
+ <Filter className="mr-1.5 h-4 w-4" />
681
+ {translate(
682
+ 'customers.companies.detail.people.filter',
683
+ 'Filters',
684
+ )}
685
+ </Button>
686
+ {filtersOpen ? (
687
+ <select
688
+ value={sortMode}
689
+ onChange={(event) =>
690
+ setSortMode(event.target.value as 'name-asc' | 'name-desc' | 'recent')
691
+ }
692
+ className="h-10 min-w-[11rem] rounded-md border bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-ring"
693
+ >
694
+ <option value="name-asc">
695
+ {translate(
696
+ 'customers.companies.detail.people.sortNameAsc',
697
+ 'Sort: Name A-Z',
698
+ )}
699
+ </option>
700
+ <option value="name-desc">
701
+ {translate(
702
+ 'customers.companies.detail.people.sortNameDesc',
703
+ 'Sort: Name Z-A',
704
+ )}
705
+ </option>
706
+ <option value="recent">
707
+ {translate(
708
+ 'customers.companies.detail.people.sortRecent',
709
+ 'Sort: Recently linked',
710
+ )}
711
+ </option>
712
+ </select>
713
+ ) : null}
714
+ </div>
715
+ </div>
716
+ ) : null}
717
+
718
+ {listLoading ? (
719
+ <p className="py-6 text-center text-sm text-muted-foreground">
720
+ {translate('customers.companies.detail.people.loading', 'Loading people…')}
721
+ </p>
722
+ ) : visiblePeople.length > 0 ? (
723
+ <>
724
+ <div
725
+ className="grid items-start gap-4"
726
+ style={{
727
+ gridTemplateColumns: 'repeat(auto-fit, minmax(min(100%, 19.5rem), 1fr))',
728
+ }}
729
+ >
730
+ {visiblePeople.map((person) => (
731
+ <PersonCard
732
+ key={person.id}
733
+ person={person}
734
+ isStarred={starredIds.has(person.id)}
735
+ onToggleStar={toggleStar}
736
+ onUnlink={handleRemove}
737
+ />
738
+ ))}
739
+ </div>
740
+ {listTotalPages > 1 ? (
741
+ <div className="flex items-center justify-between border-t border-border/60 pt-3 text-sm text-muted-foreground">
742
+ <span>
743
+ {translate(
744
+ 'customers.companies.detail.people.pageSummary',
745
+ 'Page {{page}} of {{total}}',
746
+ {
747
+ page: listPage,
748
+ total: listTotalPages,
749
+ },
750
+ )}
751
+ </span>
752
+ <div className="flex items-center gap-2">
753
+ <Button
754
+ type="button"
755
+ variant="outline"
756
+ size="sm"
757
+ onClick={() => setListPage((current) => Math.max(1, current - 1))}
758
+ disabled={listPage <= 1}
759
+ >
760
+ {translate('customers.companies.detail.people.previous', 'Previous')}
761
+ </Button>
762
+ <Button
763
+ type="button"
764
+ variant="outline"
765
+ size="sm"
766
+ onClick={() =>
767
+ setListPage((current) => Math.min(listTotalPages, current + 1))
768
+ }
769
+ disabled={listPage >= listTotalPages}
770
+ >
771
+ {translate('customers.companies.detail.people.next', 'Next')}
772
+ </Button>
773
+ </div>
774
+ </div>
775
+ ) : null}
776
+ </>
777
+ ) : totalLinkedPeople > 0 ? (
778
+ <p className="py-6 text-center text-sm text-muted-foreground">
779
+ {translate(
780
+ 'customers.companies.detail.people.noSearchResults',
781
+ 'No people match your search.',
782
+ )}
783
+ </p>
784
+ ) : null}
512
785
  </div>
513
- <DialogFooter>
514
- <Button type="button" variant="outline" onClick={() => setLinkDialogOpen(false)} disabled={linking}>
515
- {translate('customers.companies.detail.people.linkCancel', 'Cancel')}
516
- </Button>
517
- <Button type="button" onClick={() => void handleLink()} disabled={!selectedPersonId || linking}>
518
- {linking ? (
519
- <>
520
- <Loader2 className="mr-2 h-4 w-4 animate-spin" />
521
- {translate('customers.companies.detail.people.linkSubmitting', 'Linking…')}
522
- </>
523
- ) : (
524
- translate('customers.companies.detail.people.linkConfirm', 'Link person')
525
- )}
526
- </Button>
527
- </DialogFooter>
528
- </DialogContent>
529
- </Dialog>
786
+ </section>
787
+
788
+ <DecisionMakersFooter
789
+ names={decisionMakerNames}
790
+ onSendInvitation={() => {
791
+ const starredEmails = displayedPeople
792
+ .filter((person) => starredIds.has(person.id) && person.primaryEmail)
793
+ .map((person) => person.primaryEmail!)
794
+ if (starredEmails.length > 0) {
795
+ window.open(`mailto:${starredEmails.join(',')}`, '_blank')
796
+ }
797
+ }}
798
+ />
799
+ </div>
800
+
801
+ <LinkEntityDialog
802
+ open={linkDialogOpen}
803
+ onOpenChange={setLinkDialogOpen}
804
+ adapter={personLinkAdapter}
805
+ initialSelectedIds={[]}
806
+ onConfirm={handleLinkConfirm}
807
+ runGuardedMutation={runWriteMutation}
808
+ />
809
+
810
+ <CreatePersonDialog
811
+ open={createDialogOpen}
812
+ onClose={() => setCreateDialogOpen(false)}
813
+ companyId={companyId}
814
+ companyName={companyName ?? companyId}
815
+ runGuardedMutation={runWriteMutation}
816
+ onPersonCreated={() => {
817
+ setCreateDialogOpen(false)
818
+ void loadVisiblePeople()
819
+ void onDataRefresh?.()
820
+ }}
821
+ />
530
822
  </>
531
823
  )
532
824
  }
533
825
 
534
826
  export default CompanyPeopleSection
827
+
828
+ export { mergeCompanyPeople, matchesCompanyPersonSearch, sortCompanyPeople, normalizeCompanyPerson }