@actuate-media/cms-admin 0.7.3 → 0.8.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 (435) hide show
  1. package/dist/AdminRoot.d.ts.map +1 -1
  2. package/dist/AdminRoot.js +95 -42
  3. package/dist/AdminRoot.js.map +1 -1
  4. package/dist/__tests__/lib/search.test.js +10 -10
  5. package/dist/__tests__/lib/search.test.js.map +1 -1
  6. package/dist/__tests__/lib/utils.test.js.map +1 -1
  7. package/dist/__tests__/router/match-route.test.js.map +1 -1
  8. package/dist/__tests__/router/strip-base.test.js.map +1 -1
  9. package/dist/actuate-admin.css +1 -1
  10. package/dist/components/Breadcrumbs.d.ts.map +1 -1
  11. package/dist/components/Breadcrumbs.js +2 -4
  12. package/dist/components/Breadcrumbs.js.map +1 -1
  13. package/dist/components/CommandPalette.d.ts.map +1 -1
  14. package/dist/components/CommandPalette.js +7 -3
  15. package/dist/components/CommandPalette.js.map +1 -1
  16. package/dist/components/ContentOverviewChart.d.ts.map +1 -1
  17. package/dist/components/ContentOverviewChart.js.map +1 -1
  18. package/dist/components/ErrorBoundary.d.ts.map +1 -1
  19. package/dist/components/ErrorBoundary.js.map +1 -1
  20. package/dist/components/FocalPointPicker.d.ts.map +1 -1
  21. package/dist/components/FocalPointPicker.js +4 -2
  22. package/dist/components/FocalPointPicker.js.map +1 -1
  23. package/dist/components/FolderTree.d.ts.map +1 -1
  24. package/dist/components/FolderTree.js +18 -10
  25. package/dist/components/FolderTree.js.map +1 -1
  26. package/dist/components/LivePreview.d.ts +1 -1
  27. package/dist/components/LivePreview.d.ts.map +1 -1
  28. package/dist/components/LivePreview.js +6 -2
  29. package/dist/components/LivePreview.js.map +1 -1
  30. package/dist/components/LocaleProvider.d.ts.map +1 -1
  31. package/dist/components/LocaleProvider.js.map +1 -1
  32. package/dist/components/LocaleSwitcher.d.ts.map +1 -1
  33. package/dist/components/LocaleSwitcher.js +1 -1
  34. package/dist/components/LocaleSwitcher.js.map +1 -1
  35. package/dist/components/MediaPickerModal.d.ts.map +1 -1
  36. package/dist/components/MediaPickerModal.js.map +1 -1
  37. package/dist/components/PresenceIndicator.d.ts.map +1 -1
  38. package/dist/components/PresenceIndicator.js +5 -2
  39. package/dist/components/PresenceIndicator.js.map +1 -1
  40. package/dist/components/SEOPanel.d.ts +1 -1
  41. package/dist/components/SEOPanel.d.ts.map +1 -1
  42. package/dist/components/SEOPanel.js +110 -24
  43. package/dist/components/SEOPanel.js.map +1 -1
  44. package/dist/components/SEOPerformance.d.ts.map +1 -1
  45. package/dist/components/SEOPerformance.js +2 -2
  46. package/dist/components/SEOPerformance.js.map +1 -1
  47. package/dist/components/ThemeProvider.d.ts.map +1 -1
  48. package/dist/components/ThemeProvider.js.map +1 -1
  49. package/dist/components/TipTapEditor.d.ts.map +1 -1
  50. package/dist/components/TipTapEditor.js +5 -1
  51. package/dist/components/TipTapEditor.js.map +1 -1
  52. package/dist/components/VersionHistory.d.ts +1 -1
  53. package/dist/components/VersionHistory.d.ts.map +1 -1
  54. package/dist/components/VersionHistory.js +1 -1
  55. package/dist/components/VersionHistory.js.map +1 -1
  56. package/dist/components/ui/Avatar.d.ts.map +1 -1
  57. package/dist/components/ui/Avatar.js.map +1 -1
  58. package/dist/components/ui/Badge.d.ts.map +1 -1
  59. package/dist/components/ui/Badge.js.map +1 -1
  60. package/dist/components/ui/Button.d.ts.map +1 -1
  61. package/dist/components/ui/Button.js.map +1 -1
  62. package/dist/components/ui/CommandPalette.d.ts.map +1 -1
  63. package/dist/components/ui/CommandPalette.js +8 -2
  64. package/dist/components/ui/CommandPalette.js.map +1 -1
  65. package/dist/components/ui/ConfirmDialog.d.ts.map +1 -1
  66. package/dist/components/ui/ConfirmDialog.js.map +1 -1
  67. package/dist/components/ui/DataTable.d.ts.map +1 -1
  68. package/dist/components/ui/DataTable.js +1 -3
  69. package/dist/components/ui/DataTable.js.map +1 -1
  70. package/dist/components/ui/EmptyState.d.ts.map +1 -1
  71. package/dist/components/ui/EmptyState.js +1 -1
  72. package/dist/components/ui/EmptyState.js.map +1 -1
  73. package/dist/components/ui/Modal.d.ts.map +1 -1
  74. package/dist/components/ui/Modal.js.map +1 -1
  75. package/dist/components/ui/Pagination.d.ts +1 -1
  76. package/dist/components/ui/Pagination.d.ts.map +1 -1
  77. package/dist/components/ui/Pagination.js +7 -2
  78. package/dist/components/ui/Pagination.js.map +1 -1
  79. package/dist/components/ui/SearchInput.d.ts.map +1 -1
  80. package/dist/components/ui/SearchInput.js.map +1 -1
  81. package/dist/components/ui/Skeleton.d.ts.map +1 -1
  82. package/dist/components/ui/Skeleton.js.map +1 -1
  83. package/dist/components/ui/Toast.d.ts.map +1 -1
  84. package/dist/components/ui/Toast.js.map +1 -1
  85. package/dist/components/ui/index.d.ts.map +1 -1
  86. package/dist/components/ui/index.js.map +1 -1
  87. package/dist/fields/ArrayField.d.ts.map +1 -1
  88. package/dist/fields/ArrayField.js +1 -1
  89. package/dist/fields/ArrayField.js.map +1 -1
  90. package/dist/fields/BlockBuilderField.d.ts.map +1 -1
  91. package/dist/fields/BlockBuilderField.js +7 -7
  92. package/dist/fields/BlockBuilderField.js.map +1 -1
  93. package/dist/fields/DateField.d.ts.map +1 -1
  94. package/dist/fields/DateField.js +1 -1
  95. package/dist/fields/DateField.js.map +1 -1
  96. package/dist/fields/FieldRenderer.d.ts.map +1 -1
  97. package/dist/fields/FieldRenderer.js.map +1 -1
  98. package/dist/fields/GroupField.d.ts.map +1 -1
  99. package/dist/fields/GroupField.js +1 -1
  100. package/dist/fields/GroupField.js.map +1 -1
  101. package/dist/fields/MediaField.d.ts.map +1 -1
  102. package/dist/fields/MediaField.js +1 -1
  103. package/dist/fields/MediaField.js.map +1 -1
  104. package/dist/fields/NavBuilderField.d.ts.map +1 -1
  105. package/dist/fields/NavBuilderField.js +2 -5
  106. package/dist/fields/NavBuilderField.js.map +1 -1
  107. package/dist/fields/NumberField.d.ts +1 -1
  108. package/dist/fields/NumberField.d.ts.map +1 -1
  109. package/dist/fields/NumberField.js +2 -2
  110. package/dist/fields/NumberField.js.map +1 -1
  111. package/dist/fields/RelationshipField.d.ts.map +1 -1
  112. package/dist/fields/RelationshipField.js +7 -3
  113. package/dist/fields/RelationshipField.js.map +1 -1
  114. package/dist/fields/RichTextField.d.ts +1 -1
  115. package/dist/fields/RichTextField.d.ts.map +1 -1
  116. package/dist/fields/RichTextField.js +2 -2
  117. package/dist/fields/RichTextField.js.map +1 -1
  118. package/dist/fields/SelectField.d.ts.map +1 -1
  119. package/dist/fields/SelectField.js +9 -7
  120. package/dist/fields/SelectField.js.map +1 -1
  121. package/dist/fields/SlugField.d.ts.map +1 -1
  122. package/dist/fields/SlugField.js +1 -1
  123. package/dist/fields/SlugField.js.map +1 -1
  124. package/dist/fields/TextField.d.ts +1 -1
  125. package/dist/fields/TextField.d.ts.map +1 -1
  126. package/dist/fields/TextField.js +2 -2
  127. package/dist/fields/TextField.js.map +1 -1
  128. package/dist/fields/ToggleField.d.ts.map +1 -1
  129. package/dist/fields/ToggleField.js +1 -1
  130. package/dist/fields/ToggleField.js.map +1 -1
  131. package/dist/fields/block-types.d.ts.map +1 -1
  132. package/dist/fields/block-types.js +28 -8
  133. package/dist/fields/block-types.js.map +1 -1
  134. package/dist/fields/index.d.ts.map +1 -1
  135. package/dist/fields/index.js.map +1 -1
  136. package/dist/hooks/useBuilderState.d.ts.map +1 -1
  137. package/dist/hooks/useBuilderState.js.map +1 -1
  138. package/dist/hooks/useContentLock.d.ts.map +1 -1
  139. package/dist/hooks/useContentLock.js.map +1 -1
  140. package/dist/hooks/useDebounce.js.map +1 -1
  141. package/dist/hooks/useKeyboardShortcuts.d.ts.map +1 -1
  142. package/dist/hooks/useKeyboardShortcuts.js.map +1 -1
  143. package/dist/index.d.ts +2 -2
  144. package/dist/index.d.ts.map +1 -1
  145. package/dist/index.js.map +1 -1
  146. package/dist/layout/Header.d.ts.map +1 -1
  147. package/dist/layout/Header.js.map +1 -1
  148. package/dist/layout/Layout.d.ts.map +1 -1
  149. package/dist/layout/Layout.js.map +1 -1
  150. package/dist/layout/Sidebar.d.ts +1 -1
  151. package/dist/layout/Sidebar.d.ts.map +1 -1
  152. package/dist/layout/Sidebar.js +5 -8
  153. package/dist/layout/Sidebar.js.map +1 -1
  154. package/dist/lib/api.d.ts.map +1 -1
  155. package/dist/lib/api.js +33 -4
  156. package/dist/lib/api.js.map +1 -1
  157. package/dist/lib/search.d.ts.map +1 -1
  158. package/dist/lib/search.js +3 -5
  159. package/dist/lib/search.js.map +1 -1
  160. package/dist/lib/useApiData.d.ts.map +1 -1
  161. package/dist/lib/useApiData.js.map +1 -1
  162. package/dist/lib/utils.d.ts.map +1 -1
  163. package/dist/lib/utils.js.map +1 -1
  164. package/dist/router/index.d.ts.map +1 -1
  165. package/dist/router/index.js +1 -3
  166. package/dist/router/index.js.map +1 -1
  167. package/dist/views/CollectionList.d.ts.map +1 -1
  168. package/dist/views/CollectionList.js +56 -17
  169. package/dist/views/CollectionList.js.map +1 -1
  170. package/dist/views/Dashboard.d.ts.map +1 -1
  171. package/dist/views/Dashboard.js +26 -13
  172. package/dist/views/Dashboard.js.map +1 -1
  173. package/dist/views/DocumentEdit.d.ts +1 -1
  174. package/dist/views/DocumentEdit.d.ts.map +1 -1
  175. package/dist/views/DocumentEdit.js +33 -15
  176. package/dist/views/DocumentEdit.js.map +1 -1
  177. package/dist/views/ForgotPassword.d.ts.map +1 -1
  178. package/dist/views/ForgotPassword.js.map +1 -1
  179. package/dist/views/FormEditor.d.ts.map +1 -1
  180. package/dist/views/FormEditor.js +8 -2
  181. package/dist/views/FormEditor.js.map +1 -1
  182. package/dist/views/FormSubmissions.d.ts.map +1 -1
  183. package/dist/views/FormSubmissions.js +6 -6
  184. package/dist/views/FormSubmissions.js.map +1 -1
  185. package/dist/views/Forms.d.ts.map +1 -1
  186. package/dist/views/Forms.js.map +1 -1
  187. package/dist/views/Login.d.ts.map +1 -1
  188. package/dist/views/Login.js +5 -2
  189. package/dist/views/Login.js.map +1 -1
  190. package/dist/views/MediaBrowser.d.ts.map +1 -1
  191. package/dist/views/MediaBrowser.js +39 -19
  192. package/dist/views/MediaBrowser.js.map +1 -1
  193. package/dist/views/PageEditor.d.ts.map +1 -1
  194. package/dist/views/PageEditor.js.map +1 -1
  195. package/dist/views/Pages.d.ts.map +1 -1
  196. package/dist/views/Pages.js +20 -10
  197. package/dist/views/Pages.js.map +1 -1
  198. package/dist/views/PostEditor.d.ts.map +1 -1
  199. package/dist/views/PostEditor.js.map +1 -1
  200. package/dist/views/Posts.d.ts.map +1 -1
  201. package/dist/views/Posts.js +13 -7
  202. package/dist/views/Posts.js.map +1 -1
  203. package/dist/views/Redirects.d.ts.map +1 -1
  204. package/dist/views/Redirects.js +17 -5
  205. package/dist/views/Redirects.js.map +1 -1
  206. package/dist/views/ResetPassword.d.ts.map +1 -1
  207. package/dist/views/ResetPassword.js.map +1 -1
  208. package/dist/views/SEO.d.ts.map +1 -1
  209. package/dist/views/SEO.js +39 -16
  210. package/dist/views/SEO.js.map +1 -1
  211. package/dist/views/ScriptTagEditor.d.ts.map +1 -1
  212. package/dist/views/ScriptTagEditor.js +2 -1
  213. package/dist/views/ScriptTagEditor.js.map +1 -1
  214. package/dist/views/ScriptTags.d.ts.map +1 -1
  215. package/dist/views/ScriptTags.js.map +1 -1
  216. package/dist/views/Settings.d.ts.map +1 -1
  217. package/dist/views/Settings.js +38 -11
  218. package/dist/views/Settings.js.map +1 -1
  219. package/dist/views/SetupWizard.d.ts.map +1 -1
  220. package/dist/views/SetupWizard.js.map +1 -1
  221. package/dist/views/Users.d.ts.map +1 -1
  222. package/dist/views/Users.js +5 -3
  223. package/dist/views/Users.js.map +1 -1
  224. package/dist/views/page-builder/AIBlockAssist.d.ts.map +1 -1
  225. package/dist/views/page-builder/AIBlockAssist.js +1 -1
  226. package/dist/views/page-builder/AIBlockAssist.js.map +1 -1
  227. package/dist/views/page-builder/AIGenerateDialog.d.ts.map +1 -1
  228. package/dist/views/page-builder/AIGenerateDialog.js +4 -1
  229. package/dist/views/page-builder/AIGenerateDialog.js.map +1 -1
  230. package/dist/views/page-builder/BlockEditor.d.ts.map +1 -1
  231. package/dist/views/page-builder/BlockEditor.js +94 -3
  232. package/dist/views/page-builder/BlockEditor.js.map +1 -1
  233. package/dist/views/page-builder/BlockPicker.d.ts.map +1 -1
  234. package/dist/views/page-builder/BlockPicker.js.map +1 -1
  235. package/dist/views/page-builder/BottomBar.d.ts.map +1 -1
  236. package/dist/views/page-builder/BottomBar.js.map +1 -1
  237. package/dist/views/page-builder/BuilderToolbar.d.ts.map +1 -1
  238. package/dist/views/page-builder/BuilderToolbar.js.map +1 -1
  239. package/dist/views/page-builder/ContextPanel.d.ts.map +1 -1
  240. package/dist/views/page-builder/ContextPanel.js +4 -1
  241. package/dist/views/page-builder/ContextPanel.js.map +1 -1
  242. package/dist/views/page-builder/DesignScore.d.ts.map +1 -1
  243. package/dist/views/page-builder/DesignScore.js.map +1 -1
  244. package/dist/views/page-builder/NodeSettings.d.ts.map +1 -1
  245. package/dist/views/page-builder/NodeSettings.js +1 -1
  246. package/dist/views/page-builder/NodeSettings.js.map +1 -1
  247. package/dist/views/page-builder/PageBuilder.d.ts +1 -1
  248. package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
  249. package/dist/views/page-builder/PageBuilder.js +25 -3
  250. package/dist/views/page-builder/PageBuilder.js.map +1 -1
  251. package/dist/views/page-builder/PageSettings.d.ts.map +1 -1
  252. package/dist/views/page-builder/PageSettings.js.map +1 -1
  253. package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
  254. package/dist/views/page-builder/PageTemplates.js.map +1 -1
  255. package/dist/views/page-builder/SEOPanel.d.ts.map +1 -1
  256. package/dist/views/page-builder/SEOPanel.js +1 -3
  257. package/dist/views/page-builder/SEOPanel.js.map +1 -1
  258. package/dist/views/page-builder/SavedSections.d.ts.map +1 -1
  259. package/dist/views/page-builder/SavedSections.js +3 -7
  260. package/dist/views/page-builder/SavedSections.js.map +1 -1
  261. package/dist/views/page-builder/TemplatePicker.d.ts.map +1 -1
  262. package/dist/views/page-builder/TemplatePicker.js.map +1 -1
  263. package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +1 -1
  264. package/dist/views/page-builder/block-renderers/CTAPreview.js +1 -1
  265. package/dist/views/page-builder/block-renderers/CTAPreview.js.map +1 -1
  266. package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +1 -1
  267. package/dist/views/page-builder/block-renderers/CardsPreview.js +1 -1
  268. package/dist/views/page-builder/block-renderers/CardsPreview.js.map +1 -1
  269. package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +1 -1
  270. package/dist/views/page-builder/block-renderers/CodePreview.js +1 -5
  271. package/dist/views/page-builder/block-renderers/CodePreview.js.map +1 -1
  272. package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +1 -1
  273. package/dist/views/page-builder/block-renderers/FAQPreview.js +4 -1
  274. package/dist/views/page-builder/block-renderers/FAQPreview.js.map +1 -1
  275. package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +1 -1
  276. package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +1 -1
  277. package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +1 -1
  278. package/dist/views/page-builder/block-renderers/FormPreview.js +2 -2
  279. package/dist/views/page-builder/block-renderers/FormPreview.js.map +1 -1
  280. package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +1 -1
  281. package/dist/views/page-builder/block-renderers/GalleryPreview.js +1 -3
  282. package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +1 -1
  283. package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +1 -1
  284. package/dist/views/page-builder/block-renderers/HeroPreview.js.map +1 -1
  285. package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +1 -1
  286. package/dist/views/page-builder/block-renderers/ImagePreview.js.map +1 -1
  287. package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +1 -1
  288. package/dist/views/page-builder/block-renderers/TextPreview.js +2 -6
  289. package/dist/views/page-builder/block-renderers/TextPreview.js.map +1 -1
  290. package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +1 -1
  291. package/dist/views/page-builder/block-renderers/VideoPreview.js +2 -5
  292. package/dist/views/page-builder/block-renderers/VideoPreview.js.map +1 -1
  293. package/dist/views/page-builder/block-renderers/index.d.ts.map +1 -1
  294. package/dist/views/page-builder/block-renderers/index.js.map +1 -1
  295. package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +1 -1
  296. package/dist/views/page-builder/canvas/BlockRenderer.js +1 -5
  297. package/dist/views/page-builder/canvas/BlockRenderer.js.map +1 -1
  298. package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +1 -1
  299. package/dist/views/page-builder/canvas/BuilderCanvas.js.map +1 -1
  300. package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +1 -1
  301. package/dist/views/page-builder/canvas/ColumnRenderer.js +1 -5
  302. package/dist/views/page-builder/canvas/ColumnRenderer.js.map +1 -1
  303. package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +1 -1
  304. package/dist/views/page-builder/canvas/ContainerRenderer.js +1 -5
  305. package/dist/views/page-builder/canvas/ContainerRenderer.js.map +1 -1
  306. package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +1 -1
  307. package/dist/views/page-builder/canvas/RowRenderer.js +1 -5
  308. package/dist/views/page-builder/canvas/RowRenderer.js.map +1 -1
  309. package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +1 -1
  310. package/dist/views/page-builder/canvas/SectionRenderer.js +1 -5
  311. package/dist/views/page-builder/canvas/SectionRenderer.js.map +1 -1
  312. package/dist/views/page-builder/canvas/index.d.ts.map +1 -1
  313. package/dist/views/page-builder/canvas/index.js.map +1 -1
  314. package/package.json +2 -2
  315. package/src/AdminRoot.tsx +302 -177
  316. package/src/__tests__/lib/search.test.ts +60 -69
  317. package/src/__tests__/lib/utils.test.ts +12 -12
  318. package/src/__tests__/router/match-route.test.ts +24 -26
  319. package/src/__tests__/router/strip-base.test.ts +15 -15
  320. package/src/components/Breadcrumbs.tsx +27 -24
  321. package/src/components/CommandPalette.tsx +115 -99
  322. package/src/components/ContentOverviewChart.tsx +19 -14
  323. package/src/components/ErrorBoundary.tsx +13 -13
  324. package/src/components/FocalPointPicker.tsx +31 -20
  325. package/src/components/FolderTree.tsx +172 -139
  326. package/src/components/LivePreview.tsx +68 -41
  327. package/src/components/LocaleProvider.tsx +26 -20
  328. package/src/components/LocaleSwitcher.tsx +9 -11
  329. package/src/components/MediaPickerModal.tsx +46 -45
  330. package/src/components/PresenceIndicator.tsx +30 -27
  331. package/src/components/SEOPanel.tsx +378 -228
  332. package/src/components/SEOPerformance.tsx +52 -30
  333. package/src/components/ThemeProvider.tsx +46 -46
  334. package/src/components/TipTapEditor.tsx +60 -64
  335. package/src/components/VersionHistory.tsx +63 -52
  336. package/src/components/ui/Avatar.tsx +8 -8
  337. package/src/components/ui/Badge.tsx +7 -5
  338. package/src/components/ui/Button.tsx +24 -13
  339. package/src/components/ui/CommandPalette.tsx +56 -42
  340. package/src/components/ui/ConfirmDialog.tsx +14 -14
  341. package/src/components/ui/DataTable.tsx +37 -39
  342. package/src/components/ui/EmptyState.tsx +9 -11
  343. package/src/components/ui/Modal.tsx +21 -15
  344. package/src/components/ui/Pagination.tsx +34 -19
  345. package/src/components/ui/SearchInput.tsx +17 -7
  346. package/src/components/ui/Skeleton.tsx +7 -7
  347. package/src/components/ui/Toast.tsx +29 -22
  348. package/src/components/ui/index.ts +24 -24
  349. package/src/fields/ArrayField.tsx +43 -25
  350. package/src/fields/BlockBuilderField.tsx +80 -99
  351. package/src/fields/DateField.tsx +20 -12
  352. package/src/fields/FieldRenderer.tsx +34 -34
  353. package/src/fields/GroupField.tsx +8 -10
  354. package/src/fields/MediaField.tsx +8 -10
  355. package/src/fields/NavBuilderField.tsx +24 -25
  356. package/src/fields/NumberField.tsx +21 -14
  357. package/src/fields/RelationshipField.tsx +105 -91
  358. package/src/fields/RichTextField.tsx +16 -12
  359. package/src/fields/SelectField.tsx +42 -34
  360. package/src/fields/SlugField.tsx +29 -17
  361. package/src/fields/TextField.tsx +24 -16
  362. package/src/fields/ToggleField.tsx +7 -9
  363. package/src/fields/block-types.ts +50 -24
  364. package/src/fields/index.ts +17 -17
  365. package/src/hooks/useBuilderState.ts +260 -221
  366. package/src/hooks/useContentLock.ts +23 -20
  367. package/src/hooks/useDebounce.ts +7 -7
  368. package/src/hooks/useKeyboardShortcuts.ts +16 -16
  369. package/src/index.ts +69 -58
  370. package/src/layout/Header.tsx +21 -20
  371. package/src/layout/Layout.tsx +22 -24
  372. package/src/layout/Sidebar.tsx +107 -72
  373. package/src/lib/api.ts +58 -30
  374. package/src/lib/search.ts +30 -34
  375. package/src/lib/useApiData.ts +65 -62
  376. package/src/lib/utils.ts +3 -3
  377. package/src/router/index.ts +33 -35
  378. package/src/styles/build-input.css +2 -2
  379. package/src/styles/tailwind.css +1 -1
  380. package/src/styles/theme.css +7 -1
  381. package/src/views/CollectionList.tsx +275 -121
  382. package/src/views/Dashboard.tsx +164 -117
  383. package/src/views/DocumentEdit.tsx +298 -253
  384. package/src/views/ForgotPassword.tsx +27 -23
  385. package/src/views/FormEditor.tsx +165 -99
  386. package/src/views/FormSubmissions.tsx +261 -117
  387. package/src/views/Forms.tsx +56 -26
  388. package/src/views/Login.tsx +107 -84
  389. package/src/views/MediaBrowser.tsx +717 -523
  390. package/src/views/PageEditor.tsx +44 -46
  391. package/src/views/Pages.tsx +312 -149
  392. package/src/views/PostEditor.tsx +57 -51
  393. package/src/views/Posts.tsx +206 -74
  394. package/src/views/Redirects.tsx +173 -117
  395. package/src/views/ResetPassword.tsx +43 -32
  396. package/src/views/SEO.tsx +589 -160
  397. package/src/views/ScriptTagEditor.tsx +69 -69
  398. package/src/views/ScriptTags.tsx +54 -42
  399. package/src/views/Settings.tsx +430 -220
  400. package/src/views/SetupWizard.tsx +69 -46
  401. package/src/views/Users.tsx +154 -120
  402. package/src/views/page-builder/AIBlockAssist.tsx +21 -25
  403. package/src/views/page-builder/AIGenerateDialog.tsx +134 -127
  404. package/src/views/page-builder/BlockEditor.tsx +258 -81
  405. package/src/views/page-builder/BlockPicker.tsx +73 -88
  406. package/src/views/page-builder/BottomBar.tsx +15 -11
  407. package/src/views/page-builder/BuilderToolbar.tsx +32 -29
  408. package/src/views/page-builder/ContextPanel.tsx +57 -57
  409. package/src/views/page-builder/DesignScore.tsx +52 -59
  410. package/src/views/page-builder/NodeSettings.tsx +59 -59
  411. package/src/views/page-builder/PageBuilder.tsx +164 -146
  412. package/src/views/page-builder/PageSettings.tsx +16 -15
  413. package/src/views/page-builder/PageTemplates.tsx +23 -17
  414. package/src/views/page-builder/SEOPanel.tsx +90 -111
  415. package/src/views/page-builder/SavedSections.tsx +99 -105
  416. package/src/views/page-builder/TemplatePicker.tsx +44 -48
  417. package/src/views/page-builder/block-renderers/CTAPreview.tsx +11 -13
  418. package/src/views/page-builder/block-renderers/CardsPreview.tsx +13 -15
  419. package/src/views/page-builder/block-renderers/CodePreview.tsx +16 -16
  420. package/src/views/page-builder/block-renderers/FAQPreview.tsx +20 -23
  421. package/src/views/page-builder/block-renderers/FallbackPreview.tsx +5 -5
  422. package/src/views/page-builder/block-renderers/FormPreview.tsx +9 -13
  423. package/src/views/page-builder/block-renderers/GalleryPreview.tsx +22 -28
  424. package/src/views/page-builder/block-renderers/HeroPreview.tsx +17 -30
  425. package/src/views/page-builder/block-renderers/ImagePreview.tsx +12 -12
  426. package/src/views/page-builder/block-renderers/TextPreview.tsx +22 -22
  427. package/src/views/page-builder/block-renderers/VideoPreview.tsx +13 -18
  428. package/src/views/page-builder/block-renderers/index.ts +17 -17
  429. package/src/views/page-builder/canvas/BlockRenderer.tsx +19 -23
  430. package/src/views/page-builder/canvas/BuilderCanvas.tsx +17 -20
  431. package/src/views/page-builder/canvas/ColumnRenderer.tsx +22 -26
  432. package/src/views/page-builder/canvas/ContainerRenderer.tsx +20 -24
  433. package/src/views/page-builder/canvas/RowRenderer.tsx +19 -23
  434. package/src/views/page-builder/canvas/SectionRenderer.tsx +30 -34
  435. package/src/views/page-builder/canvas/index.ts +2 -2
