@actuate-media/cms-admin 0.8.0 → 0.8.2

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 (433) hide show
  1. package/dist/AdminRoot.d.ts.map +1 -1
  2. package/dist/AdminRoot.js +44 -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.js.map +1 -1
  155. package/dist/lib/search.d.ts.map +1 -1
  156. package/dist/lib/search.js +3 -5
  157. package/dist/lib/search.js.map +1 -1
  158. package/dist/lib/useApiData.d.ts.map +1 -1
  159. package/dist/lib/useApiData.js.map +1 -1
  160. package/dist/lib/utils.d.ts.map +1 -1
  161. package/dist/lib/utils.js.map +1 -1
  162. package/dist/router/index.d.ts.map +1 -1
  163. package/dist/router/index.js +1 -3
  164. package/dist/router/index.js.map +1 -1
  165. package/dist/views/CollectionList.d.ts.map +1 -1
  166. package/dist/views/CollectionList.js +56 -17
  167. package/dist/views/CollectionList.js.map +1 -1
  168. package/dist/views/Dashboard.d.ts.map +1 -1
  169. package/dist/views/Dashboard.js +26 -13
  170. package/dist/views/Dashboard.js.map +1 -1
  171. package/dist/views/DocumentEdit.d.ts +1 -1
  172. package/dist/views/DocumentEdit.d.ts.map +1 -1
  173. package/dist/views/DocumentEdit.js +33 -15
  174. package/dist/views/DocumentEdit.js.map +1 -1
  175. package/dist/views/ForgotPassword.d.ts.map +1 -1
  176. package/dist/views/ForgotPassword.js.map +1 -1
  177. package/dist/views/FormEditor.d.ts.map +1 -1
  178. package/dist/views/FormEditor.js +8 -2
  179. package/dist/views/FormEditor.js.map +1 -1
  180. package/dist/views/FormSubmissions.d.ts.map +1 -1
  181. package/dist/views/FormSubmissions.js +6 -6
  182. package/dist/views/FormSubmissions.js.map +1 -1
  183. package/dist/views/Forms.d.ts.map +1 -1
  184. package/dist/views/Forms.js.map +1 -1
  185. package/dist/views/Login.d.ts.map +1 -1
  186. package/dist/views/Login.js +5 -2
  187. package/dist/views/Login.js.map +1 -1
  188. package/dist/views/MediaBrowser.d.ts.map +1 -1
  189. package/dist/views/MediaBrowser.js +39 -19
  190. package/dist/views/MediaBrowser.js.map +1 -1
  191. package/dist/views/PageEditor.d.ts.map +1 -1
  192. package/dist/views/PageEditor.js.map +1 -1
  193. package/dist/views/Pages.d.ts.map +1 -1
  194. package/dist/views/Pages.js +20 -10
  195. package/dist/views/Pages.js.map +1 -1
  196. package/dist/views/PostEditor.d.ts.map +1 -1
  197. package/dist/views/PostEditor.js.map +1 -1
  198. package/dist/views/Posts.d.ts.map +1 -1
  199. package/dist/views/Posts.js +13 -7
  200. package/dist/views/Posts.js.map +1 -1
  201. package/dist/views/Redirects.d.ts.map +1 -1
  202. package/dist/views/Redirects.js +17 -5
  203. package/dist/views/Redirects.js.map +1 -1
  204. package/dist/views/ResetPassword.d.ts.map +1 -1
  205. package/dist/views/ResetPassword.js.map +1 -1
  206. package/dist/views/SEO.d.ts.map +1 -1
  207. package/dist/views/SEO.js +40 -17
  208. package/dist/views/SEO.js.map +1 -1
  209. package/dist/views/ScriptTagEditor.d.ts.map +1 -1
  210. package/dist/views/ScriptTagEditor.js +2 -1
  211. package/dist/views/ScriptTagEditor.js.map +1 -1
  212. package/dist/views/ScriptTags.d.ts.map +1 -1
  213. package/dist/views/ScriptTags.js.map +1 -1
  214. package/dist/views/Settings.d.ts.map +1 -1
  215. package/dist/views/Settings.js +38 -11
  216. package/dist/views/Settings.js.map +1 -1
  217. package/dist/views/SetupWizard.d.ts.map +1 -1
  218. package/dist/views/SetupWizard.js.map +1 -1
  219. package/dist/views/Users.d.ts.map +1 -1
  220. package/dist/views/Users.js +5 -3
  221. package/dist/views/Users.js.map +1 -1
  222. package/dist/views/page-builder/AIBlockAssist.d.ts.map +1 -1
  223. package/dist/views/page-builder/AIBlockAssist.js +1 -1
  224. package/dist/views/page-builder/AIBlockAssist.js.map +1 -1
  225. package/dist/views/page-builder/AIGenerateDialog.d.ts.map +1 -1
  226. package/dist/views/page-builder/AIGenerateDialog.js +4 -1
  227. package/dist/views/page-builder/AIGenerateDialog.js.map +1 -1
  228. package/dist/views/page-builder/BlockEditor.d.ts.map +1 -1
  229. package/dist/views/page-builder/BlockEditor.js +1 -1
  230. package/dist/views/page-builder/BlockEditor.js.map +1 -1
  231. package/dist/views/page-builder/BlockPicker.d.ts.map +1 -1
  232. package/dist/views/page-builder/BlockPicker.js.map +1 -1
  233. package/dist/views/page-builder/BottomBar.d.ts.map +1 -1
  234. package/dist/views/page-builder/BottomBar.js.map +1 -1
  235. package/dist/views/page-builder/BuilderToolbar.d.ts.map +1 -1
  236. package/dist/views/page-builder/BuilderToolbar.js.map +1 -1
  237. package/dist/views/page-builder/ContextPanel.d.ts.map +1 -1
  238. package/dist/views/page-builder/ContextPanel.js +4 -1
  239. package/dist/views/page-builder/ContextPanel.js.map +1 -1
  240. package/dist/views/page-builder/DesignScore.d.ts.map +1 -1
  241. package/dist/views/page-builder/DesignScore.js.map +1 -1
  242. package/dist/views/page-builder/NodeSettings.d.ts.map +1 -1
  243. package/dist/views/page-builder/NodeSettings.js +1 -1
  244. package/dist/views/page-builder/NodeSettings.js.map +1 -1
  245. package/dist/views/page-builder/PageBuilder.d.ts +1 -1
  246. package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
  247. package/dist/views/page-builder/PageBuilder.js +4 -2
  248. package/dist/views/page-builder/PageBuilder.js.map +1 -1
  249. package/dist/views/page-builder/PageSettings.d.ts.map +1 -1
  250. package/dist/views/page-builder/PageSettings.js.map +1 -1
  251. package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
  252. package/dist/views/page-builder/PageTemplates.js.map +1 -1
  253. package/dist/views/page-builder/SEOPanel.d.ts.map +1 -1
  254. package/dist/views/page-builder/SEOPanel.js +1 -3
  255. package/dist/views/page-builder/SEOPanel.js.map +1 -1
  256. package/dist/views/page-builder/SavedSections.d.ts.map +1 -1
  257. package/dist/views/page-builder/SavedSections.js +3 -7
  258. package/dist/views/page-builder/SavedSections.js.map +1 -1
  259. package/dist/views/page-builder/TemplatePicker.d.ts.map +1 -1
  260. package/dist/views/page-builder/TemplatePicker.js.map +1 -1
  261. package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +1 -1
  262. package/dist/views/page-builder/block-renderers/CTAPreview.js +1 -1
  263. package/dist/views/page-builder/block-renderers/CTAPreview.js.map +1 -1
  264. package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +1 -1
  265. package/dist/views/page-builder/block-renderers/CardsPreview.js +1 -1
  266. package/dist/views/page-builder/block-renderers/CardsPreview.js.map +1 -1
  267. package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +1 -1
  268. package/dist/views/page-builder/block-renderers/CodePreview.js +1 -5
  269. package/dist/views/page-builder/block-renderers/CodePreview.js.map +1 -1
  270. package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +1 -1
  271. package/dist/views/page-builder/block-renderers/FAQPreview.js +4 -1
  272. package/dist/views/page-builder/block-renderers/FAQPreview.js.map +1 -1
  273. package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +1 -1
  274. package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +1 -1
  275. package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +1 -1
  276. package/dist/views/page-builder/block-renderers/FormPreview.js +2 -2
  277. package/dist/views/page-builder/block-renderers/FormPreview.js.map +1 -1
  278. package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +1 -1
  279. package/dist/views/page-builder/block-renderers/GalleryPreview.js +1 -3
  280. package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +1 -1
  281. package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +1 -1
  282. package/dist/views/page-builder/block-renderers/HeroPreview.js.map +1 -1
  283. package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +1 -1
  284. package/dist/views/page-builder/block-renderers/ImagePreview.js.map +1 -1
  285. package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +1 -1
  286. package/dist/views/page-builder/block-renderers/TextPreview.js +2 -6
  287. package/dist/views/page-builder/block-renderers/TextPreview.js.map +1 -1
  288. package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +1 -1
  289. package/dist/views/page-builder/block-renderers/VideoPreview.js +2 -5
  290. package/dist/views/page-builder/block-renderers/VideoPreview.js.map +1 -1
  291. package/dist/views/page-builder/block-renderers/index.d.ts.map +1 -1
  292. package/dist/views/page-builder/block-renderers/index.js.map +1 -1
  293. package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +1 -1
  294. package/dist/views/page-builder/canvas/BlockRenderer.js +1 -5
  295. package/dist/views/page-builder/canvas/BlockRenderer.js.map +1 -1
  296. package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +1 -1
  297. package/dist/views/page-builder/canvas/BuilderCanvas.js.map +1 -1
  298. package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +1 -1
  299. package/dist/views/page-builder/canvas/ColumnRenderer.js +1 -5
  300. package/dist/views/page-builder/canvas/ColumnRenderer.js.map +1 -1
  301. package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +1 -1
  302. package/dist/views/page-builder/canvas/ContainerRenderer.js +1 -5
  303. package/dist/views/page-builder/canvas/ContainerRenderer.js.map +1 -1
  304. package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +1 -1
  305. package/dist/views/page-builder/canvas/RowRenderer.js +1 -5
  306. package/dist/views/page-builder/canvas/RowRenderer.js.map +1 -1
  307. package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +1 -1
  308. package/dist/views/page-builder/canvas/SectionRenderer.js +1 -5
  309. package/dist/views/page-builder/canvas/SectionRenderer.js.map +1 -1
  310. package/dist/views/page-builder/canvas/index.d.ts.map +1 -1
  311. package/dist/views/page-builder/canvas/index.js.map +1 -1
  312. package/package.json +2 -2
  313. package/src/AdminRoot.tsx +263 -191
  314. package/src/__tests__/lib/search.test.ts +60 -69
  315. package/src/__tests__/lib/utils.test.ts +12 -12
  316. package/src/__tests__/router/match-route.test.ts +24 -26
  317. package/src/__tests__/router/strip-base.test.ts +15 -15
  318. package/src/components/Breadcrumbs.tsx +27 -24
  319. package/src/components/CommandPalette.tsx +115 -99
  320. package/src/components/ContentOverviewChart.tsx +19 -14
  321. package/src/components/ErrorBoundary.tsx +13 -13
  322. package/src/components/FocalPointPicker.tsx +31 -20
  323. package/src/components/FolderTree.tsx +172 -139
  324. package/src/components/LivePreview.tsx +68 -41
  325. package/src/components/LocaleProvider.tsx +26 -20
  326. package/src/components/LocaleSwitcher.tsx +9 -11
  327. package/src/components/MediaPickerModal.tsx +46 -45
  328. package/src/components/PresenceIndicator.tsx +30 -27
  329. package/src/components/SEOPanel.tsx +378 -228
  330. package/src/components/SEOPerformance.tsx +52 -30
  331. package/src/components/ThemeProvider.tsx +46 -46
  332. package/src/components/TipTapEditor.tsx +60 -64
  333. package/src/components/VersionHistory.tsx +63 -52
  334. package/src/components/ui/Avatar.tsx +8 -8
  335. package/src/components/ui/Badge.tsx +7 -5
  336. package/src/components/ui/Button.tsx +24 -13
  337. package/src/components/ui/CommandPalette.tsx +56 -42
  338. package/src/components/ui/ConfirmDialog.tsx +14 -14
  339. package/src/components/ui/DataTable.tsx +37 -39
  340. package/src/components/ui/EmptyState.tsx +9 -11
  341. package/src/components/ui/Modal.tsx +21 -15
  342. package/src/components/ui/Pagination.tsx +34 -19
  343. package/src/components/ui/SearchInput.tsx +17 -7
  344. package/src/components/ui/Skeleton.tsx +7 -7
  345. package/src/components/ui/Toast.tsx +29 -22
  346. package/src/components/ui/index.ts +24 -24
  347. package/src/fields/ArrayField.tsx +43 -25
  348. package/src/fields/BlockBuilderField.tsx +80 -99
  349. package/src/fields/DateField.tsx +20 -12
  350. package/src/fields/FieldRenderer.tsx +34 -34
  351. package/src/fields/GroupField.tsx +8 -10
  352. package/src/fields/MediaField.tsx +8 -10
  353. package/src/fields/NavBuilderField.tsx +24 -25
  354. package/src/fields/NumberField.tsx +21 -14
  355. package/src/fields/RelationshipField.tsx +105 -91
  356. package/src/fields/RichTextField.tsx +16 -12
  357. package/src/fields/SelectField.tsx +42 -34
  358. package/src/fields/SlugField.tsx +29 -17
  359. package/src/fields/TextField.tsx +24 -16
  360. package/src/fields/ToggleField.tsx +7 -9
  361. package/src/fields/block-types.ts +50 -24
  362. package/src/fields/index.ts +17 -17
  363. package/src/hooks/useBuilderState.ts +260 -221
  364. package/src/hooks/useContentLock.ts +23 -20
  365. package/src/hooks/useDebounce.ts +7 -7
  366. package/src/hooks/useKeyboardShortcuts.ts +16 -16
  367. package/src/index.ts +69 -58
  368. package/src/layout/Header.tsx +21 -20
  369. package/src/layout/Layout.tsx +22 -24
  370. package/src/layout/Sidebar.tsx +107 -72
  371. package/src/lib/api.ts +34 -34
  372. package/src/lib/search.ts +30 -34
  373. package/src/lib/useApiData.ts +65 -62
  374. package/src/lib/utils.ts +3 -3
  375. package/src/router/index.ts +33 -35
  376. package/src/styles/build-input.css +2 -2
  377. package/src/styles/tailwind.css +1 -1
  378. package/src/styles/theme.css +26 -2
  379. package/src/views/CollectionList.tsx +275 -121
  380. package/src/views/Dashboard.tsx +164 -117
  381. package/src/views/DocumentEdit.tsx +298 -253
  382. package/src/views/ForgotPassword.tsx +27 -23
  383. package/src/views/FormEditor.tsx +165 -99
  384. package/src/views/FormSubmissions.tsx +261 -117
  385. package/src/views/Forms.tsx +56 -26
  386. package/src/views/Login.tsx +107 -84
  387. package/src/views/MediaBrowser.tsx +717 -523
  388. package/src/views/PageEditor.tsx +44 -46
  389. package/src/views/Pages.tsx +312 -149
  390. package/src/views/PostEditor.tsx +57 -51
  391. package/src/views/Posts.tsx +206 -74
  392. package/src/views/Redirects.tsx +173 -117
  393. package/src/views/ResetPassword.tsx +43 -32
  394. package/src/views/SEO.tsx +607 -160
  395. package/src/views/ScriptTagEditor.tsx +69 -69
  396. package/src/views/ScriptTags.tsx +54 -42
  397. package/src/views/Settings.tsx +430 -220
  398. package/src/views/SetupWizard.tsx +69 -46
  399. package/src/views/Users.tsx +154 -120
  400. package/src/views/page-builder/AIBlockAssist.tsx +21 -25
  401. package/src/views/page-builder/AIGenerateDialog.tsx +134 -127
  402. package/src/views/page-builder/BlockEditor.tsx +94 -96
  403. package/src/views/page-builder/BlockPicker.tsx +73 -88
  404. package/src/views/page-builder/BottomBar.tsx +15 -11
  405. package/src/views/page-builder/BuilderToolbar.tsx +32 -29
  406. package/src/views/page-builder/ContextPanel.tsx +57 -57
  407. package/src/views/page-builder/DesignScore.tsx +52 -59
  408. package/src/views/page-builder/NodeSettings.tsx +59 -59
  409. package/src/views/page-builder/PageBuilder.tsx +156 -155
  410. package/src/views/page-builder/PageSettings.tsx +16 -15
  411. package/src/views/page-builder/PageTemplates.tsx +23 -17
  412. package/src/views/page-builder/SEOPanel.tsx +90 -111
  413. package/src/views/page-builder/SavedSections.tsx +99 -105
  414. package/src/views/page-builder/TemplatePicker.tsx +44 -48
  415. package/src/views/page-builder/block-renderers/CTAPreview.tsx +11 -13
  416. package/src/views/page-builder/block-renderers/CardsPreview.tsx +13 -15
  417. package/src/views/page-builder/block-renderers/CodePreview.tsx +16 -16
  418. package/src/views/page-builder/block-renderers/FAQPreview.tsx +20 -23
  419. package/src/views/page-builder/block-renderers/FallbackPreview.tsx +5 -5
  420. package/src/views/page-builder/block-renderers/FormPreview.tsx +9 -13
  421. package/src/views/page-builder/block-renderers/GalleryPreview.tsx +22 -28
  422. package/src/views/page-builder/block-renderers/HeroPreview.tsx +17 -30
  423. package/src/views/page-builder/block-renderers/ImagePreview.tsx +12 -12
  424. package/src/views/page-builder/block-renderers/TextPreview.tsx +22 -22
  425. package/src/views/page-builder/block-renderers/VideoPreview.tsx +13 -18
  426. package/src/views/page-builder/block-renderers/index.ts +17 -17
  427. package/src/views/page-builder/canvas/BlockRenderer.tsx +19 -23
  428. package/src/views/page-builder/canvas/BuilderCanvas.tsx +17 -20
  429. package/src/views/page-builder/canvas/ColumnRenderer.tsx +22 -26
  430. package/src/views/page-builder/canvas/ContainerRenderer.tsx +20 -24
  431. package/src/views/page-builder/canvas/RowRenderer.tsx +19 -23
  432. package/src/views/page-builder/canvas/SectionRenderer.tsx +30 -34
  433. package/src/views/page-builder/canvas/index.ts +2 -2
