@multiplatform.one/backoffice 7.2.2 → 7.3.0

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 (511) hide show
  1. package/dist/cjs/chrome/BackofficeAwesomebar.native.js +1 -1
  2. package/dist/cjs/chrome/BackofficeAwesomebar.native.js.map +1 -1
  3. package/dist/cjs/chrome/CommandPalette.native.js +1 -1
  4. package/dist/cjs/chrome/CommandPalette.native.js.map +1 -1
  5. package/dist/cjs/chrome/HelpMenu.native.js +1 -1
  6. package/dist/cjs/chrome/HelpMenu.native.js.map +1 -1
  7. package/dist/cjs/chrome/NotificationsButton.native.js +1 -1
  8. package/dist/cjs/chrome/NotificationsButton.native.js.map +1 -1
  9. package/dist/cjs/chrome/UserMenu.native.js +1 -1
  10. package/dist/cjs/chrome/UserMenu.native.js.map +1 -1
  11. package/dist/cjs/chrome/awesomebar-data.native.js.map +1 -1
  12. package/dist/cjs/chrome/command-palette-model.native.js +1 -1
  13. package/dist/cjs/chrome/command-palette-model.native.js.map +1 -1
  14. package/dist/cjs/chrome/help-about-dialog.native.js +1 -1
  15. package/dist/cjs/chrome/help-about-dialog.native.js.map +1 -1
  16. package/dist/cjs/chrome/help-shortcuts-dialog.native.js +1 -1
  17. package/dist/cjs/chrome/help-shortcuts-dialog.native.js.map +1 -1
  18. package/dist/cjs/chrome/notifications-data.native.js.map +1 -1
  19. package/dist/cjs/chrome/notifications-panel.native.js +1 -1
  20. package/dist/cjs/chrome/notifications-panel.native.js.map +1 -1
  21. package/dist/cjs/chrome/notifications-utils.native.js.map +1 -1
  22. package/dist/cjs/chrome/shortcuts.native.js.map +1 -1
  23. package/dist/cjs/chrome/user-session.native.js.map +1 -1
  24. package/dist/cjs/config/BackofficeProvider.cjs +4 -10
  25. package/dist/cjs/config/BackofficeProvider.native.js +10 -30
  26. package/dist/cjs/config/BackofficeProvider.native.js.map +1 -1
  27. package/dist/cjs/config/capabilities.native.js.map +1 -1
  28. package/dist/cjs/config/events.native.js.map +1 -1
  29. package/dist/cjs/config/slots.native.js.map +1 -1
  30. package/dist/cjs/desk/Desk.cjs +187 -0
  31. package/dist/cjs/desk/Desk.native.js +214 -0
  32. package/dist/cjs/desk/Desk.native.js.map +1 -0
  33. package/dist/cjs/desk/DeskSurface.cjs +123 -0
  34. package/dist/cjs/desk/DeskSurface.native.js +130 -0
  35. package/dist/cjs/desk/DeskSurface.native.js.map +1 -0
  36. package/dist/cjs/desk/resolveDeskPath.cjs +118 -0
  37. package/dist/cjs/desk/resolveDeskPath.native.js +123 -0
  38. package/dist/cjs/desk/resolveDeskPath.native.js.map +1 -0
  39. package/dist/cjs/desk/scope.cjs +54 -0
  40. package/dist/cjs/desk/scope.native.js +61 -0
  41. package/dist/cjs/desk/scope.native.js.map +1 -0
  42. package/dist/cjs/desk/target.cjs +174 -0
  43. package/dist/cjs/desk/target.native.js +178 -0
  44. package/dist/cjs/desk/target.native.js.map +1 -0
  45. package/dist/cjs/form/DocFormScreen.native.js +1 -1
  46. package/dist/cjs/form/DocFormScreen.native.js.map +1 -1
  47. package/dist/cjs/form/FormConnections.native.js +1 -1
  48. package/dist/cjs/form/FormConnections.native.js.map +1 -1
  49. package/dist/cjs/form/FormDialogs.native.js +1 -1
  50. package/dist/cjs/form/FormDialogs.native.js.map +1 -1
  51. package/dist/cjs/form/FormErrorBoundary.native.js +1 -1
  52. package/dist/cjs/form/FormErrorBoundary.native.js.map +1 -1
  53. package/dist/cjs/form/FormHeaderCluster.native.js +1 -1
  54. package/dist/cjs/form/FormHeaderCluster.native.js.map +1 -1
  55. package/dist/cjs/form/FormSidebarPanel.native.js +1 -1
  56. package/dist/cjs/form/FormSidebarPanel.native.js.map +1 -1
  57. package/dist/cjs/form/FormTimeline.native.js +1 -1
  58. package/dist/cjs/form/FormTimeline.native.js.map +1 -1
  59. package/dist/cjs/form/FormToolbar.native.js +1 -1
  60. package/dist/cjs/form/FormToolbar.native.js.map +1 -1
  61. package/dist/cjs/form/connections-data.native.js.map +1 -1
  62. package/dist/cjs/form/createPrefill.native.js.map +1 -1
  63. package/dist/cjs/form/dependsOn.native.js.map +1 -1
  64. package/dist/cjs/form/dirtyTracking.native.js.map +1 -1
  65. package/dist/cjs/form/docinfo.native.js.map +1 -1
  66. package/dist/cjs/form/fetchFrom.native.js.map +1 -1
  67. package/dist/cjs/form/fieldPermissions.native.js.map +1 -1
  68. package/dist/cjs/form/formActions.native.js.map +1 -1
  69. package/dist/cjs/form/formLayout.native.js.map +1 -1
  70. package/dist/cjs/form/platformHooks.native.js.map +1 -1
  71. package/dist/cjs/form/sidebar-attachments.native.js +1 -1
  72. package/dist/cjs/form/sidebar-attachments.native.js.map +1 -1
  73. package/dist/cjs/form/sidebar-dialogs.native.js +1 -1
  74. package/dist/cjs/form/sidebar-dialogs.native.js.map +1 -1
  75. package/dist/cjs/form/sidebar-people.native.js +1 -1
  76. package/dist/cjs/form/sidebar-people.native.js.map +1 -1
  77. package/dist/cjs/form/sidebar-rpc.native.js.map +1 -1
  78. package/dist/cjs/form/sidebar-utils.native.js.map +1 -1
  79. package/dist/cjs/form/timeline-entries.native.js.map +1 -1
  80. package/dist/cjs/form/timeline-utils.native.js.map +1 -1
  81. package/dist/cjs/form/useFormEngine.native.js.map +1 -1
  82. package/dist/cjs/i18n/en.native.js.map +1 -1
  83. package/dist/cjs/i18n/index.native.js.map +1 -1
  84. package/dist/cjs/index.cjs +13 -0
  85. package/dist/cjs/index.native.js +14 -1
  86. package/dist/cjs/index.native.js.map +1 -1
  87. package/dist/cjs/list/AddTagsDialog.native.js +1 -1
  88. package/dist/cjs/list/AddTagsDialog.native.js.map +1 -1
  89. package/dist/cjs/list/AssignToDialog.native.js +1 -1
  90. package/dist/cjs/list/AssignToDialog.native.js.map +1 -1
  91. package/dist/cjs/list/DoctypeListScreen.cjs +3 -1
  92. package/dist/cjs/list/DoctypeListScreen.native.js +4 -2
  93. package/dist/cjs/list/DoctypeListScreen.native.js.map +1 -1
  94. package/dist/cjs/list/ListBulkActionsBar.native.js +1 -1
  95. package/dist/cjs/list/ListBulkActionsBar.native.js.map +1 -1
  96. package/dist/cjs/list/ListFilterBar.native.js +1 -1
  97. package/dist/cjs/list/ListFilterBar.native.js.map +1 -1
  98. package/dist/cjs/list/ListSidebar.native.js +1 -1
  99. package/dist/cjs/list/ListSidebar.native.js.map +1 -1
  100. package/dist/cjs/list/bulk-edit-dialog.native.js +1 -1
  101. package/dist/cjs/list/bulk-edit-dialog.native.js.map +1 -1
  102. package/dist/cjs/list/bulk-export.native.js.map +1 -1
  103. package/dist/cjs/list/bulk-submit-cancel.native.js.map +1 -1
  104. package/dist/cjs/list/bulkActions.native.js.map +1 -1
  105. package/dist/cjs/list/filter-chips.native.js +1 -1
  106. package/dist/cjs/list/filter-chips.native.js.map +1 -1
  107. package/dist/cjs/list/filter-editor.native.js +1 -1
  108. package/dist/cjs/list/filter-editor.native.js.map +1 -1
  109. package/dist/cjs/list/filter-fields.native.js.map +1 -1
  110. package/dist/cjs/list/filter-saved.native.js +1 -1
  111. package/dist/cjs/list/filter-saved.native.js.map +1 -1
  112. package/dist/cjs/list/filter-sort.native.js +1 -1
  113. package/dist/cjs/list/filter-sort.native.js.map +1 -1
  114. package/dist/cjs/list/filter-standard.native.js.map +1 -1
  115. package/dist/cjs/list/filterConversion.native.js.map +1 -1
  116. package/dist/cjs/list/list-tools-sheet.native.js +1 -1
  117. package/dist/cjs/list/list-tools-sheet.native.js.map +1 -1
  118. package/dist/cjs/list/listColumns.native.js.map +1 -1
  119. package/dist/cjs/list/listFields.native.js.map +1 -1
  120. package/dist/cjs/list/listFormat.native.js.map +1 -1
  121. package/dist/cjs/list/listUrlState.native.js.map +1 -1
  122. package/dist/cjs/list/sidebar-section.native.js +1 -1
  123. package/dist/cjs/list/sidebar-section.native.js.map +1 -1
  124. package/dist/cjs/list/sidebar-stats.native.js.map +1 -1
  125. package/dist/cjs/list/statusColors.native.js.map +1 -1
  126. package/dist/cjs/navigation/navigator.native.js.map +1 -1
  127. package/dist/cjs/navigation/paths.native.js.map +1 -1
  128. package/dist/cjs/oneAdapter/index.native.js.map +1 -1
  129. package/dist/cjs/quickEntry/QuickEntryDialog.native.js +1 -1
  130. package/dist/cjs/quickEntry/QuickEntryDialog.native.js.map +1 -1
  131. package/dist/cjs/quickEntry/quickEntryPlan.native.js.map +1 -1
  132. package/dist/cjs/report/DoctypeReportView.cjs +3 -1
  133. package/dist/cjs/report/DoctypeReportView.native.js +5 -3
  134. package/dist/cjs/report/DoctypeReportView.native.js.map +1 -1
  135. package/dist/cjs/report/QueryReportScreen.native.js +1 -1
  136. package/dist/cjs/report/QueryReportScreen.native.js.map +1 -1
  137. package/dist/cjs/report/queryReportColumns.native.js.map +1 -1
  138. package/dist/cjs/report/queryReportFilterBar.native.js +1 -1
  139. package/dist/cjs/report/queryReportFilterBar.native.js.map +1 -1
  140. package/dist/cjs/report/queryReportUrlState.native.js.map +1 -1
  141. package/dist/cjs/report/report-grid.native.js +1 -1
  142. package/dist/cjs/report/report-grid.native.js.map +1 -1
  143. package/dist/cjs/report/report-toolbar.native.js +1 -1
  144. package/dist/cjs/report/report-toolbar.native.js.map +1 -1
  145. package/dist/cjs/report/reportShapes.native.js.map +1 -1
  146. package/dist/cjs/report/reportViewModel.native.js.map +1 -1
  147. package/dist/cjs/report/savedReports.native.js.map +1 -1
  148. package/dist/cjs/report/useQueryReport.native.js.map +1 -1
  149. package/dist/cjs/shared/deskLinks.native.js.map +1 -1
  150. package/dist/cjs/shared/listNavContext.native.js.map +1 -1
  151. package/dist/cjs/shared/types.native.js.map +1 -1
  152. package/dist/cjs/shared/useDocinfo.native.js.map +1 -1
  153. package/dist/cjs/shared/useDoctypeMeta.native.js.map +1 -1
  154. package/dist/cjs/shared/useFrappeApp.native.js.map +1 -1
  155. package/dist/cjs/shared/useRecentVisit.native.js.map +1 -1
  156. package/dist/cjs/shared/useSessionUserId.native.js.map +1 -1
  157. package/dist/cjs/shared/useSlugTarget.native.js.map +1 -1
  158. package/dist/cjs/shared/useUserRoles.native.js.map +1 -1
  159. package/dist/cjs/shell/BackofficeShell.native.js +1 -1
  160. package/dist/cjs/shell/BackofficeShell.native.js.map +1 -1
  161. package/dist/cjs/shell/HomeRedirect.native.js +1 -1
  162. package/dist/cjs/shell/HomeRedirect.native.js.map +1 -1
  163. package/dist/cjs/shell/NotFoundScreen.native.js +1 -1
  164. package/dist/cjs/shell/NotFoundScreen.native.js.map +1 -1
  165. package/dist/cjs/shell/SlugScreen.native.js +1 -1
  166. package/dist/cjs/shell/SlugScreen.native.js.map +1 -1
  167. package/dist/cjs/shell/WorkspaceSidebar.native.js +1 -1
  168. package/dist/cjs/shell/WorkspaceSidebar.native.js.map +1 -1
  169. package/dist/cjs/shell/useWorkspaces.native.js.map +1 -1
  170. package/dist/cjs/shell/workspaceIcons.native.js.map +1 -1
  171. package/dist/cjs/views/CalendarView.cjs +5 -0
  172. package/dist/cjs/views/CalendarView.native.js +6 -1
  173. package/dist/cjs/views/CalendarView.native.js.map +1 -1
  174. package/dist/cjs/views/DashboardView.cjs +5 -3
  175. package/dist/cjs/views/DashboardView.native.js +6 -4
  176. package/dist/cjs/views/DashboardView.native.js.map +1 -1
  177. package/dist/cjs/views/DoctypeViewScreen.native.js.map +1 -1
  178. package/dist/cjs/views/ImageView.cjs +5 -0
  179. package/dist/cjs/views/ImageView.native.js +6 -1
  180. package/dist/cjs/views/ImageView.native.js.map +1 -1
  181. package/dist/cjs/views/KanbanView.cjs +5 -2
  182. package/dist/cjs/views/KanbanView.native.js +6 -3
  183. package/dist/cjs/views/KanbanView.native.js.map +1 -1
  184. package/dist/cjs/views/ReportView.native.js +1 -1
  185. package/dist/cjs/views/ReportView.native.js.map +1 -1
  186. package/dist/cjs/views/TreeView.cjs +5 -0
  187. package/dist/cjs/views/TreeView.native.js +6 -1
  188. package/dist/cjs/views/TreeView.native.js.map +1 -1
  189. package/dist/cjs/views/ViewHeader.native.js +1 -1
  190. package/dist/cjs/views/ViewHeader.native.js.map +1 -1
  191. package/dist/cjs/views/ViewStates.native.js +1 -1
  192. package/dist/cjs/views/ViewStates.native.js.map +1 -1
  193. package/dist/cjs/views/ViewSwitcherMenu.native.js +1 -1
  194. package/dist/cjs/views/ViewSwitcherMenu.native.js.map +1 -1
  195. package/dist/cjs/views/calendar-field-selector.native.js +1 -1
  196. package/dist/cjs/views/calendar-field-selector.native.js.map +1 -1
  197. package/dist/cjs/views/connection.native.js.map +1 -1
  198. package/dist/cjs/views/dashboard-data.native.js.map +1 -1
  199. package/dist/cjs/views/deskViewConfigs.native.js.map +1 -1
  200. package/dist/cjs/views/kanban-board-controls.native.js +1 -1
  201. package/dist/cjs/views/kanban-board-controls.native.js.map +1 -1
  202. package/dist/cjs/views/kanban-field-picker.native.js +1 -1
  203. package/dist/cjs/views/kanban-field-picker.native.js.map +1 -1
  204. package/dist/cjs/views/kanbanBoardOps.native.js.map +1 -1
  205. package/dist/cjs/views/report-controls.native.js +1 -1
  206. package/dist/cjs/views/report-controls.native.js.map +1 -1
  207. package/dist/cjs/views/viewAvailability.native.js.map +1 -1
  208. package/dist/cjs/views/viewMeta.native.js.map +1 -1
  209. package/dist/cjs/workspace/WorkspaceChartBlock.native.js +1 -1
  210. package/dist/cjs/workspace/WorkspaceChartBlock.native.js.map +1 -1
  211. package/dist/cjs/workspace/WorkspaceGrid.native.js.map +1 -1
  212. package/dist/cjs/workspace/WorkspaceLinkCard.native.js +1 -1
  213. package/dist/cjs/workspace/WorkspaceLinkCard.native.js.map +1 -1
  214. package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js +1 -1
  215. package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js.map +1 -1
  216. package/dist/cjs/workspace/WorkspaceNumberCard.native.js +1 -1
  217. package/dist/cjs/workspace/WorkspaceNumberCard.native.js.map +1 -1
  218. package/dist/cjs/workspace/WorkspaceQuickList.native.js +1 -1
  219. package/dist/cjs/workspace/WorkspaceQuickList.native.js.map +1 -1
  220. package/dist/cjs/workspace/WorkspaceRecents.native.js +1 -1
  221. package/dist/cjs/workspace/WorkspaceRecents.native.js.map +1 -1
  222. package/dist/cjs/workspace/WorkspaceScreen.native.js +1 -1
  223. package/dist/cjs/workspace/WorkspaceScreen.native.js.map +1 -1
  224. package/dist/cjs/workspace/WorkspaceShortcut.native.js +1 -1
  225. package/dist/cjs/workspace/WorkspaceShortcut.native.js.map +1 -1
  226. package/dist/cjs/workspace/parseContent.native.js.map +1 -1
  227. package/dist/cjs/workspace/types.native.js.map +1 -1
  228. package/dist/cjs/workspace/useDashboardChart.native.js.map +1 -1
  229. package/dist/cjs/workspace/useWorkspaceDoc.native.js.map +1 -1
  230. package/dist/esm/chrome/BackofficeAwesomebar.native.js.map +1 -1
  231. package/dist/esm/chrome/CommandPalette.native.js.map +1 -1
  232. package/dist/esm/chrome/HelpMenu.native.js.map +1 -1
  233. package/dist/esm/chrome/NotificationsButton.native.js.map +1 -1
  234. package/dist/esm/chrome/UserMenu.native.js.map +1 -1
  235. package/dist/esm/chrome/awesomebar-data.native.js.map +1 -1
  236. package/dist/esm/chrome/command-palette-model.native.js.map +1 -1
  237. package/dist/esm/chrome/help-about-dialog.native.js.map +1 -1
  238. package/dist/esm/chrome/help-shortcuts-dialog.native.js.map +1 -1
  239. package/dist/esm/chrome/notifications-data.native.js.map +1 -1
  240. package/dist/esm/chrome/notifications-panel.native.js.map +1 -1
  241. package/dist/esm/chrome/notifications-utils.native.js.map +1 -1
  242. package/dist/esm/chrome/shortcuts.native.js.map +1 -1
  243. package/dist/esm/chrome/user-session.native.js.map +1 -1
  244. package/dist/esm/config/BackofficeProvider.mjs +5 -11
  245. package/dist/esm/config/BackofficeProvider.mjs.map +1 -1
  246. package/dist/esm/config/BackofficeProvider.native.js +10 -30
  247. package/dist/esm/config/BackofficeProvider.native.js.map +1 -1
  248. package/dist/esm/config/capabilities.native.js.map +1 -1
  249. package/dist/esm/config/events.native.js.map +1 -1
  250. package/dist/esm/config/slots.native.js.map +1 -1
  251. package/dist/esm/desk/Desk.mjs +162 -0
  252. package/dist/esm/desk/Desk.mjs.map +1 -0
  253. package/dist/esm/desk/Desk.native.js +186 -0
  254. package/dist/esm/desk/Desk.native.js.map +1 -0
  255. package/dist/esm/desk/DeskSurface.mjs +97 -0
  256. package/dist/esm/desk/DeskSurface.mjs.map +1 -0
  257. package/dist/esm/desk/DeskSurface.native.js +101 -0
  258. package/dist/esm/desk/DeskSurface.native.js.map +1 -0
  259. package/dist/esm/desk/resolveDeskPath.mjs +93 -0
  260. package/dist/esm/desk/resolveDeskPath.mjs.map +1 -0
  261. package/dist/esm/desk/resolveDeskPath.native.js +95 -0
  262. package/dist/esm/desk/resolveDeskPath.native.js.map +1 -0
  263. package/dist/esm/desk/scope.mjs +27 -0
  264. package/dist/esm/desk/scope.mjs.map +1 -0
  265. package/dist/esm/desk/scope.native.js +31 -0
  266. package/dist/esm/desk/scope.native.js.map +1 -0
  267. package/dist/esm/desk/target.mjs +147 -0
  268. package/dist/esm/desk/target.mjs.map +1 -0
  269. package/dist/esm/desk/target.native.js +148 -0
  270. package/dist/esm/desk/target.native.js.map +1 -0
  271. package/dist/esm/form/DocFormScreen.native.js.map +1 -1
  272. package/dist/esm/form/FormConnections.native.js.map +1 -1
  273. package/dist/esm/form/FormDialogs.native.js.map +1 -1
  274. package/dist/esm/form/FormErrorBoundary.native.js.map +1 -1
  275. package/dist/esm/form/FormHeaderCluster.native.js.map +1 -1
  276. package/dist/esm/form/FormSidebarPanel.native.js.map +1 -1
  277. package/dist/esm/form/FormTimeline.native.js.map +1 -1
  278. package/dist/esm/form/FormToolbar.native.js.map +1 -1
  279. package/dist/esm/form/connections-data.native.js.map +1 -1
  280. package/dist/esm/form/createPrefill.native.js.map +1 -1
  281. package/dist/esm/form/dependsOn.native.js.map +1 -1
  282. package/dist/esm/form/dirtyTracking.native.js.map +1 -1
  283. package/dist/esm/form/docinfo.native.js.map +1 -1
  284. package/dist/esm/form/fetchFrom.native.js.map +1 -1
  285. package/dist/esm/form/fieldPermissions.native.js.map +1 -1
  286. package/dist/esm/form/formActions.native.js.map +1 -1
  287. package/dist/esm/form/formLayout.native.js.map +1 -1
  288. package/dist/esm/form/platformHooks.native.js.map +1 -1
  289. package/dist/esm/form/sidebar-attachments.native.js.map +1 -1
  290. package/dist/esm/form/sidebar-dialogs.native.js.map +1 -1
  291. package/dist/esm/form/sidebar-people.native.js.map +1 -1
  292. package/dist/esm/form/sidebar-rpc.native.js.map +1 -1
  293. package/dist/esm/form/sidebar-utils.native.js.map +1 -1
  294. package/dist/esm/form/timeline-entries.native.js.map +1 -1
  295. package/dist/esm/form/timeline-utils.native.js.map +1 -1
  296. package/dist/esm/form/useFormEngine.native.js.map +1 -1
  297. package/dist/esm/i18n/en.native.js.map +1 -1
  298. package/dist/esm/i18n/index.native.js.map +1 -1
  299. package/dist/esm/index.mjs +6 -1
  300. package/dist/esm/index.mjs.map +1 -1
  301. package/dist/esm/index.native.js +6 -1
  302. package/dist/esm/index.native.js.map +1 -1
  303. package/dist/esm/list/AddTagsDialog.native.js.map +1 -1
  304. package/dist/esm/list/AssignToDialog.native.js.map +1 -1
  305. package/dist/esm/list/DoctypeListScreen.mjs +3 -1
  306. package/dist/esm/list/DoctypeListScreen.mjs.map +1 -1
  307. package/dist/esm/list/DoctypeListScreen.native.js +3 -1
  308. package/dist/esm/list/DoctypeListScreen.native.js.map +1 -1
  309. package/dist/esm/list/ListBulkActionsBar.native.js.map +1 -1
  310. package/dist/esm/list/ListFilterBar.native.js.map +1 -1
  311. package/dist/esm/list/ListSidebar.native.js.map +1 -1
  312. package/dist/esm/list/bulk-edit-dialog.native.js.map +1 -1
  313. package/dist/esm/list/bulk-export.native.js.map +1 -1
  314. package/dist/esm/list/bulk-submit-cancel.native.js.map +1 -1
  315. package/dist/esm/list/bulkActions.native.js.map +1 -1
  316. package/dist/esm/list/filter-chips.native.js.map +1 -1
  317. package/dist/esm/list/filter-editor.native.js.map +1 -1
  318. package/dist/esm/list/filter-fields.native.js.map +1 -1
  319. package/dist/esm/list/filter-saved.native.js.map +1 -1
  320. package/dist/esm/list/filter-sort.native.js.map +1 -1
  321. package/dist/esm/list/filter-standard.native.js.map +1 -1
  322. package/dist/esm/list/filterConversion.native.js.map +1 -1
  323. package/dist/esm/list/list-tools-sheet.native.js.map +1 -1
  324. package/dist/esm/list/listColumns.native.js.map +1 -1
  325. package/dist/esm/list/listFields.native.js.map +1 -1
  326. package/dist/esm/list/listFormat.native.js.map +1 -1
  327. package/dist/esm/list/listUrlState.native.js.map +1 -1
  328. package/dist/esm/list/sidebar-section.native.js.map +1 -1
  329. package/dist/esm/list/sidebar-stats.native.js.map +1 -1
  330. package/dist/esm/list/statusColors.native.js.map +1 -1
  331. package/dist/esm/navigation/navigator.native.js.map +1 -1
  332. package/dist/esm/navigation/paths.native.js.map +1 -1
  333. package/dist/esm/oneAdapter/index.native.js.map +1 -1
  334. package/dist/esm/quickEntry/QuickEntryDialog.native.js.map +1 -1
  335. package/dist/esm/quickEntry/quickEntryPlan.native.js.map +1 -1
  336. package/dist/esm/report/DoctypeReportView.mjs +3 -1
  337. package/dist/esm/report/DoctypeReportView.mjs.map +1 -1
  338. package/dist/esm/report/DoctypeReportView.native.js +4 -2
  339. package/dist/esm/report/DoctypeReportView.native.js.map +1 -1
  340. package/dist/esm/report/QueryReportScreen.native.js.map +1 -1
  341. package/dist/esm/report/queryReportColumns.native.js.map +1 -1
  342. package/dist/esm/report/queryReportFilterBar.native.js.map +1 -1
  343. package/dist/esm/report/queryReportUrlState.native.js.map +1 -1
  344. package/dist/esm/report/report-grid.native.js.map +1 -1
  345. package/dist/esm/report/report-toolbar.native.js.map +1 -1
  346. package/dist/esm/report/reportShapes.native.js.map +1 -1
  347. package/dist/esm/report/reportViewModel.native.js.map +1 -1
  348. package/dist/esm/report/savedReports.native.js.map +1 -1
  349. package/dist/esm/report/useQueryReport.native.js.map +1 -1
  350. package/dist/esm/shared/deskLinks.native.js.map +1 -1
  351. package/dist/esm/shared/listNavContext.native.js.map +1 -1
  352. package/dist/esm/shared/types.native.js.map +1 -1
  353. package/dist/esm/shared/useDocinfo.native.js.map +1 -1
  354. package/dist/esm/shared/useDoctypeMeta.native.js.map +1 -1
  355. package/dist/esm/shared/useFrappeApp.native.js.map +1 -1
  356. package/dist/esm/shared/useRecentVisit.native.js.map +1 -1
  357. package/dist/esm/shared/useSessionUserId.native.js.map +1 -1
  358. package/dist/esm/shared/useSlugTarget.native.js.map +1 -1
  359. package/dist/esm/shared/useUserRoles.native.js.map +1 -1
  360. package/dist/esm/shell/BackofficeShell.native.js.map +1 -1
  361. package/dist/esm/shell/HomeRedirect.native.js.map +1 -1
  362. package/dist/esm/shell/NotFoundScreen.native.js.map +1 -1
  363. package/dist/esm/shell/SlugScreen.native.js.map +1 -1
  364. package/dist/esm/shell/WorkspaceSidebar.native.js.map +1 -1
  365. package/dist/esm/shell/useWorkspaces.native.js.map +1 -1
  366. package/dist/esm/shell/workspaceIcons.native.js.map +1 -1
  367. package/dist/esm/views/CalendarView.mjs +5 -0
  368. package/dist/esm/views/CalendarView.mjs.map +1 -1
  369. package/dist/esm/views/CalendarView.native.js +5 -0
  370. package/dist/esm/views/CalendarView.native.js.map +1 -1
  371. package/dist/esm/views/DashboardView.mjs +5 -3
  372. package/dist/esm/views/DashboardView.mjs.map +1 -1
  373. package/dist/esm/views/DashboardView.native.js +5 -3
  374. package/dist/esm/views/DashboardView.native.js.map +1 -1
  375. package/dist/esm/views/DoctypeViewScreen.native.js.map +1 -1
  376. package/dist/esm/views/ImageView.mjs +5 -0
  377. package/dist/esm/views/ImageView.mjs.map +1 -1
  378. package/dist/esm/views/ImageView.native.js +5 -0
  379. package/dist/esm/views/ImageView.native.js.map +1 -1
  380. package/dist/esm/views/KanbanView.mjs +5 -2
  381. package/dist/esm/views/KanbanView.mjs.map +1 -1
  382. package/dist/esm/views/KanbanView.native.js +5 -2
  383. package/dist/esm/views/KanbanView.native.js.map +1 -1
  384. package/dist/esm/views/ReportView.native.js.map +1 -1
  385. package/dist/esm/views/TreeView.mjs +5 -0
  386. package/dist/esm/views/TreeView.mjs.map +1 -1
  387. package/dist/esm/views/TreeView.native.js +5 -0
  388. package/dist/esm/views/TreeView.native.js.map +1 -1
  389. package/dist/esm/views/ViewHeader.native.js.map +1 -1
  390. package/dist/esm/views/ViewStates.native.js.map +1 -1
  391. package/dist/esm/views/ViewSwitcherMenu.native.js.map +1 -1
  392. package/dist/esm/views/calendar-field-selector.native.js.map +1 -1
  393. package/dist/esm/views/connection.native.js.map +1 -1
  394. package/dist/esm/views/dashboard-data.native.js.map +1 -1
  395. package/dist/esm/views/deskViewConfigs.native.js.map +1 -1
  396. package/dist/esm/views/kanban-board-controls.native.js.map +1 -1
  397. package/dist/esm/views/kanban-field-picker.native.js.map +1 -1
  398. package/dist/esm/views/kanbanBoardOps.native.js.map +1 -1
  399. package/dist/esm/views/report-controls.native.js.map +1 -1
  400. package/dist/esm/views/viewAvailability.native.js.map +1 -1
  401. package/dist/esm/views/viewMeta.native.js.map +1 -1
  402. package/dist/esm/workspace/WorkspaceChartBlock.native.js.map +1 -1
  403. package/dist/esm/workspace/WorkspaceGrid.native.js.map +1 -1
  404. package/dist/esm/workspace/WorkspaceLinkCard.native.js.map +1 -1
  405. package/dist/esm/workspace/WorkspaceLinkDirectory.native.js.map +1 -1
  406. package/dist/esm/workspace/WorkspaceNumberCard.native.js.map +1 -1
  407. package/dist/esm/workspace/WorkspaceQuickList.native.js.map +1 -1
  408. package/dist/esm/workspace/WorkspaceRecents.native.js.map +1 -1
  409. package/dist/esm/workspace/WorkspaceScreen.native.js.map +1 -1
  410. package/dist/esm/workspace/WorkspaceShortcut.native.js.map +1 -1
  411. package/dist/esm/workspace/parseContent.native.js.map +1 -1
  412. package/dist/esm/workspace/types.native.js.map +1 -1
  413. package/dist/esm/workspace/useDashboardChart.native.js.map +1 -1
  414. package/dist/esm/workspace/useWorkspaceDoc.native.js.map +1 -1
  415. package/dist/jsx/config/BackofficeProvider.mjs +5 -11
  416. package/dist/jsx/config/BackofficeProvider.mjs.map +1 -1
  417. package/dist/jsx/config/BackofficeProvider.native.js +9 -29
  418. package/dist/jsx/config/BackofficeProvider.native.js.map +1 -1
  419. package/dist/jsx/desk/Desk.mjs +162 -0
  420. package/dist/jsx/desk/Desk.mjs.map +1 -0
  421. package/dist/jsx/desk/Desk.native.js +214 -0
  422. package/dist/jsx/desk/Desk.native.js.map +1 -0
  423. package/dist/jsx/desk/DeskSurface.mjs +97 -0
  424. package/dist/jsx/desk/DeskSurface.mjs.map +1 -0
  425. package/dist/jsx/desk/DeskSurface.native.js +130 -0
  426. package/dist/jsx/desk/DeskSurface.native.js.map +1 -0
  427. package/dist/jsx/desk/resolveDeskPath.mjs +93 -0
  428. package/dist/jsx/desk/resolveDeskPath.mjs.map +1 -0
  429. package/dist/jsx/desk/resolveDeskPath.native.js +123 -0
  430. package/dist/jsx/desk/resolveDeskPath.native.js.map +1 -0
  431. package/dist/jsx/desk/scope.mjs +27 -0
  432. package/dist/jsx/desk/scope.mjs.map +1 -0
  433. package/dist/jsx/desk/scope.native.js +61 -0
  434. package/dist/jsx/desk/scope.native.js.map +1 -0
  435. package/dist/jsx/desk/target.mjs +147 -0
  436. package/dist/jsx/desk/target.mjs.map +1 -0
  437. package/dist/jsx/desk/target.native.js +178 -0
  438. package/dist/jsx/desk/target.native.js.map +1 -0
  439. package/dist/jsx/index.js +6 -1
  440. package/dist/jsx/index.js.map +1 -1
  441. package/dist/jsx/index.mjs +6 -1
  442. package/dist/jsx/index.mjs.map +1 -1
  443. package/dist/jsx/index.native.js +13 -0
  444. package/dist/jsx/index.native.js.map +1 -1
  445. package/dist/jsx/list/DoctypeListScreen.mjs +3 -1
  446. package/dist/jsx/list/DoctypeListScreen.mjs.map +1 -1
  447. package/dist/jsx/list/DoctypeListScreen.native.js +3 -1
  448. package/dist/jsx/list/DoctypeListScreen.native.js.map +1 -1
  449. package/dist/jsx/report/DoctypeReportView.mjs +3 -1
  450. package/dist/jsx/report/DoctypeReportView.mjs.map +1 -1
  451. package/dist/jsx/report/DoctypeReportView.native.js +4 -2
  452. package/dist/jsx/report/DoctypeReportView.native.js.map +1 -1
  453. package/dist/jsx/views/CalendarView.mjs +5 -0
  454. package/dist/jsx/views/CalendarView.mjs.map +1 -1
  455. package/dist/jsx/views/CalendarView.native.js +5 -0
  456. package/dist/jsx/views/CalendarView.native.js.map +1 -1
  457. package/dist/jsx/views/DashboardView.mjs +5 -3
  458. package/dist/jsx/views/DashboardView.mjs.map +1 -1
  459. package/dist/jsx/views/DashboardView.native.js +5 -3
  460. package/dist/jsx/views/DashboardView.native.js.map +1 -1
  461. package/dist/jsx/views/ImageView.mjs +5 -0
  462. package/dist/jsx/views/ImageView.mjs.map +1 -1
  463. package/dist/jsx/views/ImageView.native.js +5 -0
  464. package/dist/jsx/views/ImageView.native.js.map +1 -1
  465. package/dist/jsx/views/KanbanView.mjs +5 -2
  466. package/dist/jsx/views/KanbanView.mjs.map +1 -1
  467. package/dist/jsx/views/KanbanView.native.js +5 -2
  468. package/dist/jsx/views/KanbanView.native.js.map +1 -1
  469. package/dist/jsx/views/TreeView.mjs +5 -0
  470. package/dist/jsx/views/TreeView.mjs.map +1 -1
  471. package/dist/jsx/views/TreeView.native.js +5 -0
  472. package/dist/jsx/views/TreeView.native.js.map +1 -1
  473. package/package.json +12 -12
  474. package/src/config/BackofficeProvider.tsx +11 -18
  475. package/src/desk/Desk.spec.tsx +287 -0
  476. package/src/desk/Desk.tsx +308 -0
  477. package/src/desk/DeskSurface.tsx +132 -0
  478. package/src/desk/doctrine.spec.ts +48 -0
  479. package/src/desk/resolveDeskPath.spec.ts +174 -0
  480. package/src/desk/resolveDeskPath.ts +111 -0
  481. package/src/desk/scope.tsx +52 -0
  482. package/src/desk/target.ts +169 -0
  483. package/src/index.ts +17 -0
  484. package/src/list/DoctypeListScreen.tsx +4 -1
  485. package/src/report/DoctypeReportView.tsx +6 -2
  486. package/src/views/CalendarView.tsx +3 -0
  487. package/src/views/DashboardView.tsx +5 -3
  488. package/src/views/ImageView.tsx +3 -0
  489. package/src/views/KanbanView.tsx +5 -1
  490. package/src/views/TreeView.tsx +3 -0
  491. package/types/config/BackofficeProvider.d.ts +6 -4
  492. package/types/config/BackofficeProvider.d.ts.map +1 -1
  493. package/types/desk/Desk.d.ts +91 -0
  494. package/types/desk/Desk.d.ts.map +1 -0
  495. package/types/desk/DeskSurface.d.ts +24 -0
  496. package/types/desk/DeskSurface.d.ts.map +1 -0
  497. package/types/desk/resolveDeskPath.d.ts +60 -0
  498. package/types/desk/resolveDeskPath.d.ts.map +1 -0
  499. package/types/desk/scope.d.ts +31 -0
  500. package/types/desk/scope.d.ts.map +1 -0
  501. package/types/desk/target.d.ts +57 -0
  502. package/types/desk/target.d.ts.map +1 -0
  503. package/types/index.d.ts +10 -0
  504. package/types/index.d.ts.map +1 -1
  505. package/types/list/DoctypeListScreen.d.ts.map +1 -1
  506. package/types/report/DoctypeReportView.d.ts.map +1 -1
  507. package/types/views/CalendarView.d.ts.map +1 -1
  508. package/types/views/DashboardView.d.ts.map +1 -1
  509. package/types/views/ImageView.d.ts.map +1 -1
  510. package/types/views/KanbanView.d.ts.map +1 -1
  511. package/types/views/TreeView.d.ts.map +1 -1
