@open-mercato/core 0.6.5-develop.4534.1.b459babe6d → 0.6.5-develop.4544.1.71c003c861

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 (633) 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/optimisticLockUi.js +104 -0
  10. package/dist/helpers/integration/optimisticLockUi.js.map +7 -0
  11. package/dist/helpers/integration/salesFixtures.js +17 -0
  12. package/dist/helpers/integration/salesFixtures.js.map +2 -2
  13. package/dist/modules/api_keys/backend/api-keys/page.js +9 -5
  14. package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
  15. package/dist/modules/attachments/components/AttachmentPartitionSettings.js +17 -9
  16. package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
  17. package/dist/modules/auth/api/roles/acl/route.js +32 -13
  18. package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
  19. package/dist/modules/auth/api/roles/route.js +3 -1
  20. package/dist/modules/auth/api/roles/route.js.map +2 -2
  21. package/dist/modules/auth/api/sidebar/preferences/route.js +71 -3
  22. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  23. package/dist/modules/auth/api/users/acl/route.js +42 -19
  24. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  25. package/dist/modules/auth/api/users/route.js +3 -1
  26. package/dist/modules/auth/api/users/route.js.map +2 -2
  27. package/dist/modules/auth/backend/roles/[id]/edit/page.js +24 -4
  28. package/dist/modules/auth/backend/roles/[id]/edit/page.js.map +2 -2
  29. package/dist/modules/auth/backend/roles/page.js +8 -4
  30. package/dist/modules/auth/backend/roles/page.js.map +2 -2
  31. package/dist/modules/auth/backend/users/[id]/edit/page.js +27 -5
  32. package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
  33. package/dist/modules/auth/backend/users/page.js +6 -2
  34. package/dist/modules/auth/backend/users/page.js.map +2 -2
  35. package/dist/modules/auth/components/AclEditor.js +3 -1
  36. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  37. package/dist/modules/auth/data/entities.js +6 -0
  38. package/dist/modules/auth/data/entities.js.map +2 -2
  39. package/dist/modules/auth/services/sidebarPreferencesService.js +32 -4
  40. package/dist/modules/auth/services/sidebarPreferencesService.js.map +2 -2
  41. package/dist/modules/business_rules/api/rules/route.js +28 -0
  42. package/dist/modules/business_rules/api/rules/route.js.map +2 -2
  43. package/dist/modules/business_rules/api/sets/route.js +28 -0
  44. package/dist/modules/business_rules/api/sets/route.js.map +2 -2
  45. package/dist/modules/business_rules/backend/rules/[id]/page.js +11 -4
  46. package/dist/modules/business_rules/backend/rules/[id]/page.js.map +3 -3
  47. package/dist/modules/business_rules/backend/rules/page.js +20 -11
  48. package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
  49. package/dist/modules/business_rules/backend/sets/[id]/page.js +11 -4
  50. package/dist/modules/business_rules/backend/sets/[id]/page.js.map +2 -2
  51. package/dist/modules/business_rules/backend/sets/page.js +20 -11
  52. package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
  53. package/dist/modules/catalog/api/categories/route.js +2 -0
  54. package/dist/modules/catalog/api/categories/route.js.map +2 -2
  55. package/dist/modules/catalog/api/products/route.js +2 -1
  56. package/dist/modules/catalog/api/products/route.js.map +2 -2
  57. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js +2 -0
  58. package/dist/modules/catalog/backend/catalog/categories/[id]/edit/page.js.map +2 -2
  59. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +94 -40
  60. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  61. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +37 -8
  62. package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
  63. package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js.map +2 -2
  64. package/dist/modules/catalog/commands/variants.js +32 -31
  65. package/dist/modules/catalog/commands/variants.js.map +2 -2
  66. package/dist/modules/catalog/components/PriceKindSettings.js +12 -5
  67. package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
  68. package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
  69. package/dist/modules/catalog/components/products/ProductMediaManager.js.map +2 -2
  70. package/dist/modules/catalog/components/products/ProductsDataTable.js +5 -3
  71. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  72. package/dist/modules/catalog/components/products/productForm.js.map +2 -2
  73. package/dist/modules/catalog/components/products/variantForm.js +2 -1
  74. package/dist/modules/catalog/components/products/variantForm.js.map +2 -2
  75. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js +5 -0
  76. package/dist/modules/communication_channels/backend/profile/communication-channels/page.js.map +2 -2
  77. package/dist/modules/currencies/backend/currencies/[id]/page.js +6 -3
  78. package/dist/modules/currencies/backend/currencies/[id]/page.js.map +2 -2
  79. package/dist/modules/currencies/backend/currencies/page.js +18 -11
  80. package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
  81. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js +1 -0
  82. package/dist/modules/currencies/backend/exchange-rates/[id]/page.js.map +2 -2
  83. package/dist/modules/currencies/backend/exchange-rates/page.js +10 -6
  84. package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
  85. package/dist/modules/currencies/commands/currencies.js +7 -5
  86. package/dist/modules/currencies/commands/currencies.js.map +2 -2
  87. package/dist/modules/currencies/components/CurrencyFetchingConfig.js +26 -19
  88. package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
  89. package/dist/modules/customer_accounts/api/admin/roles/[id].js +28 -5
  90. package/dist/modules/customer_accounts/api/admin/roles/[id].js.map +2 -2
  91. package/dist/modules/customer_accounts/api/admin/roles.js +4 -2
  92. package/dist/modules/customer_accounts/api/admin/roles.js.map +2 -2
  93. package/dist/modules/customer_accounts/api/admin/users/[id].js +28 -5
  94. package/dist/modules/customer_accounts/api/admin/users/[id].js.map +2 -2
  95. package/dist/modules/customer_accounts/api/admin/users.js +2 -0
  96. package/dist/modules/customer_accounts/api/admin/users.js.map +2 -2
  97. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js +16 -8
  98. package/dist/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.js.map +2 -2
  99. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +8 -4
  100. package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
  101. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js +8 -4
  102. package/dist/modules/customer_accounts/backend/customer_accounts/settings/domain/page.js.map +2 -2
  103. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js +29 -18
  104. package/dist/modules/customer_accounts/backend/customer_accounts/users/[id]/page.js.map +2 -2
  105. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js +18 -11
  106. package/dist/modules/customer_accounts/backend/customer_accounts/users/page.js.map +2 -2
  107. package/dist/modules/customers/api/companies/route.js +13 -2
  108. package/dist/modules/customers/api/companies/route.js.map +2 -2
  109. package/dist/modules/customers/api/deals/route.js +2 -0
  110. package/dist/modules/customers/api/deals/route.js.map +2 -2
  111. package/dist/modules/customers/api/people/route.js +11 -2
  112. package/dist/modules/customers/api/people/route.js.map +2 -2
  113. package/dist/modules/customers/api/todos/route.js +1 -0
  114. package/dist/modules/customers/api/todos/route.js.map +2 -2
  115. package/dist/modules/customers/backend/config/customers/deals/page.js.map +2 -2
  116. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js +34 -21
  117. package/dist/modules/customers/backend/config/customers/pipeline-stages/page.js.map +2 -2
  118. package/dist/modules/customers/backend/customers/companies/[id]/page.js +45 -27
  119. package/dist/modules/customers/backend/customers/companies/[id]/page.js.map +2 -2
  120. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  121. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +22 -5
  122. package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
  123. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js +30 -8
  124. package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.js.map +2 -2
  125. package/dist/modules/customers/backend/customers/deals/[id]/page.js +1 -0
  126. package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
  127. package/dist/modules/customers/backend/customers/deals/page.js +16 -6
  128. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  129. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +62 -39
  130. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  131. package/dist/modules/customers/backend/customers/people/[id]/page.js +41 -26
  132. package/dist/modules/customers/backend/customers/people/[id]/page.js.map +2 -2
  133. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  134. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +50 -23
  135. package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
  136. package/dist/modules/customers/commands/addresses.js +16 -14
  137. package/dist/modules/customers/commands/addresses.js.map +2 -2
  138. package/dist/modules/customers/commands/companies.js +1 -1
  139. package/dist/modules/customers/commands/companies.js.map +2 -2
  140. package/dist/modules/customers/commands/interactions.js +41 -4
  141. package/dist/modules/customers/commands/interactions.js.map +2 -2
  142. package/dist/modules/customers/commands/people.js +1 -1
  143. package/dist/modules/customers/commands/people.js.map +2 -2
  144. package/dist/modules/customers/commands/personCompanyLinks.js +8 -5
  145. package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
  146. package/dist/modules/customers/commands/pipeline-stages.js +13 -11
  147. package/dist/modules/customers/commands/pipeline-stages.js.map +3 -3
  148. package/dist/modules/customers/components/AddressFormatSettings.js.map +2 -2
  149. package/dist/modules/customers/components/DictionarySettings.js +20 -13
  150. package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
  151. package/dist/modules/customers/components/DictionarySortSettings.js +4 -0
  152. package/dist/modules/customers/components/DictionarySortSettings.js.map +2 -2
  153. package/dist/modules/customers/components/PipelineSettings.js +38 -23
  154. package/dist/modules/customers/components/PipelineSettings.js.map +2 -2
  155. package/dist/modules/customers/components/detail/ActivityTimeline.js +1 -1
  156. package/dist/modules/customers/components/detail/ActivityTimeline.js.map +2 -2
  157. package/dist/modules/customers/components/detail/AddressesSection.js +4 -0
  158. package/dist/modules/customers/components/detail/AddressesSection.js.map +2 -2
  159. package/dist/modules/customers/components/detail/CompanyPeopleSection.js +28 -22
  160. package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
  161. package/dist/modules/customers/components/detail/DealsSection.js +36 -24
  162. package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
  163. package/dist/modules/customers/components/detail/EmailCardActions.js +5 -0
  164. package/dist/modules/customers/components/detail/EmailCardActions.js.map +2 -2
  165. package/dist/modules/customers/components/detail/EntityTagsDialog.js +7 -0
  166. package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
  167. package/dist/modules/customers/components/detail/ManageTagsDialog.js +34 -22
  168. package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
  169. package/dist/modules/customers/components/detail/PersonCompaniesSection.js +41 -29
  170. package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
  171. package/dist/modules/customers/components/detail/RoleAssignmentRow.js +14 -8
  172. package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
  173. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +14 -6
  174. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  175. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js +29 -13
  176. package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js.map +2 -2
  177. package/dist/modules/customers/components/detail/hooks/useInteractions.js +77 -35
  178. package/dist/modules/customers/components/detail/hooks/useInteractions.js.map +2 -2
  179. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +25 -17
  180. package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
  181. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  182. package/dist/modules/customers/components/formConfig.js.map +2 -2
  183. package/dist/modules/customers/data/guards.js +66 -0
  184. package/dist/modules/customers/data/guards.js.map +7 -0
  185. package/dist/modules/customers/di.js +37 -0
  186. package/dist/modules/customers/di.js.map +2 -2
  187. package/dist/modules/customers/lib/todoCompatibility.js +11 -0
  188. package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
  189. package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
  190. package/dist/modules/data_sync/api/options.js +4 -4
  191. package/dist/modules/data_sync/api/options.js.map +2 -2
  192. package/dist/modules/data_sync/api/schedules/route.js +9 -1
  193. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  194. package/dist/modules/data_sync/backend/data-sync/page.js +17 -8
  195. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  196. package/dist/modules/data_sync/components/IntegrationScheduleTab.js +43 -22
  197. package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
  198. package/dist/modules/data_sync/lib/sync-schedule-service.js +9 -0
  199. package/dist/modules/data_sync/lib/sync-schedule-service.js.map +2 -2
  200. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +8 -1
  201. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  202. package/dist/modules/dictionaries/api/[dictionaryId]/route.js +17 -1
  203. package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
  204. package/dist/modules/dictionaries/components/DictionariesManager.js +31 -10
  205. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  206. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +28 -15
  207. package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
  208. package/dist/modules/directory/api/organizations/route.js +3 -0
  209. package/dist/modules/directory/api/organizations/route.js.map +2 -2
  210. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +2 -0
  211. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
  212. package/dist/modules/directory/backend/directory/organizations/page.js +9 -5
  213. package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
  214. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js +7 -3
  215. package/dist/modules/directory/backend/directory/tenants/[id]/edit/page.js.map +2 -2
  216. package/dist/modules/directory/backend/directory/tenants/page.js +8 -4
  217. package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
  218. package/dist/modules/directory/commands/organizations.js +7 -2
  219. package/dist/modules/directory/commands/organizations.js.map +2 -2
  220. package/dist/modules/entities/api/records.js +66 -0
  221. package/dist/modules/entities/api/records.js.map +2 -2
  222. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js +1 -0
  223. package/dist/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.js.map +2 -2
  224. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +8 -4
  225. package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
  226. package/dist/modules/entities/lib/helpers.js +17 -0
  227. package/dist/modules/entities/lib/helpers.js.map +2 -2
  228. package/dist/modules/feature_toggles/api/global/[id]/override/route.js +2 -1
  229. package/dist/modules/feature_toggles/api/global/[id]/override/route.js.map +2 -2
  230. package/dist/modules/feature_toggles/api/overrides/route.js +15 -0
  231. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  232. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js +15 -14
  233. package/dist/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.js.map +2 -2
  234. package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js +20 -12
  235. package/dist/modules/feature_toggles/components/FeatureToggleOverrideCard.js.map +2 -2
  236. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +6 -2
  237. package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
  238. package/dist/modules/feature_toggles/components/formConfig.js +2 -1
  239. package/dist/modules/feature_toggles/components/formConfig.js.map +2 -2
  240. package/dist/modules/feature_toggles/components/overrideFormConfig.js +5 -1
  241. package/dist/modules/feature_toggles/components/overrideFormConfig.js.map +2 -2
  242. package/dist/modules/feature_toggles/data/validators.js +7 -4
  243. package/dist/modules/feature_toggles/data/validators.js.map +2 -2
  244. package/dist/modules/inbox_ops/api/settings/route.js +17 -2
  245. package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
  246. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js +13 -8
  247. package/dist/modules/inbox_ops/backend/inbox-ops/settings/page.js.map +2 -2
  248. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js +9 -4
  249. package/dist/modules/inbox_ops/components/proposals/EditActionDialog.js.map +2 -2
  250. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +18 -11
  251. package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
  252. package/dist/modules/integrations/backend/integrations/page.js +12 -8
  253. package/dist/modules/integrations/backend/integrations/page.js.map +2 -2
  254. package/dist/modules/messages/commands/messages.js +13 -10
  255. package/dist/modules/messages/commands/messages.js.map +2 -2
  256. package/dist/modules/perspectives/api/[tableId]/route.js +39 -30
  257. package/dist/modules/perspectives/api/[tableId]/route.js.map +2 -2
  258. package/dist/modules/perspectives/services/perspectiveService.js +7 -0
  259. package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
  260. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js +6 -14
  261. package/dist/modules/planner/backend/planner/availability-rulesets/[id]/page.js.map +3 -3
  262. package/dist/modules/planner/backend/planner/availability-rulesets/page.js +4 -2
  263. package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
  264. package/dist/modules/planner/components/AvailabilityRuleSetForm.js +2 -0
  265. package/dist/modules/planner/components/AvailabilityRuleSetForm.js.map +2 -2
  266. package/dist/modules/planner/components/AvailabilityRulesEditor.js +36 -11
  267. package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
  268. package/dist/modules/planner/components/AvailabilitySchedule.js +9 -5
  269. package/dist/modules/planner/components/AvailabilitySchedule.js.map +2 -2
  270. package/dist/modules/query_index/lib/engine.js +19 -0
  271. package/dist/modules/query_index/lib/engine.js.map +2 -2
  272. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +1 -0
  273. package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
  274. package/dist/modules/resources/backend/resources/resource-types/page.js +4 -2
  275. package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
  276. package/dist/modules/resources/backend/resources/resources/[id]/page.js +14 -3
  277. package/dist/modules/resources/backend/resources/resources/[id]/page.js.map +2 -2
  278. package/dist/modules/resources/backend/resources/resources/page.js +8 -4
  279. package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
  280. package/dist/modules/resources/components/ResourceCrudForm.js +2 -0
  281. package/dist/modules/resources/components/ResourceCrudForm.js.map +2 -2
  282. package/dist/modules/resources/components/ResourceTypeCrudForm.js +1 -0
  283. package/dist/modules/resources/components/ResourceTypeCrudForm.js.map +2 -2
  284. package/dist/modules/sales/api/documents/factory.js +7 -2
  285. package/dist/modules/sales/api/documents/factory.js.map +2 -2
  286. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +3 -1
  287. package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
  288. package/dist/modules/sales/backend/sales/channels/offers/page.js +13 -4
  289. package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
  290. package/dist/modules/sales/backend/sales/channels/page.js +16 -4
  291. package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
  292. package/dist/modules/sales/backend/sales/documents/[id]/page.js +68 -22
  293. package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
  294. package/dist/modules/sales/backend/sales/documents/create/page.js.map +2 -2
  295. package/dist/modules/sales/commands/documentAddresses.js +181 -2
  296. package/dist/modules/sales/commands/documentAddresses.js.map +2 -2
  297. package/dist/modules/sales/commands/documents.js +29 -1
  298. package/dist/modules/sales/commands/documents.js.map +2 -2
  299. package/dist/modules/sales/commands/returns.js +12 -2
  300. package/dist/modules/sales/commands/returns.js.map +2 -2
  301. package/dist/modules/sales/commands/shared.js +15 -0
  302. package/dist/modules/sales/commands/shared.js.map +2 -2
  303. package/dist/modules/sales/commands/shipments.js +4 -1
  304. package/dist/modules/sales/commands/shipments.js.map +2 -2
  305. package/dist/modules/sales/components/AdjustmentKindSettings.js +19 -11
  306. package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
  307. package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
  308. package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
  309. package/dist/modules/sales/components/PaymentMethodsSettings.js +12 -4
  310. package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
  311. package/dist/modules/sales/components/ShippingMethodsSettings.js +12 -4
  312. package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
  313. package/dist/modules/sales/components/StatusSettings.js +18 -11
  314. package/dist/modules/sales/components/StatusSettings.js.map +2 -2
  315. package/dist/modules/sales/components/TaxRatesSettings.js +12 -4
  316. package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
  317. package/dist/modules/sales/components/channels/ChannelOfferForm.js +47 -16
  318. package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
  319. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +8 -4
  320. package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
  321. package/dist/modules/sales/components/documents/AddressesSection.js +44 -25
  322. package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
  323. package/dist/modules/sales/components/documents/AdjustmentsSection.js +43 -23
  324. package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
  325. package/dist/modules/sales/components/documents/ItemsSection.js +22 -13
  326. package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
  327. package/dist/modules/sales/components/documents/LineItemDialog.js +23 -10
  328. package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
  329. package/dist/modules/sales/components/documents/PaymentDialog.js +29 -14
  330. package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
  331. package/dist/modules/sales/components/documents/PaymentsSection.js +20 -10
  332. package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
  333. package/dist/modules/sales/components/documents/ReturnDialog.js +26 -17
  334. package/dist/modules/sales/components/documents/ReturnDialog.js.map +2 -2
  335. package/dist/modules/sales/components/documents/ReturnsSection.js +3 -1
  336. package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
  337. package/dist/modules/sales/components/documents/SalesDocumentsTable.js +10 -5
  338. package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
  339. package/dist/modules/sales/components/documents/ShipmentDialog.js +21 -7
  340. package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
  341. package/dist/modules/sales/components/documents/ShipmentsSection.js +19 -10
  342. package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
  343. package/dist/modules/sales/components/documents/optimisticLock.js +27 -0
  344. package/dist/modules/sales/components/documents/optimisticLock.js.map +7 -0
  345. package/dist/modules/sales/di.js +18 -0
  346. package/dist/modules/sales/di.js.map +2 -2
  347. package/dist/modules/staff/api/job-histories.js +11 -2
  348. package/dist/modules/staff/api/job-histories.js.map +2 -2
  349. package/dist/modules/staff/api/timesheets/time-entries/route.js +11 -4
  350. package/dist/modules/staff/api/timesheets/time-entries/route.js.map +2 -2
  351. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js +13 -8
  352. package/dist/modules/staff/backend/staff/leave-requests/[id]/page.js.map +2 -2
  353. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js +2 -1
  354. package/dist/modules/staff/backend/staff/my-leave-requests/[id]/page.js.map +2 -2
  355. package/dist/modules/staff/backend/staff/team-members/[id]/page.js +7 -4
  356. package/dist/modules/staff/backend/staff/team-members/[id]/page.js.map +2 -2
  357. package/dist/modules/staff/backend/staff/team-members/page.js +4 -2
  358. package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
  359. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js +1 -0
  360. package/dist/modules/staff/backend/staff/team-roles/[id]/edit/page.js.map +2 -2
  361. package/dist/modules/staff/backend/staff/team-roles/page.js +4 -2
  362. package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
  363. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +5 -2
  364. package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
  365. package/dist/modules/staff/backend/staff/teams/page.js +12 -3
  366. package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
  367. package/dist/modules/staff/backend/staff/timesheets/page.js +4 -1
  368. package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
  369. package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +2 -2
  370. package/dist/modules/staff/backend/staff/timesheets/projects/page.js +12 -3
  371. package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
  372. package/dist/modules/staff/commands/job-histories.js +40 -3
  373. package/dist/modules/staff/commands/job-histories.js.map +2 -2
  374. package/dist/modules/staff/components/LeaveRequestForm.js +1 -0
  375. package/dist/modules/staff/components/LeaveRequestForm.js.map +2 -2
  376. package/dist/modules/staff/components/TeamForm.js +1 -0
  377. package/dist/modules/staff/components/TeamForm.js.map +2 -2
  378. package/dist/modules/staff/components/TeamMemberForm.js +1 -0
  379. package/dist/modules/staff/components/TeamMemberForm.js.map +2 -2
  380. package/dist/modules/staff/components/TeamRoleForm.js +1 -0
  381. package/dist/modules/staff/components/TeamRoleForm.js.map +2 -2
  382. package/dist/modules/staff/components/detail/JobHistorySection.js +20 -7
  383. package/dist/modules/staff/components/detail/JobHistorySection.js.map +2 -2
  384. package/dist/modules/staff/data/validators.js +7 -1
  385. package/dist/modules/staff/data/validators.js.map +2 -2
  386. package/dist/modules/staff/lib/leaveRequestHelpers.js +2 -1
  387. package/dist/modules/staff/lib/leaveRequestHelpers.js.map +2 -2
  388. package/dist/modules/translations/components/TranslationManager.js +12 -8
  389. package/dist/modules/translations/components/TranslationManager.js.map +2 -2
  390. package/dist/modules/workflows/api/definitions/[id]/route.js +106 -0
  391. package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
  392. package/dist/modules/workflows/backend/definitions/[id]/page.js +11 -3
  393. package/dist/modules/workflows/backend/definitions/[id]/page.js.map +2 -2
  394. package/dist/modules/workflows/backend/definitions/page.js +19 -8
  395. package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
  396. package/dist/modules/workflows/backend/definitions/visual-editor/page.js +29 -16
  397. package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
  398. package/dist/modules/workflows/components/formConfig.js +4 -1
  399. package/dist/modules/workflows/components/formConfig.js.map +2 -2
  400. package/dist/modules/workflows/di.js +12 -0
  401. package/dist/modules/workflows/di.js.map +2 -2
  402. package/generated/entities/role/index.ts +1 -0
  403. package/generated/entities/user/index.ts +1 -0
  404. package/generated/entity-fields-registry.ts +2 -0
  405. package/jest.setup.ts +17 -0
  406. package/package.json +8 -7
  407. package/src/helpers/integration/optimisticLockUi.ts +172 -0
  408. package/src/helpers/integration/salesFixtures.ts +29 -0
  409. package/src/modules/api_keys/backend/api-keys/page.tsx +10 -5
  410. package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +19 -9
  411. package/src/modules/auth/api/roles/acl/route.ts +37 -11
  412. package/src/modules/auth/api/roles/route.ts +2 -0
  413. package/src/modules/auth/api/sidebar/preferences/route.ts +73 -0
  414. package/src/modules/auth/api/users/acl/route.ts +46 -18
  415. package/src/modules/auth/api/users/route.ts +2 -0
  416. package/src/modules/auth/backend/roles/[id]/edit/page.tsx +29 -4
  417. package/src/modules/auth/backend/roles/page.tsx +9 -4
  418. package/src/modules/auth/backend/users/[id]/edit/page.tsx +37 -4
  419. package/src/modules/auth/backend/users/page.tsx +7 -2
  420. package/src/modules/auth/components/AclEditor.tsx +10 -1
  421. package/src/modules/auth/data/entities.ts +7 -1
  422. package/src/modules/auth/services/sidebarPreferencesService.ts +38 -4
  423. package/src/modules/business_rules/api/rules/route.ts +30 -0
  424. package/src/modules/business_rules/api/sets/route.ts +30 -0
  425. package/src/modules/business_rules/backend/rules/[id]/page.tsx +16 -4
  426. package/src/modules/business_rules/backend/rules/page.tsx +20 -11
  427. package/src/modules/business_rules/backend/sets/[id]/page.tsx +16 -4
  428. package/src/modules/business_rules/backend/sets/page.tsx +20 -11
  429. package/src/modules/catalog/api/categories/route.ts +3 -0
  430. package/src/modules/catalog/api/products/route.ts +4 -0
  431. package/src/modules/catalog/backend/catalog/categories/[id]/edit/page.tsx +5 -0
  432. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +112 -35
  433. package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +56 -7
  434. package/src/modules/catalog/backend/catalog/products/optionSchemaClient.ts +2 -0
  435. package/src/modules/catalog/commands/variants.ts +32 -32
  436. package/src/modules/catalog/components/PriceKindSettings.tsx +20 -7
  437. package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +1 -0
  438. package/src/modules/catalog/components/products/ProductMediaManager.tsx +2 -0
  439. package/src/modules/catalog/components/products/ProductsDataTable.tsx +8 -4
  440. package/src/modules/catalog/components/products/productForm.ts +3 -0
  441. package/src/modules/catalog/components/products/variantForm.ts +9 -0
  442. package/src/modules/communication_channels/backend/profile/communication-channels/page.tsx +5 -0
  443. package/src/modules/currencies/backend/currencies/[id]/page.tsx +13 -6
  444. package/src/modules/currencies/backend/currencies/page.tsx +18 -11
  445. package/src/modules/currencies/backend/exchange-rates/[id]/page.tsx +3 -0
  446. package/src/modules/currencies/backend/exchange-rates/page.tsx +10 -6
  447. package/src/modules/currencies/commands/currencies.ts +10 -5
  448. package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +31 -21
  449. package/src/modules/customer_accounts/api/admin/roles/[id].ts +35 -5
  450. package/src/modules/customer_accounts/api/admin/roles.ts +2 -0
  451. package/src/modules/customer_accounts/api/admin/users/[id].ts +38 -5
  452. package/src/modules/customer_accounts/api/admin/users.ts +2 -0
  453. package/src/modules/customer_accounts/backend/customer_accounts/roles/[id]/page.tsx +34 -20
  454. package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +9 -4
  455. package/src/modules/customer_accounts/backend/customer_accounts/settings/domain/page.tsx +11 -4
  456. package/src/modules/customer_accounts/backend/customer_accounts/users/[id]/page.tsx +28 -17
  457. package/src/modules/customer_accounts/backend/customer_accounts/users/page.tsx +19 -11
  458. package/src/modules/customers/AGENTS.md +2 -2
  459. package/src/modules/customers/api/companies/route.ts +14 -1
  460. package/src/modules/customers/api/deals/route.ts +3 -0
  461. package/src/modules/customers/api/people/route.ts +12 -1
  462. package/src/modules/customers/api/todos/route.ts +1 -0
  463. package/src/modules/customers/backend/config/customers/deals/page.tsx +1 -0
  464. package/src/modules/customers/backend/config/customers/pipeline-stages/page.tsx +36 -21
  465. package/src/modules/customers/backend/customers/companies/[id]/page.tsx +52 -27
  466. package/src/modules/customers/backend/customers/companies/page.tsx +2 -0
  467. package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +27 -5
  468. package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealFormHandlers.ts +39 -7
  469. package/src/modules/customers/backend/customers/deals/[id]/page.tsx +1 -0
  470. package/src/modules/customers/backend/customers/deals/page.tsx +18 -6
  471. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +64 -39
  472. package/src/modules/customers/backend/customers/people/[id]/page.tsx +46 -26
  473. package/src/modules/customers/backend/customers/people/page.tsx +2 -0
  474. package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +84 -24
  475. package/src/modules/customers/commands/addresses.ts +16 -14
  476. package/src/modules/customers/commands/companies.ts +3 -1
  477. package/src/modules/customers/commands/interactions.ts +50 -4
  478. package/src/modules/customers/commands/people.ts +2 -1
  479. package/src/modules/customers/commands/personCompanyLinks.ts +8 -5
  480. package/src/modules/customers/commands/pipeline-stages.ts +16 -16
  481. package/src/modules/customers/components/AddressFormatSettings.tsx +1 -0
  482. package/src/modules/customers/components/DictionarySettings.tsx +18 -13
  483. package/src/modules/customers/components/DictionarySortSettings.tsx +4 -0
  484. package/src/modules/customers/components/PipelineSettings.tsx +42 -21
  485. package/src/modules/customers/components/detail/ActivityTimeline.tsx +3 -3
  486. package/src/modules/customers/components/detail/AddressesSection.tsx +4 -0
  487. package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +2 -0
  488. package/src/modules/customers/components/detail/DealsSection.tsx +4 -0
  489. package/src/modules/customers/components/detail/EmailCardActions.tsx +5 -0
  490. package/src/modules/customers/components/detail/EntityTagsDialog.tsx +7 -0
  491. package/src/modules/customers/components/detail/ManageTagsDialog.tsx +4 -0
  492. package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +4 -0
  493. package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +2 -0
  494. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +23 -7
  495. package/src/modules/customers/components/detail/hooks/useInteractionMutations.ts +25 -15
  496. package/src/modules/customers/components/detail/hooks/useInteractions.ts +76 -35
  497. package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +30 -17
  498. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +2 -0
  499. package/src/modules/customers/components/detail/types.ts +1 -0
  500. package/src/modules/customers/components/formConfig.tsx +2 -0
  501. package/src/modules/customers/data/guards.ts +67 -0
  502. package/src/modules/customers/di.ts +66 -0
  503. package/src/modules/customers/i18n/de.json +2 -0
  504. package/src/modules/customers/i18n/en.json +2 -0
  505. package/src/modules/customers/i18n/es.json +2 -0
  506. package/src/modules/customers/i18n/pl.json +2 -0
  507. package/src/modules/customers/lib/todoCompatibility.ts +14 -0
  508. package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +2 -0
  509. package/src/modules/data_sync/api/options.ts +7 -4
  510. package/src/modules/data_sync/api/schedules/route.ts +9 -1
  511. package/src/modules/data_sync/backend/data-sync/page.tsx +18 -5
  512. package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +46 -19
  513. package/src/modules/data_sync/lib/sync-schedule-service.ts +11 -0
  514. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +8 -1
  515. package/src/modules/dictionaries/api/[dictionaryId]/route.ts +23 -0
  516. package/src/modules/dictionaries/components/DictionariesManager.tsx +32 -9
  517. package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +30 -14
  518. package/src/modules/dictionaries/i18n/de.json +1 -0
  519. package/src/modules/dictionaries/i18n/en.json +1 -0
  520. package/src/modules/dictionaries/i18n/es.json +1 -0
  521. package/src/modules/dictionaries/i18n/pl.json +1 -0
  522. package/src/modules/directory/api/organizations/route.ts +3 -0
  523. package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +8 -0
  524. package/src/modules/directory/backend/directory/organizations/page.tsx +10 -5
  525. package/src/modules/directory/backend/directory/tenants/[id]/edit/page.tsx +16 -5
  526. package/src/modules/directory/backend/directory/tenants/page.tsx +8 -4
  527. package/src/modules/directory/commands/organizations.ts +7 -4
  528. package/src/modules/entities/api/records.ts +99 -0
  529. package/src/modules/entities/backend/entities/user/[entityId]/records/[recordId]/page.tsx +7 -0
  530. package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +8 -4
  531. package/src/modules/entities/lib/helpers.ts +17 -0
  532. package/src/modules/feature_toggles/api/global/[id]/override/route.ts +1 -0
  533. package/src/modules/feature_toggles/api/overrides/route.ts +19 -0
  534. package/src/modules/feature_toggles/backend/feature-toggles/global/[id]/edit/page.tsx +19 -13
  535. package/src/modules/feature_toggles/components/FeatureToggleOverrideCard.tsx +22 -12
  536. package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +7 -2
  537. package/src/modules/feature_toggles/components/formConfig.tsx +2 -1
  538. package/src/modules/feature_toggles/components/overrideFormConfig.tsx +10 -1
  539. package/src/modules/feature_toggles/data/validators.ts +11 -3
  540. package/src/modules/inbox_ops/api/settings/route.ts +18 -0
  541. package/src/modules/inbox_ops/backend/inbox-ops/settings/page.tsx +15 -10
  542. package/src/modules/inbox_ops/components/proposals/EditActionDialog.tsx +9 -4
  543. package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +20 -11
  544. package/src/modules/integrations/backend/integrations/page.tsx +13 -8
  545. package/src/modules/messages/commands/messages.ts +27 -15
  546. package/src/modules/perspectives/api/[tableId]/route.ts +11 -2
  547. package/src/modules/perspectives/services/perspectiveService.ts +13 -1
  548. package/src/modules/planner/backend/planner/availability-rulesets/[id]/page.tsx +16 -14
  549. package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +6 -3
  550. package/src/modules/planner/components/AvailabilityRuleSetForm.tsx +3 -0
  551. package/src/modules/planner/components/AvailabilityRulesEditor.tsx +58 -15
  552. package/src/modules/planner/components/AvailabilitySchedule.tsx +22 -7
  553. package/src/modules/query_index/lib/engine.ts +34 -0
  554. package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +7 -1
  555. package/src/modules/resources/backend/resources/resource-types/page.tsx +6 -3
  556. package/src/modules/resources/backend/resources/resources/[id]/page.tsx +23 -3
  557. package/src/modules/resources/backend/resources/resources/page.tsx +15 -4
  558. package/src/modules/resources/components/ResourceCrudForm.tsx +3 -0
  559. package/src/modules/resources/components/ResourceTypeCrudForm.tsx +2 -0
  560. package/src/modules/sales/api/documents/factory.ts +13 -1
  561. package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +6 -0
  562. package/src/modules/sales/backend/sales/channels/offers/page.tsx +10 -4
  563. package/src/modules/sales/backend/sales/channels/page.tsx +19 -4
  564. package/src/modules/sales/backend/sales/documents/[id]/page.tsx +73 -20
  565. package/src/modules/sales/backend/sales/documents/create/page.tsx +2 -0
  566. package/src/modules/sales/commands/documentAddresses.ts +226 -4
  567. package/src/modules/sales/commands/documents.ts +28 -0
  568. package/src/modules/sales/commands/returns.ts +12 -3
  569. package/src/modules/sales/commands/shared.ts +36 -0
  570. package/src/modules/sales/commands/shipments.ts +17 -1
  571. package/src/modules/sales/components/AdjustmentKindSettings.tsx +20 -11
  572. package/src/modules/sales/components/DocumentNumberSettings.tsx +1 -0
  573. package/src/modules/sales/components/OrderEditingSettings.tsx +1 -0
  574. package/src/modules/sales/components/PaymentMethodsSettings.tsx +12 -4
  575. package/src/modules/sales/components/ShippingMethodsSettings.tsx +12 -4
  576. package/src/modules/sales/components/StatusSettings.tsx +20 -11
  577. package/src/modules/sales/components/TaxRatesSettings.tsx +12 -5
  578. package/src/modules/sales/components/channels/ChannelOfferForm.tsx +67 -14
  579. package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +7 -4
  580. package/src/modules/sales/components/documents/AddressesSection.tsx +35 -25
  581. package/src/modules/sales/components/documents/AdjustmentsSection.tsx +50 -25
  582. package/src/modules/sales/components/documents/ItemsSection.tsx +24 -13
  583. package/src/modules/sales/components/documents/LineItemDialog.tsx +26 -9
  584. package/src/modules/sales/components/documents/PaymentDialog.tsx +33 -14
  585. package/src/modules/sales/components/documents/PaymentsSection.tsx +22 -10
  586. package/src/modules/sales/components/documents/ReturnDialog.tsx +28 -17
  587. package/src/modules/sales/components/documents/ReturnsSection.tsx +4 -1
  588. package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +11 -4
  589. package/src/modules/sales/components/documents/ShipmentDialog.tsx +23 -8
  590. package/src/modules/sales/components/documents/ShipmentsSection.tsx +20 -10
  591. package/src/modules/sales/components/documents/optimisticLock.ts +34 -0
  592. package/src/modules/sales/components/documents/shipmentTypes.ts +1 -0
  593. package/src/modules/sales/di.ts +35 -0
  594. package/src/modules/sales/i18n/de.json +3 -0
  595. package/src/modules/sales/i18n/en.json +3 -0
  596. package/src/modules/sales/i18n/es.json +3 -0
  597. package/src/modules/sales/i18n/pl.json +3 -0
  598. package/src/modules/staff/api/job-histories.ts +12 -2
  599. package/src/modules/staff/api/timesheets/time-entries/route.ts +16 -4
  600. package/src/modules/staff/backend/staff/leave-requests/[id]/page.tsx +12 -7
  601. package/src/modules/staff/backend/staff/my-leave-requests/[id]/page.tsx +2 -0
  602. package/src/modules/staff/backend/staff/team-members/[id]/page.tsx +16 -5
  603. package/src/modules/staff/backend/staff/team-members/page.tsx +6 -2
  604. package/src/modules/staff/backend/staff/team-roles/[id]/edit/page.tsx +8 -0
  605. package/src/modules/staff/backend/staff/team-roles/page.tsx +6 -2
  606. package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +13 -3
  607. package/src/modules/staff/backend/staff/teams/page.tsx +9 -3
  608. package/src/modules/staff/backend/staff/timesheets/page.tsx +10 -1
  609. package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -0
  610. package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +9 -3
  611. package/src/modules/staff/commands/job-histories.ts +42 -3
  612. package/src/modules/staff/components/LeaveRequestForm.tsx +2 -0
  613. package/src/modules/staff/components/TeamForm.tsx +2 -0
  614. package/src/modules/staff/components/TeamMemberForm.tsx +2 -0
  615. package/src/modules/staff/components/TeamRoleForm.tsx +2 -0
  616. package/src/modules/staff/components/detail/JobHistorySection.tsx +28 -6
  617. package/src/modules/staff/data/validators.ts +6 -0
  618. package/src/modules/staff/i18n/de.json +1 -0
  619. package/src/modules/staff/i18n/en.json +1 -0
  620. package/src/modules/staff/i18n/es.json +1 -0
  621. package/src/modules/staff/i18n/pl.json +1 -0
  622. package/src/modules/staff/lib/leaveRequestHelpers.ts +4 -0
  623. package/src/modules/translations/components/TranslationManager.tsx +13 -8
  624. package/src/modules/workflows/api/definitions/[id]/route.ts +112 -0
  625. package/src/modules/workflows/backend/definitions/[id]/page.tsx +20 -4
  626. package/src/modules/workflows/backend/definitions/page.tsx +20 -9
  627. package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +29 -16
  628. package/src/modules/workflows/components/formConfig.tsx +5 -0
  629. package/src/modules/workflows/di.ts +20 -0
  630. package/src/modules/workflows/i18n/de.json +1 -0
  631. package/src/modules/workflows/i18n/en.json +1 -0
  632. package/src/modules/workflows/i18n/es.json +1 -0
  633. package/src/modules/workflows/i18n/pl.json +1 -0
@@ -7,12 +7,16 @@ import { User, Hash, Users, Building2 } from 'lucide-react'
7
7
  import { Page, PageBody } from '@open-mercato/ui/backend/Page'
8
8
  import { CrudForm } from '@open-mercato/ui/backend/CrudForm'
9
9
  import { CollapsibleZoneLayout, type ZoneSectionDescriptor } from '@open-mercato/ui/backend/crud/CollapsibleZoneLayout'
10
+ import { useIsMobile } from '@open-mercato/ui/hooks/useIsMobile'
10
11
  import { updateCrud, deleteCrud } from '@open-mercato/ui/backend/utils/crud'
11
12
  import { apiCallOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
12
13
  import { readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
14
+ import { withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
15
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
13
16
  import { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'
14
17
  import { E } from '#generated/entities.ids.generated'
15
18
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
19
+ import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
16
20
  import { useT } from '@open-mercato/shared/lib/i18n/context'
17
21
  import { useOrganizationScopeDetail } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
18
22
  import { Button } from '@open-mercato/ui/primitives/button'
@@ -53,6 +57,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
53
57
  const router = useRouter()
54
58
  const searchParams = useSearchParams()
55
59
  const { organizationId } = useOrganizationScopeDetail()
60
+ const isMobile = useIsMobile()
56
61
  const { confirm, ConfirmDialogElement } = useConfirmDialog()
57
62
 
58
63
  const detailTranslator = React.useMemo(() => createTranslatorWithFallback(t), [t])
@@ -62,6 +67,15 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
62
67
  const fields = React.useMemo(() => createPersonEditFields(t), [t])
63
68
 
64
69
  const [data, setData] = React.useState<PersonOverview | null>(null)
70
+ // Mirror the latest `data` into a ref so save handlers always read the current
71
+ // optimistic-lock token (`person.updatedAt`) instead of the value captured in
72
+ // their `useCallback` closure. Without this, a header-field save issued after a
73
+ // prior in-page reload would send a stale token (or none), letting a concurrent
74
+ // two-tab overwrite slip through without the 409 + conflict bar (#2055, Alina A7).
75
+ const dataRef = React.useRef<PersonOverview | null>(null)
76
+ React.useEffect(() => {
77
+ dataRef.current = data
78
+ }, [data])
65
79
  const [isLoading, setIsLoading] = React.useState(true)
66
80
  const [error, setError] = React.useState<string | null>(null)
67
81
  const [isNotFound, setIsNotFound] = React.useState(false)
@@ -124,7 +138,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
124
138
 
125
139
  // Data loading
126
140
  const initialLoadDoneRef = React.useRef(false)
127
- const loadData = React.useCallback(async () => {
141
+ const loadData = React.useCallback(async (lockTokenOverride?: string | null) => {
128
142
  if (!id) {
129
143
  setIsNotFound(true)
130
144
  setIsLoading(false)
@@ -141,7 +155,15 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
141
155
  undefined,
142
156
  { errorMessage: t('customers.people.detail.error.load', 'Failed to load person.') },
143
157
  )
144
- setData(payload as PersonOverview)
158
+ // When the caller is the save handler, pin the optimistic-lock token to the
159
+ // value the write itself returned rather than the one this GET observed — a
160
+ // concurrent third-party bump between save and reload must stay stale so the
161
+ // next in-page save 409s (#2055, Alina A7). Applied in the same state update
162
+ // as the refresh to avoid a redundant second re-render.
163
+ const next = lockTokenOverride && payload?.person
164
+ ? { ...payload, person: { ...payload.person, updatedAt: lockTokenOverride, updated_at: lockTokenOverride } }
165
+ : payload
166
+ setData(next as PersonOverview)
145
167
  } catch (err) {
146
168
  if ((err as { status?: number }).status === 404) {
147
169
  setIsNotFound(true)
@@ -230,6 +252,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
230
252
  // moment instead of "today" (#1807 prefill).
231
253
  const editPayload = {
232
254
  id: activity.id,
255
+ updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt as string : typeof raw.updated_at === 'string' ? raw.updated_at as string : null,
233
256
  interactionType: typeof activity.interactionType === 'string' ? activity.interactionType : undefined,
234
257
  title: typeof activity.title === 'string' ? activity.title : null,
235
258
  body: typeof activity.body === 'string' ? activity.body : null,
@@ -329,9 +352,27 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
329
352
  throw err
330
353
  }
331
354
 
332
- await updateCrud('customers/people', payload)
355
+ // Attach the current optimistic-lock token directly on this write path so
356
+ // every header-field edit (displayName/status/…) carries `updatedAt`, not
357
+ // just the fields the embedded CrudForm intercepts. Read from `dataRef` so
358
+ // the token reflects the latest in-page reload rather than a stale closure
359
+ // capture, and let the 409 propagate to CrudForm's surfaceRecordConflict so
360
+ // the unified conflict bar renders (#2055, Alina A7).
361
+ const lockedUpdatedAt = dataRef.current?.person?.updatedAt
362
+ ?? dataRef.current?.person?.updated_at
363
+ ?? null
364
+ const updateResponse = await withScopedApiRequestHeaders(
365
+ buildOptimisticLockHeader(lockedUpdatedAt),
366
+ () => updateCrud<{ updatedAt?: string | null }>('customers/people', payload),
367
+ )
333
368
  flash(t('customers.people.form.updateSuccess', 'Person updated.'), 'success')
334
- await loadData()
369
+ // Refresh the view and pin the optimistic-lock token to the write's OWN
370
+ // authoritative `updatedAt` in a single reload (see loadData) so a
371
+ // concurrent third-party bump stays stale on the next save (#2055, Alina A7).
372
+ const savedUpdatedAt = typeof updateResponse.result?.updatedAt === 'string'
373
+ ? updateResponse.result.updatedAt
374
+ : null
375
+ await loadData(savedUpdatedAt)
335
376
  } finally {
336
377
  setIsSaving(false)
337
378
  }
@@ -351,10 +392,28 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
351
392
  variant: 'destructive',
352
393
  })
353
394
  if (!approved) return
354
- await runMutationWithContext(
355
- () => deleteCrud('customers/people', { id: personId }),
356
- { id: personId, operation: 'deletePerson' },
357
- )
395
+ try {
396
+ await runMutationWithContext(
397
+ () => withScopedApiRequestHeaders(
398
+ buildOptimisticLockHeader(data?.person?.updatedAt ?? data?.person?.updated_at ?? null),
399
+ () => deleteCrud('customers/people', { id: personId }),
400
+ ),
401
+ { id: personId, operation: 'deletePerson' },
402
+ )
403
+ } catch (err) {
404
+ // The guarded mutation routes a 409 to the unified conflict bar; surface
405
+ // any other server error (e.g. a linked-records delete guard) as a flash
406
+ // instead of letting it crash the page.
407
+ if (!surfaceRecordConflict(err, t)) {
408
+ flash(
409
+ err instanceof Error && err.message.trim().length > 0
410
+ ? err.message
411
+ : t('customers.people.detail.deleteError', 'Failed to delete person.'),
412
+ 'error',
413
+ )
414
+ }
415
+ return
416
+ }
358
417
  flash(t('customers.people.list.deleteSuccess', 'Person deleted.'), 'success')
359
418
  router.push('/backend/customers/people')
360
419
  },
@@ -464,6 +523,7 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
464
523
  fields={fields}
465
524
  groups={groups}
466
525
  initialValues={initialValues}
526
+ optimisticLockUpdatedAt={data.person.updatedAt ?? data.person.updated_at ?? null}
467
527
  onSubmit={handleFormSubmit}
468
528
  onDelete={handleFormDelete}
469
529
  hideFooterActions
@@ -602,22 +662,22 @@ export default function PersonDetailV2Page({ params }: { params?: { id?: string
602
662
  </div>
603
663
  </PersonDetailTabs>
604
664
  )
605
- return (
606
- <>
607
- <div className="md:hidden">
608
- <MobilePersonDetail zone1={zone1Content} zone2={zone2Content} />
609
- </div>
610
- <div className="hidden md:block">
611
- <CollapsibleZoneLayout
612
- pageType="person-v2"
613
- entityName={personName}
614
- isDirty={isDirty}
615
- sections={zoneSections}
616
- zone1={zone1Content}
617
- zone2={zone2Content}
618
- />
619
- </div>
620
- </>
665
+ // Render only the layout variant that matches the viewport. Mounting
666
+ // both the mobile and desktop layouts at once would create two
667
+ // CrudForm instances bound to one `formWrapperRef`, so header Save
668
+ // could submit the hidden instance's stale values and silently
669
+ // discard the edit (#2453). `useIsMobile` is SSR-safe.
670
+ return isMobile ? (
671
+ <MobilePersonDetail zone1={zone1Content} zone2={zone2Content} />
672
+ ) : (
673
+ <CollapsibleZoneLayout
674
+ pageType="person-v2"
675
+ entityName={personName}
676
+ isDirty={isDirty}
677
+ sections={zoneSections}
678
+ zone1={zone1Content}
679
+ zone2={zone2Content}
680
+ />
621
681
  )
622
682
  })()}
623
683
 
@@ -213,22 +213,24 @@ const updateAddressCommand: CommandHandler<AddressUpdateInput, { addressId: stri
213
213
  ensureSameScope(entity, address.organizationId, address.tenantId)
214
214
  address.entity = entity
215
215
  }
216
- if (parsed.name !== undefined) address.name = parsed.name ?? null
217
- if (parsed.purpose !== undefined) address.purpose = parsed.purpose ?? null
218
- if (parsed.companyName !== undefined) address.companyName = parsed.companyName ?? null
219
- if (parsed.addressLine1 !== undefined) address.addressLine1 = parsed.addressLine1
220
- if (parsed.addressLine2 !== undefined) address.addressLine2 = parsed.addressLine2 ?? null
221
- if (parsed.buildingNumber !== undefined) address.buildingNumber = parsed.buildingNumber ?? null
222
- if (parsed.flatNumber !== undefined) address.flatNumber = parsed.flatNumber ?? null
223
- if (parsed.city !== undefined) address.city = parsed.city ?? null
224
- if (parsed.region !== undefined) address.region = parsed.region ?? null
225
- if (parsed.postalCode !== undefined) address.postalCode = parsed.postalCode ?? null
226
- if (parsed.country !== undefined) address.country = parsed.country ?? null
227
- if (parsed.latitude !== undefined) address.latitude = parsed.latitude ?? null
228
- if (parsed.longitude !== undefined) address.longitude = parsed.longitude ?? null
229
- if (parsed.isPrimary !== undefined) address.isPrimary = parsed.isPrimary
230
216
 
231
217
  await withAtomicFlush(em, [
218
+ () => {
219
+ if (parsed.name !== undefined) address.name = parsed.name ?? null
220
+ if (parsed.purpose !== undefined) address.purpose = parsed.purpose ?? null
221
+ if (parsed.companyName !== undefined) address.companyName = parsed.companyName ?? null
222
+ if (parsed.addressLine1 !== undefined) address.addressLine1 = parsed.addressLine1
223
+ if (parsed.addressLine2 !== undefined) address.addressLine2 = parsed.addressLine2 ?? null
224
+ if (parsed.buildingNumber !== undefined) address.buildingNumber = parsed.buildingNumber ?? null
225
+ if (parsed.flatNumber !== undefined) address.flatNumber = parsed.flatNumber ?? null
226
+ if (parsed.city !== undefined) address.city = parsed.city ?? null
227
+ if (parsed.region !== undefined) address.region = parsed.region ?? null
228
+ if (parsed.postalCode !== undefined) address.postalCode = parsed.postalCode ?? null
229
+ if (parsed.country !== undefined) address.country = parsed.country ?? null
230
+ if (parsed.latitude !== undefined) address.latitude = parsed.latitude ?? null
231
+ if (parsed.longitude !== undefined) address.longitude = parsed.longitude ?? null
232
+ if (parsed.isPrimary !== undefined) address.isPrimary = parsed.isPrimary
233
+ },
232
234
  async () => {
233
235
  if (address.isPrimary) {
234
236
  await enforcePrimaryAddress(em, typeof address.entity === 'string' ? address.entity : address.entity.id, address.id)
@@ -677,7 +677,9 @@ const updateCompanyCommand: CommandHandler<CompanyUpdateInput, { entityId: strin
677
677
  })
678
678
  await emitQueryIndexUpsertEvents(ctx, [companyEntityIndexEntry(record)])
679
679
 
680
- return { entityId: record.id }
680
+ // Expose the freshly-bumped updatedAt so the CRUD update response can hand it to
681
+ // inline-edit detail pages for sequential-save lock-token refresh (#2055).
682
+ return { entityId: record.id, updatedAt: record.updatedAt }
681
683
  },
682
684
  captureAfter: async (_input, result, ctx) => {
683
685
  const em = (ctx.container.resolve('em') as EntityManager).fork()
@@ -38,6 +38,7 @@ import {
38
38
  buildCustomFieldResetMap,
39
39
  } from '@open-mercato/shared/lib/commands/customFieldSnapshots'
40
40
  import { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'
41
+ import { enforceRecordGoneIsConflict, enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
41
42
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
42
43
  import type { CrudIndexerConfig, CrudEventsConfig } from '@open-mercato/shared/lib/crud/types'
43
44
  import { recomputeNextInteraction } from '../lib/interactionProjection'
@@ -451,10 +452,25 @@ const updateInteractionCommand: CommandHandler<InteractionUpdateInput, { interac
451
452
  const em = (ctx.container.resolve('em') as EntityManager).fork()
452
453
  const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
453
454
  const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })
454
- if (!interaction) throw new CrudHttpError(404, { error: 'Interaction not found' })
455
+ if (!interaction) {
456
+ enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })
457
+ throw new CrudHttpError(404, { error: 'Interaction not found' })
458
+ }
455
459
  ensureTenantScope(ctx, interaction.tenantId)
456
460
  ensureOrganizationScope(ctx, interaction.organizationId)
457
461
 
462
+ // Concurrent-edit guard for command-driven callers (e.g. the legacy
463
+ // /api/customers/todos route, which bypasses the makeCrudRoute lock guard):
464
+ // when the client opted into optimistic locking, a stale edit fails with the
465
+ // unified 409 instead of silently overwriting (#2055). Strictly additive —
466
+ // no-op when no expected-version header is present.
467
+ enforceCommandOptimisticLock({
468
+ resourceKind: 'customers.interaction',
469
+ resourceId: interaction.id,
470
+ current: interaction.updatedAt,
471
+ request: ctx.request ?? null,
472
+ })
473
+
458
474
  // Email visibility is an access-controlled field: only the interaction's
459
475
  // author may change a
460
476
  // private email's visibility (mirrors the dedicated PATCH .../visibility
@@ -709,10 +725,20 @@ const completeInteractionCommand: CommandHandler<InteractionCompleteInput, { int
709
725
  const em = (ctx.container.resolve('em') as EntityManager).fork()
710
726
  const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
711
727
  const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })
712
- if (!interaction) throw new CrudHttpError(404, { error: 'Interaction not found' })
728
+ if (!interaction) {
729
+ enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })
730
+ throw new CrudHttpError(404, { error: 'Interaction not found' })
731
+ }
713
732
  ensureTenantScope(ctx, interaction.tenantId)
714
733
  ensureOrganizationScope(ctx, interaction.organizationId)
715
734
 
735
+ enforceCommandOptimisticLock({
736
+ resourceKind: 'customers.interaction',
737
+ resourceId: interaction.id,
738
+ current: interaction.updatedAt,
739
+ request: ctx.request ?? null,
740
+ })
741
+
716
742
  interaction.status = 'done'
717
743
  interaction.occurredAt = parsed.occurredAt ?? new Date()
718
744
  await trx.flush()
@@ -839,10 +865,20 @@ const cancelInteractionCommand: CommandHandler<InteractionCancelInput, { interac
839
865
  const em = (ctx.container.resolve('em') as EntityManager).fork()
840
866
  const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
841
867
  const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id: parsed.id, deletedAt: null })
842
- if (!interaction) throw new CrudHttpError(404, { error: 'Interaction not found' })
868
+ if (!interaction) {
869
+ enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: parsed.id, request: ctx.request ?? null })
870
+ throw new CrudHttpError(404, { error: 'Interaction not found' })
871
+ }
843
872
  ensureTenantScope(ctx, interaction.tenantId)
844
873
  ensureOrganizationScope(ctx, interaction.organizationId)
845
874
 
875
+ enforceCommandOptimisticLock({
876
+ resourceKind: 'customers.interaction',
877
+ resourceId: interaction.id,
878
+ current: interaction.updatedAt,
879
+ request: ctx.request ?? null,
880
+ })
881
+
846
882
  interaction.status = 'canceled'
847
883
  await trx.flush()
848
884
 
@@ -967,10 +1003,20 @@ const deleteInteractionCommand: CommandHandler<{ body?: Record<string, unknown>;
967
1003
  const em = (ctx.container.resolve('em') as EntityManager).fork()
968
1004
  const { interaction, entityId, nextInteractionId } = await runInTransaction(em, async (trx) => {
969
1005
  const interaction = await findOneWithDecryption(trx, CustomerInteraction, { id, deletedAt: null })
970
- if (!interaction) throw new CrudHttpError(404, { error: 'Interaction not found' })
1006
+ if (!interaction) {
1007
+ enforceRecordGoneIsConflict({ resourceKind: 'customers.interaction', resourceId: id, request: ctx.request ?? null })
1008
+ throw new CrudHttpError(404, { error: 'Interaction not found' })
1009
+ }
971
1010
  ensureTenantScope(ctx, interaction.tenantId)
972
1011
  ensureOrganizationScope(ctx, interaction.organizationId)
973
1012
 
1013
+ enforceCommandOptimisticLock({
1014
+ resourceKind: 'customers.interaction',
1015
+ resourceId: interaction.id,
1016
+ current: interaction.updatedAt,
1017
+ request: ctx.request ?? null,
1018
+ })
1019
+
974
1020
  const entityId = typeof interaction.entity === 'string' ? interaction.entity : interaction.entity.id
975
1021
  interaction.deletedAt = new Date()
976
1022
  await trx.flush()
@@ -866,7 +866,8 @@ const updatePersonCommand: CommandHandler<PersonUpdateInput, { entityId: string
866
866
  })
867
867
  await emitQueryIndexUpsertEvents(ctx, [personEntityIndexEntry(record)])
868
868
 
869
- return { entityId: record.id }
869
+ // Expose the freshly-bumped updatedAt for inline-edit sequential-save lock refresh (#2055).
870
+ return { entityId: record.id, updatedAt: record.updatedAt }
870
871
  },
871
872
  captureAfter: async (_input, result, ctx) => {
872
873
  const em = (ctx.container.resolve('em') as EntityManager).fork()
@@ -382,6 +382,7 @@ const updatePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkUpdateInpu
382
382
  const profile = await requirePersonProfile(em, person)
383
383
  const linkedCompany = await requireCompanyEntity(em, companyId, parsed.tenantId, parsed.organizationId)
384
384
 
385
+ const linkWasPrimary = link.isPrimary
385
386
  await withAtomicFlush(em, [
386
387
  async () => {
387
388
  if (parsed.isPrimary) {
@@ -389,16 +390,18 @@ const updatePersonCompanyLinkCommand: CommandHandler<PersonCompanyLinkUpdateInpu
389
390
  link.isPrimary = true
390
391
  profile.company = linkedCompany
391
392
  } else if (!parsed.isPrimary) {
392
- const linkWasPrimary = link.isPrimary
393
393
  link.isPrimary = false
394
- if (linkWasPrimary) {
395
- const remainingLinks = (await loadPersonCompanyLinks(em, person)).filter((entry) => entry.id !== link.id)
396
- await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, companyId)
397
- } else if (profile.company && typeof profile.company !== 'string' && profile.company.id === companyId) {
394
+ if (!linkWasPrimary && profile.company && typeof profile.company !== 'string' && profile.company.id === companyId) {
398
395
  profile.company = null
399
396
  }
400
397
  }
401
398
  },
399
+ async () => {
400
+ if (!parsed.isPrimary && linkWasPrimary) {
401
+ const remainingLinks = (await loadPersonCompanyLinks(em, person)).filter((entry) => entry.id !== link.id)
402
+ await promoteFallbackPrimaryLink(em, person, profile, remainingLinks, companyId)
403
+ }
404
+ },
402
405
  ], { transaction: true })
403
406
 
404
407
  const dataEngine = ctx.container.resolve('dataEngine') as DataEngine
@@ -49,18 +49,6 @@ const createPipelineStageCommand: CommandHandler<PipelineStageCreateInput, { sta
49
49
  ? existingStages.length
50
50
  : Math.max(0, Math.min(requestedOrder, existingStages.length))
51
51
 
52
- // Shift the order of every stage at or after the insert position. Skipping
53
- // this step would either duplicate `order` values (silently corrupting kanban
54
- // ordering) or push the new stage to the wrong spot when re-sorting.
55
- if (requestedOrder !== undefined) {
56
- for (const stage of existingStages) {
57
- if (stage.order >= insertOrder) {
58
- stage.order += 1
59
- stage.updatedAt = new Date()
60
- }
61
- }
62
- }
63
-
64
52
  const stage = em.create(CustomerPipelineStage, {
65
53
  organizationId: parsed.organizationId,
66
54
  tenantId: parsed.tenantId,
@@ -73,6 +61,17 @@ const createPipelineStageCommand: CommandHandler<PipelineStageCreateInput, { sta
73
61
 
74
62
  await withAtomicFlush(em, [
75
63
  () => {
64
+ // Shift the order of every stage at or after the insert position. Skipping
65
+ // this step would either duplicate `order` values (silently corrupting kanban
66
+ // ordering) or push the new stage to the wrong spot when re-sorting.
67
+ if (requestedOrder !== undefined) {
68
+ for (const existing of existingStages) {
69
+ if (existing.order >= insertOrder) {
70
+ existing.order += 1
71
+ existing.updatedAt = new Date()
72
+ }
73
+ }
74
+ }
76
75
  em.persist(stage)
77
76
  },
78
77
  async () => {
@@ -112,11 +111,12 @@ const updatePipelineStageCommand: CommandHandler<PipelineStageUpdateInput, void>
112
111
  ensureTenantScope(ctx, stage.tenantId)
113
112
  ensureOrganizationScope(ctx, stage.organizationId)
114
113
 
115
- if (parsed.label !== undefined) stage.label = parsed.label
116
- if (parsed.order !== undefined) stage.order = parsed.order
117
- stage.updatedAt = new Date()
118
-
119
114
  await withAtomicFlush(em, [
115
+ () => {
116
+ if (parsed.label !== undefined) stage.label = parsed.label
117
+ if (parsed.order !== undefined) stage.order = parsed.order
118
+ stage.updatedAt = new Date()
119
+ },
120
120
  async () => {
121
121
  if (parsed.label !== undefined || parsed.color !== undefined || parsed.icon !== undefined) {
122
122
  await ensureDictionaryEntry(em, {
@@ -87,6 +87,7 @@ export function AddressFormatSettings() {
87
87
  setPending(next)
88
88
  setError(null)
89
89
  try {
90
+ // optimistic-lock-exempt: single-row tenant address-format preference toggle — no per-record version / concurrent record edit
90
91
  const call = await apiCall<Record<string, unknown>>(
91
92
  '/api/customers/settings/address-format',
92
93
  {
@@ -8,7 +8,8 @@ import {
8
8
  DialogTitle,
9
9
  } from '@open-mercato/ui/primitives/dialog'
10
10
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
11
- import { apiCallOrThrow, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
11
+ import { apiCallOrThrow, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
12
13
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
13
14
  import { useT } from '@open-mercato/shared/lib/i18n/context'
14
15
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
@@ -220,10 +221,12 @@ function CustomerDictionarySection({ kind, title, description }: CustomerDiction
220
221
  })
221
222
  if (!confirmed) return
222
223
  try {
223
- await apiCallOrThrow(
224
- `/api/customers/dictionaries/${kind}/${encodeURIComponent(entry.id)}`,
225
- { method: 'DELETE' },
226
- { errorMessage: errorDelete },
224
+ await withScopedApiRequestHeaders(buildOptimisticLockHeader(entry.updatedAt), () =>
225
+ apiCallOrThrow(
226
+ `/api/customers/dictionaries/${kind}/${encodeURIComponent(entry.id)}`,
227
+ { method: 'DELETE' },
228
+ { errorMessage: errorDelete },
229
+ ),
227
230
  )
228
231
  flash(successDelete, 'success')
229
232
  await loadEntries()
@@ -281,14 +284,16 @@ function CustomerDictionarySection({ kind, title, description }: CustomerDiction
281
284
  closeDialog()
282
285
  return
283
286
  }
284
- await apiCallOrThrow(
285
- `/api/customers/dictionaries/${kind}/${encodeURIComponent(target.id)}`,
286
- {
287
- method: 'PATCH',
288
- headers: { 'content-type': 'application/json' },
289
- body: JSON.stringify(body),
290
- },
291
- { errorMessage: errorSave },
287
+ await withScopedApiRequestHeaders(buildOptimisticLockHeader(target.updatedAt), () =>
288
+ apiCallOrThrow(
289
+ `/api/customers/dictionaries/${kind}/${encodeURIComponent(target.id)}`,
290
+ {
291
+ method: 'PATCH',
292
+ headers: { 'content-type': 'application/json' },
293
+ body: JSON.stringify(body),
294
+ },
295
+ { errorMessage: errorSave },
296
+ ),
292
297
  )
293
298
  flash(successSave, 'success')
294
299
  }
@@ -170,6 +170,10 @@ export function DictionarySortSettings() {
170
170
  setError(null)
171
171
  try {
172
172
  await runMutation({
173
+ // optimistic-lock-exempt: tenant-scoped settings blob (dictionary sort
174
+ // modes), not a versioned per-record entity — there is no `updatedAt`
175
+ // round-trip to lock against, and concurrent writes converge on the
176
+ // last-selected preference. Mirrors other singleton settings PATCHes.
173
177
  operation: async () => {
174
178
  await apiCallOrThrow(
175
179
  '/api/customers/settings/dictionary-sort-modes',
@@ -13,7 +13,8 @@ import {
13
13
  DialogTitle,
14
14
  } from '@open-mercato/ui/primitives/dialog'
15
15
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
16
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
16
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
17
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
17
18
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
18
19
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
19
20
  import { useT } from '@open-mercato/shared/lib/i18n/context'
@@ -40,6 +41,7 @@ type Pipeline = {
40
41
  isDefault: boolean
41
42
  organizationId: string
42
43
  tenantId: string
44
+ updatedAt?: string | null
43
45
  }
44
46
 
45
47
  type PipelineStage = {
@@ -49,6 +51,7 @@ type PipelineStage = {
49
51
  order: number
50
52
  color: string | null
51
53
  icon: string | null
54
+ updatedAt?: string | null
52
55
  }
53
56
 
54
57
  type PipelineDialogState =
@@ -66,6 +69,7 @@ function normalizePipeline(raw: Record<string, unknown>): Pipeline {
66
69
  isDefault: raw.isDefault === true || raw.is_default === true,
67
70
  organizationId: typeof raw.organizationId === 'string' ? raw.organizationId : (typeof raw.organization_id === 'string' ? raw.organization_id : ''),
68
71
  tenantId: typeof raw.tenantId === 'string' ? raw.tenantId : (typeof raw.tenant_id === 'string' ? raw.tenant_id : ''),
72
+ updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : (typeof raw.updated_at === 'string' ? raw.updated_at : null),
69
73
  }
70
74
  }
71
75
 
@@ -77,6 +81,7 @@ function normalizeStage(raw: Record<string, unknown>): PipelineStage {
77
81
  order: typeof raw.order === 'number' ? raw.order : 0,
78
82
  color: typeof raw.color === 'string' && raw.color.trim().length ? raw.color.trim() : null,
79
83
  icon: typeof raw.icon === 'string' && raw.icon.trim().length ? raw.icon.trim() : null,
84
+ updatedAt: typeof raw.updatedAt === 'string' ? raw.updatedAt : (typeof raw.updated_at === 'string' ? raw.updated_at : null),
80
85
  }
81
86
  }
82
87
 
@@ -171,11 +176,15 @@ export default function PipelineSettings(): React.ReactElement {
171
176
  }
172
177
  flash(t('customers.pipelines.flash.created', 'Pipeline created'), 'success')
173
178
  } else if (pipelineDialog?.mode === 'edit') {
174
- const res = await apiCall('/api/customers/pipelines', {
175
- method: 'PUT',
176
- headers: { 'content-type': 'application/json' },
177
- body: JSON.stringify({ id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }),
178
- })
179
+ const res = await withScopedApiRequestHeaders(
180
+ buildOptimisticLockHeader(pipelineDialog.entry.updatedAt),
181
+ () =>
182
+ apiCall('/api/customers/pipelines', {
183
+ method: 'PUT',
184
+ headers: { 'content-type': 'application/json' },
185
+ body: JSON.stringify({ id: pipelineDialog.entry.id, name: pipelineForm.name.trim(), isDefault: pipelineForm.isDefault }),
186
+ }),
187
+ )
179
188
  if (!res.ok) {
180
189
  await raiseCrudError(res.response, t('customers.pipelines.errors.updateFailed', 'Failed to update pipeline'))
181
190
  return
@@ -197,11 +206,15 @@ export default function PipelineSettings(): React.ReactElement {
197
206
  variant: 'destructive',
198
207
  })
199
208
  if (!confirmed) return
200
- const res = await apiCall('/api/customers/pipelines', {
201
- method: 'DELETE',
202
- headers: { 'content-type': 'application/json' },
203
- body: JSON.stringify({ id: pipeline.id }),
204
- })
209
+ const res = await withScopedApiRequestHeaders(
210
+ buildOptimisticLockHeader(pipeline.updatedAt),
211
+ () =>
212
+ apiCall('/api/customers/pipelines', {
213
+ method: 'DELETE',
214
+ headers: { 'content-type': 'application/json' },
215
+ body: JSON.stringify({ id: pipeline.id }),
216
+ }),
217
+ )
205
218
  if (!res.ok) {
206
219
  const body = (res.result ?? {}) as Record<string, unknown>
207
220
  const msg = typeof body.error === 'string' ? body.error : t('customers.pipelines.errors.deleteFailed', 'Failed to delete pipeline')
@@ -248,11 +261,15 @@ export default function PipelineSettings(): React.ReactElement {
248
261
  flash(t('customers.pipelines.flash.stageCreated', 'Stage created'), 'success')
249
262
  await loadStages(stageDialog.pipelineId)
250
263
  } else if (stageDialog?.mode === 'edit') {
251
- const res = await apiCall('/api/customers/pipeline-stages', {
252
- method: 'PUT',
253
- headers: { 'content-type': 'application/json' },
254
- body: JSON.stringify({ id: stageDialog.entry.id, label: stageForm.label.trim(), ...appearance }),
255
- })
264
+ const res = await withScopedApiRequestHeaders(
265
+ buildOptimisticLockHeader(stageDialog.entry.updatedAt),
266
+ () =>
267
+ apiCall('/api/customers/pipeline-stages', {
268
+ method: 'PUT',
269
+ headers: { 'content-type': 'application/json' },
270
+ body: JSON.stringify({ id: stageDialog.entry.id, label: stageForm.label.trim(), ...appearance }),
271
+ }),
272
+ )
256
273
  if (!res.ok) {
257
274
  await raiseCrudError(res.response, t('customers.pipelines.errors.stageUpdateFailed', 'Failed to update stage'))
258
275
  return
@@ -274,11 +291,15 @@ export default function PipelineSettings(): React.ReactElement {
274
291
  variant: 'destructive',
275
292
  })
276
293
  if (!confirmed) return
277
- const res = await apiCall('/api/customers/pipeline-stages', {
278
- method: 'DELETE',
279
- headers: { 'content-type': 'application/json' },
280
- body: JSON.stringify({ id: stage.id }),
281
- })
294
+ const res = await withScopedApiRequestHeaders(
295
+ buildOptimisticLockHeader(stage.updatedAt),
296
+ () =>
297
+ apiCall('/api/customers/pipeline-stages', {
298
+ method: 'DELETE',
299
+ headers: { 'content-type': 'application/json' },
300
+ body: JSON.stringify({ id: stage.id }),
301
+ }),
302
+ )
282
303
  if (!res.ok) {
283
304
  const body = (res.result ?? {}) as Record<string, unknown>
284
305
  const msg = typeof body.error === 'string' ? body.error : t('customers.pipelines.errors.stageDeleteFailed', 'Failed to delete stage')