@open-mercato/core 0.6.5-develop.4534.1.b459babe6d → 0.6.5-develop.4559.1.839e136509

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 (644) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +5 -0
  3. package/dist/generated/entities/role/index.js +3 -1
  4. package/dist/generated/entities/role/index.js.map +2 -2
  5. package/dist/generated/entities/user/index.js +3 -1
  6. package/dist/generated/entities/user/index.js.map +2 -2
  7. package/dist/generated/entity-fields-registry.js +2 -0
  8. package/dist/generated/entity-fields-registry.js.map +2 -2
  9. package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
  10. package/dist/helpers/integration/dbFixtures.js +2 -1
  11. package/dist/helpers/integration/dbFixtures.js.map +2 -2
  12. package/dist/helpers/integration/optimisticLockUi.js +104 -0
  13. package/dist/helpers/integration/optimisticLockUi.js.map +7 -0
  14. package/dist/helpers/integration/salesFixtures.js +17 -0
  15. package/dist/helpers/integration/salesFixtures.js.map +2 -2
  16. package/dist/modules/api_keys/backend/api-keys/page.js +9 -5
  17. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  18. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +17 -9
  19. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  20. package/dist/modules/auth/api/roles/acl/route.js +32 -13
  21. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  22. package/dist/modules/auth/api/roles/route.js +3 -1
  23. package/dist/modules/auth/api/roles/route.js.map +2 -2
  24. package/dist/modules/auth/api/sidebar/preferences/route.js +71 -3
  25. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  26. package/dist/modules/auth/api/users/acl/route.js +42 -19
  27. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  28. package/dist/modules/auth/api/users/route.js +3 -1
  29. package/dist/modules/auth/api/users/route.js.map +2 -2
  30. package/dist/modules/auth/backend/roles/[id]/edit/page.js +24 -4
  31. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  32. package/dist/modules/auth/backend/roles/page.js +8 -4
  33. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  34. package/dist/modules/auth/backend/users/[id]/edit/page.js +27 -5
  35. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  36. package/dist/modules/auth/backend/users/page.js +6 -2
  37. package/dist/modules/auth/backend/users/page.js.map +2 -2
  38. package/dist/modules/auth/components/AclEditor.js +3 -1
  39. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  40. package/dist/modules/auth/data/entities.js +6 -0
  41. package/dist/modules/auth/data/entities.js.map +2 -2
  42. package/dist/modules/auth/services/sidebarPreferencesService.js +32 -4
  43. package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
  44. package/dist/modules/business_rules/api/rules/route.js +28 -0
  45. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  46. package/dist/modules/business_rules/api/sets/route.js +28 -0
  47. package/dist/modules/business_rules/api/sets/route.js.map +2 -2
  48. package/dist/modules/business_rules/backend/rules/[id]/page.js +11 -4
  49. package/dist/modules/business_rules/backend/rules/[id]/page.js.map +3 -3
  50. package/dist/modules/business_rules/backend/rules/page.js +20 -11
  51. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  52. package/dist/modules/business_rules/backend/sets/[id]/page.js +11 -4
  53. package/dist/modules/business_rules/backend/sets/[id]/page.js.map +2 -2
  54. package/dist/modules/business_rules/backend/sets/page.js +20 -11
  55. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  56. package/dist/modules/catalog/api/categories/route.js +2 -0
  57. package/dist/modules/catalog/api/categories/route.js.map +2 -2
  58. package/dist/modules/catalog/api/products/route.js +2 -1
  59. package/dist/modules/catalog/api/products/route.js.map +2 -2
  60. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +2 -0
  61. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  62. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +94 -40
  63. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  64. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +37 -8
  65. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  66. package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js.map +2 -2
  67. package/dist/modules/catalog/commands/variants.js +32 -31
  68. package/dist/modules/catalog/commands/variants.js.map +2 -2
  69. package/dist/modules/catalog/components/PriceKindSettings.js +12 -5
  70. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  71. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  72. package/dist/modules/catalog/components/products/ProductMediaManager.js.map +2 -2
  73. package/dist/modules/catalog/components/products/ProductsDataTable.js +5 -3
  74. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  75. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  76. package/dist/modules/catalog/components/products/variantForm.js +2 -1
  77. package/dist/modules/catalog/components/products/variantForm.js.map +2 -2
  78. package/dist/modules/communication_channels/api/post/test-seed/route.js +23 -2
  79. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
  80. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +5 -0
  81. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
  82. package/dist/modules/communication_channels/commands/set-primary-channel.js +2 -1
  83. package/dist/modules/communication_channels/commands/set-primary-channel.js.map +2 -2
  84. package/dist/modules/currencies/backend/currencies/[id]/page.js +6 -3
  85. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  86. package/dist/modules/currencies/backend/currencies/page.js +18 -11
  87. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  88. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +1 -0
  89. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +2 -2
  90. package/dist/modules/currencies/backend/exchange-rates/page.js +10 -6
  91. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  92. package/dist/modules/currencies/commands/currencies.js +7 -5
  93. package/dist/modules/currencies/commands/currencies.js.map +2 -2
  94. package/dist/modules/currencies/components/CurrencyFetchingConfig.js +26 -19
  95. package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
  96. package/dist/modules/customer_accounts/api/admin/roles/[id].js +28 -5
  97. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
  98. package/dist/modules/customer_accounts/api/admin/roles.js +4 -2
  99. package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
  100. package/dist/modules/customer_accounts/api/admin/users/[id].js +28 -5
  101. package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
  102. package/dist/modules/customer_accounts/api/admin/users.js +2 -0
  103. package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
  104. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +16 -8
  105. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
  106. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +8 -4
  107. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  108. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +8 -4
  109. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +2 -2
  110. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +29 -18
  111. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  112. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +18 -11
  113. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  114. package/dist/modules/customers/api/companies/route.js +13 -2
  115. package/dist/modules/customers/api/companies/route.js.map +2 -2
  116. package/dist/modules/customers/api/deals/route.js +2 -0
  117. package/dist/modules/customers/api/deals/route.js.map +2 -2
  118. package/dist/modules/customers/api/people/route.js +11 -2
  119. package/dist/modules/customers/api/people/route.js.map +2 -2
  120. package/dist/modules/customers/api/todos/route.js +1 -0
  121. package/dist/modules/customers/api/todos/route.js.map +2 -2
  122. package/dist/modules/customers/backend/config/customers/deals/page.js.map +2 -2
  123. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +34 -21
  124. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
  125. package/dist/modules/customers/backend/customers/companies/[id]/page.js +45 -27
  126. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  127. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  128. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +22 -5
  129. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  130. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js +30 -8
  131. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js.map +2 -2
  132. package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -0
  133. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  134. package/dist/modules/customers/backend/customers/deals/page.js +16 -6
  135. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  136. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +62 -39
  137. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  138. package/dist/modules/customers/backend/customers/people/[id]/page.js +41 -26
  139. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  140. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  141. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +50 -23
  142. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  143. package/dist/modules/customers/commands/addresses.js +16 -14
  144. package/dist/modules/customers/commands/addresses.js.map +2 -2
  145. package/dist/modules/customers/commands/companies.js +1 -1
  146. package/dist/modules/customers/commands/companies.js.map +2 -2
  147. package/dist/modules/customers/commands/interactions.js +41 -4
  148. package/dist/modules/customers/commands/interactions.js.map +2 -2
  149. package/dist/modules/customers/commands/people.js +1 -1
  150. package/dist/modules/customers/commands/people.js.map +2 -2
  151. package/dist/modules/customers/commands/personCompanyLinks.js +8 -5
  152. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  153. package/dist/modules/customers/commands/pipeline-stages.js +13 -11
  154. package/dist/modules/customers/commands/pipeline-stages.js.map +3 -3
  155. package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
  156. package/dist/modules/customers/components/DictionarySettings.js +20 -13
  157. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  158. package/dist/modules/customers/components/DictionarySortSettings.js +4 -0
  159. package/dist/modules/customers/components/DictionarySortSettings.js.map +2 -2
  160. package/dist/modules/customers/components/PipelineSettings.js +38 -23
  161. package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
  162. package/dist/modules/customers/components/detail/ActivityTimeline.js +1 -1
  163. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
  164. package/dist/modules/customers/components/detail/AddressesSection.js +4 -0
  165. package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
  166. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +28 -22
  167. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  168. package/dist/modules/customers/components/detail/DealsSection.js +36 -24
  169. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  170. package/dist/modules/customers/components/detail/EmailCardActions.js +5 -0
  171. package/dist/modules/customers/components/detail/EmailCardActions.js.map +2 -2
  172. package/dist/modules/customers/components/detail/EntityTagsDialog.js +7 -0
  173. package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
  174. package/dist/modules/customers/components/detail/ManageTagsDialog.js +34 -22
  175. package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
  176. package/dist/modules/customers/components/detail/PersonCompaniesSection.js +41 -29
  177. package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
  178. package/dist/modules/customers/components/detail/RoleAssignmentRow.js +14 -8
  179. package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
  180. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +14 -6
  181. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  182. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js +29 -13
  183. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js.map +2 -2
  184. package/dist/modules/customers/components/detail/hooks/useInteractions.js +77 -35
  185. package/dist/modules/customers/components/detail/hooks/useInteractions.js.map +2 -2
  186. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +25 -17
  187. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
  188. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  189. package/dist/modules/customers/components/formConfig.js.map +2 -2
  190. package/dist/modules/customers/data/guards.js +66 -0
  191. package/dist/modules/customers/data/guards.js.map +7 -0
  192. package/dist/modules/customers/di.js +37 -0
  193. package/dist/modules/customers/di.js.map +2 -2
  194. package/dist/modules/customers/lib/todoCompatibility.js +11 -0
  195. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  196. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
  197. package/dist/modules/data_sync/api/options.js +4 -4
  198. package/dist/modules/data_sync/api/options.js.map +2 -2
  199. package/dist/modules/data_sync/api/schedules/route.js +9 -1
  200. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  201. package/dist/modules/data_sync/backend/data-sync/page.js +17 -8
  202. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  203. package/dist/modules/data_sync/components/IntegrationScheduleTab.js +43 -22
  204. package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
  205. package/dist/modules/data_sync/lib/sync-schedule-service.js +9 -0
  206. package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
  207. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +8 -1
  208. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  209. package/dist/modules/dictionaries/api/[dictionaryId]/route.js +17 -1
  210. package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
  211. package/dist/modules/dictionaries/components/DictionariesManager.js +31 -10
  212. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  213. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +28 -15
  214. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
  215. package/dist/modules/directory/api/organizations/route.js +3 -0
  216. package/dist/modules/directory/api/organizations/route.js.map +2 -2
  217. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +2 -0
  218. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
  219. package/dist/modules/directory/backend/directory/organizations/page.js +9 -5
  220. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  221. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +7 -3
  222. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
  223. package/dist/modules/directory/backend/directory/tenants/page.js +8 -4
  224. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  225. package/dist/modules/directory/commands/organizations.js +7 -2
  226. package/dist/modules/directory/commands/organizations.js.map +2 -2
  227. package/dist/modules/entities/api/records.js +66 -0
  228. package/dist/modules/entities/api/records.js.map +2 -2
  229. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +1 -0
  230. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
  231. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +8 -4
  232. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  233. package/dist/modules/entities/lib/helpers.js +17 -0
  234. package/dist/modules/entities/lib/helpers.js.map +2 -2
  235. package/dist/modules/feature_toggles/api/global/[id]/override/route.js +2 -1
  236. package/dist/modules/feature_toggles/api/global/[id]/override/route.js.map +2 -2
  237. package/dist/modules/feature_toggles/api/overrides/route.js +15 -0
  238. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  239. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js +15 -14
  240. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js.map +2 -2
  241. package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js +20 -12
  242. package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js.map +2 -2
  243. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +6 -2
  244. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  245. package/dist/modules/feature_toggles/components/formConfig.js +2 -1
  246. package/dist/modules/feature_toggles/components/formConfig.js.map +2 -2
  247. package/dist/modules/feature_toggles/components/overrideFormConfig.js +5 -1
  248. package/dist/modules/feature_toggles/components/overrideFormConfig.js.map +2 -2
  249. package/dist/modules/feature_toggles/data/validators.js +7 -4
  250. package/dist/modules/feature_toggles/data/validators.js.map +2 -2
  251. package/dist/modules/inbox_ops/api/settings/route.js +17 -2
  252. package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
  253. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +13 -8
  254. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
  255. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +9 -4
  256. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
  257. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +18 -11
  258. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  259. package/dist/modules/integrations/backend/integrations/page.js +12 -8
  260. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  261. package/dist/modules/messages/commands/messages.js +13 -10
  262. package/dist/modules/messages/commands/messages.js.map +2 -2
  263. package/dist/modules/perspectives/api/[tableId]/route.js +39 -30
  264. package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
  265. package/dist/modules/perspectives/services/perspectiveService.js +7 -0
  266. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  267. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +6 -14
  268. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +3 -3
  269. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +4 -2
  270. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  271. package/dist/modules/planner/components/AvailabilityRuleSetForm.js +2 -0
  272. package/dist/modules/planner/components/AvailabilityRuleSetForm.js.map +2 -2
  273. package/dist/modules/planner/components/AvailabilityRulesEditor.js +36 -11
  274. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  275. package/dist/modules/planner/components/AvailabilitySchedule.js +9 -5
  276. package/dist/modules/planner/components/AvailabilitySchedule.js.map +2 -2
  277. package/dist/modules/query_index/lib/engine.js +19 -0
  278. package/dist/modules/query_index/lib/engine.js.map +2 -2
  279. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +1 -0
  280. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
  281. package/dist/modules/resources/backend/resources/resource-types/page.js +4 -2
  282. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  283. package/dist/modules/resources/backend/resources/resources/[id]/page.js +14 -3
  284. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  285. package/dist/modules/resources/backend/resources/resources/page.js +8 -4
  286. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  287. package/dist/modules/resources/components/ResourceCrudForm.js +2 -0
  288. package/dist/modules/resources/components/ResourceCrudForm.js.map +2 -2
  289. package/dist/modules/resources/components/ResourceTypeCrudForm.js +1 -0
  290. package/dist/modules/resources/components/ResourceTypeCrudForm.js.map +2 -2
  291. package/dist/modules/sales/api/documents/factory.js +7 -2
  292. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  293. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +3 -1
  294. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  295. package/dist/modules/sales/backend/sales/channels/offers/page.js +13 -4
  296. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  297. package/dist/modules/sales/backend/sales/channels/page.js +16 -4
  298. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  299. package/dist/modules/sales/backend/sales/documents/[id]/page.js +68 -22
  300. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  301. package/dist/modules/sales/backend/sales/documents/create/page.js.map +2 -2
  302. package/dist/modules/sales/commands/documentAddresses.js +181 -2
  303. package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
  304. package/dist/modules/sales/commands/documents.js +29 -1
  305. package/dist/modules/sales/commands/documents.js.map +2 -2
  306. package/dist/modules/sales/commands/returns.js +12 -2
  307. package/dist/modules/sales/commands/returns.js.map +2 -2
  308. package/dist/modules/sales/commands/shared.js +15 -0
  309. package/dist/modules/sales/commands/shared.js.map +2 -2
  310. package/dist/modules/sales/commands/shipments.js +4 -1
  311. package/dist/modules/sales/commands/shipments.js.map +2 -2
  312. package/dist/modules/sales/components/AdjustmentKindSettings.js +19 -11
  313. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  314. package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
  315. package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
  316. package/dist/modules/sales/components/PaymentMethodsSettings.js +12 -4
  317. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  318. package/dist/modules/sales/components/ShippingMethodsSettings.js +12 -4
  319. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  320. package/dist/modules/sales/components/StatusSettings.js +18 -11
  321. package/dist/modules/sales/components/StatusSettings.js.map +2 -2
  322. package/dist/modules/sales/components/TaxRatesSettings.js +12 -4
  323. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  324. package/dist/modules/sales/components/channels/ChannelOfferForm.js +47 -16
  325. package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
  326. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +8 -4
  327. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  328. package/dist/modules/sales/components/documents/AddressesSection.js +44 -25
  329. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  330. package/dist/modules/sales/components/documents/AdjustmentsSection.js +43 -23
  331. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  332. package/dist/modules/sales/components/documents/ItemsSection.js +22 -13
  333. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  334. package/dist/modules/sales/components/documents/LineItemDialog.js +23 -10
  335. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  336. package/dist/modules/sales/components/documents/PaymentDialog.js +29 -14
  337. package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
  338. package/dist/modules/sales/components/documents/PaymentsSection.js +20 -10
  339. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  340. package/dist/modules/sales/components/documents/ReturnDialog.js +26 -17
  341. package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
  342. package/dist/modules/sales/components/documents/ReturnsSection.js +3 -1
  343. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  344. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +10 -5
  345. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  346. package/dist/modules/sales/components/documents/ShipmentDialog.js +21 -7
  347. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  348. package/dist/modules/sales/components/documents/ShipmentsSection.js +19 -10
  349. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  350. package/dist/modules/sales/components/documents/optimisticLock.js +27 -0
  351. package/dist/modules/sales/components/documents/optimisticLock.js.map +7 -0
  352. package/dist/modules/sales/di.js +18 -0
  353. package/dist/modules/sales/di.js.map +2 -2
  354. package/dist/modules/staff/api/job-histories.js +11 -2
  355. package/dist/modules/staff/api/job-histories.js.map +2 -2
  356. package/dist/modules/staff/api/timesheets/time-entries/route.js +11 -4
  357. package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
  358. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +13 -8
  359. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  360. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +2 -1
  361. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  362. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +7 -4
  363. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  364. package/dist/modules/staff/backend/staff/team-members/page.js +4 -2
  365. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  366. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +1 -0
  367. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  368. package/dist/modules/staff/backend/staff/team-roles/page.js +4 -2
  369. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  370. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +5 -2
  371. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  372. package/dist/modules/staff/backend/staff/teams/page.js +12 -3
  373. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  374. package/dist/modules/staff/backend/staff/timesheets/page.js +4 -1
  375. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  376. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +2 -2
  377. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +12 -3
  378. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  379. package/dist/modules/staff/commands/job-histories.js +40 -3
  380. package/dist/modules/staff/commands/job-histories.js.map +2 -2
  381. package/dist/modules/staff/components/LeaveRequestForm.js +1 -0
  382. package/dist/modules/staff/components/LeaveRequestForm.js.map +2 -2
  383. package/dist/modules/staff/components/TeamForm.js +1 -0
  384. package/dist/modules/staff/components/TeamForm.js.map +2 -2
  385. package/dist/modules/staff/components/TeamMemberForm.js +1 -0
  386. package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
  387. package/dist/modules/staff/components/TeamRoleForm.js +1 -0
  388. package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
  389. package/dist/modules/staff/components/detail/JobHistorySection.js +20 -7
  390. package/dist/modules/staff/components/detail/JobHistorySection.js.map +2 -2
  391. package/dist/modules/staff/data/validators.js +7 -1
  392. package/dist/modules/staff/data/validators.js.map +2 -2
  393. package/dist/modules/staff/lib/leaveRequestHelpers.js +2 -1
  394. package/dist/modules/staff/lib/leaveRequestHelpers.js.map +2 -2
  395. package/dist/modules/translations/components/TranslationManager.js +12 -8
  396. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  397. package/dist/modules/workflows/api/definitions/[id]/route.js +106 -0
  398. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  399. package/dist/modules/workflows/backend/definitions/[id]/page.js +11 -3
  400. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  401. package/dist/modules/workflows/backend/definitions/page.js +19 -8
  402. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  403. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +29 -16
  404. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  405. package/dist/modules/workflows/components/formConfig.js +4 -1
  406. package/dist/modules/workflows/components/formConfig.js.map +2 -2
  407. package/dist/modules/workflows/di.js +12 -0
  408. package/dist/modules/workflows/di.js.map +2 -2
  409. package/generated/entities/role/index.ts +1 -0
  410. package/generated/entities/user/index.ts +1 -0
  411. package/generated/entity-fields-registry.ts +2 -0
  412. package/jest.setup.ts +17 -0
  413. package/package.json +8 -7
  414. package/src/helpers/integration/communicationChannelsFixtures.ts +6 -0
  415. package/src/helpers/integration/dbFixtures.ts +1 -1
  416. package/src/helpers/integration/optimisticLockUi.ts +172 -0
  417. package/src/helpers/integration/salesFixtures.ts +29 -0
  418. package/src/modules/api_keys/backend/api-keys/page.tsx +10 -5
  419. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +19 -9
  420. package/src/modules/auth/api/roles/acl/route.ts +37 -11
  421. package/src/modules/auth/api/roles/route.ts +2 -0
  422. package/src/modules/auth/api/sidebar/preferences/route.ts +73 -0
  423. package/src/modules/auth/api/users/acl/route.ts +46 -18
  424. package/src/modules/auth/api/users/route.ts +2 -0
  425. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +29 -4
  426. package/src/modules/auth/backend/roles/page.tsx +9 -4
  427. package/src/modules/auth/backend/users/[id]/edit/page.tsx +37 -4
  428. package/src/modules/auth/backend/users/page.tsx +7 -2
  429. package/src/modules/auth/components/AclEditor.tsx +10 -1
  430. package/src/modules/auth/data/entities.ts +7 -1
  431. package/src/modules/auth/services/sidebarPreferencesService.ts +38 -4
  432. package/src/modules/business_rules/api/rules/route.ts +30 -0
  433. package/src/modules/business_rules/api/sets/route.ts +30 -0
  434. package/src/modules/business_rules/backend/rules/[id]/page.tsx +16 -4
  435. package/src/modules/business_rules/backend/rules/page.tsx +20 -11
  436. package/src/modules/business_rules/backend/sets/[id]/page.tsx +16 -4
  437. package/src/modules/business_rules/backend/sets/page.tsx +20 -11
  438. package/src/modules/catalog/api/categories/route.ts +3 -0
  439. package/src/modules/catalog/api/products/route.ts +4 -0
  440. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +5 -0
  441. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +112 -35
  442. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +56 -7
  443. package/src/modules/catalog/backend/catalog/products/optionSchemaClient.ts +2 -0
  444. package/src/modules/catalog/commands/variants.ts +32 -32
  445. package/src/modules/catalog/components/PriceKindSettings.tsx +20 -7
  446. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +1 -0
  447. package/src/modules/catalog/components/products/ProductMediaManager.tsx +2 -0
  448. package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -4
  449. package/src/modules/catalog/components/products/productForm.ts +3 -0
  450. package/src/modules/catalog/components/products/variantForm.ts +9 -0
  451. package/src/modules/communication_channels/api/post/test-seed/route.ts +28 -1
  452. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +5 -0
  453. package/src/modules/communication_channels/commands/set-primary-channel.ts +10 -7
  454. package/src/modules/currencies/backend/currencies/[id]/page.tsx +13 -6
  455. package/src/modules/currencies/backend/currencies/page.tsx +18 -11
  456. package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +3 -0
  457. package/src/modules/currencies/backend/exchange-rates/page.tsx +10 -6
  458. package/src/modules/currencies/commands/currencies.ts +10 -5
  459. package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +31 -21
  460. package/src/modules/customer_accounts/api/admin/roles/[id].ts +35 -5
  461. package/src/modules/customer_accounts/api/admin/roles.ts +2 -0
  462. package/src/modules/customer_accounts/api/admin/users/[id].ts +38 -5
  463. package/src/modules/customer_accounts/api/admin/users.ts +2 -0
  464. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -20
  465. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +9 -4
  466. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +11 -4
  467. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +28 -17
  468. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +19 -11
  469. package/src/modules/customers/AGENTS.md +2 -2
  470. package/src/modules/customers/api/companies/route.ts +14 -1
  471. package/src/modules/customers/api/deals/route.ts +3 -0
  472. package/src/modules/customers/api/people/route.ts +12 -1
  473. package/src/modules/customers/api/todos/route.ts +1 -0
  474. package/src/modules/customers/backend/config/customers/deals/page.tsx +1 -0
  475. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +36 -21
  476. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +52 -27
  477. package/src/modules/customers/backend/customers/companies/page.tsx +2 -0
  478. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +27 -5
  479. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.ts +39 -7
  480. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +1 -0
  481. package/src/modules/customers/backend/customers/deals/page.tsx +18 -6
  482. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +64 -39
  483. package/src/modules/customers/backend/customers/people/[id]/page.tsx +46 -26
  484. package/src/modules/customers/backend/customers/people/page.tsx +2 -0
  485. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +84 -24
  486. package/src/modules/customers/commands/addresses.ts +16 -14
  487. package/src/modules/customers/commands/companies.ts +3 -1
  488. package/src/modules/customers/commands/interactions.ts +50 -4
  489. package/src/modules/customers/commands/people.ts +2 -1
  490. package/src/modules/customers/commands/personCompanyLinks.ts +8 -5
  491. package/src/modules/customers/commands/pipeline-stages.ts +16 -16
  492. package/src/modules/customers/components/AddressFormatSettings.tsx +1 -0
  493. package/src/modules/customers/components/DictionarySettings.tsx +18 -13
  494. package/src/modules/customers/components/DictionarySortSettings.tsx +4 -0
  495. package/src/modules/customers/components/PipelineSettings.tsx +42 -21
  496. package/src/modules/customers/components/detail/ActivityTimeline.tsx +3 -3
  497. package/src/modules/customers/components/detail/AddressesSection.tsx +4 -0
  498. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +2 -0
  499. package/src/modules/customers/components/detail/DealsSection.tsx +4 -0
  500. package/src/modules/customers/components/detail/EmailCardActions.tsx +5 -0
  501. package/src/modules/customers/components/detail/EntityTagsDialog.tsx +7 -0
  502. package/src/modules/customers/components/detail/ManageTagsDialog.tsx +4 -0
  503. package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +4 -0
  504. package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +2 -0
  505. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +23 -7
  506. package/src/modules/customers/components/detail/hooks/useInteractionMutations.ts +25 -15
  507. package/src/modules/customers/components/detail/hooks/useInteractions.ts +76 -35
  508. package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +30 -17
  509. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +2 -0
  510. package/src/modules/customers/components/detail/types.ts +1 -0
  511. package/src/modules/customers/components/formConfig.tsx +2 -0
  512. package/src/modules/customers/data/guards.ts +67 -0
  513. package/src/modules/customers/di.ts +66 -0
  514. package/src/modules/customers/i18n/de.json +2 -0
  515. package/src/modules/customers/i18n/en.json +2 -0
  516. package/src/modules/customers/i18n/es.json +2 -0
  517. package/src/modules/customers/i18n/pl.json +2 -0
  518. package/src/modules/customers/lib/todoCompatibility.ts +14 -0
  519. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +2 -0
  520. package/src/modules/data_sync/api/options.ts +7 -4
  521. package/src/modules/data_sync/api/schedules/route.ts +9 -1
  522. package/src/modules/data_sync/backend/data-sync/page.tsx +18 -5
  523. package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +46 -19
  524. package/src/modules/data_sync/lib/sync-schedule-service.ts +11 -0
  525. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +8 -1
  526. package/src/modules/dictionaries/api/[dictionaryId]/route.ts +23 -0
  527. package/src/modules/dictionaries/components/DictionariesManager.tsx +32 -9
  528. package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +30 -14
  529. package/src/modules/dictionaries/i18n/de.json +1 -0
  530. package/src/modules/dictionaries/i18n/en.json +1 -0
  531. package/src/modules/dictionaries/i18n/es.json +1 -0
  532. package/src/modules/dictionaries/i18n/pl.json +1 -0
  533. package/src/modules/directory/api/organizations/route.ts +3 -0
  534. package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +8 -0
  535. package/src/modules/directory/backend/directory/organizations/page.tsx +10 -5
  536. package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +16 -5
  537. package/src/modules/directory/backend/directory/tenants/page.tsx +8 -4
  538. package/src/modules/directory/commands/organizations.ts +7 -4
  539. package/src/modules/entities/api/records.ts +99 -0
  540. package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +7 -0
  541. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +8 -4
  542. package/src/modules/entities/lib/helpers.ts +17 -0
  543. package/src/modules/feature_toggles/api/global/[id]/override/route.ts +1 -0
  544. package/src/modules/feature_toggles/api/overrides/route.ts +19 -0
  545. package/src/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.tsx +19 -13
  546. package/src/modules/feature_toggles/components/FeatureToggleOverrideCard.tsx +22 -12
  547. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +7 -2
  548. package/src/modules/feature_toggles/components/formConfig.tsx +2 -1
  549. package/src/modules/feature_toggles/components/overrideFormConfig.tsx +10 -1
  550. package/src/modules/feature_toggles/data/validators.ts +11 -3
  551. package/src/modules/inbox_ops/api/settings/route.ts +18 -0
  552. package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +15 -10
  553. package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +9 -4
  554. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +20 -11
  555. package/src/modules/integrations/backend/integrations/page.tsx +13 -8
  556. package/src/modules/messages/commands/messages.ts +27 -15
  557. package/src/modules/perspectives/api/[tableId]/route.ts +11 -2
  558. package/src/modules/perspectives/services/perspectiveService.ts +13 -1
  559. package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +16 -14
  560. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +6 -3
  561. package/src/modules/planner/components/AvailabilityRuleSetForm.tsx +3 -0
  562. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +58 -15
  563. package/src/modules/planner/components/AvailabilitySchedule.tsx +22 -7
  564. package/src/modules/query_index/lib/engine.ts +34 -0
  565. package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +7 -1
  566. package/src/modules/resources/backend/resources/resource-types/page.tsx +6 -3
  567. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +23 -3
  568. package/src/modules/resources/backend/resources/resources/page.tsx +15 -4
  569. package/src/modules/resources/components/ResourceCrudForm.tsx +3 -0
  570. package/src/modules/resources/components/ResourceTypeCrudForm.tsx +2 -0
  571. package/src/modules/sales/api/documents/factory.ts +13 -1
  572. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +6 -0
  573. package/src/modules/sales/backend/sales/channels/offers/page.tsx +10 -4
  574. package/src/modules/sales/backend/sales/channels/page.tsx +19 -4
  575. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +73 -20
  576. package/src/modules/sales/backend/sales/documents/create/page.tsx +2 -0
  577. package/src/modules/sales/commands/documentAddresses.ts +226 -4
  578. package/src/modules/sales/commands/documents.ts +28 -0
  579. package/src/modules/sales/commands/returns.ts +12 -3
  580. package/src/modules/sales/commands/shared.ts +36 -0
  581. package/src/modules/sales/commands/shipments.ts +17 -1
  582. package/src/modules/sales/components/AdjustmentKindSettings.tsx +20 -11
  583. package/src/modules/sales/components/DocumentNumberSettings.tsx +1 -0
  584. package/src/modules/sales/components/OrderEditingSettings.tsx +1 -0
  585. package/src/modules/sales/components/PaymentMethodsSettings.tsx +12 -4
  586. package/src/modules/sales/components/ShippingMethodsSettings.tsx +12 -4
  587. package/src/modules/sales/components/StatusSettings.tsx +20 -11
  588. package/src/modules/sales/components/TaxRatesSettings.tsx +12 -5
  589. package/src/modules/sales/components/channels/ChannelOfferForm.tsx +67 -14
  590. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +7 -4
  591. package/src/modules/sales/components/documents/AddressesSection.tsx +35 -25
  592. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +50 -25
  593. package/src/modules/sales/components/documents/ItemsSection.tsx +24 -13
  594. package/src/modules/sales/components/documents/LineItemDialog.tsx +26 -9
  595. package/src/modules/sales/components/documents/PaymentDialog.tsx +33 -14
  596. package/src/modules/sales/components/documents/PaymentsSection.tsx +22 -10
  597. package/src/modules/sales/components/documents/ReturnDialog.tsx +28 -17
  598. package/src/modules/sales/components/documents/ReturnsSection.tsx +4 -1
  599. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +11 -4
  600. package/src/modules/sales/components/documents/ShipmentDialog.tsx +23 -8
  601. package/src/modules/sales/components/documents/ShipmentsSection.tsx +20 -10
  602. package/src/modules/sales/components/documents/optimisticLock.ts +34 -0
  603. package/src/modules/sales/components/documents/shipmentTypes.ts +1 -0
  604. package/src/modules/sales/di.ts +35 -0
  605. package/src/modules/sales/i18n/de.json +3 -0
  606. package/src/modules/sales/i18n/en.json +3 -0
  607. package/src/modules/sales/i18n/es.json +3 -0
  608. package/src/modules/sales/i18n/pl.json +3 -0
  609. package/src/modules/staff/api/job-histories.ts +12 -2
  610. package/src/modules/staff/api/timesheets/time-entries/route.ts +16 -4
  611. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +12 -7
  612. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +2 -0
  613. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +16 -5
  614. package/src/modules/staff/backend/staff/team-members/page.tsx +6 -2
  615. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +8 -0
  616. package/src/modules/staff/backend/staff/team-roles/page.tsx +6 -2
  617. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +13 -3
  618. package/src/modules/staff/backend/staff/teams/page.tsx +9 -3
  619. package/src/modules/staff/backend/staff/timesheets/page.tsx +10 -1
  620. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -0
  621. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +9 -3
  622. package/src/modules/staff/commands/job-histories.ts +42 -3
  623. package/src/modules/staff/components/LeaveRequestForm.tsx +2 -0
  624. package/src/modules/staff/components/TeamForm.tsx +2 -0
  625. package/src/modules/staff/components/TeamMemberForm.tsx +2 -0
  626. package/src/modules/staff/components/TeamRoleForm.tsx +2 -0
  627. package/src/modules/staff/components/detail/JobHistorySection.tsx +28 -6
  628. package/src/modules/staff/data/validators.ts +6 -0
  629. package/src/modules/staff/i18n/de.json +1 -0
  630. package/src/modules/staff/i18n/en.json +1 -0
  631. package/src/modules/staff/i18n/es.json +1 -0
  632. package/src/modules/staff/i18n/pl.json +1 -0
  633. package/src/modules/staff/lib/leaveRequestHelpers.ts +4 -0
  634. package/src/modules/translations/components/TranslationManager.tsx +13 -8
  635. package/src/modules/workflows/api/definitions/[id]/route.ts +112 -0
  636. package/src/modules/workflows/backend/definitions/[id]/page.tsx +20 -4
  637. package/src/modules/workflows/backend/definitions/page.tsx +20 -9
  638. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +29 -16
  639. package/src/modules/workflows/components/formConfig.tsx +5 -0
  640. package/src/modules/workflows/di.ts +20 -0
  641. package/src/modules/workflows/i18n/de.json +1 -0
  642. package/src/modules/workflows/i18n/en.json +1 -0
  643. package/src/modules/workflows/i18n/es.json +1 -0
  644. package/src/modules/workflows/i18n/pl.json +1 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/PersonCompaniesSection.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ArrowLeft, ArrowRight, Building2, Link2 } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { CompanyCard, type EnrichedCompanyData } from './CompanyCard'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { LinkEntityDialog, type LinkEntityOption } from '../linking/LinkEntityDialog'\nimport { createCompanyLinkAdapter } from '../linking/adapters/companyAdapter'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype LinkedCompanySummary = {\n id: string\n displayName: string\n isPrimary: boolean\n}\n\ntype PersonCompaniesSectionProps = {\n personId: string\n personName: string\n initialLinkedCompanies?: LinkedCompanySummary[]\n onChanged?: () => Promise<void> | void\n runGuardedMutation?: GuardedMutationRunner\n}\n\nconst LINKED_PAGE_SIZE = 20\n\nfunction sameIdSet(left: string[], right: string[]): boolean {\n if (left.length !== right.length) return false\n const rightSet = new Set(right)\n return left.every((value) => rightSet.has(value))\n}\n\nfunction Pagination({\n page,\n totalPages,\n onPageChange,\n}: {\n page: number\n totalPages: number\n onPageChange: (page: number) => void\n}) {\n if (totalPages <= 1) return null\n return (\n <div className=\"flex items-center justify-between border-t border-border/60 pt-3 text-sm text-muted-foreground\">\n <span>\n Page {page} of {totalPages}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(Math.max(1, page - 1))}\n disabled={page <= 1}\n >\n <ArrowLeft className=\"mr-1.5 size-3.5\" />\n Previous\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n disabled={page >= totalPages}\n >\n Next\n <ArrowRight className=\"ml-1.5 size-3.5\" />\n </Button>\n </div>\n </div>\n )\n}\n\nexport function PersonCompaniesSection({\n personId,\n personName: _personName,\n initialLinkedCompanies = [],\n onChanged,\n runGuardedMutation,\n}: PersonCompaniesSectionProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [items, setItems] = React.useState<EnrichedCompanyData[]>([])\n const [loading, setLoading] = React.useState(true)\n const [unlinkingId, setUnlinkingId] = React.useState<string | null>(null)\n const [search, setSearch] = React.useState('')\n const [sort, setSort] = React.useState<'name-asc' | 'name-desc' | 'recent'>('name-asc')\n const [page, setPage] = React.useState(1)\n const [totalPages, setTotalPages] = React.useState(1)\n const [linkedCompanies, setLinkedCompanies] = React.useState<LinkedCompanySummary[]>(\n initialLinkedCompanies,\n )\n const [dialogOpen, setDialogOpen] = React.useState(false)\n\n const { data: statusDict } = useCustomerDictionary('statuses')\n const { data: lifecycleDict } = useCustomerDictionary('lifecycle-stages')\n const { data: temperatureDict } = useCustomerDictionary('temperature')\n const { data: renewalQuarterDict } = useCustomerDictionary('renewal-quarters')\n const { data: roleDict } = useCustomerDictionary('person-company-roles')\n\n React.useEffect(() => {\n setLinkedCompanies(initialLinkedCompanies)\n }, [initialLinkedCompanies])\n\n const runWriteMutation = React.useCallback(\n async <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n ): Promise<T> => {\n if (!runGuardedMutation) {\n return operation()\n }\n return runGuardedMutation(operation, mutationPayload)\n },\n [runGuardedMutation],\n )\n\n const loadData = React.useCallback(\n async (options?: { showLoading?: boolean }) => {\n const showLoading = options?.showLoading ?? true\n if (showLoading) setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(LINKED_PAGE_SIZE),\n sort,\n })\n if (search.trim().length > 0) {\n params.set('search', search.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: EnrichedCompanyData[]\n totalPages?: number\n }>(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/enriched?${params.toString()}`,\n { cache: 'no-store' },\n )\n const nextItems = Array.isArray(payload?.items) ? payload.items : []\n setItems(nextItems)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t('customers.people.detail.companies.loadError', 'Failed to load companies.')\n flash(message, 'error')\n setItems([])\n setTotalPages(1)\n } finally {\n if (showLoading) setLoading(false)\n }\n },\n [page, personId, search, sort, t],\n )\n\n React.useEffect(() => {\n void loadData()\n }, [loadData])\n\n React.useEffect(() => {\n setPage(1)\n }, [search, sort])\n\n const linkedIds = React.useMemo(\n () => linkedCompanies.map((entry) => entry.id),\n [linkedCompanies],\n )\n const linkedPrimaryId = React.useMemo(\n () =>\n linkedCompanies.find((entry) => entry.isPrimary)?.id ??\n linkedCompanies[0]?.id ??\n null,\n [linkedCompanies],\n )\n\n const companyLinkAdapter = React.useMemo(\n () =>\n createCompanyLinkAdapter({\n dialogTitle: t('customers.linking.company.dialogTitle', 'Link company'),\n dialogSubtitle: _personName\n ? t('customers.linking.company.dialogSubtitleFor', 'Link an existing company to {{name}}', {\n name: _personName,\n })\n : t('customers.linking.company.dialogSubtitle', 'Link an existing company to this person'),\n sectionLabel: t('customers.linking.company.sectionLabel', 'MATCHING COMPANIES'),\n searchPlaceholder: t(\n 'customers.linking.company.searchPlaceholder',\n 'Search all companies\u2026',\n ),\n searchEmptyHint: t(\n 'customers.linking.company.searchEmpty',\n 'No matching companies found.',\n ),\n selectedEmptyHint: t(\n 'customers.linking.company.selectedEmpty',\n 'No companies selected.',\n ),\n confirmButtonLabel: t('customers.linking.company.confirmButton', 'Link company'),\n excludeLinkedPersonId: personId,\n }),\n [_personName, personId, t],\n )\n\n const handleLinkConfirm = React.useCallback(\n async ({\n addedIds,\n removedIds,\n nextSelectedIds,\n primaryId,\n optionsById,\n }: {\n addedIds: string[]\n removedIds: string[]\n nextSelectedIds: string[]\n primaryId?: string | null\n optionsById: Record<string, LinkEntityOption>\n }) => {\n const currentPrimaryId = linkedPrimaryId\n const nextPrimaryId = nextSelectedIds.length\n ? primaryId && nextSelectedIds.includes(primaryId)\n ? primaryId\n : nextSelectedIds[0]\n : null\n\n try {\n for (const companyId of removedIds) {\n await runWriteMutation(\n () =>\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n ),\n { companyId, personId, operation: 'removePersonCompanyLink' },\n )\n }\n\n for (const companyId of addedIds) {\n await runWriteMutation(\n () =>\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n companyId,\n isPrimary: nextPrimaryId === companyId,\n }),\n },\n ),\n { companyId, personId, operation: 'addPersonCompanyLink' },\n )\n }\n\n if (\n nextPrimaryId &&\n nextPrimaryId !== currentPrimaryId &&\n !addedIds.includes(nextPrimaryId)\n ) {\n await runWriteMutation(\n () =>\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(nextPrimaryId)}`,\n {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isPrimary: true }),\n },\n ),\n { companyId: nextPrimaryId, personId, operation: 'setPrimaryPersonCompanyLink' },\n )\n }\n\n const nextLinkedCompanies: LinkedCompanySummary[] = nextSelectedIds.map((id) => {\n const option = optionsById[id]\n const fromCurrent = linkedCompanies.find((entry) => entry.id === id)\n return {\n id,\n displayName: option?.label ?? fromCurrent?.displayName ?? id,\n isPrimary: id === nextPrimaryId,\n }\n })\n setLinkedCompanies(nextLinkedCompanies)\n await loadData({ showLoading: false })\n await onChanged?.()\n flash(\n t('customers.people.detail.companies.manageSuccess', 'Linked companies updated.'),\n 'success',\n )\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t(\n 'customers.people.detail.companies.manageError',\n 'Failed to update linked companies.',\n )\n flash(message, 'error')\n throw error\n }\n },\n [linkedCompanies, linkedPrimaryId, loadData, onChanged, personId, runWriteMutation, t],\n )\n\n const handleUnlink = React.useCallback(\n async (companyId: string, displayName: string) => {\n if (!companyId || unlinkingId) return\n const confirmed = await confirm({\n title: t('customers.people.detail.companies.unlinkConfirmTitle', 'Unlink company'),\n description: t(\n 'customers.people.detail.companies.unlinkConfirm',\n 'Unlink {{company}} from {{person}}?',\n { company: displayName, person: _personName },\n ),\n confirmText: t('customers.people.detail.companies.unlinkAction', 'Unlink'),\n cancelText: t('customers.linking.actions.cancel', 'Cancel'),\n })\n if (!confirmed) return\n setUnlinkingId(companyId)\n try {\n await runWriteMutation(\n () =>\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n {\n errorMessage: t(\n 'customers.people.detail.companies.unlinkError',\n 'Failed to unlink company.',\n ),\n },\n ),\n { companyId, personId, operation: 'unlinkPersonCompanyLink' },\n )\n await loadData({ showLoading: false })\n await onChanged?.()\n flash(\n t('customers.people.detail.companies.unlinkSuccess', 'Company unlinked.'),\n 'success',\n )\n } catch (error) {\n const message = error instanceof Error\n ? error.message\n : t(\n 'customers.people.detail.companies.unlinkError',\n 'Failed to unlink company.',\n )\n flash(message, 'error')\n } finally {\n setUnlinkingId(null)\n }\n },\n [_personName, confirm, loadData, onChanged, personId, runWriteMutation, t, unlinkingId],\n )\n\n useAppEvent('customers.person_company_link.deleted', (event) => {\n const payload = event.payload as { personEntityId?: string | null } | null | undefined\n if (payload && payload.personEntityId === personId) {\n void loadData({ showLoading: false })\n }\n }, [personId, loadData])\n\n return (\n <>\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 rounded-2xl border border-border/70 bg-card px-4 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.people.detail.companies.manageTitle', 'Manage linked companies')}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n {t('customers.people.detail.companies.summary', '{{count}} linked companies', {\n count: linkedCompanies.length,\n })}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Input\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder={t(\n 'customers.people.detail.companies.searchPlaceholder',\n 'Search linked companies\u2026',\n )}\n className=\"sm:w-[260px]\"\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setDialogOpen(true)}\n >\n <Link2 className=\"mr-2 size-4\" />\n {t('customers.people.detail.companies.manageAction', 'Manage links')}\n </Button>\n </div>\n </div>\n\n <div className=\"flex items-center justify-end\">\n <select\n value={sort}\n onChange={(event) =>\n setSort(event.target.value as 'name-asc' | 'name-desc' | 'recent')\n }\n className=\"h-10 rounded-md border bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n >\n <option value=\"name-asc\">\n {t('customers.people.detail.companies.sortNameAsc', 'Sort: Name A-Z')}\n </option>\n <option value=\"name-desc\">\n {t('customers.people.detail.companies.sortNameDesc', 'Sort: Name Z-A')}\n </option>\n <option value=\"recent\">\n {t('customers.people.detail.companies.sortRecent', 'Sort: Recently active')}\n </option>\n </select>\n </div>\n\n {loading ? (\n <div className=\"space-y-4\">\n {[1, 2].map((idx) => (\n <div\n key={idx}\n className=\"h-[320px] animate-pulse rounded-2xl border border-border/60 bg-muted/30\"\n />\n ))}\n </div>\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Building2 className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={\n search.trim().length\n ? t(\n 'customers.people.detail.companies.noSearchResults',\n 'No linked companies match your search.',\n )\n : t(\n 'customers.people.detail.empty.companies',\n 'No company linked to this person.',\n )\n }\n />\n ) : (\n <>\n <div className=\"space-y-4\">\n {items.map((item) => (\n <CompanyCard\n key={item.companyId}\n data={item}\n personName={_personName}\n statusMap={statusDict?.map}\n lifecycleMap={lifecycleDict?.map}\n temperatureMap={temperatureDict?.map}\n renewalQuarterMap={renewalQuarterDict?.map}\n roleMap={roleDict?.map}\n onUnlink={() => handleUnlink(item.companyId, item.displayName)}\n unlinkLabel={t('customers.people.detail.companies.unlinkAction', 'Unlink')}\n unlinkDisabled={unlinkingId === item.companyId}\n />\n ))}\n </div>\n <Pagination page={page} totalPages={totalPages} onPageChange={setPage} />\n </>\n )}\n </div>\n\n <LinkEntityDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n adapter={companyLinkAdapter}\n initialSelectedIds={linkedIds}\n initialPrimaryId={linkedPrimaryId}\n primarySupported\n onConfirm={handleLinkConfirm}\n runGuardedMutation={runWriteMutation}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n\nexport default PersonCompaniesSection\n\nexport { sameIdSet }\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { ArrowLeft, ArrowRight, Building2, Link2 } from 'lucide-react'\nimport { EmptyState } from '@open-mercato/ui/primitives/empty-state'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Input } from '@open-mercato/ui/primitives/input'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { CompanyCard, type EnrichedCompanyData } from './CompanyCard'\nimport { useCustomerDictionary } from './hooks/useCustomerDictionary'\nimport { LinkEntityDialog, type LinkEntityOption } from '../linking/LinkEntityDialog'\nimport { createCompanyLinkAdapter } from '../linking/adapters/companyAdapter'\n\ntype GuardedMutationRunner = <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n) => Promise<T>\n\ntype LinkedCompanySummary = {\n id: string\n displayName: string\n isPrimary: boolean\n}\n\ntype PersonCompaniesSectionProps = {\n personId: string\n personName: string\n initialLinkedCompanies?: LinkedCompanySummary[]\n onChanged?: () => Promise<void> | void\n runGuardedMutation?: GuardedMutationRunner\n}\n\nconst LINKED_PAGE_SIZE = 20\n\nfunction sameIdSet(left: string[], right: string[]): boolean {\n if (left.length !== right.length) return false\n const rightSet = new Set(right)\n return left.every((value) => rightSet.has(value))\n}\n\nfunction Pagination({\n page,\n totalPages,\n onPageChange,\n}: {\n page: number\n totalPages: number\n onPageChange: (page: number) => void\n}) {\n if (totalPages <= 1) return null\n return (\n <div className=\"flex items-center justify-between border-t border-border/60 pt-3 text-sm text-muted-foreground\">\n <span>\n Page {page} of {totalPages}\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(Math.max(1, page - 1))}\n disabled={page <= 1}\n >\n <ArrowLeft className=\"mr-1.5 size-3.5\" />\n Previous\n </Button>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => onPageChange(Math.min(totalPages, page + 1))}\n disabled={page >= totalPages}\n >\n Next\n <ArrowRight className=\"ml-1.5 size-3.5\" />\n </Button>\n </div>\n </div>\n )\n}\n\nexport function PersonCompaniesSection({\n personId,\n personName: _personName,\n initialLinkedCompanies = [],\n onChanged,\n runGuardedMutation,\n}: PersonCompaniesSectionProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [items, setItems] = React.useState<EnrichedCompanyData[]>([])\n const [loading, setLoading] = React.useState(true)\n const [unlinkingId, setUnlinkingId] = React.useState<string | null>(null)\n const [search, setSearch] = React.useState('')\n const [sort, setSort] = React.useState<'name-asc' | 'name-desc' | 'recent'>('name-asc')\n const [page, setPage] = React.useState(1)\n const [totalPages, setTotalPages] = React.useState(1)\n const [linkedCompanies, setLinkedCompanies] = React.useState<LinkedCompanySummary[]>(\n initialLinkedCompanies,\n )\n const [dialogOpen, setDialogOpen] = React.useState(false)\n\n const { data: statusDict } = useCustomerDictionary('statuses')\n const { data: lifecycleDict } = useCustomerDictionary('lifecycle-stages')\n const { data: temperatureDict } = useCustomerDictionary('temperature')\n const { data: renewalQuarterDict } = useCustomerDictionary('renewal-quarters')\n const { data: roleDict } = useCustomerDictionary('person-company-roles')\n\n React.useEffect(() => {\n setLinkedCompanies(initialLinkedCompanies)\n }, [initialLinkedCompanies])\n\n const runWriteMutation = React.useCallback(\n async <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n ): Promise<T> => {\n if (!runGuardedMutation) {\n return operation()\n }\n return runGuardedMutation(operation, mutationPayload)\n },\n [runGuardedMutation],\n )\n\n const loadData = React.useCallback(\n async (options?: { showLoading?: boolean }) => {\n const showLoading = options?.showLoading ?? true\n if (showLoading) setLoading(true)\n try {\n const params = new URLSearchParams({\n page: String(page),\n pageSize: String(LINKED_PAGE_SIZE),\n sort,\n })\n if (search.trim().length > 0) {\n params.set('search', search.trim())\n }\n const payload = await readApiResultOrThrow<{\n items?: EnrichedCompanyData[]\n totalPages?: number\n }>(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/enriched?${params.toString()}`,\n { cache: 'no-store' },\n )\n const nextItems = Array.isArray(payload?.items) ? payload.items : []\n setItems(nextItems)\n setTotalPages(typeof payload?.totalPages === 'number' ? payload.totalPages : 1)\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t('customers.people.detail.companies.loadError', 'Failed to load companies.')\n flash(message, 'error')\n setItems([])\n setTotalPages(1)\n } finally {\n if (showLoading) setLoading(false)\n }\n },\n [page, personId, search, sort, t],\n )\n\n React.useEffect(() => {\n void loadData()\n }, [loadData])\n\n React.useEffect(() => {\n setPage(1)\n }, [search, sort])\n\n const linkedIds = React.useMemo(\n () => linkedCompanies.map((entry) => entry.id),\n [linkedCompanies],\n )\n const linkedPrimaryId = React.useMemo(\n () =>\n linkedCompanies.find((entry) => entry.isPrimary)?.id ??\n linkedCompanies[0]?.id ??\n null,\n [linkedCompanies],\n )\n\n const companyLinkAdapter = React.useMemo(\n () =>\n createCompanyLinkAdapter({\n dialogTitle: t('customers.linking.company.dialogTitle', 'Link company'),\n dialogSubtitle: _personName\n ? t('customers.linking.company.dialogSubtitleFor', 'Link an existing company to {{name}}', {\n name: _personName,\n })\n : t('customers.linking.company.dialogSubtitle', 'Link an existing company to this person'),\n sectionLabel: t('customers.linking.company.sectionLabel', 'MATCHING COMPANIES'),\n searchPlaceholder: t(\n 'customers.linking.company.searchPlaceholder',\n 'Search all companies\u2026',\n ),\n searchEmptyHint: t(\n 'customers.linking.company.searchEmpty',\n 'No matching companies found.',\n ),\n selectedEmptyHint: t(\n 'customers.linking.company.selectedEmpty',\n 'No companies selected.',\n ),\n confirmButtonLabel: t('customers.linking.company.confirmButton', 'Link company'),\n excludeLinkedPersonId: personId,\n }),\n [_personName, personId, t],\n )\n\n const handleLinkConfirm = React.useCallback(\n async ({\n addedIds,\n removedIds,\n nextSelectedIds,\n primaryId,\n optionsById,\n }: {\n addedIds: string[]\n removedIds: string[]\n nextSelectedIds: string[]\n primaryId?: string | null\n optionsById: Record<string, LinkEntityOption>\n }) => {\n const currentPrimaryId = linkedPrimaryId\n const nextPrimaryId = nextSelectedIds.length\n ? primaryId && nextSelectedIds.includes(primaryId)\n ? primaryId\n : nextSelectedIds[0]\n : null\n\n try {\n for (const companyId of removedIds) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n ),\n { companyId, personId, operation: 'removePersonCompanyLink' },\n )\n }\n\n for (const companyId of addedIds) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies`,\n {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n companyId,\n isPrimary: nextPrimaryId === companyId,\n }),\n },\n ),\n { companyId, personId, operation: 'addPersonCompanyLink' },\n )\n }\n\n if (\n nextPrimaryId &&\n nextPrimaryId !== currentPrimaryId &&\n !addedIds.includes(nextPrimaryId)\n ) {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link set-primary (add/remove)\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(nextPrimaryId)}`,\n {\n method: 'PATCH',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ isPrimary: true }),\n },\n ),\n { companyId: nextPrimaryId, personId, operation: 'setPrimaryPersonCompanyLink' },\n )\n }\n\n const nextLinkedCompanies: LinkedCompanySummary[] = nextSelectedIds.map((id) => {\n const option = optionsById[id]\n const fromCurrent = linkedCompanies.find((entry) => entry.id === id)\n return {\n id,\n displayName: option?.label ?? fromCurrent?.displayName ?? id,\n isPrimary: id === nextPrimaryId,\n }\n })\n setLinkedCompanies(nextLinkedCompanies)\n await loadData({ showLoading: false })\n await onChanged?.()\n flash(\n t('customers.people.detail.companies.manageSuccess', 'Linked companies updated.'),\n 'success',\n )\n } catch (error) {\n const message =\n error instanceof Error\n ? error.message\n : t(\n 'customers.people.detail.companies.manageError',\n 'Failed to update linked companies.',\n )\n flash(message, 'error')\n throw error\n }\n },\n [linkedCompanies, linkedPrimaryId, loadData, onChanged, personId, runWriteMutation, t],\n )\n\n const handleUnlink = React.useCallback(\n async (companyId: string, displayName: string) => {\n if (!companyId || unlinkingId) return\n const confirmed = await confirm({\n title: t('customers.people.detail.companies.unlinkConfirmTitle', 'Unlink company'),\n description: t(\n 'customers.people.detail.companies.unlinkConfirm',\n 'Unlink {{company}} from {{person}}?',\n { company: displayName, person: _personName },\n ),\n confirmText: t('customers.people.detail.companies.unlinkAction', 'Unlink'),\n cancelText: t('customers.linking.actions.cancel', 'Cancel'),\n })\n if (!confirmed) return\n setUnlinkingId(companyId)\n try {\n await runWriteMutation(\n () =>\n // optimistic-lock-exempt: person-company link add/remove\n apiCallOrThrow(\n `/api/customers/people/${encodeURIComponent(personId)}/companies/${encodeURIComponent(companyId)}`,\n { method: 'DELETE' },\n {\n errorMessage: t(\n 'customers.people.detail.companies.unlinkError',\n 'Failed to unlink company.',\n ),\n },\n ),\n { companyId, personId, operation: 'unlinkPersonCompanyLink' },\n )\n await loadData({ showLoading: false })\n await onChanged?.()\n flash(\n t('customers.people.detail.companies.unlinkSuccess', 'Company unlinked.'),\n 'success',\n )\n } catch (error) {\n const message = error instanceof Error\n ? error.message\n : t(\n 'customers.people.detail.companies.unlinkError',\n 'Failed to unlink company.',\n )\n flash(message, 'error')\n } finally {\n setUnlinkingId(null)\n }\n },\n [_personName, confirm, loadData, onChanged, personId, runWriteMutation, t, unlinkingId],\n )\n\n useAppEvent('customers.person_company_link.deleted', (event) => {\n const payload = event.payload as { personEntityId?: string | null } | null | undefined\n if (payload && payload.personEntityId === personId) {\n void loadData({ showLoading: false })\n }\n }, [personId, loadData])\n\n return (\n <>\n <div className=\"space-y-4\">\n <div className=\"flex flex-col gap-3 rounded-2xl border border-border/70 bg-card px-4 py-4 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"space-y-1\">\n <div className=\"text-sm font-semibold text-foreground\">\n {t('customers.people.detail.companies.manageTitle', 'Manage linked companies')}\n </div>\n <div className=\"text-sm text-muted-foreground\">\n {t('customers.people.detail.companies.summary', '{{count}} linked companies', {\n count: linkedCompanies.length,\n })}\n </div>\n </div>\n <div className=\"flex items-center gap-2\">\n <Input\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder={t(\n 'customers.people.detail.companies.searchPlaceholder',\n 'Search linked companies\u2026',\n )}\n className=\"sm:w-[260px]\"\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setDialogOpen(true)}\n >\n <Link2 className=\"mr-2 size-4\" />\n {t('customers.people.detail.companies.manageAction', 'Manage links')}\n </Button>\n </div>\n </div>\n\n <div className=\"flex items-center justify-end\">\n <select\n value={sort}\n onChange={(event) =>\n setSort(event.target.value as 'name-asc' | 'name-desc' | 'recent')\n }\n className=\"h-10 rounded-md border bg-background px-3 text-sm focus:outline-none focus:ring-1 focus:ring-ring\"\n >\n <option value=\"name-asc\">\n {t('customers.people.detail.companies.sortNameAsc', 'Sort: Name A-Z')}\n </option>\n <option value=\"name-desc\">\n {t('customers.people.detail.companies.sortNameDesc', 'Sort: Name Z-A')}\n </option>\n <option value=\"recent\">\n {t('customers.people.detail.companies.sortRecent', 'Sort: Recently active')}\n </option>\n </select>\n </div>\n\n {loading ? (\n <div className=\"space-y-4\">\n {[1, 2].map((idx) => (\n <div\n key={idx}\n className=\"h-[320px] animate-pulse rounded-2xl border border-border/60 bg-muted/30\"\n />\n ))}\n </div>\n ) : items.length === 0 ? (\n <EmptyState\n icon={<Building2 className=\"h-8 w-8\" aria-hidden=\"true\" />}\n title={\n search.trim().length\n ? t(\n 'customers.people.detail.companies.noSearchResults',\n 'No linked companies match your search.',\n )\n : t(\n 'customers.people.detail.empty.companies',\n 'No company linked to this person.',\n )\n }\n />\n ) : (\n <>\n <div className=\"space-y-4\">\n {items.map((item) => (\n <CompanyCard\n key={item.companyId}\n data={item}\n personName={_personName}\n statusMap={statusDict?.map}\n lifecycleMap={lifecycleDict?.map}\n temperatureMap={temperatureDict?.map}\n renewalQuarterMap={renewalQuarterDict?.map}\n roleMap={roleDict?.map}\n onUnlink={() => handleUnlink(item.companyId, item.displayName)}\n unlinkLabel={t('customers.people.detail.companies.unlinkAction', 'Unlink')}\n unlinkDisabled={unlinkingId === item.companyId}\n />\n ))}\n </div>\n <Pagination page={page} totalPages={totalPages} onPageChange={setPage} />\n </>\n )}\n </div>\n\n <LinkEntityDialog\n open={dialogOpen}\n onOpenChange={setDialogOpen}\n adapter={companyLinkAdapter}\n initialSelectedIds={linkedIds}\n initialPrimaryId={linkedPrimaryId}\n primarySupported\n onConfirm={handleLinkConfirm}\n runGuardedMutation={runWriteMutation}\n />\n {ConfirmDialogElement}\n </>\n )\n}\n\nexport default PersonCompaniesSection\n\nexport { sameIdSet }\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -53,11 +53,14 @@ function RoleAssignmentRow({
53
53
  const basePath = entityType === "company" ? "companies" : "people";
54
54
  try {
55
55
  await runMutationWithContext(
56
- () => apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles?roleId=${role.id}`, {
57
- method: "PUT",
58
- headers: { "content-type": "application/json" },
59
- body: JSON.stringify({ userId })
60
- }),
56
+ () => (
57
+ // optimistic-lock-exempt: role assignment add/remove
58
+ apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles?roleId=${role.id}`, {
59
+ method: "PUT",
60
+ headers: { "content-type": "application/json" },
61
+ body: JSON.stringify({ userId })
62
+ })
63
+ ),
61
64
  { roleId: role.id, userId }
62
65
  );
63
66
  setChangingUser(false);
@@ -78,9 +81,12 @@ function RoleAssignmentRow({
78
81
  const basePath = entityType === "company" ? "companies" : "people";
79
82
  try {
80
83
  await runMutationWithContext(
81
- () => apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles?roleId=${role.id}`, {
82
- method: "DELETE"
83
- }),
84
+ () => (
85
+ // optimistic-lock-exempt: role assignment add/remove
86
+ apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles?roleId=${role.id}`, {
87
+ method: "DELETE"
88
+ })
89
+ ),
84
90
  { roleId: role.id }
85
91
  );
86
92
  onRemoved();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/RoleAssignmentRow.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Mail, Phone, X } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { LookupSelect, type LookupSelectItem } from '@open-mercato/ui/backend/inputs/LookupSelect'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { fetchAssignableStaffMembers } from './assignableStaff'\n\nexport interface RoleAssignment {\n id: string\n roleType: string\n userId: string\n userName?: string | null\n userEmail?: string | null\n userPhone?: string | null\n createdAt?: string\n updatedAt?: string\n}\n\ninterface RoleAssignmentRowProps {\n role: RoleAssignment\n roleTypeLabel: string\n runMutationWithContext: <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n ) => Promise<T>\n entityType: 'company' | 'person'\n entityId: string\n onRemoved: () => void\n onUpdated: () => void\n}\n\nfunction formatAssignedAt(value: string | undefined, t: ReturnType<typeof useT>): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n const diffMs = Date.now() - date.getTime()\n const diffDays = Math.floor(diffMs / 86_400_000)\n if (diffDays <= 0) return t('customers.roles.assignedToday', 'Assigned today')\n if (diffDays === 1) return t('customers.roles.assignedYesterday', 'Assigned yesterday')\n return t('customers.roles.assignedDaysAgo', 'Assigned {{count}} days ago', { count: diffDays })\n}\n\nexport function RoleAssignmentRow({\n role,\n roleTypeLabel,\n runMutationWithContext,\n entityType,\n entityId,\n onRemoved,\n onUpdated,\n}: RoleAssignmentRowProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [removing, setRemoving] = React.useState(false)\n const [changingUser, setChangingUser] = React.useState(false)\n\n const searchUsers = React.useCallback(async (query: string): Promise<LookupSelectItem[]> => {\n try {\n const members = await fetchAssignableStaffMembers(query, { pageSize: 20 })\n return members.map((member) => ({\n id: member.userId,\n title: member.displayName,\n subtitle: member.displayName && member.email ? member.email : null,\n }))\n } catch (error) {\n console.error('customers.roles.searchUsers failed', error)\n return []\n }\n }, [])\n\n const handleUserChange = React.useCallback(async (userId: string | null) => {\n if (!userId || userId === role.userId) return\n const basePath = entityType === 'company' ? 'companies' : 'people'\n try {\n await runMutationWithContext(\n () =>\n apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles?roleId=${role.id}`, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ userId }),\n }),\n { roleId: role.id, userId },\n )\n setChangingUser(false)\n onUpdated()\n } catch (error) {\n console.error('customers.roles.update failed', error)\n flash(t('customers.roles.updateFailed', 'Failed to update role assignment.'), 'error')\n setChangingUser(false)\n }\n }, [entityId, entityType, onUpdated, role.id, role.userId, runMutationWithContext, t])\n\n const handleRemove = React.useCallback(async () => {\n const confirmed = await confirm({\n title: t('customers.roles.removeConfirm', 'Remove this role assignment?'),\n variant: 'default',\n })\n if (!confirmed) return\n\n setRemoving(true)\n const basePath = entityType === 'company' ? 'companies' : 'people'\n try {\n await runMutationWithContext(\n () =>\n apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles?roleId=${role.id}`, {\n method: 'DELETE',\n }),\n { roleId: role.id },\n )\n onRemoved()\n } catch (error) {\n console.error('customers.roles.remove failed', error)\n flash(t('customers.roles.removeFailed', 'Failed to remove role assignment.'), 'error')\n } finally {\n setRemoving(false)\n }\n }, [confirm, entityId, entityType, onRemoved, role.id, runMutationWithContext, t])\n\n const currentUserOptions = React.useMemo<LookupSelectItem[]>(\n () =>\n role.userId\n ? [{\n id: role.userId,\n title: role.userName ?? role.userEmail ?? role.userId,\n subtitle: role.userName && role.userEmail ? role.userEmail : null,\n }]\n : [],\n [role.userEmail, role.userId, role.userName],\n )\n\n const userLabel = React.useMemo(() => {\n return role.userName ?? role.userEmail ?? '?'\n }, [role.userEmail, role.userName])\n\n const displayName = role.userName ?? role.userEmail ?? role.userId\n const assignedLabel = formatAssignedAt(role.createdAt, t)\n\n return (\n <>\n {ConfirmDialogElement}\n <div className=\"flex h-full min-w-0 flex-col overflow-hidden rounded-xl border bg-card p-4 shadow-sm\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <Badge variant=\"outline\" className=\"max-w-full break-words rounded-full px-2 py-0 text-left text-xs font-semibold\">\n {roleTypeLabel}\n </Badge>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleRemove}\n disabled={removing}\n aria-label={t('customers.roles.remove', 'Remove role')}\n >\n <X className=\"size-4\" />\n </IconButton>\n </div>\n\n <div className=\"mt-4 flex min-w-0 flex-col gap-3 sm:flex-row sm:items-start\">\n <Avatar label={userLabel} size=\"lg\" variant=\"monochrome\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"break-all text-sm font-semibold leading-5 text-foreground\">{displayName}</div>\n {role.userEmail ? (\n <div className=\"mt-1 break-all text-xs text-muted-foreground\">{role.userEmail}</div>\n ) : null}\n {assignedLabel ? (\n <div className=\"mt-2 text-overline uppercase tracking-[0.08em] text-muted-foreground\">\n {assignedLabel}\n </div>\n ) : null}\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center gap-2\">\n {role.userEmail ? (\n <IconButton asChild variant=\"outline\" size=\"sm\" className=\"shrink-0\">\n <a href={`mailto:${role.userEmail}`} aria-label={t('customers.roles.email', 'Send email')}>\n <Mail className=\"size-4\" />\n </a>\n </IconButton>\n ) : (\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"shrink-0\"\n disabled\n aria-label={t('customers.roles.emailUnavailable', 'Email unavailable')}\n >\n <Mail className=\"size-4\" />\n </IconButton>\n )}\n {role.userPhone ? (\n <IconButton asChild variant=\"outline\" size=\"sm\" className=\"shrink-0\">\n <a href={`tel:${role.userPhone}`} aria-label={t('customers.roles.call', 'Call')}>\n <Phone className=\"size-4\" />\n </a>\n </IconButton>\n ) : (\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"shrink-0\"\n disabled\n aria-label={t('customers.roles.phoneUnavailable', 'Phone unavailable')}\n >\n <Phone className=\"size-4\" />\n </IconButton>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto w-full justify-start px-2 py-1 text-xs sm:ml-auto sm:w-auto\"\n onClick={() => setChangingUser((current) => !current)}\n >\n {t('customers.roles.changeUser', 'Change user')}\n </Button>\n </div>\n\n {changingUser ? (\n <div className=\"mt-4 border-t pt-4\">\n <LookupSelect\n value={role.userId}\n onChange={async (userId) => {\n await handleUserChange(userId)\n }}\n fetchItems={searchUsers}\n options={currentUserOptions}\n placeholder={t('customers.roles.searchPlaceholder', 'Search team member...')}\n />\n </div>\n ) : null}\n </div>\n </>\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Mail, Phone, X } from 'lucide-react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { LookupSelect, type LookupSelectItem } from '@open-mercato/ui/backend/inputs/LookupSelect'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Avatar } from '@open-mercato/ui/primitives/avatar'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { Badge } from '@open-mercato/ui/primitives/badge'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { fetchAssignableStaffMembers } from './assignableStaff'\n\nexport interface RoleAssignment {\n id: string\n roleType: string\n userId: string\n userName?: string | null\n userEmail?: string | null\n userPhone?: string | null\n createdAt?: string\n updatedAt?: string\n}\n\ninterface RoleAssignmentRowProps {\n role: RoleAssignment\n roleTypeLabel: string\n runMutationWithContext: <T,>(\n operation: () => Promise<T>,\n mutationPayload?: Record<string, unknown>,\n ) => Promise<T>\n entityType: 'company' | 'person'\n entityId: string\n onRemoved: () => void\n onUpdated: () => void\n}\n\nfunction formatAssignedAt(value: string | undefined, t: ReturnType<typeof useT>): string | null {\n if (!value) return null\n const date = new Date(value)\n if (Number.isNaN(date.getTime())) return null\n const diffMs = Date.now() - date.getTime()\n const diffDays = Math.floor(diffMs / 86_400_000)\n if (diffDays <= 0) return t('customers.roles.assignedToday', 'Assigned today')\n if (diffDays === 1) return t('customers.roles.assignedYesterday', 'Assigned yesterday')\n return t('customers.roles.assignedDaysAgo', 'Assigned {{count}} days ago', { count: diffDays })\n}\n\nexport function RoleAssignmentRow({\n role,\n roleTypeLabel,\n runMutationWithContext,\n entityType,\n entityId,\n onRemoved,\n onUpdated,\n}: RoleAssignmentRowProps) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [removing, setRemoving] = React.useState(false)\n const [changingUser, setChangingUser] = React.useState(false)\n\n const searchUsers = React.useCallback(async (query: string): Promise<LookupSelectItem[]> => {\n try {\n const members = await fetchAssignableStaffMembers(query, { pageSize: 20 })\n return members.map((member) => ({\n id: member.userId,\n title: member.displayName,\n subtitle: member.displayName && member.email ? member.email : null,\n }))\n } catch (error) {\n console.error('customers.roles.searchUsers failed', error)\n return []\n }\n }, [])\n\n const handleUserChange = React.useCallback(async (userId: string | null) => {\n if (!userId || userId === role.userId) return\n const basePath = entityType === 'company' ? 'companies' : 'people'\n try {\n await runMutationWithContext(\n () =>\n // optimistic-lock-exempt: role assignment add/remove\n apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles?roleId=${role.id}`, {\n method: 'PUT',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ userId }),\n }),\n { roleId: role.id, userId },\n )\n setChangingUser(false)\n onUpdated()\n } catch (error) {\n console.error('customers.roles.update failed', error)\n flash(t('customers.roles.updateFailed', 'Failed to update role assignment.'), 'error')\n setChangingUser(false)\n }\n }, [entityId, entityType, onUpdated, role.id, role.userId, runMutationWithContext, t])\n\n const handleRemove = React.useCallback(async () => {\n const confirmed = await confirm({\n title: t('customers.roles.removeConfirm', 'Remove this role assignment?'),\n variant: 'default',\n })\n if (!confirmed) return\n\n setRemoving(true)\n const basePath = entityType === 'company' ? 'companies' : 'people'\n try {\n await runMutationWithContext(\n () =>\n // optimistic-lock-exempt: role assignment add/remove\n apiCallOrThrow(`/api/customers/${basePath}/${entityId}/roles?roleId=${role.id}`, {\n method: 'DELETE',\n }),\n { roleId: role.id },\n )\n onRemoved()\n } catch (error) {\n console.error('customers.roles.remove failed', error)\n flash(t('customers.roles.removeFailed', 'Failed to remove role assignment.'), 'error')\n } finally {\n setRemoving(false)\n }\n }, [confirm, entityId, entityType, onRemoved, role.id, runMutationWithContext, t])\n\n const currentUserOptions = React.useMemo<LookupSelectItem[]>(\n () =>\n role.userId\n ? [{\n id: role.userId,\n title: role.userName ?? role.userEmail ?? role.userId,\n subtitle: role.userName && role.userEmail ? role.userEmail : null,\n }]\n : [],\n [role.userEmail, role.userId, role.userName],\n )\n\n const userLabel = React.useMemo(() => {\n return role.userName ?? role.userEmail ?? '?'\n }, [role.userEmail, role.userName])\n\n const displayName = role.userName ?? role.userEmail ?? role.userId\n const assignedLabel = formatAssignedAt(role.createdAt, t)\n\n return (\n <>\n {ConfirmDialogElement}\n <div className=\"flex h-full min-w-0 flex-col overflow-hidden rounded-xl border bg-card p-4 shadow-sm\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <Badge variant=\"outline\" className=\"max-w-full break-words rounded-full px-2 py-0 text-left text-xs font-semibold\">\n {roleTypeLabel}\n </Badge>\n <IconButton\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n onClick={handleRemove}\n disabled={removing}\n aria-label={t('customers.roles.remove', 'Remove role')}\n >\n <X className=\"size-4\" />\n </IconButton>\n </div>\n\n <div className=\"mt-4 flex min-w-0 flex-col gap-3 sm:flex-row sm:items-start\">\n <Avatar label={userLabel} size=\"lg\" variant=\"monochrome\" />\n <div className=\"min-w-0 flex-1\">\n <div className=\"break-all text-sm font-semibold leading-5 text-foreground\">{displayName}</div>\n {role.userEmail ? (\n <div className=\"mt-1 break-all text-xs text-muted-foreground\">{role.userEmail}</div>\n ) : null}\n {assignedLabel ? (\n <div className=\"mt-2 text-overline uppercase tracking-[0.08em] text-muted-foreground\">\n {assignedLabel}\n </div>\n ) : null}\n </div>\n </div>\n\n <div className=\"mt-4 flex flex-wrap items-center gap-2\">\n {role.userEmail ? (\n <IconButton asChild variant=\"outline\" size=\"sm\" className=\"shrink-0\">\n <a href={`mailto:${role.userEmail}`} aria-label={t('customers.roles.email', 'Send email')}>\n <Mail className=\"size-4\" />\n </a>\n </IconButton>\n ) : (\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"shrink-0\"\n disabled\n aria-label={t('customers.roles.emailUnavailable', 'Email unavailable')}\n >\n <Mail className=\"size-4\" />\n </IconButton>\n )}\n {role.userPhone ? (\n <IconButton asChild variant=\"outline\" size=\"sm\" className=\"shrink-0\">\n <a href={`tel:${role.userPhone}`} aria-label={t('customers.roles.call', 'Call')}>\n <Phone className=\"size-4\" />\n </a>\n </IconButton>\n ) : (\n <IconButton\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n className=\"shrink-0\"\n disabled\n aria-label={t('customers.roles.phoneUnavailable', 'Phone unavailable')}\n >\n <Phone className=\"size-4\" />\n </IconButton>\n )}\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"h-auto w-full justify-start px-2 py-1 text-xs sm:ml-auto sm:w-auto\"\n onClick={() => setChangingUser((current) => !current)}\n >\n {t('customers.roles.changeUser', 'Change user')}\n </Button>\n </div>\n\n {changingUser ? (\n <div className=\"mt-4 border-t pt-4\">\n <LookupSelect\n value={role.userId}\n onChange={async (userId) => {\n await handleUserChange(userId)\n }}\n fetchItems={searchUsers}\n options={currentUserOptions}\n placeholder={t('customers.roles.searchPlaceholder', 'Search team member...')}\n />\n </div>\n ) : null}\n </div>\n </>\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -5,7 +5,8 @@ import { Users, Phone, Check, Mail, Calendar, AlertTriangle, X, StickyNote } fro
5
5
  import { cn } from "@open-mercato/shared/lib/utils";
6
6
  import { useT } from "@open-mercato/shared/lib/i18n/context";
7
7
  import { validatePhoneNumber } from "@open-mercato/shared/lib/phone";
8
- import { apiCallOrThrow, readApiResultOrThrow } from "@open-mercato/ui/backend/utils/apiCall";
8
+ import { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
9
+ import { buildOptimisticLockHeader, extractOptimisticLockConflict } from "@open-mercato/ui/backend/utils/optimisticLock";
9
10
  import { mapCrudServerErrorToFormErrors } from "@open-mercato/ui/backend/utils/serverErrors";
10
11
  import { flash } from "@open-mercato/ui/backend/FlashMessages";
11
12
  import { useGuardedMutation } from "@open-mercato/ui/backend/injection/useGuardedMutation";
@@ -368,11 +369,14 @@ function ScheduleActivityDialog({
368
369
  ...Object.keys(customValues).length > 0 ? { customValues } : {}
369
370
  };
370
371
  await runGuardedMutation(
371
- () => apiCallOrThrow("/api/customers/interactions", {
372
- method: isSaveEdit ? "PUT" : "POST",
373
- headers: { "content-type": "application/json" },
374
- body: JSON.stringify(payload)
375
- }),
372
+ () => {
373
+ const call = () => apiCallOrThrow("/api/customers/interactions", {
374
+ method: isSaveEdit ? "PUT" : "POST",
375
+ headers: { "content-type": "application/json" },
376
+ body: JSON.stringify(payload)
377
+ });
378
+ return isSaveEdit ? withScopedApiRequestHeaders(buildOptimisticLockHeader(editData?.updatedAt), call) : call();
379
+ },
376
380
  {
377
381
  operation: isSaveEdit ? "updateActivity" : "createActivity",
378
382
  interactionId: editData?.id ?? null,
@@ -385,6 +389,10 @@ function ScheduleActivityDialog({
385
389
  onActivityCreated?.();
386
390
  });
387
391
  } catch (err) {
392
+ if (extractOptimisticLockConflict(err)) {
393
+ onClose();
394
+ return;
395
+ }
388
396
  const { message, fieldErrors } = mapCrudServerErrorToFormErrors(err);
389
397
  const phoneFieldError = fieldErrors?.phoneNumber;
390
398
  if (state.activityType === "call" && phoneFieldError) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../src/modules/customers/components/detail/ScheduleActivityDialog.tsx"],
4
- "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Users, Phone, Check, Mail, Calendar, AlertTriangle, X, StickyNote } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { validatePhoneNumber } from '@open-mercato/shared/lib/phone'\nimport { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'\nimport { mapCrudServerErrorToFormErrors } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { PhoneNumberField, SwitchableMarkdownInput } from '@open-mercato/ui/backend/inputs'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport {\n useScheduleFormState,\n FIELD_VISIBILITY,\n getFieldLabel,\n DateTimeFields,\n ParticipantsField,\n LocationField,\n FooterFields,\n LinkedEntitiesField,\n} from './schedule'\nimport type { ActivityType, ScheduleActivityEditData } from './schedule'\n\nconst TYPE_TABS: Array<{ type: ActivityType; icon: React.ComponentType<{ className?: string }>; labelKey: string; fallback: string }> = [\n { type: 'meeting', icon: Users, labelKey: 'customers.schedule.types.meeting', fallback: 'Meeting' },\n { type: 'call', icon: Phone, labelKey: 'customers.schedule.types.call', fallback: 'Call' },\n { type: 'task', icon: Check, labelKey: 'customers.schedule.types.task', fallback: 'Task' },\n { type: 'email', icon: Mail, labelKey: 'customers.schedule.types.email', fallback: 'Email' },\n { type: 'note', icon: StickyNote, labelKey: 'customers.schedule.types.note', fallback: 'Note' },\n]\n\ntype DialogChrome = { titleKey: string; titleFallback: string; subtitleKey: string; subtitleFallback: string; saveKey: string; saveFallback: string; saveIcon: React.ComponentType<{ className?: string }> }\n\nconst TYPE_CHROME: Record<ActivityType, DialogChrome> = {\n meeting: {\n titleKey: 'customers.schedule.meeting.title', titleFallback: 'New meeting',\n subtitleKey: 'customers.schedule.meeting.subtitle', subtitleFallback: 'Block time on the calendar with attendees',\n saveKey: 'customers.schedule.meeting.save', saveFallback: 'Save activity', saveIcon: Calendar,\n },\n call: {\n titleKey: 'customers.schedule.call.title', titleFallback: 'Log call',\n subtitleKey: 'customers.schedule.call.subtitle', subtitleFallback: 'Log a call you just had or schedule one',\n saveKey: 'customers.schedule.call.save', saveFallback: 'Log call', saveIcon: Phone,\n },\n task: {\n titleKey: 'customers.schedule.task.title', titleFallback: 'New task',\n subtitleKey: 'customers.schedule.task.subtitle', subtitleFallback: 'Capture something to follow up on',\n saveKey: 'customers.schedule.task.save', saveFallback: 'Save task', saveIcon: Check,\n },\n email: {\n titleKey: 'customers.schedule.email.title', titleFallback: 'Compose email',\n subtitleKey: 'customers.schedule.email.subtitle', subtitleFallback: 'Compose and send a tracked email',\n saveKey: 'customers.schedule.email.save', saveFallback: 'Send email', saveIcon: Mail,\n },\n note: {\n titleKey: 'customers.schedule.note.title', titleFallback: 'Add note',\n subtitleKey: 'customers.schedule.note.subtitle', subtitleFallback: 'Write down a note about this interaction',\n saveKey: 'customers.schedule.note.save', saveFallback: 'Save note', saveIcon: StickyNote,\n },\n}\n\nconst CALL_DIRECTIONS: Array<{ key: 'outbound' | 'inbound'; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'outbound', labelKey: 'customers.schedule.call.direction.outbound', labelFallback: 'Outbound', dot: 'bg-status-info-icon' },\n { key: 'inbound', labelKey: 'customers.schedule.call.direction.inbound', labelFallback: 'Inbound', dot: 'bg-status-success-icon' },\n]\n\nconst CALL_OUTCOMES: Array<{ key: string; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'connected', labelKey: 'customers.schedule.call.outcome.connected', labelFallback: 'Connected', dot: 'bg-status-success-icon' },\n { key: 'voicemail', labelKey: 'customers.schedule.call.outcome.voicemail', labelFallback: 'Voicemail', dot: 'bg-status-warning-icon' },\n { key: 'noanswer', labelKey: 'customers.schedule.call.outcome.noAnswer', labelFallback: 'No answer', dot: 'bg-muted-foreground' },\n { key: 'busy', labelKey: 'customers.schedule.call.outcome.busy', labelFallback: 'Busy', dot: 'bg-status-warning-icon' },\n { key: 'badnumber', labelKey: 'customers.schedule.call.outcome.badNumber', labelFallback: 'Bad number', dot: 'bg-status-error-icon' },\n]\n\nconst TASK_PRIORITIES: Array<{ key: string; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'low', labelKey: 'customers.schedule.task.priority.low', labelFallback: 'Low', dot: 'bg-muted-foreground' },\n { key: 'medium', labelKey: 'customers.schedule.task.priority.medium', labelFallback: 'Medium', dot: 'bg-status-info-icon' },\n { key: 'high', labelKey: 'customers.schedule.task.priority.high', labelFallback: 'High', dot: 'bg-status-warning-icon' },\n { key: 'urgent', labelKey: 'customers.schedule.task.priority.urgent', labelFallback: 'Urgent', dot: 'bg-status-error-icon' },\n]\n\ninterface ScheduleActivityDialogProps {\n open: boolean\n onClose: () => void\n entityId: string\n dealId?: string | null\n entityName?: string\n companyName?: string | null\n entityType: 'company' | 'person' | 'deal'\n onActivityCreated?: () => void\n /** When provided, dialog opens in edit mode with pre-filled data */\n editData?: ScheduleActivityEditData | null\n}\n\nexport function ScheduleActivityDialog({\n open,\n onClose,\n entityId,\n dealId = null,\n entityName,\n companyName,\n entityType,\n onActivityCreated,\n editData,\n}: ScheduleActivityDialogProps) {\n const t = useT()\n const state = useScheduleFormState({ open, editData: editData ?? null })\n const visibleFields = FIELD_VISIBILITY[state.activityType]\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const isEditing = Boolean(editData?.id)\n const chrome = TYPE_CHROME[state.activityType]\n const SaveIcon = chrome.saveIcon\n const [callDirection, setCallDirection] = React.useState<'outbound' | 'inbound'>('outbound')\n const [callOutcome, setCallOutcome] = React.useState<string | null>(null)\n const [callPhoneNumber, setCallPhoneNumber] = React.useState('')\n const [callPhoneError, setCallPhoneError] = React.useState<string | null>(null)\n const [taskPriority, setTaskPriority] = React.useState<string>('medium')\n const callPhoneInvalidMessage = React.useMemo(\n () =>\n t(\n 'customers.activities.errors.phoneInvalid',\n 'Enter a valid phone number with country code (e.g. +1 212 555 1234)',\n ),\n [t],\n )\n const translateErrorMessage = React.useCallback(\n (message: string | undefined, fallback: string) => {\n const key = typeof message === 'string' ? message.trim() : ''\n return key ? t(key, key) : fallback\n },\n [t],\n )\n\n React.useEffect(() => {\n if (!open) return\n const raw = editData as (Record<string, unknown> & { customValues?: unknown; phoneNumber?: unknown }) | null | undefined\n const cv = (raw?.customValues && typeof raw.customValues === 'object' ? raw.customValues : null) as Record<string, unknown> | null\n setCallDirection(typeof cv?.callDirection === 'string' && cv.callDirection === 'inbound' ? 'inbound' : 'outbound')\n setCallOutcome(typeof cv?.callOutcome === 'string' ? cv.callOutcome : null)\n // Seed phone number from either top-level `phoneNumber` (newer write path)\n // or legacy `customValues.callPhoneNumber` so previously-saved calls still\n // round-trip on edit (#1808).\n const seededPhone =\n typeof raw?.phoneNumber === 'string' && raw.phoneNumber.trim().length > 0\n ? raw.phoneNumber\n : typeof cv?.callPhoneNumber === 'string'\n ? cv.callPhoneNumber\n : ''\n setCallPhoneNumber(seededPhone)\n setCallPhoneError(null)\n setTaskPriority(typeof cv?.taskPriority === 'string' ? cv.taskPriority : 'medium')\n }, [open, editData])\n\n // Reset per-type chip state when the user switches activity type in create mode.\n // In edit mode, the persisted customValues should win, so we skip the reset.\n React.useEffect(() => {\n if (!open || isEditing) return\n setCallDirection('outbound')\n setCallOutcome(null)\n setCallPhoneNumber('')\n setCallPhoneError(null)\n setTaskPriority('medium')\n }, [state.activityType, open, isEditing])\n\n const handleCallPhoneChange = React.useCallback((next: string | undefined) => {\n setCallPhoneNumber(next ?? '')\n setCallPhoneError(null)\n }, [])\n\n const formSnapshot = React.useMemo(() => JSON.stringify({\n activityType: state.activityType,\n title: state.title,\n date: state.date,\n startTime: state.startTime,\n duration: state.duration,\n allDay: state.allDay,\n description: state.description,\n location: state.location,\n reminderMinutes: state.reminderMinutes,\n visibility: state.visibility,\n participants: state.participants,\n linkedEntities: state.linkedEntities,\n recurrenceEnabled: state.recurrenceEnabled,\n recurrenceDays: state.recurrenceDays,\n recurrenceEndType: state.recurrenceEndType,\n recurrenceCount: state.recurrenceCount,\n recurrenceEndDate: state.recurrenceEndDate,\n guestPermissions: state.guestPermissions,\n }), [\n state.activityType, state.title, state.date, state.startTime, state.duration, state.allDay,\n state.description, state.location, state.reminderMinutes, state.visibility, state.participants,\n state.linkedEntities, state.recurrenceEnabled, state.recurrenceDays, state.recurrenceEndType,\n state.recurrenceCount, state.recurrenceEndDate, state.guestPermissions,\n ])\n const initialSnapshotRef = React.useRef<string | null>(null)\n const snapshotOpenKeyRef = React.useRef<string | null>(null)\n const snapshotSettleCountRef = React.useRef(0)\n const openKey = open ? `${editData?.id ?? 'new'}` : null\n React.useEffect(() => {\n if (!open) {\n initialSnapshotRef.current = null\n snapshotOpenKeyRef.current = null\n snapshotSettleCountRef.current = 0\n return\n }\n if (snapshotOpenKeyRef.current !== openKey) {\n snapshotOpenKeyRef.current = openKey\n snapshotSettleCountRef.current = 0\n initialSnapshotRef.current = null\n }\n if (snapshotSettleCountRef.current < 2) {\n initialSnapshotRef.current = formSnapshot\n snapshotSettleCountRef.current += 1\n }\n }, [open, openKey, formSnapshot])\n\n const isDirty = React.useCallback(() => {\n if (initialSnapshotRef.current == null) return false\n return initialSnapshotRef.current !== formSnapshot\n }, [formSnapshot])\n\n const guardedClose = React.useCallback(async () => {\n if (!isDirty()) {\n onClose()\n return\n }\n const ok = await confirm({\n title: t('customers.schedule.discardConfirm.title', 'Discard unsaved changes?'),\n description: t(\n 'customers.schedule.discardConfirm.description',\n 'You have unsaved edits in this activity. Save them first or continue to discard them.',\n ),\n confirmText: t('customers.schedule.discardConfirm.confirm', 'Discard'),\n cancelText: t('customers.schedule.discardConfirm.cancel', 'Keep editing'),\n variant: 'destructive',\n })\n if (ok) onClose()\n }, [confirm, isDirty, onClose, t])\n\n const mutationContextId = React.useMemo(\n () => `customer-activity:${entityType}:${entityId}`,\n [entityId, entityType],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n entityType: 'company' | 'person' | 'deal'\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const mutationContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n resourceKind:\n entityType === 'company'\n ? 'customers.company'\n : entityType === 'person'\n ? 'customers.person'\n : 'customers.deal',\n resourceId: entityId,\n entityType,\n retryLastMutation,\n }),\n [entityId, entityType, mutationContextId, retryLastMutation],\n )\n const runGuardedMutation = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload: Record<string, unknown>) =>\n runMutation({\n operation,\n mutationPayload,\n context: mutationContext,\n }),\n [mutationContext, runMutation],\n )\n\n // Conflict detection -- debounced check when date/time/duration changes\n React.useEffect(() => {\n if (!open || state.allDay || !state.date || !state.startTime) {\n state.setConflict(null)\n return\n }\n const timer = setTimeout(async () => {\n try {\n const localStart = new Date(`${state.date}T${state.startTime}:00`)\n const params = new URLSearchParams({\n date: state.date,\n startTime: state.startTime,\n duration: String(state.duration),\n })\n if (editData?.id) {\n params.set('excludeId', editData.id)\n }\n if (!Number.isNaN(localStart.getTime())) {\n params.set('timezoneOffsetMinutes', String(-localStart.getTimezoneOffset()))\n }\n const data = await readApiResultOrThrow<{\n hasConflicts: boolean\n conflicts: Array<{ id: string; title: string | null; startTime: string; endTime: string; type: string }>\n }>(`/api/customers/interactions/conflicts?${params.toString()}`)\n if (data?.hasConflicts && Array.isArray(data.conflicts) && data.conflicts.length > 0) {\n const descriptions = data.conflicts\n .map((c) => `${c.startTime}\u2013${c.endTime}: ${c.title ?? c.type}`)\n .join(', ')\n state.setConflict(\n t('customers.schedule.conflict.description', 'Overlaps with: {{items}}', { items: descriptions }),\n )\n } else {\n state.setConflict(null)\n }\n } catch {\n state.setConflict(null)\n }\n }, 500)\n return () => clearTimeout(timer)\n }, [editData?.id, open, state.date, state.startTime, state.duration, state.allDay, t]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const trimmedDate = state.date.trim()\n const trimmedStartTime = state.startTime.trim()\n const trimmedCallPhone = callPhoneNumber.trim()\n const isDateMissing = !trimmedDate\n const isTimeMissing = !state.allDay && !trimmedStartTime\n const isSubmitDisabled =\n state.saving ||\n !state.title.trim() ||\n isDateMissing ||\n isTimeMissing\n\n const handleSave = React.useCallback(async () => {\n if (!state.title.trim()) return\n if (isDateMissing) {\n flash(t('customers.activities.errors.dateRequired', 'Date is required'), 'error')\n return\n }\n if (isTimeMissing) {\n flash(t('customers.activities.errors.timeRequired', 'Time is required'), 'error')\n return\n }\n let phoneNumberForPayload: string | undefined\n if (state.activityType === 'call' && trimmedCallPhone) {\n const phoneValidation = validatePhoneNumber(trimmedCallPhone)\n if (!phoneValidation.valid) {\n setCallPhoneError(callPhoneInvalidMessage)\n flash(callPhoneInvalidMessage, 'error')\n return\n }\n phoneNumberForPayload = phoneValidation.normalized ?? trimmedCallPhone\n setCallPhoneError(null)\n if (phoneNumberForPayload !== callPhoneNumber) {\n setCallPhoneNumber(phoneNumberForPayload)\n }\n }\n state.setSaving(true)\n try {\n const scheduledAt = state.allDay\n ? new Date(`${state.date}T00:00:00`).toISOString()\n : new Date(`${state.date}T${state.startTime}:00`).toISOString()\n\n const recurrenceRule = state.recurrenceEnabled\n ? buildRecurrenceRule(state.recurrenceDays, state.recurrenceEndType, state.recurrenceCount, state.recurrenceEndDate)\n : null\n\n const isSaveEdit = Boolean(editData?.id)\n const customValues: Record<string, unknown> = {}\n if (state.activityType === 'call') {\n customValues.callDirection = callDirection\n if (callOutcome) customValues.callOutcome = callOutcome\n if (phoneNumberForPayload) customValues.callPhoneNumber = phoneNumberForPayload\n }\n if (state.activityType === 'task') {\n customValues.taskPriority = taskPriority\n }\n const payload = {\n ...(isSaveEdit ? { id: editData!.id } : {}),\n entityId,\n dealId,\n interactionType: state.activityType,\n title: state.title.trim(),\n body: state.description.trim() || null,\n status: 'planned',\n date: trimmedDate,\n time: state.allDay ? '00:00' : trimmedStartTime,\n phoneNumber: state.activityType === 'call' ? phoneNumberForPayload : undefined,\n scheduledAt,\n durationMinutes: visibleFields.has('duration') && !state.allDay ? state.duration : null,\n location: visibleFields.has('location') ? (state.location.trim() || null) : null,\n allDay: visibleFields.has('allDay') ? state.allDay : null,\n recurrenceRule: visibleFields.has('recurrence') ? recurrenceRule : null,\n recurrenceEnd: visibleFields.has('recurrence') && state.recurrenceEndType === 'date' && state.recurrenceEndDate\n ? new Date(state.recurrenceEndDate).toISOString()\n : null,\n participants: visibleFields.has('participants') && state.participants.length > 0\n ? state.participants.map((p) => ({ userId: p.userId, name: p.name, email: p.email, status: p.status ?? 'pending' }))\n : null,\n guestPermissions: visibleFields.has('participants') && state.participants.length > 0 ? state.guestPermissions : null,\n linkedEntities: state.linkedEntities.length > 0\n ? state.linkedEntities.map((e) => ({ id: e.id, type: e.type, label: e.label }))\n : null,\n reminderMinutes: visibleFields.has('reminder') ? state.reminderMinutes : null,\n visibility: visibleFields.has('visibility') ? state.visibility : null,\n ...(Object.keys(customValues).length > 0 ? { customValues } : {}),\n }\n await runGuardedMutation(\n () =>\n apiCallOrThrow('/api/customers/interactions', {\n method: isSaveEdit ? 'PUT' : 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n }),\n {\n operation: isSaveEdit ? 'updateActivity' : 'createActivity',\n interactionId: editData?.id ?? null,\n interactionType: state.activityType,\n },\n )\n flash(t('customers.schedule.saved', 'Activity scheduled'), 'success')\n onClose()\n // Delay data reload so the dialog can unmount cleanly and Radix restores body scroll\n requestAnimationFrame(() => { onActivityCreated?.() })\n } catch (err) {\n const { message, fieldErrors } = mapCrudServerErrorToFormErrors(err)\n const phoneFieldError = fieldErrors?.phoneNumber\n if (state.activityType === 'call' && phoneFieldError) {\n const translatedPhoneError = translateErrorMessage(phoneFieldError, callPhoneInvalidMessage)\n setCallPhoneError(translatedPhoneError)\n flash(translatedPhoneError, 'error')\n return\n }\n flash(\n translateErrorMessage(message, t('customers.schedule.error', 'Failed to schedule activity')),\n 'error',\n )\n } finally {\n state.setSaving(false)\n }\n }, [callDirection, callOutcome, callPhoneInvalidMessage, callPhoneNumber, isDateMissing, isTimeMissing, state.activityType, state.allDay, state.date, state.description, dealId, state.duration, editData, entityId, state.guestPermissions, state.linkedEntities, state.location, onActivityCreated, onClose, state.participants, state.recurrenceCount, state.recurrenceDays, state.recurrenceEnabled, state.recurrenceEndDate, state.recurrenceEndType, state.reminderMinutes, runGuardedMutation, state.startTime, t, taskPriority, state.title, translateErrorMessage, trimmedCallPhone, trimmedDate, trimmedStartTime, state.visibility, visibleFields]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleKeyDown = useDialogKeyHandler({ onConfirm: handleSave })\n\n return (\n <Dialog open={open} onOpenChange={(o) => { if (!o) void guardedClose() }}>\n {ConfirmDialogElement}\n <DialogContent className=\"flex max-h-[90vh] flex-col overflow-hidden border-border p-0 shadow-xl sm:max-w-[760px] sm:rounded-xl [&>[data-dialog-close]]:hidden\" onKeyDown={handleKeyDown} aria-describedby={undefined}>\n <VisuallyHidden>\n <DialogTitle>{isEditing ? t('customers.schedule.editTitle', 'Edit activity') : t(chrome.titleKey, chrome.titleFallback)}</DialogTitle>\n </VisuallyHidden>\n\n {/* Header */}\n <div className=\"flex shrink-0 items-start justify-between gap-3 border-b border-border bg-background px-6 py-5\">\n <div className=\"flex flex-col gap-1\">\n <h2 className=\"text-lg font-semibold leading-tight tracking-tight text-foreground\">\n {isEditing ? t('customers.schedule.editTitle', 'Edit activity') : t(chrome.titleKey, chrome.titleFallback)}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(chrome.subtitleKey, chrome.subtitleFallback)}\n </p>\n {entityName ? (\n <p className=\"mt-0.5 text-xs text-muted-foreground/80\">\n {t('customers.schedule.context', 'On timeline: {{name}}', { name: entityName })}\n {companyName ? ` \u00B7 ${companyName}` : ''}\n </p>\n ) : null}\n </div>\n <IconButton type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => { void guardedClose() }} className=\"flex size-9 shrink-0 items-center justify-center rounded-md border border-border bg-background\" aria-label={t('customers.schedule.cancel', 'Cancel')}>\n <X className=\"size-4 text-muted-foreground\" />\n </IconButton>\n </div>\n\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"flex flex-col gap-4 bg-background p-6\">\n\n {/* Conflict warning */}\n {state.conflict && (\n <Alert variant=\"warning\" className=\"rounded-lg\">\n <AlertTriangle className=\"size-5\" />\n <AlertTitle>\n {t('customers.schedule.conflict.title', 'Calendar conflict')}\n </AlertTitle>\n <AlertDescription>{state.conflict}</AlertDescription>\n </Alert>\n )}\n\n {/* Type tabs \u2014 large rectangular tiles per Figma */}\n <div className=\"grid grid-cols-4 gap-2\">\n {TYPE_TABS.map(({ type, icon: Icon, labelKey, fallback }) => {\n const isActive = state.activityType === type\n return (\n <button\n key={type}\n type=\"button\"\n onClick={() => state.setActivityType(type)}\n aria-pressed={isActive}\n className={cn(\n 'flex h-[80px] flex-col items-center justify-center gap-2 rounded-md border text-[14px] font-semibold transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40 hover:text-foreground',\n )}\n >\n <Icon className=\"size-[18px]\" />\n {t(labelKey, fallback)}\n </button>\n )\n })}\n </div>\n\n {/* Title */}\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\">\n {getFieldLabel(state.activityType, 'title', t, 'customers.schedule.titleLabel', 'Title')}\n </label>\n <input\n type=\"text\"\n value={state.title}\n onChange={(e) => state.setTitle(e.target.value)}\n placeholder={\n state.activityType === 'email'\n ? t('customers.schedule.subjectPlaceholder', 'Subject...')\n : t('customers.schedule.titlePlaceholder', 'Activity title...')\n }\n className=\"w-full rounded-md border border-border bg-background px-3 py-2.5 text-sm text-foreground outline-none focus:border-foreground\"\n autoFocus\n />\n </div>\n\n {/* Date/Time/Duration \u2014 placed before per-type chip rows so the call/task\n workflows match Figma 829:50 / 790:280 (date row first, then status chips). */}\n <DateTimeFields\n visible={visibleFields}\n activityType={state.activityType}\n date={state.date}\n setDate={state.setDate}\n startTime={state.startTime}\n setStartTime={state.setStartTime}\n duration={state.duration}\n setDuration={state.setDuration}\n allDay={state.allDay}\n setAllDay={state.setAllDay}\n recurrenceEnabled={state.recurrenceEnabled}\n setRecurrenceEnabled={state.setRecurrenceEnabled}\n recurrenceDays={state.recurrenceDays}\n toggleRecurrenceDay={state.toggleRecurrenceDay}\n recurrenceEndType={state.recurrenceEndType}\n setRecurrenceEndType={state.setRecurrenceEndType}\n recurrenceCount={state.recurrenceCount}\n setRecurrenceCount={state.setRecurrenceCount}\n recurrenceEndDate={state.recurrenceEndDate}\n setRecurrenceEndDate={state.setRecurrenceEndDate}\n />\n\n {/* Call: Direction + Outcome chips */}\n {state.activityType === 'call' && (\n <div className=\"flex flex-col gap-3\">\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.directionLabel', 'Direction')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {CALL_DIRECTIONS.map((opt) => {\n const isActive = callDirection === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setCallDirection(opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.outcomeLabel', 'Outcome')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {CALL_OUTCOMES.map((opt) => {\n const isActive = callOutcome === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setCallOutcome(isActive ? null : opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n </div>\n )}\n\n {/* Task: Priority chips */}\n {state.activityType === 'task' && (\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.task.priorityLabel', 'Priority')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {TASK_PRIORITIES.map((opt) => {\n const isActive = taskPriority === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setTaskPriority(opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Participants */}\n <ParticipantsField\n visible={visibleFields}\n activityType={state.activityType}\n participants={state.participants}\n setParticipants={state.setParticipants}\n removeParticipant={state.removeParticipant}\n guestPermissions={state.guestPermissions}\n setGuestPermissions={state.setGuestPermissions}\n />\n\n {/* Location (or phone number for calls) */}\n {state.activityType === 'call' ? (\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor=\"schedule-call-phone\" className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.phoneLabel', 'Phone number')}\n </label>\n <PhoneNumberField\n id=\"schedule-call-phone\"\n value={callPhoneNumber}\n onValueChange={handleCallPhoneChange}\n placeholder={t('customers.schedule.call.phonePlaceholder', '+1 555 000 0000')}\n externalError={callPhoneError}\n invalidLabel={callPhoneInvalidMessage}\n minDigits={7}\n />\n </div>\n ) : (\n <LocationField\n visible={visibleFields}\n activityType={state.activityType}\n location={state.location}\n setLocation={state.setLocation}\n />\n )}\n\n {/* Linked Entities */}\n <LinkedEntitiesField\n visible={visibleFields}\n activityType={state.activityType}\n linkedEntities={state.linkedEntities}\n setLinkedEntities={state.setLinkedEntities}\n />\n\n {/* Description */}\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {getFieldLabel(state.activityType, 'description', t, 'customers.schedule.description', 'Description')}\n </label>\n <div className=\"mt-[8px]\">\n <SwitchableMarkdownInput\n value={state.description}\n onChange={state.setDescription}\n isMarkdownEnabled={state.markdownEnabled}\n height={120}\n placeholder={t('customers.schedule.descriptionPlaceholder', 'Add details...')}\n />\n </div>\n </div>\n\n {/* Reminder + Visibility */}\n <FooterFields\n visible={visibleFields}\n activityType={state.activityType}\n reminderMinutes={state.reminderMinutes}\n setReminderMinutes={state.setReminderMinutes}\n visibility={state.visibility}\n setVisibility={state.setVisibility}\n />\n\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex shrink-0 items-center justify-end gap-2.5 border-t border-border bg-muted/50 px-6 py-4\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { void guardedClose() }} className=\"rounded-md border border-input bg-background px-5 py-3 text-sm font-semibold text-foreground\">\n {t('customers.schedule.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSave} disabled={isSubmitDisabled} className=\"flex items-center gap-2 rounded-md bg-foreground px-5 py-3 text-sm font-semibold text-background hover:bg-foreground/90 disabled:opacity-50\">\n <SaveIcon className=\"size-3.5\" />\n {state.saving\n ? t('customers.schedule.saving', 'Saving...')\n : isEditing\n ? t('customers.schedule.update', 'Update activity')\n : t(chrome.saveKey, chrome.saveFallback)}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type { ScheduleActivityEditData }\n\nfunction buildRecurrenceRule(\n days: boolean[],\n endType: 'never' | 'count' | 'date',\n count: number,\n endDate: string,\n): string {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const selectedDays = days.map((active, i) => (active ? dayNames[i] : null)).filter(Boolean)\n let rule = `FREQ=WEEKLY;BYDAY=${selectedDays.join(',')}`\n if (endType === 'count') rule += `;COUNT=${count}`\n if (endType === 'date' && endDate) rule += `;UNTIL=${endDate.replace(/-/g, '')}T235959Z`\n return rule\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { Users, Phone, Check, Mail, Calendar, AlertTriangle, X, StickyNote } from 'lucide-react'\nimport { cn } from '@open-mercato/shared/lib/utils'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { validatePhoneNumber } from '@open-mercato/shared/lib/phone'\nimport { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'\nimport { buildOptimisticLockHeader, extractOptimisticLockConflict } from '@open-mercato/ui/backend/utils/optimisticLock'\nimport { mapCrudServerErrorToFormErrors } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport { useGuardedMutation } from '@open-mercato/ui/backend/injection/useGuardedMutation'\nimport { Alert, AlertDescription, AlertTitle } from '@open-mercato/ui/primitives/alert'\nimport { Button } from '@open-mercato/ui/primitives/button'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { Dialog, DialogContent, DialogTitle } from '@open-mercato/ui/primitives/dialog'\nimport { useDialogKeyHandler } from '@open-mercato/ui/hooks/useDialogKeyHandler'\nimport { VisuallyHidden } from '@radix-ui/react-visually-hidden'\nimport { PhoneNumberField, SwitchableMarkdownInput } from '@open-mercato/ui/backend/inputs'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport {\n useScheduleFormState,\n FIELD_VISIBILITY,\n getFieldLabel,\n DateTimeFields,\n ParticipantsField,\n LocationField,\n FooterFields,\n LinkedEntitiesField,\n} from './schedule'\nimport type { ActivityType, ScheduleActivityEditData } from './schedule'\n\nconst TYPE_TABS: Array<{ type: ActivityType; icon: React.ComponentType<{ className?: string }>; labelKey: string; fallback: string }> = [\n { type: 'meeting', icon: Users, labelKey: 'customers.schedule.types.meeting', fallback: 'Meeting' },\n { type: 'call', icon: Phone, labelKey: 'customers.schedule.types.call', fallback: 'Call' },\n { type: 'task', icon: Check, labelKey: 'customers.schedule.types.task', fallback: 'Task' },\n { type: 'email', icon: Mail, labelKey: 'customers.schedule.types.email', fallback: 'Email' },\n { type: 'note', icon: StickyNote, labelKey: 'customers.schedule.types.note', fallback: 'Note' },\n]\n\ntype DialogChrome = { titleKey: string; titleFallback: string; subtitleKey: string; subtitleFallback: string; saveKey: string; saveFallback: string; saveIcon: React.ComponentType<{ className?: string }> }\n\nconst TYPE_CHROME: Record<ActivityType, DialogChrome> = {\n meeting: {\n titleKey: 'customers.schedule.meeting.title', titleFallback: 'New meeting',\n subtitleKey: 'customers.schedule.meeting.subtitle', subtitleFallback: 'Block time on the calendar with attendees',\n saveKey: 'customers.schedule.meeting.save', saveFallback: 'Save activity', saveIcon: Calendar,\n },\n call: {\n titleKey: 'customers.schedule.call.title', titleFallback: 'Log call',\n subtitleKey: 'customers.schedule.call.subtitle', subtitleFallback: 'Log a call you just had or schedule one',\n saveKey: 'customers.schedule.call.save', saveFallback: 'Log call', saveIcon: Phone,\n },\n task: {\n titleKey: 'customers.schedule.task.title', titleFallback: 'New task',\n subtitleKey: 'customers.schedule.task.subtitle', subtitleFallback: 'Capture something to follow up on',\n saveKey: 'customers.schedule.task.save', saveFallback: 'Save task', saveIcon: Check,\n },\n email: {\n titleKey: 'customers.schedule.email.title', titleFallback: 'Compose email',\n subtitleKey: 'customers.schedule.email.subtitle', subtitleFallback: 'Compose and send a tracked email',\n saveKey: 'customers.schedule.email.save', saveFallback: 'Send email', saveIcon: Mail,\n },\n note: {\n titleKey: 'customers.schedule.note.title', titleFallback: 'Add note',\n subtitleKey: 'customers.schedule.note.subtitle', subtitleFallback: 'Write down a note about this interaction',\n saveKey: 'customers.schedule.note.save', saveFallback: 'Save note', saveIcon: StickyNote,\n },\n}\n\nconst CALL_DIRECTIONS: Array<{ key: 'outbound' | 'inbound'; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'outbound', labelKey: 'customers.schedule.call.direction.outbound', labelFallback: 'Outbound', dot: 'bg-status-info-icon' },\n { key: 'inbound', labelKey: 'customers.schedule.call.direction.inbound', labelFallback: 'Inbound', dot: 'bg-status-success-icon' },\n]\n\nconst CALL_OUTCOMES: Array<{ key: string; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'connected', labelKey: 'customers.schedule.call.outcome.connected', labelFallback: 'Connected', dot: 'bg-status-success-icon' },\n { key: 'voicemail', labelKey: 'customers.schedule.call.outcome.voicemail', labelFallback: 'Voicemail', dot: 'bg-status-warning-icon' },\n { key: 'noanswer', labelKey: 'customers.schedule.call.outcome.noAnswer', labelFallback: 'No answer', dot: 'bg-muted-foreground' },\n { key: 'busy', labelKey: 'customers.schedule.call.outcome.busy', labelFallback: 'Busy', dot: 'bg-status-warning-icon' },\n { key: 'badnumber', labelKey: 'customers.schedule.call.outcome.badNumber', labelFallback: 'Bad number', dot: 'bg-status-error-icon' },\n]\n\nconst TASK_PRIORITIES: Array<{ key: string; labelKey: string; labelFallback: string; dot: string }> = [\n { key: 'low', labelKey: 'customers.schedule.task.priority.low', labelFallback: 'Low', dot: 'bg-muted-foreground' },\n { key: 'medium', labelKey: 'customers.schedule.task.priority.medium', labelFallback: 'Medium', dot: 'bg-status-info-icon' },\n { key: 'high', labelKey: 'customers.schedule.task.priority.high', labelFallback: 'High', dot: 'bg-status-warning-icon' },\n { key: 'urgent', labelKey: 'customers.schedule.task.priority.urgent', labelFallback: 'Urgent', dot: 'bg-status-error-icon' },\n]\n\ninterface ScheduleActivityDialogProps {\n open: boolean\n onClose: () => void\n entityId: string\n dealId?: string | null\n entityName?: string\n companyName?: string | null\n entityType: 'company' | 'person' | 'deal'\n onActivityCreated?: () => void\n /** When provided, dialog opens in edit mode with pre-filled data */\n editData?: ScheduleActivityEditData | null\n}\n\nexport function ScheduleActivityDialog({\n open,\n onClose,\n entityId,\n dealId = null,\n entityName,\n companyName,\n entityType,\n onActivityCreated,\n editData,\n}: ScheduleActivityDialogProps) {\n const t = useT()\n const state = useScheduleFormState({ open, editData: editData ?? null })\n const visibleFields = FIELD_VISIBILITY[state.activityType]\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const isEditing = Boolean(editData?.id)\n const chrome = TYPE_CHROME[state.activityType]\n const SaveIcon = chrome.saveIcon\n const [callDirection, setCallDirection] = React.useState<'outbound' | 'inbound'>('outbound')\n const [callOutcome, setCallOutcome] = React.useState<string | null>(null)\n const [callPhoneNumber, setCallPhoneNumber] = React.useState('')\n const [callPhoneError, setCallPhoneError] = React.useState<string | null>(null)\n const [taskPriority, setTaskPriority] = React.useState<string>('medium')\n const callPhoneInvalidMessage = React.useMemo(\n () =>\n t(\n 'customers.activities.errors.phoneInvalid',\n 'Enter a valid phone number with country code (e.g. +1 212 555 1234)',\n ),\n [t],\n )\n const translateErrorMessage = React.useCallback(\n (message: string | undefined, fallback: string) => {\n const key = typeof message === 'string' ? message.trim() : ''\n return key ? t(key, key) : fallback\n },\n [t],\n )\n\n React.useEffect(() => {\n if (!open) return\n const raw = editData as (Record<string, unknown> & { customValues?: unknown; phoneNumber?: unknown }) | null | undefined\n const cv = (raw?.customValues && typeof raw.customValues === 'object' ? raw.customValues : null) as Record<string, unknown> | null\n setCallDirection(typeof cv?.callDirection === 'string' && cv.callDirection === 'inbound' ? 'inbound' : 'outbound')\n setCallOutcome(typeof cv?.callOutcome === 'string' ? cv.callOutcome : null)\n // Seed phone number from either top-level `phoneNumber` (newer write path)\n // or legacy `customValues.callPhoneNumber` so previously-saved calls still\n // round-trip on edit (#1808).\n const seededPhone =\n typeof raw?.phoneNumber === 'string' && raw.phoneNumber.trim().length > 0\n ? raw.phoneNumber\n : typeof cv?.callPhoneNumber === 'string'\n ? cv.callPhoneNumber\n : ''\n setCallPhoneNumber(seededPhone)\n setCallPhoneError(null)\n setTaskPriority(typeof cv?.taskPriority === 'string' ? cv.taskPriority : 'medium')\n }, [open, editData])\n\n // Reset per-type chip state when the user switches activity type in create mode.\n // In edit mode, the persisted customValues should win, so we skip the reset.\n React.useEffect(() => {\n if (!open || isEditing) return\n setCallDirection('outbound')\n setCallOutcome(null)\n setCallPhoneNumber('')\n setCallPhoneError(null)\n setTaskPriority('medium')\n }, [state.activityType, open, isEditing])\n\n const handleCallPhoneChange = React.useCallback((next: string | undefined) => {\n setCallPhoneNumber(next ?? '')\n setCallPhoneError(null)\n }, [])\n\n const formSnapshot = React.useMemo(() => JSON.stringify({\n activityType: state.activityType,\n title: state.title,\n date: state.date,\n startTime: state.startTime,\n duration: state.duration,\n allDay: state.allDay,\n description: state.description,\n location: state.location,\n reminderMinutes: state.reminderMinutes,\n visibility: state.visibility,\n participants: state.participants,\n linkedEntities: state.linkedEntities,\n recurrenceEnabled: state.recurrenceEnabled,\n recurrenceDays: state.recurrenceDays,\n recurrenceEndType: state.recurrenceEndType,\n recurrenceCount: state.recurrenceCount,\n recurrenceEndDate: state.recurrenceEndDate,\n guestPermissions: state.guestPermissions,\n }), [\n state.activityType, state.title, state.date, state.startTime, state.duration, state.allDay,\n state.description, state.location, state.reminderMinutes, state.visibility, state.participants,\n state.linkedEntities, state.recurrenceEnabled, state.recurrenceDays, state.recurrenceEndType,\n state.recurrenceCount, state.recurrenceEndDate, state.guestPermissions,\n ])\n const initialSnapshotRef = React.useRef<string | null>(null)\n const snapshotOpenKeyRef = React.useRef<string | null>(null)\n const snapshotSettleCountRef = React.useRef(0)\n const openKey = open ? `${editData?.id ?? 'new'}` : null\n React.useEffect(() => {\n if (!open) {\n initialSnapshotRef.current = null\n snapshotOpenKeyRef.current = null\n snapshotSettleCountRef.current = 0\n return\n }\n if (snapshotOpenKeyRef.current !== openKey) {\n snapshotOpenKeyRef.current = openKey\n snapshotSettleCountRef.current = 0\n initialSnapshotRef.current = null\n }\n if (snapshotSettleCountRef.current < 2) {\n initialSnapshotRef.current = formSnapshot\n snapshotSettleCountRef.current += 1\n }\n }, [open, openKey, formSnapshot])\n\n const isDirty = React.useCallback(() => {\n if (initialSnapshotRef.current == null) return false\n return initialSnapshotRef.current !== formSnapshot\n }, [formSnapshot])\n\n const guardedClose = React.useCallback(async () => {\n if (!isDirty()) {\n onClose()\n return\n }\n const ok = await confirm({\n title: t('customers.schedule.discardConfirm.title', 'Discard unsaved changes?'),\n description: t(\n 'customers.schedule.discardConfirm.description',\n 'You have unsaved edits in this activity. Save them first or continue to discard them.',\n ),\n confirmText: t('customers.schedule.discardConfirm.confirm', 'Discard'),\n cancelText: t('customers.schedule.discardConfirm.cancel', 'Keep editing'),\n variant: 'destructive',\n })\n if (ok) onClose()\n }, [confirm, isDirty, onClose, t])\n\n const mutationContextId = React.useMemo(\n () => `customer-activity:${entityType}:${entityId}`,\n [entityId, entityType],\n )\n const { runMutation, retryLastMutation } = useGuardedMutation<{\n formId: string\n resourceKind: string\n resourceId: string\n entityType: 'company' | 'person' | 'deal'\n retryLastMutation: () => Promise<boolean>\n }>({\n contextId: mutationContextId,\n blockedMessage: t('ui.forms.flash.saveBlocked', 'Save blocked by validation'),\n })\n const mutationContext = React.useMemo(\n () => ({\n formId: mutationContextId,\n resourceKind:\n entityType === 'company'\n ? 'customers.company'\n : entityType === 'person'\n ? 'customers.person'\n : 'customers.deal',\n resourceId: entityId,\n entityType,\n retryLastMutation,\n }),\n [entityId, entityType, mutationContextId, retryLastMutation],\n )\n const runGuardedMutation = React.useCallback(\n async <T,>(operation: () => Promise<T>, mutationPayload: Record<string, unknown>) =>\n runMutation({\n operation,\n mutationPayload,\n context: mutationContext,\n }),\n [mutationContext, runMutation],\n )\n\n // Conflict detection -- debounced check when date/time/duration changes\n React.useEffect(() => {\n if (!open || state.allDay || !state.date || !state.startTime) {\n state.setConflict(null)\n return\n }\n const timer = setTimeout(async () => {\n try {\n const localStart = new Date(`${state.date}T${state.startTime}:00`)\n const params = new URLSearchParams({\n date: state.date,\n startTime: state.startTime,\n duration: String(state.duration),\n })\n if (editData?.id) {\n params.set('excludeId', editData.id)\n }\n if (!Number.isNaN(localStart.getTime())) {\n params.set('timezoneOffsetMinutes', String(-localStart.getTimezoneOffset()))\n }\n const data = await readApiResultOrThrow<{\n hasConflicts: boolean\n conflicts: Array<{ id: string; title: string | null; startTime: string; endTime: string; type: string }>\n }>(`/api/customers/interactions/conflicts?${params.toString()}`)\n if (data?.hasConflicts && Array.isArray(data.conflicts) && data.conflicts.length > 0) {\n const descriptions = data.conflicts\n .map((c) => `${c.startTime}\u2013${c.endTime}: ${c.title ?? c.type}`)\n .join(', ')\n state.setConflict(\n t('customers.schedule.conflict.description', 'Overlaps with: {{items}}', { items: descriptions }),\n )\n } else {\n state.setConflict(null)\n }\n } catch {\n state.setConflict(null)\n }\n }, 500)\n return () => clearTimeout(timer)\n }, [editData?.id, open, state.date, state.startTime, state.duration, state.allDay, t]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const trimmedDate = state.date.trim()\n const trimmedStartTime = state.startTime.trim()\n const trimmedCallPhone = callPhoneNumber.trim()\n const isDateMissing = !trimmedDate\n const isTimeMissing = !state.allDay && !trimmedStartTime\n const isSubmitDisabled =\n state.saving ||\n !state.title.trim() ||\n isDateMissing ||\n isTimeMissing\n\n const handleSave = React.useCallback(async () => {\n if (!state.title.trim()) return\n if (isDateMissing) {\n flash(t('customers.activities.errors.dateRequired', 'Date is required'), 'error')\n return\n }\n if (isTimeMissing) {\n flash(t('customers.activities.errors.timeRequired', 'Time is required'), 'error')\n return\n }\n let phoneNumberForPayload: string | undefined\n if (state.activityType === 'call' && trimmedCallPhone) {\n const phoneValidation = validatePhoneNumber(trimmedCallPhone)\n if (!phoneValidation.valid) {\n setCallPhoneError(callPhoneInvalidMessage)\n flash(callPhoneInvalidMessage, 'error')\n return\n }\n phoneNumberForPayload = phoneValidation.normalized ?? trimmedCallPhone\n setCallPhoneError(null)\n if (phoneNumberForPayload !== callPhoneNumber) {\n setCallPhoneNumber(phoneNumberForPayload)\n }\n }\n state.setSaving(true)\n try {\n const scheduledAt = state.allDay\n ? new Date(`${state.date}T00:00:00`).toISOString()\n : new Date(`${state.date}T${state.startTime}:00`).toISOString()\n\n const recurrenceRule = state.recurrenceEnabled\n ? buildRecurrenceRule(state.recurrenceDays, state.recurrenceEndType, state.recurrenceCount, state.recurrenceEndDate)\n : null\n\n const isSaveEdit = Boolean(editData?.id)\n const customValues: Record<string, unknown> = {}\n if (state.activityType === 'call') {\n customValues.callDirection = callDirection\n if (callOutcome) customValues.callOutcome = callOutcome\n if (phoneNumberForPayload) customValues.callPhoneNumber = phoneNumberForPayload\n }\n if (state.activityType === 'task') {\n customValues.taskPriority = taskPriority\n }\n const payload = {\n ...(isSaveEdit ? { id: editData!.id } : {}),\n entityId,\n dealId,\n interactionType: state.activityType,\n title: state.title.trim(),\n body: state.description.trim() || null,\n status: 'planned',\n date: trimmedDate,\n time: state.allDay ? '00:00' : trimmedStartTime,\n phoneNumber: state.activityType === 'call' ? phoneNumberForPayload : undefined,\n scheduledAt,\n durationMinutes: visibleFields.has('duration') && !state.allDay ? state.duration : null,\n location: visibleFields.has('location') ? (state.location.trim() || null) : null,\n allDay: visibleFields.has('allDay') ? state.allDay : null,\n recurrenceRule: visibleFields.has('recurrence') ? recurrenceRule : null,\n recurrenceEnd: visibleFields.has('recurrence') && state.recurrenceEndType === 'date' && state.recurrenceEndDate\n ? new Date(state.recurrenceEndDate).toISOString()\n : null,\n participants: visibleFields.has('participants') && state.participants.length > 0\n ? state.participants.map((p) => ({ userId: p.userId, name: p.name, email: p.email, status: p.status ?? 'pending' }))\n : null,\n guestPermissions: visibleFields.has('participants') && state.participants.length > 0 ? state.guestPermissions : null,\n linkedEntities: state.linkedEntities.length > 0\n ? state.linkedEntities.map((e) => ({ id: e.id, type: e.type, label: e.label }))\n : null,\n reminderMinutes: visibleFields.has('reminder') ? state.reminderMinutes : null,\n visibility: visibleFields.has('visibility') ? state.visibility : null,\n ...(Object.keys(customValues).length > 0 ? { customValues } : {}),\n }\n await runGuardedMutation(\n () => {\n const call = () =>\n apiCallOrThrow('/api/customers/interactions', {\n method: isSaveEdit ? 'PUT' : 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(payload),\n })\n // Optimistic lock only applies to edits \u2014 a stale modal save against a\n // concurrently changed/deleted activity surfaces the conflict bar (#2055).\n return isSaveEdit\n ? withScopedApiRequestHeaders(buildOptimisticLockHeader(editData?.updatedAt), call)\n : call()\n },\n {\n operation: isSaveEdit ? 'updateActivity' : 'createActivity',\n interactionId: editData?.id ?? null,\n interactionType: state.activityType,\n },\n )\n flash(t('customers.schedule.saved', 'Activity scheduled'), 'success')\n onClose()\n // Delay data reload so the dialog can unmount cleanly and Radix restores body scroll\n requestAnimationFrame(() => { onActivityCreated?.() })\n } catch (err) {\n // An optimistic-lock 409 was already surfaced as the persistent conflict\n // bar by `runGuardedMutation` (useGuardedMutation \u2192 surfaceRecordConflict).\n // Do NOT additionally flash the raw `record_modified` code here \u2014 that\n // showed an untranslated toast on top of the localized bar (#2055 QA).\n if (extractOptimisticLockConflict(err)) {\n onClose()\n return\n }\n const { message, fieldErrors } = mapCrudServerErrorToFormErrors(err)\n const phoneFieldError = fieldErrors?.phoneNumber\n if (state.activityType === 'call' && phoneFieldError) {\n const translatedPhoneError = translateErrorMessage(phoneFieldError, callPhoneInvalidMessage)\n setCallPhoneError(translatedPhoneError)\n flash(translatedPhoneError, 'error')\n return\n }\n flash(\n translateErrorMessage(message, t('customers.schedule.error', 'Failed to schedule activity')),\n 'error',\n )\n } finally {\n state.setSaving(false)\n }\n }, [callDirection, callOutcome, callPhoneInvalidMessage, callPhoneNumber, isDateMissing, isTimeMissing, state.activityType, state.allDay, state.date, state.description, dealId, state.duration, editData, entityId, state.guestPermissions, state.linkedEntities, state.location, onActivityCreated, onClose, state.participants, state.recurrenceCount, state.recurrenceDays, state.recurrenceEnabled, state.recurrenceEndDate, state.recurrenceEndType, state.reminderMinutes, runGuardedMutation, state.startTime, t, taskPriority, state.title, translateErrorMessage, trimmedCallPhone, trimmedDate, trimmedStartTime, state.visibility, visibleFields]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleKeyDown = useDialogKeyHandler({ onConfirm: handleSave })\n\n return (\n <Dialog open={open} onOpenChange={(o) => { if (!o) void guardedClose() }}>\n {ConfirmDialogElement}\n <DialogContent className=\"flex max-h-[90vh] flex-col overflow-hidden border-border p-0 shadow-xl sm:max-w-[760px] sm:rounded-xl [&>[data-dialog-close]]:hidden\" onKeyDown={handleKeyDown} aria-describedby={undefined}>\n <VisuallyHidden>\n <DialogTitle>{isEditing ? t('customers.schedule.editTitle', 'Edit activity') : t(chrome.titleKey, chrome.titleFallback)}</DialogTitle>\n </VisuallyHidden>\n\n {/* Header */}\n <div className=\"flex shrink-0 items-start justify-between gap-3 border-b border-border bg-background px-6 py-5\">\n <div className=\"flex flex-col gap-1\">\n <h2 className=\"text-lg font-semibold leading-tight tracking-tight text-foreground\">\n {isEditing ? t('customers.schedule.editTitle', 'Edit activity') : t(chrome.titleKey, chrome.titleFallback)}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(chrome.subtitleKey, chrome.subtitleFallback)}\n </p>\n {entityName ? (\n <p className=\"mt-0.5 text-xs text-muted-foreground/80\">\n {t('customers.schedule.context', 'On timeline: {{name}}', { name: entityName })}\n {companyName ? ` \u00B7 ${companyName}` : ''}\n </p>\n ) : null}\n </div>\n <IconButton type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => { void guardedClose() }} className=\"flex size-9 shrink-0 items-center justify-center rounded-md border border-border bg-background\" aria-label={t('customers.schedule.cancel', 'Cancel')}>\n <X className=\"size-4 text-muted-foreground\" />\n </IconButton>\n </div>\n\n <div className=\"flex-1 overflow-y-auto\">\n <div className=\"flex flex-col gap-4 bg-background p-6\">\n\n {/* Conflict warning */}\n {state.conflict && (\n <Alert variant=\"warning\" className=\"rounded-lg\">\n <AlertTriangle className=\"size-5\" />\n <AlertTitle>\n {t('customers.schedule.conflict.title', 'Calendar conflict')}\n </AlertTitle>\n <AlertDescription>{state.conflict}</AlertDescription>\n </Alert>\n )}\n\n {/* Type tabs \u2014 large rectangular tiles per Figma */}\n <div className=\"grid grid-cols-4 gap-2\">\n {TYPE_TABS.map(({ type, icon: Icon, labelKey, fallback }) => {\n const isActive = state.activityType === type\n return (\n <button\n key={type}\n type=\"button\"\n onClick={() => state.setActivityType(type)}\n aria-pressed={isActive}\n className={cn(\n 'flex h-[80px] flex-col items-center justify-center gap-2 rounded-md border text-[14px] font-semibold transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40 hover:text-foreground',\n )}\n >\n <Icon className=\"size-[18px]\" />\n {t(labelKey, fallback)}\n </button>\n )\n })}\n </div>\n\n {/* Title */}\n <div className=\"flex flex-col gap-1.5\">\n <label className=\"text-overline font-semibold text-muted-foreground tracking-wider\">\n {getFieldLabel(state.activityType, 'title', t, 'customers.schedule.titleLabel', 'Title')}\n </label>\n <input\n type=\"text\"\n value={state.title}\n onChange={(e) => state.setTitle(e.target.value)}\n placeholder={\n state.activityType === 'email'\n ? t('customers.schedule.subjectPlaceholder', 'Subject...')\n : t('customers.schedule.titlePlaceholder', 'Activity title...')\n }\n className=\"w-full rounded-md border border-border bg-background px-3 py-2.5 text-sm text-foreground outline-none focus:border-foreground\"\n autoFocus\n />\n </div>\n\n {/* Date/Time/Duration \u2014 placed before per-type chip rows so the call/task\n workflows match Figma 829:50 / 790:280 (date row first, then status chips). */}\n <DateTimeFields\n visible={visibleFields}\n activityType={state.activityType}\n date={state.date}\n setDate={state.setDate}\n startTime={state.startTime}\n setStartTime={state.setStartTime}\n duration={state.duration}\n setDuration={state.setDuration}\n allDay={state.allDay}\n setAllDay={state.setAllDay}\n recurrenceEnabled={state.recurrenceEnabled}\n setRecurrenceEnabled={state.setRecurrenceEnabled}\n recurrenceDays={state.recurrenceDays}\n toggleRecurrenceDay={state.toggleRecurrenceDay}\n recurrenceEndType={state.recurrenceEndType}\n setRecurrenceEndType={state.setRecurrenceEndType}\n recurrenceCount={state.recurrenceCount}\n setRecurrenceCount={state.setRecurrenceCount}\n recurrenceEndDate={state.recurrenceEndDate}\n setRecurrenceEndDate={state.setRecurrenceEndDate}\n />\n\n {/* Call: Direction + Outcome chips */}\n {state.activityType === 'call' && (\n <div className=\"flex flex-col gap-3\">\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.directionLabel', 'Direction')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {CALL_DIRECTIONS.map((opt) => {\n const isActive = callDirection === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setCallDirection(opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.outcomeLabel', 'Outcome')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {CALL_OUTCOMES.map((opt) => {\n const isActive = callOutcome === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setCallOutcome(isActive ? null : opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n </div>\n )}\n\n {/* Task: Priority chips */}\n {state.activityType === 'task' && (\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.task.priorityLabel', 'Priority')}\n </label>\n <div className=\"mt-2 flex flex-wrap gap-2\">\n {TASK_PRIORITIES.map((opt) => {\n const isActive = taskPriority === opt.key\n return (\n <button\n key={opt.key}\n type=\"button\"\n aria-pressed={isActive}\n onClick={() => setTaskPriority(opt.key)}\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-md border px-2.5 py-1.5 text-sm font-medium transition-colors',\n isActive\n ? 'border-transparent bg-foreground text-background'\n : 'border-border bg-card text-muted-foreground hover:border-foreground/40',\n )}\n >\n <span className={cn('inline-block size-1.5 rounded-full', opt.dot)} aria-hidden />\n {t(opt.labelKey, opt.labelFallback)}\n </button>\n )\n })}\n </div>\n </div>\n )}\n\n {/* Participants */}\n <ParticipantsField\n visible={visibleFields}\n activityType={state.activityType}\n participants={state.participants}\n setParticipants={state.setParticipants}\n removeParticipant={state.removeParticipant}\n guestPermissions={state.guestPermissions}\n setGuestPermissions={state.setGuestPermissions}\n />\n\n {/* Location (or phone number for calls) */}\n {state.activityType === 'call' ? (\n <div className=\"flex flex-col gap-1.5\">\n <label htmlFor=\"schedule-call-phone\" className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {t('customers.schedule.call.phoneLabel', 'Phone number')}\n </label>\n <PhoneNumberField\n id=\"schedule-call-phone\"\n value={callPhoneNumber}\n onValueChange={handleCallPhoneChange}\n placeholder={t('customers.schedule.call.phonePlaceholder', '+1 555 000 0000')}\n externalError={callPhoneError}\n invalidLabel={callPhoneInvalidMessage}\n minDigits={7}\n />\n </div>\n ) : (\n <LocationField\n visible={visibleFields}\n activityType={state.activityType}\n location={state.location}\n setLocation={state.setLocation}\n />\n )}\n\n {/* Linked Entities */}\n <LinkedEntitiesField\n visible={visibleFields}\n activityType={state.activityType}\n linkedEntities={state.linkedEntities}\n setLinkedEntities={state.setLinkedEntities}\n />\n\n {/* Description */}\n <div>\n <label className=\"text-overline font-semibold uppercase text-muted-foreground tracking-wider\">\n {getFieldLabel(state.activityType, 'description', t, 'customers.schedule.description', 'Description')}\n </label>\n <div className=\"mt-[8px]\">\n <SwitchableMarkdownInput\n value={state.description}\n onChange={state.setDescription}\n isMarkdownEnabled={state.markdownEnabled}\n height={120}\n placeholder={t('customers.schedule.descriptionPlaceholder', 'Add details...')}\n />\n </div>\n </div>\n\n {/* Reminder + Visibility */}\n <FooterFields\n visible={visibleFields}\n activityType={state.activityType}\n reminderMinutes={state.reminderMinutes}\n setReminderMinutes={state.setReminderMinutes}\n visibility={state.visibility}\n setVisibility={state.setVisibility}\n />\n\n </div>\n </div>\n\n {/* Footer */}\n <div className=\"flex shrink-0 items-center justify-end gap-2.5 border-t border-border bg-muted/50 px-6 py-4\">\n <Button type=\"button\" variant=\"outline\" onClick={() => { void guardedClose() }} className=\"rounded-md border border-input bg-background px-5 py-3 text-sm font-semibold text-foreground\">\n {t('customers.schedule.cancel', 'Cancel')}\n </Button>\n <Button type=\"button\" onClick={handleSave} disabled={isSubmitDisabled} className=\"flex items-center gap-2 rounded-md bg-foreground px-5 py-3 text-sm font-semibold text-background hover:bg-foreground/90 disabled:opacity-50\">\n <SaveIcon className=\"size-3.5\" />\n {state.saving\n ? t('customers.schedule.saving', 'Saving...')\n : isEditing\n ? t('customers.schedule.update', 'Update activity')\n : t(chrome.saveKey, chrome.saveFallback)}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n )\n}\n\nexport type { ScheduleActivityEditData }\n\nfunction buildRecurrenceRule(\n days: boolean[],\n endType: 'never' | 'count' | 'date',\n count: number,\n endDate: string,\n): string {\n const dayNames = ['MO', 'TU', 'WE', 'TH', 'FR', 'SA', 'SU']\n const selectedDays = days.map((active, i) => (active ? dayNames[i] : null)).filter(Boolean)\n let rule = `FREQ=WEEKLY;BYDAY=${selectedDays.join(',')}`\n if (endType === 'count') rule += `;COUNT=${count}`\n if (endType === 'date' && endDate) rule += `;UNTIL=${endDate.replace(/-/g, '')}T235959Z`\n return rule\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }