@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
@@ -1,97 +1,95 @@
1
- 'use client';
1
+ 'use client'
2
2
 
3
- import { useState, useEffect } from 'react';
4
- import { ArrowLeft, Eye, Loader2, AlertTriangle } from 'lucide-react';
5
- import { toast } from 'sonner';
6
- import { TipTapEditor } from '../components/TipTapEditor.js';
7
- import { SEOPanel, type SEOData } from '../components/SEOPanel.js';
8
- import { useApiData } from '../lib/useApiData.js';
9
- import { cmsApi } from '../lib/api.js';
3
+ import { useState, useEffect } from 'react'
4
+ import { ArrowLeft, Eye, Loader2, AlertTriangle } from 'lucide-react'
5
+ import { toast } from 'sonner'
6
+ import { TipTapEditor } from '../components/TipTapEditor.js'
7
+ import { SEOPanel, type SEOData } from '../components/SEOPanel.js'
8
+ import { useApiData } from '../lib/useApiData.js'
9
+ import { cmsApi } from '../lib/api.js'
10
10
 
11
11
  export interface PostEditorProps {
12
- id?: string;
13
- onNavigate?: (path: string) => void;
12
+ id?: string
13
+ onNavigate?: (path: string) => void
14
14
  }
15
15
 
16
16
  export function PostEditor({ id, onNavigate }: PostEditorProps) {
17
- const isNew = !id;
18
- const { data, loading, error } = useApiData<any>(
19
- isNew ? '' : `/collections/posts/${id}`
20
- );
21
-
22
- const [title, setTitle] = useState('');
23
- const [content, setContent] = useState('');
24
- const [status, setStatus] = useState<'draft' | 'published'>('draft');
25
- const [slug, setSlug] = useState('');
26
- const [saving, setSaving] = useState(false);
27
- const [initialized, setInitialized] = useState(isNew);
28
- const [seoData, setSeoData] = useState<SEOData>({});
17
+ const isNew = !id
18
+ const { data, loading, error } = useApiData<any>(isNew ? '' : `/collections/posts/${id}`)
19
+
20
+ const [title, setTitle] = useState('')
21
+ const [content, setContent] = useState('')
22
+ const [status, setStatus] = useState<'draft' | 'published'>('draft')
23
+ const [slug, setSlug] = useState('')
24
+ const [saving, setSaving] = useState(false)
25
+ const [initialized, setInitialized] = useState(isNew)
26
+ const [seoData, setSeoData] = useState<SEOData>({})
29
27
 
30
28
  useEffect(() => {
31
29
  if (data && !initialized) {
32
- setTitle(data.title ?? '');
33
- setContent(data.content ?? data.data?.content ?? '');
34
- setStatus(data.status === 'PUBLISHED' ? 'published' : 'draft');
35
- setSlug(data.slug ?? '');
36
- setInitialized(true);
30
+ setTitle(data.title ?? '')
31
+ setContent(data.content ?? data.data?.content ?? '')
32
+ setStatus(data.status === 'PUBLISHED' ? 'published' : 'draft')
33
+ setSlug(data.slug ?? '')
34
+ setInitialized(true)
37
35
  }
38
- }, [data, initialized]);
36
+ }, [data, initialized])
39
37
 
40
38
  const savePost = async () => {
41
- setSaving(true);
39
+ setSaving(true)
42
40
  const body = JSON.stringify({
43
41
  title,
44
42
  content,
45
43
  slug,
46
44
  status: status === 'published' ? 'PUBLISHED' : 'DRAFT',
47
- });
45
+ })
48
46
 
49
47
  const res = isNew
50
48
  ? await cmsApi('/collections/posts', { method: 'POST', body })
51
- : await cmsApi(`/collections/posts/${id}`, { method: 'PUT', body });
49
+ : await cmsApi(`/collections/posts/${id}`, { method: 'PUT', body })
52
50
 
53
- setSaving(false);
51
+ setSaving(false)
54
52
  if (res.error) {
55
- toast.error(res.error);
53
+ toast.error(res.error)
56
54
  } else {
57
- toast.success('Post saved successfully!');
55
+ toast.success('Post saved successfully!')
58
56
  if (isNew && (res.data as any)?.id) {
59
- onNavigate?.(`/posts/${(res.data as any).id}`);
57
+ onNavigate?.(`/posts/${(res.data as any).id}`)
60
58
  }
61
59
  }
62
- };
60
+ }
63
61
 
64
62
  const publishPost = async () => {
65
- setSaving(true);
63
+ setSaving(true)
66
64
  const body = JSON.stringify({
67
65
  title,
68
66
  content,
69
67
  slug,
70
68
  status: 'PUBLISHED',
71
- });
69
+ })
72
70
 
73
- const endpoint = isNew ? '/collections/posts' : `/collections/posts/${id}`;
74
- const method = isNew ? 'POST' : 'PUT';
75
- const res = await cmsApi(endpoint, { method, body });
71
+ const endpoint = isNew ? '/collections/posts' : `/collections/posts/${id}`
72
+ const method = isNew ? 'POST' : 'PUT'
73
+ const res = await cmsApi(endpoint, { method, body })
76
74
 
77
- setSaving(false);
75
+ setSaving(false)
78
76
  if (res.error) {
79
- toast.error(res.error);
77
+ toast.error(res.error)
80
78
  } else {
81
- setStatus('published');
82
- toast.success('Post published!');
79
+ setStatus('published')
80
+ toast.success('Post published!')
83
81
  if (isNew && (res.data as any)?.id) {
84
- onNavigate?.(`/posts/${(res.data as any).id}`);
82
+ onNavigate?.(`/posts/${(res.data as any).id}`)
85
83
  }
86
84
  }
87
- };
85
+ }
88
86
 
89
87
  if (!isNew && loading) {
90
88
  return (
91
89
  <div className="h-full flex items-center justify-center">
92
90
  <Loader2 className="w-6 h-6 animate-spin text-blue-600" />
93
91
  </div>
94
- );
92
+ )
95
93
  }
96
94
 
97
95
  return (
@@ -167,7 +165,9 @@ export function PostEditor({ id, onNavigate }: PostEditorProps) {
167
165
  </select>
168
166
  </div>
169
167
  <div>
170
- <label className="block text-xs font-medium text-gray-700 mb-1">Publish Date</label>
168
+ <label className="block text-xs font-medium text-gray-700 mb-1">
169
+ Publish Date
170
+ </label>
171
171
  <input
172
172
  type="datetime-local"
173
173
  className="w-full px-3 py-1.5 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
@@ -221,7 +221,13 @@ export function PostEditor({ id, onNavigate }: PostEditorProps) {
221
221
  </button>
222
222
  </div>
223
223
 
224
- <SEOPanel title={title} slug={slug} content={content} seoData={seoData} onChange={setSeoData} />
224
+ <SEOPanel
225
+ title={title}
226
+ slug={slug}
227
+ content={content}
228
+ seoData={seoData}
229
+ onChange={setSeoData}
230
+ />
225
231
  </div>
226
232
  </div>
227
233
 
@@ -247,5 +253,5 @@ export function PostEditor({ id, onNavigate }: PostEditorProps) {
247
253
  </div>
248
254
  </div>
249
255
  </div>
250
- );
256
+ )
251
257
  }
@@ -1,76 +1,99 @@
1
- 'use client';
1
+ 'use client'
2
2
 
3
- import { Plus, Search, Trash2, SlidersHorizontal, Pencil, ArrowUpDown, ArrowUp, ArrowDown, Loader2, AlertTriangle } from 'lucide-react';
4
- import { useState, useMemo } from 'react';
5
- import { toast } from 'sonner';
6
- import { sortByRelevance, type SortConfig, toggleSort } from '../lib/search.js';
7
- import { useApiData } from '../lib/useApiData.js';
8
- import { cmsApi } from '../lib/api.js';
3
+ import {
4
+ Plus,
5
+ Search,
6
+ Trash2,
7
+ SlidersHorizontal,
8
+ Pencil,
9
+ ArrowUpDown,
10
+ ArrowUp,
11
+ ArrowDown,
12
+ Loader2,
13
+ AlertTriangle,
14
+ } from 'lucide-react'
15
+ import { useState, useMemo } from 'react'
16
+ import { toast } from 'sonner'
17
+ import { sortByRelevance, type SortConfig, toggleSort } from '../lib/search.js'
18
+ import { useApiData } from '../lib/useApiData.js'
19
+ import { cmsApi } from '../lib/api.js'
9
20
 
10
- type PostSortKey = 'title' | 'author' | 'category' | 'status' | 'date';
21
+ type PostSortKey = 'title' | 'author' | 'category' | 'status' | 'date'
11
22
 
12
23
  export interface PostsProps {
13
- onNavigate?: (path: string) => void;
24
+ onNavigate?: (path: string) => void
14
25
  }
15
26
 
16
27
  export function Posts({ onNavigate }: PostsProps) {
17
- const { data, loading, error, refetch } = useApiData<{ docs: any[]; total: number }>('/collections/posts?pageSize=100');
18
- const [searchQuery, setSearchQuery] = useState('');
19
- const [filterStatus, setFilterStatus] = useState<string>('all');
20
- const [filterCategory, setFilterCategory] = useState<string>('all');
21
- const [selectedPosts, setSelectedPosts] = useState<number[]>([]);
22
- const [sortConfig, setSortConfig] = useState<SortConfig<PostSortKey> | null>(null);
28
+ const { data, loading, error, refetch } = useApiData<{ docs: any[]; total: number }>(
29
+ '/collections/posts?pageSize=100',
30
+ )
31
+ const [searchQuery, setSearchQuery] = useState('')
32
+ const [filterStatus, setFilterStatus] = useState<string>('all')
33
+ const [filterCategory, setFilterCategory] = useState<string>('all')
34
+ const [selectedPosts, setSelectedPosts] = useState<number[]>([])
35
+ const [sortConfig, setSortConfig] = useState<SortConfig<PostSortKey> | null>(null)
23
36
 
24
- const posts = data?.docs ?? [];
37
+ const posts = data?.docs ?? []
25
38
 
26
39
  const filteredAndSorted = useMemo(() => {
27
40
  let results = posts.filter((post: any) => {
28
- const matchesSearch = (post.title ?? '').toLowerCase().includes(searchQuery.toLowerCase()) ||
29
- (post.author ?? '').toLowerCase().includes(searchQuery.toLowerCase());
30
- const matchesStatus = filterStatus === 'all' || (post.status ?? '').toLowerCase() === filterStatus.toLowerCase();
31
- const matchesCategory = filterCategory === 'all' || (post.category ?? '').toLowerCase() === filterCategory.toLowerCase();
32
- return matchesSearch && matchesStatus && matchesCategory;
33
- });
41
+ const matchesSearch =
42
+ (post.title ?? '').toLowerCase().includes(searchQuery.toLowerCase()) ||
43
+ (post.author ?? '').toLowerCase().includes(searchQuery.toLowerCase())
44
+ const matchesStatus =
45
+ filterStatus === 'all' || (post.status ?? '').toLowerCase() === filterStatus.toLowerCase()
46
+ const matchesCategory =
47
+ filterCategory === 'all' ||
48
+ (post.category ?? '').toLowerCase() === filterCategory.toLowerCase()
49
+ return matchesSearch && matchesStatus && matchesCategory
50
+ })
34
51
 
35
52
  if (searchQuery.trim()) {
36
- results = sortByRelevance(results, searchQuery, (p: any) => [p.title, p.author ?? '', p.category ?? '']);
53
+ results = sortByRelevance(results, searchQuery, (p: any) => [
54
+ p.title,
55
+ p.author ?? '',
56
+ p.category ?? '',
57
+ ])
37
58
  } else if (sortConfig) {
38
59
  results = [...results].sort((a: any, b: any) => {
39
- const aVal = a[sortConfig.key] ?? '';
40
- const bVal = b[sortConfig.key] ?? '';
41
- const cmp = String(aVal).localeCompare(String(bVal));
42
- return sortConfig.direction === 'asc' ? cmp : -cmp;
43
- });
60
+ const aVal = a[sortConfig.key] ?? ''
61
+ const bVal = b[sortConfig.key] ?? ''
62
+ const cmp = String(aVal).localeCompare(String(bVal))
63
+ return sortConfig.direction === 'asc' ? cmp : -cmp
64
+ })
44
65
  }
45
- return results;
46
- }, [posts, searchQuery, filterStatus, filterCategory, sortConfig]);
66
+ return results
67
+ }, [posts, searchQuery, filterStatus, filterCategory, sortConfig])
47
68
 
48
69
  const handleSelectAll = (checked: boolean) => {
49
- setSelectedPosts(checked ? filteredAndSorted.map((p: any) => p.id) : []);
50
- };
70
+ setSelectedPosts(checked ? filteredAndSorted.map((p: any) => p.id) : [])
71
+ }
51
72
 
52
73
  const handleSelectPost = (id: number) => {
53
- setSelectedPosts(prev => prev.includes(id) ? prev.filter(pid => pid !== id) : [...prev, id]);
54
- };
74
+ setSelectedPosts((prev) =>
75
+ prev.includes(id) ? prev.filter((pid) => pid !== id) : [...prev, id],
76
+ )
77
+ }
55
78
 
56
79
  const handleBulkDelete = async () => {
57
80
  for (const id of selectedPosts) {
58
- await cmsApi(`/collections/posts/${id}`, { method: 'DELETE' });
81
+ await cmsApi(`/collections/posts/${id}`, { method: 'DELETE' })
59
82
  }
60
- toast.success(`${selectedPosts.length} posts deleted`);
61
- setSelectedPosts([]);
62
- refetch();
63
- };
83
+ toast.success(`${selectedPosts.length} posts deleted`)
84
+ setSelectedPosts([])
85
+ refetch()
86
+ }
64
87
 
65
88
  const handleDelete = async (id: number) => {
66
- await cmsApi(`/collections/posts/${id}`, { method: 'DELETE' });
67
- toast.success('Post deleted');
68
- setSelectedPosts(prev => prev.filter(pid => pid !== id));
69
- refetch();
70
- };
89
+ await cmsApi(`/collections/posts/${id}`, { method: 'DELETE' })
90
+ toast.success('Post deleted')
91
+ setSelectedPosts((prev) => prev.filter((pid) => pid !== id))
92
+ refetch()
93
+ }
71
94
 
72
95
  function SortHeader({ label, sortKey }: { label: string; sortKey: PostSortKey }) {
73
- const active = sortConfig?.key === sortKey;
96
+ const active = sortConfig?.key === sortKey
74
97
  return (
75
98
  <button
76
99
  type="button"
@@ -79,12 +102,16 @@ export function Posts({ onNavigate }: PostsProps) {
79
102
  >
80
103
  {label}
81
104
  {active ? (
82
- sortConfig!.direction === 'asc' ? <ArrowUp className="w-3 h-3" /> : <ArrowDown className="w-3 h-3" />
105
+ sortConfig!.direction === 'asc' ? (
106
+ <ArrowUp className="w-3 h-3" />
107
+ ) : (
108
+ <ArrowDown className="w-3 h-3" />
109
+ )
83
110
  ) : (
84
111
  <ArrowUpDown className="w-3 h-3 text-gray-400" />
85
112
  )}
86
113
  </button>
87
- );
114
+ )
88
115
  }
89
116
 
90
117
  if (loading) {
@@ -92,7 +119,7 @@ export function Posts({ onNavigate }: PostsProps) {
92
119
  <div className="p-3 pr-6 sm:p-4 sm:pr-8 flex items-center justify-center h-64">
93
120
  <Loader2 className="w-6 h-6 animate-spin text-blue-600" />
94
121
  </div>
95
- );
122
+ )
96
123
  }
97
124
 
98
125
  return (
@@ -101,7 +128,12 @@ export function Posts({ onNavigate }: PostsProps) {
101
128
  <div className="mb-4 flex items-center gap-3 rounded-lg border border-red-200 bg-red-50 p-3">
102
129
  <AlertTriangle className="w-5 h-5 text-red-600 shrink-0" />
103
130
  <span className="text-sm text-red-800 flex-1">{error}</span>
104
- <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>
131
+ <button
132
+ onClick={refetch}
133
+ className="px-3 py-1 text-sm text-red-700 border border-red-300 rounded-lg hover:bg-red-100 transition-colors"
134
+ >
135
+ Retry
136
+ </button>
105
137
  </div>
106
138
  )}
107
139
 
@@ -124,21 +156,38 @@ export function Posts({ onNavigate }: PostsProps) {
124
156
  <div className="p-3 flex flex-col gap-3">
125
157
  <div className="relative">
126
158
  <Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-gray-400" />
127
- <input type="text" placeholder="Search posts..." value={searchQuery} onChange={(e) => setSearchQuery(e.target.value)} className="w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
159
+ <input
160
+ type="text"
161
+ placeholder="Search posts..."
162
+ value={searchQuery}
163
+ onChange={(e) => setSearchQuery(e.target.value)}
164
+ className="w-full pl-9 pr-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
165
+ />
128
166
  </div>
129
167
  <div className="flex flex-col sm:flex-row gap-2">
130
- <select value={filterStatus} onChange={(e) => setFilterStatus(e.target.value)} className="flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
168
+ <select
169
+ value={filterStatus}
170
+ onChange={(e) => setFilterStatus(e.target.value)}
171
+ className="flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
172
+ >
131
173
  <option value="all">All Status</option>
132
174
  <option value="published">Published</option>
133
175
  <option value="draft">Draft</option>
134
176
  </select>
135
- <select value={filterCategory} onChange={(e) => setFilterCategory(e.target.value)} className="flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500">
177
+ <select
178
+ value={filterCategory}
179
+ onChange={(e) => setFilterCategory(e.target.value)}
180
+ className="flex-1 px-3 py-2 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
181
+ >
136
182
  <option value="all">All Categories</option>
137
183
  <option value="technology">Technology</option>
138
184
  <option value="design">Design</option>
139
185
  <option value="business">Business</option>
140
186
  </select>
141
- <button type="button" className="flex items-center justify-center gap-2 px-3 py-2 text-sm border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">
187
+ <button
188
+ type="button"
189
+ className="flex items-center justify-center gap-2 px-3 py-2 text-sm border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors"
190
+ >
142
191
  <SlidersHorizontal className="w-4 h-4" />
143
192
  <span className="hidden sm:inline">More Filters</span>
144
193
  </button>
@@ -149,10 +198,24 @@ export function Posts({ onNavigate }: PostsProps) {
149
198
  {selectedPosts.length > 0 && (
150
199
  <div className="bg-blue-50 border border-blue-200 rounded-lg p-3 mb-4">
151
200
  <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
152
- <span className="text-sm text-blue-900">{selectedPosts.length} post{selectedPosts.length !== 1 ? 's' : ''} selected</span>
201
+ <span className="text-sm text-blue-900">
202
+ {selectedPosts.length} post{selectedPosts.length !== 1 ? 's' : ''} selected
203
+ </span>
153
204
  <div className="flex items-center gap-2">
154
- <button type="button" onClick={handleBulkDelete} className="flex-1 sm:flex-none px-3 py-1.5 text-sm bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">Delete</button>
155
- <button type="button" onClick={() => setSelectedPosts([])} className="flex-1 sm:flex-none px-3 py-1.5 text-sm border border-gray-300 bg-white rounded-lg hover:bg-gray-50 transition-colors">Cancel</button>
205
+ <button
206
+ type="button"
207
+ onClick={handleBulkDelete}
208
+ className="flex-1 sm:flex-none px-3 py-1.5 text-sm bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
209
+ >
210
+ Delete
211
+ </button>
212
+ <button
213
+ type="button"
214
+ onClick={() => setSelectedPosts([])}
215
+ className="flex-1 sm:flex-none px-3 py-1.5 text-sm border border-gray-300 bg-white rounded-lg hover:bg-gray-50 transition-colors"
216
+ >
217
+ Cancel
218
+ </button>
156
219
  </div>
157
220
  </div>
158
221
  </div>
@@ -161,7 +224,12 @@ export function Posts({ onNavigate }: PostsProps) {
161
224
  {filteredAndSorted.length === 0 && !error ? (
162
225
  <div className="bg-white rounded-lg border border-gray-200 p-8 text-center flex-1 flex flex-col items-center justify-center">
163
226
  <p className="text-sm text-gray-500 mb-2">No posts yet</p>
164
- <button onClick={() => onNavigate?.('/posts/new')} className="px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">Create your first post</button>
227
+ <button
228
+ onClick={() => onNavigate?.('/posts/new')}
229
+ className="px-4 py-2 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors"
230
+ >
231
+ Create your first post
232
+ </button>
165
233
  </div>
166
234
  ) : (
167
235
  <>
@@ -171,37 +239,82 @@ export function Posts({ onNavigate }: PostsProps) {
171
239
  <thead className="bg-gray-50 border-b border-gray-200 sticky top-0">
172
240
  <tr>
173
241
  <th className="w-8 px-3 py-2 text-left">
174
- <input type="checkbox" checked={selectedPosts.length === filteredAndSorted.length && filteredAndSorted.length > 0} onChange={(e) => handleSelectAll(e.target.checked)} className="rounded border-gray-300" />
242
+ <input
243
+ type="checkbox"
244
+ checked={
245
+ selectedPosts.length === filteredAndSorted.length &&
246
+ filteredAndSorted.length > 0
247
+ }
248
+ onChange={(e) => handleSelectAll(e.target.checked)}
249
+ className="rounded border-gray-300"
250
+ />
251
+ </th>
252
+ <th className="px-3 py-2 text-left">
253
+ <SortHeader label="Title" sortKey="title" />
254
+ </th>
255
+ <th className="px-3 py-2 text-left">
256
+ <SortHeader label="Author" sortKey="author" />
257
+ </th>
258
+ <th className="px-3 py-2 text-left">
259
+ <SortHeader label="Category" sortKey="category" />
260
+ </th>
261
+ <th className="px-3 py-2 text-left">
262
+ <SortHeader label="Status" sortKey="status" />
263
+ </th>
264
+ <th className="px-3 py-2 text-left">
265
+ <SortHeader label="Date" sortKey="date" />
266
+ </th>
267
+ <th className="px-3 py-2 text-left text-xs font-medium text-gray-700">
268
+ Actions
175
269
  </th>
176
- <th className="px-3 py-2 text-left"><SortHeader label="Title" sortKey="title" /></th>
177
- <th className="px-3 py-2 text-left"><SortHeader label="Author" sortKey="author" /></th>
178
- <th className="px-3 py-2 text-left"><SortHeader label="Category" sortKey="category" /></th>
179
- <th className="px-3 py-2 text-left"><SortHeader label="Status" sortKey="status" /></th>
180
- <th className="px-3 py-2 text-left"><SortHeader label="Date" sortKey="date" /></th>
181
- <th className="px-3 py-2 text-left text-xs font-medium text-gray-700">Actions</th>
182
270
  </tr>
183
271
  </thead>
184
272
  <tbody className="divide-y divide-gray-200">
185
273
  {filteredAndSorted.map((post: any) => (
186
274
  <tr key={post.id} className="hover:bg-gray-50 transition-colors">
187
275
  <td className="px-3 py-2">
188
- <input type="checkbox" checked={selectedPosts.includes(post.id)} onChange={() => handleSelectPost(post.id)} className="rounded border-gray-300" />
276
+ <input
277
+ type="checkbox"
278
+ checked={selectedPosts.includes(post.id)}
279
+ onChange={() => handleSelectPost(post.id)}
280
+ className="rounded border-gray-300"
281
+ />
189
282
  </td>
190
283
  <td className="px-3 py-2">
191
- <button type="button" onClick={() => onNavigate?.(`/posts/${post.id}`)} className="font-medium text-gray-900 hover:text-blue-600 text-sm text-left">{post.title}</button>
284
+ <button
285
+ type="button"
286
+ onClick={() => onNavigate?.(`/posts/${post.id}`)}
287
+ className="font-medium text-gray-900 hover:text-blue-600 text-sm text-left"
288
+ >
289
+ {post.title}
290
+ </button>
192
291
  </td>
193
292
  <td className="px-3 py-2 text-sm text-gray-600">{post.author}</td>
194
293
  <td className="px-3 py-2 text-sm text-gray-600">{post.category}</td>
195
294
  <td className="px-3 py-2">
196
- <span className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${post.status === 'Published' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`}>{post.status}</span>
295
+ <span
296
+ className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium ${post.status === 'Published' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`}
297
+ >
298
+ {post.status}
299
+ </span>
197
300
  </td>
198
301
  <td className="px-3 py-2 text-sm text-gray-600">{post.date}</td>
199
302
  <td className="px-3 py-2">
200
303
  <div className="flex items-center gap-2">
201
- <button type="button" onClick={() => onNavigate?.(`/posts/${post.id}`)} className="p-1.5 hover:bg-gray-100 rounded transition-colors" title="Edit">
304
+ <button
305
+ type="button"
306
+ onClick={() => onNavigate?.(`/posts/${post.id}`)}
307
+ className="p-1.5 hover:bg-gray-100 rounded transition-colors"
308
+ title="Edit"
309
+ >
202
310
  <Pencil className="w-4 h-4 text-gray-600" />
203
311
  </button>
204
- <button type="button" onClick={() => handleDelete(post.id)} className="p-1.5 hover:bg-gray-100 rounded transition-colors" title="Delete">
312
+ <button
313
+ type="button"
314
+ onClick={() => handleDelete(post.id)}
315
+ className="p-1.5 hover:bg-gray-100 rounded transition-colors"
316
+ title="Delete"
317
+ >
205
318
  <Trash2 className="w-4 h-4 text-red-600" />
206
319
  </button>
207
320
  </div>
@@ -218,17 +331,36 @@ export function Posts({ onNavigate }: PostsProps) {
218
331
  {filteredAndSorted.map((post: any) => (
219
332
  <div key={post.id} className="p-3">
220
333
  <div className="flex items-start gap-3">
221
- <input type="checkbox" checked={selectedPosts.includes(post.id)} onChange={() => handleSelectPost(post.id)} className="rounded border-gray-300 mt-1" />
334
+ <input
335
+ type="checkbox"
336
+ checked={selectedPosts.includes(post.id)}
337
+ onChange={() => handleSelectPost(post.id)}
338
+ className="rounded border-gray-300 mt-1"
339
+ />
222
340
  <div className="flex-1 min-w-0">
223
- <button type="button" onClick={() => onNavigate?.(`/posts/${post.id}`)} className="font-medium text-sm text-gray-900 hover:text-blue-600 block mb-1 text-left">{post.title}</button>
341
+ <button
342
+ type="button"
343
+ onClick={() => onNavigate?.(`/posts/${post.id}`)}
344
+ className="font-medium text-sm text-gray-900 hover:text-blue-600 block mb-1 text-left"
345
+ >
346
+ {post.title}
347
+ </button>
224
348
  <div className="flex flex-wrap items-center gap-2 text-xs text-gray-600 mb-2">
225
349
  <span>{post.author}</span>
226
350
  <span>•</span>
227
351
  <span>{post.date}</span>
228
- <span className={`px-2 py-0.5 rounded-full text-xs font-medium ${post.status === 'Published' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`}>{post.status}</span>
352
+ <span
353
+ className={`px-2 py-0.5 rounded-full text-xs font-medium ${post.status === 'Published' ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'}`}
354
+ >
355
+ {post.status}
356
+ </span>
229
357
  </div>
230
358
  </div>
231
- <button type="button" onClick={() => handleDelete(post.id)} className="p-1.5 hover:bg-gray-100 rounded transition-colors">
359
+ <button
360
+ type="button"
361
+ onClick={() => handleDelete(post.id)}
362
+ className="p-1.5 hover:bg-gray-100 rounded transition-colors"
363
+ >
232
364
  <Trash2 className="w-4 h-4 text-red-600" />
233
365
  </button>
234
366
  </div>
@@ -239,5 +371,5 @@ export function Posts({ onNavigate }: PostsProps) {
239
371
  </>
240
372
  )}
241
373
  </div>
242
- );
374
+ )
243
375
  }