package/src/AdminRoot.tsx CHANGED
@@ -1,87 +1,148 @@
1
- 'use client';
2
-
3
- import { useState, useMemo, useEffect, useRef } from 'react';
4
- import { Layout } from './layout/Layout.js';
5
- import { useAdminRouter } from './router/index.js';
6
- import { Dashboard } from './views/Dashboard.js';
7
- import { CollectionList } from './views/CollectionList.js';
8
- import { DocumentEdit } from './views/DocumentEdit.js';
9
- import { MediaBrowser } from './views/MediaBrowser.js';
10
- import { Settings } from './views/Settings.js';
11
- import { Forms } from './views/Forms.js';
12
- import { FormEditor } from './views/FormEditor.js';
13
- import { FormSubmissions } from './views/FormSubmissions.js';
14
- import { Users } from './views/Users.js';
15
- import { SEO } from './views/SEO.js';
16
- import { ScriptTags } from './views/ScriptTags.js';
17
- import { ScriptTagEditor } from './views/ScriptTagEditor.js';
18
- import { SetupWizard } from './views/SetupWizard.js';
19
- import { Login } from './views/Login.js';
20
- import { ForgotPassword } from './views/ForgotPassword.js';
21
- import { ResetPassword } from './views/ResetPassword.js';
22
- import { ErrorBoundary } from './components/ErrorBoundary.js';
23
- import { ThemeProvider } from './components/ThemeProvider.js';
24
- import { LocaleProvider } from './components/LocaleProvider.js';
25
- import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts.js';
26
- import { PageBuilder } from './views/page-builder/PageBuilder.js';
27
- import { SavedSections } from './views/page-builder/SavedSections.js';
28
- import { PageTemplates } from './views/page-builder/PageTemplates.js';
1
+ 'use client'
2
+
3
+ import { useState, useMemo, useEffect, useRef } from 'react'
4
+ import { Layout } from './layout/Layout.js'
5
+ import { useAdminRouter } from './router/index.js'
6
+ import { ensureCsrfToken } from './lib/api.js'
7
+ import { Dashboard } from './views/Dashboard.js'
8
+ import { CollectionList } from './views/CollectionList.js'
9
+ import { DocumentEdit } from './views/DocumentEdit.js'
10
+ import { MediaBrowser } from './views/MediaBrowser.js'
11
+ import { Settings } from './views/Settings.js'
12
+ import { Forms } from './views/Forms.js'
13
+ import { FormEditor } from './views/FormEditor.js'
14
+ import { FormSubmissions } from './views/FormSubmissions.js'
15
+ import { Users } from './views/Users.js'
16
+ import { SEO } from './views/SEO.js'
17
+ import { ScriptTags } from './views/ScriptTags.js'
18
+ import { ScriptTagEditor } from './views/ScriptTagEditor.js'
19
+ import { SetupWizard } from './views/SetupWizard.js'
20
+ import { Login } from './views/Login.js'
21
+ import { ForgotPassword } from './views/ForgotPassword.js'
22
+ import { ResetPassword } from './views/ResetPassword.js'
23
+ import { ErrorBoundary } from './components/ErrorBoundary.js'
24
+ import { ThemeProvider } from './components/ThemeProvider.js'
25
+ import { LocaleProvider } from './components/LocaleProvider.js'
26
+ import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts.js'
27
+ import { PageBuilder } from './views/page-builder/PageBuilder.js'
28
+ import { SavedSections } from './views/page-builder/SavedSections.js'
29
+ import { PageTemplates } from './views/page-builder/PageTemplates.js'
29
30
 
