@multiplatform.one/backoffice 6.3.0 → 6.4.1

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 (338) hide show
  1. package/dist/cjs/chrome/CommandPalette.cjs +20 -9
  2. package/dist/cjs/chrome/CommandPalette.native.js +18 -10
  3. package/dist/cjs/chrome/CommandPalette.native.js.map +1 -1
  4. package/dist/cjs/i18n/en.cjs +21 -0
  5. package/dist/cjs/i18n/en.native.js +21 -0
  6. package/dist/cjs/i18n/en.native.js.map +1 -1
  7. package/dist/cjs/index.cjs +49 -1
  8. package/dist/cjs/index.native.js +48 -0
  9. package/dist/cjs/index.native.js.map +1 -1
  10. package/dist/cjs/shared/types.cjs +18 -0
  11. package/dist/cjs/shared/types.native.js +21 -0
  12. package/dist/cjs/shared/types.native.js.map +1 -0
  13. package/dist/cjs/shared/useDocinfo.cjs +116 -0
  14. package/dist/cjs/shared/useDocinfo.native.js +167 -0
  15. package/dist/cjs/shared/useDocinfo.native.js.map +1 -0
  16. package/dist/cjs/shared/useDoctypeMeta.cjs +124 -0
  17. package/dist/cjs/shared/useDoctypeMeta.native.js +158 -0
  18. package/dist/cjs/shared/useDoctypeMeta.native.js.map +1 -0
  19. package/dist/cjs/shared/useRecentVisit.cjs +40 -0
  20. package/dist/cjs/shared/useRecentVisit.native.js +43 -0
  21. package/dist/cjs/shared/useRecentVisit.native.js.map +1 -0
  22. package/dist/cjs/shared/useSessionUserId.cjs +54 -0
  23. package/dist/cjs/shared/useSessionUserId.native.js +59 -0
  24. package/dist/cjs/shared/useSessionUserId.native.js.map +1 -0
  25. package/dist/cjs/shared/useSlugTarget.cjs +138 -0
  26. package/dist/cjs/shared/useSlugTarget.native.js +166 -0
  27. package/dist/cjs/shared/useSlugTarget.native.js.map +1 -0
  28. package/dist/cjs/shared/useUserRoles.cjs +72 -0
  29. package/dist/cjs/shared/useUserRoles.native.js +79 -0
  30. package/dist/cjs/shared/useUserRoles.native.js.map +1 -0
  31. package/dist/cjs/shell/WorkspaceSidebar.cjs +4 -0
  32. package/dist/cjs/shell/WorkspaceSidebar.native.js +4 -0
  33. package/dist/cjs/shell/WorkspaceSidebar.native.js.map +1 -1
  34. package/dist/cjs/workspace/WorkspaceChartBlock.cjs +136 -0
  35. package/dist/cjs/workspace/WorkspaceChartBlock.native.js +144 -0
  36. package/dist/cjs/workspace/WorkspaceChartBlock.native.js.map +1 -0
  37. package/dist/cjs/workspace/WorkspaceGrid.cjs +91 -0
  38. package/dist/cjs/workspace/WorkspaceGrid.native.js +126 -0
  39. package/dist/cjs/workspace/WorkspaceGrid.native.js.map +1 -0
  40. package/dist/cjs/workspace/WorkspaceLinkCard.cjs +139 -0
  41. package/dist/cjs/workspace/WorkspaceLinkCard.native.js +155 -0
  42. package/dist/cjs/workspace/WorkspaceLinkCard.native.js.map +1 -0
  43. package/dist/cjs/workspace/WorkspaceLinkDirectory.cjs +110 -0
  44. package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js +127 -0
  45. package/dist/cjs/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
  46. package/dist/cjs/workspace/WorkspaceNumberCard.cjs +133 -0
  47. package/dist/cjs/workspace/WorkspaceNumberCard.native.js +138 -0
  48. package/dist/cjs/workspace/WorkspaceNumberCard.native.js.map +1 -0
  49. package/dist/cjs/workspace/WorkspaceQuickList.cjs +112 -0
  50. package/dist/cjs/workspace/WorkspaceQuickList.native.js +130 -0
  51. package/dist/cjs/workspace/WorkspaceQuickList.native.js.map +1 -0
  52. package/dist/cjs/workspace/WorkspaceRecents.cjs +116 -0
  53. package/dist/cjs/workspace/WorkspaceRecents.native.js +132 -0
  54. package/dist/cjs/workspace/WorkspaceRecents.native.js.map +1 -0
  55. package/dist/cjs/workspace/WorkspaceScreen.cjs +291 -0
  56. package/dist/cjs/workspace/WorkspaceScreen.native.js +323 -0
  57. package/dist/cjs/workspace/WorkspaceScreen.native.js.map +1 -0
  58. package/dist/cjs/workspace/WorkspaceShortcut.cjs +185 -0
  59. package/dist/cjs/workspace/WorkspaceShortcut.native.js +202 -0
  60. package/dist/cjs/workspace/WorkspaceShortcut.native.js.map +1 -0
  61. package/dist/cjs/workspace/parseContent.cjs +180 -0
  62. package/dist/cjs/workspace/parseContent.native.js +299 -0
  63. package/dist/cjs/workspace/parseContent.native.js.map +1 -0
  64. package/dist/cjs/workspace/types.cjs +18 -0
  65. package/dist/cjs/workspace/types.native.js +21 -0
  66. package/dist/cjs/workspace/types.native.js.map +1 -0
  67. package/dist/cjs/workspace/useDashboardChart.cjs +96 -0
  68. package/dist/cjs/workspace/useDashboardChart.native.js +110 -0
  69. package/dist/cjs/workspace/useDashboardChart.native.js.map +1 -0
  70. package/dist/cjs/workspace/useWorkspaceDoc.cjs +103 -0
  71. package/dist/cjs/workspace/useWorkspaceDoc.native.js +120 -0
  72. package/dist/cjs/workspace/useWorkspaceDoc.native.js.map +1 -0
  73. package/dist/esm/chrome/CommandPalette.mjs +20 -9
  74. package/dist/esm/chrome/CommandPalette.mjs.map +1 -1
  75. package/dist/esm/chrome/CommandPalette.native.js +18 -10
  76. package/dist/esm/chrome/CommandPalette.native.js.map +1 -1
  77. package/dist/esm/i18n/en.mjs +21 -0
  78. package/dist/esm/i18n/en.mjs.map +1 -1
  79. package/dist/esm/i18n/en.native.js +21 -0
  80. package/dist/esm/i18n/en.native.js.map +1 -1
  81. package/dist/esm/index.mjs +19 -1
  82. package/dist/esm/index.mjs.map +1 -1
  83. package/dist/esm/index.native.js +19 -1
  84. package/dist/esm/index.native.js.map +1 -1
  85. package/dist/esm/shared/types.mjs +2 -0
  86. package/dist/esm/shared/types.mjs.map +1 -0
  87. package/dist/esm/shared/types.native.js +2 -0
  88. package/dist/esm/shared/types.native.js.map +1 -0
  89. package/dist/esm/shared/useDocinfo.mjs +90 -0
  90. package/dist/esm/shared/useDocinfo.mjs.map +1 -0
  91. package/dist/esm/shared/useDocinfo.native.js +138 -0
  92. package/dist/esm/shared/useDocinfo.native.js.map +1 -0
  93. package/dist/esm/shared/useDoctypeMeta.mjs +99 -0
  94. package/dist/esm/shared/useDoctypeMeta.mjs.map +1 -0
  95. package/dist/esm/shared/useDoctypeMeta.native.js +130 -0
  96. package/dist/esm/shared/useDoctypeMeta.native.js.map +1 -0
  97. package/dist/esm/shared/useRecentVisit.mjs +15 -0
  98. package/dist/esm/shared/useRecentVisit.mjs.map +1 -0
  99. package/dist/esm/shared/useRecentVisit.native.js +15 -0
  100. package/dist/esm/shared/useRecentVisit.native.js.map +1 -0
  101. package/dist/esm/shared/useSessionUserId.mjs +29 -0
  102. package/dist/esm/shared/useSessionUserId.mjs.map +1 -0
  103. package/dist/esm/shared/useSessionUserId.native.js +31 -0
  104. package/dist/esm/shared/useSessionUserId.native.js.map +1 -0
  105. package/dist/esm/shared/useSlugTarget.mjs +113 -0
  106. package/dist/esm/shared/useSlugTarget.mjs.map +1 -0
  107. package/dist/esm/shared/useSlugTarget.native.js +138 -0
  108. package/dist/esm/shared/useSlugTarget.native.js.map +1 -0
  109. package/dist/esm/shared/useUserRoles.mjs +47 -0
  110. package/dist/esm/shared/useUserRoles.mjs.map +1 -0
  111. package/dist/esm/shared/useUserRoles.native.js +51 -0
  112. package/dist/esm/shared/useUserRoles.native.js.map +1 -0
  113. package/dist/esm/shell/WorkspaceSidebar.mjs +4 -0
  114. package/dist/esm/shell/WorkspaceSidebar.mjs.map +1 -1
  115. package/dist/esm/shell/WorkspaceSidebar.native.js +4 -0
  116. package/dist/esm/shell/WorkspaceSidebar.native.js.map +1 -1
  117. package/dist/esm/workspace/WorkspaceChartBlock.mjs +111 -0
  118. package/dist/esm/workspace/WorkspaceChartBlock.mjs.map +1 -0
  119. package/dist/esm/workspace/WorkspaceChartBlock.native.js +116 -0
  120. package/dist/esm/workspace/WorkspaceChartBlock.native.js.map +1 -0
  121. package/dist/esm/workspace/WorkspaceGrid.mjs +66 -0
  122. package/dist/esm/workspace/WorkspaceGrid.mjs.map +1 -0
  123. package/dist/esm/workspace/WorkspaceGrid.native.js +98 -0
  124. package/dist/esm/workspace/WorkspaceGrid.native.js.map +1 -0
  125. package/dist/esm/workspace/WorkspaceLinkCard.mjs +113 -0
  126. package/dist/esm/workspace/WorkspaceLinkCard.mjs.map +1 -0
  127. package/dist/esm/workspace/WorkspaceLinkCard.native.js +126 -0
  128. package/dist/esm/workspace/WorkspaceLinkCard.native.js.map +1 -0
  129. package/dist/esm/workspace/WorkspaceLinkDirectory.mjs +84 -0
  130. package/dist/esm/workspace/WorkspaceLinkDirectory.mjs.map +1 -0
  131. package/dist/esm/workspace/WorkspaceLinkDirectory.native.js +98 -0
  132. package/dist/esm/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
  133. package/dist/esm/workspace/WorkspaceNumberCard.mjs +108 -0
  134. package/dist/esm/workspace/WorkspaceNumberCard.mjs.map +1 -0
  135. package/dist/esm/workspace/WorkspaceNumberCard.native.js +110 -0
  136. package/dist/esm/workspace/WorkspaceNumberCard.native.js.map +1 -0
  137. package/dist/esm/workspace/WorkspaceQuickList.mjs +87 -0
  138. package/dist/esm/workspace/WorkspaceQuickList.mjs.map +1 -0
  139. package/dist/esm/workspace/WorkspaceQuickList.native.js +102 -0
  140. package/dist/esm/workspace/WorkspaceQuickList.native.js.map +1 -0
  141. package/dist/esm/workspace/WorkspaceRecents.mjs +91 -0
  142. package/dist/esm/workspace/WorkspaceRecents.mjs.map +1 -0
  143. package/dist/esm/workspace/WorkspaceRecents.native.js +104 -0
  144. package/dist/esm/workspace/WorkspaceRecents.native.js.map +1 -0
  145. package/dist/esm/workspace/WorkspaceScreen.mjs +266 -0
  146. package/dist/esm/workspace/WorkspaceScreen.mjs.map +1 -0
  147. package/dist/esm/workspace/WorkspaceScreen.native.js +295 -0
  148. package/dist/esm/workspace/WorkspaceScreen.native.js.map +1 -0
  149. package/dist/esm/workspace/WorkspaceShortcut.mjs +159 -0
  150. package/dist/esm/workspace/WorkspaceShortcut.mjs.map +1 -0
  151. package/dist/esm/workspace/WorkspaceShortcut.native.js +173 -0
  152. package/dist/esm/workspace/WorkspaceShortcut.native.js.map +1 -0
  153. package/dist/esm/workspace/parseContent.mjs +148 -0
  154. package/dist/esm/workspace/parseContent.mjs.map +1 -0
  155. package/dist/esm/workspace/parseContent.native.js +264 -0
  156. package/dist/esm/workspace/parseContent.native.js.map +1 -0
  157. package/dist/esm/workspace/types.mjs +2 -0
  158. package/dist/esm/workspace/types.mjs.map +1 -0
  159. package/dist/esm/workspace/types.native.js +2 -0
  160. package/dist/esm/workspace/types.native.js.map +1 -0
  161. package/dist/esm/workspace/useDashboardChart.mjs +71 -0
  162. package/dist/esm/workspace/useDashboardChart.mjs.map +1 -0
  163. package/dist/esm/workspace/useDashboardChart.native.js +82 -0
  164. package/dist/esm/workspace/useDashboardChart.native.js.map +1 -0
  165. package/dist/esm/workspace/useWorkspaceDoc.mjs +77 -0
  166. package/dist/esm/workspace/useWorkspaceDoc.mjs.map +1 -0
  167. package/dist/esm/workspace/useWorkspaceDoc.native.js +91 -0
  168. package/dist/esm/workspace/useWorkspaceDoc.native.js.map +1 -0
  169. package/dist/jsx/chrome/CommandPalette.mjs +20 -9
  170. package/dist/jsx/chrome/CommandPalette.mjs.map +1 -1
  171. package/dist/jsx/chrome/CommandPalette.native.js +18 -10
  172. package/dist/jsx/chrome/CommandPalette.native.js.map +1 -1
  173. package/dist/jsx/i18n/en.mjs +21 -0
  174. package/dist/jsx/i18n/en.mjs.map +1 -1
  175. package/dist/jsx/i18n/en.native.js +21 -0
  176. package/dist/jsx/i18n/en.native.js.map +1 -1
  177. package/dist/jsx/index.js +19 -1
  178. package/dist/jsx/index.js.map +1 -1
  179. package/dist/jsx/index.mjs +19 -1
  180. package/dist/jsx/index.mjs.map +1 -1
  181. package/dist/jsx/index.native.js +48 -0
  182. package/dist/jsx/index.native.js.map +1 -1
  183. package/dist/jsx/shared/types.mjs +2 -0
  184. package/dist/jsx/shared/types.mjs.map +1 -0
  185. package/dist/jsx/shared/types.native.js +21 -0
  186. package/dist/jsx/shared/types.native.js.map +1 -0
  187. package/dist/jsx/shared/useDocinfo.mjs +90 -0
  188. package/dist/jsx/shared/useDocinfo.mjs.map +1 -0
  189. package/dist/jsx/shared/useDocinfo.native.js +167 -0
  190. package/dist/jsx/shared/useDocinfo.native.js.map +1 -0
  191. package/dist/jsx/shared/useDoctypeMeta.mjs +99 -0
  192. package/dist/jsx/shared/useDoctypeMeta.mjs.map +1 -0
  193. package/dist/jsx/shared/useDoctypeMeta.native.js +158 -0
  194. package/dist/jsx/shared/useDoctypeMeta.native.js.map +1 -0
  195. package/dist/jsx/shared/useRecentVisit.mjs +15 -0
  196. package/dist/jsx/shared/useRecentVisit.mjs.map +1 -0
  197. package/dist/jsx/shared/useRecentVisit.native.js +43 -0
  198. package/dist/jsx/shared/useRecentVisit.native.js.map +1 -0
  199. package/dist/jsx/shared/useSessionUserId.mjs +29 -0
  200. package/dist/jsx/shared/useSessionUserId.mjs.map +1 -0
  201. package/dist/jsx/shared/useSessionUserId.native.js +59 -0
  202. package/dist/jsx/shared/useSessionUserId.native.js.map +1 -0
  203. package/dist/jsx/shared/useSlugTarget.mjs +113 -0
  204. package/dist/jsx/shared/useSlugTarget.mjs.map +1 -0
  205. package/dist/jsx/shared/useSlugTarget.native.js +166 -0
  206. package/dist/jsx/shared/useSlugTarget.native.js.map +1 -0
  207. package/dist/jsx/shared/useUserRoles.mjs +47 -0
  208. package/dist/jsx/shared/useUserRoles.mjs.map +1 -0
  209. package/dist/jsx/shared/useUserRoles.native.js +79 -0
  210. package/dist/jsx/shared/useUserRoles.native.js.map +1 -0
  211. package/dist/jsx/shell/WorkspaceSidebar.mjs +4 -0
  212. package/dist/jsx/shell/WorkspaceSidebar.mjs.map +1 -1
  213. package/dist/jsx/shell/WorkspaceSidebar.native.js +4 -0
  214. package/dist/jsx/shell/WorkspaceSidebar.native.js.map +1 -1
  215. package/dist/jsx/workspace/WorkspaceChartBlock.mjs +111 -0
  216. package/dist/jsx/workspace/WorkspaceChartBlock.mjs.map +1 -0
  217. package/dist/jsx/workspace/WorkspaceChartBlock.native.js +144 -0
  218. package/dist/jsx/workspace/WorkspaceChartBlock.native.js.map +1 -0
  219. package/dist/jsx/workspace/WorkspaceGrid.mjs +66 -0
  220. package/dist/jsx/workspace/WorkspaceGrid.mjs.map +1 -0
  221. package/dist/jsx/workspace/WorkspaceGrid.native.js +126 -0
  222. package/dist/jsx/workspace/WorkspaceGrid.native.js.map +1 -0
  223. package/dist/jsx/workspace/WorkspaceLinkCard.mjs +113 -0
  224. package/dist/jsx/workspace/WorkspaceLinkCard.mjs.map +1 -0
  225. package/dist/jsx/workspace/WorkspaceLinkCard.native.js +155 -0
  226. package/dist/jsx/workspace/WorkspaceLinkCard.native.js.map +1 -0
  227. package/dist/jsx/workspace/WorkspaceLinkDirectory.mjs +84 -0
  228. package/dist/jsx/workspace/WorkspaceLinkDirectory.mjs.map +1 -0
  229. package/dist/jsx/workspace/WorkspaceLinkDirectory.native.js +127 -0
  230. package/dist/jsx/workspace/WorkspaceLinkDirectory.native.js.map +1 -0
  231. package/dist/jsx/workspace/WorkspaceNumberCard.mjs +108 -0
  232. package/dist/jsx/workspace/WorkspaceNumberCard.mjs.map +1 -0
  233. package/dist/jsx/workspace/WorkspaceNumberCard.native.js +138 -0
  234. package/dist/jsx/workspace/WorkspaceNumberCard.native.js.map +1 -0
  235. package/dist/jsx/workspace/WorkspaceQuickList.mjs +87 -0
  236. package/dist/jsx/workspace/WorkspaceQuickList.mjs.map +1 -0
  237. package/dist/jsx/workspace/WorkspaceQuickList.native.js +130 -0
  238. package/dist/jsx/workspace/WorkspaceQuickList.native.js.map +1 -0
  239. package/dist/jsx/workspace/WorkspaceRecents.mjs +91 -0
  240. package/dist/jsx/workspace/WorkspaceRecents.mjs.map +1 -0
  241. package/dist/jsx/workspace/WorkspaceRecents.native.js +132 -0
  242. package/dist/jsx/workspace/WorkspaceRecents.native.js.map +1 -0
  243. package/dist/jsx/workspace/WorkspaceScreen.mjs +266 -0
  244. package/dist/jsx/workspace/WorkspaceScreen.mjs.map +1 -0
  245. package/dist/jsx/workspace/WorkspaceScreen.native.js +323 -0
  246. package/dist/jsx/workspace/WorkspaceScreen.native.js.map +1 -0
  247. package/dist/jsx/workspace/WorkspaceShortcut.mjs +159 -0
  248. package/dist/jsx/workspace/WorkspaceShortcut.mjs.map +1 -0
  249. package/dist/jsx/workspace/WorkspaceShortcut.native.js +202 -0
  250. package/dist/jsx/workspace/WorkspaceShortcut.native.js.map +1 -0
  251. package/dist/jsx/workspace/parseContent.mjs +148 -0
  252. package/dist/jsx/workspace/parseContent.mjs.map +1 -0
  253. package/dist/jsx/workspace/parseContent.native.js +299 -0
  254. package/dist/jsx/workspace/parseContent.native.js.map +1 -0
  255. package/dist/jsx/workspace/types.mjs +2 -0
  256. package/dist/jsx/workspace/types.mjs.map +1 -0
  257. package/dist/jsx/workspace/types.native.js +21 -0
  258. package/dist/jsx/workspace/types.native.js.map +1 -0
  259. package/dist/jsx/workspace/useDashboardChart.mjs +71 -0
  260. package/dist/jsx/workspace/useDashboardChart.mjs.map +1 -0
  261. package/dist/jsx/workspace/useDashboardChart.native.js +110 -0
  262. package/dist/jsx/workspace/useDashboardChart.native.js.map +1 -0
  263. package/dist/jsx/workspace/useWorkspaceDoc.mjs +77 -0
  264. package/dist/jsx/workspace/useWorkspaceDoc.mjs.map +1 -0
  265. package/dist/jsx/workspace/useWorkspaceDoc.native.js +120 -0
  266. package/dist/jsx/workspace/useWorkspaceDoc.native.js.map +1 -0
  267. package/package.json +12 -12
  268. package/src/chrome/CommandPalette.tsx +35 -15
  269. package/src/i18n/en.ts +21 -0
  270. package/src/index.ts +69 -0
  271. package/src/shared/types.ts +14 -0
  272. package/src/shared/useDocinfo.ts +163 -0
  273. package/src/shared/useDoctypeMeta.ts +243 -0
  274. package/src/shared/useRecentVisit.ts +33 -0
  275. package/src/shared/useSessionUserId.ts +42 -0
  276. package/src/shared/useSlugTarget.ts +143 -0
  277. package/src/shared/useUserRoles.ts +66 -0
  278. package/src/shell/WorkspaceSidebar.tsx +3 -0
  279. package/src/workspace/WorkspaceChartBlock.tsx +115 -0
  280. package/src/workspace/WorkspaceGrid.tsx +82 -0
  281. package/src/workspace/WorkspaceLinkCard.tsx +134 -0
  282. package/src/workspace/WorkspaceLinkDirectory.tsx +126 -0
  283. package/src/workspace/WorkspaceNumberCard.tsx +126 -0
  284. package/src/workspace/WorkspaceQuickList.tsx +100 -0
  285. package/src/workspace/WorkspaceRecents.tsx +100 -0
  286. package/src/workspace/WorkspaceScreen.tsx +322 -0
  287. package/src/workspace/WorkspaceShortcut.tsx +223 -0
  288. package/src/workspace/parseContent.spec.ts +327 -0
  289. package/src/workspace/parseContent.ts +240 -0
  290. package/src/workspace/types.ts +135 -0
  291. package/src/workspace/useDashboardChart.ts +115 -0
  292. package/src/workspace/useWorkspaceDoc.ts +111 -0
  293. package/types/chrome/CommandPalette.d.ts.map +1 -1
  294. package/types/i18n/en.d.ts +19 -0
  295. package/types/i18n/en.d.ts.map +1 -1
  296. package/types/index.d.ts +34 -0
  297. package/types/index.d.ts.map +1 -1
  298. package/types/shared/types.d.ts +21 -0
  299. package/types/shared/types.d.ts.map +1 -0
  300. package/types/shared/useDocinfo.d.ts +61 -0
  301. package/types/shared/useDocinfo.d.ts.map +1 -0
  302. package/types/shared/useDoctypeMeta.d.ts +118 -0
  303. package/types/shared/useDoctypeMeta.d.ts.map +1 -0
  304. package/types/shared/useRecentVisit.d.ts +10 -0
  305. package/types/shared/useRecentVisit.d.ts.map +1 -0
  306. package/types/shared/useSessionUserId.d.ts +6 -0
  307. package/types/shared/useSessionUserId.d.ts.map +1 -0
  308. package/types/shared/useSlugTarget.d.ts +18 -0
  309. package/types/shared/useSlugTarget.d.ts.map +1 -0
  310. package/types/shared/useUserRoles.d.ts +15 -0
  311. package/types/shared/useUserRoles.d.ts.map +1 -0
  312. package/types/shell/WorkspaceSidebar.d.ts.map +1 -1
  313. package/types/workspace/WorkspaceChartBlock.d.ts +6 -0
  314. package/types/workspace/WorkspaceChartBlock.d.ts.map +1 -0
  315. package/types/workspace/WorkspaceGrid.d.ts +23 -0
  316. package/types/workspace/WorkspaceGrid.d.ts.map +1 -0
  317. package/types/workspace/WorkspaceLinkCard.d.ts +20 -0
  318. package/types/workspace/WorkspaceLinkCard.d.ts.map +1 -0
  319. package/types/workspace/WorkspaceLinkDirectory.d.ts +19 -0
  320. package/types/workspace/WorkspaceLinkDirectory.d.ts.map +1 -0
  321. package/types/workspace/WorkspaceNumberCard.d.ts +19 -0
  322. package/types/workspace/WorkspaceNumberCard.d.ts.map +1 -0
  323. package/types/workspace/WorkspaceQuickList.d.ts +6 -0
  324. package/types/workspace/WorkspaceQuickList.d.ts.map +1 -0
  325. package/types/workspace/WorkspaceRecents.d.ts +2 -0
  326. package/types/workspace/WorkspaceRecents.d.ts.map +1 -0
  327. package/types/workspace/WorkspaceScreen.d.ts +24 -0
  328. package/types/workspace/WorkspaceScreen.d.ts.map +1 -0
  329. package/types/workspace/WorkspaceShortcut.d.ts +27 -0
  330. package/types/workspace/WorkspaceShortcut.d.ts.map +1 -0
  331. package/types/workspace/parseContent.d.ts +77 -0
  332. package/types/workspace/parseContent.d.ts.map +1 -0
  333. package/types/workspace/types.d.ts +112 -0
  334. package/types/workspace/types.d.ts.map +1 -0
  335. package/types/workspace/useDashboardChart.d.ts +29 -0
  336. package/types/workspace/useDashboardChart.d.ts.map +1 -0
  337. package/types/workspace/useWorkspaceDoc.d.ts +18 -0
  338. package/types/workspace/useWorkspaceDoc.d.ts.map +1 -0
@@ -0,0 +1,143 @@
1
+ // Contract: resolve a {basePath}/[slug] segment to a workspace or doctype.
2
+ // Every backoffice route relies on this hook; keep the signature stable.
3
+ import { HttpClient, type AuthConfig, type FixtureProvider } from "@multiplatform.one/frappe";
4
+ import { useFrappeConfig, type FrappeConfig } from "@multiplatform.one/frappe-ui";
5
+ import { useEffect, useMemo, useState } from "react";
6
+ import { slugToDisplayName, toSlug } from "../navigation/paths";
7
+ import type { SlugTarget } from "./types";
8
+
9
+ /**
10
+ * Slug → proper-cased-name indexes, fetched once per session.
11
+ *
12
+ * Workspaces win over doctypes on collision (desk parity: `/app/tools` is the
13
+ * Tools workspace even though a "Tools" doctype could exist).
14
+ */
15
+ interface SlugIndex {
16
+ workspaces: Map<string, string>;
17
+ doctypes: Map<string, string>;
18
+ }
19
+
20
+ interface SlugIndexSource {
21
+ baseURL: string;
22
+ auth?: FrappeConfig["auth"];
23
+ fixtures?: FixtureProvider;
24
+ }
25
+
26
+ /** Module-level cache: one fetch per (baseURL, fixture-mode) per session. */
27
+ const slugIndexCache = new Map<string, Promise<SlugIndex>>();
28
+
29
+ function toSlugMap(names: readonly { name?: string }[]): Map<string, string> {
30
+ const map = new Map<string, string>();
31
+ for (const row of names) {
32
+ if (row.name) map.set(toSlug(row.name), row.name);
33
+ }
34
+ return map;
35
+ }
36
+
37
+ async function fetchSlugIndex(source: SlugIndexSource): Promise<SlugIndex> {
38
+ if (source.fixtures) {
39
+ const [workspaces, doctypes] = await Promise.all([
40
+ source.fixtures.getList("Workspace", { fields: ["name"] }),
41
+ source.fixtures.getList("DocType", { fields: ["name"] }),
42
+ ]);
43
+ return {
44
+ workspaces: toSlugMap(workspaces as { name?: string }[]),
45
+ doctypes: toSlugMap(doctypes as { name?: string }[]),
46
+ };
47
+ }
48
+ const client = new HttpClient(source.baseURL, source.auth as AuthConfig | undefined);
49
+ const [workspacesRes, doctypesRes] = await Promise.all([
50
+ client.get<{ data?: { name: string }[] }>("/api/resource/Workspace", {
51
+ fields: '["name"]',
52
+ limit_page_length: "0",
53
+ }),
54
+ client.get<{ message?: { name: string }[] }>("/api/method/frappe.client.get_list", {
55
+ doctype: "DocType",
56
+ fields: '["name"]',
57
+ limit_page_length: "0",
58
+ }),
59
+ ]);
60
+ return {
61
+ workspaces: toSlugMap(workspacesRes.payload?.data ?? []),
62
+ doctypes: toSlugMap(doctypesRes.payload?.message ?? []),
63
+ };
64
+ }
65
+
66
+ function getSlugIndex(source: SlugIndexSource): Promise<SlugIndex> {
67
+ const key = `${source.baseURL}:${source.fixtures ? "fixtures" : "live"}`;
68
+ let promise = slugIndexCache.get(key);
69
+ if (!promise) {
70
+ promise = fetchSlugIndex(source);
71
+ // Failed fetches are evicted so a later mount can retry.
72
+ promise.catch(() => {
73
+ if (slugIndexCache.get(key) === promise) slugIndexCache.delete(key);
74
+ });
75
+ slugIndexCache.set(key, promise);
76
+ }
77
+ return promise;
78
+ }
79
+
80
+ /**
81
+ * Resolve a URL slug ("sales-order") to its target:
82
+ * - a Workspace whose slugged name matches → workspace page (workspaces win)
83
+ * - otherwise a DocType whose slugged name matches → doctype list
84
+ * - otherwise not-found
85
+ *
86
+ * The Workspace + DocType name indexes are fetched once per session (module
87
+ * cache) via the frappe client, honoring FrappeProvider fixtures in stories
88
+ * and tests. Connection comes exclusively from the ambient FrappeProvider
89
+ * (mounted by BackofficeProvider); without one the hook degrades the same
90
+ * way a failed fetch does. SSR-safe: the fetch runs in an effect, so the
91
+ * server render (and first client paint) reports `loading`. If the fetch
92
+ * fails the hook degrades gracefully by treating the slug as a title-cased
93
+ * doctype.
94
+ */
95
+ export function useSlugTarget(slug: string | undefined): SlugTarget {
96
+ const config = useFrappeConfig();
97
+ const baseURL = config?.baseURL;
98
+ const fixtures = config?.fixtures;
99
+ const auth = config?.auth;
100
+
101
+ const [index, setIndex] = useState<SlugIndex | undefined>(undefined);
102
+ const [failed, setFailed] = useState(false);
103
+
104
+ useEffect(() => {
105
+ let cancelled = false;
106
+ // No provider config → no bench to ask; degrade like a failed fetch.
107
+ if (!baseURL && !fixtures) {
108
+ setFailed(true);
109
+ return;
110
+ }
111
+ setFailed(false);
112
+ getSlugIndex({ baseURL: baseURL ?? "", auth, fixtures })
113
+ .then((result) => {
114
+ if (!cancelled) setIndex(result);
115
+ })
116
+ .catch(() => {
117
+ if (!cancelled) setFailed(true);
118
+ });
119
+ return () => {
120
+ cancelled = true;
121
+ };
122
+ // `auth` is intentionally not a dependency: the cache is keyed by
123
+ // baseURL/fixtures and provider auth is stable for the session.
124
+ // oxlint-disable-next-line exhaustive-deps
125
+ }, [baseURL, fixtures]);
126
+
127
+ return useMemo<SlugTarget>(() => {
128
+ if (!slug) return { status: "loading" };
129
+ if (index) {
130
+ const workspace = index.workspaces.get(slug);
131
+ if (workspace) return { status: "resolved", kind: "workspace", workspace };
132
+ const doctype = index.doctypes.get(slug);
133
+ if (doctype) return { status: "resolved", kind: "doctype", doctype };
134
+ return { status: "not-found", slug };
135
+ }
136
+ if (failed) {
137
+ // Degrade gracefully: guess a title-cased doctype so deep links still
138
+ // land somewhere useful when the metadata fetch is unavailable.
139
+ return { status: "resolved", kind: "doctype", doctype: slugToDisplayName(slug) };
140
+ }
141
+ return { status: "loading" };
142
+ }, [slug, index, failed]);
143
+ }
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Session user's role list — the client-side half of desk's
3
+ * `frappe.user_roles` (bootinfo). Fetched once per session via the
4
+ * whitelisted `frappe.core.doctype.user.user.get_roles` RPC (probed:
5
+ * `?uid=<user>` → `{"message": ["Role", ...]}`), keyed off
6
+ * `frappe.auth.get_logged_user`.
7
+ *
8
+ * `roles` stays undefined while loading, in fixture mode, or when the RPC
9
+ * fails — consumers (permlevel matrix) fall back to the conservative
10
+ * "permlevel > 0 → read-only" rule in that case.
11
+ */
12
+
13
+ import { useFrappeConfig } from "@multiplatform.one/frappe-ui";
14
+ import { useEffect, useState } from "react";
15
+ import { useFrappeApp } from "./useFrappeApp";
16
+
17
+ const rolesCache = new Map<string, Promise<string[]>>();
18
+
19
+ export function useUserRoles(): { roles?: string[] } {
20
+ const app = useFrappeApp();
21
+ const config = useFrappeConfig();
22
+ const fixtures = config?.fixtures;
23
+ const baseURL = config?.baseURL;
24
+ const [roles, setRoles] = useState<string[] | undefined>(undefined);
25
+
26
+ useEffect(() => {
27
+ // Fixture mode has no session — permlevel falls back to the coarse rule.
28
+ if (!app || fixtures || !baseURL) {
29
+ setRoles(undefined);
30
+ return;
31
+ }
32
+ let cancelled = false;
33
+ let promise = rolesCache.get(baseURL);
34
+ if (!promise) {
35
+ promise = app
36
+ .call()
37
+ .get<{ message?: string }>("frappe.auth.get_logged_user")
38
+ .then((res) => {
39
+ const user = res.payload?.message;
40
+ if (!user) throw new Error("No session user");
41
+ return app.call().get<{ message?: string[] }>("frappe.core.doctype.user.user.get_roles", {
42
+ uid: user,
43
+ });
44
+ })
45
+ .then((res) => {
46
+ const list = res.payload?.message;
47
+ if (!Array.isArray(list)) throw new Error("No roles returned");
48
+ return list;
49
+ });
50
+ rolesCache.set(baseURL, promise);
51
+ promise.catch(() => rolesCache.delete(baseURL));
52
+ }
53
+ promise
54
+ .then((list) => {
55
+ if (!cancelled) setRoles(list);
56
+ })
57
+ .catch(() => {
58
+ if (!cancelled) setRoles(undefined);
59
+ });
60
+ return () => {
61
+ cancelled = true;
62
+ };
63
+ }, [app, fixtures, baseURL]);
64
+
65
+ return { roles };
66
+ }
@@ -99,6 +99,9 @@ export function WorkspaceSidebar({ activePath, onNavigate }: WorkspaceSidebarPro
99
99
  items: flattenTree(publicRoots, childrenByParent, paths, activePath, onNavigate),
100
100
  });
101
101
  }
102
+ // A lone group label is noise (owner-flagged): only caption sections when
103
+ // there is more than one group to distinguish.
104
+ if (result.length === 1) result[0] = { ...result[0], title: undefined };
102
105
  return result;
103
106
  }, [privateWorkspaces, publicRoots, childrenByParent, paths, activePath, onNavigate, t]);
104
107
 
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Workspace chart block — renders a Dashboard Chart by name using the desk
3
+ * chart RPC for real series data, drawn with the catalog ChartCard.
4
+ *
5
+ * Mappings (frappe → catalog): Line→line, Bar→bar, Pie/Donut/Percentage→pie.
6
+ * Heatmap has no catalog equivalent and falls back to bar (documented gap).
7
+ * Only the first dataset is drawn — the catalog chart is single-series
8
+ * (documented gap). In fixture mode (stories) the RPC is unavailable, so
9
+ * the block renders an informational card instead.
10
+ */
11
+ import { AsyncBoundary, Card, SizableText, Skeleton, YStack } from "@multiplatform.one/components";
12
+ import { ChartCard, type ChartConfig, type ChartType } from "@multiplatform.one/frappe-ui";
13
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
14
+ import { useMemo } from "react";
15
+ import { useTranslation } from "react-i18next";
16
+ import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
17
+ import type { WorkspaceChartRow } from "./types";
18
+ import { useDashboardChartData, type DashboardChartDoc } from "./useDashboardChart";
19
+ import { useFrappeDoc } from "./useWorkspaceDoc";
20
+
21
+ function toChartType(frappeType: string | undefined): ChartType {
22
+ switch (frappeType) {
23
+ case "Line":
24
+ return "line";
25
+ case "Pie":
26
+ case "Donut":
27
+ case "Percentage":
28
+ return "pie";
29
+ default:
30
+ // "Bar", "Heatmap" (no catalog heatmap yet), unknown → bar.
31
+ return "bar";
32
+ }
33
+ }
34
+
35
+ export interface WorkspaceChartBlockProps {
36
+ row: WorkspaceChartRow;
37
+ }
38
+
39
+ export function WorkspaceChartBlock({ row }: WorkspaceChartBlockProps) {
40
+ const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
41
+ const { knobProps } = useResolvedKnobs();
42
+ const title = row.label || row.chart_name;
43
+
44
+ const {
45
+ doc: chartDoc,
46
+ isLoading: docLoading,
47
+ error: docError,
48
+ } = useFrappeDoc<DashboardChartDoc>("Dashboard Chart", row.chart_name);
49
+ const {
50
+ series,
51
+ isLoading: dataLoading,
52
+ error: dataError,
53
+ unsupported,
54
+ } = useDashboardChartData(row.chart_name);
55
+
56
+ const config = useMemo<ChartConfig | undefined>(() => {
57
+ if (!series) return undefined;
58
+ const dataset = series.datasets?.[0];
59
+ if (!dataset) return undefined;
60
+ return {
61
+ id: `workspace-chart-${row.name}`,
62
+ title,
63
+ type: toChartType(chartDoc?.type),
64
+ data: series.labels.map((label, index) => ({
65
+ label,
66
+ value: dataset.values[index] ?? 0,
67
+ })),
68
+ height: 220,
69
+ };
70
+ }, [series, chartDoc?.type, row.name, title]);
71
+
72
+ if (unsupported) {
73
+ return (
74
+ <Card flexGrow={1}>
75
+ <YStack {...knobProps.panelPadding} {...knobProps.gap}>
76
+ <SizableText size="$4" fontWeight="700">
77
+ {title}
78
+ </SizableText>
79
+ <SizableText size="$2" color="$color10">
80
+ {t(
81
+ "workspaceChartFixture",
82
+ "Chart data is only available on a live bench",
83
+ )}
84
+ </SizableText>
85
+ </YStack>
86
+ </Card>
87
+ );
88
+ }
89
+
90
+ if (docLoading || dataLoading) {
91
+ return (
92
+ <Card flexGrow={1} aria-busy aria-label={t("loading", "Loading")}>
93
+ <YStack {...knobProps.panelPadding} {...knobProps.gap}>
94
+ <Skeleton width="40%" height={16} />
95
+ <Skeleton variant="rounded" width="100%" height={220} />
96
+ </YStack>
97
+ </Card>
98
+ );
99
+ }
100
+
101
+ const error = docError ?? dataError;
102
+ if (error || !config) {
103
+ return (
104
+ <AsyncBoundary
105
+ compact
106
+ error={error ?? true}
107
+ errorTitle={t("workspaceChartError", "Couldn't load chart {{title}}", {
108
+ title,
109
+ })}
110
+ />
111
+ );
112
+ }
113
+
114
+ return <ChartCard config={config} flexGrow={1} />;
115
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * 12-column workspace grid — desk parity for the editorjs block layout.
3
+ *
4
+ * Blocks carry a `col` span (3 = shortcut, 4 = card, 12 = full row).
5
+ * Consecutive blocks accumulate into rows of up to 12 columns and each row
6
+ * lays out with flex ratios, so no pixel math is needed. The container
7
+ * measures itself with `onLayout` (cross-platform — no media queries, no
8
+ * DOM) and collapses spans on narrow widths: everything full-width on
9
+ * phones, two-up on mid widths, desk's ~3–4 column layout when wide.
10
+ */
11
+ import { XStack, YStack, type YStackProps } from "@multiplatform.one/components";
12
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
13
+ import { useState, type ReactNode } from "react";
14
+
15
+ /** Container width below which every block is full-width. */
16
+ const NARROW_MAX = 640;
17
+ /** Container width below which small blocks go two-up. */
18
+ const MEDIUM_MAX = 960;
19
+
20
+ export interface WorkspaceGridItem {
21
+ key: string;
22
+ /** 12-column span requested by the block (defaults to 12). */
23
+ col?: number;
24
+ node: ReactNode;
25
+ }
26
+
27
+ function effectiveSpan(raw: number | undefined, width: number): number {
28
+ const span = Math.min(Math.max(Math.round(raw ?? 12), 1), 12);
29
+ if (width <= 0) return span; // not measured yet — desk-wide layout
30
+ if (width < NARROW_MAX) return 12;
31
+ if (width < MEDIUM_MAX) return span <= 6 ? 6 : 12;
32
+ return span;
33
+ }
34
+
35
+ export interface WorkspaceGridProps extends YStackProps {
36
+ items: WorkspaceGridItem[];
37
+ }
38
+
39
+ export function WorkspaceGrid({ items, ...stackProps }: WorkspaceGridProps) {
40
+ const { knobProps } = useResolvedKnobs();
41
+ const [width, setWidth] = useState(0);
42
+
43
+ // Chunk consecutive blocks into rows of up to 12 columns (desk behavior).
44
+ const rows: Array<Array<{ item: WorkspaceGridItem; span: number }>> = [];
45
+ let current: Array<{ item: WorkspaceGridItem; span: number }> = [];
46
+ let used = 0;
47
+ for (const item of items) {
48
+ const span = effectiveSpan(item.col, width);
49
+ if (used + span > 12 && current.length > 0) {
50
+ rows.push(current);
51
+ current = [];
52
+ used = 0;
53
+ }
54
+ current.push({ item, span });
55
+ used += span;
56
+ }
57
+ if (current.length > 0) rows.push(current);
58
+
59
+ return (
60
+ <YStack
61
+ {...knobProps.gap}
62
+ onLayout={(event) => setWidth(event.nativeEvent.layout.width)}
63
+ {...stackProps}
64
+ >
65
+ {rows.map((row, rowIndex) => {
66
+ const rowUsed = row.reduce((sum, cell) => sum + cell.span, 0);
67
+ return (
68
+ <XStack key={row[0]?.item.key ?? rowIndex} {...knobProps.gap} alignItems="stretch">
69
+ {row.map(({ item, span }) => (
70
+ <YStack key={item.key} flexGrow={span} flexShrink={1} flexBasis={0} minWidth={0}>
71
+ {item.node}
72
+ </YStack>
73
+ ))}
74
+ {/* Filler keeps a partial row's cells at their 12-col width
75
+ (a lone col-3 shortcut stays ~25%, like desk). */}
76
+ {rowUsed < 12 ? <YStack flexGrow={12 - rowUsed} flexBasis={0} /> : null}
77
+ </XStack>
78
+ );
79
+ })}
80
+ </YStack>
81
+ );
82
+ }
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Workspace link card — desk parity for the `card` content block: a titled
3
+ * card listing the Link rows that follow the matching "Card Break" row in
4
+ * the `links` child table. DocType links navigate to the doctype list;
5
+ * Report/Page links go through the slug router best-effort (see
6
+ * `resolveLinkTarget`).
7
+ *
8
+ * Rendering gates: link rows targeting doctypes outside the provider's
9
+ * `doctypes` scope are hidden, and DocType/Report rows (list-surface
10
+ * navigations) are hidden entirely when the `views` capability is off.
11
+ */
12
+ import { Card, SizableText, XStack, YStack } from "@multiplatform.one/components";
13
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
14
+ import { useTranslation } from "react-i18next";
15
+ import {
16
+ useBackofficeCapabilities,
17
+ useBackofficeNavigator,
18
+ useBackofficePaths,
19
+ useIsDoctypeAllowed,
20
+ } from "../config/BackofficeProvider";
21
+ import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
22
+ import { createBackofficePaths, type BackofficePaths } from "../navigation/paths";
23
+ import type { WorkspaceLinkGroup, WorkspaceLinkRow } from "./types";
24
+
25
+ const defaultPaths = createBackofficePaths();
26
+
27
+ /**
28
+ * Resolve a Workspace Link row to a router path (basePath-aware when the
29
+ * provider's `paths` are passed; defaults to the canonical "/backoffice").
30
+ * - DocType → paths.list(link_to)
31
+ * - Report → paths.list(link_to) fallback (no query-report route yet — the
32
+ * slug router decides; documented gap)
33
+ * - Page → `{base}/<link_to>` (page names are already slugs)
34
+ * - anything else → disabled
35
+ */
36
+ export function resolveLinkTarget(
37
+ row: WorkspaceLinkRow,
38
+ paths: BackofficePaths = defaultPaths,
39
+ ): {
40
+ path?: string;
41
+ disabled?: boolean;
42
+ } {
43
+ const target = row.link_to ?? "";
44
+ if (!target) return { disabled: true };
45
+ const prefix = paths.base === "/" ? "" : paths.base;
46
+ switch (row.link_type) {
47
+ case "DocType":
48
+ return { path: paths.list(target) };
49
+ case "Report":
50
+ return { path: paths.list(target) };
51
+ case "Page":
52
+ return { path: `${prefix}/${target}` };
53
+ default:
54
+ return { disabled: true };
55
+ }
56
+ }
57
+
58
+ function LinkRow({ row }: { row: WorkspaceLinkRow }) {
59
+ const navigator = useBackofficeNavigator();
60
+ const paths = useBackofficePaths();
61
+ const target = resolveLinkTarget(row, paths);
62
+ return (
63
+ <XStack
64
+ onPress={target.disabled ? undefined : () => target.path && navigator.navigate(target.path)}
65
+ disabled={target.disabled}
66
+ opacity={target.disabled ? 0.5 : 1}
67
+ cursor={target.disabled ? "default" : "pointer"}
68
+ paddingVertical="$1.5"
69
+ alignItems="center"
70
+ role="link"
71
+ aria-label={row.label}
72
+ hoverStyle={target.disabled ? undefined : { backgroundColor: "$color3" }}
73
+ borderRadius="$2"
74
+ >
75
+ <SizableText
76
+ size="$3"
77
+ color={target.disabled ? "$color10" : "$color12"}
78
+ numberOfLines={1}
79
+ flexShrink={1}
80
+ hoverStyle={target.disabled ? undefined : { color: "$color12" }}
81
+ >
82
+ {row.label}
83
+ </SizableText>
84
+ </XStack>
85
+ );
86
+ }
87
+
88
+ export interface WorkspaceLinkCardProps {
89
+ group: WorkspaceLinkGroup;
90
+ }
91
+
92
+ export function WorkspaceLinkCard({ group }: WorkspaceLinkCardProps) {
93
+ const { knobProps } = useResolvedKnobs();
94
+ const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
95
+ const { views } = useBackofficeCapabilities();
96
+ const isDoctypeAllowed = useIsDoctypeAllowed();
97
+
98
+ // Visibility gates: doctype scope + the views capability (DocType/Report
99
+ // rows navigate to list surfaces). Defaults (scope open, views on) keep
100
+ // every authored row, desk parity.
101
+ const visibleLinks = group.links.filter((row) => {
102
+ if (row.link_type === "DocType" || row.link_type === "Report") {
103
+ if (!views.enabled) return false;
104
+ if (row.link_type === "DocType" && row.link_to && !isDoctypeAllowed(row.link_to)) {
105
+ return false;
106
+ }
107
+ }
108
+ return true;
109
+ });
110
+
111
+ return (
112
+ <Card flexGrow={1} height="100%">
113
+ <YStack {...knobProps.panelPadding} {...knobProps.gap} flex={1}>
114
+ {/* Catalog list-group caption (Sidebar section-label treatment):
115
+ small-caps muted label, no underline rule — a full-width rule
116
+ under uneven columns read as table chrome (Axiom 16). */}
117
+ <SizableText size="$2" fontWeight="600" textTransform="uppercase" color="$color11">
118
+ {group.label}
119
+ </SizableText>
120
+ {visibleLinks.length === 0 ? (
121
+ <SizableText size="$2" color="$color10">
122
+ {t("workspaceCardEmpty", "No links in this section")}
123
+ </SizableText>
124
+ ) : (
125
+ <YStack>
126
+ {visibleLinks.map((row) => (
127
+ <LinkRow key={row.name} row={row} />
128
+ ))}
129
+ </YStack>
130
+ )}
131
+ </YStack>
132
+ </Card>
133
+ );
134
+ }
@@ -0,0 +1,126 @@
1
+ /**
2
+ * "Browse all" disclosure for the workspace link farm (MOD-11): the grouped
3
+ * link cards render inside one collapsible region with a local type-to-filter
4
+ * input, so 20–30 links stay fully reachable and searchable (capability
5
+ * parity with desk) without owning the top of the landing page. Expanded by
6
+ * default on desktop-width viewports, collapsed on phones and native.
7
+ */
8
+ import { EmptyState, Accordion, YStack } from "@multiplatform.one/components";
9
+ import { SearchInput, useViewportGtSm } from "@multiplatform.one/forms";
10
+ import { useResolvedKnobs } from "@multiplatform.one/theme";
11
+ import { useMemo, useState } from "react";
12
+ import { useTranslation } from "react-i18next";
13
+ import { BACKOFFICE_I18N_NAMESPACE } from "../i18n";
14
+ import type { WorkspaceLinkGroup } from "./types";
15
+ import { WorkspaceGrid, type WorkspaceGridItem } from "./WorkspaceGrid";
16
+ import { WorkspaceLinkCard } from "./WorkspaceLinkCard";
17
+
18
+ export interface WorkspaceLinkDirectoryItem {
19
+ /** Stable key from the authored card block. */
20
+ key: string;
21
+ /** Authored 12-column span (defaults to 4, desk's card width). */
22
+ col?: number;
23
+ group: WorkspaceLinkGroup;
24
+ }
25
+
26
+ export interface WorkspaceLinkDirectoryProps {
27
+ items: WorkspaceLinkDirectoryItem[];
28
+ }
29
+
30
+ const DIRECTORY_VALUE = "links";
31
+
32
+ /**
33
+ * Case-insensitive filter over a link group: a match on the group label
34
+ * keeps the whole card; otherwise the card narrows to its matching links
35
+ * (and disappears when none match).
36
+ */
37
+ export function filterLinkGroup(
38
+ group: WorkspaceLinkGroup,
39
+ query: string,
40
+ ): WorkspaceLinkGroup | undefined {
41
+ const needle = query.trim().toLowerCase();
42
+ if (!needle) return group;
43
+ if (group.label.toLowerCase().includes(needle)) return group;
44
+ const links = group.links.filter((row) => row.label.toLowerCase().includes(needle));
45
+ return links.length > 0 ? { ...group, links } : undefined;
46
+ }
47
+
48
+ export function WorkspaceLinkDirectory({ items }: WorkspaceLinkDirectoryProps) {
49
+ const { t } = useTranslation(BACKOFFICE_I18N_NAMESPACE);
50
+ const { knobProps } = useResolvedKnobs();
51
+ const viewportGtSm = useViewportGtSm();
52
+ const [filter, setFilter] = useState("");
53
+ // Uncontrolled-once default: open on desktop-width viewports, collapsed on
54
+ // phones/native (SPA render — the viewport is known at first render).
55
+ // Later resizes don't override the user's toggle.
56
+ const [openValue, setOpenValue] = useState(() => (viewportGtSm ? DIRECTORY_VALUE : ""));
57
+
58
+ const totalLinks = useMemo(
59
+ () => items.reduce((sum, item) => sum + item.group.links.length, 0),
60
+ [items],
61
+ );
62
+
63
+ const filteredItems = useMemo<WorkspaceGridItem[]>(
64
+ () =>
65
+ items.flatMap((item) => {
66
+ const group = filterLinkGroup(item.group, filter);
67
+ if (!group) return [];
68
+ return [
69
+ {
70
+ key: item.key,
71
+ col: item.col ?? 4,
72
+ node: <WorkspaceLinkCard group={group} />,
73
+ },
74
+ ];
75
+ }),
76
+ [items, filter],
77
+ );
78
+
79
+ return (
80
+ <Accordion
81
+ type="single"
82
+ collapsible
83
+ variant="card"
84
+ value={openValue}
85
+ onValueChange={setOpenValue}
86
+ >
87
+ <Accordion.Item value={DIRECTORY_VALUE}>
88
+ <Accordion.Trigger
89
+ // Count-based plural via i18next suffix resolution ("1 link" /
90
+ // "N links"); defaultValue_* carries the en fallback per form.
91
+ description={t("workspaceBrowseAllCount", {
92
+ count: totalLinks,
93
+ defaultValue_one: "{{count}} link",
94
+ defaultValue_other: "{{count}} links",
95
+ })}
96
+ >
97
+ {t("workspaceBrowseAll", "Browse all")}
98
+ </Accordion.Trigger>
99
+ <Accordion.Content>
100
+ <YStack {...knobProps.gap}>
101
+ <SearchInput
102
+ value={filter}
103
+ onChange={setFilter}
104
+ debounceMs={0}
105
+ compact
106
+ placeholder={t("workspaceFilterLinks", "Filter links...")}
107
+ aria-label={t("workspaceFilterLinks", "Filter links...")}
108
+ />
109
+ {filteredItems.length === 0 ? (
110
+ <EmptyState
111
+ compact
112
+ title={t(
113
+ "workspaceFilterNoMatch",
114
+ 'No links match "{{query}}"',
115
+ { query: filter.trim() },
116
+ )}
117
+ />
118
+ ) : (
119
+ <WorkspaceGrid items={filteredItems} />
120
+ )}
121
+ </YStack>
122
+ </Accordion.Content>
123
+ </Accordion.Item>
124
+ </Accordion>
125
+ );
126
+ }