@@ -1,122 +1,158 @@
1
- 'use client';
1
+ 'use client'
2
2
 
3
- import * as Tabs from '@radix-ui/react-tabs';
4
- import { Bot, Eye, EyeOff, Image, FileCode2, BookOpen, Sparkles, MessageSquare, Languages, Loader2, AlertTriangle, Download, CheckCircle2, ArrowUpCircle, ExternalLink, RefreshCw, GitPullRequest, Layers } from 'lucide-react';
5
- import { useState, useEffect } from 'react';
6
- import { toast } from 'sonner';
7
- import { useApiData } from '../lib/useApiData.js';
8
- import { cmsApi } from '../lib/api.js';
9
- import { useTheme } from '../components/ThemeProvider.js';
10
- import { RelationshipField } from '../fields/RelationshipField.js';
3
+ import * as Tabs from '@radix-ui/react-tabs'
4
+ import {
5
+ Bot,
6
+ Eye,
7
+ EyeOff,
8
+ Image,
9
+ FileCode2,
10
+ BookOpen,
11
+ Sparkles,
12
+ MessageSquare,
13
+ Languages,
14
+ Loader2,
15
+ AlertTriangle,
16
+ Download,
17
+ CheckCircle2,
18
+ ArrowUpCircle,
19
+ ExternalLink,
20
+ RefreshCw,
21
+ GitPullRequest,
22
+ Layers,
23
+ } from 'lucide-react'
24
+ import { useState, useEffect } from 'react'
25
+ import { toast } from 'sonner'
26
+ import { useApiData } from '../lib/useApiData.js'
27
+ import { cmsApi } from '../lib/api.js'
28
+ import { useTheme } from '../components/ThemeProvider.js'
29
+ import { RelationshipField } from '../fields/RelationshipField.js'
11
30
 
12
31
  export interface SettingsProps {
13
- onNavigate?: (path: string) => void;
14
- config?: any;
32
+ onNavigate?: (path: string) => void
33
+ config?: any
15
34
  }
16
35
 
17
36
  export function Settings({ config, ..._props }: SettingsProps = {}) {
18
- const { data, loading, error, refetch } = useApiData<any>('/globals/settings');
19
-
20
- const [siteTitle, setSiteTitle] = useState('My CMS');
21
- const [tagline, setTagline] = useState('A lightweight content management system');
22
- const [siteUrl, setSiteUrl] = useState('https://example.com');
23
- const [language, setLanguage] = useState('en');
24
- const [timezone, setTimezone] = useState('UTC');
25
- const [defaultNoIndex, setDefaultNoIndex] = useState(false);
26
- const [defaultNoFollow, setDefaultNoFollow] = useState(false);
27
- const [noIndexNonProduction, setNoIndexNonProduction] = useState(false);
28
- const [twoFactorEnabled, setTwoFactorEnabled] = useState(false);
29
- const [sessionTimeout, setSessionTimeout] = useState(false);
30
- const [ipWhitelist, setIpWhitelist] = useState(false);
31
- const [activeTab, setActiveTab] = useState('general');
32
- const [saving, setSaving] = useState(false);
37
+ const { data, loading, error, refetch } = useApiData<any>('/globals/settings')
38
+
39
+ const [siteTitle, setSiteTitle] = useState('My CMS')
40
+ const [tagline, setTagline] = useState('A lightweight content management system')
41
+ const [siteUrl, setSiteUrl] = useState('https://example.com')
42
+ const [language, setLanguage] = useState('en')
43
+ const [timezone, setTimezone] = useState('UTC')
44
+ const [defaultNoIndex, setDefaultNoIndex] = useState(false)
45
+ const [defaultNoFollow, setDefaultNoFollow] = useState(false)
46
+ const [noIndexNonProduction, setNoIndexNonProduction] = useState(false)
47
+ const [twoFactorEnabled, setTwoFactorEnabled] = useState(false)
48
+ const [sessionTimeout, setSessionTimeout] = useState(false)
49
+ const [ipWhitelist, setIpWhitelist] = useState(false)
50
+ const [activeTab, setActiveTab] = useState('general')
51
+ const [saving, setSaving] = useState(false)
33
52
 
34
53
  // Layout defaults
35
- const [defaultLayout, setDefaultLayout] = useState<Record<string, string>>({});
36
- const layoutConfig = config?.layout;
37
- const layoutRegions: Array<{ name: string; collection: string; label: string }> = layoutConfig?.regions
38
- ? Object.entries(layoutConfig.regions).map(([name, region]: [string, any]) => ({
39
- name,
40
- collection: region.collection,
41
- label: region.label ?? name.charAt(0).toUpperCase() + name.slice(1),
42
- }))
43
- : [];
44
- const hasLayoutRegions = layoutRegions.length > 0;
54
+ const [defaultLayout, setDefaultLayout] = useState<Record<string, string>>({})
55
+ const layoutConfig = config?.layout
56
+ const layoutRegions: Array<{ name: string; collection: string; label: string }> =
57
+ layoutConfig?.regions
58
+ ? Object.entries(layoutConfig.regions).map(([name, region]: [string, any]) => ({
59
+ name,
60
+ collection: region.collection,
61
+ label: region.label ?? name.charAt(0).toUpperCase() + name.slice(1),
62
+ }))
63
+ : []
64
+ const hasLayoutRegions = layoutRegions.length > 0
45
65
 
46
66
  // AI settings
47
- const [aiProvider, setAiProvider] = useState('anthropic');
48
- const [aiApiKey, setAiApiKey] = useState('');
49
- const [showApiKey, setShowApiKey] = useState(false);
50
- const [aiAltTags, setAiAltTags] = useState(true);
51
- const [aiMediaCategorize, setAiMediaCategorize] = useState(false);
52
- const [aiMetaDescriptions, setAiMetaDescriptions] = useState(true);
53
- const [aiReadability, setAiReadability] = useState(true);
54
- const [aiSchema, setAiSchema] = useState(true);
55
- const [aiBrandVoice, setAiBrandVoice] = useState(false);
56
- const [aiWritingAssistant, setAiWritingAssistant] = useState(true);
57
- const [aiContentScoring, setAiContentScoring] = useState(true);
58
- const [aiTranslation, setAiTranslation] = useState(false);
67
+ const [aiProvider, setAiProvider] = useState('anthropic')
68
+ const [aiApiKey, setAiApiKey] = useState('')
69
+ const [showApiKey, setShowApiKey] = useState(false)
70
+ const [aiAltTags, setAiAltTags] = useState(true)
71
+ const [aiMediaCategorize, setAiMediaCategorize] = useState(false)
72
+ const [aiMetaDescriptions, setAiMetaDescriptions] = useState(true)
73
+ const [aiReadability, setAiReadability] = useState(true)
74
+ const [aiSchema, setAiSchema] = useState(true)
75
+ const [aiBrandVoice, setAiBrandVoice] = useState(false)
76
+ const [aiWritingAssistant, setAiWritingAssistant] = useState(true)
77
+ const [aiContentScoring, setAiContentScoring] = useState(true)
78
+ const [aiTranslation, setAiTranslation] = useState(false)
59
79
 
60
80
  useEffect(() => {
61
81
  if (data) {
62
- setSiteTitle(data.siteTitle ?? 'My CMS');
63
- setTagline(data.tagline ?? '');
64
- setSiteUrl(data.siteUrl ?? '');
65
- setLanguage(data.language ?? 'en');
66
- setTimezone(data.timezone ?? 'UTC');
67
- setDefaultNoIndex(data.defaultNoIndex ?? false);
68
- setDefaultNoFollow(data.defaultNoFollow ?? false);
69
- setNoIndexNonProduction(data.noIndexNonProduction ?? false);
70
- setTwoFactorEnabled(data.twoFactorEnabled ?? false);
71
- setSessionTimeout(data.sessionTimeout ?? false);
72
- setIpWhitelist(data.ipWhitelist ?? false);
73
- setAiProvider(data.aiProvider ?? 'anthropic');
74
- setAiAltTags(data.aiAltTags ?? true);
75
- setAiMediaCategorize(data.aiMediaCategorize ?? false);
76
- setAiMetaDescriptions(data.aiMetaDescriptions ?? true);
77
- setAiReadability(data.aiReadability ?? true);
78
- setAiSchema(data.aiSchema ?? true);
79
- setAiBrandVoice(data.aiBrandVoice ?? false);
80
- setAiWritingAssistant(data.aiWritingAssistant ?? true);
81
- setAiContentScoring(data.aiContentScoring ?? true);
82
- setAiTranslation(data.aiTranslation ?? false);
82
+ setSiteTitle(data.siteTitle ?? 'My CMS')
83
+ setTagline(data.tagline ?? '')
84
+ setSiteUrl(data.siteUrl ?? '')
85
+ setLanguage(data.language ?? 'en')
86
+ setTimezone(data.timezone ?? 'UTC')
87
+ setDefaultNoIndex(data.defaultNoIndex ?? false)
88
+ setDefaultNoFollow(data.defaultNoFollow ?? false)
89
+ setNoIndexNonProduction(data.noIndexNonProduction ?? false)
90
+ setTwoFactorEnabled(data.twoFactorEnabled ?? false)
91
+ setSessionTimeout(data.sessionTimeout ?? false)
92
+ setIpWhitelist(data.ipWhitelist ?? false)
93
+ setAiProvider(data.aiProvider ?? 'anthropic')
94
+ setAiAltTags(data.aiAltTags ?? true)
95
+ setAiMediaCategorize(data.aiMediaCategorize ?? false)
96
+ setAiMetaDescriptions(data.aiMetaDescriptions ?? true)
97
+ setAiReadability(data.aiReadability ?? true)
98
+ setAiSchema(data.aiSchema ?? true)
99
+ setAiBrandVoice(data.aiBrandVoice ?? false)
100
+ setAiWritingAssistant(data.aiWritingAssistant ?? true)
101
+ setAiContentScoring(data.aiContentScoring ?? true)
102
+ setAiTranslation(data.aiTranslation ?? false)
83
103
  if (data.defaultLayout && typeof data.defaultLayout === 'object') {
84
- setDefaultLayout(data.defaultLayout);
104
+ setDefaultLayout(data.defaultLayout)
85
105
  }
86
106
  }
87
- }, [data]);
107
+ }, [data])
88
108
 
89
109
  const handleSave = async () => {
90
- setSaving(true);
91
- const layoutPayload = Object.keys(defaultLayout).length > 0 ? { defaultLayout } : {};
110
+ setSaving(true)
111
+ const layoutPayload = Object.keys(defaultLayout).length > 0 ? { defaultLayout } : {}
92
112
  const res = await cmsApi('/globals/settings', {
93
113
  method: 'PUT',
94
114
  body: JSON.stringify({
95
- siteTitle, tagline, siteUrl, language, timezone,
96
- defaultNoIndex, defaultNoFollow, noIndexNonProduction,
97
- twoFactorEnabled, sessionTimeout, ipWhitelist,
98
- aiProvider, aiAltTags, aiMediaCategorize, aiMetaDescriptions,
99
- aiReadability, aiSchema, aiBrandVoice, aiWritingAssistant,
100
- aiContentScoring, aiTranslation,
115
+ siteTitle,
116
+ tagline,
117
+ siteUrl,
118
+ language,
119
+ timezone,
120
+ defaultNoIndex,
121
+ defaultNoFollow,
122
+ noIndexNonProduction,
123
+ twoFactorEnabled,
124
+ sessionTimeout,
125
+ ipWhitelist,
126
+ aiProvider,
127
+ aiAltTags,
128
+ aiMediaCategorize,
129
+ aiMetaDescriptions,
130
+ aiReadability,
131
+ aiSchema,
132
+ aiBrandVoice,
133
+ aiWritingAssistant,
134
+ aiContentScoring,
135
+ aiTranslation,
101
136
  ...layoutPayload,
102
137
  }),
103
- });
104
- setSaving(false);
138
+ })
139
+ setSaving(false)
105
140
  if (res.error) {
106
- toast.error(res.error);
141
+ toast.error(res.error)
107
142
  } else {
108
- toast.success('Settings saved successfully!');
143
+ toast.success('Settings saved successfully!')
109
144
  }
110
- };
145
+ }
111
146
 
112
- const tabTriggerClass = "px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:text-gray-900 data-[state=active]:border-b-2 data-[state=active]:border-blue-600 data-[state=active]:text-blue-600";
147
+ const tabTriggerClass =
148
+ 'px-4 py-2 text-sm font-medium text-gray-600 transition-colors hover:text-gray-900 data-[state=active]:border-b-2 data-[state=active]:border-blue-600 data-[state=active]:text-blue-600'
113
149
 
114
150
  if (loading) {
115
151
  return (
116
152
  <div className="p-3 pr-6 sm:p-4 sm:pr-8 flex items-center justify-center h-64">
117
153
  <Loader2 className="w-6 h-6 animate-spin text-blue-600" />
118
154
  </div>
119
- );
155
+ )
120
156
  }
121
157
 
122
158
  return (
@@ -125,7 +161,12 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
125
161
  <div className="mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
126
162
  <AlertTriangle className="w-5 h-5 text-red-600 shrink-0" />
127
163
  <span className="text-sm text-red-800 flex-1">{error}</span>
128
- <button onClick={refetch} className="px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors">Retry</button>
164
+ <button
165
+ onClick={refetch}
166
+ className="px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors"
167
+ >
168
+ Retry
169
+ </button>
129
170
  </div>
130
171
  )}
131
172
 
@@ -136,8 +177,12 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
136
177
 
137
178
  <Tabs.Root value={activeTab} onValueChange={setActiveTab}>
138
179
  <Tabs.List className="mb-4 flex gap-1 border-b border-gray-200 overflow-x-auto">
139
- <Tabs.Trigger value="general" className={tabTriggerClass}>General</Tabs.Trigger>
140
- <Tabs.Trigger value="appearance" className={tabTriggerClass}>Appearance</Tabs.Trigger>
180
+ <Tabs.Trigger value="general" className={tabTriggerClass}>
181
+ General
182
+ </Tabs.Trigger>
183
+ <Tabs.Trigger value="appearance" className={tabTriggerClass}>
184
+ Appearance
185
+ </Tabs.Trigger>
141
186
  {hasLayoutRegions && (
142
187
  <Tabs.Trigger value="layout" className={tabTriggerClass}>
143
188
  <span className="flex items-center gap-1.5">
@@ -146,14 +191,18 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
146
191
  </span>
147
192
  </Tabs.Trigger>
148
193
  )}
149
- <Tabs.Trigger value="security" className={tabTriggerClass}>Security</Tabs.Trigger>
194
+ <Tabs.Trigger value="security" className={tabTriggerClass}>
195
+ Security
196
+ </Tabs.Trigger>
150
197
  <Tabs.Trigger value="ai" className={tabTriggerClass}>
151
198
  <span className="flex items-center gap-1.5">
152
199
  <Bot className="w-4 h-4" />
153
200
  AI
154
201
  </span>
155
202
  </Tabs.Trigger>
156
- <Tabs.Trigger value="integrations" className={tabTriggerClass}>Integrations</Tabs.Trigger>
203
+ <Tabs.Trigger value="integrations" className={tabTriggerClass}>
204
+ Integrations
205
+ </Tabs.Trigger>
157
206
  <Tabs.Trigger value="updates" className={tabTriggerClass}>
158
207
  <span className="flex items-center gap-1.5">
159
208
  <Download className="w-4 h-4" />
@@ -168,15 +217,30 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
168
217
  <div className="space-y-4">
169
218
  <div>
170
219
  <label className="mb-1 block text-sm font-medium text-gray-700">Site Title</label>