30
31
  export interface AdminRootProps {
31
- config: any;
32
- session: any;
33
- basePath?: string;
34
- initialPath?: string;
35
- setupRequired?: boolean;
36
- onSetupComplete?: (data: { name: string; email: string; password: string }) => Promise<{ success: boolean; error?: string }>;
37
- onLogin?: (email: string, password: string, captchaToken?: string) => Promise<{ success: boolean; error?: string }>;
38
- captchaConfig?: { provider: 'recaptcha' | 'turnstile' | 'none'; siteKey: string | null };
32
+ config: any
33
+ session: any
34
+ basePath?: string
35
+ initialPath?: string
36
+ setupRequired?: boolean
37
+ onSetupComplete?: (data: {
38
+ name: string
39
+ email: string
40
+ password: string
41
+ }) => Promise<{ success: boolean; error?: string }>
42
+ onLogin?: (
43
+ email: string,
44
+ password: string,
45
+ captchaToken?: string,
46
+ ) => Promise<{ success: boolean; error?: string }>
47
+ captchaConfig?: { provider: 'recaptcha' | 'turnstile' | 'none'; siteKey: string | null }
39
48
  }
40
49
 
41
- function AdminShell({ config, session, basePath = '/admin', initialPath = '/', setupRequired, onSetupComplete, onLogin, captchaConfig }: AdminRootProps) {
42
- const { currentPath, navigate, matchRoute } = useAdminRouter(basePath, initialPath);
43
- const [shortcutHelpOpen, setShortcutHelpOpen] = useState(false);
44
-
45
- useBranding(config);
46
- useAdminPageTitle(config, currentPath);
47
-
48
- const shortcuts = useMemo(() => ({
49
- 'mod+k': () => {
50
- const searchInput = document.querySelector<HTMLInputElement>('input[placeholder*="Search"]');
51
- searchInput?.focus();
52
- },
53
- 'mod+s': () => {
54
- const saveBtn = document.querySelector<HTMLButtonElement>('[data-shortcut="save"]');
55
- saveBtn?.click();
56
- },
57
- 'escape': () => {
58
- const closeBtn = document.querySelector<HTMLButtonElement>('[data-shortcut="close"]');
59
- closeBtn?.click();
60
- },
61
- 'mod+/': () => {
62
- setShortcutHelpOpen(prev => !prev);
63
- },
64
- }), []);
65
-
66
- useKeyboardShortcuts(shortcuts);
50
+ function AdminShell({
51
+ config,
52
+ session,
53
+ basePath = '/admin',
54
+ initialPath = '/',
55
+ setupRequired,
56
+ onSetupComplete,
57
+ onLogin,
58
+ captchaConfig,
59
+ }: AdminRootProps) {
60
+ const { currentPath, navigate, matchRoute } = useAdminRouter(basePath, initialPath)
61
+ const [shortcutHelpOpen, setShortcutHelpOpen] = useState(false)
62
+
63
+ useBranding(config)
64
+ useAdminPageTitle(config, currentPath)
65
+
66
+ // When the session was minted via a custom Server Action (the Next.js App
67
+ // Router pattern) the admin won't have hit /auth/login directly and thus
68
+ // won't have an actuate_csrf cookie yet — every subsequent mutation 403s.
69
+ // Proactively bootstrap one as soon as we mount with a session. cmsApi
70
+ // also bootstraps lazily inside non-GET requests as a belt-and-braces.
71
+ useEffect(() => {
72
+ if (session) {
73
+ void ensureCsrfToken()
74
+ }
75
+ }, [session])
76
+
77
+ const shortcuts = useMemo(
78
+ () => ({
79
+ 'mod+k': () => {
80
+ const searchInput = document.querySelector<HTMLInputElement>('input[placeholder*="Search"]')
81
+ searchInput?.focus()
82
+ },
83
+ 'mod+s': () => {
84
+ const saveBtn = document.querySelector<HTMLButtonElement>('[data-shortcut="save"]')
85
+ saveBtn?.click()
86
+ },
87
+ escape: () => {
88
+ const closeBtn = document.querySelector<HTMLButtonElement>('[data-shortcut="close"]')
89
+ closeBtn?.click()
90
+ },
91
+ 'mod+/': () => {
92
+ setShortcutHelpOpen((prev) => !prev)
93
+ },
94
+ }),
95
+ [],
96
+ )
97
+
98
+ useKeyboardShortcuts(shortcuts)
99
+
100
+ // IMPORTANT: this useMemo MUST stay above the `setupRequired` / `!session`
101
+ // early returns. React's Rules of Hooks require the same number of hooks
102
+ // on every render — putting it below the early returns means we call N
103
+ // hooks pre-login and N+1 post-login, which crashes AdminShell with
104
+ // "Rendered more hooks than during the previous render." (caught by the
105
+ // e2e auth.spec.ts → login redirects to dashboard test).
106
+ const collectionMap = useMemo(() => {
107
+ const map = new Map<string, { slug: string; type?: string }>()
108
+ if (config?.collections) {
109
+ for (const c of Object.values(
110
+ config.collections as Record<string, { slug: string; type?: string }>,
111
+ )) {
112
+ if (c?.slug) map.set(c.slug, c)
113
+ }
114
+ }
115
+ if (map.size === 0) {
116
+ map.set('pages', { slug: 'pages', type: 'page' })
117
+ map.set('posts', { slug: 'posts' })
118
+ }
119
+ return map
120
+ }, [config])
67
121
 
68
122
  if (setupRequired && onSetupComplete) {
69
- return <SetupWizard onComplete={onSetupComplete} siteName={config?.admin?.branding?.name ?? 'Actuate CMS'} />;
123
+ return (
124
+ <SetupWizard
125
+ onComplete={onSetupComplete}
126
+ siteName={config?.admin?.branding?.name ?? 'Actuate CMS'}
127
+ />
128
+ )
70
129
  }
71
130
 
72
131
  if (!session && !setupRequired) {
73
132
  if (matchRoute('/forgot-password')) {
74
- return <ForgotPassword onNavigate={navigate} />;
133
+ return <ForgotPassword onNavigate={navigate} />
75
134
  }
76
135
 
77
- const resetMatch = matchRoute('/reset-password');
136
+ const resetMatch = matchRoute('/reset-password')
78
137
  if (resetMatch) {
79
- const params = new URLSearchParams(typeof window !== 'undefined' ? window.location.search : '');
80
- return <ResetPassword onNavigate={navigate} token={params.get('token')} />;
138
+ const params = new URLSearchParams(
139
+ typeof window !== 'undefined' ? window.location.search : '',
140
+ )
141
+ return <ResetPassword onNavigate={navigate} token={params.get('token')} />
81
142
  }
82
143
 
83
144
  if (onLogin) {
84
- return <Login onLogin={onLogin} onNavigate={navigate} captchaConfig={captchaConfig} />;
145
+ return <Login onLogin={onLogin} onNavigate={navigate} captchaConfig={captchaConfig} />
85
146
  }
86
147
  return (
87
148
  <div className="min-h-screen flex items-center justify-center bg-background">
@@ -90,114 +151,178 @@ function AdminShell({ config, session, basePath = '/admin', initialPath = '/', s
90
151
  <p className="text-sm text-muted-foreground">Please log in to access the admin panel.</p>
91
152
  </div>
92
153
  </div>
93
- );
154
+ )
94
155
  }
95
156
 
96
- const collectionSlugs = config?.collections
97
- ? Object.values(config.collections as Record<string, { slug: string }>).map((c) => c.slug)
98
- : ['pages', 'posts'];
99
-
100
157
  function renderView() {
101
158
  if (matchRoute('/')) {
102
- return <Dashboard config={config} session={session} onNavigate={navigate} />;
103
- }
104
-
105
- const pageBuilderNew = matchRoute('/page-builder/new');
106
- if (pageBuilderNew) {
107
- return <PageBuilder collectionSlug="pages" config={config} onNavigate={navigate} />;
108
- }
109
-
110
- const pageBuilderEdit = matchRoute('/page-builder/:id');
111
- if (pageBuilderEdit?.id) {
112
- return <PageBuilder documentId={pageBuilderEdit.id} collectionSlug="pages" config={config} onNavigate={navigate} />;
113
- }
114
-
115
- for (const slug of collectionSlugs) {
116
- const newMatch = matchRoute(`/${slug}/new`);
117
- if (newMatch) {
118
- return <DocumentEdit collectionSlug={slug} config={config} />;
119
- }
120
- const editMatch = matchRoute(`/${slug}/:id`);
121
- if (editMatch?.id) {
122
- return <DocumentEdit collectionSlug={slug} documentId={editMatch.id} config={config} />;
123
- }
124
- if (matchRoute(`/${slug}`)) {
125
- return <CollectionList collectionSlug={slug} config={config} onNavigate={navigate} />;
126
- }
127
- }
128
-
129
- const newCollMatch = matchRoute('/collections/:slug/new');
130
- if (newCollMatch?.slug) {
131
- return <DocumentEdit collectionSlug={newCollMatch.slug} config={config} />;
132
- }
133
-
134
- const editCollMatch = matchRoute('/collections/:slug/:id');
135
- if (editCollMatch?.slug && editCollMatch.id) {
136
- return <DocumentEdit collectionSlug={editCollMatch.slug} documentId={editCollMatch.id} config={config} />;
159
+ return <Dashboard config={config} session={session} onNavigate={navigate} />
137
160
  }
138
161
 
139
- const collectionMatch = matchRoute('/collections/:slug');
140
- if (collectionMatch?.slug) {
141
- return <CollectionList collectionSlug={collectionMatch.slug} config={config} onNavigate={navigate} />;
142
- }
162
+ // Reserved admin routes — must match BEFORE iterating collection slugs
163
+ // so they cannot be shadowed by a consumer-defined collection (e.g.
164
+ // a collection slugged `media`, `users`, or `settings`).
143
165
 
144
166
  if (matchRoute('/media')) {
145
- return <MediaBrowser onNavigate={navigate} />;
167
+ return <MediaBrowser onNavigate={navigate} />
146
168
  }
147
169
 
148
170
  if (matchRoute('/forms/new')) {
149
- return <FormEditor onNavigate={navigate} />;
171
+ return <FormEditor onNavigate={navigate} />
150
172
  }
151
- const formEdit = matchRoute('/forms/:id/edit');
173
+ const formEdit = matchRoute('/forms/:id/edit')
152
174
  if (formEdit?.id) {
153
- return <FormEditor formId={formEdit.id} onNavigate={navigate} />;
175
+ return <FormEditor formId={formEdit.id} onNavigate={navigate} />
154
176
  }
155
- const formSubmissions = matchRoute('/forms/:id/submissions');
177
+ const formSubmissions = matchRoute('/forms/:id/submissions')
156
178
  if (formSubmissions?.id) {
157
- return <FormSubmissions formId={formSubmissions.id} onNavigate={navigate} />;
179
+ return <FormSubmissions formId={formSubmissions.id} onNavigate={navigate} />
158
180
  }
159
181
  if (matchRoute('/forms')) {
160
- return <Forms onNavigate={navigate} />;
182
+ return <Forms onNavigate={navigate} />
161
183
  }
162
184
 
163
185
  if (matchRoute('/seo/redirects')) {
164
- return <SEO onNavigate={navigate} initialTab="redirects" />;
186
+ return <SEO onNavigate={navigate} initialTab="redirects" />
165
187
  }
166
188
  if (matchRoute('/seo/canonicals')) {
167
- return <SEO onNavigate={navigate} initialTab="canonicals" />;
189
+ return <SEO onNavigate={navigate} initialTab="canonicals" />
168
190
  }
169
191
  if (matchRoute('/seo/links')) {
170
- return <SEO onNavigate={navigate} initialTab="links" />;
192
+ return <SEO onNavigate={navigate} initialTab="links" />
171
193
  }
172
194
  if (matchRoute('/seo')) {
173
- return <SEO onNavigate={navigate} initialTab="pages" />;
195
+ return <SEO onNavigate={navigate} initialTab="pages" />
174
196
  }
175
197
 
176
198
  if (matchRoute('/script-tags/new')) {
177
- return <ScriptTagEditor onNavigate={navigate} />;
199
+ return <ScriptTagEditor onNavigate={navigate} />
178
200
  }
179
- const scriptTagEdit = matchRoute('/script-tags/:id');
201
+ const scriptTagEdit = matchRoute('/script-tags/:id')
180
202
  if (scriptTagEdit?.id) {
181
- return <ScriptTagEditor tagId={scriptTagEdit.id} onNavigate={navigate} />;
203
+ return <ScriptTagEditor tagId={scriptTagEdit.id} onNavigate={navigate} />
182
204
  }
183
205
  if (matchRoute('/script-tags')) {
184
- return <ScriptTags onNavigate={navigate} />;
206
+ return <ScriptTags onNavigate={navigate} />
185
207
  }
186
208
 
187
209
  if (matchRoute('/saved-sections')) {
188
- return <SavedSections onNavigate={navigate} config={config} />;
210
+ return <SavedSections onNavigate={navigate} config={config} />
189
211
  }
190
212
 
191
213
  if (matchRoute('/page-templates')) {
192
- return <PageTemplates onNavigate={navigate} />;
214
+ return <PageTemplates onNavigate={navigate} />
193
215
  }
194
216
 
195
217
  if (matchRoute('/users')) {
196
- return <Users onNavigate={navigate} />;
218
+ return <Users onNavigate={navigate} />
197
219
  }
198
220
 
199
221
  if (matchRoute('/settings')) {
200
- return <Settings onNavigate={navigate} config={config} />;
222
+ return <Settings onNavigate={navigate} config={config} />
223
+ }
224
+
225
+ // Page Builder routes — explicit /page-builder/* takes precedence over
226
+ // the collection-derived routing below, but we ALSO route a collection
227
+ // declared as `type: 'page'` through the Page Builder by default.
228
+ const pageBuilderNew = matchRoute('/page-builder/new')
229
+ if (pageBuilderNew) {
230
+ return <PageBuilder collectionSlug="pages" config={config} onNavigate={navigate} />
231
+ }
232
+
233
+ const pageBuilderEdit = matchRoute('/page-builder/:id')
234
+ if (pageBuilderEdit?.id) {
235
+ return (
236
+ <PageBuilder
237
+ documentId={pageBuilderEdit.id}
238
+ collectionSlug="pages"
239
+ config={config}
240
+ onNavigate={navigate}
241
+ />
242
+ )
243
+ }
244
+
245
+ const RESERVED_SLUGS = new Set([
246
+ 'media',
247
+ 'forms',
248
+ 'seo',
249
+ 'script-tags',
250
+ 'saved-sections',
251
+ 'page-templates',
252
+ 'users',
253
+ 'settings',
254
+ 'page-builder',
255
+ 'collections',
256
+ ])
257
+
258
+ for (const [slug, def] of collectionMap) {
259
+ if (RESERVED_SLUGS.has(slug)) continue
260
+
261
+ const isPageType = def.type === 'page'
262
+ const newMatch = matchRoute(`/${slug}/new`)
263
+ if (newMatch) {
264
+ return isPageType ? (
265
+ <PageBuilder collectionSlug={slug} config={config} onNavigate={navigate} />
266
+ ) : (
267
+ <DocumentEdit collectionSlug={slug} config={config} />
268
+ )
269
+ }
270
+ const editMatch = matchRoute(`/${slug}/:id`)
271
+ if (editMatch?.id) {
272
+ return isPageType ? (
273
+ <PageBuilder
274
+ documentId={editMatch.id}
275
+ collectionSlug={slug}
276
+ config={config}
277
+ onNavigate={navigate}
278
+ />
279
+ ) : (
280
+ <DocumentEdit collectionSlug={slug} documentId={editMatch.id} config={config} />
281
+ )
282
+ }
283
+ if (matchRoute(`/${slug}`)) {
284
+ return <CollectionList collectionSlug={slug} config={config} onNavigate={navigate} />
285
+ }
286
+ }
287
+
288
+ const newCollMatch = matchRoute('/collections/:slug/new')
289
+ if (newCollMatch?.slug) {
290
+ const def = collectionMap.get(newCollMatch.slug)
291
+ return def?.type === 'page' ? (
292
+ <PageBuilder collectionSlug={newCollMatch.slug} config={config} onNavigate={navigate} />
293
+ ) : (
294
+ <DocumentEdit collectionSlug={newCollMatch.slug} config={config} />
295
+ )
296
+ }
297
+
298
+ const editCollMatch = matchRoute('/collections/:slug/:id')
299
+ if (editCollMatch?.slug && editCollMatch.id) {
300
+ const def = collectionMap.get(editCollMatch.slug)
301
+ return def?.type === 'page' ? (
302
+ <PageBuilder
303
+ documentId={editCollMatch.id}
304
+ collectionSlug={editCollMatch.slug}
305
+ config={config}
306
+ onNavigate={navigate}
307
+ />
308
+ ) : (
309
+ <DocumentEdit
310
+ collectionSlug={editCollMatch.slug}
311
+ documentId={editCollMatch.id}
312
+ config={config}
313
+ />
314
+ )
315
+ }
316
+
317
+ const collectionMatch = matchRoute('/collections/:slug')
318
+ if (collectionMatch?.slug) {
319
+ return (
320
+ <CollectionList
321
+ collectionSlug={collectionMatch.slug}
322
+ config={config}
323
+ onNavigate={navigate}
324
+ />
325
+ )
201
326
  }
202
327
 
203
328
  return (
@@ -211,24 +336,24 @@ function AdminShell({ config, session, basePath = '/admin', initialPath = '/', s
211
336
  Back to Dashboard
212
337
  </button>
213
338
  </div>
214
- );
339
+ )
215
340
  }
216
341
 
217
342
  return (
218
343
  <Layout config={config} session={session} currentPath={currentPath} onNavigate={navigate}>
219
- <ErrorBoundary>
220
- {renderView()}
221
- </ErrorBoundary>
344
+ <ErrorBoundary>{renderView()}</ErrorBoundary>
222
345
  {shortcutHelpOpen && <ShortcutHelp onClose={() => setShortcutHelpOpen(false)} />}
223
346
  </Layout>
224
- );
347
+ )
225
348
  }
226
349
 
227
350
  function ShortcutHelp({ onClose }: { onClose: () => void }) {
228
351
  return (
229
352
  <div
230
353
  className="fixed inset-0 z-100 flex items-center justify-center bg-black/50"
231
- onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
354
+ onClick={(e) => {
355
+ if (e.target === e.currentTarget) onClose()
356
+ }}
232
357
  >
233
358
  <div className="bg-card text-card-foreground rounded-xl shadow-2xl border border-border p-6 max-w-sm w-full mx-4">
234
359
  <h3 className="text-lg font-semibold mb-4">Keyboard Shortcuts</h3>
@@ -253,86 +378,86 @@ function ShortcutHelp({ onClose }: { onClose: () => void }) {
253
378
  </button>
254
379
  </div>
255
380
  </div>
256
- );
381
+ )
257
382
  }
258
383
 
259
384
  function useBranding(config: any) {
260
- const originalFaviconRef = useRef<string | null>(null);
261
- const originalTitleRef = useRef<string | null>(null);
262
- const injectedLinkRef = useRef<HTMLLinkElement | null>(null);
385
+ const originalFaviconRef = useRef<string | null>(null)
386
+ const originalTitleRef = useRef<string | null>(null)
387
+ const injectedLinkRef = useRef<HTMLLinkElement | null>(null)
263
388
 
264
389
  useEffect(() => {
265
- const branding = config?.admin?.branding;
266
- if (!branding) return;
390
+ const branding = config?.admin?.branding
391
+ if (!branding) return
267
392
 
268
393
  // --- Favicon ---
269
394
  if (branding.favicon) {
270
- const existing = document.querySelector<HTMLLinkElement>('link[rel="icon"]');
271
- originalFaviconRef.current = existing?.href ?? null;
395
+ const existing = document.querySelector<HTMLLinkElement>('link[rel="icon"]')
396
+ originalFaviconRef.current = existing?.href ?? null
272
397
 
273
398
  if (existing) {
274
- existing.href = branding.favicon;
399
+ existing.href = branding.favicon
275
400
  } else {
276
- const link = document.createElement('link');
277
- link.rel = 'icon';
278
- link.href = branding.favicon;
279
- document.head.appendChild(link);
280
- injectedLinkRef.current = link;
401
+ const link = document.createElement('link')
402
+ link.rel = 'icon'
403
+ link.href = branding.favicon
404
+ document.head.appendChild(link)
405
+ injectedLinkRef.current = link
281
406
  }
282
407
  }
283
408
 
284
409
  // --- Document title ---
285
- originalTitleRef.current = document.title;
410
+ originalTitleRef.current = document.title
286
411
  if (branding.title) {
287
- document.title = branding.title;
412
+ document.title = branding.title
288
413
  } else if (branding.name) {
289
- document.title = `${branding.name} Admin`;
414
+ document.title = `${branding.name} Admin`
290
415
  }
291
416
 
292
417
  // --- Primary color ---
293
- const adminEl = document.querySelector<HTMLElement>('.actuate-admin');
418
+ const adminEl = document.querySelector<HTMLElement>('.actuate-admin')
294
419
  if (branding.primaryColor && adminEl) {
295
- adminEl.style.setProperty('--primary', branding.primaryColor);
296
- adminEl.style.setProperty('--sidebar-primary', branding.primaryColor);
420
+ adminEl.style.setProperty('--primary', branding.primaryColor)
421
+ adminEl.style.setProperty('--sidebar-primary', branding.primaryColor)
297
422
  }
298
423
 
299
424
  return () => {
300
425
  // Restore favicon
301
426
  if (branding.favicon) {
302
- const link = document.querySelector<HTMLLinkElement>('link[rel="icon"]');
427
+ const link = document.querySelector<HTMLLinkElement>('link[rel="icon"]')
303
428
  if (link && originalFaviconRef.current) {
304
- link.href = originalFaviconRef.current;
429
+ link.href = originalFaviconRef.current
305
430
  }
306
431
  if (injectedLinkRef.current) {
307
- injectedLinkRef.current.remove();
308
- injectedLinkRef.current = null;
432
+ injectedLinkRef.current.remove()
433
+ injectedLinkRef.current = null
309
434
  }
310
435
  }
311
436
 
312
437
  // Restore title
313
438
  if (originalTitleRef.current !== null) {
314
- document.title = originalTitleRef.current;
439
+ document.title = originalTitleRef.current
315
440
  }
316
441
 
317
442
  // Remove primary color override
318
443
  if (branding.primaryColor && adminEl) {
319
- adminEl.style.removeProperty('--primary');
320
- adminEl.style.removeProperty('--sidebar-primary');
444
+ adminEl.style.removeProperty('--primary')
445
+ adminEl.style.removeProperty('--sidebar-primary')
321
446
  }
322
- };
323
- }, [config]);
447
+ }
448
+ }, [config])
324
449
  }
