@multiplatform.one/backoffice 6.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 (414) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +310 -0
  3. package/dist/cjs/chrome/BackofficeAwesomebar.cjs +95 -0
  4. package/dist/cjs/chrome/BackofficeAwesomebar.native.js +106 -0
  5. package/dist/cjs/chrome/BackofficeAwesomebar.native.js.map +1 -0
  6. package/dist/cjs/chrome/CommandPalette.cjs +453 -0
  7. package/dist/cjs/chrome/CommandPalette.native.js +505 -0
  8. package/dist/cjs/chrome/CommandPalette.native.js.map +1 -0
  9. package/dist/cjs/chrome/HelpMenu.cjs +131 -0
  10. package/dist/cjs/chrome/HelpMenu.native.js +145 -0
  11. package/dist/cjs/chrome/HelpMenu.native.js.map +1 -0
  12. package/dist/cjs/chrome/NotificationsButton.cjs +112 -0
  13. package/dist/cjs/chrome/NotificationsButton.native.js +115 -0
  14. package/dist/cjs/chrome/NotificationsButton.native.js.map +1 -0
  15. package/dist/cjs/chrome/UserMenu.cjs +158 -0
  16. package/dist/cjs/chrome/UserMenu.native.js +186 -0
  17. package/dist/cjs/chrome/UserMenu.native.js.map +1 -0
  18. package/dist/cjs/chrome/awesomebar-data.cjs +173 -0
  19. package/dist/cjs/chrome/awesomebar-data.native.js +233 -0
  20. package/dist/cjs/chrome/awesomebar-data.native.js.map +1 -0
  21. package/dist/cjs/chrome/command-palette-model.cjs +208 -0
  22. package/dist/cjs/chrome/command-palette-model.native.js +301 -0
  23. package/dist/cjs/chrome/command-palette-model.native.js.map +1 -0
  24. package/dist/cjs/chrome/help-about-dialog.cjs +136 -0
  25. package/dist/cjs/chrome/help-about-dialog.native.js +146 -0
  26. package/dist/cjs/chrome/help-about-dialog.native.js.map +1 -0
  27. package/dist/cjs/chrome/help-shortcuts-dialog.cjs +155 -0
  28. package/dist/cjs/chrome/help-shortcuts-dialog.native.js +168 -0
  29. package/dist/cjs/chrome/help-shortcuts-dialog.native.js.map +1 -0
  30. package/dist/cjs/chrome/notifications-data.cjs +190 -0
  31. package/dist/cjs/chrome/notifications-data.native.js +271 -0
  32. package/dist/cjs/chrome/notifications-data.native.js.map +1 -0
  33. package/dist/cjs/chrome/notifications-panel.cjs +171 -0
  34. package/dist/cjs/chrome/notifications-panel.native.js +189 -0
  35. package/dist/cjs/chrome/notifications-panel.native.js.map +1 -0
  36. package/dist/cjs/chrome/notifications-utils.cjs +43 -0
  37. package/dist/cjs/chrome/notifications-utils.native.js +46 -0
  38. package/dist/cjs/chrome/notifications-utils.native.js.map +1 -0
  39. package/dist/cjs/chrome/shortcuts.cjs +58 -0
  40. package/dist/cjs/chrome/shortcuts.native.js +72 -0
  41. package/dist/cjs/chrome/shortcuts.native.js.map +1 -0
  42. package/dist/cjs/chrome/user-session.cjs +100 -0
  43. package/dist/cjs/chrome/user-session.native.js +120 -0
  44. package/dist/cjs/chrome/user-session.native.js.map +1 -0
  45. package/dist/cjs/config/BackofficeProvider.cjs +164 -0
  46. package/dist/cjs/config/BackofficeProvider.native.js +217 -0
  47. package/dist/cjs/config/BackofficeProvider.native.js.map +1 -0
  48. package/dist/cjs/config/capabilities.cjs +123 -0
  49. package/dist/cjs/config/capabilities.native.js +155 -0
  50. package/dist/cjs/config/capabilities.native.js.map +1 -0
  51. package/dist/cjs/config/events.cjs +18 -0
  52. package/dist/cjs/config/events.native.js +21 -0
  53. package/dist/cjs/config/events.native.js.map +1 -0
  54. package/dist/cjs/config/slots.cjs +18 -0
  55. package/dist/cjs/config/slots.native.js +21 -0
  56. package/dist/cjs/config/slots.native.js.map +1 -0
  57. package/dist/cjs/i18n/en.cjs +101 -0
  58. package/dist/cjs/i18n/en.native.js +104 -0
  59. package/dist/cjs/i18n/en.native.js.map +1 -0
  60. package/dist/cjs/i18n/index.cjs +77 -0
  61. package/dist/cjs/i18n/index.native.js +102 -0
  62. package/dist/cjs/i18n/index.native.js.map +1 -0
  63. package/dist/cjs/index.cjs +109 -0
  64. package/dist/cjs/index.native.js +112 -0
  65. package/dist/cjs/index.native.js.map +1 -0
  66. package/dist/cjs/navigation/navigator.cjs +144 -0
  67. package/dist/cjs/navigation/navigator.native.js +307 -0
  68. package/dist/cjs/navigation/navigator.native.js.map +1 -0
  69. package/dist/cjs/navigation/paths.cjs +103 -0
  70. package/dist/cjs/navigation/paths.native.js +143 -0
  71. package/dist/cjs/navigation/paths.native.js.map +1 -0
  72. package/dist/cjs/oneAdapter/index.cjs +53 -0
  73. package/dist/cjs/oneAdapter/index.native.js +64 -0
  74. package/dist/cjs/oneAdapter/index.native.js.map +1 -0
  75. package/dist/cjs/shared/useFrappeApp.cjs +56 -0
  76. package/dist/cjs/shared/useFrappeApp.native.js +60 -0
  77. package/dist/cjs/shared/useFrappeApp.native.js.map +1 -0
  78. package/dist/cjs/shell/BackofficeShell.cjs +194 -0
  79. package/dist/cjs/shell/BackofficeShell.native.js +223 -0
  80. package/dist/cjs/shell/BackofficeShell.native.js.map +1 -0
  81. package/dist/cjs/shell/WorkspaceSidebar.cjs +115 -0
  82. package/dist/cjs/shell/WorkspaceSidebar.native.js +143 -0
  83. package/dist/cjs/shell/WorkspaceSidebar.native.js.map +1 -0
  84. package/dist/cjs/shell/useWorkspaces.cjs +131 -0
  85. package/dist/cjs/shell/useWorkspaces.native.js +173 -0
  86. package/dist/cjs/shell/useWorkspaces.native.js.map +1 -0
  87. package/dist/cjs/shell/workspaceIcons.cjs +64 -0
  88. package/dist/cjs/shell/workspaceIcons.native.js +67 -0
  89. package/dist/cjs/shell/workspaceIcons.native.js.map +1 -0
  90. package/dist/esm/chrome/BackofficeAwesomebar.mjs +70 -0
  91. package/dist/esm/chrome/BackofficeAwesomebar.mjs.map +1 -0
  92. package/dist/esm/chrome/BackofficeAwesomebar.native.js +78 -0
  93. package/dist/esm/chrome/BackofficeAwesomebar.native.js.map +1 -0
  94. package/dist/esm/chrome/CommandPalette.mjs +428 -0
  95. package/dist/esm/chrome/CommandPalette.mjs.map +1 -0
  96. package/dist/esm/chrome/CommandPalette.native.js +477 -0
  97. package/dist/esm/chrome/CommandPalette.native.js.map +1 -0
  98. package/dist/esm/chrome/HelpMenu.mjs +106 -0
  99. package/dist/esm/chrome/HelpMenu.mjs.map +1 -0
  100. package/dist/esm/chrome/HelpMenu.native.js +117 -0
  101. package/dist/esm/chrome/HelpMenu.native.js.map +1 -0
  102. package/dist/esm/chrome/NotificationsButton.mjs +87 -0
  103. package/dist/esm/chrome/NotificationsButton.mjs.map +1 -0
  104. package/dist/esm/chrome/NotificationsButton.native.js +87 -0
  105. package/dist/esm/chrome/NotificationsButton.native.js.map +1 -0
  106. package/dist/esm/chrome/UserMenu.mjs +133 -0
  107. package/dist/esm/chrome/UserMenu.mjs.map +1 -0
  108. package/dist/esm/chrome/UserMenu.native.js +147 -0
  109. package/dist/esm/chrome/UserMenu.native.js.map +1 -0
  110. package/dist/esm/chrome/awesomebar-data.mjs +144 -0
  111. package/dist/esm/chrome/awesomebar-data.mjs.map +1 -0
  112. package/dist/esm/chrome/awesomebar-data.native.js +190 -0
  113. package/dist/esm/chrome/awesomebar-data.native.js.map +1 -0
  114. package/dist/esm/chrome/command-palette-model.mjs +183 -0
  115. package/dist/esm/chrome/command-palette-model.mjs.map +1 -0
  116. package/dist/esm/chrome/command-palette-model.native.js +273 -0
  117. package/dist/esm/chrome/command-palette-model.native.js.map +1 -0
  118. package/dist/esm/chrome/help-about-dialog.mjs +111 -0
  119. package/dist/esm/chrome/help-about-dialog.mjs.map +1 -0
  120. package/dist/esm/chrome/help-about-dialog.native.js +118 -0
  121. package/dist/esm/chrome/help-about-dialog.native.js.map +1 -0
  122. package/dist/esm/chrome/help-shortcuts-dialog.mjs +130 -0
  123. package/dist/esm/chrome/help-shortcuts-dialog.mjs.map +1 -0
  124. package/dist/esm/chrome/help-shortcuts-dialog.native.js +140 -0
  125. package/dist/esm/chrome/help-shortcuts-dialog.native.js.map +1 -0
  126. package/dist/esm/chrome/notifications-data.mjs +165 -0
  127. package/dist/esm/chrome/notifications-data.mjs.map +1 -0
  128. package/dist/esm/chrome/notifications-data.native.js +232 -0
  129. package/dist/esm/chrome/notifications-data.native.js.map +1 -0
  130. package/dist/esm/chrome/notifications-panel.mjs +146 -0
  131. package/dist/esm/chrome/notifications-panel.mjs.map +1 -0
  132. package/dist/esm/chrome/notifications-panel.native.js +161 -0
  133. package/dist/esm/chrome/notifications-panel.native.js.map +1 -0
  134. package/dist/esm/chrome/notifications-utils.mjs +17 -0
  135. package/dist/esm/chrome/notifications-utils.mjs.map +1 -0
  136. package/dist/esm/chrome/notifications-utils.native.js +17 -0
  137. package/dist/esm/chrome/notifications-utils.native.js.map +1 -0
  138. package/dist/esm/chrome/shortcuts.mjs +33 -0
  139. package/dist/esm/chrome/shortcuts.mjs.map +1 -0
  140. package/dist/esm/chrome/shortcuts.native.js +44 -0
  141. package/dist/esm/chrome/shortcuts.native.js.map +1 -0
  142. package/dist/esm/chrome/user-session.mjs +73 -0
  143. package/dist/esm/chrome/user-session.mjs.map +1 -0
  144. package/dist/esm/chrome/user-session.native.js +79 -0
  145. package/dist/esm/chrome/user-session.native.js.map +1 -0
  146. package/dist/esm/config/BackofficeProvider.mjs +132 -0
  147. package/dist/esm/config/BackofficeProvider.mjs.map +1 -0
  148. package/dist/esm/config/BackofficeProvider.native.js +171 -0
  149. package/dist/esm/config/BackofficeProvider.native.js.map +1 -0
  150. package/dist/esm/config/capabilities.mjs +97 -0
  151. package/dist/esm/config/capabilities.mjs.map +1 -0
  152. package/dist/esm/config/capabilities.native.js +126 -0
  153. package/dist/esm/config/capabilities.native.js.map +1 -0
  154. package/dist/esm/config/events.mjs +2 -0
  155. package/dist/esm/config/events.mjs.map +1 -0
  156. package/dist/esm/config/events.native.js +2 -0
  157. package/dist/esm/config/events.native.js.map +1 -0
  158. package/dist/esm/config/slots.mjs +2 -0
  159. package/dist/esm/config/slots.mjs.map +1 -0
  160. package/dist/esm/config/slots.native.js +2 -0
  161. package/dist/esm/config/slots.native.js.map +1 -0
  162. package/dist/esm/i18n/en.mjs +76 -0
  163. package/dist/esm/i18n/en.mjs.map +1 -0
  164. package/dist/esm/i18n/en.native.js +76 -0
  165. package/dist/esm/i18n/en.native.js.map +1 -0
  166. package/dist/esm/i18n/index.mjs +40 -0
  167. package/dist/esm/i18n/index.mjs.map +1 -0
  168. package/dist/esm/i18n/index.native.js +62 -0
  169. package/dist/esm/i18n/index.native.js.map +1 -0
  170. package/dist/esm/index.mjs +26 -0
  171. package/dist/esm/index.mjs.map +1 -0
  172. package/dist/esm/index.native.js +26 -0
  173. package/dist/esm/index.native.js.map +1 -0
  174. package/dist/esm/navigation/navigator.mjs +115 -0
  175. package/dist/esm/navigation/navigator.mjs.map +1 -0
  176. package/dist/esm/navigation/navigator.native.js +275 -0
  177. package/dist/esm/navigation/navigator.native.js.map +1 -0
  178. package/dist/esm/navigation/paths.mjs +66 -0
  179. package/dist/esm/navigation/paths.mjs.map +1 -0
  180. package/dist/esm/navigation/paths.native.js +103 -0
  181. package/dist/esm/navigation/paths.native.js.map +1 -0
  182. package/dist/esm/oneAdapter/index.mjs +28 -0
  183. package/dist/esm/oneAdapter/index.mjs.map +1 -0
  184. package/dist/esm/oneAdapter/index.native.js +36 -0
  185. package/dist/esm/oneAdapter/index.native.js.map +1 -0
  186. package/dist/esm/shared/useFrappeApp.mjs +31 -0
  187. package/dist/esm/shared/useFrappeApp.mjs.map +1 -0
  188. package/dist/esm/shared/useFrappeApp.native.js +32 -0
  189. package/dist/esm/shared/useFrappeApp.native.js.map +1 -0
  190. package/dist/esm/shell/BackofficeShell.mjs +169 -0
  191. package/dist/esm/shell/BackofficeShell.mjs.map +1 -0
  192. package/dist/esm/shell/BackofficeShell.native.js +195 -0
  193. package/dist/esm/shell/BackofficeShell.native.js.map +1 -0
  194. package/dist/esm/shell/WorkspaceSidebar.mjs +89 -0
  195. package/dist/esm/shell/WorkspaceSidebar.mjs.map +1 -0
  196. package/dist/esm/shell/WorkspaceSidebar.native.js +114 -0
  197. package/dist/esm/shell/WorkspaceSidebar.native.js.map +1 -0
  198. package/dist/esm/shell/useWorkspaces.mjs +104 -0
  199. package/dist/esm/shell/useWorkspaces.mjs.map +1 -0
  200. package/dist/esm/shell/useWorkspaces.native.js +143 -0
  201. package/dist/esm/shell/useWorkspaces.native.js.map +1 -0
  202. package/dist/esm/shell/workspaceIcons.mjs +39 -0
  203. package/dist/esm/shell/workspaceIcons.mjs.map +1 -0
  204. package/dist/esm/shell/workspaceIcons.native.js +39 -0
  205. package/dist/esm/shell/workspaceIcons.native.js.map +1 -0
  206. package/dist/jsx/chrome/BackofficeAwesomebar.mjs +70 -0
  207. package/dist/jsx/chrome/BackofficeAwesomebar.mjs.map +1 -0
  208. package/dist/jsx/chrome/BackofficeAwesomebar.native.js +106 -0
  209. package/dist/jsx/chrome/BackofficeAwesomebar.native.js.map +1 -0
  210. package/dist/jsx/chrome/CommandPalette.mjs +428 -0
  211. package/dist/jsx/chrome/CommandPalette.mjs.map +1 -0
  212. package/dist/jsx/chrome/CommandPalette.native.js +505 -0
  213. package/dist/jsx/chrome/CommandPalette.native.js.map +1 -0
  214. package/dist/jsx/chrome/HelpMenu.mjs +106 -0
  215. package/dist/jsx/chrome/HelpMenu.mjs.map +1 -0
  216. package/dist/jsx/chrome/HelpMenu.native.js +145 -0
  217. package/dist/jsx/chrome/HelpMenu.native.js.map +1 -0
  218. package/dist/jsx/chrome/NotificationsButton.mjs +87 -0
  219. package/dist/jsx/chrome/NotificationsButton.mjs.map +1 -0
  220. package/dist/jsx/chrome/NotificationsButton.native.js +115 -0
  221. package/dist/jsx/chrome/NotificationsButton.native.js.map +1 -0
  222. package/dist/jsx/chrome/UserMenu.mjs +133 -0
  223. package/dist/jsx/chrome/UserMenu.mjs.map +1 -0
  224. package/dist/jsx/chrome/UserMenu.native.js +186 -0
  225. package/dist/jsx/chrome/UserMenu.native.js.map +1 -0
  226. package/dist/jsx/chrome/awesomebar-data.mjs +144 -0
  227. package/dist/jsx/chrome/awesomebar-data.mjs.map +1 -0
  228. package/dist/jsx/chrome/awesomebar-data.native.js +233 -0
  229. package/dist/jsx/chrome/awesomebar-data.native.js.map +1 -0
  230. package/dist/jsx/chrome/command-palette-model.mjs +183 -0
  231. package/dist/jsx/chrome/command-palette-model.mjs.map +1 -0
  232. package/dist/jsx/chrome/command-palette-model.native.js +301 -0
  233. package/dist/jsx/chrome/command-palette-model.native.js.map +1 -0
  234. package/dist/jsx/chrome/help-about-dialog.mjs +111 -0
  235. package/dist/jsx/chrome/help-about-dialog.mjs.map +1 -0
  236. package/dist/jsx/chrome/help-about-dialog.native.js +146 -0
  237. package/dist/jsx/chrome/help-about-dialog.native.js.map +1 -0
  238. package/dist/jsx/chrome/help-shortcuts-dialog.mjs +130 -0
  239. package/dist/jsx/chrome/help-shortcuts-dialog.mjs.map +1 -0
  240. package/dist/jsx/chrome/help-shortcuts-dialog.native.js +168 -0
  241. package/dist/jsx/chrome/help-shortcuts-dialog.native.js.map +1 -0
  242. package/dist/jsx/chrome/notifications-data.mjs +165 -0
  243. package/dist/jsx/chrome/notifications-data.mjs.map +1 -0
  244. package/dist/jsx/chrome/notifications-data.native.js +271 -0
  245. package/dist/jsx/chrome/notifications-data.native.js.map +1 -0
  246. package/dist/jsx/chrome/notifications-panel.mjs +146 -0
  247. package/dist/jsx/chrome/notifications-panel.mjs.map +1 -0
  248. package/dist/jsx/chrome/notifications-panel.native.js +189 -0
  249. package/dist/jsx/chrome/notifications-panel.native.js.map +1 -0
  250. package/dist/jsx/chrome/notifications-utils.mjs +17 -0
  251. package/dist/jsx/chrome/notifications-utils.mjs.map +1 -0
  252. package/dist/jsx/chrome/notifications-utils.native.js +46 -0
  253. package/dist/jsx/chrome/notifications-utils.native.js.map +1 -0
  254. package/dist/jsx/chrome/shortcuts.mjs +33 -0
  255. package/dist/jsx/chrome/shortcuts.mjs.map +1 -0
  256. package/dist/jsx/chrome/shortcuts.native.js +72 -0
  257. package/dist/jsx/chrome/shortcuts.native.js.map +1 -0
  258. package/dist/jsx/chrome/user-session.mjs +73 -0
  259. package/dist/jsx/chrome/user-session.mjs.map +1 -0
  260. package/dist/jsx/chrome/user-session.native.js +120 -0
  261. package/dist/jsx/chrome/user-session.native.js.map +1 -0
  262. package/dist/jsx/config/BackofficeProvider.mjs +132 -0
  263. package/dist/jsx/config/BackofficeProvider.mjs.map +1 -0
  264. package/dist/jsx/config/BackofficeProvider.native.js +217 -0
  265. package/dist/jsx/config/BackofficeProvider.native.js.map +1 -0
  266. package/dist/jsx/config/capabilities.mjs +97 -0
  267. package/dist/jsx/config/capabilities.mjs.map +1 -0
  268. package/dist/jsx/config/capabilities.native.js +155 -0
  269. package/dist/jsx/config/capabilities.native.js.map +1 -0
  270. package/dist/jsx/config/events.mjs +2 -0
  271. package/dist/jsx/config/events.mjs.map +1 -0
  272. package/dist/jsx/config/events.native.js +21 -0
  273. package/dist/jsx/config/events.native.js.map +1 -0
  274. package/dist/jsx/config/slots.mjs +2 -0
  275. package/dist/jsx/config/slots.mjs.map +1 -0
  276. package/dist/jsx/config/slots.native.js +21 -0
  277. package/dist/jsx/config/slots.native.js.map +1 -0
  278. package/dist/jsx/i18n/en.mjs +76 -0
  279. package/dist/jsx/i18n/en.mjs.map +1 -0
  280. package/dist/jsx/i18n/en.native.js +104 -0
  281. package/dist/jsx/i18n/en.native.js.map +1 -0
  282. package/dist/jsx/i18n/index.mjs +40 -0
  283. package/dist/jsx/i18n/index.mjs.map +1 -0
  284. package/dist/jsx/i18n/index.native.js +102 -0
  285. package/dist/jsx/i18n/index.native.js.map +1 -0
  286. package/dist/jsx/index.js +26 -0
  287. package/dist/jsx/index.js.map +1 -0
  288. package/dist/jsx/index.mjs +26 -0
  289. package/dist/jsx/index.mjs.map +1 -0
  290. package/dist/jsx/index.native.js +112 -0
  291. package/dist/jsx/index.native.js.map +1 -0
  292. package/dist/jsx/navigation/navigator.mjs +115 -0
  293. package/dist/jsx/navigation/navigator.mjs.map +1 -0
  294. package/dist/jsx/navigation/navigator.native.js +307 -0
  295. package/dist/jsx/navigation/navigator.native.js.map +1 -0
  296. package/dist/jsx/navigation/paths.mjs +66 -0
  297. package/dist/jsx/navigation/paths.mjs.map +1 -0
  298. package/dist/jsx/navigation/paths.native.js +143 -0
  299. package/dist/jsx/navigation/paths.native.js.map +1 -0
  300. package/dist/jsx/oneAdapter/index.mjs +28 -0
  301. package/dist/jsx/oneAdapter/index.mjs.map +1 -0
  302. package/dist/jsx/oneAdapter/index.native.js +64 -0
  303. package/dist/jsx/oneAdapter/index.native.js.map +1 -0
  304. package/dist/jsx/shared/useFrappeApp.mjs +31 -0
  305. package/dist/jsx/shared/useFrappeApp.mjs.map +1 -0
  306. package/dist/jsx/shared/useFrappeApp.native.js +60 -0
  307. package/dist/jsx/shared/useFrappeApp.native.js.map +1 -0
  308. package/dist/jsx/shell/BackofficeShell.mjs +169 -0
  309. package/dist/jsx/shell/BackofficeShell.mjs.map +1 -0
  310. package/dist/jsx/shell/BackofficeShell.native.js +223 -0
  311. package/dist/jsx/shell/BackofficeShell.native.js.map +1 -0
  312. package/dist/jsx/shell/WorkspaceSidebar.mjs +89 -0
  313. package/dist/jsx/shell/WorkspaceSidebar.mjs.map +1 -0
  314. package/dist/jsx/shell/WorkspaceSidebar.native.js +143 -0
  315. package/dist/jsx/shell/WorkspaceSidebar.native.js.map +1 -0
  316. package/dist/jsx/shell/useWorkspaces.mjs +104 -0
  317. package/dist/jsx/shell/useWorkspaces.mjs.map +1 -0
  318. package/dist/jsx/shell/useWorkspaces.native.js +173 -0
  319. package/dist/jsx/shell/useWorkspaces.native.js.map +1 -0
  320. package/dist/jsx/shell/workspaceIcons.mjs +39 -0
  321. package/dist/jsx/shell/workspaceIcons.mjs.map +1 -0
  322. package/dist/jsx/shell/workspaceIcons.native.js +67 -0
  323. package/dist/jsx/shell/workspaceIcons.native.js.map +1 -0
  324. package/package.json +123 -0
  325. package/src/chrome/BackofficeAwesomebar.tsx +89 -0
  326. package/src/chrome/CommandPalette.spec.tsx +252 -0
  327. package/src/chrome/CommandPalette.tsx +520 -0
  328. package/src/chrome/HelpMenu.tsx +119 -0
  329. package/src/chrome/NotificationsButton.tsx +87 -0
  330. package/src/chrome/UserMenu.tsx +173 -0
  331. package/src/chrome/awesomebar-data.tsx +252 -0
  332. package/src/chrome/command-palette-model.ts +291 -0
  333. package/src/chrome/help-about-dialog.tsx +130 -0
  334. package/src/chrome/help-shortcuts-dialog.tsx +144 -0
  335. package/src/chrome/notifications-data.tsx +287 -0
  336. package/src/chrome/notifications-panel.tsx +175 -0
  337. package/src/chrome/notifications-utils.tsx +44 -0
  338. package/src/chrome/shortcuts.ts +53 -0
  339. package/src/chrome/user-session.tsx +116 -0
  340. package/src/config/BackofficeProvider.spec.tsx +126 -0
  341. package/src/config/BackofficeProvider.tsx +315 -0
  342. package/src/config/capabilities.ts +172 -0
  343. package/src/config/events.ts +19 -0
  344. package/src/config/slots.ts +28 -0
  345. package/src/i18n/en.ts +83 -0
  346. package/src/i18n/index.ts +69 -0
  347. package/src/index.ts +150 -0
  348. package/src/navigation/navigator.spec.ts +106 -0
  349. package/src/navigation/navigator.ts +209 -0
  350. package/src/navigation/paths.ts +146 -0
  351. package/src/oneAdapter/index.tsx +61 -0
  352. package/src/shared/useFrappeApp.ts +39 -0
  353. package/src/shell/BackofficeShell.tsx +222 -0
  354. package/src/shell/WorkspaceSidebar.tsx +119 -0
  355. package/src/shell/useWorkspaces.ts +202 -0
  356. package/src/shell/workspaceIcons.ts +76 -0
  357. package/types/chrome/BackofficeAwesomebar.d.ts +12 -0
  358. package/types/chrome/BackofficeAwesomebar.d.ts.map +1 -0
  359. package/types/chrome/CommandPalette.d.ts +6 -0
  360. package/types/chrome/CommandPalette.d.ts.map +1 -0
  361. package/types/chrome/HelpMenu.d.ts +2 -0
  362. package/types/chrome/HelpMenu.d.ts.map +1 -0
  363. package/types/chrome/NotificationsButton.d.ts +2 -0
  364. package/types/chrome/NotificationsButton.d.ts.map +1 -0
  365. package/types/chrome/UserMenu.d.ts +6 -0
  366. package/types/chrome/UserMenu.d.ts.map +1 -0
  367. package/types/chrome/awesomebar-data.d.ts +52 -0
  368. package/types/chrome/awesomebar-data.d.ts.map +1 -0
  369. package/types/chrome/command-palette-model.d.ts +47 -0
  370. package/types/chrome/command-palette-model.d.ts.map +1 -0
  371. package/types/chrome/help-about-dialog.d.ts +12 -0
  372. package/types/chrome/help-about-dialog.d.ts.map +1 -0
  373. package/types/chrome/help-shortcuts-dialog.d.ts +6 -0
  374. package/types/chrome/help-shortcuts-dialog.d.ts.map +1 -0
  375. package/types/chrome/notifications-data.d.ts +34 -0
  376. package/types/chrome/notifications-data.d.ts.map +1 -0
  377. package/types/chrome/notifications-panel.d.ts +11 -0
  378. package/types/chrome/notifications-panel.d.ts.map +1 -0
  379. package/types/chrome/notifications-utils.d.ts +21 -0
  380. package/types/chrome/notifications-utils.d.ts.map +1 -0
  381. package/types/chrome/shortcuts.d.ts +6 -0
  382. package/types/chrome/shortcuts.d.ts.map +1 -0
  383. package/types/chrome/user-session.d.ts +19 -0
  384. package/types/chrome/user-session.d.ts.map +1 -0
  385. package/types/config/BackofficeProvider.d.ts +128 -0
  386. package/types/config/BackofficeProvider.d.ts.map +1 -0
  387. package/types/config/capabilities.d.ts +109 -0
  388. package/types/config/capabilities.d.ts.map +1 -0
  389. package/types/config/events.d.ts +39 -0
  390. package/types/config/events.d.ts.map +1 -0
  391. package/types/config/slots.d.ts +32 -0
  392. package/types/config/slots.d.ts.map +1 -0
  393. package/types/i18n/en.d.ts +83 -0
  394. package/types/i18n/en.d.ts.map +1 -0
  395. package/types/i18n/index.d.ts +27 -0
  396. package/types/i18n/index.d.ts.map +1 -0
  397. package/types/index.d.ts +55 -0
  398. package/types/index.d.ts.map +1 -0
  399. package/types/navigation/navigator.d.ts +98 -0
  400. package/types/navigation/navigator.d.ts.map +1 -0
  401. package/types/navigation/paths.d.ts +60 -0
  402. package/types/navigation/paths.d.ts.map +1 -0
  403. package/types/oneAdapter/index.d.ts +8 -0
  404. package/types/oneAdapter/index.d.ts.map +1 -0
  405. package/types/shared/useFrappeApp.d.ts +10 -0
  406. package/types/shared/useFrappeApp.d.ts.map +1 -0
  407. package/types/shell/BackofficeShell.d.ts +10 -0
  408. package/types/shell/BackofficeShell.d.ts.map +1 -0
  409. package/types/shell/WorkspaceSidebar.d.ts +9 -0
  410. package/types/shell/WorkspaceSidebar.d.ts.map +1 -0
  411. package/types/shell/useWorkspaces.d.ts +43 -0
  412. package/types/shell/useWorkspaces.d.ts.map +1 -0
  413. package/types/shell/workspaceIcons.d.ts +9 -0
  414. package/types/shell/workspaceIcons.d.ts.map +1 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Backoffice route paths.
3
+ *
4
+ * Route map (relative to the mount `basePath`, default "/backoffice"):
5
+ * {base} → home (default workspace)
6
+ * {base}/[slug] → workspace page OR doctype list (runtime-resolved)
7
+ * {base}/[slug]/view/[view] → alternate doctype views (report, kanban, …)
8
+ * {base}/[slug]/new → new document (optional `?d=` JSON prefill)
9
+ * {base}/[slug]/[name] → document form page
10
+ *
11
+ * Slugs follow frappe's router slugs: lowercase, spaces → dashes
12
+ * ("Sales Order" → "sales-order").
13
+ *
14
+ * Surfaces inside the package MUST build paths through
15
+ * `useBackofficeConfig().paths` (basePath-aware). The default-bound helpers
16
+ * at the bottom exist for host apps mounted at the canonical "/backoffice"
17
+ * prefix — their template-literal return types match one-router's generated
18
+ * typed-route unions so they can be passed straight to router.push and
19
+ * <Link href>.
20
+ */
21
+
22
+ export const BACKOFFICE_BASE = "/backoffice";
23
+
24
+ export type BackofficeSubPath = `${typeof BACKOFFICE_BASE}/${string}`;
25
+ export type BackofficePath = typeof BACKOFFICE_BASE | BackofficeSubPath;
26
+
27
+ export const BACKOFFICE_VIEWS = [
28
+ "list",
29
+ "report",
30
+ "kanban",
31
+ "calendar",
32
+ "tree",
33
+ "image",
34
+ "dashboard",
35
+ ] as const;
36
+
37
+ export type BackofficeView = (typeof BACKOFFICE_VIEWS)[number];
38
+
39
+ export function isBackofficeView(value: string): value is BackofficeView {
40
+ return (BACKOFFICE_VIEWS as readonly string[]).includes(value);
41
+ }
42
+
43
+ /** frappe.router.slug: "Sales Order" → "sales-order" */
44
+ export function toSlug(name: string): string {
45
+ return name.toLowerCase().replace(/ /g, "-");
46
+ }
47
+
48
+ /** Best-effort display name from a slug: "sales-order" → "Sales Order" */
49
+ export function slugToDisplayName(slug: string): string {
50
+ return slug.replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
51
+ }
52
+
53
+ /**
54
+ * Parse the `?d=` new-doc defaults query (urlencoded JSON object). The
55
+ * router usually decodes the param already, so the raw value is tried first.
56
+ * Malformed or non-object input degrades to no defaults.
57
+ */
58
+ export function parseDocDefaults(raw: string | undefined): Record<string, unknown> | undefined {
59
+ if (!raw) return undefined;
60
+ const candidates = [raw];
61
+ try {
62
+ const decoded = decodeURIComponent(raw);
63
+ if (decoded !== raw) candidates.push(decoded);
64
+ } catch {
65
+ // raw is not percent-encoded — the direct candidate covers it
66
+ }
67
+ for (const candidate of candidates) {
68
+ try {
69
+ const parsed: unknown = JSON.parse(candidate);
70
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
71
+ return parsed as Record<string, unknown>;
72
+ }
73
+ } catch {
74
+ // not JSON in this shape — try the next candidate
75
+ }
76
+ }
77
+ return undefined;
78
+ }
79
+
80
+ /** Path builders bound to a mount prefix. */
81
+ export interface BackofficePaths {
82
+ /** The mount prefix these builders are bound to (no trailing slash). */
83
+ base: string;
84
+ home(): string;
85
+ workspace(workspaceName: string): string;
86
+ list(doctype: string): string;
87
+ view(doctype: string, view: BackofficeView): string;
88
+ /**
89
+ * New-document route. Optional `defaults` become the create form's initial
90
+ * field values, carried as `?d=<urlencoded JSON object>` — desk parity for
91
+ * creating from context (kanban column "+", calendar day click, …).
92
+ */
93
+ newDoc(doctype: string, defaults?: Record<string, unknown>): string;
94
+ form(doctype: string, name: string): string;
95
+ }
96
+
97
+ export function createBackofficePaths(basePath: string = BACKOFFICE_BASE): BackofficePaths {
98
+ const base = basePath.replace(/\/+$/, "") || "/";
99
+ const prefix = base === "/" ? "" : base;
100
+ return {
101
+ base,
102
+ home: () => base,
103
+ workspace: (workspaceName) => `${prefix}/${toSlug(workspaceName)}`,
104
+ list: (doctype) => `${prefix}/${toSlug(doctype)}`,
105
+ view: (doctype, view) =>
106
+ view === "list"
107
+ ? `${prefix}/${toSlug(doctype)}`
108
+ : `${prefix}/${toSlug(doctype)}/view/${view}`,
109
+ newDoc: (doctype, defaults) => {
110
+ const path = `${prefix}/${toSlug(doctype)}/new`;
111
+ if (!defaults || Object.keys(defaults).length === 0) return path;
112
+ return `${path}?d=${encodeURIComponent(JSON.stringify(defaults))}`;
113
+ },
114
+ form: (doctype, name) => `${prefix}/${toSlug(doctype)}/${encodeURIComponent(name)}`,
115
+ };
116
+ }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // Default-bound helpers ("/backoffice" prefix — one-router typed-route compat)
120
+ // ---------------------------------------------------------------------------
121
+
122
+ const defaultPaths = createBackofficePaths(BACKOFFICE_BASE);
123
+
124
+ export function homePath(): BackofficePath {
125
+ return defaultPaths.home() as BackofficePath;
126
+ }
127
+
128
+ export function workspacePath(workspaceName: string): BackofficeSubPath {
129
+ return defaultPaths.workspace(workspaceName) as BackofficeSubPath;
130
+ }
131
+
132
+ export function listPath(doctype: string): BackofficeSubPath {
133
+ return defaultPaths.list(doctype) as BackofficeSubPath;
134
+ }
135
+
136
+ export function viewPath(doctype: string, view: BackofficeView): BackofficeSubPath {
137
+ return defaultPaths.view(doctype, view) as BackofficeSubPath;
138
+ }
139
+
140
+ export function newDocPath(doctype: string, defaults?: Record<string, unknown>): BackofficeSubPath {
141
+ return defaultPaths.newDoc(doctype, defaults) as BackofficeSubPath;
142
+ }
143
+
144
+ export function formPath(doctype: string, name: string): BackofficeSubPath {
145
+ return defaultPaths.form(doctype, name) as BackofficeSubPath;
146
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * One-router adapter — subpath export `@multiplatform.one/backoffice/one-adapter`.
3
+ *
4
+ * Lives on its own export path so the MAIN entry stays router-free: `one` is
5
+ * an optional peer, only resolved by hosts that import this adapter (same
6
+ * pattern as `@multiplatform.one/router`'s `./seam` subpath).
7
+ *
8
+ * Usage (a One app's backoffice layout):
9
+ *
10
+ * const navigation = useOneNavigator();
11
+ * return (
12
+ * <BackofficeProvider frappe={{ baseURL }} navigation={navigation}>
13
+ * <BackofficeShell><Slot /></BackofficeShell>
14
+ * </BackofficeProvider>
15
+ * );
16
+ */
17
+ import { useLocalSearchParams, usePathname, useRouter } from "one";
18
+ import { useEffect, useLayoutEffect, useRef, useState } from "react";
19
+ import {
20
+ ControlledNavigator,
21
+ type BackofficeNavigator,
22
+ type BackofficeParams,
23
+ } from "../navigation/navigator";
24
+
25
+ // SSR-safe layout effect: One renders routes server-side, where
26
+ // useLayoutEffect warns; the location mirror runs post-render either way.
27
+ const useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect : useLayoutEffect;
28
+
29
+ /**
30
+ * A {@link BackofficeNavigator} backed by One's router. Referentially stable
31
+ * across renders; the current pathname + search params are mirrored into the
32
+ * navigator's subscribable location after every route change.
33
+ */
34
+ export function useOneNavigator(): BackofficeNavigator {
35
+ const router = useRouter();
36
+ const pathname = usePathname();
37
+ const params = useLocalSearchParams() as BackofficeParams;
38
+
39
+ // One's router object is referentially stable, but route through a ref so
40
+ // even a host swapping router implementations stays wired.
41
+ const routerRef = useRef(router);
42
+ routerRef.current = router;
43
+
44
+ const [navigator] = useState(
45
+ () =>
46
+ new ControlledNavigator(
47
+ {
48
+ navigate: (path) => routerRef.current.push(path as never),
49
+ replace: (path) => routerRef.current.replace(path as never),
50
+ back: () => routerRef.current.back(),
51
+ },
52
+ { path: pathname, params },
53
+ ),
54
+ );
55
+
56
+ useIsomorphicLayoutEffect(() => {
57
+ navigator.setLocation({ path: pathname, params });
58
+ }, [navigator, pathname, params]);
59
+
60
+ return navigator;
61
+ }
@@ -0,0 +1,39 @@
1
+ import { FrappeApp, type AuthConfig } from "@multiplatform.one/frappe";
2
+ import { useFrappeConfig } from "@multiplatform.one/frappe-ui";
3
+ import { useMemo } from "react";
4
+
5
+ const appCache = new Map<string, FrappeApp>();
6
+
7
+ /**
8
+ * Memoized FrappeApp built from the ambient FrappeProvider config (mounted
9
+ * by BackofficeProvider). Use `app.call()` for desk RPCs, `app.db()` for
10
+ * CRUD, `app.file()` for uploads. Prefer Frappe* components /
11
+ * useFrappeCollection where possible — reach for this only when no component
12
+ * covers the API. Returns undefined outside a provider.
13
+ */
14
+ export function useFrappeApp(): FrappeApp | undefined {
15
+ const config = useFrappeConfig();
16
+ const baseURL = config?.baseURL;
17
+ // Without socketPort the realtime module dials `${baseURL}/socket.io`
18
+ // (:8000 in dev) instead of frappe's socketio server (:9000), producing a
19
+ // CORS-failed handshake on every mount.
20
+ const socketPort = config?.socketPort;
21
+ const useToken = config?.auth?.useToken;
22
+ const token = config?.auth?.token;
23
+ const type = config?.auth?.type;
24
+ return useMemo(() => {
25
+ if (!baseURL) return undefined;
26
+ const key = `${baseURL}|${socketPort ?? ""}|${useToken ? "t" : ""}|${token ?? ""}|${type ?? ""}`;
27
+ let app = appCache.get(key);
28
+ if (!app) {
29
+ app = new FrappeApp(baseURL, {
30
+ useToken,
31
+ token,
32
+ type: (type === "Bearer" ? "Bearer" : type ? "token" : undefined) as AuthConfig["type"],
33
+ realtimeOptions: socketPort ? { socketPort } : undefined,
34
+ });
35
+ appCache.set(key, app);
36
+ }
37
+ return app;
38
+ }, [baseURL, socketPort, useToken, token, type]);
39
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * Backoffice desk chrome: top navbar (app title → home, centered awesomebar,
3
+ * notifications / help / user menus) and the left workspace sidebar around
4
+ * the routed content.
5
+ *
6
+ * Router-free by design — navigation and the active path come from the
7
+ * BackofficeProvider's navigator (explicit `activePath`/`onNavigate` props
8
+ * override it for stories/tests). Capability flags from the provider gate
9
+ * every piece of chrome; `renderShellHeader`/`renderSidebarFooter` slots
10
+ * replace/extend the built-ins. Narrow viewports collapse the sidebar into
11
+ * an overlay drawer (same breakpoint as frappe-ui's DeskShell:
12
+ * DESK_NARROW_BREAKPOINT === theme NARROW_BREAKPOINT).
13
+ */
14
+ import { Button, SizableText, XStack, YStack, isWeb } from "@multiplatform.one/components";
15
+ import { useLayoutSizeClass, useResolvedKnobs } from "@multiplatform.one/theme";
16
+ import { ListIcon, XIcon } from "@phosphor-icons/react";
17
+ import type React from "react";
18
+ import { useEffect, useRef, useState } from "react";
19
+ import { useTranslation } from "react-i18next";
20
+ import {
21
+ BackofficeAwesomebar,
22
+ type BackofficeAwesomebarHandle,
23
+ } from "../chrome/BackofficeAwesomebar";
24
+ import { HelpMenu } from "../chrome/HelpMenu";
25
+ import { NotificationsButton } from "../chrome/NotificationsButton";
26
+ import { useBackofficeShortcuts } from "../chrome/shortcuts";
27
+ import { UserMenu } from "../chrome/UserMenu";
28
+ import {
29
+ useBackofficeCapabilities,
30
+ useBackofficeLocation,
31
+ useBackofficeNavigator,
32
+ useBackofficePaths,
33
+ useBackofficeSlots,
34
+ } from "../config/BackofficeProvider";
35
+ import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
36
+ import { WorkspaceSidebar } from "./WorkspaceSidebar";
37
+
38
+ export interface BackofficeShellProps {
39
+ children: React.ReactNode;
40
+ /** Current path for the sidebar active state (default: navigator location). */
41
+ activePath?: string;
42
+ /** Navigation handler (default: navigator push). */
43
+ onNavigate?: (path: string) => void;
44
+ }
45
+
46
+ /**
47
+ * Cross-platform narrow-viewport detection: theme `compact` size class =
48
+ * width < NARROW_BREAKPOINT (640) — matchMedia on web, window dimensions on
49
+ * native, so phones get the drawer instead of a permanent desktop rail.
50
+ */
51
+ function useIsNarrow(): boolean {
52
+ return useLayoutSizeClass() === "compact";
53
+ }
54
+
55
+ export function BackofficeShell({ children, activePath, onNavigate }: BackofficeShellProps) {
56
+ const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
57
+ const { knobProps } = useResolvedKnobs();
58
+ const capabilities = useBackofficeCapabilities();
59
+ const slots = useBackofficeSlots();
60
+ const navigator = useBackofficeNavigator();
61
+ const location = useBackofficeLocation();
62
+ const paths = useBackofficePaths();
63
+ const isNarrow = useIsNarrow();
64
+ const [drawerOpen, setDrawerOpen] = useState(false);
65
+
66
+ const resolvedActivePath = activePath ?? location.path;
67
+ const navigate = onNavigate ?? navigator.navigate;
68
+
69
+ const sidebarEnabled = capabilities.sidebar.enabled;
70
+
71
+ // Global desk shortcuts (Mod+K / Mod+G → awesomebar). One document
72
+ // listener for the whole chrome; SSR/native-safe, skips editable targets.
73
+ const awesomebarRef = useRef<BackofficeAwesomebarHandle>(null);
74
+ useBackofficeShortcuts({
75
+ onFocusSearch: capabilities.awesomebar.enabled
76
+ ? () => awesomebarRef.current?.focus()
77
+ : undefined,
78
+ });
79
+
80
+ // Leaving the narrow range always closes the drawer.
81
+ useEffect(() => {
82
+ if (!isNarrow) setDrawerOpen(false);
83
+ }, [isNarrow]);
84
+
85
+ // Escape dismisses the drawer (web-only keyboard listener).
86
+ useEffect(() => {
87
+ if (!isWeb || typeof document === "undefined" || !drawerOpen) return;
88
+ const onKeyDown = (event: KeyboardEvent) => {
89
+ if (event.key === "Escape") setDrawerOpen(false);
90
+ };
91
+ document.addEventListener("keydown", onKeyDown);
92
+ return () => document.removeEventListener("keydown", onKeyDown);
93
+ }, [drawerOpen]);
94
+
95
+ const handleNavigate = (path: string) => {
96
+ if (isNarrow) setDrawerOpen(false);
97
+ navigate(path);
98
+ };
99
+
100
+ const surfaceBorderWidth = knobProps.surface.borderWidth;
101
+
102
+ const sidebarContent = (
103
+ <>
104
+ <WorkspaceSidebar activePath={resolvedActivePath} onNavigate={handleNavigate} />
105
+ {slots.renderSidebarFooter?.()}
106
+ </>
107
+ );
108
+
109
+ const headerContent = (
110
+ <>
111
+ {isNarrow && sidebarEnabled && (
112
+ <Button
113
+ chromeless
114
+ circular
115
+ size="$3"
116
+ icon={drawerOpen ? XIcon : ListIcon}
117
+ aria-label={t("shell.toggleSidebar", "Toggle sidebar")}
118
+ aria-expanded={drawerOpen}
119
+ onPress={() => setDrawerOpen((open) => !open)}
120
+ data-testid="backoffice-sidebar-toggle"
121
+ />
122
+ )}
123
+ <Button
124
+ chromeless
125
+ size="$3"
126
+ paddingHorizontal="$2"
127
+ onPress={() => handleNavigate(paths.home())}
128
+ aria-label={t("shell.home", "Home")}
129
+ data-testid="backoffice-home"
130
+ >
131
+ <SizableText size="$5" fontWeight="700">
132
+ {t("shell.title", "Backoffice")}
133
+ </SizableText>
134
+ </Button>
135
+
136
+ {/* Centered awesomebar (the flex spacer keeps the right chrome pinned
137
+ when the awesomebar capability is off) */}
138
+ <XStack flex={1} minWidth={0} justifyContent="center">
139
+ {capabilities.awesomebar.enabled && (
140
+ <XStack flex={1} maxWidth={560} minWidth={0}>
141
+ <BackofficeAwesomebar focusRef={awesomebarRef} />
142
+ </XStack>
143
+ )}
144
+ </XStack>
145
+
146
+ {/* Right chrome */}
147
+ <XStack alignItems="center" gap="$1" flexShrink={0}>
148
+ {capabilities.notifications.enabled && <NotificationsButton />}
149
+ {capabilities.help.enabled && <HelpMenu />}
150
+ {capabilities.userMenu.enabled && <UserMenu />}
151
+ </XStack>
152
+ </>
153
+ );
154
+
155
+ return (
156
+ <YStack flex={1} height="100%" width="100%" overflow="hidden" data-testid="backoffice-shell">
157
+ {/* Top navbar */}
158
+ <XStack
159
+ role="banner"
160
+ {...knobProps.surface}
161
+ // Surface knobs paint a full box border; keep only the bottom edge
162
+ // (DeskShell precedent, SB-M-1708).
163
+ borderWidth={0}
164
+ borderBottomWidth={surfaceBorderWidth}
165
+ borderBottomColor={knobProps.surface.borderColor}
166
+ alignItems="center"
167
+ paddingHorizontal="$3"
168
+ paddingVertical="$2"
169
+ gap="$2"
170
+ flexShrink={0}
171
+ >
172
+ {slots.renderShellHeader
173
+ ? slots.renderShellHeader({ defaultContent: headerContent })
174
+ : headerContent}
175
+ </XStack>
176
+
177
+ <XStack flex={1} minWidth={0} position="relative" overflow="hidden">
178
+ {/* Desktop rail — in flow */}
179
+ {sidebarEnabled && !isNarrow && (
180
+ <YStack role="navigation" flexShrink={0} data-testid="backoffice-sidebar-rail">
181
+ {sidebarContent}
182
+ </YStack>
183
+ )}
184
+
185
+ {/* Narrow drawer + scrim */}
186
+ {sidebarEnabled && isNarrow && drawerOpen && (
187
+ <>
188
+ <YStack
189
+ position="absolute"
190
+ top={0}
191
+ left={0}
192
+ right={0}
193
+ bottom={0}
194
+ backgroundColor="$shadowColor"
195
+ opacity={0.5}
196
+ zIndex={10}
197
+ onPress={() => setDrawerOpen(false)}
198
+ data-testid="backoffice-sidebar-scrim"
199
+ />
200
+ <YStack
201
+ role="navigation"
202
+ position="absolute"
203
+ top={0}
204
+ left={0}
205
+ bottom={0}
206
+ zIndex={11}
207
+ backgroundColor="$background"
208
+ data-testid="backoffice-sidebar-drawer"
209
+ >
210
+ {sidebarContent}
211
+ </YStack>
212
+ </>
213
+ )}
214
+
215
+ {/* Routed content — pages own their own headers/breadcrumbs */}
216
+ <YStack role="main" flex={1} minWidth={0}>
217
+ {children}
218
+ </YStack>
219
+ </XStack>
220
+ </YStack>
221
+ );
222
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Left workspace navigation for the backoffice shell (desk parity).
3
+ *
4
+ * Private workspaces (for_user = session user) on top, public workspaces
5
+ * below ordered by sequence_id, children nested via parent_page (rendered
6
+ * indented under their parent). Data is live via useWorkspaces; paths come
7
+ * from the BackofficeProvider (basePath-aware).
8
+ */
9
+ import {
10
+ Sidebar,
11
+ Spinner,
12
+ XStack,
13
+ YStack,
14
+ type SidebarItem,
15
+ type SidebarSection,
16
+ } from "@multiplatform.one/components";
17
+ import { useMemo } from "react";
18
+ import { useTranslation } from "react-i18next";
19
+ import { useBackofficePaths } from "../config/BackofficeProvider";
20
+ import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
21
+ import type { BackofficePaths } from "../navigation/paths";
22
+ import { useWorkspaces, type WorkspaceDoc } from "./useWorkspaces";
23
+ import { workspaceIcon } from "./workspaceIcons";
24
+
25
+ export const WORKSPACE_SIDEBAR_WIDTH = 250;
26
+
27
+ export interface WorkspaceSidebarProps {
28
+ /** Current path, used to highlight the active workspace. */
29
+ activePath?: string;
30
+ /** Called with the target path when a workspace is selected. */
31
+ onNavigate?: (path: string) => void;
32
+ }
33
+
34
+ function workspaceItem(
35
+ workspace: WorkspaceDoc,
36
+ depth: number,
37
+ paths: BackofficePaths,
38
+ activePath: string | undefined,
39
+ onNavigate: ((path: string) => void) | undefined,
40
+ ): SidebarItem {
41
+ const path = paths.workspace(workspace.name);
42
+ const IconComponent = workspaceIcon(workspace.icon);
43
+ const icon = (
44
+ // Depth-based inset carried by the icon slot — the Sidebar row API has
45
+ // no nesting support, so children indent icon+label via this wrapper.
46
+ <XStack paddingLeft={depth > 0 ? depth * 16 : 0}>
47
+ <IconComponent size={16} />
48
+ </XStack>
49
+ );
50
+ return {
51
+ label: workspace.title || workspace.name,
52
+ value: path,
53
+ icon,
54
+ active: activePath === path,
55
+ onPress: onNavigate ? () => onNavigate(path) : undefined,
56
+ };
57
+ }
58
+
59
+ /** Flatten a workspace tree into indented sidebar items (children follow parents). */
60
+ function flattenTree(
61
+ roots: WorkspaceDoc[],
62
+ childrenByParent: Map<string, WorkspaceDoc[]>,
63
+ paths: BackofficePaths,
64
+ activePath: string | undefined,
65
+ onNavigate: ((path: string) => void) | undefined,
66
+ depth = 0,
67
+ ): SidebarItem[] {
68
+ const items: SidebarItem[] = [];
69
+ for (const workspace of roots) {
70
+ items.push(workspaceItem(workspace, depth, paths, activePath, onNavigate));
71
+ const children = childrenByParent.get(workspace.name);
72
+ if (children?.length) {
73
+ items.push(
74
+ ...flattenTree(children, childrenByParent, paths, activePath, onNavigate, depth + 1),
75
+ );
76
+ }
77
+ }
78
+ return items;
79
+ }
80
+
81
+ export function WorkspaceSidebar({ activePath, onNavigate }: WorkspaceSidebarProps) {
82
+ const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
83
+ const paths = useBackofficePaths();
84
+ const { privateWorkspaces, publicRoots, childrenByParent, isLoading } = useWorkspaces();
85
+
86
+ const sections = useMemo<SidebarSection[]>(() => {
87
+ const result: SidebarSection[] = [];
88
+ if (privateWorkspaces.length > 0) {
89
+ result.push({
90
+ title: t("sidebar.private", "Private"),
91
+ items: privateWorkspaces.map((workspace) =>
92
+ workspaceItem(workspace, 0, paths, activePath, onNavigate),
93
+ ),
94
+ });
95
+ }
96
+ if (publicRoots.length > 0) {
97
+ result.push({
98
+ title: t("sidebar.public", "Public"),
99
+ items: flattenTree(publicRoots, childrenByParent, paths, activePath, onNavigate),
100
+ });
101
+ }
102
+ return result;
103
+ }, [privateWorkspaces, publicRoots, childrenByParent, paths, activePath, onNavigate, t]);
104
+
105
+ if (isLoading && sections.length === 0) {
106
+ return (
107
+ <YStack
108
+ width={WORKSPACE_SIDEBAR_WIDTH}
109
+ alignItems="center"
110
+ paddingTop="$6"
111
+ aria-label={t("sidebar.loading", "Loading workspaces")}
112
+ >
113
+ <Spinner size="small" />
114
+ </YStack>
115
+ );
116
+ }
117
+
118
+ return <Sidebar sections={sections} width={WORKSPACE_SIDEBAR_WIDTH} />;
119
+ }