@@ -5,15 +5,16 @@
5
5
  * It composes @multiplatform.one/frappe-ui's FrappeProvider internally (the
6
6
  * `frappe` prop), publishes the navigation seam + capability flags + slots +
7
7
  * event bus through BackofficeConfigContext (consumed by every backoffice
8
- * surface), registers the package's "backoffice" i18n namespace, and applies
9
- * host field-registry overrides.
8
+ * surface), registers the package's "backoffice" i18n namespace, and scopes
9
+ * host field overrides to this subtree via FieldOverridesProvider (never the
10
+ * module-global registry — GL-4).
10
11
  *
11
12
  * Surfaces render without a provider too (stories/tests): the context
12
13
  * default is a fully-enabled config with an inert navigator.
13
14
  */
14
15
  import {
16
+ FieldOverridesProvider,
15
17
  FrappeProvider,
16
- registerField,
17
18
  type FieldRenderProps,
18
19
  type FrappeProviderProps,
19
20
  } from "@multiplatform.one/frappe-ui";
@@ -155,8 +156,9 @@ export interface BackofficeProviderProps {
155
156
  /** Restrict which doctypes are reachable (awesomebar, lists, links). */
156
157
  doctypes?: BackofficeDoctypeScope;
157
158
  /**
158
- * Field-renderer overrides merged into frappe-ui's field registry
159
- * (fieldtype → component), e.g. `{ Rating: MyRating }`.
159
+ * Field-renderer overrides (fieldtype component), e.g.
160
+ * `{ Rating: MyRating }` — context-scoped to this provider, so two
161
+ * providers with different overrides coexist on one page.
160
162
  */
161
163
  fields?: Record<string, ComponentType<FieldRenderProps>>;
162
164
  /** Host-injected custom actions (form/list toolbars). */
@@ -271,18 +273,6 @@ export function BackofficeProvider({
271
273
  return true;
272
274
  });
273
275
 
274
- // Host field-registry overrides — applied during render (frappe-ui's
275
- // registry is module-global) so first-pass field rendering sees them.
276
- const appliedFields = useRef<Record<string, ComponentType<FieldRenderProps>>>({});
277
- if (fields) {
278
- for (const [fieldtype, component] of Object.entries(fields)) {
279
- if (appliedFields.current[fieldtype] !== component) {
280
- registerField(fieldtype, component);
281
- appliedFields.current[fieldtype] = component;
282
- }
283
- }
284
- }
285
-
286
276
  // Keep the handler on a ref so the wrapped navigator's identity survives
287
277
  // inline `onEvent` props.
288
278
  const onEventRef = useRef<BackofficeEventHandler | undefined>(onEvent);
@@ -373,7 +363,10 @@ export function BackofficeProvider({
373
363
  socketOptions={frappe.socketOptions}
374
364
  fixtures={frappe.fixtures}
375
365
  >
376
- {children}
366
+ {/* Context-scoped field overrides (GL-4): per-provider, never the
367
+ module-global registry — two providers with different `fields` on
368
+ one page must not clobber each other. */}
369
+ <FieldOverridesProvider fields={fields}>{children}</FieldOverridesProvider>
377
370
  </FrappeProvider>
378
371
  </BackofficeConfigContext.Provider>
379
372
  );
@@ -0,0 +1,287 @@
1
+ /**
2
+ * <Desk /> assembly specs (MPO-14):
3
+ * - end-to-end render over a mocked (fixture) doctype, both chrome modes
4
+ * - navigator-driven dispatch through the resolver + slug index
5
+ * - surface override registry (GL-6): per-kind and per-doctype
6
+ * - two Desks on one page: field-registry (GL-4) and scope (GL-5) isolation
7
+ */
8
+ import React from "react";
9
+ import { InMemoryFixtureProvider } from "@multiplatform.one/frappe";
10
+ import { getFieldComponent, useFieldResolver } from "@multiplatform.one/frappe-ui";
11
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
12
+ import { fireEvent, screen, waitFor } from "@testing-library/react";
13
+ import { describe, expect, it, vi } from "vitest";
14
+ import { useBackofficeNavigator, useBackofficePaths } from "../config/BackofficeProvider";
15
+ import { createMemoryNavigator } from "../navigation/navigator";
16
+ import { Desk } from "./Desk";
17
+ import type { DeskSurfaceProps } from "./DeskSurface";
18
+ import { useDeskScope } from "./scope";
19
+
20
+ const MOCK_HOST = "https://desk-assembly.mock.test";
21
+
22
+ const makeFixtures = () =>
23
+ new InMemoryFixtureProvider({
24
+ Workspace: [
25
+ {
26
+ name: "Tools",
27
+ doctype: "Workspace",
28
+ title: "Tools",
29
+ icon: "tool",
30
+ parent_page: "",
31
+ public: 1,
32
+ for_user: "",
33
+ sequence_id: 1,
34
+ module: "Automation",
35
+ is_hidden: 0,
36
+ },
37
+ ],
38
+ DocType: [{ name: "ToDo", doctype: "DocType" }],
39
+ "Notification Log": [],
40
+ });
41
+
42
+ const frappe = () => ({ baseURL: MOCK_HOST, fixtures: makeFixtures() });
43
+
44
+ function SurfaceProbe({ target }: DeskSurfaceProps) {
45
+ return <div data-testid={`probe-${target.kind}`}>{JSON.stringify(target)}</div>;
46
+ }
47
+
48
+ describe("<Desk /> chrome and dispatch", () => {
49
+ it("renders the full desk with no required props (DK-1)", async () => {
50
+ renderWithProviders(<Desk frappe={frappe()} />);
51
+ // Full chrome mounts...
52
+ expect(screen.getByTestId("backoffice-shell")).toBeTruthy();
53
+ expect(screen.getByTestId("backoffice-awesomebar")).toBeTruthy();
54
+ // ...and home lands on the default workspace (fixture-backed redirect).
55
+ await waitFor(() => {
56
+ expect(screen.getAllByText("Tools").length).toBeGreaterThan(0);
57
+ });
58
+ });
59
+
60
+ it("a doctype pin renders that doctype's surface in content chrome, host URL untouched", () => {
61
+ const onTargetChange = vi.fn();
62
+ renderWithProviders(
63
+ <Desk
64
+ frappe={frappe()}
65
+ doctype="Sales Order"
66
+ surfaces={{ list: SurfaceProbe }}
67
+ onTargetChange={onTargetChange}
68
+ />,
69
+ );
70
+ // content chrome: no banner, no rail
71
+ expect(screen.queryByTestId("backoffice-shell")).toBeNull();
72
+ // the pin dispatched the list surface for the proper-cased doctype
73
+ expect(screen.getByTestId("probe-list").textContent).toContain('"Sales Order"');
74
+ expect(onTargetChange).toHaveBeenCalledWith({ kind: "list", doctype: "Sales Order" });
75
+ });
76
+
77
+ it("doctype + name renders the form surface; chrome='full' overrides the pin default", () => {
78
+ renderWithProviders(
79
+ <Desk
80
+ frappe={frappe()}
81
+ doctype="Sales Order"
82
+ name="SO-0001"
83
+ chrome="full"
84
+ surfaces={{ form: SurfaceProbe }}
85
+ />,
86
+ );
87
+ expect(screen.getByTestId("backoffice-shell")).toBeTruthy();
88
+ const probe = screen.getByTestId("probe-form");
89
+ expect(probe.textContent).toContain('"Sales Order"');
90
+ expect(probe.textContent).toContain('"SO-0001"');
91
+ });
92
+
93
+ it("workspace and report pins dispatch their surfaces", () => {
94
+ renderWithProviders(
95
+ <Desk frappe={frappe()} workspace="Accounting" surfaces={{ workspace: SurfaceProbe }} />,
96
+ );
97
+ expect(screen.getByTestId("probe-workspace").textContent).toContain('"Accounting"');
98
+
99
+ renderWithProviders(
100
+ <Desk frappe={frappe()} report="Accounts Receivable" surfaces={{ report: SurfaceProbe }} />,
101
+ );
102
+ expect(screen.getByTestId("probe-report").textContent).toContain('"Accounts Receivable"');
103
+ });
104
+
105
+ it("resolves navigator-driven paths through the slug index (routed mode)", async () => {
106
+ const navigation = createMemoryNavigator("/backoffice/todo");
107
+ renderWithProviders(
108
+ <Desk frappe={frappe()} navigation={navigation} surfaces={{ list: SurfaceProbe }} />,
109
+ );
110
+ // "todo" is ambiguous until the fixture index resolves it to ToDo.
111
+ await waitFor(() => {
112
+ expect(screen.getByTestId("probe-list").textContent).toContain('"ToDo"');
113
+ });
114
+ });
115
+
116
+ it("re-resolves when the navigator moves: list → form → back (deep-link round-trip)", async () => {
117
+ const navigation = createMemoryNavigator("/backoffice/todo");
118
+ renderWithProviders(
119
+ <Desk
120
+ frappe={frappe()}
121
+ navigation={navigation}
122
+ surfaces={{ list: SurfaceProbe, form: SurfaceProbe, "not-found": SurfaceProbe }}
123
+ />,
124
+ );
125
+ await waitFor(() => expect(screen.getByTestId("probe-list")).toBeTruthy());
126
+
127
+ navigation.navigate("/backoffice/todo/T-0001");
128
+ await waitFor(() => {
129
+ expect(screen.getByTestId("probe-form").textContent).toContain('"T-0001"');
130
+ });
131
+
132
+ navigation.back();
133
+ await waitFor(() => expect(screen.getByTestId("probe-list")).toBeTruthy());
134
+ });
135
+
136
+ it("onNavigate intercepts desk-issued navigation and cancels it (the drawer seam)", async () => {
137
+ // The override navigates the way every surface does: through the
138
+ // provider's navigator — the seam onNavigate wraps.
139
+ function ListWithRowPress() {
140
+ const navigator = useBackofficeNavigator();
141
+ const paths = useBackofficePaths();
142
+ return (
143
+ <button
144
+ type="button"
145
+ data-testid="open-doc"
146
+ onClick={() => navigator.navigate(paths.form("ToDo", "T-0001"))}
147
+ >
148
+ open
149
+ </button>
150
+ );
151
+ }
152
+ const seen: string[] = [];
153
+ const navigation = createMemoryNavigator("/backoffice/todo");
154
+ renderWithProviders(
155
+ <Desk
156
+ frappe={frappe()}
157
+ navigation={navigation}
158
+ surfaces={{ list: ListWithRowPress, form: SurfaceProbe }}
159
+ onNavigate={(target) => {
160
+ seen.push(target.kind);
161
+ if (target.kind === "form") return false;
162
+ }}
163
+ />,
164
+ );
165
+ await waitFor(() => expect(screen.getByTestId("open-doc")).toBeTruthy());
166
+
167
+ fireEvent.click(screen.getByTestId("open-doc"));
168
+ // cancelled: the host saw the target, the desk never left the list
169
+ expect(seen).toContain("form");
170
+ expect(navigation.getLocation().path).toBe("/backoffice/todo");
171
+ expect(screen.queryByTestId("probe-form")).toBeNull();
172
+ });
173
+
174
+ it("per-doctype surface overrides beat per-kind, and miss falls through", () => {
175
+ function DoctypeProbe({ target }: DeskSurfaceProps) {
176
+ return <div data-testid="doctype-probe">{JSON.stringify(target)}</div>;
177
+ }
178
+ renderWithProviders(
179
+ <Desk
180
+ frappe={frappe()}
181
+ doctype="Sales Order"
182
+ surfaces={{ list: { "Sales Order": DoctypeProbe } }}
183
+ />,
184
+ );
185
+ expect(screen.getByTestId("doctype-probe")).toBeTruthy();
186
+
187
+ // A record keyed by a different doctype misses → built-in list mounts
188
+ // (its loading state), not the probe.
189
+ renderWithProviders(
190
+ <Desk
191
+ frappe={frappe()}
192
+ doctype="ToDo"
193
+ surfaces={{ list: { "Sales Order": DoctypeProbe } }}
194
+ />,
195
+ );
196
+ expect(screen.queryAllByTestId("doctype-probe")).toHaveLength(1);
197
+ });
198
+
199
+ it("basePath rebinds resolution (mounted at /admin)", async () => {
200
+ const navigation = createMemoryNavigator("/admin/todo");
201
+ renderWithProviders(
202
+ <Desk
203
+ frappe={frappe()}
204
+ navigation={navigation}
205
+ basePath="/admin"
206
+ surfaces={{ list: SurfaceProbe }}
207
+ />,
208
+ );
209
+ await waitFor(() => {
210
+ expect(screen.getByTestId("probe-list").textContent).toContain('"ToDo"');
211
+ });
212
+ });
213
+ });
214
+
215
+ describe("two Desks on one page (DK-7)", () => {
216
+ function FieldProbe({ testId }: { testId: string }) {
217
+ const resolve = useFieldResolver();
218
+ const Field = resolve("Data");
219
+ if (!Field) return null;
220
+ return (
221
+ <div data-testid={testId}>
222
+ <Field value="" onChange={() => {}} label="probe" />
223
+ </div>
224
+ );
225
+ }
226
+
227
+ it("field overrides are per-Desk and never touch the module registry (GL-4)", () => {
228
+ const globalData = getFieldComponent("Data");
229
+ const CustomA = () => <div data-testid="custom-a">A</div>;
230
+ const CustomB = () => <div data-testid="custom-b">B</div>;
231
+ const probeA = () => <FieldProbe testId="desk-a-field" />;
232
+ const probeB = () => <FieldProbe testId="desk-b-field" />;
233
+
234
+ renderWithProviders(
235
+ <>
236
+ <Desk
237
+ frappe={frappe()}
238
+ doctype="ToDo"
239
+ fields={{ Data: CustomA }}
240
+ surfaces={{ list: probeA }}
241
+ />
242
+ <Desk
243
+ frappe={frappe()}
244
+ doctype="ToDo"
245
+ fields={{ Data: CustomB }}
246
+ surfaces={{ list: probeB }}
247
+ />
248
+ </>,
249
+ );
250
+
251
+ // Each Desk resolves its own override…
252
+ expect(
253
+ screen.getByTestId("desk-a-field").querySelector('[data-testid="custom-a"]'),
254
+ ).toBeTruthy();
255
+ expect(
256
+ screen.getByTestId("desk-b-field").querySelector('[data-testid="custom-b"]'),
257
+ ).toBeTruthy();
258
+ expect(screen.getByTestId("desk-a-field").querySelector('[data-testid="custom-b"]')).toBeNull();
259
+ // …and the module-global default table is untouched.
260
+ expect(getFieldComponent("Data")).toBe(globalData);
261
+ });
262
+
263
+ it("scope is per-Desk context (GL-5)", () => {
264
+ function ScopeProbe({ testId }: { testId: string }) {
265
+ const scope = useDeskScope();
266
+ return <div data-testid={testId}>{JSON.stringify(scope)}</div>;
267
+ }
268
+ renderWithProviders(
269
+ <>
270
+ <Desk
271
+ frappe={frappe()}
272
+ doctype="ToDo"
273
+ scope={[["company", "=", "A"]]}
274
+ surfaces={{ list: () => <ScopeProbe testId="scope-a" /> }}
275
+ />
276
+ <Desk
277
+ frappe={frappe()}
278
+ doctype="ToDo"
279
+ scope={[["company", "=", "B"]]}
280
+ surfaces={{ list: () => <ScopeProbe testId="scope-b" /> }}
281
+ />
282
+ </>,
283
+ );
284
+ expect(screen.getByTestId("scope-a").textContent).toBe('[["company","=","A"]]');
285
+ expect(screen.getByTestId("scope-b").textContent).toBe('[["company","=","B"]]');
286
+ });
287
+ });
@@ -0,0 +1,308 @@
1
+ /**
2
+ * <Desk /> — the whole desk as one importable component (MPO-14 / gap GL-2,
3
+ * spec 09). One required prop, and that prop is nothing: `<Desk />` renders
4
+ * the full desk; everything else is a narrowing.
5
+ *
6
+ * Desk is assembly, not implementation: prop normalization into
7
+ * BackofficeProvider, a navigator pick (host adapter or an internal
8
+ * MemoryNavigator seeded from the pins), the path→surface resolution
9
+ * (useDeskTarget), and the chrome pick. No fetch, no filter construction,
10
+ * no field mapping, no visual values — the §9.2 doctrine greps over this
11
+ * directory ship as a spec test (doctrine.spec.ts).
12
+ */
13
+ import { YStack } from "@multiplatform.one/components";
14
+ import { useFrappeConfig, type FieldRenderProps } from "@multiplatform.one/frappe-ui";
15
+ import type { ComponentType } from "react";
16
+ import { useEffect, useMemo, useRef } from "react";
17
+ import {
18
+ BackofficeProvider,
19
+ type BackofficeActions,
20
+ type BackofficeAppearance,
21
+ type BackofficeFrappeConfig,
22
+ type BackofficeIntegrations,
23
+ } from "../config/BackofficeProvider";
24
+ import type { BackofficeEventHandler } from "../config/events";
25
+ import type { BackofficeCapabilities, BackofficeDoctypeScope } from "../config/capabilities";
26
+ import type { BackofficeSlots } from "../config/slots";
27
+ import { createMemoryNavigator, type BackofficeNavigator } from "../navigation/navigator";
28
+ import { BACKOFFICE_BASE, createBackofficePaths, type BackofficeView } from "../navigation/paths";
29
+ import type { FrappeFilters } from "../shared/types";
30
+ import { BackofficeShell } from "../shell/BackofficeShell";
31
+ import { DeskSurface, DeskSurfaceLoading, type DeskSurfaceOverrides } from "./DeskSurface";
32
+ import { DeskScopeProvider } from "./scope";
33
+ import {
34
+ guessDeskTarget,
35
+ hasDeskPin,
36
+ useDeskTarget,
37
+ type DeskPins,
38
+ type DeskTarget,
39
+ } from "./target";
40
+
41
+ export type DeskChrome = "full" | "bare" | "content";
42
+
43
+ export interface DeskProps {
44
+ // ─── connection ────────────────────────────────────────────────────────
45
+ /**
46
+ * Frappe connection. Omit to inherit an ambient FrappeProvider (or
47
+ * BackofficeProvider). IGNORED when an ambient provider exists — Desk
48
+ * warns once, because the SyncModule is cached per baseURL and a second
49
+ * config for the same server silently loses to the first.
50
+ */
51
+ frappe?: BackofficeFrappeConfig;
52
+
53
+ // ─── what it shows (the pins) ──────────────────────────────────────────
54
+ /** Pin the desk to one doctype. Sets the initial target AND the scope. */
55
+ doctype?: string;
56
+ /** With `doctype`: a document name, or "new". Renders the form. */
57
+ name?: string;
58
+ /** With `doctype`: which view. Default "list". */
59
+ view?: BackofficeView;
60
+ /** Pin the desk to one workspace page. */
61
+ workspace?: string;
62
+ /** Pin the desk to one Query/Script Report. */
63
+ report?: string;
64
+ /**
65
+ * Explicit surface address relative to `basePath` (e.g.
66
+ * "/sales-order/SO-0001"). Wins over every pin — the escape hatch for a
67
+ * host with its own address scheme.
68
+ */
69
+ path?: string;
70
+
71
+ // ─── scoping ───────────────────────────────────────────────────────────
72
+ /** Seed the user-visible filter channel; the user can change or clear. */
73
+ filters?: FrappeFilters;
74
+ /**
75
+ * ALWAYS ANDed into every doctype-homed query, invisible to the filter
76
+ * chrome, not removable — the tenant seam (DeskScopeProvider).
77
+ */
78
+ scope?: FrappeFilters;
79
+ /** Doctype reachability. A `doctype` pin defaults this to that doctype. */
80
+ doctypes?: BackofficeDoctypeScope;
81
+
82
+ // ─── which surfaces are enabled ────────────────────────────────────────
83
+ /** Feature flags, everything defaults on (config/capabilities.ts). */
84
+ capabilities?: BackofficeCapabilities;
85
+ /** View-switcher allowlist, intersected with capabilities.views.allow. */
86
+ views?: readonly BackofficeView[];
87
+ /** Force every form read-only regardless of permissions. */
88
+ readOnly?: boolean;
89
+
90
+ // ─── chrome ────────────────────────────────────────────────────────────
91
+ /**
92
+ * "full" banner + workspace rail; "bare" banner only; "content" no
93
+ * chrome. Default: "content" when any pin is set, "full" otherwise.
94
+ */
95
+ chrome?: DeskChrome;
96
+ /** Sugar for chrome="content". */
97
+ embedded?: boolean;
98
+ /** Render-prop chrome replacements (config/slots.ts). */
99
+ slots?: BackofficeSlots;
100
+ /** Color-scheme adapter for the user menu's "Toggle Theme". */
101
+ appearance?: BackofficeAppearance;
102
+ /** Router-integration hooks (navigation blocker, …). */
103
+ integrations?: BackofficeIntegrations;
104
+
105
+ // ─── routing ───────────────────────────────────────────────────────────
106
+ /**
107
+ * Host router adapter. Supply it and Desk owns URLs; omit it and Desk
108
+ * navigates internally over a MemoryNavigator seeded from the pins.
109
+ */
110
+ navigation?: BackofficeNavigator;
111
+ /** URL prefix Desk is mounted under. Default "/backoffice". */
112
+ basePath?: string;
113
+ /**
114
+ * Intercept push/replace navigations before they happen; return false to
115
+ * cancel (host renders the target itself, e.g. in a drawer). The target
116
+ * is resolved best-effort: an ambiguous slug degrades to a title-cased
117
+ * doctype guess, the same degrade the slug index applies offline.
118
+ */
119
+ onNavigate?: (target: DeskTarget, path: string) => boolean | void;
120
+ /** Fires whenever the resolved surface changes (host breadcrumbs/titles). */
121
+ onTargetChange?: (target: DeskTarget) => void;
122
+
123
+ // ─── extension ─────────────────────────────────────────────────────────
124
+ /** Replace a whole surface, per kind or per doctype under a kind. */
125
+ surfaces?: DeskSurfaceOverrides;
126
+ /** Replace a field control by fieldtype — context-scoped per Desk. */
127
+ fields?: Record<string, ComponentType<FieldRenderProps>>;
128
+ /** Host actions on form and list toolbars, optionally per-doctype. */
129
+ actions?: BackofficeActions;
130
+ /** Every notable desk action lands here (config/events.ts). */
131
+ onEvent?: BackofficeEventHandler;
132
+ }
133
+
134
+ /** Pins → the internal navigator's seed path. */
135
+ function pinnedPath(pins: DeskPins, basePath: string): string {
136
+ const paths = createBackofficePaths(basePath);
137
+ if (pins.path) {
138
+ const prefix = basePath === "/" ? "" : basePath.replace(/\/+$/, "");
139
+ return `${prefix}${pins.path}`;
140
+ }
141
+ if (pins.doctype) {
142
+ if (pins.name === "new") return paths.newDoc(pins.doctype);
143
+ if (pins.name) return paths.form(pins.doctype, pins.name);
144
+ if (pins.view && pins.view !== "list") return paths.view(pins.doctype, pins.view);
145
+ return paths.list(pins.doctype);
146
+ }
147
+ if (pins.workspace) return paths.workspace(pins.workspace);
148
+ if (pins.report) return paths.queryReport(pins.report);
149
+ return paths.home();
150
+ }
151
+
152
+ export function Desk(props: DeskProps) {
153
+ const {
154
+ doctype,
155
+ name,
156
+ view,
157
+ workspace,
158
+ report,
159
+ path,
160
+ basePath = BACKOFFICE_BASE,
161
+ chrome: chromeProp,
162
+ embedded,
163
+ onNavigate,
164
+ } = props;
165
+
166
+ const pins = useMemo<DeskPins>(
167
+ () => ({ doctype, name, view, workspace, report, path }),
168
+ [doctype, name, view, workspace, report, path],
169
+ );
170
+
171
+ // ── connection: ambient provider wins over the prop, warned once ──────
172
+ const ambient = useFrappeConfig();
173
+ const warned = useRef(false);
174
+ if (ambient && props.frappe && !warned.current) {
175
+ warned.current = true;
176
+ console.warn(
177
+ "[backoffice] <Desk frappe> is ignored: an ambient FrappeProvider exists and the SyncModule is cached per baseURL.",
178
+ );
179
+ }
180
+ const frappe = useMemo<BackofficeFrappeConfig>(() => {
181
+ if (ambient) {
182
+ return { baseURL: ambient.baseURL, auth: ambient.auth, fixtures: ambient.fixtures };
183
+ }
184
+ return props.frappe ?? { baseURL: "" };
185
+ }, [ambient, props.frappe]);
186
+
187
+ // ── navigation: host adapter, else internal memory (seeded once) ──────
188
+ const memoryNavigator = useMemo(
189
+ () => (props.navigation ? undefined : createMemoryNavigator(pinnedPath(pins, basePath))),
190
+ // The seed is the ENTRY; later pin changes re-enter through useDeskTarget.
191
+ // oxlint-disable-next-line exhaustive-deps
192
+ [props.navigation ? "host" : "memory"],
193
+ );
194
+ const baseNavigation = props.navigation ?? memoryNavigator!;
195
+
196
+ const onNavigateRef = useRef(onNavigate);
197
+ onNavigateRef.current = onNavigate;
198
+ const navigation = useMemo<BackofficeNavigator>(() => {
199
+ const intercept = (path: string): boolean =>
200
+ onNavigateRef.current?.(guessDeskTarget(path, basePath), path) !== false;
201
+ return {
202
+ ...baseNavigation,
203
+ navigate: (path) => {
204
+ if (intercept(path)) baseNavigation.navigate(path);
205
+ },
206
+ replace: (path) => {
207
+ if (intercept(path)) baseNavigation.replace(path);
208
+ },
209
+ back: () => baseNavigation.back(),
210
+ getLocation: () => baseNavigation.getLocation(),
211
+ subscribe: (listener) => baseNavigation.subscribe(listener),
212
+ ...(baseNavigation.setParams
213
+ ? {
214
+ setParams: (params: Record<string, string | undefined>) =>
215
+ baseNavigation.setParams?.(params),
216
+ }
217
+ : {}),
218
+ };
219
+ }, [baseNavigation, basePath]);
220
+
221
+ // ── narrowings: chrome, doctype fence, bare-chrome capability merge ───
222
+ const chrome: DeskChrome = chromeProp ?? (embedded || hasDeskPin(pins) ? "content" : "full");
223
+ const doctypes = props.doctypes ?? (doctype ? { allow: [doctype] } : undefined);
224
+ const capabilities = useMemo<BackofficeCapabilities | undefined>(
225
+ () => (chrome === "bare" ? { ...props.capabilities, sidebar: false } : props.capabilities),
226
+ [chrome, props.capabilities],
227
+ );
228
+
229
+ return (
230
+ <BackofficeProvider
231
+ frappe={frappe}
232
+ navigation={navigation}
233
+ basePath={basePath}
234
+ capabilities={capabilities}
235
+ doctypes={doctypes}
236
+ fields={props.fields}
237
+ actions={props.actions}
238
+ slots={props.slots}
239
+ appearance={props.appearance}
240
+ integrations={props.integrations}
241
+ onEvent={props.onEvent}
242
+ >
243
+ <DeskScopeProvider scope={props.scope}>
244
+ <DeskBody
245
+ pins={pins}
246
+ chrome={chrome}
247
+ readOnly={props.readOnly}
248
+ surfaces={props.surfaces}
249
+ initialFilters={props.filters}
250
+ views={props.views}
251
+ onTargetChange={props.onTargetChange}
252
+ />
253
+ </DeskScopeProvider>
254
+ </BackofficeProvider>
255
+ );
256
+ }
257
+
258
+ interface DeskBodyProps {
259
+ pins: DeskPins;
260
+ chrome: DeskChrome;
261
+ readOnly?: boolean;
262
+ surfaces?: DeskSurfaceOverrides;
263
+ initialFilters?: FrappeFilters;
264
+ views?: readonly BackofficeView[];
265
+ onTargetChange?: (target: DeskTarget) => void;
266
+ }
267
+
268
+ /** Inside the provider: resolve the target, pick the chrome, mount the surface. */
269
+ function DeskBody({
270
+ pins,
271
+ chrome,
272
+ readOnly,
273
+ surfaces,
274
+ initialFilters,
275
+ views,
276
+ onTargetChange,
277
+ }: DeskBodyProps) {
278
+ const target = useDeskTarget(pins);
279
+
280
+ const onTargetChangeRef = useRef(onTargetChange);
281
+ onTargetChangeRef.current = onTargetChange;
282
+ const targetKey = target ? JSON.stringify(target) : undefined;
283
+ useEffect(() => {
284
+ if (target) onTargetChangeRef.current?.(target);
285
+ // oxlint-disable-next-line exhaustive-deps
286
+ }, [targetKey]);
287
+
288
+ const surface = target ? (
289
+ <DeskSurface
290
+ target={target}
291
+ overrides={surfaces}
292
+ readOnly={readOnly}
293
+ initialFilters={initialFilters}
294
+ views={views}
295
+ />
296
+ ) : (
297
+ <DeskSurfaceLoading />
298
+ );
299
+
300
+ if (chrome === "content") {
301
+ return (
302
+ <YStack flex={1} minHeight={0} minWidth={0}>
303
+ {surface}
304
+ </YStack>
305
+ );
306
+ }
307
+ return <BackofficeShell>{surface}</BackofficeShell>;
308
+ }