@open-mercato/core 0.5.1-develop.2672.g2128128275 → 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
@@ -35,6 +35,7 @@ import {
35
35
  resolveAttachmentMaxBytes,
36
36
  willExceedAttachmentTenantQuota
37
37
  } from "../lib/upload-limits.js";
38
+ import { findWithDecryption } from "@open-mercato/shared/lib/encryption/find";
38
39
  const metadata = {
39
40
  GET: { requireAuth: true, requireFeatures: ["attachments.view"] },
40
41
  POST: { requireAuth: true, requireFeatures: ["attachments.manage"] },
@@ -42,7 +43,9 @@ const metadata = {
42
43
  };
43
44
  const attachmentQuerySchema = z.object({
44
45
  entityId: z.string().min(1).describe("Entity identifier that owns the attachments"),
45
- recordId: z.string().min(1).describe("Record identifier within the entity")
46
+ recordId: z.string().min(1).describe("Record identifier within the entity"),
47
+ page: z.coerce.number().min(1).optional(),
48
+ pageSize: z.coerce.number().min(1).max(100).optional()
46
49
  });
47
50
  const attachmentAssignmentSchema = z.object({
48
51
  type: z.string().describe("Assignment type identifier"),
@@ -64,7 +67,11 @@ const attachmentItemSchema = z.object({
64
67
  assignments: z.array(attachmentAssignmentSchema).optional().describe("Records that reference this attachment")
65
68
  });
66
69
  const attachmentListResponseSchema = z.object({
67
- items: z.array(attachmentItemSchema)
70
+ items: z.array(attachmentItemSchema),
71
+ total: z.number().int().nonnegative().optional(),
72
+ page: z.number().int().min(1).optional(),
73
+ pageSize: z.number().int().min(1).optional(),
74
+ totalPages: z.number().int().min(1).optional()
68
75
  });
69
76
  const attachmentUploadBodySchema = z.object({
70
77
  entityId: z.string().min(1),
@@ -154,17 +161,42 @@ async function GET(req) {
154
161
  const auth = await getAuthFromRequest(req);
155
162
  if (!auth || !auth.tenantId || !auth.orgId && !auth.isSuperAdmin) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
156
163
  const url = new URL(req.url);
157
- const entityId = url.searchParams.get("entityId") || "";
158
- const recordId = url.searchParams.get("recordId") || "";
159
- if (!entityId || !recordId) return NextResponse.json({ error: "entityId and recordId are required" }, { status: 400 });
164
+ const parsedQuery = attachmentQuerySchema.safeParse({
165
+ entityId: url.searchParams.get("entityId") || "",
166
+ recordId: url.searchParams.get("recordId") || "",
167
+ page: url.searchParams.get("page") ?? void 0,
168
+ pageSize: url.searchParams.get("pageSize") ?? void 0
169
+ });
170
+ if (!parsedQuery.success) {
171
+ return NextResponse.json({ error: "entityId and recordId are required" }, { status: 400 });
172
+ }
173
+ const { entityId, recordId, page, pageSize } = parsedQuery.data;
160
174
  const { resolve } = await createRequestContainer();
161
175
  const em = resolve("em");
162
176
  const filter = { entityId, recordId, tenantId: auth.tenantId };
163
177
  if (auth.orgId) filter.organizationId = auth.orgId;
164
- const items = await em.find(
178
+ const orderBy = { createdAt: "DESC" };
179
+ const usePaging = typeof page === "number" && typeof pageSize === "number";
180
+ const total = usePaging ? await em.count(Attachment, filter) : null;
181
+ const currentPage = usePaging ? Math.max(1, page) : null;
182
+ const currentPageSize = usePaging ? pageSize : null;
183
+ const totalPages = usePaging && total !== null ? Math.max(1, Math.ceil(total / currentPageSize)) : null;
184
+ const pageOffset = usePaging ? (Math.min(currentPage, totalPages) - 1) * currentPageSize : void 0;
185
+ const items = await findWithDecryption(
186
+ em,
165
187
  Attachment,
166
188
  filter,
167
- { orderBy: { createdAt: "desc" } }
189
+ {
190
+ orderBy,
191
+ ...usePaging ? {
192
+ limit: currentPageSize,
193
+ offset: pageOffset
194
+ } : {}
195
+ },
196
+ {
197
+ tenantId: auth.tenantId ?? null,
198
+ organizationId: auth.orgId ?? null
199
+ }
168
200
  );
169
201
  return NextResponse.json({
170
202
  items: items.map((a) => {
@@ -186,7 +218,13 @@ async function GET(req) {
186
218
  tags: metadata2.tags ?? [],
187
219
  assignments: metadata2.assignments ?? []
188
220
  };
189
- })
221
+ }),
222
+ ...usePaging ? {
223
+ total,
224
+ page: Math.min(currentPage, totalPages),
225
+ pageSize: currentPageSize,
226
+ totalPages
227
+ } : {}
190
228
  });
191
229
  }
192
230
  async function POST(req) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/attachments/api/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { buildAttachmentFileUrl, buildAttachmentImageUrl, slugifyAttachmentFileName } from '../lib/imageUrls'\nimport { ensureDefaultPartitions, resolveDefaultPartitionCode, sanitizePartitionCode } from '../lib/partitions'\nimport { Attachment, AttachmentPartition } from '../data/entities'\nimport { storePartitionFile, deletePartitionFile } from '../lib/storage'\nimport { extractAttachmentContent } from '../lib/textExtraction'\nimport { requestOcrProcessing } from '../lib/ocrQueue'\nimport { OcrService, shouldUseLlmOcr } from '../lib/ocrService'\nimport { clearAttachmentThumbnailCache } from '../lib/thumbnailCache'\nimport {\n mergeAttachmentMetadata,\n normalizeAttachmentAssignments,\n normalizeAttachmentTags,\n readAttachmentMetadata,\n upsertAssignment,\n type AttachmentAssignment,\n} from '../lib/metadata'\nimport { randomUUID } from 'crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { emitCrudSideEffects, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { attachmentCrudEvents, attachmentCrudIndexer } from '../lib/crud'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveDefaultAttachmentOcrEnabled } from '../lib/ocrConfig'\nimport {\n detectAttachmentMimeType,\n hasDangerousExecutableExtension,\n isActiveContentAttachment,\n sanitizeUploadedFileName,\n} from '../lib/security'\nimport {\n isMultipartRequestWithinUploadLimit,\n resolveAttachmentMaxBytes,\n willExceedAttachmentTenantQuota,\n} from '../lib/upload-limits'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['attachments.view'] },\n POST: { requireAuth: true, requireFeatures: ['attachments.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['attachments.manage'] },\n}\n\nconst attachmentQuerySchema = z.object({\n entityId: z.string().min(1).describe('Entity identifier that owns the attachments'),\n recordId: z.string().min(1).describe('Record identifier within the entity'),\n})\n\nconst attachmentAssignmentSchema = z.object({\n type: z.string().describe('Assignment type identifier'),\n id: z.string().describe('Assignment record identifier'),\n href: z.string().nullable().optional().describe('Optional link to the related record'),\n label: z.string().nullable().optional().describe('Optional label for the assignment'),\n})\n\nconst attachmentItemSchema = z.object({\n id: z.string().describe('Attachment identifier'),\n url: z.string().describe('Public path to the stored asset'),\n fileName: z.string().describe('Original filename'),\n fileSize: z.number().int().nonnegative().describe('File size in bytes'),\n createdAt: z.string().describe('Upload timestamp (ISO 8601)'),\n mimeType: z.string().nullable().optional().describe('MIME type of the file'),\n thumbnailUrl: z.string().optional().describe('Helper route that renders a thumbnail'),\n partitionCode: z.string().optional().describe('Partition identifier'),\n tags: z.array(z.string()).optional().describe('Tags assigned to the attachment'),\n content: z.string().nullable().optional().describe('Extracted text or markdown content'),\n assignments: z.array(attachmentAssignmentSchema).optional().describe('Records that reference this attachment'),\n})\n\nconst attachmentListResponseSchema = z.object({\n items: z.array(attachmentItemSchema),\n})\n\nconst attachmentUploadBodySchema = z.object({\n entityId: z.string().min(1),\n recordId: z.string().min(1),\n fieldKey: z.string().optional(),\n file: z.string().min(1).describe('Binary file payload; supplied as multipart form-data'),\n customFields: z\n .string()\n .optional()\n .describe('JSON encoded map of custom field values collected from the upload form.'),\n})\n\nconst attachmentDeleteQuerySchema = z.object({\n id: z.string().uuid(),\n})\n\nconst uploadResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string(),\n url: z.string(),\n fileName: z.string(),\n fileSize: z.number().int().nonnegative(),\n thumbnailUrl: z.string().optional(),\n content: z.string().nullable().optional(),\n tags: z.array(z.string()).optional(),\n assignments: z.array(attachmentAssignmentSchema).optional(),\n customFields: z.record(z.string(), z.unknown()).optional(),\n }),\n})\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nconst LIBRARY_ENTITY_ID = 'attachments:library'\n\nfunction parseCustomFieldsEntry(value: FormDataEntryValue | null): Record<string, unknown> {\n if (!value) return {}\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return {}\n try {\n const parsed = JSON.parse(trimmed)\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>\n }\n } catch {\n return {}\n }\n }\n if (typeof value === 'object' && !Array.isArray(value) && !(value instanceof File)) {\n return { ...(value as Record<string, unknown>) }\n }\n return {}\n}\n\nfunction buildFormPayload(form: FormData): Record<string, unknown> {\n const payload: Record<string, unknown> = {}\n form.forEach((value, key) => {\n if (key === 'customFields') {\n payload.customFields = parseCustomFieldsEntry(value)\n return\n }\n payload[key] = value\n })\n return payload\n}\n\nfunction parseFormTags(value: FormDataEntryValue | null): string[] {\n if (!value) return []\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return []\n try {\n const parsed = JSON.parse(trimmed)\n return normalizeAttachmentTags(parsed)\n } catch {\n return normalizeAttachmentTags(value)\n }\n }\n return []\n}\n\nfunction parseFormAssignments(value: FormDataEntryValue | null): AttachmentAssignment[] {\n if (!value) return []\n if (typeof value !== 'string') return []\n const trimmed = value.trim()\n if (!trimmed) return []\n try {\n const parsed = JSON.parse(trimmed)\n return normalizeAttachmentAssignments(parsed)\n } catch {\n return []\n }\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const url = new URL(req.url)\n const entityId = url.searchParams.get('entityId') || ''\n const recordId = url.searchParams.get('recordId') || ''\n if (!entityId || !recordId) return NextResponse.json({ error: 'entityId and recordId are required' }, { status: 400 })\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as any\n const filter: Record<string, unknown> = { entityId, recordId, tenantId: auth.tenantId! }\n if (auth.orgId) filter.organizationId = auth.orgId\n const items = await em.find(\n Attachment,\n filter,\n { orderBy: { createdAt: 'desc' } as any }\n )\n return NextResponse.json({\n items: items.map((a: any) => {\n const metadata = readAttachmentMetadata(a.storageMetadata)\n return {\n id: a.id,\n url: a.url,\n fileName: a.fileName,\n fileSize: a.fileSize,\n createdAt: a.createdAt,\n mimeType: a.mimeType ?? null,\n partitionCode: a.partitionCode,\n content: a.content ?? null,\n thumbnailUrl: buildAttachmentImageUrl(a.id, {\n width: 320,\n height: 320,\n slug: slugifyAttachmentFileName(a.fileName),\n }),\n tags: metadata.tags ?? [],\n assignments: metadata.assignments ?? [],\n }\n }),\n })\n}\n\nexport async function POST(req: Request) {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const tenantId = auth.tenantId\n const orgId = auth.orgId\n\n const contentType = req.headers.get('content-type') || ''\n if (!contentType.toLowerCase().includes('multipart/form-data')) {\n return NextResponse.json({ error: 'Expected multipart/form-data' }, { status: 400 })\n }\n if (!isMultipartRequestWithinUploadLimit(req.headers.get('content-length'))) {\n return NextResponse.json({ error: 'Attachment exceeds the maximum upload size.' }, { status: 413 })\n }\n\n const form = await req.formData()\n const formPayload = buildFormPayload(form)\n const customFieldValues = splitCustomFieldPayload(formPayload).custom\n const entityId = String(form.get('entityId') || '')\n const recordId = String(form.get('recordId') || '')\n const fieldKey = String(form.get('fieldKey') || '')\n const file = form.get('file') as unknown as File | null\n if (!entityId || !recordId || !file) return NextResponse.json({ error: 'entityId, recordId and file are required' }, { status: 400 })\n const partitionOverrideRaw = form.get('partitionCode')\n const partitionOverride =\n typeof partitionOverrideRaw === 'string' && partitionOverrideRaw.trim().length > 0\n ? sanitizePartitionCode(partitionOverrideRaw)\n : null\n const tags = parseFormTags(form.get('tags'))\n const assignmentsFromForm = parseFormAssignments(form.get('assignments'))\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const dataEngine = resolve('dataEngine')\n await ensureDefaultPartitions(em)\n // Optional per-field validations\n let partitionFromField: string | null = null\n let fieldMaxAttachmentSizeMb: number | null = null\n if (fieldKey) {\n try {\n const { CustomFieldDef } = await import('@open-mercato/core/modules/entities/data/entities')\n const def = await em.findOne(CustomFieldDef, {\n entityId,\n key: fieldKey,\n $and: [\n { $or: [ { tenantId: auth.tenantId }, { tenantId: null } ] },\n ],\n isActive: true,\n })\n const cfg = (def as any)?.configJson || {}\n const ext = (file.name || '').split('.').pop()?.toLowerCase() || ''\n if (Array.isArray(cfg.acceptExtensions) && cfg.acceptExtensions.length) {\n const allowed = new Set((cfg.acceptExtensions as any[]).map((x: any) => String(x).toLowerCase().replace(/^\\./, '')))\n if (!allowed.has(ext)) return NextResponse.json({ error: 'File type not allowed' }, { status: 400 })\n }\n if (typeof cfg.maxAttachmentSizeMb === 'number' && cfg.maxAttachmentSizeMb > 0) {\n fieldMaxAttachmentSizeMb = cfg.maxAttachmentSizeMb\n }\n if (typeof cfg.partitionCode === 'string' && cfg.partitionCode.trim().length > 0) {\n partitionFromField = sanitizePartitionCode(cfg.partitionCode)\n }\n } catch {}\n }\n if (hasDangerousExecutableExtension(file.name)) {\n return NextResponse.json({\n error: t('attachments.errors.dangerousExecutable', 'Executable file types are not allowed as attachments.'),\n }, { status: 400 })\n }\n const effectiveMaxBytes = resolveAttachmentMaxBytes(fieldMaxAttachmentSizeMb)\n if (file.size > effectiveMaxBytes) {\n return NextResponse.json({\n error: t('attachments.errors.maxUploadSize', 'Attachment exceeds the maximum upload size.'),\n }, { status: 413 })\n }\n const tenantUsageBytes = await readTenantAttachmentUsageBytes(em, tenantId)\n if (willExceedAttachmentTenantQuota(tenantUsageBytes, file.size)) {\n return NextResponse.json({\n error: t('attachments.errors.quotaExceeded', 'Attachment storage quota exceeded for this tenant.'),\n }, { status: 413 })\n }\n const buf = Buffer.from(await file.arrayBuffer())\n const safeName = sanitizeUploadedFileName(file.name)\n const fileMimeType = detectAttachmentMimeType(buf, safeName, (file as any).type)\n if (isActiveContentAttachment(buf, safeName, fileMimeType)) {\n return NextResponse.json({ error: t('attachments.errors.activeContentBlocked', 'Active content uploads are not allowed.') }, { status: 400 })\n }\n const defaultPartitionCode = resolveDefaultPartitionCode(entityId)\n const resolvedPartitionCode = partitionOverride ?? partitionFromField ?? defaultPartitionCode\n const partitionCodeCandidates = Array.from(\n new Set(\n [partitionOverride, partitionFromField, resolvedPartitionCode].filter(\n (code): code is string => typeof code === 'string' && code.length > 0,\n ),\n ),\n )\n let partition: AttachmentPartition | null = null\n for (const code of partitionCodeCandidates) {\n const record = await em.findOne(AttachmentPartition, { code })\n if (record) {\n partition = record\n break\n }\n }\n if (!partition) {\n partition = await em.findOne(AttachmentPartition, { code: defaultPartitionCode })\n }\n if (!partition) {\n return NextResponse.json({ error: 'Storage partition is not configured.' }, { status: 400 })\n }\n const requestedPublicOverride =\n typeof partitionOverride === 'string' &&\n partitionOverride.length > 0 &&\n partition.code === partitionOverride &&\n partition.isPublic === true &&\n partition.code !== defaultPartitionCode &&\n partition.code !== partitionFromField\n if (requestedPublicOverride) {\n return NextResponse.json({ error: t('attachments.errors.publicPartitionBlocked', 'Public storage partitions cannot be selected explicitly for this upload.') }, { status: 403 })\n }\n let stored\n try {\n stored = await storePartitionFile({\n partitionCode: partition.code,\n orgId,\n tenantId,\n fileName: safeName,\n buffer: buf,\n })\n } catch (error) {\n console.error('[attachments] failed to persist file', error)\n return NextResponse.json({ error: 'Failed to persist attachment.' }, { status: 500 })\n }\n\n const requiresOcr =\n typeof (partition as any).requiresOcr === 'boolean'\n ? Boolean((partition as any).requiresOcr)\n : resolveDefaultAttachmentOcrEnabled()\n let extractedContent: string | null = null\n const wantsLlmOcr = requiresOcr && shouldUseLlmOcr(fileMimeType, safeName)\n const ocrService = wantsLlmOcr ? new OcrService() : null\n const useLlmOcr = Boolean(wantsLlmOcr && ocrService?.available)\n\n if (requiresOcr && !useLlmOcr) {\n try {\n extractedContent = await extractAttachmentContent({\n filePath: stored.absolutePath,\n mimeType: fileMimeType,\n })\n } catch (error) {\n console.error('[attachments] failed to extract attachment content', error)\n }\n }\n\n let assignments = assignmentsFromForm.slice()\n if (entityId !== LIBRARY_ENTITY_ID) {\n assignments = upsertAssignment(assignments, { type: entityId, id: recordId })\n }\n const metadata = mergeAttachmentMetadata(null, { assignments, tags })\n const attachmentId = randomUUID()\n const att = em.create(Attachment, {\n id: attachmentId,\n entityId,\n recordId,\n organizationId: auth.orgId!,\n tenantId: auth.tenantId!,\n fileName: safeName,\n mimeType: fileMimeType,\n fileSize: buf.length,\n partitionCode: partition.code,\n storageDriver: partition.storageDriver || 'local',\n storagePath: stored.storagePath,\n url: buildAttachmentFileUrl(attachmentId),\n content: extractedContent,\n storageMetadata: metadata,\n })\n await em.persistAndFlush(att)\n\n if (useLlmOcr) {\n requestOcrProcessing(em, att, stored.absolutePath).catch((error) => {\n console.error('[attachments] failed to queue OCR processing', error)\n })\n } else if (wantsLlmOcr) {\n console.warn('[attachments] OCR requested but OPENAI_API_KEY not configured, falling back to text extraction when available')\n }\n\n if (dataEngine) {\n try {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.attachments.attachment,\n recordId: attachmentId,\n tenantId,\n organizationId: orgId,\n values: customFieldValues,\n })\n } catch (error) {\n console.error('[attachments] failed to persist custom attributes', error)\n return NextResponse.json({ error: 'Failed to save attachment attributes.' }, { status: 500 })\n }\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: att,\n identifiers: {\n id: att.id,\n organizationId: att.organizationId ?? null,\n tenantId: att.tenantId ?? null,\n },\n events: attachmentCrudEvents,\n indexer: attachmentCrudIndexer,\n })\n await dataEngine.flushOrmEntityChanges()\n }\n\n return NextResponse.json({\n ok: true,\n item: {\n id: attachmentId,\n url: att.url,\n fileName: safeName,\n fileSize: buf.length,\n partitionCode: partition.code,\n thumbnailUrl: buildAttachmentImageUrl(attachmentId, {\n width: 320,\n height: 320,\n slug: slugifyAttachmentFileName(safeName),\n }),\n content: extractedContent ?? null,\n tags: metadata.tags ?? [],\n assignments: metadata.assignments ?? [],\n customFields: Object.keys(customFieldValues).length ? customFieldValues : undefined,\n },\n })\n}\n\nasync function readTenantAttachmentUsageBytes(em: EntityManager, tenantId: string): Promise<number> {\n try {\n const knex = (em as any).getConnection().getKnex()\n const row = await knex('attachments')\n .where({ tenant_id: tenantId })\n .sum({ totalSize: 'file_size' })\n .first()\n const total = row?.totalSize\n if (typeof total === 'number') return Number.isFinite(total) ? total : 0\n if (typeof total === 'string') {\n const parsed = Number(total)\n return Number.isFinite(parsed) ? parsed : 0\n }\n return 0\n } catch {\n return 0\n }\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const url = new URL(req.url)\n const id = url.searchParams.get('id') || ''\n if (!id) return NextResponse.json({ error: 'Attachment id is required' }, { status: 400 })\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const dataEngine = resolve('dataEngine')\n const deleteFilter: Record<string, unknown> = { id, tenantId: auth.tenantId!, organizationId: auth.orgId }\n const record = await em.findOne(Attachment, deleteFilter)\n if (!record) return NextResponse.json({ error: 'Attachment not found' }, { status: 404 })\n await em.removeAndFlush(record)\n await clearAttachmentThumbnailCache(record.partitionCode, record.id).catch((error) => {\n console.error('[attachments] failed to cleanup cached thumbnails', error)\n })\n if (record.storagePath) {\n await deletePartitionFile(record.partitionCode, record.storagePath, record.storageDriver)\n }\n if (dataEngine) {\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId ?? null,\n tenantId: record.tenantId ?? null,\n },\n events: attachmentCrudEvents,\n indexer: attachmentCrudIndexer,\n })\n await dataEngine.flushOrmEntityChanges()\n }\n return NextResponse.json({ ok: true })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Manage entity attachments',\n description: 'Upload and list attachments associated with module entities and records.',\n methods: {\n GET: {\n summary: 'List attachments for a record',\n description: 'Returns uploaded attachments for the given entity record, ordered by newest first.',\n query: attachmentQuerySchema,\n responses: [\n { status: 200, description: 'Attachments found for the record', schema: attachmentListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Missing entity or record identifiers', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n ],\n },\n POST: {\n summary: 'Upload attachment',\n description: 'Uploads a new attachment using multipart form-data and stores metadata for later retrieval.',\n requestBody: {\n contentType: 'multipart/form-data',\n schema: attachmentUploadBodySchema,\n },\n responses: [\n { status: 200, description: 'Attachment stored successfully', schema: uploadResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Payload validation error', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 403, description: 'Attachment violates field constraints', schema: errorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete attachment',\n description: 'Removes an uploaded attachment and deletes the stored asset.',\n query: attachmentDeleteQuerySchema,\n responses: [\n { status: 200, description: 'Attachment deleted', schema: z.object({ ok: z.literal(true) }) },\n { status: 404, description: 'Attachment not found', schema: errorSchema },\n ],\n errors: [\n { status: 400, description: 'Missing attachment identifier', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { z } from 'zod'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { buildAttachmentFileUrl, buildAttachmentImageUrl, slugifyAttachmentFileName } from '../lib/imageUrls'\nimport { ensureDefaultPartitions, resolveDefaultPartitionCode, sanitizePartitionCode } from '../lib/partitions'\nimport { Attachment, AttachmentPartition } from '../data/entities'\nimport { storePartitionFile, deletePartitionFile } from '../lib/storage'\nimport { extractAttachmentContent } from '../lib/textExtraction'\nimport { requestOcrProcessing } from '../lib/ocrQueue'\nimport { OcrService, shouldUseLlmOcr } from '../lib/ocrService'\nimport { clearAttachmentThumbnailCache } from '../lib/thumbnailCache'\nimport {\n mergeAttachmentMetadata,\n normalizeAttachmentAssignments,\n normalizeAttachmentTags,\n readAttachmentMetadata,\n upsertAssignment,\n type AttachmentAssignment,\n} from '../lib/metadata'\nimport { randomUUID } from 'crypto'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { splitCustomFieldPayload } from '@open-mercato/shared/lib/crud/custom-fields'\nimport { emitCrudSideEffects, setCustomFieldsIfAny } from '@open-mercato/shared/lib/commands/helpers'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { attachmentCrudEvents, attachmentCrudIndexer } from '../lib/crud'\nimport { E } from '#generated/entities.ids.generated'\nimport { resolveDefaultAttachmentOcrEnabled } from '../lib/ocrConfig'\nimport {\n detectAttachmentMimeType,\n hasDangerousExecutableExtension,\n isActiveContentAttachment,\n sanitizeUploadedFileName,\n} from '../lib/security'\nimport {\n isMultipartRequestWithinUploadLimit,\n resolveAttachmentMaxBytes,\n willExceedAttachmentTenantQuota,\n} from '../lib/upload-limits'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['attachments.view'] },\n POST: { requireAuth: true, requireFeatures: ['attachments.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['attachments.manage'] },\n}\n\nconst attachmentQuerySchema = z.object({\n entityId: z.string().min(1).describe('Entity identifier that owns the attachments'),\n recordId: z.string().min(1).describe('Record identifier within the entity'),\n page: z.coerce.number().min(1).optional(),\n pageSize: z.coerce.number().min(1).max(100).optional(),\n})\n\nconst attachmentAssignmentSchema = z.object({\n type: z.string().describe('Assignment type identifier'),\n id: z.string().describe('Assignment record identifier'),\n href: z.string().nullable().optional().describe('Optional link to the related record'),\n label: z.string().nullable().optional().describe('Optional label for the assignment'),\n})\n\nconst attachmentItemSchema = z.object({\n id: z.string().describe('Attachment identifier'),\n url: z.string().describe('Public path to the stored asset'),\n fileName: z.string().describe('Original filename'),\n fileSize: z.number().int().nonnegative().describe('File size in bytes'),\n createdAt: z.string().describe('Upload timestamp (ISO 8601)'),\n mimeType: z.string().nullable().optional().describe('MIME type of the file'),\n thumbnailUrl: z.string().optional().describe('Helper route that renders a thumbnail'),\n partitionCode: z.string().optional().describe('Partition identifier'),\n tags: z.array(z.string()).optional().describe('Tags assigned to the attachment'),\n content: z.string().nullable().optional().describe('Extracted text or markdown content'),\n assignments: z.array(attachmentAssignmentSchema).optional().describe('Records that reference this attachment'),\n})\n\nconst attachmentListResponseSchema = z.object({\n items: z.array(attachmentItemSchema),\n total: z.number().int().nonnegative().optional(),\n page: z.number().int().min(1).optional(),\n pageSize: z.number().int().min(1).optional(),\n totalPages: z.number().int().min(1).optional(),\n})\n\nconst attachmentUploadBodySchema = z.object({\n entityId: z.string().min(1),\n recordId: z.string().min(1),\n fieldKey: z.string().optional(),\n file: z.string().min(1).describe('Binary file payload; supplied as multipart form-data'),\n customFields: z\n .string()\n .optional()\n .describe('JSON encoded map of custom field values collected from the upload form.'),\n})\n\nconst attachmentDeleteQuerySchema = z.object({\n id: z.string().uuid(),\n})\n\nconst uploadResponseSchema = z.object({\n ok: z.literal(true),\n item: z.object({\n id: z.string(),\n url: z.string(),\n fileName: z.string(),\n fileSize: z.number().int().nonnegative(),\n thumbnailUrl: z.string().optional(),\n content: z.string().nullable().optional(),\n tags: z.array(z.string()).optional(),\n assignments: z.array(attachmentAssignmentSchema).optional(),\n customFields: z.record(z.string(), z.unknown()).optional(),\n }),\n})\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nconst LIBRARY_ENTITY_ID = 'attachments:library'\n\nfunction parseCustomFieldsEntry(value: FormDataEntryValue | null): Record<string, unknown> {\n if (!value) return {}\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return {}\n try {\n const parsed = JSON.parse(trimmed)\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>\n }\n } catch {\n return {}\n }\n }\n if (typeof value === 'object' && !Array.isArray(value) && !(value instanceof File)) {\n return { ...(value as Record<string, unknown>) }\n }\n return {}\n}\n\nfunction buildFormPayload(form: FormData): Record<string, unknown> {\n const payload: Record<string, unknown> = {}\n form.forEach((value, key) => {\n if (key === 'customFields') {\n payload.customFields = parseCustomFieldsEntry(value)\n return\n }\n payload[key] = value\n })\n return payload\n}\n\nfunction parseFormTags(value: FormDataEntryValue | null): string[] {\n if (!value) return []\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (!trimmed) return []\n try {\n const parsed = JSON.parse(trimmed)\n return normalizeAttachmentTags(parsed)\n } catch {\n return normalizeAttachmentTags(value)\n }\n }\n return []\n}\n\nfunction parseFormAssignments(value: FormDataEntryValue | null): AttachmentAssignment[] {\n if (!value) return []\n if (typeof value !== 'string') return []\n const trimmed = value.trim()\n if (!trimmed) return []\n try {\n const parsed = JSON.parse(trimmed)\n return normalizeAttachmentAssignments(parsed)\n } catch {\n return []\n }\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || (!auth.orgId && !auth.isSuperAdmin)) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const url = new URL(req.url)\n const parsedQuery = attachmentQuerySchema.safeParse({\n entityId: url.searchParams.get('entityId') || '',\n recordId: url.searchParams.get('recordId') || '',\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n })\n if (!parsedQuery.success) {\n return NextResponse.json({ error: 'entityId and recordId are required' }, { status: 400 })\n }\n const { entityId, recordId, page, pageSize } = parsedQuery.data\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const filter: Record<string, unknown> = { entityId, recordId, tenantId: auth.tenantId! }\n if (auth.orgId) filter.organizationId = auth.orgId\n const orderBy: Record<string, 'ASC' | 'DESC'> = { createdAt: 'DESC' }\n const usePaging = typeof page === 'number' && typeof pageSize === 'number'\n const total = usePaging ? await em.count(Attachment, filter) : null\n const currentPage = usePaging ? Math.max(1, page) : null\n const currentPageSize = usePaging ? pageSize : null\n const totalPages = usePaging && total !== null ? Math.max(1, Math.ceil(total / currentPageSize!)) : null\n const pageOffset = usePaging ? (Math.min(currentPage!, totalPages!) - 1) * currentPageSize! : undefined\n const items = await findWithDecryption(\n em,\n Attachment,\n filter,\n {\n orderBy,\n ...(usePaging\n ? {\n limit: currentPageSize!,\n offset: pageOffset,\n }\n : {}),\n },\n {\n tenantId: auth.tenantId ?? null,\n organizationId: auth.orgId ?? null,\n },\n )\n return NextResponse.json({\n items: items.map((a: any) => {\n const metadata = readAttachmentMetadata(a.storageMetadata)\n return {\n id: a.id,\n url: a.url,\n fileName: a.fileName,\n fileSize: a.fileSize,\n createdAt: a.createdAt,\n mimeType: a.mimeType ?? null,\n partitionCode: a.partitionCode,\n content: a.content ?? null,\n thumbnailUrl: buildAttachmentImageUrl(a.id, {\n width: 320,\n height: 320,\n slug: slugifyAttachmentFileName(a.fileName),\n }),\n tags: metadata.tags ?? [],\n assignments: metadata.assignments ?? [],\n }\n }),\n ...(usePaging\n ? {\n total,\n page: Math.min(currentPage!, totalPages!),\n pageSize: currentPageSize,\n totalPages,\n }\n : {}),\n })\n}\n\nexport async function POST(req: Request) {\n const { t } = await resolveTranslations()\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const tenantId = auth.tenantId\n const orgId = auth.orgId\n\n const contentType = req.headers.get('content-type') || ''\n if (!contentType.toLowerCase().includes('multipart/form-data')) {\n return NextResponse.json({ error: 'Expected multipart/form-data' }, { status: 400 })\n }\n if (!isMultipartRequestWithinUploadLimit(req.headers.get('content-length'))) {\n return NextResponse.json({ error: 'Attachment exceeds the maximum upload size.' }, { status: 413 })\n }\n\n const form = await req.formData()\n const formPayload = buildFormPayload(form)\n const customFieldValues = splitCustomFieldPayload(formPayload).custom\n const entityId = String(form.get('entityId') || '')\n const recordId = String(form.get('recordId') || '')\n const fieldKey = String(form.get('fieldKey') || '')\n const file = form.get('file') as unknown as File | null\n if (!entityId || !recordId || !file) return NextResponse.json({ error: 'entityId, recordId and file are required' }, { status: 400 })\n const partitionOverrideRaw = form.get('partitionCode')\n const partitionOverride =\n typeof partitionOverrideRaw === 'string' && partitionOverrideRaw.trim().length > 0\n ? sanitizePartitionCode(partitionOverrideRaw)\n : null\n const tags = parseFormTags(form.get('tags'))\n const assignmentsFromForm = parseFormAssignments(form.get('assignments'))\n\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const dataEngine = resolve('dataEngine')\n await ensureDefaultPartitions(em)\n // Optional per-field validations\n let partitionFromField: string | null = null\n let fieldMaxAttachmentSizeMb: number | null = null\n if (fieldKey) {\n try {\n const { CustomFieldDef } = await import('@open-mercato/core/modules/entities/data/entities')\n const def = await em.findOne(CustomFieldDef, {\n entityId,\n key: fieldKey,\n $and: [\n { $or: [ { tenantId: auth.tenantId }, { tenantId: null } ] },\n ],\n isActive: true,\n })\n const cfg = (def as any)?.configJson || {}\n const ext = (file.name || '').split('.').pop()?.toLowerCase() || ''\n if (Array.isArray(cfg.acceptExtensions) && cfg.acceptExtensions.length) {\n const allowed = new Set((cfg.acceptExtensions as any[]).map((x: any) => String(x).toLowerCase().replace(/^\\./, '')))\n if (!allowed.has(ext)) return NextResponse.json({ error: 'File type not allowed' }, { status: 400 })\n }\n if (typeof cfg.maxAttachmentSizeMb === 'number' && cfg.maxAttachmentSizeMb > 0) {\n fieldMaxAttachmentSizeMb = cfg.maxAttachmentSizeMb\n }\n if (typeof cfg.partitionCode === 'string' && cfg.partitionCode.trim().length > 0) {\n partitionFromField = sanitizePartitionCode(cfg.partitionCode)\n }\n } catch {}\n }\n if (hasDangerousExecutableExtension(file.name)) {\n return NextResponse.json({\n error: t('attachments.errors.dangerousExecutable', 'Executable file types are not allowed as attachments.'),\n }, { status: 400 })\n }\n const effectiveMaxBytes = resolveAttachmentMaxBytes(fieldMaxAttachmentSizeMb)\n if (file.size > effectiveMaxBytes) {\n return NextResponse.json({\n error: t('attachments.errors.maxUploadSize', 'Attachment exceeds the maximum upload size.'),\n }, { status: 413 })\n }\n const tenantUsageBytes = await readTenantAttachmentUsageBytes(em, tenantId)\n if (willExceedAttachmentTenantQuota(tenantUsageBytes, file.size)) {\n return NextResponse.json({\n error: t('attachments.errors.quotaExceeded', 'Attachment storage quota exceeded for this tenant.'),\n }, { status: 413 })\n }\n const buf = Buffer.from(await file.arrayBuffer())\n const safeName = sanitizeUploadedFileName(file.name)\n const fileMimeType = detectAttachmentMimeType(buf, safeName, (file as any).type)\n if (isActiveContentAttachment(buf, safeName, fileMimeType)) {\n return NextResponse.json({ error: t('attachments.errors.activeContentBlocked', 'Active content uploads are not allowed.') }, { status: 400 })\n }\n const defaultPartitionCode = resolveDefaultPartitionCode(entityId)\n const resolvedPartitionCode = partitionOverride ?? partitionFromField ?? defaultPartitionCode\n const partitionCodeCandidates = Array.from(\n new Set(\n [partitionOverride, partitionFromField, resolvedPartitionCode].filter(\n (code): code is string => typeof code === 'string' && code.length > 0,\n ),\n ),\n )\n let partition: AttachmentPartition | null = null\n for (const code of partitionCodeCandidates) {\n const record = await em.findOne(AttachmentPartition, { code })\n if (record) {\n partition = record\n break\n }\n }\n if (!partition) {\n partition = await em.findOne(AttachmentPartition, { code: defaultPartitionCode })\n }\n if (!partition) {\n return NextResponse.json({ error: 'Storage partition is not configured.' }, { status: 400 })\n }\n const requestedPublicOverride =\n typeof partitionOverride === 'string' &&\n partitionOverride.length > 0 &&\n partition.code === partitionOverride &&\n partition.isPublic === true &&\n partition.code !== defaultPartitionCode &&\n partition.code !== partitionFromField\n if (requestedPublicOverride) {\n return NextResponse.json({ error: t('attachments.errors.publicPartitionBlocked', 'Public storage partitions cannot be selected explicitly for this upload.') }, { status: 403 })\n }\n let stored\n try {\n stored = await storePartitionFile({\n partitionCode: partition.code,\n orgId,\n tenantId,\n fileName: safeName,\n buffer: buf,\n })\n } catch (error) {\n console.error('[attachments] failed to persist file', error)\n return NextResponse.json({ error: 'Failed to persist attachment.' }, { status: 500 })\n }\n\n const requiresOcr =\n typeof (partition as any).requiresOcr === 'boolean'\n ? Boolean((partition as any).requiresOcr)\n : resolveDefaultAttachmentOcrEnabled()\n let extractedContent: string | null = null\n const wantsLlmOcr = requiresOcr && shouldUseLlmOcr(fileMimeType, safeName)\n const ocrService = wantsLlmOcr ? new OcrService() : null\n const useLlmOcr = Boolean(wantsLlmOcr && ocrService?.available)\n\n if (requiresOcr && !useLlmOcr) {\n try {\n extractedContent = await extractAttachmentContent({\n filePath: stored.absolutePath,\n mimeType: fileMimeType,\n })\n } catch (error) {\n console.error('[attachments] failed to extract attachment content', error)\n }\n }\n\n let assignments = assignmentsFromForm.slice()\n if (entityId !== LIBRARY_ENTITY_ID) {\n assignments = upsertAssignment(assignments, { type: entityId, id: recordId })\n }\n const metadata = mergeAttachmentMetadata(null, { assignments, tags })\n const attachmentId = randomUUID()\n const att = em.create(Attachment, {\n id: attachmentId,\n entityId,\n recordId,\n organizationId: auth.orgId!,\n tenantId: auth.tenantId!,\n fileName: safeName,\n mimeType: fileMimeType,\n fileSize: buf.length,\n partitionCode: partition.code,\n storageDriver: partition.storageDriver || 'local',\n storagePath: stored.storagePath,\n url: buildAttachmentFileUrl(attachmentId),\n content: extractedContent,\n storageMetadata: metadata,\n })\n await em.persistAndFlush(att)\n\n if (useLlmOcr) {\n requestOcrProcessing(em, att, stored.absolutePath).catch((error) => {\n console.error('[attachments] failed to queue OCR processing', error)\n })\n } else if (wantsLlmOcr) {\n console.warn('[attachments] OCR requested but OPENAI_API_KEY not configured, falling back to text extraction when available')\n }\n\n if (dataEngine) {\n try {\n await setCustomFieldsIfAny({\n dataEngine,\n entityId: E.attachments.attachment,\n recordId: attachmentId,\n tenantId,\n organizationId: orgId,\n values: customFieldValues,\n })\n } catch (error) {\n console.error('[attachments] failed to persist custom attributes', error)\n return NextResponse.json({ error: 'Failed to save attachment attributes.' }, { status: 500 })\n }\n await emitCrudSideEffects({\n dataEngine,\n action: 'created',\n entity: att,\n identifiers: {\n id: att.id,\n organizationId: att.organizationId ?? null,\n tenantId: att.tenantId ?? null,\n },\n events: attachmentCrudEvents,\n indexer: attachmentCrudIndexer,\n })\n await dataEngine.flushOrmEntityChanges()\n }\n\n return NextResponse.json({\n ok: true,\n item: {\n id: attachmentId,\n url: att.url,\n fileName: safeName,\n fileSize: buf.length,\n partitionCode: partition.code,\n thumbnailUrl: buildAttachmentImageUrl(attachmentId, {\n width: 320,\n height: 320,\n slug: slugifyAttachmentFileName(safeName),\n }),\n content: extractedContent ?? null,\n tags: metadata.tags ?? [],\n assignments: metadata.assignments ?? [],\n customFields: Object.keys(customFieldValues).length ? customFieldValues : undefined,\n },\n })\n}\n\nasync function readTenantAttachmentUsageBytes(em: EntityManager, tenantId: string): Promise<number> {\n try {\n const knex = (em as any).getConnection().getKnex()\n const row = await knex('attachments')\n .where({ tenant_id: tenantId })\n .sum({ totalSize: 'file_size' })\n .first()\n const total = row?.totalSize\n if (typeof total === 'number') return Number.isFinite(total) ? total : 0\n if (typeof total === 'string') {\n const parsed = Number(total)\n return Number.isFinite(parsed) ? parsed : 0\n }\n return 0\n } catch {\n return 0\n }\n}\n\nexport async function DELETE(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth || !auth.tenantId || !auth.orgId) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n const url = new URL(req.url)\n const id = url.searchParams.get('id') || ''\n if (!id) return NextResponse.json({ error: 'Attachment id is required' }, { status: 400 })\n const { resolve } = await createRequestContainer()\n const em = resolve('em') as EntityManager\n const dataEngine = resolve('dataEngine')\n const deleteFilter: Record<string, unknown> = { id, tenantId: auth.tenantId!, organizationId: auth.orgId }\n const record = await em.findOne(Attachment, deleteFilter)\n if (!record) return NextResponse.json({ error: 'Attachment not found' }, { status: 404 })\n await em.removeAndFlush(record)\n await clearAttachmentThumbnailCache(record.partitionCode, record.id).catch((error) => {\n console.error('[attachments] failed to cleanup cached thumbnails', error)\n })\n if (record.storagePath) {\n await deletePartitionFile(record.partitionCode, record.storagePath, record.storageDriver)\n }\n if (dataEngine) {\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: record,\n identifiers: {\n id: record.id,\n organizationId: record.organizationId ?? null,\n tenantId: record.tenantId ?? null,\n },\n events: attachmentCrudEvents,\n indexer: attachmentCrudIndexer,\n })\n await dataEngine.flushOrmEntityChanges()\n }\n return NextResponse.json({ ok: true })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Manage entity attachments',\n description: 'Upload and list attachments associated with module entities and records.',\n methods: {\n GET: {\n summary: 'List attachments for a record',\n description: 'Returns uploaded attachments for the given entity record, ordered by newest first.',\n query: attachmentQuerySchema,\n responses: [\n { status: 200, description: 'Attachments found for the record', schema: attachmentListResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Missing entity or record identifiers', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n ],\n },\n POST: {\n summary: 'Upload attachment',\n description: 'Uploads a new attachment using multipart form-data and stores metadata for later retrieval.',\n requestBody: {\n contentType: 'multipart/form-data',\n schema: attachmentUploadBodySchema,\n },\n responses: [\n { status: 200, description: 'Attachment stored successfully', schema: uploadResponseSchema },\n ],\n errors: [\n { status: 400, description: 'Payload validation error', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n { status: 403, description: 'Attachment violates field constraints', schema: errorSchema },\n ],\n },\n DELETE: {\n summary: 'Delete attachment',\n description: 'Removes an uploaded attachment and deletes the stored asset.',\n query: attachmentDeleteQuerySchema,\n responses: [\n { status: 200, description: 'Attachment deleted', schema: z.object({ ok: z.literal(true) }) },\n { status: 404, description: 'Attachment not found', schema: errorSchema },\n ],\n errors: [\n { status: 400, description: 'Missing attachment identifier', schema: errorSchema },\n { status: 401, description: 'Unauthorized', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["metadata"]
7
7
  }
