@open-mercato/core 0.8.1-develop.7274.1.21a0083828 → 0.8.1-develop.7294.1.0ef99fc518

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 (261) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/users/acl/route.js.map +2 -2
  3. package/dist/modules/auth/components/AclEditor.js +21 -3
  4. package/dist/modules/auth/components/AclEditor.js.map +2 -2
  5. package/dist/modules/customers/backend/customers/companies/page.js +6 -4
  6. package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
  7. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js +6 -4
  8. package/dist/modules/customers/backend/customers/deals/map/components/DealsMapView.js.map +2 -2
  9. package/dist/modules/customers/backend/customers/deals/page.js +6 -4
  10. package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
  11. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js +5 -3
  12. package/dist/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.js.map +2 -2
  13. package/dist/modules/customers/backend/customers/deals/pipeline/page.js +6 -4
  14. package/dist/modules/customers/backend/customers/deals/pipeline/page.js.map +2 -2
  15. package/dist/modules/customers/backend/customers/people/page.js +6 -4
  16. package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
  17. package/dist/modules/customers/components/calendar/editor/PeopleField.js +9 -2
  18. package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
  19. package/dist/modules/customers/components/calendar/editor/hooks.js +4 -2
  20. package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
  21. package/dist/modules/customers/components/calendar/editor/lookups.js +7 -3
  22. package/dist/modules/customers/components/calendar/editor/lookups.js.map +2 -2
  23. package/dist/modules/customers/components/detail/AssignRoleDialog.js +5 -2
  24. package/dist/modules/customers/components/detail/AssignRoleDialog.js.map +2 -2
  25. package/dist/modules/customers/components/detail/RoleAssignmentRow.js +4 -2
  26. package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
  27. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js +8 -5
  28. package/dist/modules/customers/components/detail/ScheduleActivityDialog.js.map +2 -2
  29. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js +10 -7
  30. package/dist/modules/customers/components/detail/schedule/DateTimeFields.js.map +2 -2
  31. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -2
  32. package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
  33. package/dist/modules/customers/components/detail/schedule/fieldConfig.js +9 -0
  34. package/dist/modules/customers/components/detail/schedule/fieldConfig.js.map +2 -2
  35. package/dist/modules/customers/components/detail/schedule/index.js +3 -1
  36. package/dist/modules/customers/components/detail/schedule/index.js.map +2 -2
  37. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +11 -4
  38. package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
  39. package/dist/modules/customers/components/formConfig.js +17 -9
  40. package/dist/modules/customers/components/formConfig.js.map +2 -2
  41. package/dist/modules/customers/data/validators.js +12 -7
  42. package/dist/modules/customers/data/validators.js.map +2 -2
  43. package/dist/modules/customers/lib/assignableStaff.js +103 -22
  44. package/dist/modules/customers/lib/assignableStaff.js.map +2 -2
  45. package/dist/modules/customers/setup.js +10 -2
  46. package/dist/modules/customers/setup.js.map +2 -2
  47. package/dist/modules/data_sync/api/schedules/[id]/route.js +5 -0
  48. package/dist/modules/data_sync/api/schedules/[id]/route.js.map +2 -2
  49. package/dist/modules/data_sync/api/schedules/route.js +5 -0
  50. package/dist/modules/data_sync/api/schedules/route.js.map +2 -2
  51. package/dist/modules/data_sync/backend/data-sync/page.js +29 -5
  52. package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
  53. package/dist/modules/data_sync/components/IntegrationScheduleTab.js +48 -11
  54. package/dist/modules/data_sync/components/IntegrationScheduleTab.js.map +2 -2
  55. package/dist/modules/data_sync/data/validators.js +13 -2
  56. package/dist/modules/data_sync/data/validators.js.map +2 -2
  57. package/dist/modules/data_sync/lib/schedule-value.js +30 -0
  58. package/dist/modules/data_sync/lib/schedule-value.js.map +7 -0
  59. package/dist/modules/eudr/data/validators.js.map +2 -2
  60. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js +3 -4
  61. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.js.map +2 -2
  62. package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js +3 -1
  63. package/dist/modules/portal/frontend/[orgSlug]/portal/invite/page.js.map +2 -2
  64. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js +2 -1
  65. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.js.map +2 -2
  66. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js +3 -4
  67. package/dist/modules/portal/frontend/[orgSlug]/portal/page.js.map +2 -2
  68. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js +3 -4
  69. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.js.map +2 -2
  70. package/dist/modules/portal/lib/navigation.js +3 -4
  71. package/dist/modules/portal/lib/navigation.js.map +2 -2
  72. package/dist/modules/query_index/lib/engine.js +79 -14
  73. package/dist/modules/query_index/lib/engine.js.map +2 -2
  74. package/dist/modules/staff/backend/staff/time-tracking/page.js +122 -86
  75. package/dist/modules/staff/backend/staff/time-tracking/page.js.map +2 -2
  76. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js +38 -17
  77. package/dist/modules/staff/backend/staff/time-tracking/projects/[id]/page.js.map +2 -2
  78. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js +45 -18
  79. package/dist/modules/staff/backend/staff/time-tracking/reports/[id]/page.js.map +2 -2
  80. package/dist/modules/staff/extension-points.js +16 -0
  81. package/dist/modules/staff/extension-points.js.map +2 -2
  82. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js +3 -4
  83. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.js.map +2 -2
  84. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js +3 -4
  85. package/dist/modules/staff/frontend/[orgSlug]/portal/time-reports/page.js.map +2 -2
  86. package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js +14 -13
  87. package/dist/modules/staff/lib/time-tracking-ui/ReportSheet.js.map +2 -2
  88. package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js +60 -41
  89. package/dist/modules/staff/lib/time-tracking-ui/TaskListView.js.map +2 -2
  90. package/dist/modules/warranty_claims/api/ai/assess/route.js +2 -2
  91. package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
  92. package/dist/modules/warranty_claims/api/ai/draft-reply/route.js +3 -4
  93. package/dist/modules/warranty_claims/api/ai/draft-reply/route.js.map +3 -3
  94. package/dist/modules/warranty_claims/api/ai/suggest/route.js +3 -3
  95. package/dist/modules/warranty_claims/api/ai/suggest/route.js.map +2 -2
  96. package/dist/modules/warranty_claims/api/assign/route.js +3 -3
  97. package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
  98. package/dist/modules/warranty_claims/api/credit-memo/route.js +2 -2
  99. package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
  100. package/dist/modules/warranty_claims/api/entitlement/route.js +2 -2
  101. package/dist/modules/warranty_claims/api/entitlement/route.js.map +2 -2
  102. package/dist/modules/warranty_claims/api/events/route.js +4 -4
  103. package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
  104. package/dist/modules/warranty_claims/api/external/claims/route.js +3 -3
  105. package/dist/modules/warranty_claims/api/external/claims/route.js.map +2 -2
  106. package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +2 -2
  107. package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
  108. package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +2 -2
  109. package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
  110. package/dist/modules/warranty_claims/api/portal/claims/route.js +2 -2
  111. package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
  112. package/dist/modules/warranty_claims/api/portal/events/route.js +2 -2
  113. package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
  114. package/dist/modules/warranty_claims/api/receiving/route.js +2 -2
  115. package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
  116. package/dist/modules/warranty_claims/api/replacement-order/route.js +2 -2
  117. package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
  118. package/dist/modules/warranty_claims/api/return-label/route.js +2 -2
  119. package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
  120. package/dist/modules/warranty_claims/api/risk/route.js +2 -2
  121. package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
  122. package/dist/modules/warranty_claims/api/sales-return/route.js +2 -2
  123. package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
  124. package/dist/modules/warranty_claims/api/settings-general/route.js +4 -4
  125. package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
  126. package/dist/modules/warranty_claims/api/stats/route.js +2 -2
  127. package/dist/modules/warranty_claims/api/stats/route.js.map +2 -2
  128. package/dist/modules/warranty_claims/api/submit/route.js +3 -3
  129. package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
  130. package/dist/modules/warranty_claims/api/transition/route.js +3 -3
  131. package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
  132. package/dist/modules/warranty_claims/api/vendor-recovery/route.js +3 -3
  133. package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
  134. package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -2
  135. package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
  136. package/dist/modules/warranty_claims/backend/page.js +4 -2
  137. package/dist/modules/warranty_claims/backend/page.js.map +2 -2
  138. package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js +9 -2
  139. package/dist/modules/warranty_claims/backend/warranty_claims/[id]/page.js.map +2 -2
  140. package/dist/modules/warranty_claims/backend/warranty_claims/settings/page.js +4 -2
  141. package/dist/modules/warranty_claims/backend/warranty_claims/settings/page.js.map +2 -2
  142. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js +3 -4
  143. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.js.map +2 -2
  144. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.js +3 -2
  145. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.js.map +2 -2
  146. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -2
  147. package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
  148. package/dist/modules/workflows/components/DefinitionTriggersEditor.js +1 -1
  149. package/dist/modules/workflows/components/DefinitionTriggersEditor.js.map +2 -2
  150. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js +2 -2
  151. package/dist/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.js.map +2 -2
  152. package/dist/modules/workflows/lib/event-trigger-service.js +56 -1
  153. package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
  154. package/package.json +7 -7
  155. package/src/modules/auth/api/users/acl/route.ts +4 -3
  156. package/src/modules/auth/components/AclEditor.tsx +38 -2
  157. package/src/modules/auth/i18n/de.json +5 -1
  158. package/src/modules/auth/i18n/en.json +5 -1
  159. package/src/modules/auth/i18n/es.json +5 -1
  160. package/src/modules/auth/i18n/ko.json +5 -1
  161. package/src/modules/auth/i18n/pl.json +5 -1
  162. package/src/modules/customers/backend/customers/companies/page.tsx +6 -4
  163. package/src/modules/customers/backend/customers/deals/map/components/DealsMapView.tsx +6 -4
  164. package/src/modules/customers/backend/customers/deals/page.tsx +6 -4
  165. package/src/modules/customers/backend/customers/deals/pipeline/components/ChangeOwnerDialog.tsx +5 -3
  166. package/src/modules/customers/backend/customers/deals/pipeline/page.tsx +6 -4
  167. package/src/modules/customers/backend/customers/people/page.tsx +6 -4
  168. package/src/modules/customers/components/calendar/editor/PeopleField.tsx +9 -2
  169. package/src/modules/customers/components/calendar/editor/hooks.ts +4 -2
  170. package/src/modules/customers/components/calendar/editor/lookups.ts +12 -4
  171. package/src/modules/customers/components/detail/AssignRoleDialog.tsx +4 -1
  172. package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +4 -2
  173. package/src/modules/customers/components/detail/ScheduleActivityDialog.tsx +15 -7
  174. package/src/modules/customers/components/detail/schedule/DateTimeFields.tsx +16 -7
  175. package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +4 -2
  176. package/src/modules/customers/components/detail/schedule/fieldConfig.ts +15 -0
  177. package/src/modules/customers/components/detail/schedule/index.ts +1 -1
  178. package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +16 -9
  179. package/src/modules/customers/components/formConfig.tsx +35 -12
  180. package/src/modules/customers/data/validators.ts +14 -8
  181. package/src/modules/customers/i18n/de.json +1 -0
  182. package/src/modules/customers/i18n/en.json +1 -0
  183. package/src/modules/customers/i18n/es.json +1 -0
  184. package/src/modules/customers/i18n/ko.json +1 -0
  185. package/src/modules/customers/i18n/pl.json +1 -0
  186. package/src/modules/customers/lib/assignableStaff.ts +172 -34
  187. package/src/modules/customers/setup.ts +8 -0
  188. package/src/modules/data_sync/api/schedules/[id]/route.ts +5 -0
  189. package/src/modules/data_sync/api/schedules/route.ts +5 -0
  190. package/src/modules/data_sync/backend/data-sync/page.tsx +48 -4
  191. package/src/modules/data_sync/components/IntegrationScheduleTab.tsx +61 -2
  192. package/src/modules/data_sync/data/validators.ts +26 -2
  193. package/src/modules/data_sync/i18n/de.json +2 -0
  194. package/src/modules/data_sync/i18n/en.json +2 -0
  195. package/src/modules/data_sync/i18n/es.json +2 -0
  196. package/src/modules/data_sync/i18n/ko.json +2 -0
  197. package/src/modules/data_sync/i18n/pl.json +2 -0
  198. package/src/modules/data_sync/lib/schedule-value.ts +45 -0
  199. package/src/modules/eudr/data/validators.ts +3 -4
  200. package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.tsx +5 -4
  201. package/src/modules/portal/frontend/[orgSlug]/portal/invite/page.tsx +3 -1
  202. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.tsx +2 -1
  203. package/src/modules/portal/frontend/[orgSlug]/portal/page.tsx +5 -5
  204. package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.tsx +5 -4
  205. package/src/modules/portal/i18n/de.json +1 -0
  206. package/src/modules/portal/i18n/en.json +1 -0
  207. package/src/modules/portal/i18n/es.json +1 -0
  208. package/src/modules/portal/i18n/ko.json +1 -0
  209. package/src/modules/portal/i18n/pl.json +1 -0
  210. package/src/modules/portal/lib/navigation.ts +8 -3
  211. package/src/modules/query_index/lib/engine.ts +111 -16
  212. package/src/modules/staff/backend/staff/time-tracking/page.tsx +161 -130
  213. package/src/modules/staff/backend/staff/time-tracking/projects/[id]/page.tsx +55 -43
  214. package/src/modules/staff/backend/staff/time-tracking/reports/[id]/page.tsx +61 -38
  215. package/src/modules/staff/extension-points.ts +16 -0
  216. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/[id]/page.tsx +3 -4
  217. package/src/modules/staff/frontend/[orgSlug]/portal/time-reports/page.tsx +3 -4
  218. package/src/modules/staff/i18n/de.json +3 -0
  219. package/src/modules/staff/i18n/en.json +3 -0
  220. package/src/modules/staff/i18n/es.json +3 -0
  221. package/src/modules/staff/i18n/ko.json +3 -0
  222. package/src/modules/staff/i18n/pl.json +3 -0
  223. package/src/modules/staff/lib/time-tracking-ui/ReportSheet.tsx +32 -29
  224. package/src/modules/staff/lib/time-tracking-ui/TaskListView.tsx +89 -79
  225. package/src/modules/warranty_claims/api/ai/assess/route.ts +2 -2
  226. package/src/modules/warranty_claims/api/ai/draft-reply/route.ts +2 -3
  227. package/src/modules/warranty_claims/api/ai/suggest/route.ts +3 -3
  228. package/src/modules/warranty_claims/api/assign/route.ts +3 -3
  229. package/src/modules/warranty_claims/api/credit-memo/route.ts +2 -2
  230. package/src/modules/warranty_claims/api/entitlement/route.ts +2 -2
  231. package/src/modules/warranty_claims/api/events/route.ts +4 -4
  232. package/src/modules/warranty_claims/api/external/claims/route.ts +3 -3
  233. package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +2 -2
  234. package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +2 -2
  235. package/src/modules/warranty_claims/api/portal/claims/route.ts +2 -2
  236. package/src/modules/warranty_claims/api/portal/events/route.ts +2 -2
  237. package/src/modules/warranty_claims/api/receiving/route.ts +2 -2
  238. package/src/modules/warranty_claims/api/replacement-order/route.ts +2 -2
  239. package/src/modules/warranty_claims/api/return-label/route.ts +2 -2
  240. package/src/modules/warranty_claims/api/risk/route.ts +2 -2
  241. package/src/modules/warranty_claims/api/sales-return/route.ts +2 -2
  242. package/src/modules/warranty_claims/api/settings-general/route.ts +4 -4
  243. package/src/modules/warranty_claims/api/stats/route.ts +2 -2
  244. package/src/modules/warranty_claims/api/submit/route.ts +3 -3
  245. package/src/modules/warranty_claims/api/transition/route.ts +3 -3
  246. package/src/modules/warranty_claims/api/vendor-recovery/route.ts +3 -3
  247. package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +2 -2
  248. package/src/modules/warranty_claims/backend/page.tsx +4 -2
  249. package/src/modules/warranty_claims/backend/warranty_claims/[id]/page.tsx +9 -2
  250. package/src/modules/warranty_claims/backend/warranty_claims/settings/page.tsx +4 -2
  251. package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/[id]/page.tsx +5 -4
  252. package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/new/page.tsx +5 -2
  253. package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +5 -2
  254. package/src/modules/workflows/components/DefinitionTriggersEditor.tsx +1 -1
  255. package/src/modules/workflows/frontend/[orgSlug]/portal/tasks/[id]/page.tsx +2 -2
  256. package/src/modules/workflows/i18n/de.json +1 -1
  257. package/src/modules/workflows/i18n/en.json +1 -1
  258. package/src/modules/workflows/i18n/es.json +1 -1
  259. package/src/modules/workflows/i18n/ko.json +1 -1
  260. package/src/modules/workflows/i18n/pl.json +1 -1
  261. package/src/modules/workflows/lib/event-trigger-service.ts +139 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.8.1-develop.7274.1.21a0083828",