325
450
 
326
451
  function useAdminPageTitle(config: any, currentPath: string) {
327
452
  useEffect(() => {
328
- const branding = config?.admin?.branding;
329
- const baseName = branding?.title ?? (branding?.name ? `${branding.name} Admin` : null);
330
- if (!baseName) return;
331
-
332
- const segment = currentPath === '/' ? 'Dashboard' : currentPath.split('/').filter(Boolean)[0];
333
- const pageName = segment ? segment.charAt(0).toUpperCase() + segment.slice(1) : 'Dashboard';
334
- document.title = `${pageName} — ${baseName}`;
335
- }, [config, currentPath]);
453
+ const branding = config?.admin?.branding
454
+ const baseName = branding?.title ?? (branding?.name ? `${branding.name} Admin` : null)
455
+ if (!baseName) return
456
+
457
+ const segment = currentPath === '/' ? 'Dashboard' : currentPath.split('/').filter(Boolean)[0]
458
+ const pageName = segment ? segment.charAt(0).toUpperCase() + segment.slice(1) : 'Dashboard'
459
+ document.title = `${pageName} — ${baseName}`
460
+ }, [config, currentPath])
336
461
  }
337
462
 
338
463
  const ISOLATION_STYLE: React.CSSProperties = {
@@ -341,10 +466,10 @@ const ISOLATION_STYLE: React.CSSProperties = {
341
466
  zIndex: 50,
342
467
  overflow: 'auto',
343
468
  isolation: 'isolate',
344
- };
469
+ }
345
470
 
346
471
  export function AdminRoot(props: AdminRootProps) {
347
- const defaultDarkMode = props.config?.admin?.branding?.darkMode;
472
+ const defaultDarkMode = props.config?.admin?.branding?.darkMode
348
473
 
349
474
  return (
350
475
  <div style={ISOLATION_STYLE} className="actuate-admin">
@@ -354,5 +479,5 @@ export function AdminRoot(props: AdminRootProps) {
354
479
  </LocaleProvider>
355
480
  </ThemeProvider>
356
481
  </div>
357
- );
482
+ )
358
483
  }