@@ -0,0 +1,208 @@
1
+ import { NextResponse } from "next/server";
2
+ import { z } from "zod";
3
+ import { createRequestContainer } from "@open-mercato/shared/lib/di/container";
4
+ import { getAuthFromRequest } from "@open-mercato/shared/lib/auth/server";
5
+ import { defaultExportFilename, serializeExport } from "@open-mercato/shared/lib/crud/exporters";
6
+ import { parseBooleanToken } from "@open-mercato/shared/lib/boolean";
7
+ import { resolveFeatureCheckContext } from "@open-mercato/core/modules/directory/utils/organizationScope";
8
+ import { extractChangeRows } from "@open-mercato/core/modules/audit_logs/lib/changeRows";
9
+ import {
10
+ ACTION_LOG_FILTER_TYPES,
11
+ deriveActionLogActionType,
12
+ deriveActionLogSource
13
+ } from "@open-mercato/core/modules/audit_logs/lib/projections";
14
+ import { loadAuditLogDisplayMaps } from "../../display.js";
15
+ const metadata = {
16
+ GET: { requireAuth: true, requireFeatures: ["audit_logs.view_self"] }
17
+ };
18
+ const ACTION_TYPE_TOKENS = ACTION_LOG_FILTER_TYPES;
19
+ const SORT_FIELDS = ["createdAt", "user", "action", "field", "source"];
20
+ const SORT_DIRECTIONS = ["asc", "desc"];
21
+ const exportQuerySchema = z.object({
22
+ organizationId: z.string().uuid().describe("Limit results to a specific organization").optional(),
23
+ actorUserId: z.string().describe("Filter logs created by specific actor IDs (tenant administrators only). Accepts a single UUID or a comma-separated UUID list.").optional(),
24
+ resourceKind: z.string().describe('Filter by resource kind (e.g., "order", "product")').optional(),
25
+ resourceId: z.string().describe("Filter by resource ID (UUID of the specific record)").optional(),
26
+ actionType: z.string().describe("Filter by action type (`create`, `edit`, `delete`, `assign`). Accepts a single value or a comma-separated list.").optional(),
27
+ fieldName: z.string().describe("Filter to entries where the given field changed. Accepts a single field name or a comma-separated list.").optional(),
28
+ includeRelated: z.enum(["true", "false"]).default("false").describe("When `true`, also returns changes to child entities linked via parentResourceKind/parentResourceId").optional(),
29
+ undoableOnly: z.enum(["true", "false"]).default("false").describe("When `true`, only undoable actions are returned").optional(),
30
+ limit: z.string().describe("Maximum number of records to export (default 1000, capped at 1000)").optional(),
31
+ sortField: z.enum(SORT_FIELDS).describe("Sort field: `createdAt`, `user`, `action`, `field`, or `source`.").optional(),
32
+ sortDir: z.enum(SORT_DIRECTIONS).describe("Sort direction: `asc` or `desc`.").optional(),
33
+ before: z.string().describe("Return actions created before this ISO-8601 timestamp").optional(),
34
+ after: z.string().describe("Return actions created after this ISO-8601 timestamp").optional()
35
+ });
36
+ const responseSchema = z.object({
37
+ file: z.literal("csv")
38
+ });
39
+ const errorSchema = z.object({
40
+ error: z.string()
41
+ });
42
+ function splitCsv(value) {
43
+ if (!value) return [];
44
+ return value.split(",").map((entry) => entry.trim()).filter(Boolean);
45
+ }
46
+ function parseActionTypes(param) {
47
+ return splitCsv(param).filter(
48
+ (value) => ACTION_TYPE_TOKENS.includes(value)
49
+ );
50
+ }
51
+ function parseLimit(param) {
52
+ if (!param) return 1e3;
53
+ const value = Number(param);
54
+ if (!Number.isFinite(value)) return 1e3;
55
+ return Math.min(Math.max(Math.trunc(value), 1), 1e3);
56
+ }
57
+ function parseDate(value) {
58
+ if (!value) return void 0;
59
+ const timestamp = Date.parse(value);
60
+ if (Number.isNaN(timestamp)) return void 0;
61
+ return new Date(timestamp);
62
+ }
63
+ function formatValue(value) {
64
+ if (value == null) return "";
65
+ if (value instanceof Date) return value.toISOString();
66
+ if (Array.isArray(value)) return value.map((entry) => formatValue(entry)).filter(Boolean).join(", ");
67
+ if (typeof value === "object") return JSON.stringify(value);
68
+ return String(value);
69
+ }
70
+ async function GET(req) {
71
+ const auth = await getAuthFromRequest(req);
72
+ if (!auth) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
73
+ const container = await createRequestContainer();
74
+ const { organizationId: defaultOrganizationId, scope } = await resolveFeatureCheckContext({ container, auth, request: req });
75
+ const rbac = container.resolve("rbacService");
76
+ const actionLogs = container.resolve("actionLogService");
77
+ const em = container.resolve("em");
78
+ const canViewTenant = await rbac.userHasAllFeatures(
79
+ auth.sub,
80
+ ["audit_logs.view_tenant"],
81
+ { tenantId: auth.tenantId ?? null, organizationId: defaultOrganizationId ?? null }
82
+ );
83
+ const url = new URL(req.url);
84
+ const queryOrgId = url.searchParams.get("organizationId");
85
+ const actorQuery = url.searchParams.get("actorUserId");
86
+ const resourceKind = url.searchParams.get("resourceKind") ?? void 0;
87
+ const resourceId = url.searchParams.get("resourceId") ?? void 0;
88
+ const actionTypes = parseActionTypes(url.searchParams.get("actionType"));
89
+ const fieldNames = splitCsv(url.searchParams.get("fieldName"));
90
+ const includeRelated = parseBooleanToken(url.searchParams.get("includeRelated")) === true;
91
+ const undoableOnly = parseBooleanToken(url.searchParams.get("undoableOnly")) === true;
92
+ const limit = parseLimit(url.searchParams.get("limit"));
93
+ const sortField = SORT_FIELDS.find((value) => value === url.searchParams.get("sortField")) ?? "createdAt";
94
+ const sortDir = SORT_DIRECTIONS.find((value) => value === url.searchParams.get("sortDir")) ?? "desc";
95
+ const before = parseDate(url.searchParams.get("before"));
96
+ const after = parseDate(url.searchParams.get("after"));
97
+ let organizationId = defaultOrganizationId;
98
+ if (queryOrgId) {
99
+ if (scope.allowedIds === null || scope.allowedIds.includes(queryOrgId)) {
100
+ organizationId = queryOrgId;
101
+ }
102
+ }
103
+ let actorUserId = canViewTenant ? void 0 : auth.sub;
104
+ let actorUserIds;
105
+ if (canViewTenant && actorQuery) {
106
+ const parsedActorUserIds = splitCsv(actorQuery);
107
+ if (parsedActorUserIds.length === 1) {
108
+ actorUserId = parsedActorUserIds[0];
109
+ } else if (parsedActorUserIds.length > 1) {
110
+ actorUserId = void 0;
111
+ actorUserIds = parsedActorUserIds;
112
+ }
113
+ }
114
+ const entriesResult = await actionLogs.list({
115
+ tenantId: auth.tenantId ?? void 0,
116
+ organizationId: organizationId ?? void 0,
117
+ actorUserId,
118
+ actorUserIds,
119
+ resourceKind,
120
+ resourceId,
121
+ actionTypes,
122
+ fieldNames,
123
+ includeRelated,
124
+ undoableOnly,
125
+ sortField,
126
+ sortDir,
127
+ limit,
128
+ before,
129
+ after
130
+ });
131
+ const entries = entriesResult.items;
132
+ const displayMaps = await loadAuditLogDisplayMaps(em, {
133
+ userIds: entries.map((entry) => entry.actorUserId).filter((value) => Boolean(value)),
134
+ tenantIds: entries.map((entry) => entry.tenantId).filter((value) => Boolean(value)),
135
+ organizationIds: entries.map((entry) => entry.organizationId).filter((value) => Boolean(value))
136
+ });
137
+ const rows = entries.flatMap((entry) => {
138
+ const actionType = deriveActionLogActionType(entry);
139
+ const actionLabel = actionType === "system" ? entry.actionLabel ?? "System" : actionType.charAt(0).toUpperCase() + actionType.slice(1);
140
+ const baseRow = {
141
+ when: entry.createdAt?.toISOString?.() ?? "",
142
+ user: entry.actorUserId ? displayMaps.users[entry.actorUserId] ?? entry.actorUserId : "System",
143
+ action: actionLabel,
144
+ source: deriveActionLogSource(entry.contextJson, entry.actorUserId).toUpperCase()
145
+ };
146
+ const changes = extractChangeRows(entry.changesJson, entry.snapshotBefore);
147
+ if (changes.length === 0) {
148
+ return [{
149
+ ...baseRow,
150
+ field: "",
151
+ oldValue: "",
152
+ newValue: ""
153
+ }];
154
+ }
155
+ return changes.map((change) => {
156
+ return {
157
+ ...baseRow,
158
+ field: change.field,
159
+ oldValue: formatValue(change.from),
160
+ newValue: formatValue(change.to)
161
+ };
162
+ });
163
+ });
164
+ const prepared = {
165
+ columns: [
166
+ { field: "when", header: "When" },
167
+ { field: "user", header: "User" },
168
+ { field: "action", header: "Action" },
169
+ { field: "field", header: "Field" },
170
+ { field: "oldValue", header: "Old Value" },
171
+ { field: "newValue", header: "New Value" },
172
+ { field: "source", header: "Source" }
173
+ ],
174
+ rows
175
+ };
176
+ const serialized = serializeExport(prepared, "csv");
177
+ const filename = defaultExportFilename("changelog-export", "csv");
178
+ return new Response(serialized.body, {
179
+ headers: {
180
+ "content-type": serialized.contentType,
181
+ "content-disposition": `attachment; filename="${filename}"`
182
+ }
183
+ });
184
+ }
185
+ const openApi = {
186
+ summary: "Export action audit logs",
187
+ description: "Exports filtered action audit log entries for the current tenant as CSV.",
188
+ methods: {
189
+ GET: {
190
+ summary: "Export action logs as CSV",
191
+ description: "Returns a CSV attachment containing filtered action audit log entries. Tenant administrators can widen the scope to other actors or organizations.",
192
+ query: exportQuerySchema,
193
+ responses: [
194
+ { status: 200, description: "CSV export generated successfully", schema: responseSchema }
195
+ ],
196
+ errors: [
197
+ { status: 400, description: "Invalid filter values", schema: errorSchema },
198
+ { status: 401, description: "Authentication required", schema: errorSchema }
199
+ ]
200
+ }
201
+ }
202
+ };
203
+ export {
204
+ GET,
205
+ metadata,
206
+ openApi
207
+ };
208
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/audit_logs/api/audit-logs/actions/export/route.ts"],
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { defaultExportFilename, serializeExport, type PreparedExport } from '@open-mercato/shared/lib/crud/exporters'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { parseBooleanToken } from '@open-mercato/shared/lib/boolean'\nimport { resolveFeatureCheckContext } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport type { RbacService } from '@open-mercato/core/modules/auth/services/rbacService'\nimport { extractChangeRows } from '@open-mercato/core/modules/audit_logs/lib/changeRows'\nimport {\n ACTION_LOG_FILTER_TYPES,\n deriveActionLogActionType,\n deriveActionLogSource,\n} from '@open-mercato/core/modules/audit_logs/lib/projections'\nimport { ActionLogService } from '@open-mercato/core/modules/audit_logs/services/actionLogService'\nimport { loadAuditLogDisplayMaps } from '../../display'\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['audit_logs.view_self'] },\n}\n\nconst ACTION_TYPE_TOKENS = ACTION_LOG_FILTER_TYPES\nconst SORT_FIELDS = ['createdAt', 'user', 'action', 'field', 'source'] as const\nconst SORT_DIRECTIONS = ['asc', 'desc'] as const\n\nconst exportQuerySchema = z.object({\n organizationId: z.string().uuid().describe('Limit results to a specific organization').optional(),\n actorUserId: z\n .string()\n .describe('Filter logs created by specific actor IDs (tenant administrators only). Accepts a single UUID or a comma-separated UUID list.')\n .optional(),\n resourceKind: z.string().describe('Filter by resource kind (e.g., \"order\", \"product\")').optional(),\n resourceId: z.string().describe('Filter by resource ID (UUID of the specific record)').optional(),\n actionType: z\n .string()\n .describe('Filter by action type (`create`, `edit`, `delete`, `assign`). Accepts a single value or a comma-separated list.')\n .optional(),\n fieldName: z\n .string()\n .describe('Filter to entries where the given field changed. Accepts a single field name or a comma-separated list.')\n .optional(),\n includeRelated: z\n .enum(['true', 'false'])\n .default('false')\n .describe('When `true`, also returns changes to child entities linked via parentResourceKind/parentResourceId')\n .optional(),\n undoableOnly: z\n .enum(['true', 'false'])\n .default('false')\n .describe('When `true`, only undoable actions are returned')\n .optional(),\n limit: z.string().describe('Maximum number of records to export (default 1000, capped at 1000)').optional(),\n sortField: z\n .enum(SORT_FIELDS)\n .describe('Sort field: `createdAt`, `user`, `action`, `field`, or `source`.')\n .optional(),\n sortDir: z\n .enum(SORT_DIRECTIONS)\n .describe('Sort direction: `asc` or `desc`.')\n .optional(),\n before: z.string().describe('Return actions created before this ISO-8601 timestamp').optional(),\n after: z.string().describe('Return actions created after this ISO-8601 timestamp').optional(),\n})\n\nconst responseSchema = z.object({\n file: z.literal('csv'),\n})\n\nconst errorSchema = z.object({\n error: z.string(),\n})\n\nfunction splitCsv(value: string | null): string[] {\n if (!value) return []\n return value\n .split(',')\n .map((entry) => entry.trim())\n .filter(Boolean)\n}\n\nfunction parseActionTypes(param: string | null) {\n return splitCsv(param).filter((value): value is (typeof ACTION_TYPE_TOKENS)[number] =>\n ACTION_TYPE_TOKENS.includes(value as (typeof ACTION_TYPE_TOKENS)[number]),\n )\n}\n\nfunction parseLimit(param: string | null): number {\n if (!param) return 1000\n const value = Number(param)\n if (!Number.isFinite(value)) return 1000\n return Math.min(Math.max(Math.trunc(value), 1), 1000)\n}\n\nfunction parseDate(value: string | null): Date | undefined {\n if (!value) return undefined\n const timestamp = Date.parse(value)\n if (Number.isNaN(timestamp)) return undefined\n return new Date(timestamp)\n}\n\nfunction formatValue(value: unknown): string {\n if (value == null) return ''\n if (value instanceof Date) return value.toISOString()\n if (Array.isArray(value)) return value.map((entry) => formatValue(entry)).filter(Boolean).join(', ')\n if (typeof value === 'object') return JSON.stringify(value)\n return String(value)\n}\n\nexport async function GET(req: Request) {\n const auth = await getAuthFromRequest(req)\n if (!auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })\n\n const container = await createRequestContainer()\n const { organizationId: defaultOrganizationId, scope } = await resolveFeatureCheckContext({ container, auth, request: req })\n const rbac = container.resolve('rbacService') as RbacService\n const actionLogs = container.resolve('actionLogService') as ActionLogService\n const em = container.resolve('em') as EntityManager\n\n const canViewTenant = await rbac.userHasAllFeatures(\n auth.sub,\n ['audit_logs.view_tenant'],\n { tenantId: auth.tenantId ?? null, organizationId: defaultOrganizationId ?? null },\n )\n\n const url = new URL(req.url)\n const queryOrgId = url.searchParams.get('organizationId')\n const actorQuery = url.searchParams.get('actorUserId')\n const resourceKind = url.searchParams.get('resourceKind') ?? undefined\n const resourceId = url.searchParams.get('resourceId') ?? undefined\n const actionTypes = parseActionTypes(url.searchParams.get('actionType'))\n const fieldNames = splitCsv(url.searchParams.get('fieldName'))\n const includeRelated = parseBooleanToken(url.searchParams.get('includeRelated')) === true\n const undoableOnly = parseBooleanToken(url.searchParams.get('undoableOnly')) === true\n const limit = parseLimit(url.searchParams.get('limit'))\n const sortField = SORT_FIELDS.find((value) => value === url.searchParams.get('sortField')) ?? 'createdAt'\n const sortDir = SORT_DIRECTIONS.find((value) => value === url.searchParams.get('sortDir')) ?? 'desc'\n const before = parseDate(url.searchParams.get('before'))\n const after = parseDate(url.searchParams.get('after'))\n\n let organizationId = defaultOrganizationId\n if (queryOrgId) {\n if (scope.allowedIds === null || scope.allowedIds.includes(queryOrgId)) {\n organizationId = queryOrgId\n }\n }\n\n let actorUserId: string | undefined = canViewTenant ? undefined : auth.sub\n let actorUserIds: string[] | undefined\n if (canViewTenant && actorQuery) {\n const parsedActorUserIds = splitCsv(actorQuery)\n if (parsedActorUserIds.length === 1) {\n actorUserId = parsedActorUserIds[0]\n } else if (parsedActorUserIds.length > 1) {\n actorUserId = undefined\n actorUserIds = parsedActorUserIds\n }\n }\n\n const entriesResult = await actionLogs.list({\n tenantId: auth.tenantId ?? undefined,\n organizationId: organizationId ?? undefined,\n actorUserId,\n actorUserIds,\n resourceKind,\n resourceId,\n actionTypes,\n fieldNames,\n includeRelated,\n undoableOnly,\n sortField,\n sortDir,\n limit,\n before,\n after,\n })\n const entries = entriesResult.items\n\n const displayMaps = await loadAuditLogDisplayMaps(em, {\n userIds: entries.map((entry) => entry.actorUserId).filter((value): value is string => Boolean(value)),\n tenantIds: entries.map((entry) => entry.tenantId).filter((value): value is string => Boolean(value)),\n organizationIds: entries.map((entry) => entry.organizationId).filter((value): value is string => Boolean(value)),\n })\n\n const rows = entries.flatMap((entry) => {\n const actionType = deriveActionLogActionType(entry)\n const actionLabel = actionType === 'system'\n ? entry.actionLabel ?? 'System'\n : actionType.charAt(0).toUpperCase() + actionType.slice(1)\n const baseRow = {\n when: entry.createdAt?.toISOString?.() ?? '',\n user: entry.actorUserId ? displayMaps.users[entry.actorUserId] ?? entry.actorUserId : 'System',\n action: actionLabel,\n source: deriveActionLogSource(entry.contextJson, entry.actorUserId).toUpperCase(),\n }\n const changes = extractChangeRows(entry.changesJson, entry.snapshotBefore)\n\n if (changes.length === 0) {\n return [{\n ...baseRow,\n field: '',\n oldValue: '',\n newValue: '',\n }]\n }\n\n return changes.map((change) => {\n return {\n ...baseRow,\n field: change.field,\n oldValue: formatValue(change.from),\n newValue: formatValue(change.to),\n }\n })\n })\n\n const prepared: PreparedExport = {\n columns: [\n { field: 'when', header: 'When' },\n { field: 'user', header: 'User' },\n { field: 'action', header: 'Action' },\n { field: 'field', header: 'Field' },\n { field: 'oldValue', header: 'Old Value' },\n { field: 'newValue', header: 'New Value' },\n { field: 'source', header: 'Source' },\n ],\n rows,\n }\n\n const serialized = serializeExport(prepared, 'csv')\n const filename = defaultExportFilename('changelog-export', 'csv')\n\n return new Response(serialized.body, {\n headers: {\n 'content-type': serialized.contentType,\n 'content-disposition': `attachment; filename=\"${filename}\"`,\n },\n })\n}\n\nexport const openApi: OpenApiRouteDoc = {\n summary: 'Export action audit logs',\n description: 'Exports filtered action audit log entries for the current tenant as CSV.',\n methods: {\n GET: {\n summary: 'Export action logs as CSV',\n description:\n 'Returns a CSV attachment containing filtered action audit log entries. Tenant administrators can widen the scope to other actors or organizations.',\n query: exportQuerySchema,\n responses: [\n { status: 200, description: 'CSV export generated successfully', schema: responseSchema },\n ],\n errors: [\n { status: 400, description: 'Invalid filter values', schema: errorSchema },\n { status: 401, description: 'Authentication required', schema: errorSchema },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }