@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
@@ -6,7 +6,8 @@ import { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/b
6
6
  import { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'
7
7
  import { updateCrud } from '@open-mercato/ui/backend/utils/crud'
8
8
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
9
- import { readApiResultOrThrow, apiCall } from '@open-mercato/ui/backend/utils/apiCall'
9
+ import { readApiResultOrThrow, apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
10
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
10
11
  import { ErrorMessage, RecordNotFoundState } from '@open-mercato/ui/backend/detail'
11
12
  import { useT } from '@open-mercato/shared/lib/i18n/context'
12
13
  import { extractCustomFieldEntries } from '@open-mercato/shared/lib/crud/custom-fields-client'
@@ -15,6 +16,7 @@ type TenantFormValues = {
15
16
  id: string
16
17
  name: string
17
18
  isActive: boolean
19
+ updatedAt?: string | null
18
20
  } & Record<string, unknown>
19
21
 
20
22
  export default function EditTenantPage({ params }: { params?: { id?: string } }) {
@@ -57,6 +59,11 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
57
59
  id: String(row.id),
58
60
  name: String(row.name),
59
61
  isActive: !!row.isActive,
62
+ updatedAt: typeof row.updatedAt === 'string'
63
+ ? row.updatedAt
64
+ : typeof row.updated_at === 'string'
65
+ ? row.updated_at
66
+ : null,
60
67
  ...cfValues,
61
68
  }
62
69
  if (!cancelled) setInitial(values)
@@ -114,6 +121,7 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
114
121
  groups={groups}
115
122
  entityId={E.directory.tenant}
116
123
  initialValues={(initial || { id: tenantId, name: '', isActive: true }) as Partial<TenantFormValues>}
124
+ optimisticLockUpdatedAt={initial?.updatedAt}
117
125
  isLoading={loading}
118
126
  loadingMessage={t('directory.tenants.form.loading', 'Loading tenant…')}
119
127
  submitLabel={t('common.save', 'Save')}
@@ -137,10 +145,13 @@ export default function EditTenantPage({ params }: { params?: { id?: string } })
137
145
  await updateCrud('directory/tenants', payload)
138
146
  }}
139
147
  onDelete={async () => {
140
- const call = await apiCall(
141
- `/api/directory/tenants?id=${encodeURIComponent(tenantId)}`,
142
- { method: 'DELETE' },
143
- )
148
+ const headers = buildOptimisticLockHeader(initial?.updatedAt)
149
+ const call = await withScopedApiRequestHeaders(headers, () => (
150
+ apiCall(
151
+ `/api/directory/tenants?id=${encodeURIComponent(tenantId)}`,
152
+ { method: 'DELETE' },
153
+ )
154
+ ))
144
155
  if (!call.ok) {
145
156
  await raiseCrudError(call.response, t('directory.tenants.form.errors.delete', 'Failed to delete tenant'))
146
157
  }
@@ -9,7 +9,8 @@ import { RowActions } from '@open-mercato/ui/backend/RowActions'
9
9
  import { BooleanIcon } from '@open-mercato/ui/backend/ValueIcons'
10
10
  import type { FilterValues } from '@open-mercato/ui/backend/FilterBar'
11
11
  import { Button } from '@open-mercato/ui/primitives/button'
12
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
12
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
13
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
13
14
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
14
15
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
15
16
  import { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'
@@ -125,9 +126,12 @@ export default function DirectoryTenantsPage() {
125
126
  if (!confirmed) return
126
127
 
127
128
  try {
128
- const call = await apiCall(
129
- `/api/directory/tenants?id=${encodeURIComponent(tenant.id)}`,
130
- { method: 'DELETE' },
129
+ const call = await withScopedApiRequestHeaders(
130
+ buildOptimisticLockHeader(tenant.updatedAt),
131
+ () => apiCall(
132
+ `/api/directory/tenants?id=${encodeURIComponent(tenant.id)}`,
133
+ { method: 'DELETE' },
134
+ ),
131
135
  )
132
136
  if (!call.ok) {
133
137
  await raiseCrudError(call.response, t('directory.tenants.list.error.delete', 'Failed to delete tenant'))
@@ -689,7 +689,10 @@ const deleteOrganizationCommand: CommandHandler<{ body: any; query: Record<strin
689
689
  setInternalTenantId(deleted, tenantId)
690
690
  deleted.isActive = false
691
691
  deleted.parentId = null
692
-
692
+ resolvedDeleted = deleted
693
+ },
694
+ async () => {
695
+ const deleted = resolvedDeleted
693
696
  const childrenFilter: FilterQuery<Organization> = { tenant: tenantId, parentId: id, deletedAt: null }
694
697
  const children = await em.find(Organization, childrenFilter)
695
698
  const toPersist: Organization[] = []
@@ -698,11 +701,11 @@ const deleteOrganizationCommand: CommandHandler<{ body: any; query: Record<strin
698
701
  toPersist.push(child)
699
702
  }
700
703
  toPersist.push(deleted)
701
- if (toPersist.length) await em.persist(toPersist).flush()
704
+ if (toPersist.length) em.persist(toPersist)
702
705
  setUndoMeta(deleted, { childParentsBefore: childSnapshotsBefore })
703
-
706
+ },
707
+ async () => {
704
708
  await rebuildHierarchyForTenant(em, tenantId)
705
- resolvedDeleted = deleted
706
709
  },
707
710
  ], { transaction: true })
708
711
 
@@ -10,6 +10,34 @@ import { parseBooleanToken, parseBooleanWithDefault } from '@open-mercato/shared
10
10
  import { setRecordCustomFields } from '../lib/helpers'
11
11
  import { CustomFieldValue } from '../data/entities'
12
12
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
13
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
14
+ import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
15
+
16
+ const CUSTOM_ENTITY_RECORD_RESOURCE_KIND = 'entities.record'
17
+
18
+ async function readCustomEntityRecordUpdatedAt(
19
+ em: any,
20
+ input: { entityType: string; entityId: string; organizationId: string | null },
21
+ ): Promise<string | null> {
22
+ try {
23
+ const db = em.getKysely()
24
+ let query = db
25
+ .selectFrom('custom_entities_storage' as any)
26
+ .select(['updated_at' as any])
27
+ .where('entity_type' as any, '=', input.entityType)
28
+ .where('entity_id' as any, '=', input.entityId)
29
+ query = input.organizationId === null
30
+ ? query.where('organization_id' as any, 'is', null as any)
31
+ : query.where('organization_id' as any, '=', input.organizationId)
32
+ const row = await query.executeTakeFirst()
33
+ const value = (row as any)?.updated_at
34
+ if (value instanceof Date) return value.toISOString()
35
+ if (typeof value === 'string' && value.length > 0) return value
36
+ return null
37
+ } catch {
38
+ return null
39
+ }
40
+ }
13
41
 
14
42
  export const metadata = {
15
43
  GET: { requireAuth: true, requireFeatures: ['entities.records.view'] },
@@ -85,6 +113,23 @@ export async function GET(req: Request) {
85
113
  const found = await em.findOne(CustomEntity as any, { entityId, isActive: true })
86
114
  isCustomEntity = !!found
87
115
  } catch {}
116
+ // Read/write symmetry: this endpoint writes every record to custom_entities_storage
117
+ // via the data engine, including module-declared custom entities whose id is a
118
+ // frozen system id and therefore never registered in `custom_entities`. Detect
119
+ // those by their doc-storage rows so `mapRow` strips the `cf_` prefix and the edit
120
+ // form can read back the saved values (mirrors HybridQueryEngine.isCustomEntity).
121
+ if (!isCustomEntity) {
122
+ try {
123
+ const db = em.getKysely()
124
+ const row = await db
125
+ .selectFrom('custom_entities_storage' as any)
126
+ .select(['entity_id' as any])
127
+ .where('entity_type' as any, '=', entityId)
128
+ .limit(1)
129
+ .executeTakeFirst()
130
+ isCustomEntity = !!row
131
+ } catch {}
132
+ }
88
133
  if (organizationIds && organizationIds.length === 0) {
89
134
  return NextResponse.json({ items: [], total: 0, page, pageSize, totalPages: 0 })
90
135
  }
@@ -164,6 +209,41 @@ export async function GET(req: Request) {
164
209
  const rawItems = res.items || []
165
210
  const viewPageItems = rawItems.map(mapRow)
166
211
  const fullPageItems = rawItems.map(mapFullRow)
212
+
213
+ // Expose `updated_at` on custom-entity records. The query engine returns only
214
+ // the `doc` fields + `id`, dropping the base `updated_at` column — which made
215
+ // optimistic locking impossible end-to-end (no version for the edit page to
216
+ // round-trip as the lock header). Batch-read it from storage and merge it in.
217
+ if (isCustomEntity && viewPageItems.length) {
218
+ try {
219
+ const recordIds = viewPageItems
220
+ .map((it: any) => it?.id)
221
+ .filter((v: any): v is string => typeof v === 'string' && v.length > 0)
222
+ if (recordIds.length) {
223
+ const db = em.getKysely()
224
+ const rows = await db
225
+ .selectFrom('custom_entities_storage' as any)
226
+ .select(['entity_id' as any, 'updated_at' as any])
227
+ .where('entity_type' as any, '=', entityId)
228
+ .where('entity_id' as any, 'in', recordIds as any)
229
+ .execute()
230
+ const updatedById = new Map<string, string>()
231
+ for (const row of rows as any[]) {
232
+ const value = row?.updated_at
233
+ const iso = value instanceof Date ? value.toISOString() : (typeof value === 'string' && value.length > 0 ? value : null)
234
+ if (iso && row?.entity_id) updatedById.set(String(row.entity_id), iso)
235
+ }
236
+ for (const item of viewPageItems as any[]) {
237
+ const iso = updatedById.get(String(item?.id))
238
+ if (iso) {
239
+ item.updated_at = iso
240
+ item.updatedAt = iso
241
+ }
242
+ }
243
+ }
244
+ } catch { /* best-effort: locking simply will not engage if storage is unavailable */ }
245
+ }
246
+
167
247
  const total = typeof res.total === 'number' ? res.total : rawItems.length
168
248
  const effectivePageSize = res.pageSize || pageSize
169
249
  const payload = {
@@ -343,6 +423,25 @@ export async function PUT(req: Request) {
343
423
  return NextResponse.json({ ok: true, item: { entityId, recordId: created.id } })
344
424
  }
345
425
 
426
+ try {
427
+ const currentUpdatedAt = await readCustomEntityRecordUpdatedAt(em, {
428
+ entityType: entityId,
429
+ entityId: rid,
430
+ organizationId: targetOrgId,
431
+ })
432
+ enforceCommandOptimisticLock({
433
+ resourceKind: CUSTOM_ENTITY_RECORD_RESOURCE_KIND,
434
+ resourceId: rid,
435
+ current: currentUpdatedAt,
436
+ request: req,
437
+ })
438
+ } catch (lockError) {
439
+ if (isCrudHttpError(lockError)) {
440
+ return NextResponse.json(lockError.body, { status: lockError.status })
441
+ }
442
+ throw lockError
443
+ }
444
+
346
445
  await de.updateCustomEntityRecord({
347
446
  entityId,
348
447
  recordId: rid,
@@ -79,6 +79,13 @@ export default function EditRecordPage({ params }: { params: { entityId?: string
79
79
  entityId={entityId}
80
80
  customEntity
81
81
  initialValues={initialValues || {}}
82
+ optimisticLockUpdatedAt={
83
+ typeof initialValues?.updatedAt === 'string'
84
+ ? initialValues.updatedAt
85
+ : typeof initialValues?.updated_at === 'string'
86
+ ? initialValues.updated_at
87
+ : null
88
+ }
82
89
  isLoading={loading}
83
90
  loadingMessage={t('entities.userEntities.records.loading', 'Loading record...')}
84
91
  submitLabel={t('entities.userEntities.records.form.submitSave', 'Save')}
@@ -11,7 +11,8 @@ import { ContextHelp } from '@open-mercato/ui/backend/ContextHelp'
11
11
  import { Button } from '@open-mercato/ui/primitives/button'
12
12
  import { RowActions } from '@open-mercato/ui/backend/RowActions'
13
13
  import Link from 'next/link'
14
- import { apiCall, readApiResultOrThrow } from '@open-mercato/ui/backend/utils/apiCall'
14
+ import { apiCall, readApiResultOrThrow, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
15
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
15
16
  import { flash } from '@open-mercato/ui/backend/FlashMessages'
16
17
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
17
18
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
@@ -349,9 +350,12 @@ export RECORD_ID="<record uuid>"`}</code></pre>
349
350
  variant: 'destructive',
350
351
  })
351
352
  if (!confirmed) return
352
- const deleteCall = await apiCall(
353
- `/api/entities/records?entityId=${encodeURIComponent(entityId)}&recordId=${encodeURIComponent(String((row as any).id))}`,
354
- { method: 'DELETE' },
353
+ const deleteCall = await withScopedApiRequestHeaders(
354
+ buildOptimisticLockHeader((row as any).updatedAt),
355
+ () => apiCall(
356
+ `/api/entities/records?entityId=${encodeURIComponent(entityId)}&recordId=${encodeURIComponent(String((row as any).id))}`,
357
+ { method: 'DELETE' },
358
+ ),
355
359
  )
356
360
  if (!deleteCall.ok) {
357
361
  await raiseCrudError(deleteCall.response, 'Failed to delete record')
@@ -186,6 +186,23 @@ export async function setRecordCustomFields(
186
186
 
187
187
  if (toPersist.length) em.persist(toPersist)
188
188
  await em.flush()
189
+ if (process.env.OM_CF_DEBUG) {
190
+ try {
191
+ const conn = em.getConnection()
192
+ for (const fieldKey of keys) {
193
+ if (values[fieldKey] === undefined) continue
194
+ const rows = await conn.execute(
195
+ 'select value_text, value_multiline, value_int, value_float, value_bool from custom_field_values where entity_id = ? and record_id = ? and field_key = ? and ((organization_id is null and ? is null) or organization_id = ?) and ((tenant_id is null and ? is null) or tenant_id = ?)',
196
+ [entityId, recordId, fieldKey, organizationId, organizationId, tenantId, tenantId],
197
+ 'all',
198
+ ) as Array<Record<string, unknown>>
199
+ const persisted = rows.map((row) => row.value_text ?? row.value_multiline ?? row.value_int ?? row.value_float ?? row.value_bool)
200
+ console.warn(`[CF_DEBUG] setRecordCustomFields entityId=${entityId} recordId=${recordId} fieldKey=${fieldKey} input=${JSON.stringify(values[fieldKey])} persistedRows=${rows.length} persisted=${JSON.stringify(persisted)}`)
201
+ }
202
+ } catch (err) {
203
+ console.warn(`[CF_DEBUG] re-query failed: ${(err as Error)?.message ?? String(err)}`)
204
+ }
205
+ }
189
206
  // Emit hook for indexing if requested (outside CRUD flows)
190
207
  try {
191
208
  if (typeof opts.onChanged === 'function') {
@@ -45,6 +45,7 @@ export async function GET(req: Request, { params }: { params: { id: string } })
45
45
  tenantName: tenant.name,
46
46
  tenantId: tenant.id,
47
47
  toggleType: toggle.type,
48
+ updatedAt: override?.updatedAt instanceof Date ? override.updatedAt.toISOString() : null,
48
49
  }
49
50
 
50
51
  await logCrudAccess({
@@ -6,7 +6,9 @@ import type { CommandBus } from '@open-mercato/shared/lib/commands'
6
6
  import { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'
7
7
  import { getOverrides } from '../../lib/queries'
8
8
  import { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
9
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
9
10
  import { logCrudAccess } from '@open-mercato/shared/lib/crud/factory'
11
+ import { FeatureToggleOverride } from '../../data/entities'
10
12
  import { buildContext } from '../../lib/utils'
11
13
  import { resolveFeatureCheckContext } from "@open-mercato/core/modules/directory/utils/organizationScope"
12
14
  import { ProcessedChangeOverrideStateInput } from '../../data/validators'
@@ -87,6 +89,23 @@ export async function PUT(req: Request) {
87
89
  }, { status: 400 })
88
90
  }
89
91
 
92
+ // Optimistic lock: refuse a stale override overwrite when two admins edit the
93
+ // same global toggle override in parallel. Only enforced when an override row
94
+ // already exists (first set has no prior version). Strictly additive.
95
+ const em = ctx.container.resolve('em') as EntityManager
96
+ const existingOverride = await em.findOne(FeatureToggleOverride, {
97
+ tenantId: scope.tenantId,
98
+ toggle: { id: parsed.data.toggleId },
99
+ })
100
+ if (existingOverride) {
101
+ enforceCommandOptimisticLock({
102
+ resourceKind: 'feature_toggles.feature_toggle_override',
103
+ resourceId: existingOverride.id,
104
+ current: existingOverride.updatedAt ?? null,
105
+ request: req,
106
+ })
107
+ }
108
+
90
109
  const commandBus = ctx.container.resolve('commandBus') as CommandBus
91
110
  const { result, logEntry } = await commandBus.execute<ProcessedChangeOverrideStateInput, { overrideToggleId: string | null }>('feature_toggles.overrides.changeState', {
92
111
  input: {
@@ -10,7 +10,6 @@ import { createFieldDefinitions, createFormGroups } from "@open-mercato/core/mod
10
10
 
11
11
 
12
12
  export default function EditFeatureTogglePage({ params }: { params?: { id?: string } }) {
13
- const [initialValues, setInitialValues] = React.useState<any>(null)
14
13
  const { id } = params ?? {}
15
14
  const t = useT()
16
15
  const fields = createFieldDefinitions(t);
@@ -18,30 +17,37 @@ export default function EditFeatureTogglePage({ params }: { params?: { id?: stri
18
17
 
19
18
  const { data: featureToggleItem, isLoading } = useFeatureToggleItem(id)
20
19
 
21
- React.useEffect(() => {
22
- if (featureToggleItem) {
23
- setInitialValues({
24
- identifier: featureToggleItem.identifier,
25
- name: featureToggleItem.name,
26
- description: featureToggleItem.description,
27
- category: featureToggleItem.category,
28
-
29
- type: featureToggleItem.type,
30
- defaultValue: featureToggleItem.defaultValue,
31
- })
20
+ // Derive the form's initial values synchronously from the loaded record. Using
21
+ // a useState+useEffect here caused #2452: `isLoading` flips to false one render
22
+ // before the effect set `initialValues`, so CrudForm mounted with `{}` and the
23
+ // `type` SELECT captured an empty value (text inputs re-sync from the prop, the
24
+ // select does not). Deriving here means the value is present in the same render
25
+ // the data arrives; the `key` below also forces a clean remount on load.
26
+ const initialValues = React.useMemo(() => {
27
+ if (!featureToggleItem) return null
28
+ return {
29
+ id: featureToggleItem.id ?? (id ? String(id) : ''),
30
+ identifier: featureToggleItem.identifier,
31
+ name: featureToggleItem.name,
32
+ description: featureToggleItem.description,
33
+ category: featureToggleItem.category,
34
+ type: featureToggleItem.type,
35
+ defaultValue: featureToggleItem.defaultValue,
32
36
  }
33
- }, [featureToggleItem])
37
+ }, [featureToggleItem, id])
34
38
 
35
39
  return (
36
40
  <Page>
37
41
  <PageBody>
38
42
  <CrudForm
43
+ key={initialValues ? 'ft-edit-loaded' : 'ft-edit-loading'}
39
44
  title={t('feature_toggles.form.title.edit', 'Edit Feature Toggle')}
40
45
  backHref="/backend/feature-toggles/global"
41
46
  versionHistory={{ resourceKind: 'feature_toggles.global', resourceId: id ? String(id) : '' }}
42
47
  fields={fields}
43
48
  entityId={E.feature_toggles.feature_toggle}
44
49
  initialValues={initialValues ?? {}}
50
+ optimisticLockUpdatedAt={featureToggleItem?.updatedAt ?? null}
45
51
  isLoading={isLoading}
46
52
  groups={formGroups}
47
53
  loadingMessage={t('feature_toggles.form.loading', 'Loading feature toggles')}
@@ -2,7 +2,9 @@
2
2
  import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@open-mercato/ui/primitives/card'
3
3
  import { DataLoader, ErrorNotice } from "@open-mercato/ui";
4
4
  import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
5
- import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
5
+ import { apiCall, withScopedApiRequestHeaders } from "@open-mercato/ui/backend/utils/apiCall";
6
+ import { buildOptimisticLockHeader } from "@open-mercato/ui/backend/utils/optimisticLock";
7
+ import { surfaceRecordConflict } from "@open-mercato/ui/backend/conflicts";
6
8
  import { raiseCrudError } from "@open-mercato/ui/backend/utils/serverErrors";
7
9
  import { useT } from "@open-mercato/shared/lib/i18n/context";
8
10
  import { CrudForm } from "@open-mercato/ui/backend/CrudForm";
@@ -28,23 +30,31 @@ export function FeatureToggleOverrideCard({ toggleId }: { toggleId: string }) {
28
30
 
29
31
  const mutation = useMutation({
30
32
  mutationFn: async (input: { toggleId: string; isOverride: boolean; overrideValue?: any; tenantId: string }) => {
31
- const call = await apiCall<{ ok: boolean }>(
32
- `/api/feature_toggles/overrides`,
33
- {
34
- method: 'PUT',
35
- headers: { 'content-type': 'application/json' },
36
- body: JSON.stringify({
37
- toggleId: input.toggleId,
38
- isOverride: input.isOverride,
39
- overrideValue: input.overrideValue,
40
- }),
41
- },
33
+ const call = await withScopedApiRequestHeaders(
34
+ buildOptimisticLockHeader(overrideData?.updatedAt ?? null),
35
+ () => apiCall<{ ok: boolean }>(
36
+ `/api/feature_toggles/overrides`,
37
+ {
38
+ method: 'PUT',
39
+ headers: { 'content-type': 'application/json' },
40
+ body: JSON.stringify({
41
+ toggleId: input.toggleId,
42
+ isOverride: input.isOverride,
43
+ overrideValue: input.overrideValue,
44
+ }),
45
+ },
46
+ ),
42
47
  )
43
48
  if (!call.ok) {
44
49
  await raiseCrudError(call.response, t('feature_toggles.overrides.error.update', 'Failed to update override'))
45
50
  }
46
51
  return call.result
47
52
  },
53
+ onError: (err: unknown) => {
54
+ // Route an optimistic-lock 409 to the unified conflict bar; other errors
55
+ // already surfaced via raiseCrudError inside mutationFn.
56
+ surfaceRecordConflict(err, t)
57
+ },
48
58
  onSettled: async () => {
49
59
  await queryClient.invalidateQueries({ queryKey: ['feature_toggle_override', toggleId] })
50
60
  await queryClient.invalidateQueries({ queryKey: ['feature_toggle_overrides'] })
@@ -2,7 +2,8 @@
2
2
  import { DataTable } from "@open-mercato/ui/backend/DataTable";
3
3
  import { RowActions } from "@open-mercato/ui/backend/RowActions";
4
4
  import { useT } from "@open-mercato/shared/lib/i18n/context";
5
- import { apiCall } from '@open-mercato/ui/backend/utils/apiCall'
5
+ import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
6
+ import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
6
7
  import { raiseCrudError } from '@open-mercato/ui/backend/utils/serverErrors'
7
8
  import { useQuery, useQueryClient } from '@tanstack/react-query'
8
9
  import type { ColumnDef, SortingState } from '@tanstack/react-table'
@@ -23,6 +24,7 @@ type Row = {
23
24
  description: string
24
25
  category?: string
25
26
  type: FeatureToggleType
27
+ updatedAt?: string | null
26
28
  }
27
29
 
28
30
  type BadgeVariant = 'default' | 'secondary' | 'destructive' | 'outline' | 'muted'
@@ -126,7 +128,10 @@ export function FeatureTogglesTable() {
126
128
 
127
129
  const deleteFeatureToggleMutation = useMutation({
128
130
  mutationFn: async (row: Row) => {
129
- await deleteCrud('feature_toggles/global', row.id)
131
+ await withScopedApiRequestHeaders(
132
+ buildOptimisticLockHeader(row.updatedAt),
133
+ () => deleteCrud('feature_toggles/global', row.id),
134
+ )
130
135
  },
131
136
  onSuccess: async () => {
132
137
  await queryClient.invalidateQueries({ queryKey: ['feature_toggles'] })
@@ -10,6 +10,7 @@ import {
10
10
  SelectValue,
11
11
  } from "@open-mercato/ui/primitives/select";
12
12
  import { useT } from '@open-mercato/shared/lib/i18n/context'
13
+ import { booleanOverrideSelectValue } from "./overrideFormConfig";
13
14
 
14
15
 
15
16
  export function renderDefaultValueCreateComponent(props: CrudCustomFieldRenderProps) {
@@ -22,7 +23,7 @@ export function renderDefaultValueCreateComponent(props: CrudCustomFieldRenderPr
22
23
  <div>
23
24
  <label className="block text-sm font-medium mb-2">{t('feature_toggles.form.fields.defaultValue.boolean.label', 'Default Value (Boolean)')}</label>
24
25
  <Select
25
- value={props.value as string || 'false'}
26
+ value={booleanOverrideSelectValue(props.value)}
26
27
  onValueChange={(value) => props.setValue(value === 'true')}
27
28
  disabled={props.disabled}
28
29
  >
@@ -11,6 +11,15 @@ import {
11
11
  } from "@open-mercato/ui/primitives/select";
12
12
  import { useT } from '@open-mercato/shared/lib/i18n/context'
13
13
 
14
+ /**
15
+ * Normalize a boolean override value (which may be stored as a real boolean or a
16
+ * 'true'/'false' string) to the string the `<Select>` needs. Passing a raw boolean
17
+ * to `Select.value` matched no `<SelectItem>` and rendered blank (#2410).
18
+ */
19
+ export function booleanOverrideSelectValue(value: unknown): 'true' | 'false' {
20
+ return value === true || value === 'true' ? 'true' : 'false'
21
+ }
22
+
14
23
  export function renderOverrideValueComponent(props: CrudCustomFieldRenderProps) {
15
24
  const t = useT()
16
25
  const toggleType = props.values?.toggleType as string;
@@ -30,7 +39,7 @@ export function renderOverrideValueComponent(props: CrudCustomFieldRenderProps)
30
39
  <div>
31
40
  <label className="block text-sm font-medium mb-2">{t('feature_toggles.override.fields.value.boolean.label', 'Override Value (Boolean)')}</label>
32
41
  <Select
33
- value={props.value as string || 'false'}
42
+ value={booleanOverrideSelectValue(props.value)}
34
43
  onValueChange={(value) => props.setValue(value === 'true')}
35
44
  disabled={props.disabled}
36
45
  >
@@ -1,13 +1,20 @@
1
1
  import { z } from 'zod'
2
2
 
3
- export const IDENTIFIER_PATTERN = /^[a-z][a-z0-9_]*$/
3
+ // Namespaced feature-flag identifiers use dots and dashes as separators
4
+ // (e.g. the seeded `customers.interactions.legacy-adapters`), so the validator
5
+ // accepts lowercase letters, digits, `_`, `.`, and `-` after the leading
6
+ // lowercase letter (#2055 QA round-6).
7
+ export const IDENTIFIER_PATTERN = /^[a-z][a-z0-9_.-]*$/
8
+
9
+ export const IDENTIFIER_PATTERN_MESSAGE =
10
+ 'identifier must start with a lowercase letter and contain only lowercase letters, numbers, dots, dashes, and underscores'
4
11
 
5
12
  export const toggleTypeSchema = z.enum(['boolean', 'string', 'number', 'json'])
6
13
 
7
14
  export const toggleCreateSchema = z.object({
8
15
  identifier: z.string().min(1).regex(
9
16
  IDENTIFIER_PATTERN,
10
- 'identifier must start with a lowercase letter and contain only lowercase letters, numbers, and underscores',
17
+ IDENTIFIER_PATTERN_MESSAGE,
11
18
  ),
12
19
  name: z.string().min(1),
13
20
  description: z.string().nullable().optional(),
@@ -42,6 +49,7 @@ export const featureToggleOverrideResponseSchema = z.object({
42
49
  tenantName: z.string(),
43
50
  tenantId: z.string().uuid(),
44
51
  toggleType: toggleTypeSchema,
52
+ updatedAt: z.string().nullable().optional(),
45
53
  })
46
54
 
47
55
  export type FeatureToggleOverrideResponse = z.infer<typeof featureToggleOverrideResponseSchema>
@@ -70,7 +78,7 @@ export const featureToggleSchema = z.object({
70
78
  id: z.string().uuid().optional(),
71
79
  identifier: z.string().min(1).regex(
72
80
  IDENTIFIER_PATTERN,
73
- 'identifier must start with a lowercase letter and contain only lowercase letters, numbers, and underscores',
81
+ IDENTIFIER_PATTERN_MESSAGE,
74
82
  ),
75
83
  name: z.string().min(1),
76
84
  description: z.string().nullable().optional(),
@@ -2,6 +2,8 @@ import { NextResponse } from 'next/server'
2
2
  import type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'
3
3
  import { runWithCacheTenant } from '@open-mercato/cache'
4
4
  import { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'
5
+ import { enforceCommandOptimisticLock } from '@open-mercato/shared/lib/crud/optimistic-lock-command'
6
+ import { isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'
5
7
  import { InboxSettings } from '../../data/entities'
6
8
  import { updateSettingsSchema } from '../../data/validators'
7
9
  import { resolveRequestContext, handleRouteError } from '../routeHelpers'
@@ -49,6 +51,7 @@ export async function GET(req: Request) {
49
51
  inboxAddress: settings.inboxAddress,
50
52
  isActive: settings.isActive,
51
53
  workingLanguage: settings.workingLanguage,
54
+ updatedAt: settings.updatedAt instanceof Date ? settings.updatedAt.toISOString() : (settings.updatedAt ?? null),
52
55
  } : null,
53
56
  }
54
57
 
@@ -96,6 +99,20 @@ export async function PATCH(req: Request) {
96
99
  return NextResponse.json({ error: 'Settings not found' }, { status: 404 })
97
100
  }
98
101
 
102
+ // Optimistic lock: refuse a stale overwrite when two tabs edit the same inbox
103
+ // settings record. Strictly additive — a no-op without the expected-version header.
104
+ try {
105
+ enforceCommandOptimisticLock({
106
+ resourceKind: 'inbox_ops.settings',
107
+ resourceId: settings.id,
108
+ current: settings.updatedAt ?? null,
109
+ request: req,
110
+ })
111
+ } catch (err) {
112
+ if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })
113
+ throw err
114
+ }
115
+
99
116
  if (parsed.data.workingLanguage !== undefined) {
100
117
  settings.workingLanguage = parsed.data.workingLanguage
101
118
  }
@@ -115,6 +132,7 @@ export async function PATCH(req: Request) {
115
132
  inboxAddress: settings.inboxAddress,
116
133
  isActive: settings.isActive,
117
134
  workingLanguage: settings.workingLanguage,
135
+ updatedAt: settings.updatedAt instanceof Date ? settings.updatedAt.toISOString() : (settings.updatedAt ?? null),
118
136
  },
119
137
  })
120
138
  } catch (err) {