171
- <input type="text" value={siteTitle} onChange={(e) => setSiteTitle(e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" />
220
+ <input
221
+ type="text"
222
+ value={siteTitle}
223
+ onChange={(e) => setSiteTitle(e.target.value)}
224
+ className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
225
+ />
172
226
  </div>
173
227
  <div>
174
228
  <label className="mb-1 block text-sm font-medium text-gray-700">Tagline</label>
175
- <input type="text" value={tagline} onChange={(e) => setTagline(e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" />
229
+ <input
230
+ type="text"
231
+ value={tagline}
232
+ onChange={(e) => setTagline(e.target.value)}
233
+ className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
234
+ />
176
235
  </div>
177
236
  <div>
178
237
  <label className="mb-1 block text-sm font-medium text-gray-700">Site URL</label>
179
- <input type="url" value={siteUrl} onChange={(e) => setSiteUrl(e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" />
238
+ <input
239
+ type="url"
240
+ value={siteUrl}
241
+ onChange={(e) => setSiteUrl(e.target.value)}
242
+ className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
243
+ />
180
244
  </div>
181
245
  </div>
182
246
  </div>
@@ -185,7 +249,11 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
185
249
  <div className="space-y-4">
186
250
  <div>
187
251
  <label className="mb-1 block text-sm font-medium text-gray-700">Language</label>
188
- <select value={language} onChange={(e) => setLanguage(e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
252
+ <select
253
+ value={language}
254
+ onChange={(e) => setLanguage(e.target.value)}
255
+ className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
256
+ >
189
257
  <option value="en">English</option>
190
258
  <option value="es">Spanish</option>
191
259
  <option value="fr">French</option>
@@ -194,7 +262,11 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
194
262
  </div>
195
263
  <div>
196
264
  <label className="mb-1 block text-sm font-medium text-gray-700">Timezone</label>
197
- <select value={timezone} onChange={(e) => setTimezone(e.target.value)} className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
265
+ <select
266
+ value={timezone}
267
+ onChange={(e) => setTimezone(e.target.value)}
268
+ className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
269
+ >
198
270
  <option value="UTC">UTC</option>
199
271
  <option value="America/New_York">Eastern Time</option>
200
272
  <option value="America/Chicago">Central Time</option>
@@ -207,7 +279,8 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
207
279
  <div className="rounded-lg border border-border bg-card p-4">
208
280
  <h3 className="mb-1 text-sm font-medium text-foreground">SEO & Robots Defaults</h3>
209
281
  <p className="mb-4 text-xs text-muted-foreground">
210
- Set the site-wide default for search engine indexing. Individual pages can inherit or override these rules in their SEO panel.
282
+ Set the site-wide default for search engine indexing. Individual pages can inherit or
283
+ override these rules in their SEO panel.
211
284
  </p>
212
285
  <div className="space-y-4">
213
286
  <ToggleSetting
@@ -241,8 +314,14 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
241
314
  <ThemeSelect />
242
315
  </div>
243
316
  <div>
244
- <label className="mb-1 block text-sm font-medium text-gray-700">Primary Color</label>
245
- <input type="color" defaultValue="#3b82f6" className="h-10 w-full rounded-lg border border-gray-300 px-3 py-2" />
317
+ <label className="mb-1 block text-sm font-medium text-gray-700">
318
+ Primary Color
319
+ </label>
320
+ <input
321
+ type="color"
322
+ defaultValue="#3b82f6"
323
+ className="h-10 w-full rounded-lg border border-gray-300 px-3 py-2"
324
+ />
246
325
  </div>
247
326
  </div>
248
327
  </div>
@@ -253,7 +332,8 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
253
332
  <div className="rounded-lg border border-gray-200 bg-white p-4">
254
333
  <h3 className="mb-1 text-sm font-semibold text-gray-900">Default Layout Variants</h3>
255
334
  <p className="mb-4 text-xs text-gray-500">
256
- Select the default header, footer, and other layout variants used site-wide. Pages can override these individually or inherit from parent pages.
335
+ Select the default header, footer, and other layout variants used site-wide. Pages
336
+ can override these individually or inherit from parent pages.
257
337
  </p>
258
338
  <div className="space-y-4">
259
339
  {layoutRegions.map((region) => (
@@ -263,14 +343,14 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
263
343
  value={defaultLayout[region.name] ?? ''}
264
344
  onChange={(val) => {
265
345
  setDefaultLayout((prev) => {
266
- const next = { ...prev };
346
+ const next = { ...prev }
267
347
  if (val && typeof val === 'string') {
268
- next[region.name] = val;
348
+ next[region.name] = val
269
349
  } else {
270
- delete next[region.name];
350
+ delete next[region.name]
271
351
  }
272
- return next;
273
- });
352
+ return next
353
+ })
274
354
  }}
275
355
  relationTo={region.collection}
276
356
  helpText={`The ${region.label.toLowerCase()} variant used when no page in the ancestor chain specifies one`}
@@ -279,19 +359,40 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
279
359
  </div>
280
360
  </div>
281
361
  <div className="rounded-lg border border-gray-200 bg-gray-50 p-4">
282
- <h3 className="text-sm font-semibold text-gray-700 mb-2">How Layout Inheritance Works</h3>
362
+ <h3 className="text-sm font-semibold text-gray-700 mb-2">
363
+ How Layout Inheritance Works
364
+ </h3>
283
365
  <ul className="space-y-1.5 text-xs text-gray-600">
284
366
  <li className="flex items-start gap-2">
285
- <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">1</span>
286
- <span>Each page can assign specific layout variants (header, footer, etc.) from the document editor.</span>
367
+ <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">
368
+ 1
369
+ </span>
370
+ <span>
371
+ Each page can assign specific layout variants (header, footer, etc.) from the
372
+ document editor.
373
+ </span>
287
374
  </li>
288
375
  <li className="flex items-start gap-2">
289
- <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">2</span>
290
- <span>Child pages automatically inherit their parent&apos;s layout. For example, <code className="font-mono bg-gray-200 px-1 rounded">/hampton-roads/thank-you</code> inherits from <code className="font-mono bg-gray-200 px-1 rounded">/hampton-roads</code>.</span>
376
+ <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">
377
+ 2
378
+ </span>
379
+ <span>
380
+ Child pages automatically inherit their parent&apos;s layout. For example,{' '}
381
+ <code className="font-mono bg-gray-200 px-1 rounded">
382
+ /hampton-roads/thank-you
383
+ </code>{' '}
384
+ inherits from{' '}
385
+ <code className="font-mono bg-gray-200 px-1 rounded">/hampton-roads</code>.
386
+ </span>
291
387
  </li>
292
388
  <li className="flex items-start gap-2">
293
- <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">3</span>
294
- <span>If no page in the ancestor chain sets a variant, the defaults configured above are used.</span>
389
+ <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">
390
+ 3
391
+ </span>
392
+ <span>
393
+ If no page in the ancestor chain sets a variant, the defaults configured above
394
+ are used.
395
+ </span>
295
396
  </li>
296
397
  </ul>
297
398
  </div>
@@ -302,9 +403,24 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
302
403
  <div className="rounded-lg border border-gray-200 bg-white p-4">
303
404
  <h3 className="mb-4 text-sm font-semibold text-gray-900">Security Settings</h3>
304
405
  <div className="space-y-4">
305
- <ToggleSetting label="Two-Factor Authentication" description="Require 2FA for all admin users" checked={twoFactorEnabled} onChange={setTwoFactorEnabled} />
306
- <ToggleSetting label="Session Timeout" description="Automatically log out inactive users after 30 minutes" checked={sessionTimeout} onChange={setSessionTimeout} />
307
- <ToggleSetting label="IP Whitelist" description="Only allow access from approved IP addresses" checked={ipWhitelist} onChange={setIpWhitelist} />
406
+ <ToggleSetting
407
+ label="Two-Factor Authentication"
408
+ description="Require 2FA for all admin users"
409
+ checked={twoFactorEnabled}
410
+ onChange={setTwoFactorEnabled}
411
+ />
412
+ <ToggleSetting
413
+ label="Session Timeout"
414
+ description="Automatically log out inactive users after 30 minutes"
415
+ checked={sessionTimeout}
416
+ onChange={setSessionTimeout}
417
+ />
418
+ <ToggleSetting
419
+ label="IP Whitelist"
420
+ description="Only allow access from approved IP addresses"
421
+ checked={ipWhitelist}
422
+ onChange={setIpWhitelist}
423
+ />
308
424
  </div>
309
425
  </div>
310
426
  </Tabs.Content>
@@ -312,7 +428,9 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
312
428
  <Tabs.Content value="ai" className="space-y-4">
313
429
  <div className="rounded-lg border border-gray-200 bg-white p-4">
314
430
  <h3 className="mb-1 text-sm font-semibold text-gray-900">AI Provider & API Key</h3>
315
- <p className="mb-4 text-xs text-gray-500">Connect an AI provider to enable intelligent content features</p>
431
+ <p className="mb-4 text-xs text-gray-500">
432
+ Connect an AI provider to enable intelligent content features
433
+ </p>
316
434
  <div className="space-y-4">
317
435
  <div>
318
436
  <label className="mb-1 block text-sm font-medium text-gray-700">Provider</label>
@@ -326,9 +444,12 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
326
444
  <option value="google">Google (Gemini)</option>
327
445
  </select>
328
446
  <p className="mt-1 text-xs text-gray-500">
329
- {aiProvider === 'anthropic' && 'Recommended. Best for content analysis, brand voice learning, and nuanced writing.'}
330
- {aiProvider === 'openai' && 'Strong alternative. Good for general content generation and image understanding.'}
331
- {aiProvider === 'google' && 'Multimodal. Excellent for image analysis and multilingual content.'}
447
+ {aiProvider === 'anthropic' &&
448
+ 'Recommended. Best for content analysis, brand voice learning, and nuanced writing.'}
449
+ {aiProvider === 'openai' &&
450
+ 'Strong alternative. Good for general content generation and image understanding.'}
451
+ {aiProvider === 'google' &&
452
+ 'Multimodal. Excellent for image analysis and multilingual content.'}
332
453
  </p>
333
454
  </div>
334
455
  <div>
@@ -339,7 +460,13 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
339
460
  type={showApiKey ? 'text' : 'password'}
340
461
  value={aiApiKey}
341
462
  onChange={(e) => setAiApiKey(e.target.value)}
342
- placeholder={aiProvider === 'anthropic' ? 'sk-ant-...' : aiProvider === 'openai' ? 'sk-...' : 'AIza...'}
463
+ placeholder={
464
+ aiProvider === 'anthropic'
465
+ ? 'sk-ant-...'
466
+ : aiProvider === 'openai'
467
+ ? 'sk-...'
468
+ : 'AIza...'
469
+ }
343
470
  className="w-full rounded-lg border border-gray-300 px-3 py-2 pr-10 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
344
471
  />
345
472
  <button
@@ -353,15 +480,17 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
353
480
  <button
354
481
  type="button"
355
482
  onClick={() => {
356
- if (aiApiKey) toast.success('API key verified successfully');
357
- else toast.error('Please enter an API key first');
483
+ if (aiApiKey) toast.success('API key verified successfully')
484
+ else toast.error('Please enter an API key first')
358
485
  }}
359
486
  className="rounded-lg border border-gray-300 px-4 py-2 text-sm transition-colors hover:bg-gray-50"
360
487
  >
361
488
  Verify
362
489
  </button>
363
490
  </div>
364
- <p className="mt-1 text-xs text-gray-500">Your key is encrypted at rest (AES-256-GCM) and never exposed to the client</p>
491
+ <p className="mt-1 text-xs text-gray-500">
492
+ Your key is encrypted at rest (AES-256-GCM) and never exposed to the client
493
+ </p>
365
494
  </div>
366
495
  </div>
367
496
  </div>
@@ -393,7 +522,9 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
393
522
  <FileCode2 className="w-4 h-4 text-purple-600" />
394
523
  <h3 className="text-sm font-semibold text-gray-900">Content SEO</h3>
395
524
  </div>
396
- <p className="mb-4 text-xs text-gray-500">AI-driven SEO optimization for your content</p>
525
+ <p className="mb-4 text-xs text-gray-500">
526
+ AI-driven SEO optimization for your content
527
+ </p>
397
528
  <div className="space-y-4">
398
529
  <ToggleSetting
399
530
  label="Meta Description Generation"
@@ -421,7 +552,9 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
421
552
  <MessageSquare className="w-4 h-4 text-green-600" />
422
553
  <h3 className="text-sm font-semibold text-gray-900">Brand Voice & Writing</h3>
423
554
  </div>
424
- <p className="mb-4 text-xs text-gray-500">AI that understands and writes in your brand&apos;s voice</p>
555
+ <p className="mb-4 text-xs text-gray-500">
556
+ AI that understands and writes in your brand&apos;s voice
557
+ </p>
425
558
  <div className="space-y-4">
426
559
  <ToggleSetting
427
560
  label="Brand Voice Training"
@@ -443,7 +576,9 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
443
576
  <Sparkles className="w-4 h-4 text-yellow-600" />
444
577
  <h3 className="text-sm font-semibold text-gray-900">Content Quality</h3>
445
578
  </div>
446
- <p className="mb-4 text-xs text-gray-500">Automated quality scoring and content intelligence</p>
579
+ <p className="mb-4 text-xs text-gray-500">
580
+ Automated quality scoring and content intelligence
581
+ </p>
447
582
  <div className="space-y-4">
448
583
  <ToggleSetting
449
584
  label="Content Scoring"
@@ -479,7 +614,9 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
479
614
  <div className="text-xs text-indigo-700">Estimated Cost</div>
480
615
  </div>
481
616
  </div>
482
- <p className="mt-2 text-xs text-indigo-600">Add an API key above to start using AI features</p>
617
+ <p className="mt-2 text-xs text-indigo-600">
618
+ Add an API key above to start using AI features
619
+ </p>
483
620
  </div>
484
621
  </Tabs.Content>
485
622
 
@@ -490,8 +627,18 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
490
627
  <div>
491
628
  <label className="mb-1 block text-sm font-medium text-gray-700">API Key</label>
492
629
  <div className="flex gap-2">
493
- <input type="text" value="•••••••••••••••••••••••••" readOnly className="flex-1 rounded-lg border border-gray-300 bg-gray-50 px-3 py-2 text-sm" />
494
- <button type="button" className="rounded-lg border border-gray-300 px-4 py-2 text-sm transition-colors hover:bg-gray-50">Regenerate</button>
630
+ <input
631
+ type="text"
632
+ value="•••••••••••••••••••••••••"
633
+ readOnly
634
+ className="flex-1 rounded-lg border border-gray-300 bg-gray-50 px-3 py-2 text-sm"
635
+ />
636
+ <button
637
+ type="button"
638
+ className="rounded-lg border border-gray-300 px-4 py-2 text-sm transition-colors hover:bg-gray-50"
639
+ >
640
+ Regenerate
641
+ </button>
495
642
  </div>
496
643
  </div>
497
644
  </div>
@@ -514,11 +661,11 @@ export function Settings({ config, ..._props }: SettingsProps = {}) {
514
661
  </button>
515
662
  </div>
516
663
  </div>
517
- );
664
+ )
518
665
  }
519
666
 
520
667
  function ThemeSelect() {
521
- const { theme, setTheme } = useTheme();
668
+ const { theme, setTheme } = useTheme()
522
669
  return (
523
670
  <select
524
671
  value={theme}
@@ -529,7 +676,7 @@ function ThemeSelect() {
529
676
  <option value="dark">Dark</option>
530
677
  <option value="system">Auto (System)</option>
531
678
  </select>
532
- );
679
+ )
533
680
  }
534
681
 
535
682
  function ToggleSetting({
@@ -538,10 +685,10 @@ function ToggleSetting({
538
685
  checked,
539
686
  onChange,
540
687
  }: {
541
- label: string;
542
- description: string;
543
- checked: boolean;
544
- onChange: (v: boolean) => void;
688
+ label: string
689
+ description: string
690
+ checked: boolean
691
+ onChange: (v: boolean) => void
545
692
  }) {
546
693
  return (
547
694
  <div className="flex items-center justify-between gap-4">
@@ -564,7 +711,7 @@ function ToggleSetting({
564
711
  />
565
712
  </button>
566
713
  </div>
567
- );
714
+ )
568
715
  }
569
716
 
570
717
  // ---------------------------------------------------------------------------
@@ -572,56 +719,56 @@ function ToggleSetting({
572
719
  // ---------------------------------------------------------------------------
573
720
 
574
721
  interface UpdateInfo {
575
- current: string;
576
- latest: string;
577
- updateAvailable: boolean;
578
- severity?: 'patch' | 'minor' | 'major';
579
- releaseDate?: string;
580
- changelog?: Array<{ version: string; date: string; summary: string }>;
581
- updateCommand?: string;
582
- hasGithubToken?: boolean;
583
- githubRepo?: string;
722
+ current: string
723
+ latest: string
724
+ updateAvailable: boolean
725
+ severity?: 'patch' | 'minor' | 'major'
726
+ releaseDate?: string
727
+ changelog?: Array<{ version: string; date: string; summary: string }>
728
+ updateCommand?: string
729
+ hasGithubToken?: boolean
730
+ githubRepo?: string
584
731
  }
585
732
 
586
733
  function UpdatesPanel() {
587
- const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null);
588
- const [checking, setChecking] = useState(false);
589
- const [checkError, setCheckError] = useState('');
590
- const [applying, setApplying] = useState(false);
591
- const [prResult, setPrResult] = useState<{ prUrl: string; prNumber: number } | null>(null);
592
- const [hasChecked, setHasChecked] = useState(false);
734
+ const [updateInfo, setUpdateInfo] = useState<UpdateInfo | null>(null)
735
+ const [checking, setChecking] = useState(false)
736
+ const [checkError, setCheckError] = useState('')
737
+ const [applying, setApplying] = useState(false)
738
+ const [prResult, setPrResult] = useState<{ prUrl: string; prNumber: number } | null>(null)
739
+ const [hasChecked, setHasChecked] = useState(false)
593
740
 
594
741
  // GitHub config
595
- const [ghToken, setGhToken] = useState('');
596
- const [ghRepo, setGhRepo] = useState('');
597
- const [showGhToken, setShowGhToken] = useState(false);
598
- const [savingConfig, setSavingConfig] = useState(false);
599
- const [configSaved, setConfigSaved] = useState(false);
742
+ const [ghToken, setGhToken] = useState('')
743
+ const [ghRepo, setGhRepo] = useState('')
744
+ const [showGhToken, setShowGhToken] = useState(false)
745
+ const [savingConfig, setSavingConfig] = useState(false)
746
+ const [configSaved, setConfigSaved] = useState(false)
600
747
 
601
748
  const checkForUpdates = async () => {
602
- setChecking(true);
603
- setCheckError('');
604
- setPrResult(null);
749
+ setChecking(true)
750
+ setCheckError('')
751
+ setPrResult(null)
605
752
 
606
753
  try {
607
- const res = await cmsApi<UpdateInfo>('/updates/check');
754
+ const res = await cmsApi<UpdateInfo>('/updates/check')
608
755
  if (res.error) {
609
- setCheckError(res.error);
756
+ setCheckError(res.error)
610
757
  } else if (res.data) {
611
- setUpdateInfo(res.data);
612
- if (res.data.githubRepo) setGhRepo(res.data.githubRepo);
758
+ setUpdateInfo(res.data)
759
+ if (res.data.githubRepo) setGhRepo(res.data.githubRepo)
613
760
  }
614
761
  } catch {
615
- setCheckError('Unable to check for updates. Please try again.');
762
+ setCheckError('Unable to check for updates. Please try again.')
616
763
  } finally {
617
- setChecking(false);
618
- setHasChecked(true);
764
+ setChecking(false)
765
+ setHasChecked(true)
619
766
  }
620
- };
767
+ }
621
768
 
622
769
  const saveGitHubConfig = async () => {
623
- setSavingConfig(true);
624
- setConfigSaved(false);
770
+ setSavingConfig(true)
771
+ setConfigSaved(false)
625
772
  try {
626
773
  const res = await cmsApi('/updates/config', {
627
774
  method: 'PUT',
@@ -629,56 +776,64 @@ function UpdatesPanel() {
629
776
  ...(ghToken ? { githubToken: ghToken } : {}),
630
777
  ...(ghRepo ? { githubRepo: ghRepo } : {}),
631
778
  }),
632
- });
779
+ })
633
780
  if (res.error) {
634
- toast.error(res.error);
781
+ toast.error(res.error)
635
782
  } else {
636
- toast.success('GitHub configuration saved and encrypted.');
637
- setConfigSaved(true);
638
- setGhToken('');
783
+ toast.success('GitHub configuration saved and encrypted.')
784
+ setConfigSaved(true)
785
+ setGhToken('')
639
786
  if (updateInfo) {
640
- setUpdateInfo({ ...updateInfo, hasGithubToken: true, githubRepo: ghRepo });
787
+ setUpdateInfo({ ...updateInfo, hasGithubToken: true, githubRepo: ghRepo })
641
788
  }
642
789
  }
643
790
  } catch {
644
- toast.error('Failed to save configuration.');
791
+ toast.error('Failed to save configuration.')
645
792
  } finally {
646
- setSavingConfig(false);
793
+ setSavingConfig(false)
647
794
  }
648
- };
795
+ }
649
796
 
650
797
  const applyUpdate = async () => {
651
- if (!updateInfo?.latest) return;
652
- setApplying(true);
798
+ if (!updateInfo?.latest) return
799
+ setApplying(true)
653
800
 
654
801
  try {
655
802
  const res = await cmsApi<{ prUrl: string; prNumber: number }>('/updates/apply', {
656
803
  method: 'POST',
657
804
  body: JSON.stringify({ targetVersion: updateInfo.latest }),
658
- });
805
+ })
659
806
 
660
807
  if (res.error) {
661
- toast.error(res.error);
808
+ toast.error(res.error)
662
809
  } else if (res.data) {
663
- setPrResult(res.data);
664
- toast.success('Update PR created successfully!');
810
+ setPrResult(res.data)
811
+ toast.success('Update PR created successfully!')
665
812
  }
666
813
  } catch {
667
- toast.error('Failed to create update PR. Check your GitHub configuration.');
814
+ toast.error('Failed to create update PR. Check your GitHub configuration.')
668
815
  } finally {
669
- setApplying(false);
816
+ setApplying(false)
670
817
  }
671
- };
818
+ }
672
819
 
673
820
  useEffect(() => {
674
- checkForUpdates();
675
- }, []);
821
+ checkForUpdates()
822
+ }, [])
676
823
 
677
- const severityColors: Record<string, { bg: string; text: string; border: string; label: string }> = {
824
+ const severityColors: Record<
825
+ string,
826
+ { bg: string; text: string; border: string; label: string }
827
+ > = {
678
828
  patch: { bg: 'bg-blue-50', text: 'text-blue-700', border: 'border-blue-200', label: 'Patch' },
679
- minor: { bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200', label: 'Minor' },
829
+ minor: {
830
+ bg: 'bg-yellow-50',
831
+ text: 'text-yellow-700',
832
+ border: 'border-yellow-200',
833
+ label: 'Minor',
834
+ },
680
835
  major: { bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200', label: 'Major' },
681
- };
836
+ }
682
837
 
683
838
  return (
684
839
  <>
@@ -688,7 +843,8 @@ function UpdatesPanel() {
688
843
  <div>
689
844
  <h3 className="text-sm font-semibold text-gray-900">Actuate CMS</h3>
690
845
  <p className="mt-1 text-sm text-gray-600">
691
- Current version: <span className="font-mono font-medium">{updateInfo?.current ?? '...'}</span>
846
+ Current version:{' '}
847
+ <span className="font-mono font-medium">{updateInfo?.current ?? '...'}</span>
692
848
  </p>
693
849
  </div>
694
850
  <button
@@ -708,7 +864,12 @@ function UpdatesPanel() {
708
864
  <div className="flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
709
865
  <AlertTriangle className="w-5 h-5 text-red-600 shrink-0" />
710
866
  <span className="text-sm text-red-800 flex-1">{checkError}</span>
711
- <button onClick={checkForUpdates} className="px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors">Retry</button>
867
+ <button
868
+ onClick={checkForUpdates}
869
+ className="px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors"
870
+ >
871
+ Retry
872
+ </button>
712
873
  </div>
713
874
  )}
714
875
 
@@ -719,7 +880,8 @@ function UpdatesPanel() {
719
880
  <div>
720
881
  <h3 className="text-sm font-semibold text-green-900">You&apos;re up to date!</h3>
721
882
  <p className="text-sm text-green-700 mt-0.5">
722
- Actuate CMS <span className="font-mono">{updateInfo.current}</span> is the latest version.
883
+ Actuate CMS <span className="font-mono">{updateInfo.current}</span> is the latest
884
+ version.
723
885
  </p>
724
886
  </div>
725
887
  </div>
@@ -728,24 +890,33 @@ function UpdatesPanel() {
728
890
  {/* Update Available */}
729
891
  {updateInfo?.updateAvailable && (
730
892
  <>
731
- <div className={`rounded-lg border p-4 ${severityColors[updateInfo.severity ?? 'patch']?.border ?? 'border-blue-200'} ${severityColors[updateInfo.severity ?? 'patch']?.bg ?? 'bg-blue-50'}`}>
893
+ <div
894
+ className={`rounded-lg border p-4 ${severityColors[updateInfo.severity ?? 'patch']?.border ?? 'border-blue-200'} ${severityColors[updateInfo.severity ?? 'patch']?.bg ?? 'bg-blue-50'}`}
895
+ >
732
896
  <div className="flex items-start gap-3">
733
- <ArrowUpCircle className={`w-6 h-6 mt-0.5 shrink-0 ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'}`} />
897
+ <ArrowUpCircle
898
+ className={`w-6 h-6 mt-0.5 shrink-0 ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'}`}
899
+ />
734
900
  <div className="flex-1">
735
901
  <div className="flex items-center gap-2">
736
- <h3 className={`text-sm font-semibold ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'}`}>
902
+ <h3
903
+ className={`text-sm font-semibold ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'}`}
904
+ >
737
905
  Update Available
738
906
  </h3>
739
- <span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${severityColors[updateInfo.severity ?? 'patch']?.bg ?? 'bg-blue-50'} ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'} border ${severityColors[updateInfo.severity ?? 'patch']?.border ?? 'border-blue-200'}`}>
907
+ <span
908
+ className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${severityColors[updateInfo.severity ?? 'patch']?.bg ?? 'bg-blue-50'} ${severityColors[updateInfo.severity ?? 'patch']?.text ?? 'text-blue-700'} border ${severityColors[updateInfo.severity ?? 'patch']?.border ?? 'border-blue-200'}`}
909
+ >
740
910
  {severityColors[updateInfo.severity ?? 'patch']?.label ?? 'Update'}
741
911
  </span>
742
912
  </div>
743
913
  <p className="text-sm mt-1" style={{ color: 'inherit' }}>
744
- <span className="font-mono">{updateInfo.current}</span>
745
- {' '}&rarr;{' '}
914
+ <span className="font-mono">{updateInfo.current}</span> &rarr;{' '}
746
915
  <span className="font-mono font-semibold">{updateInfo.latest}</span>
747
916
  {updateInfo.releaseDate && (
748
- <span className="text-xs ml-2 opacity-70">Released {updateInfo.releaseDate}</span>
917
+ <span className="text-xs ml-2 opacity-70">
918
+ Released {updateInfo.releaseDate}
919
+ </span>
749
920
  )}
750
921
  </p>
751
922
  </div>
@@ -790,7 +961,9 @@ function UpdatesPanel() {
790
961
  <div className="space-y-2 max-h-64 overflow-y-auto">
791
962
  {updateInfo.changelog.map((entry) => (
792
963
  <div key={entry.version} className="flex items-baseline gap-3 text-sm">
793
- <span className="font-mono text-xs text-gray-500 shrink-0 w-14">{entry.version}</span>
964
+ <span className="font-mono text-xs text-gray-500 shrink-0 w-14">
965
+ {entry.version}
966
+ </span>
794
967
  <span className="text-xs text-gray-400 shrink-0 w-20">{entry.date}</span>
795
968
  <span className="text-gray-700">{entry.summary}</span>
796
969
  </div>
@@ -809,8 +982,12 @@ function UpdatesPanel() {
809
982
  <div>
810
983
  <h3 className="text-sm font-semibold text-green-900">Pull Request Created</h3>
811
984
  <p className="text-sm text-green-700 mt-1">
812
- PR #{prResult.prNumber} has been created on your repository.
813
- Review and merge it to apply the update, then run <code className="text-xs font-mono bg-green-100 px-1 rounded">npx prisma migrate deploy</code>.
985
+ PR #{prResult.prNumber} has been created on your repository. Review and merge it to
986
+ apply the update, then run{' '}
987
+ <code className="text-xs font-mono bg-green-100 px-1 rounded">
988
+ npx prisma migrate deploy
989
+ </code>
990
+ .
814
991
  </p>
815
992
  <a
816
993
  href={prResult.prUrl}
@@ -830,7 +1007,8 @@ function UpdatesPanel() {
830
1007
  <div className="rounded-lg border border-gray-200 bg-white p-4">
831
1008
  <h3 className="text-sm font-semibold text-gray-900 mb-1">GitHub Integration</h3>
832
1009
  <p className="text-xs text-gray-500 mb-4">
833
- Connect your repository to enable one-click update PRs. Credentials are encrypted at rest (AES-256-GCM).
1010
+ Connect your repository to enable one-click update PRs. Credentials are encrypted at rest
1011
+ (AES-256-GCM).
834
1012
  </p>
835
1013
 
836
1014
  <div className="space-y-4">
@@ -843,7 +1021,12 @@ function UpdatesPanel() {
843
1021
  placeholder="owner/repo"
844
1022
  className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
845
1023
  />
846
- <p className="mt-1 text-xs text-gray-500">e.g. <code className="font-mono bg-gray-100 px-1 rounded">actuate-media/my-client-site</code></p>
1024
+ <p className="mt-1 text-xs text-gray-500">
1025
+ e.g.{' '}
1026
+ <code className="font-mono bg-gray-100 px-1 rounded">
1027
+ actuate-media/my-client-site
1028
+ </code>
1029
+ </p>
847
1030
  </div>
848
1031
  <div>
849
1032
  <label className="mb-1 block text-sm font-medium text-gray-700">
@@ -858,7 +1041,9 @@ function UpdatesPanel() {
858
1041
  type={showGhToken ? 'text' : 'password'}
859
1042
  value={ghToken}
860
1043
  onChange={(e) => setGhToken(e.target.value)}
861
- placeholder={updateInfo?.hasGithubToken ? '••••••••••••••••' : 'ghp_... or github_pat_...'}
1044
+ placeholder={
1045
+ updateInfo?.hasGithubToken ? '••••••••••••••••' : 'ghp_... or github_pat_...'
1046
+ }
862
1047
  className="w-full rounded-lg border border-gray-300 px-3 py-2 pr-10 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
863
1048
  />
864
1049
  <button
@@ -871,8 +1056,14 @@ function UpdatesPanel() {
871
1056
  </div>
872
1057
  </div>
873
1058
  <p className="mt-1 text-xs text-gray-500">
874
- Needs <code className="font-mono bg-gray-100 px-1 rounded">repo</code> scope. Create at{' '}
875
- <a href="https://github.com/settings/tokens" target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline">
1059
+ Needs <code className="font-mono bg-gray-100 px-1 rounded">repo</code> scope. Create
1060
+ at{' '}
1061
+ <a
1062
+ href="https://github.com/settings/tokens"
1063
+ target="_blank"
1064
+ rel="noopener noreferrer"
1065
+ className="text-blue-600 hover:underline"
1066
+ >
876
1067
  github.com/settings/tokens
877
1068
  </a>
878
1069
  </p>
@@ -901,23 +1092,42 @@ function UpdatesPanel() {
901
1092
  <h3 className="text-sm font-semibold text-gray-700 mb-2">How Updates Work</h3>
902
1093
  <ul className="space-y-1.5 text-xs text-gray-600">
903
1094
  <li className="flex items-start gap-2">
904
- <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">1</span>
905
- <span>Click &quot;Check for Updates&quot; to see if a new version is available on npm.</span>
1095
+ <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">
1096
+ 1
1097
+ </span>
1098
+ <span>
1099
+ Click &quot;Check for Updates&quot; to see if a new version is available on npm.
1100
+ </span>
906
1101
  </li>
907
1102
  <li className="flex items-start gap-2">
908
- <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">2</span>
909
- <span>Add your GitHub token and repository above. They&apos;re encrypted at rest — never stored in plaintext.</span>
1103
+ <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">
1104
+ 2
1105
+ </span>
1106
+ <span>
1107
+ Add your GitHub token and repository above. They&apos;re encrypted at rest — never
1108
+ stored in plaintext.
1109
+ </span>
910
1110
  </li>
911
1111
  <li className="flex items-start gap-2">
912
- <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">3</span>
913
- <span>Click &quot;Create Update PR&quot; to open a pull request that bumps your CMS packages automatically.</span>
1112
+ <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">
1113
+ 3
1114
+ </span>
1115
+ <span>
1116
+ Click &quot;Create Update PR&quot; to open a pull request that bumps your CMS packages
1117
+ automatically.
1118
+ </span>
914
1119
  </li>
915
1120
  <li className="flex items-start gap-2">
916
- <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">4</span>
917
- <span>Review and merge the PR, then deploy. Database migrations run automatically via <code className="font-mono bg-gray-200 px-1 rounded">prisma migrate deploy</code>.</span>
1121
+ <span className="w-4 h-4 rounded-full bg-blue-100 text-blue-600 flex items-center justify-center text-[10px] font-bold shrink-0 mt-0.5">
1122
+ 4
1123
+ </span>
1124
+ <span>
1125
+ Review and merge the PR, then deploy. Database migrations run automatically via{' '}
1126
+ <code className="font-mono bg-gray-200 px-1 rounded">prisma migrate deploy</code>.
1127
+ </span>
918
1128
  </li>
919
1129
  </ul>
920
1130
  </div>
921
1131
  </>
922
- );
1132
+ )
923
1133
  }