3
+ "version": "0.8.1-develop.7294.1.0ef99fc518",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -253,16 +253,16 @@
253
253
  "zod": "^4.4.3"
254
254
  },
255
255
  "peerDependencies": {
256
- "@open-mercato/ai-assistant": "0.8.1-develop.7274.1.21a0083828",
257
- "@open-mercato/shared": "0.8.1-develop.7274.1.21a0083828",
258
- "@open-mercato/ui": "0.8.1-develop.7274.1.21a0083828",
256
+ "@open-mercato/ai-assistant": "0.8.1-develop.7294.1.0ef99fc518",
257
+ "@open-mercato/shared": "0.8.1-develop.7294.1.0ef99fc518",
258
+ "@open-mercato/ui": "0.8.1-develop.7294.1.0ef99fc518",
259
259
  "react": "^19.0.0",
260
260
  "react-dom": "^19.0.0"
261
261
  },
262
262
  "devDependencies": {
263
- "@open-mercato/ai-assistant": "0.8.1-develop.7274.1.21a0083828",
264
- "@open-mercato/shared": "0.8.1-develop.7274.1.21a0083828",
265
- "@open-mercato/ui": "0.8.1-develop.7274.1.21a0083828",
263
+ "@open-mercato/ai-assistant": "0.8.1-develop.7294.1.0ef99fc518",
264
+ "@open-mercato/shared": "0.8.1-develop.7294.1.0ef99fc518",
265
+ "@open-mercato/ui": "0.8.1-develop.7294.1.0ef99fc518",
266
266
  "@testing-library/dom": "^10.4.1",
267
267
  "@testing-library/jest-dom": "^7.0.1",
268
268
  "@testing-library/react": "^16.3.3",
@@ -422,9 +422,10 @@ function normalizeOrganizations(organizations: unknown): string[] | null {
422
422
 
423
423
  // Whether the caller expressed an intentional narrowing. `null` and `__all__`
424
424
  // are the two documented ways to say "every organization"; an empty list is the
425
- // editor's "no organization picked" state ("Empty = all organizations"), which
426
- // is not a restriction an administrator chose. Only a concrete list narrows, so
427
- // only a concrete list has to justify itself against the feature grant below.
425
+ // editor's "no organization picked" state, which this route treats as
426
+ // unrestricted rather than a deliberate restriction. Only a concrete list
427
+ // narrows, so only a concrete list has to justify itself against the feature
428
+ // grant below.
428
429
  function hasOrganizationRestriction(organizations: string[] | null): boolean {
429
430
  if (!organizations || organizations.length === 0) return false
430
431
  return !organizations.includes('__all__')
@@ -282,12 +282,36 @@ export function AclEditor({
282
282
 
283
283
  const hasGlobalWildcard = granted.includes('*')
284
284
  const hasOrganizationRestriction = Array.isArray(organizations) && organizations.length > 0
285
+ // `null` (unrestricted) and `[]` (deny-all, the #4033 semantic) both leave every
286
+ // checkbox unticked, so the scope has to be reported explicitly instead of being
287
+ // inferred from checkbox emptiness (#5642).
288
+ const isDenyAllOrganizations = Array.isArray(organizations) && organizations.length === 0
285
289
  const showOrganizationWarning =
286
290
  (kind === 'role' || overrideEnabled) &&
287
291
  canEditOrganizations &&
288
292
  !isSuperAdmin &&
289
293
  hasOrganizationRestriction &&
290
294
  granted.length === 0
295
+ // A role ACL is persisted verbatim, so its empty scope really is a deny-all. A
296
+ // per-user override without a single feature is not persisted at all — the API
297
+ // clears the row instead of writing an empty scope — so warning there would
298
+ // claim a permission loss that never happens.
299
+ const denyAllOrganizationsWouldPersist = kind === 'role' || granted.length > 0
300
+ const showDenyAllOrganizationsWarning =
301
+ (kind === 'role' || overrideEnabled) &&
302
+ canEditOrganizations &&
303
+ !isSuperAdmin &&
304
+ isDenyAllOrganizations &&
305
+ denyAllOrganizationsWouldPersist
306
+ const organizationScopeSummary = isDenyAllOrganizations
307
+ ? t('auth.acl.organizationsScopeCurrent.none', 'Current scope: no organizations.')
308
+ : organizations && organizations.length > 0
309
+ ? t(
310
+ 'auth.acl.organizationsScopeCurrent.selected',
311
+ 'Current scope: {count} selected organization(s).',
312
+ { count: organizations.length },
313
+ )
314
+ : t('auth.acl.organizationsScopeCurrent.all', 'Current scope: all organizations.')
291
315
 
292
316
 
293
317
  const toggleWildcard = React.useCallback((wildcard: string, enable: boolean) => {
@@ -399,6 +423,7 @@ export function AclEditor({
399
423
  {t('auth.acl.globalWildcard.description', 'This grants access to all features in the system.')}
400
424
  </div>
401
425
  <Button
426
+ type="button"
402
427
  variant="outline"
403
428
  size="sm"
404
429
  className="mt-2"
@@ -564,7 +589,10 @@ export function AclEditor({
564
589
  {t('auth.acl.organizationsScope', 'Organizations scope')}
565
590
  </div>
566
591
  <div className="text-xs text-muted-foreground mb-2">
567
- {t('auth.acl.organizationsScopeHint', 'Empty means all organizations. Select one or more to restrict access.')}
592
+ {t('auth.acl.organizationsScopeHint', 'Select one or more organizations to restrict access to them. Use "Allow all organizations" to lift the restriction — clearing every selection instead denies access to all organizations.')}
593
+ </div>
594
+ <div className="text-xs font-medium mb-2" data-testid="acl-organization-scope-summary">
595
+ {organizationScopeSummary}
568
596
  </div>
569
597
  <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
570
598
  {orgOptions.map((o) => {
@@ -584,13 +612,21 @@ export function AclEditor({
584
612
  })}
585
613
  </div>
586
614
  <div className="mt-2">
587
- <Button variant="outline" onClick={() => setOrganizations(null)}>{t('auth.acl.allowAllOrganizations', 'Allow all organizations')}</Button>
615
+ <Button type="button" variant="outline" onClick={() => setOrganizations(null)}>{t('auth.acl.allowAllOrganizations', 'Allow all organizations')}</Button>
588
616
  </div>
589
617
  {showOrganizationWarning && (
590
618
  <div className="mt-3 rounded border border-status-warning-border bg-status-warning-bg px-3 py-2 text-sm text-status-warning-text">
591
619
  {t('auth.acl.organizationWarning', 'Organization restrictions are saved only when at least one feature override is selected. Add a feature or enable a module wildcard before saving.')}
592
620
  </div>
593
621
  )}
622
+ {showDenyAllOrganizationsWarning && (
623
+ <div
624
+ className="mt-3 rounded border border-status-warning-border bg-status-warning-bg px-3 py-2 text-sm text-status-warning-text"
625
+ data-testid="acl-organization-deny-all-warning"
626
+ >
627
+ {t('auth.acl.organizationsDenyAllWarning', 'No organization is selected, so this override denies access in every organization — including the features granted above. Choose at least one organization, or use "Allow all organizations" to leave the scope unrestricted.')}
628
+ </div>
629
+ )}
594
630
  </div>
595
631
  )}
596
632
  </>
@@ -436,8 +436,12 @@
436
436
  "auth.acl.modules.wms": "Warehouse Management System",
437
437
  "auth.acl.modules.workflows": "Workflow Engine",
438
438
  "auth.acl.organizationWarning": "Organisationseinschränkungen erfordern mindestens eine Berechtigungsüberschreibung. Füge eine Berechtigung oder einen Modul-Platzhalter hinzu, oder lösche den Organisationsbereich vor dem Speichern.",
439
+ "auth.acl.organizationsDenyAllWarning": "Es ist keine Organisation ausgewählt, daher entzieht diese Überschreibung den Zugriff in allen Organisationen — einschließlich der oben erteilten Berechtigungen. Wähle mindestens eine Organisation aus oder verwende „Alle Organisationen zulassen“, um den Bereich nicht einzuschränken.",
439
440
  "auth.acl.organizationsScope": "Organisationsbereich",
440
- "auth.acl.organizationsScopeHint": "Empty means all organizations. Select one or more to restrict access.",
441
+ "auth.acl.organizationsScopeCurrent.all": "Aktueller Bereich: alle Organisationen.",
442
+ "auth.acl.organizationsScopeCurrent.none": "Aktueller Bereich: keine Organisationen.",
443
+ "auth.acl.organizationsScopeCurrent.selected": "Aktueller Bereich: {count} ausgewählte Organisation(en).",
444
+ "auth.acl.organizationsScopeHint": "Wähle eine oder mehrere Organisationen aus, um den Zugriff darauf zu beschränken. Verwende „Alle Organisationen zulassen“, um die Beschränkung aufzuheben — werden dagegen alle Auswahlen entfernt, wird der Zugriff auf alle Organisationen entzogen.",
441
445
  "auth.acl.restricted": "Eingeschränkt",
442
446
  "auth.acl.superAdmin.hint": "Only super administrators can change this option.",
443
447
  "auth.acl.superAdmin.label": "Super Admin (all features)",
@@ -436,8 +436,12 @@
436
436
  "auth.acl.modules.wms": "Warehouse Management System",
437
437
  "auth.acl.modules.workflows": "Workflow Engine",
438
438
  "auth.acl.organizationWarning": "Organization restrictions require at least one feature override. Add a feature or module wildcard, or clear the organization scope before saving.",
439
+ "auth.acl.organizationsDenyAllWarning": "No organization is selected, so this override denies access in every organization — including the features granted above. Choose at least one organization, or use \"Allow all organizations\" to leave the scope unrestricted.",
439
440
  "auth.acl.organizationsScope": "Organizations scope",
440
- "auth.acl.organizationsScopeHint": "Empty means all organizations. Select one or more to restrict access.",
441
+ "auth.acl.organizationsScopeCurrent.all": "Current scope: all organizations.",
442
+ "auth.acl.organizationsScopeCurrent.none": "Current scope: no organizations.",
443
+ "auth.acl.organizationsScopeCurrent.selected": "Current scope: {count} selected organization(s).",
444
+ "auth.acl.organizationsScopeHint": "Select one or more organizations to restrict access to them. Use \"Allow all organizations\" to lift the restriction — clearing every selection instead denies access to all organizations.",
441
445
  "auth.acl.restricted": "Restricted",
442
446
  "auth.acl.superAdmin.hint": "Only super administrators can change this option.",
443
447
  "auth.acl.superAdmin.label": "Super Admin (all features)",
@@ -436,8 +436,12 @@
436
436
  "auth.acl.modules.wms": "Warehouse Management System",
437
437
  "auth.acl.modules.workflows": "Workflow Engine",
438
438
  "auth.acl.organizationWarning": "Las restricciones de organización requieren al menos una anulación de función. Agrega una función o un comodín de módulo, o borra el ámbito de organización antes de guardar.",
439
+ "auth.acl.organizationsDenyAllWarning": "No hay ninguna organización seleccionada, por lo que esta anulación deniega el acceso en todas las organizaciones, incluidos los permisos concedidos arriba. Selecciona al menos una organización o usa «Permitir todas las organizaciones» para no restringir el alcance.",
439
440
  "auth.acl.organizationsScope": "Alcance de organizaciones",
440
- "auth.acl.organizationsScopeHint": "Empty means all organizations. Select one or more to restrict access.",
441
+ "auth.acl.organizationsScopeCurrent.all": "Alcance actual: todas las organizaciones.",
442
+ "auth.acl.organizationsScopeCurrent.none": "Alcance actual: ninguna organización.",
443
+ "auth.acl.organizationsScopeCurrent.selected": "Alcance actual: {count} organización(es) seleccionada(s).",
444
+ "auth.acl.organizationsScopeHint": "Selecciona una o más organizaciones para restringir el acceso a ellas. Usa «Permitir todas las organizaciones» para levantar la restricción; en cambio, borrar todas las selecciones deniega el acceso a todas las organizaciones.",
441
445
  "auth.acl.restricted": "Restringido",
442
446
  "auth.acl.superAdmin.hint": "Only super administrators can change this option.",
443
447
  "auth.acl.superAdmin.label": "Super Admin (all features)",
@@ -436,8 +436,12 @@
436
436
  "auth.acl.modules.wms": "Warehouse Management System",
437
437
  "auth.acl.modules.workflows": "Workflow Engine",
438
438
  "auth.acl.organizationWarning": "조직 제한에는 하나 이상의 기능 재정의가 필요합니다. 기능이나 모듈 와일드카드를 추가하거나 저장하기 전에 조직 범위를 지우세요.",
439
+ "auth.acl.organizationsDenyAllWarning": "선택된 조직이 없으므로 이 재정의는 위에서 부여한 권한을 포함해 모든 조직에서 접근을 거부합니다. 조직을 하나 이상 선택하거나, 범위를 제한하지 않으려면 ‘모든 조직 허용’을 사용하세요.",
439
440
  "auth.acl.organizationsScope": "조직 범위",
440
- "auth.acl.organizationsScopeHint": "Empty means all organizations. Select one or more to restrict access.",
441
+ "auth.acl.organizationsScopeCurrent.all": "현재 범위: 모든 조직.",
442
+ "auth.acl.organizationsScopeCurrent.none": "현재 범위: 조직 없음.",
443
+ "auth.acl.organizationsScopeCurrent.selected": "현재 범위: 선택된 조직 {count}개.",
444
+ "auth.acl.organizationsScopeHint": "하나 이상의 조직을 선택하면 해당 조직으로 접근이 제한됩니다. 제한을 해제하려면 ‘모든 조직 허용’을 사용하세요. 반대로 모든 선택을 해제하면 모든 조직에서 접근이 거부됩니다.",
441
445
  "auth.acl.restricted": "제한됨",
442
446
  "auth.acl.superAdmin.hint": "Only super administrators can change this option.",
443
447
  "auth.acl.superAdmin.label": "Super Admin (all features)",
@@ -436,8 +436,12 @@
436
436
  "auth.acl.modules.wms": "System zarządzania magazynem",
437
437
  "auth.acl.modules.workflows": "Mechanizm przepływów pracy",
438
438
  "auth.acl.organizationWarning": "Ograniczenia organizacyjne wymagają co najmniej jednego nadpisania uprawnień. Dodaj uprawnienie lub wildcard modułu albo wyczyść zakres organizacji przed zapisaniem.",
439
+ "auth.acl.organizationsDenyAllWarning": "Nie wybrano żadnej organizacji, więc to nadpisanie odbiera dostęp we wszystkich organizacjach — łącznie z uprawnieniami przyznanymi powyżej. Wybierz co najmniej jedną organizację lub użyj przycisku „Zezwól na wszystkie organizacje”, aby nie ograniczać zakresu.",
439
440
  "auth.acl.organizationsScope": "Zakres organizacji",
440
- "auth.acl.organizationsScopeHint": "Brak wyboru oznacza wszystkie organizacje. Wybierz co najmniej jedną, aby ograniczyć dostęp.",
441
+ "auth.acl.organizationsScopeCurrent.all": "Bieżący zakres: wszystkie organizacje.",
442
+ "auth.acl.organizationsScopeCurrent.none": "Bieżący zakres: brak organizacji.",
443
+ "auth.acl.organizationsScopeCurrent.selected": "Bieżący zakres: wybrane organizacje: {count}.",
444
+ "auth.acl.organizationsScopeHint": "Wybierz co najmniej jedną organizację, aby ograniczyć do niej dostęp. Użyj przycisku „Zezwól na wszystkie organizacje”, aby znieść ograniczenie — usunięcie wszystkich zaznaczeń odbiera natomiast dostęp do wszystkich organizacji.",
441
445
  "auth.acl.restricted": "Ograniczone",
442
446
  "auth.acl.superAdmin.hint": "Tylko superadministratorzy mogą zmienić tę opcję.",
443
447
  "auth.acl.superAdmin.label": "Superadministrator (wszystkie funkcje)",
@@ -26,6 +26,7 @@ import type { AdvancedFilterTree } from '@open-mercato/shared/lib/query/advanced
26
26
  import { createEmptyTree, makeRuleTree } from '@open-mercato/shared/lib/query/advanced-filter-tree'
27
27
  import { deserializeAdvancedFilter, deserializeTree, flatToTree, mapDictionaryColorToTone, serializeTree } from '@open-mercato/shared/lib/query/advanced-filter'
28
28
  import { useCurrentUserId } from '@open-mercato/ui/backend/utils/useCurrentUserId'
29
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
29
30
  import {
30
31
  DictionaryValue,
31
32
  createEmptyCustomerDictionaryMaps,
@@ -327,11 +328,12 @@ export default function CustomersCompaniesPage() {
327
328
  { keyExtras: [scopeVersion, reloadToken] },
328
329
  )
329
330
  const currentUserId = useCurrentUserId()
331
+ const activeOrgId = useCurrentOrganization()?.id ?? null
330
332
  const [ownerFilterOptions, setOwnerFilterOptions] = React.useState<AdvancedFilterOption[]>([])
331
333
  React.useEffect(() => {
332
334
  const controller = new AbortController()
333
335
  let cancelled = false
334
- void fetchAssignableStaffMembers('', { pageSize: 100, signal: controller.signal })
336
+ void fetchAssignableStaffMembers('', { pageSize: 100, activeOrgId, signal: controller.signal })
335
337
  .then((items) => {
336
338
  if (!cancelled) setOwnerFilterOptions(mapAssignableStaffToFilterOptions(items))
337
339
  })
@@ -342,7 +344,7 @@ export default function CustomersCompaniesPage() {
342
344
  cancelled = true
343
345
  controller.abort()
344
346
  }
345
- }, [scopeVersion])
347
+ }, [activeOrgId, scopeVersion])
346
348
  const resolvedOwnerFilterOptions = React.useMemo(
347
349
  () => ensureCurrentUserFilterOption(
348
350
  ownerFilterOptions,
@@ -352,9 +354,9 @@ export default function CustomersCompaniesPage() {
352
354
  [currentUserId, ownerFilterOptions, t],
353
355
  )
354
356
  const loadOwnerFilterOptions = React.useCallback(async (query?: string): Promise<AdvancedFilterOption[]> => {
355
- const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100 })
357
+ const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100, activeOrgId })
356
358
  return mapAssignableStaffToFilterOptions(items)
357
- }, [])
359
+ }, [activeOrgId])
358
360
 
359
361
  const queryParams = React.useMemo(() => {
360
362
  const params = new URLSearchParams()
@@ -12,6 +12,7 @@ import {
12
12
  import { useT } from '@open-mercato/shared/lib/i18n/context'
13
13
  import { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'
14
14
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
15
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
15
16
  import type { FilterOptionTone } from '@open-mercato/shared/lib/query/advanced-filter'
16
17
  import { FilterBarRow, type KanbanFilterChip } from '../../pipeline/components/FilterBarRow'
17
18
  import { StatusFilterPopover } from '../../pipeline/components/StatusFilterPopover'
@@ -236,6 +237,7 @@ type DealsMapViewProps = {
236
237
  export function DealsMapView({ search }: DealsMapViewProps): React.ReactElement {
237
238
  const t = useT()
238
239
  const scopeVersion = useOrganizationScopeVersion()
240
+ const activeOrgId = useCurrentOrganization()?.id ?? null
239
241
 
240
242
  const [statusFilters, setStatusFilters] = React.useState<string[]>([])
241
243
  const [selectedPipelineId, setSelectedPipelineId] = React.useState<string | null>(null)
@@ -293,13 +295,13 @@ export function DealsMapView({ search }: DealsMapViewProps): React.ReactElement
293
295
  })
294
296
 
295
297
  const staffQuery = useQuery<Map<string, string>>({
296
- queryKey: ['customers', 'deals', 'map', 'staff', `scope:${scopeVersion}`],
298
+ queryKey: ['customers', 'deals', 'map', 'staff', `scope:${scopeVersion}`, activeOrgId],
297
299
  staleTime: 300_000,
298
300
  queryFn: async () => {
299
301
  try {
300
302
  const members = await withScopedApiRequestHeaders(
301
303
  { ...SUPPRESS_AUTH_REDIRECT_HEADERS },
302
- () => fetchAssignableStaffMembers('', { pageSize: 100 }),
304
+ () => fetchAssignableStaffMembers('', { pageSize: 100, activeOrgId }),
303
305
  )
304
306
  const names = new Map<string, string>()
305
307
  for (const member of members) {
@@ -482,7 +484,7 @@ export function DealsMapView({ search }: DealsMapViewProps): React.ReactElement
482
484
  try {
483
485
  const members = await withScopedApiRequestHeaders(
484
486
  { ...SUPPRESS_AUTH_REDIRECT_HEADERS },
485
- () => fetchAssignableStaffMembers(query ?? '', { pageSize: 100 }),
487
+ () => fetchAssignableStaffMembers(query ?? '', { pageSize: 100, activeOrgId }),
486
488
  )
487
489
  const options: EntityFilterOption[] = members
488
490
  .filter((member) => !!member.userId && !!member.displayName)
@@ -497,7 +499,7 @@ export function DealsMapView({ search }: DealsMapViewProps): React.ReactElement
497
499
  return []
498
500
  }
499
501
  },
500
- [],
502
+ [activeOrgId],
501
503
  )
502
504
 
503
505
  const loadPeopleOptions = React.useCallback(
@@ -12,6 +12,7 @@ import type { AdvancedFilterTree } from '@open-mercato/shared/lib/query/advanced
12
12
  import { createEmptyTree, makeRuleTree, makeMultiRuleTree } from '@open-mercato/shared/lib/query/advanced-filter-tree'
13
13
  import { deserializeTree, deserializeAdvancedFilter, flatToTree, mapDictionaryColorToTone, serializeTree, type FilterFieldDef, type FilterOption as AdvancedFilterOption } from '@open-mercato/shared/lib/query/advanced-filter'
14
14
  import { useCurrentUserId } from '@open-mercato/ui/backend/utils/useCurrentUserId'
15
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
15
16
  import { apiCall, withScopedApiRequestHeaders } from '@open-mercato/ui/backend/utils/apiCall'
16
17
  import { buildOptimisticLockHeader } from '@open-mercato/ui/backend/utils/optimisticLock'
17
18
  import { surfaceRecordConflict } from '@open-mercato/ui/backend/conflicts'
@@ -637,6 +638,7 @@ export default function CustomersDealsPage() {
637
638
  keyExtras: [scopeVersion, reloadToken],
638
639
  })
639
640
  const currentUserId = useCurrentUserId()
641
+ const activeOrgId = useCurrentOrganization()?.id ?? null
640
642
  const [ownerFilterOptions, setOwnerFilterOptions] = React.useState<AdvancedFilterOption[]>([])
641
643
  // Single staff load drives both the owner FILTER options and the owner-name
642
644
  // map shared with the OWNER cell + the KPI strip (userId → display name).
@@ -645,7 +647,7 @@ export default function CustomersDealsPage() {
645
647
  React.useEffect(() => {
646
648
  const controller = new AbortController()
647
649
  let cancelled = false
648
- void fetchAssignableStaffMembers('', { pageSize: 100, signal: controller.signal })
650
+ void fetchAssignableStaffMembers('', { pageSize: 100, activeOrgId, signal: controller.signal })
649
651
  .then((items) => {
650
652
  if (cancelled) return
651
653
  setOwnerFilterOptions(mapAssignableStaffToFilterOptions(items))
@@ -664,7 +666,7 @@ export default function CustomersDealsPage() {
664
666
  cancelled = true
665
667
  controller.abort()
666
668
  }
667
- }, [scopeVersion])
669
+ }, [activeOrgId, scopeVersion])
668
670
  const resolvedOwnerFilterOptions = React.useMemo(
669
671
  () => ensureCurrentUserFilterOption(
670
672
  ownerFilterOptions,
@@ -674,9 +676,9 @@ export default function CustomersDealsPage() {
674
676
  [currentUserId, ownerFilterOptions, t],
675
677
  )
676
678
  const loadOwnerFilterOptions = React.useCallback(async (query?: string): Promise<AdvancedFilterOption[]> => {
677
- const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100 })
679
+ const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100, activeOrgId })
678
680
  return mapAssignableStaffToFilterOptions(items)
679
- }, [])
681
+ }, [activeOrgId])
680
682
 
681
683
  const startOfToday = React.useMemo(() => {
682
684
  const today = new Date()
@@ -15,6 +15,7 @@ import { Input } from '@open-mercato/ui/primitives/input'
15
15
  import { Avatar } from '@open-mercato/ui/primitives/avatar'
16
16
  import { Spinner } from '@open-mercato/ui/primitives/spinner'
17
17
  import { useT } from '@open-mercato/shared/lib/i18n/context'
18
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
18
19
  import { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'
19
20
  import {
20
21
  fetchAssignableStaffMembers,
@@ -37,6 +38,7 @@ export function ChangeOwnerDialog({
37
38
  onConfirm,
38
39
  }: ChangeOwnerDialogProps): React.ReactElement {
39
40
  const t = useT()
41
+ const activeOrgId = useCurrentOrganization()?.id ?? null
40
42
  const [query, setQuery] = React.useState('')
41
43
  const [items, setItems] = React.useState<AssignableStaffMember[]>([])
42
44
  const [isLoading, setIsLoading] = React.useState(false)
@@ -53,7 +55,7 @@ export function ChangeOwnerDialog({
53
55
  let cancelled = false
54
56
  const controller = new AbortController()
55
57
  setIsLoading(true)
56
- fetchAssignableStaffMembers(query, { pageSize: 50, signal: controller.signal })
58
+ fetchAssignableStaffMembers(query, { pageSize: 50, activeOrgId, signal: controller.signal })
57
59
  .then((next) => {
58
60
  if (cancelled) return
59
61
  setItems(next)
@@ -70,7 +72,7 @@ export function ChangeOwnerDialog({
70
72
  controller.abort()
71
73
  }
72
74
  // eslint-disable-next-line react-hooks/exhaustive-deps
73
- }, [open, query])
75
+ }, [activeOrgId, open, query])
74
76
 
75
77
  const handleConfirm = () => {
76
78
  onConfirm(selectedUserId)
@@ -141,7 +143,7 @@ export function ChangeOwnerDialog({
141
143
  <Button
142
144
  variant="ghost"
143
145
  size="default"
144
- key={member.teamMemberId}
146
+ key={member.teamMemberId ?? member.userId}
145
147
  type="button"
146
148
  onClick={() => setSelectedUserId(member.userId)}
147
149
  // Overrides on the Button base class:
@@ -53,6 +53,7 @@ import type { RowActionItem } from '@open-mercato/ui/backend/RowActions'
53
53
  import { useT } from '@open-mercato/shared/lib/i18n/context'
54
54
  import { translateWithFallback } from '@open-mercato/shared/lib/i18n/translate'
55
55
  import { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'
56
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
56
57
  import type { FilterOptionTone } from '@open-mercato/shared/lib/query/advanced-filter'
57
58
  import { ViewTabsRow, VIEW_TABS_ROW_COMPONENT_ID, type ViewTabsRowProps } from './components/ViewTabsRow'
58
59
  import { LANE_WIDTH_CLASS } from './components/constants'
@@ -427,6 +428,7 @@ export default function DealsKanbanPage(): React.ReactElement {
427
428
  )
428
429
  const router = useRouter()
429
430
  const scopeVersion = useOrganizationScopeVersion()
431
+ const activeOrgId = useCurrentOrganization()?.id ?? null
430
432
  const queryClient = useQueryClient()
431
433
 
432
434
  const [selectedPipelineId, setSelectedPipelineId] = React.useState<string | null>(null)
@@ -499,9 +501,9 @@ export default function DealsKanbanPage(): React.ReactElement {
499
501
  }, [pipelinesQuery.data, selectedPipelineId])
500
502
 
501
503
  const staffQuery = useQuery<AssignableStaffMember[]>({
502
- queryKey: ['customers', 'deals', 'kanban', 'staff', `scope:${scopeVersion}`],
504
+ queryKey: ['customers', 'deals', 'kanban', 'staff', `scope:${scopeVersion}`, activeOrgId],
503
505
  staleTime: 300_000,
504
- queryFn: async () => fetchAssignableStaffMembers('', { pageSize: 100 }),
506
+ queryFn: async () => fetchAssignableStaffMembers('', { pageSize: 100, activeOrgId }),
505
507
  })
506
508
 
507
509
  const ownerNamesById = React.useMemo(() => {
@@ -2414,7 +2416,7 @@ export default function DealsKanbanPage(): React.ReactElement {
2414
2416
  // Async loaders for entity-filter popovers (Owner / People / Companies)
2415
2417
  const loadOwnerOptions = React.useCallback(
2416
2418
  async (query: string, _signal: AbortSignal): Promise<EntityFilterOption[]> => {
2417
- const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100 })
2419
+ const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100, activeOrgId })
2418
2420
  const opts: EntityFilterOption[] = items
2419
2421
  .filter((user) => !!user.userId && !!user.displayName)
2420
2422
  .map((user) => ({ value: user.userId!, label: user.displayName! }))
@@ -2426,7 +2428,7 @@ export default function DealsKanbanPage(): React.ReactElement {
2426
2428
  })
2427
2429
  return opts
2428
2430
  },
2429
- [],
2431
+ [activeOrgId],
2430
2432
  )
2431
2433
  const loadPeopleOptions = React.useCallback(
2432
2434
  async (query: string, signal: AbortSignal): Promise<EntityFilterOption[]> => {
@@ -26,6 +26,7 @@ import type { AdvancedFilterTree } from '@open-mercato/shared/lib/query/advanced
26
26
  import { createEmptyTree, makeRuleTree } from '@open-mercato/shared/lib/query/advanced-filter-tree'
27
27
  import { deserializeAdvancedFilter, deserializeTree, flatToTree, mapDictionaryColorToTone, serializeTree } from '@open-mercato/shared/lib/query/advanced-filter'
28
28
  import { useCurrentUserId } from '@open-mercato/ui/backend/utils/useCurrentUserId'
29
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
29
30
  import {
30
31
  DictionaryValue,
31
32
  createEmptyCustomerDictionaryMaps,
@@ -338,11 +339,12 @@ export default function CustomersPeoplePage() {
338
339
  { keyExtras: [scopeVersion, reloadToken] },
339
340
  )
340
341
  const currentUserId = useCurrentUserId()
342
+ const activeOrgId = useCurrentOrganization()?.id ?? null
341
343
  const [ownerFilterOptions, setOwnerFilterOptions] = React.useState<AdvancedFilterOption[]>([])
342
344
  React.useEffect(() => {
343
345
  const controller = new AbortController()
344
346
  let cancelled = false
345
- void fetchAssignableStaffMembers('', { pageSize: 100, signal: controller.signal })
347
+ void fetchAssignableStaffMembers('', { pageSize: 100, activeOrgId, signal: controller.signal })
346
348
  .then((items) => {
347
349
  if (!cancelled) setOwnerFilterOptions(mapAssignableStaffToFilterOptions(items))
348
350
  })
@@ -353,7 +355,7 @@ export default function CustomersPeoplePage() {
353
355
  cancelled = true
354
356
  controller.abort()
355
357
  }
356
- }, [scopeVersion])
358
+ }, [activeOrgId, scopeVersion])
357
359
  const resolvedOwnerFilterOptions = React.useMemo(
358
360
  () => ensureCurrentUserFilterOption(
359
361
  ownerFilterOptions,
@@ -363,9 +365,9 @@ export default function CustomersPeoplePage() {
363
365
  [currentUserId, ownerFilterOptions, t],
364
366
  )
365
367
  const loadOwnerFilterOptions = React.useCallback(async (query?: string): Promise<AdvancedFilterOption[]> => {
366
- const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100 })
368
+ const items = await fetchAssignableStaffMembers(query ?? '', { pageSize: 100, activeOrgId })
367
369
  return mapAssignableStaffToFilterOptions(items)
368
- }, [])
370
+ }, [activeOrgId])
369
371
 
370
372
  const queryParams = React.useMemo(() => {
371
373
  const params = new URLSearchParams()
@@ -9,6 +9,7 @@ import { Button } from '@open-mercato/ui/primitives/button'
9
9
  import { Input } from '@open-mercato/ui/primitives/input'
10
10
  import type { EditorParticipant } from '../../../lib/calendar/editorPayload'
11
11
  import { composeAccessibleName } from '../../../lib/calendar/labels'
12
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
12
13
  import { searchPeopleOptions, type PersonOption } from './lookups'
13
14
  import { CONTROL_BORDER, DROPDOWN_PANEL_CLASS, PersonChip, UppercaseBadge, useDropdownDismiss } from './inputs'
14
15
 
@@ -40,6 +41,7 @@ export function PeopleField({
40
41
  includeStaff?: boolean
41
42
  }) {
42
43
  const t = useT()
44
+ const activeOrgId = useCurrentOrganization()?.id ?? null
43
45
  const [query, setQuery] = React.useState('')
44
46
  const [open, setOpen] = React.useState(false)
45
47
  const [options, setOptions] = React.useState<PersonOption[]>([])
@@ -54,7 +56,12 @@ export function PeopleField({
54
56
  const timer = window.setTimeout(async () => {
55
57
  setLoading(true)
56
58
  try {
57
- const results = await searchPeopleOptions(query.trim(), { includeCustomers, includeStaff, signal: controller.signal })
59
+ const results = await searchPeopleOptions(query.trim(), {
60
+ includeCustomers,
61
+ includeStaff,
62
+ activeOrgId,
63
+ signal: controller.signal,
64
+ })
58
65
  if (cancelled) return
59
66
  setOptions(results)
60
67
  } catch {
@@ -68,7 +75,7 @@ export function PeopleField({
68
75
  controller.abort()
69
76
  window.clearTimeout(timer)
70
77
  }
71
- }, [open, query, includeCustomers, includeStaff])
78
+ }, [activeOrgId, open, query, includeCustomers, includeStaff])
72
79
 
73
80
  const selectedIds = new Set(value.map((participant) => participant.userId))
74
81
  const visibleOptions = options.filter((option) => !selectedIds.has(option.userId))
@@ -13,6 +13,7 @@ import { expandOccurrences } from '../../../lib/calendar/recurrence'
13
13
  import { getFetchWindow } from '../../../lib/calendar/range'
14
14
  import type { CalendarItem } from '../types'
15
15
  import { fetchCalendarCandidates } from '../useCalendarItems'
16
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
16
17
  import { fetchDealById, fetchRelatedEntityById, findStaffMemberName } from './lookups'
17
18
 
18
19
  // Edit-mode prefill stores ids only (parseItemToFormState is pure); resolve the
@@ -22,6 +23,7 @@ export function useEditorLabelResolution(
22
23
  form: EditorFormState,
23
24
  update: (patch: Partial<EditorFormState>) => void,
24
25
  ): void {
26
+ const activeOrgId = useCurrentOrganization()?.id ?? null
25
27
  React.useEffect(() => {
26
28
  if (!open) return
27
29
  const controller = new AbortController()
@@ -36,7 +38,7 @@ export function useEditorLabelResolution(
36
38
  if (!cancelled && deal) update({ dealLabel: deal.label })
37
39
  }
38
40
  if (form.assigneeUserId && !form.assigneeName) {
39
- const name = await findStaffMemberName(form.assigneeUserId, controller.signal)
41
+ const name = await findStaffMemberName(form.assigneeUserId, controller.signal, activeOrgId)
40
42
  if (!cancelled && name) update({ assigneeName: name })
41
43
  }
42
44
  }
@@ -50,7 +52,7 @@ export function useEditorLabelResolution(
50
52
  cancelled = true
51
53
  controller.abort()
52
54
  }
53
- }, [open, form.relatedTo, form.dealId, form.dealLabel, form.assigneeUserId, form.assigneeName, update])
55
+ }, [activeOrgId, open, form.relatedTo, form.dealId, form.dealLabel, form.assigneeUserId, form.assigneeName, update])
54
56
  }
55
57
 
56
58
  function formatClock(date: Date): string {
@@ -126,7 +126,7 @@ export async function fetchDealById(dealId: string, signal: AbortSignal): Promis
126
126
 
127
127
  export async function searchPeopleOptions(
128
128
  query: string,
129
- options: { includeCustomers: boolean; includeStaff?: boolean; signal: AbortSignal },
129
+ options: { includeCustomers: boolean; includeStaff?: boolean; signal: AbortSignal; activeOrgId?: string | null },
130
130
  ): Promise<PersonOption[]> {
131
131
  // Staff lookup is optional twice over: skipped when the staff module is not
132
132
  // loaded (includeStaff === false) and non-fatal when the endpoint errors, so
@@ -134,7 +134,11 @@ export async function searchPeopleOptions(
134
134
  const [staff, customers] = await Promise.all([
135
135
  options.includeStaff === false
136
136
  ? Promise.resolve<Awaited<ReturnType<typeof fetchAssignableStaffMembers>>>([])
137
- : fetchAssignableStaffMembers(query, { pageSize: 10, signal: options.signal }).catch(() => []),
137
+ : fetchAssignableStaffMembers(query, {
138
+ pageSize: 10,
139
+ activeOrgId: options.activeOrgId,
140
+ signal: options.signal,
141
+ }).catch(() => []),
138
142
  options.includeCustomers
139
143
  ? searchRelatedEntities('person', query, options.signal)
140
144
  : Promise.resolve<RelatedEntityOption[]>([]),
@@ -262,9 +266,13 @@ export async function fetchPeoplePhones(ids: string[], signal: AbortSignal): Pro
262
266
  }
263
267
  }
264
268
 
265
- export async function findStaffMemberName(userId: string, signal: AbortSignal): Promise<string | null> {
269
+ export async function findStaffMemberName(
270
+ userId: string,
271
+ signal: AbortSignal,
272
+ activeOrgId?: string | null,
273
+ ): Promise<string | null> {
266
274
  try {
267
- const members = await fetchAssignableStaffMembers('', { pageSize: 100, signal })
275
+ const members = await fetchAssignableStaffMembers('', { pageSize: 100, activeOrgId, signal })
268
276
  const match = members.find((member) => member.userId === userId)
269
277
  return match ? match.displayName : null
270
278
  } catch {
@@ -20,6 +20,7 @@ import {
20
20
  } from '@open-mercato/ui/primitives/dialog'
21
21
  import type { DictionaryEntryOption } from '@open-mercato/core/modules/dictionaries/lib/clientEntries'
22
22
  import type { RoleAssignment } from './RoleAssignmentRow'
23
+ import { useCurrentOrganization } from '@open-mercato/ui/backend/BackendChromeProvider'
23
24
  import { fetchAssignableStaffMembersPage } from './assignableStaff'
24
25
 
25
26
  const MANAGE_ROLE_TYPES_HREF = '/backend/config/customers'
@@ -65,6 +66,7 @@ export function AssignRoleDialog({
65
66
  canManageRoleTypes = false,
66
67
  }: AssignRoleDialogProps) {
67
68
  const t = useT()
69
+ const activeOrgId = useCurrentOrganization()?.id ?? null
68
70
  const [step, setStep] = React.useState<StepId>(1)
69
71
  const [selectedRoleType, setSelectedRoleType] = React.useState('')
70
72
  const [selectedUser, setSelectedUser] = React.useState<StaffMember | null>(null)
@@ -144,6 +146,7 @@ export function AssignRoleDialog({
144
146
  const result = await fetchAssignableStaffMembersPage(query, {
145
147
  page,
146
148
  pageSize: ASSIGNABLE_STAFF_PAGE_SIZE,
149
+ activeOrgId,
147
150
  })
148
151
  if (requestSequenceRef.current !== requestId) return
149
152
 
@@ -187,7 +190,7 @@ export function AssignRoleDialog({
187
190
  }
188
191
  }
189
192
  },
190
- [t],
193
+ [activeOrgId, t],
191
194
  )
192
195
 
193
196
  React.useEffect(() => {