convex-cms 0.0.5-alpha.0 → 0.0.5-alpha.3

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 (323) hide show
  1. package/README.md +95 -144
  2. package/admin/README.md +99 -0
  3. package/admin/src/components/AdminLayout.tsx +22 -0
  4. package/admin/src/components/BreakingChangesWarningDialog.tsx +81 -0
  5. package/admin/src/components/BulkActionBar.tsx +190 -0
  6. package/admin/src/components/BulkOperationModal.tsx +177 -0
  7. package/admin/src/components/ContentEntryEditor.tsx +1104 -0
  8. package/admin/src/components/ContentTypeFormModal.tsx +1012 -0
  9. package/admin/src/components/ErrorBoundary.tsx +83 -0
  10. package/admin/src/components/ErrorState.tsx +147 -0
  11. package/admin/src/components/Header.tsx +294 -0
  12. package/admin/src/components/RouteGuard.tsx +264 -0
  13. package/admin/src/components/Sidebar.tsx +90 -0
  14. package/admin/src/components/TaxonomyEditor.tsx +348 -0
  15. package/admin/src/components/TermTree.tsx +533 -0
  16. package/admin/src/components/UploadDropzone.tsx +383 -0
  17. package/admin/src/components/VersionCompare.tsx +250 -0
  18. package/admin/src/components/VersionHistory.tsx +279 -0
  19. package/admin/src/components/VersionRollbackModal.tsx +79 -0
  20. package/admin/src/components/cmsds/CmsButton.tsx +101 -0
  21. package/admin/src/components/cmsds/CmsDialog.tsx +139 -0
  22. package/admin/src/components/cmsds/CmsDropdown.tsx +62 -0
  23. package/admin/src/components/cmsds/CmsEmptyState.tsx +54 -0
  24. package/admin/src/components/cmsds/CmsField.tsx +47 -0
  25. package/admin/src/components/cmsds/CmsPageHeader.tsx +35 -0
  26. package/admin/src/components/cmsds/CmsStatusBadge.tsx +153 -0
  27. package/admin/src/components/cmsds/CmsSurface.tsx +52 -0
  28. package/admin/src/components/cmsds/CmsTable.tsx +164 -0
  29. package/admin/src/components/cmsds/CmsToolbar.tsx +58 -0
  30. package/admin/src/components/cmsds/index.ts +10 -0
  31. package/admin/src/components/fields/BooleanField.tsx +74 -0
  32. package/admin/src/components/fields/CategoryField.tsx +394 -0
  33. package/admin/src/components/fields/DateField.tsx +173 -0
  34. package/admin/src/components/fields/DefaultFieldRenderer.tsx +74 -0
  35. package/admin/src/components/fields/FieldRenderer.tsx +180 -0
  36. package/admin/src/components/fields/FieldWrapper.tsx +57 -0
  37. package/admin/src/components/fields/JsonField.tsx +172 -0
  38. package/admin/src/components/fields/MediaField.tsx +367 -0
  39. package/admin/src/components/fields/MultiSelectField.tsx +118 -0
  40. package/admin/src/components/fields/NumberField.tsx +77 -0
  41. package/admin/src/components/fields/ReferenceField.tsx +386 -0
  42. package/admin/src/components/fields/RichTextField.tsx +171 -0
  43. package/admin/src/components/fields/SelectField.tsx +62 -0
  44. package/admin/src/components/fields/TagField.tsx +325 -0
  45. package/admin/src/components/fields/TextAreaField.tsx +68 -0
  46. package/admin/src/components/fields/TextField.tsx +56 -0
  47. package/admin/src/components/fields/index.ts +54 -0
  48. package/admin/src/components/fields/registry.ts +64 -0
  49. package/admin/src/components/fields/types.ts +217 -0
  50. package/admin/src/components/filters/TaxonomyFilter.tsx +254 -0
  51. package/admin/src/components/filters/index.ts +1 -0
  52. package/admin/src/components/index.ts +8 -0
  53. package/admin/src/components/media/MediaAssetActions.tsx +115 -0
  54. package/admin/src/components/media/MediaAssetEditDialog.tsx +217 -0
  55. package/admin/src/components/media/MediaBulkActionBar.tsx +51 -0
  56. package/admin/src/components/media/MediaFolderActions.tsx +69 -0
  57. package/admin/src/components/media/MediaFolderEditDialog.tsx +126 -0
  58. package/admin/src/components/media/MediaMoveModal.tsx +179 -0
  59. package/admin/src/components/media/MediaPreviewModal.tsx +370 -0
  60. package/admin/src/components/media/MediaTaxonomyPicker.tsx +304 -0
  61. package/admin/src/components/media/MediaTrashBulkActionBar.tsx +59 -0
  62. package/admin/src/components/ui/accordion.tsx +64 -0
  63. package/admin/src/components/ui/alert-dialog.tsx +155 -0
  64. package/admin/src/components/ui/alert.tsx +66 -0
  65. package/admin/src/components/ui/avatar.tsx +53 -0
  66. package/admin/src/components/ui/badge.tsx +46 -0
  67. package/admin/src/components/ui/breadcrumb.tsx +109 -0
  68. package/admin/src/components/ui/button.tsx +62 -0
  69. package/admin/src/components/ui/calendar.tsx +220 -0
  70. package/admin/src/components/ui/card.tsx +92 -0
  71. package/admin/src/components/ui/checkbox.tsx +30 -0
  72. package/admin/src/components/ui/command.tsx +182 -0
  73. package/admin/src/components/ui/dialog.tsx +143 -0
  74. package/admin/src/components/ui/dropdown-menu.tsx +257 -0
  75. package/admin/src/components/ui/form.tsx +167 -0
  76. package/admin/src/components/ui/input.tsx +21 -0
  77. package/admin/src/components/ui/label.tsx +24 -0
  78. package/admin/src/components/ui/popover.tsx +46 -0
  79. package/admin/src/components/ui/scroll-area.tsx +56 -0
  80. package/admin/src/components/ui/select.tsx +190 -0
  81. package/admin/src/components/ui/separator.tsx +26 -0
  82. package/admin/src/components/ui/sheet.tsx +137 -0
  83. package/admin/src/components/ui/sidebar.tsx +724 -0
  84. package/admin/src/components/ui/skeleton.tsx +13 -0
  85. package/admin/src/components/ui/sonner.tsx +38 -0
  86. package/admin/src/components/ui/switch.tsx +31 -0
  87. package/admin/src/components/ui/table.tsx +114 -0
  88. package/admin/src/components/ui/tabs.tsx +66 -0
  89. package/admin/src/components/ui/textarea.tsx +18 -0
  90. package/admin/src/components/ui/tooltip.tsx +61 -0
  91. package/admin/src/contexts/AdminConfigContext.tsx +30 -0
  92. package/admin/src/contexts/AuthContext.tsx +330 -0
  93. package/admin/src/contexts/BreadcrumbContext.tsx +49 -0
  94. package/admin/src/contexts/SettingsConfigContext.tsx +57 -0
  95. package/admin/src/contexts/ThemeContext.tsx +91 -0
  96. package/admin/src/contexts/index.ts +20 -0
  97. package/admin/src/embed/components/EmbedHeader.tsx +103 -0
  98. package/admin/src/embed/components/EmbedLayout.tsx +29 -0
  99. package/admin/src/embed/components/EmbedSidebar.tsx +119 -0
  100. package/admin/src/embed/components/index.ts +3 -0
  101. package/admin/src/embed/contexts/ApiContext.tsx +32 -0
  102. package/admin/src/embed/index.tsx +184 -0
  103. package/admin/src/embed/navigation.tsx +202 -0
  104. package/admin/src/embed/pages/Content.tsx +19 -0
  105. package/admin/src/embed/pages/ContentTypes.tsx +19 -0
  106. package/admin/src/embed/pages/Dashboard.tsx +19 -0
  107. package/admin/src/embed/pages/Media.tsx +19 -0
  108. package/admin/src/embed/pages/Settings.tsx +22 -0
  109. package/admin/src/embed/pages/Taxonomies.tsx +22 -0
  110. package/admin/src/embed/pages/Trash.tsx +22 -0
  111. package/admin/src/embed/pages/index.ts +7 -0
  112. package/admin/src/embed/types.ts +24 -0
  113. package/admin/src/hooks/index.ts +2 -0
  114. package/admin/src/hooks/use-mobile.ts +19 -0
  115. package/admin/src/hooks/useBreadcrumbLabel.ts +15 -0
  116. package/admin/src/hooks/usePermissions.ts +211 -0
  117. package/admin/src/lib/admin-config.ts +111 -0
  118. package/admin/src/lib/cn.ts +6 -0
  119. package/admin/src/lib/config.server.ts +56 -0
  120. package/admin/src/lib/convex.ts +26 -0
  121. package/admin/src/lib/embed-adapter.ts +80 -0
  122. package/admin/src/lib/icons.tsx +96 -0
  123. package/admin/src/lib/loadAdminConfig.ts +92 -0
  124. package/admin/src/lib/motion.ts +29 -0
  125. package/admin/src/lib/navigation.ts +43 -0
  126. package/admin/src/lib/tanstack-adapter.ts +82 -0
  127. package/admin/src/pages/ContentPage.tsx +337 -0
  128. package/admin/src/pages/ContentTypesPage.tsx +457 -0
  129. package/admin/src/pages/DashboardPage.tsx +163 -0
  130. package/admin/src/pages/MediaPage.tsx +34 -0
  131. package/admin/src/pages/SettingsPage.tsx +486 -0
  132. package/admin/src/pages/TaxonomiesPage.tsx +289 -0
  133. package/admin/src/pages/TrashPage.tsx +421 -0
  134. package/admin/src/pages/index.ts +14 -0
  135. package/admin/src/routeTree.gen.ts +262 -0
  136. package/admin/src/router.tsx +22 -0
  137. package/admin/src/routes/__root.tsx +250 -0
  138. package/admin/src/routes/content-types.tsx +20 -0
  139. package/admin/src/routes/content.tsx +20 -0
  140. package/admin/src/routes/entries/$entryId.tsx +107 -0
  141. package/admin/src/routes/entries/new.$contentTypeId.tsx +69 -0
  142. package/admin/src/routes/entries/type/$contentTypeId.tsx +503 -0
  143. package/admin/src/routes/index.tsx +20 -0
  144. package/admin/src/routes/media.tsx +1095 -0
  145. package/admin/src/routes/settings.tsx +20 -0
  146. package/admin/src/routes/taxonomies.tsx +20 -0
  147. package/admin/src/routes/trash.tsx +20 -0
  148. package/admin/src/styles/globals.css +69 -0
  149. package/admin/src/styles/tailwind-config.css +74 -0
  150. package/admin/src/styles/theme.css +73 -0
  151. package/admin/src/types/index.ts +221 -0
  152. package/admin/src/utils/errorParsing.ts +163 -0
  153. package/admin/src/utils/index.ts +5 -0
  154. package/admin/src/vite-env.d.ts +14 -0
  155. package/admin/tailwind.preset.cjs +102 -0
  156. package/admin-dist/nitro.json +1 -1
  157. package/admin-dist/public/assets/{CmsEmptyState-CiMQwSQV.js → CmsEmptyState-CkqBIab3.js} +1 -1
  158. package/admin-dist/public/assets/{CmsPageHeader-ohOq0luT.js → CmsPageHeader-CUtl5MMG.js} +1 -1
  159. package/admin-dist/public/assets/{CmsStatusBadge-BdNf0V9v.js → CmsStatusBadge-CUYFgEe-.js} +1 -1
  160. package/admin-dist/public/assets/{CmsSurface-CWup6Jh7.js → CmsSurface-CsJfAVa3.js} +1 -1
  161. package/admin-dist/public/assets/{CmsToolbar-cEBlCHa3.js → CmsToolbar-CnfbcxeP.js} +1 -1
  162. package/admin-dist/public/assets/{ContentEntryEditor-BY5ypfUs.js → ContentEntryEditor-BU220CCy.js} +1 -1
  163. package/admin-dist/public/assets/TaxonomyFilter-CWCxC5HZ.js +1 -0
  164. package/admin-dist/public/assets/_contentTypeId-DK8cskRt.js +1 -0
  165. package/admin-dist/public/assets/{_entryId-BpSmrfAm.js → _entryId-CuVMExbb.js} +1 -1
  166. package/admin-dist/public/assets/{alert-Bf2l8kxw.js → alert-CF1BSzGR.js} +1 -1
  167. package/admin-dist/public/assets/{badge-qPrc4AUM.js → badge-CmuOIVKp.js} +1 -1
  168. package/admin-dist/public/assets/{circle-check-big-Dgozy3vV.js → circle-check-big-BKDVG6DU.js} +1 -1
  169. package/admin-dist/public/assets/{command-QOmNhlb0.js → command-XJxnF2Sd.js} +1 -1
  170. package/admin-dist/public/assets/content-QBUxdxbS.js +1 -0
  171. package/admin-dist/public/assets/content-types-CrNEm8Hf.js +2 -0
  172. package/admin-dist/public/assets/globals-B7Wsfh_v.css +1 -0
  173. package/admin-dist/public/assets/index-C7xOwudI.js +1 -0
  174. package/admin-dist/public/assets/{label-DCsUdvFh.js → label-CHCnXeBk.js} +1 -1
  175. package/admin-dist/public/assets/{link-2-Czw1N61H.js → link-2-Bb34judH.js} +1 -1
  176. package/admin-dist/public/assets/{list-DtCsXj8-.js → list-9Pzt48ld.js} +1 -1
  177. package/admin-dist/public/assets/{main-CXgkZMhe.js → main-CjQ2VI9L.js} +3 -3
  178. package/admin-dist/public/assets/media-Dc5PWt2Q.js +1 -0
  179. package/admin-dist/public/assets/{new._contentTypeId-CoTDxKzf.js → new._contentTypeId-C_I4YxIa.js} +1 -1
  180. package/admin-dist/public/assets/{plus-xCFJK0RC.js → plus-Ceef7DHk.js} +1 -1
  181. package/admin-dist/public/assets/{rotate-ccw-DIqK63wY.js → rotate-ccw-7k7-4VUq.js} +1 -1
  182. package/admin-dist/public/assets/{scroll-area-B-yrE66a.js → scroll-area-CC6wujnp.js} +1 -1
  183. package/admin-dist/public/assets/{search-CbCbboeU.js → search-DwoUV2pv.js} +1 -1
  184. package/admin-dist/public/assets/{select-Co3TZFJb.js → select-hOZTp8aC.js} +1 -1
  185. package/admin-dist/public/assets/{settings-BspTTv_o.js → settings-t2PbCZh4.js} +1 -1
  186. package/admin-dist/public/assets/{switch-CfavASmR.js → switch-jX2pDaNU.js} +1 -1
  187. package/admin-dist/public/assets/{tabs-CN5s5u2W.js → tabs-q4EbZk7c.js} +1 -1
  188. package/admin-dist/public/assets/{tanstack-adapter-npeE3RdY.js → tanstack-adapter-B-Glm4kH.js} +1 -1
  189. package/admin-dist/public/assets/taxonomies-kyk5P4ZW.js +1 -0
  190. package/admin-dist/public/assets/{textarea-BJ0XFZpT.js → textarea-B6SfBmr0.js} +1 -1
  191. package/admin-dist/public/assets/trash-BOCnIznD.js +1 -0
  192. package/admin-dist/public/assets/{triangle-alert-BZRcqsUg.js → triangle-alert-CXFIO_Gu.js} +1 -1
  193. package/admin-dist/public/assets/{useBreadcrumbLabel-DwZlwvFF.js → useBreadcrumbLabel-_6qBagc3.js} +1 -1
  194. package/admin-dist/public/assets/{usePermissions-C1JQhfqb.js → usePermissions-M1ijZ7a6.js} +1 -1
  195. package/admin-dist/server/_ssr/{CmsButton-B45JAKR1.mjs → CmsButton-DOiTVKQq.mjs} +1 -1
  196. package/admin-dist/server/_ssr/{CmsEmptyState-D_BQFAVR.mjs → CmsEmptyState-fbnGt3LD.mjs} +2 -2
  197. package/admin-dist/server/_ssr/{CmsPageHeader-CrUZA59A.mjs → CmsPageHeader-DHRrdOZa.mjs} +1 -1
  198. package/admin-dist/server/_ssr/{CmsStatusBadge-B-sj6yaj.mjs → CmsStatusBadge-s7obWbKZ.mjs} +2 -2
  199. package/admin-dist/server/_ssr/{CmsSurface-DKJZhpjk.mjs → CmsSurface-rFoYjb62.mjs} +1 -1
  200. package/admin-dist/server/_ssr/{CmsToolbar-ByaW5iXf.mjs → CmsToolbar-zTE45z2q.mjs} +2 -2
  201. package/admin-dist/server/_ssr/{ContentEntryEditor-D3_Jb1dq.mjs → ContentEntryEditor-BLoEjT_m.mjs} +12 -12
  202. package/admin-dist/server/_ssr/{TaxonomyFilter-BRJkuCtA.mjs → TaxonomyFilter-XAtaJC2z.mjs} +5 -5
  203. package/admin-dist/server/_ssr/{_contentTypeId-B9kA6CaM.mjs → _contentTypeId-Csl4822C.mjs} +13 -13
  204. package/admin-dist/server/_ssr/{_entryId-BddcMkZN.mjs → _entryId-D8alLFBx.mjs} +15 -15
  205. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BffZedId.mjs +4 -0
  206. package/admin-dist/server/_ssr/{command-CGtVr8Gb.mjs → command-C0Di14--.mjs} +1 -1
  207. package/admin-dist/server/_ssr/{content-D1tbeOd0.mjs → content-CT-FPsmV.mjs} +12 -55
  208. package/admin-dist/server/_ssr/{content-types-BZqY_BER.mjs → content-types-C8cBFdzE.mjs} +15 -46
  209. package/admin-dist/server/_ssr/{index-BIdq4xaC.mjs → index-BJtcrEc-.mjs} +5 -5
  210. package/admin-dist/server/_ssr/index.mjs +2 -2
  211. package/admin-dist/server/_ssr/{label-T-QNKAr6.mjs → label-qn2Afwl4.mjs} +1 -1
  212. package/admin-dist/server/_ssr/{media-C-xqjBrl.mjs → media-qv5IAsMZ.mjs} +14 -14
  213. package/admin-dist/server/_ssr/{new._contentTypeId-DWic9cRq.mjs → new._contentTypeId-DdGyrhqs.mjs} +13 -13
  214. package/admin-dist/server/_ssr/{router-D1BMAMJT.mjs → router-nSVkxb6Y.mjs} +11 -11
  215. package/admin-dist/server/_ssr/{scroll-area-C0pic_WA.mjs → scroll-area-BCinP455.mjs} +1 -1
  216. package/admin-dist/server/_ssr/{select-CqmuN2F6.mjs → select-BKQlQScw.mjs} +1 -1
  217. package/admin-dist/server/_ssr/{settings-CAkncGGV.mjs → settings-BCr2KQlk.mjs} +55 -40
  218. package/admin-dist/server/_ssr/{switch-CgmuJkT9.mjs → switch-BaOi42fE.mjs} +1 -1
  219. package/admin-dist/server/_ssr/{tabs-CnMj0aRy.mjs → tabs-DYXEi9kq.mjs} +2 -2
  220. package/admin-dist/server/_ssr/{tanstack-adapter-BXZrMauE.mjs → tanstack-adapter-Bsz8kha-.mjs} +1 -1
  221. package/admin-dist/server/_ssr/{taxonomies-thl3BfVm.mjs → taxonomies-CueMHTbE.mjs} +30 -19
  222. package/admin-dist/server/_ssr/{textarea-4K5OJgeh.mjs → textarea-CI0Jqx2x.mjs} +1 -1
  223. package/admin-dist/server/_ssr/{trash-B40Gx5zP.mjs → trash-DE6W8GoX.mjs} +20 -17
  224. package/admin-dist/server/_ssr/{useBreadcrumbLabel-rn-fL4zV.mjs → useBreadcrumbLabel-B5Yi72lM.mjs} +1 -1
  225. package/admin-dist/server/_ssr/{usePermissions-CKeM6_Vw.mjs → usePermissions-C3nZ-Izm.mjs} +1 -1
  226. package/admin-dist/server/index.mjs +187 -194
  227. package/dist/client/admin/bulk.d.ts +79 -0
  228. package/dist/client/admin/bulk.d.ts.map +1 -0
  229. package/dist/client/admin/bulk.js +72 -0
  230. package/dist/client/admin/bulk.js.map +1 -0
  231. package/dist/client/admin/contentLock.d.ts +118 -0
  232. package/dist/client/admin/contentLock.d.ts.map +1 -0
  233. package/dist/client/admin/contentLock.js +81 -0
  234. package/dist/client/admin/contentLock.js.map +1 -0
  235. package/dist/client/{adminApi.d.ts → admin/contentTypes.d.ts} +39 -1134
  236. package/dist/client/admin/contentTypes.d.ts.map +1 -0
  237. package/dist/client/admin/contentTypes.js +122 -0
  238. package/dist/client/admin/contentTypes.js.map +1 -0
  239. package/dist/client/admin/dashboard.d.ts +16 -0
  240. package/dist/client/admin/dashboard.d.ts.map +1 -0
  241. package/dist/client/admin/dashboard.js +33 -0
  242. package/dist/client/admin/dashboard.js.map +1 -0
  243. package/dist/client/admin/entries.d.ts +358 -0
  244. package/dist/client/admin/entries.d.ts.map +1 -0
  245. package/dist/client/admin/entries.js +220 -0
  246. package/dist/client/admin/entries.js.map +1 -0
  247. package/dist/client/admin/index.d.ts +6568 -0
  248. package/dist/client/admin/index.d.ts.map +1 -0
  249. package/dist/client/admin/index.js +305 -0
  250. package/dist/client/admin/index.js.map +1 -0
  251. package/dist/client/admin/media.d.ts +1038 -0
  252. package/dist/client/admin/media.d.ts.map +1 -0
  253. package/dist/client/admin/media.js +489 -0
  254. package/dist/client/admin/media.js.map +1 -0
  255. package/dist/client/admin/taxonomies.d.ts +339 -0
  256. package/dist/client/admin/taxonomies.d.ts.map +1 -0
  257. package/dist/client/admin/taxonomies.js +364 -0
  258. package/dist/client/admin/taxonomies.js.map +1 -0
  259. package/dist/client/admin/trash.d.ts +91 -0
  260. package/dist/client/admin/trash.d.ts.map +1 -0
  261. package/dist/client/admin/trash.js +71 -0
  262. package/dist/client/admin/trash.js.map +1 -0
  263. package/dist/client/admin/types.d.ts +320 -0
  264. package/dist/client/admin/types.d.ts.map +1 -0
  265. package/dist/client/admin/types.js +7 -0
  266. package/dist/client/admin/types.js.map +1 -0
  267. package/dist/client/admin/validators.d.ts +3886 -0
  268. package/dist/client/admin/validators.d.ts.map +1 -0
  269. package/dist/client/admin/validators.js +322 -0
  270. package/dist/client/admin/validators.js.map +1 -0
  271. package/dist/client/admin/versions.d.ts +106 -0
  272. package/dist/client/admin/versions.d.ts.map +1 -0
  273. package/dist/client/admin/versions.js +57 -0
  274. package/dist/client/admin/versions.js.map +1 -0
  275. package/dist/client/adminApiTypes.d.ts +27 -0
  276. package/dist/client/adminApiTypes.d.ts.map +1 -0
  277. package/dist/client/adminApiTypes.js +12 -0
  278. package/dist/client/adminApiTypes.js.map +1 -0
  279. package/dist/client/{admin-config.d.ts → adminConfig.d.ts} +2 -2
  280. package/dist/client/adminConfig.d.ts.map +1 -0
  281. package/dist/client/{admin-config.js → adminConfig.js} +1 -1
  282. package/dist/client/adminConfig.js.map +1 -0
  283. package/dist/client/agentTools.d.ts +4 -4
  284. package/dist/client/index.d.ts +2 -2
  285. package/dist/client/index.d.ts.map +1 -1
  286. package/dist/client/index.js +15 -2
  287. package/dist/client/index.js.map +1 -1
  288. package/dist/component/contentEntries.d.ts +4 -4
  289. package/dist/component/contentEntryMutations.d.ts +46 -0
  290. package/dist/component/contentEntryMutations.d.ts.map +1 -1
  291. package/dist/component/contentEntryMutations.js +1 -1
  292. package/dist/component/contentEntryMutations.js.map +1 -1
  293. package/dist/component/contentTypeMigration.d.ts +1 -1
  294. package/dist/component/contentTypeMutations.d.ts +22 -0
  295. package/dist/component/contentTypeMutations.d.ts.map +1 -1
  296. package/dist/component/contentTypeMutations.js +1 -1
  297. package/dist/component/contentTypeMutations.js.map +1 -1
  298. package/dist/component/mediaAssetMutations.d.ts +47 -0
  299. package/dist/component/mediaAssetMutations.d.ts.map +1 -1
  300. package/dist/component/mediaAssetMutations.js +1 -1
  301. package/dist/component/mediaAssetMutations.js.map +1 -1
  302. package/dist/component/schema.d.ts +9 -0
  303. package/dist/component/schema.d.ts.map +1 -1
  304. package/dist/component/schema.js +1 -1
  305. package/dist/component/schema.js.map +1 -1
  306. package/package.json +85 -3
  307. package/admin-dist/public/assets/ErrorState-C4nJ-ml4.js +0 -1
  308. package/admin-dist/public/assets/TaxonomyFilter-BgE_SR_O.js +0 -1
  309. package/admin-dist/public/assets/_contentTypeId-DtZectcC.js +0 -1
  310. package/admin-dist/public/assets/content-OEBGlxg1.js +0 -1
  311. package/admin-dist/public/assets/content-types-CjQliqVV.js +0 -2
  312. package/admin-dist/public/assets/globals-hAmgC66w.css +0 -1
  313. package/admin-dist/public/assets/index-BH_ECMhv.js +0 -1
  314. package/admin-dist/public/assets/media-DTJ3-ViE.js +0 -1
  315. package/admin-dist/public/assets/taxonomies-CgG46fIF.js +0 -1
  316. package/admin-dist/public/assets/trash-B3daldm5.js +0 -1
  317. package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +0 -89
  318. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dd7AmelK.mjs +0 -4
  319. package/dist/client/admin-config.d.ts.map +0 -1
  320. package/dist/client/admin-config.js.map +0 -1
  321. package/dist/client/adminApi.d.ts.map +0 -1
  322. package/dist/client/adminApi.js +0 -736
  323. package/dist/client/adminApi.js.map +0 -1
package/README.md CHANGED
@@ -1,189 +1,140 @@
1
1
  # Convex CMS
2
2
 
3
- A developer-first headless CMS built as a [Convex Component](https://docs.convex.dev/components).
3
+ A headless CMS built as a [Convex Component](https://docs.convex.dev/components) — content management that runs inside your Convex app.
4
4
 
5
- ## Features
5
+ ## Choose Your Path
6
6
 
7
- - **Type-safe APIs** - Full TypeScript support with generated types
8
- - **Flexible content modeling** - Define content types with 13 field types
9
- - **Publishing workflows** - Draft, publish, schedule, and version content
10
- - **Media management** - Upload, organize, and serve media with variants
11
- - **Role-based access control** - Fine-grained permissions with custom roles
12
- - **Multi-locale support** - Content localization with fallback chains
13
- - **Admin UI** - Ready-to-use React admin interface (CLI or embeddable)
7
+ ### Need an Admin Interface?
14
8
 
15
- ## Installation
9
+ Use **`defineAdminAPI`** — one line creates all the backend functions for a working admin UI.
16
10
 
17
- ```bash
18
- npm install convex-cms
11
+ ```
12
+ Your App Admin UI
13
+ │ │
14
+ └── convex/admin.ts ────────────┘
15
+ defineAdminAPI()
16
+
17
+ ├── listContentTypes
18
+ ├── getEntry
19
+ ├── publishEntry
20
+ └── ... (30+ functions)
21
+
22
+
23
+ CMS Component
19
24
  ```
20
25
 
21
- ## Quick Start
26
+ **[Admin UI Setup Guide](./docs/guides/admin-ui-setup.md)**
22
27
 
23
- ### 1. Configure Convex Component
28
+ ### Building Custom Content Logic?
24
29
 
25
- ```typescript
26
- // convex/convex.config.ts
27
- import { defineApp } from "convex/server";
28
- import convexCms from "convex-cms/convex.config";
30
+ Use **`createCmsClient`** — full programmatic control with typed methods in your Convex functions.
29
31
 
30
- const app = defineApp();
31
- app.use(convexCms);
32
- export default app;
33
32
  ```
34
-
35
- ### 2. Initialize Admin API
36
-
37
- ```bash
38
- npx convex-cms init
33
+ Your Convex Functions
34
+
35
+ └── cms.contentEntries.list(ctx, { status: "published" })
36
+ cms.contentTypes.create(ctx, { name: "blog", ... })
37
+ cms.mediaAssets.upload(ctx, { ... })
38
+
39
+
40
+ CMS Component
39
41
  ```
40
42
 
41
- This creates `convex/admin.ts` with all the admin functions the UI needs.
43
+ **[Getting Started Guide](./docs/guides/getting-started.md)**
42
44
 
43
- ### 3. Start Development
45
+ ### Want Full Type Safety?
44
46
 
45
- ```bash
46
- npx convex dev
47
- ```
47
+ Use **code-first schemas** — define content types with Convex validators, get TypeScript inference.
48
48
 
49
- ### 4. Access Admin UI
49
+ ```typescript
50
+ const blogPost = defineContentType({
51
+ name: "blog_post",
52
+ validator: v.object({
53
+ title: v.string(),
54
+ content: v.string(),
55
+ }),
56
+ });
50
57
 
51
- ```bash
52
- npx convex-cms admin
58
+ // TypeScript knows entry.data.title is string
59
+ const entry = await cms.typedContentEntries.get<"blog_post">(ctx, id);
53
60
  ```
54
61
 
55
- This opens the CMS admin interface at http://localhost:3000.
62
+ **[Code-First Schema Reference](./docs/api/code-first-schema.md)**
56
63
 
57
- ## Admin UI Options
64
+ ### Need Both?
58
65
 
59
- ### Option 1: Standalone CLI (Development)
66
+ **Most apps use both.** This is the typical setup:
60
67
 
61
- Perfect for quick access during development:
68
+ - `defineAdminAPI` powers the admin interface for content editors
69
+ - `createCmsClient` gives you typed methods for custom queries on your frontend
62
70
 
63
- ```bash
64
- npx convex-cms admin
65
- npx convex-cms admin --port 4000 # Custom port
66
- npx convex-cms admin --demo # Demo mode with mock data
67
- ```
71
+ They work together through the same CMS component.
68
72
 
69
- ### Option 2: Embed in Your App (Production)
70
-
71
- For production deployments, embed the admin UI in your application:
72
-
73
- ```tsx
74
- import { CmsAdmin } from "convex-cms-admin";
75
-
76
- function AdminPage() {
77
- return (
78
- <CmsAdmin
79
- convexUrl={process.env.VITE_CONVEX_URL}
80
- auth={{
81
- getUser: () => yourAuthProvider.getUser(),
82
- getUserRole: (id) => yourAuthProvider.getRole(id),
83
- onLogout: () => yourAuthProvider.signOut(),
84
- }}
85
- config={{
86
- branding: {
87
- appName: "My CMS",
88
- logoUrl: "/logo.svg",
89
- },
90
- }}
91
- />
92
- );
93
- }
94
- ```
95
-
96
- ## Using the CMS Client
73
+ ## Quick Start
97
74
 
98
- For programmatic access in your Convex functions:
75
+ ### 1. Install
99
76
 
100
- ```typescript
101
- // convex/cms.ts
102
- import { createCmsClient } from "convex-cms";
103
- import { components } from "./_generated/api";
104
-
105
- export const cms = createCmsClient(components.convexCms, {
106
- defaultLocale: "en",
107
- features: {
108
- versioning: true,
109
- localization: true,
110
- },
111
- });
77
+ ```bash
78
+ npm install convex-cms
112
79
  ```
113
80
 
81
+ ### 2. Add the Component
82
+
114
83
  ```typescript
115
- // convex/blog.ts
116
- import { query, mutation } from "./_generated/server";
117
- import { v } from "convex/values";
118
- import { cms } from "./cms";
119
-
120
- export const getPosts = query({
121
- handler: async (ctx) => {
122
- const result = await cms.contentEntries.list(ctx, {
123
- status: "published",
124
- });
125
- return result.items;
126
- },
127
- });
84
+ // convex/convex.config.ts
85
+ import { defineApp } from "convex/server";
86
+ import convexCms from "convex-cms/convex.config";
128
87
 
129
- export const createPost = mutation({
130
- args: { title: v.string(), content: v.string() },
131
- handler: async (ctx, args) => {
132
- return await cms.contentEntries.create(ctx, {
133
- contentTypeId: blogTypeId,
134
- data: args,
135
- });
136
- },
137
- });
88
+ const app = defineApp();
89
+ app.use(convexCms);
90
+ export default app;
138
91
  ```
139
92
 
140
- ## Auth Integration
93
+ ### 3. Choose Your Setup
141
94
 
142
- The admin UI is auth-agnostic - integrate with any provider:
95
+ **For Admin UI:** Run `npx convex-cms init` then `npx convex-cms admin`
96
+ → [Full Admin UI Setup](./docs/guides/admin-ui-setup.md)
143
97
 
144
- ### Frontend Auth Config
98
+ **For Custom Functions:** Create a CMS client and use it in your functions
99
+ → [Full Getting Started Guide](./docs/guides/getting-started.md)
145
100
 
146
- ```typescript
147
- <CmsAdmin
148
- auth={{
149
- // Return current user for display
150
- getUser: () => ({
151
- id: currentUser.id,
152
- name: currentUser.name,
153
- email: currentUser.email,
154
- }),
155
- // Map user ID to CMS role
156
- getUserRole: (userId) => userRoles[userId] ?? null,
157
- // Handle logout
158
- onLogout: () => signOut(),
159
- }}
160
- />
161
- ```
101
+ ## What's Included
162
102
 
163
- ### Backend Auth (Optional)
103
+ - **13 field types** — text, richText, media, reference, select, and more
104
+ - **Publishing workflows** — draft → scheduled → published with version history
105
+ - **Media management** — uploads, folders, variants, and metadata
106
+ - **RBAC** — 4 built-in roles + custom roles with fine-grained permissions
107
+ - **Multi-locale** — content localization with fallback chains
108
+ - **Admin UI** — pre-built React interface (CLI or embeddable)
109
+ - **Agent tools** — Zod schemas for AI integration
164
110
 
165
- Add auth validation to admin operations:
111
+ ## Admin UI Modes
166
112
 
167
- ```typescript
168
- // convex/admin.ts
169
- export const { ... } = defineAdminAPI(components.convexCms, {
170
- auth: async (ctx, operation) => {
171
- const identity = await ctx.auth.getUserIdentity();
172
- if (!identity) throw new Error("Unauthorized");
173
-
174
- // Optionally check operation.type for fine-grained access
175
- return identity.subject;
176
- },
177
- });
178
- ```
113
+ | Mode | Command | Best For |
114
+ |------|---------|----------|
115
+ | **CLI** | `npx convex-cms admin` | Development |
116
+ | **Embed** | `<CmsAdmin api={api.admin} />` | Production |
117
+
118
+ Both modes call the same functions from your `convex/admin.ts`.
179
119
 
180
120
  ## Documentation
181
121
 
182
- - [Getting Started Guide](./docs/guides/getting-started.md)
183
- - [Content Modeling](./docs/guides/content-modeling.md)
184
- - [Media Management](./docs/guides/media.md)
185
- - [Authorization](./docs/guides/authorization.md)
186
- - [API Reference](./docs/api/client-api.md)
122
+ | Guide | Description |
123
+ |-------|-------------|
124
+ | [Admin UI Setup](./docs/guides/admin-ui-setup.md) | CLI and embed modes, auth integration |
125
+ | [Getting Started](./docs/guides/getting-started.md) | Programmatic usage with createCmsClient |
126
+ | [Integration Patterns](./docs/guides/integration-patterns.md) | Common setups and when to use each |
127
+ | [Content Modeling](./docs/guides/content-modeling.md) | Content types and field definitions |
128
+ | [Authorization](./docs/guides/authorization.md) | Roles, permissions, and custom auth |
129
+ | [Media Management](./docs/guides/media.md) | Uploads, folders, and variants |
130
+
131
+ | Reference | Description |
132
+ |-----------|-------------|
133
+ | [Client API](./docs/api/client-api.md) | createCmsClient methods |
134
+ | [Admin API](./docs/api/admin-api.md) | defineAdminAPI functions |
135
+ | [Code-First Schema](./docs/api/code-first-schema.md) | TypeScript-first content types |
136
+ | [Field Types](./docs/api/field-types.md) | All 13 field types |
137
+ | [Configuration](./docs/api/configuration.md) | All config options |
187
138
 
188
139
  ## Requirements
189
140
 
@@ -0,0 +1,99 @@
1
+ # Convex CMS Admin UI
2
+
3
+ The Admin UI for Convex CMS, built with TanStack Start and React.
4
+
5
+ ## Getting Started
6
+
7
+ ### Prerequisites
8
+
9
+ - Node.js 18+
10
+ - A Convex account and project
11
+
12
+ ### Installation
13
+
14
+ ```bash
15
+ cd admin
16
+ npm install
17
+ ```
18
+
19
+ ### Development
20
+
21
+ 1. Start the Convex development server:
22
+
23
+ ```bash
24
+ npx convex dev
25
+ ```
26
+
27
+ This will generate the Convex API types and provide a `VITE_CONVEX_URL`.
28
+
29
+ 2. Create a `.env` file with your Convex URL:
30
+
31
+ ```bash
32
+ cp .env.example .env
33
+ # Edit .env with your VITE_CONVEX_URL from step 1
34
+ ```
35
+
36
+ 3. Start the development server:
37
+
38
+ ```bash
39
+ npm run dev:vite
40
+ ```
41
+
42
+ Or run both Convex and Vite together:
43
+
44
+ ```bash
45
+ npm run dev
46
+ ```
47
+
48
+ 4. Open [http://localhost:3000](http://localhost:3000) in your browser.
49
+
50
+ ## Project Structure
51
+
52
+ ```
53
+ admin/
54
+ ├── convex/ # Convex configuration
55
+ │ └── convex.config.ts # CMS component integration
56
+ ├── src/
57
+ │ ├── routes/ # TanStack Router routes
58
+ │ │ ├── __root.tsx # Root layout with Convex provider
59
+ │ │ ├── index.tsx # Dashboard page
60
+ │ │ ├── content.tsx # Content entries list
61
+ │ │ ├── media.tsx # Media library
62
+ │ │ ├── content-types.tsx # Content type management
63
+ │ │ └── settings.tsx # CMS settings
64
+ │ ├── components/ # Reusable React components
65
+ │ ├── lib/ # Utilities and helpers
66
+ │ │ └── convex.ts # Convex client utilities
67
+ │ ├── styles/ # CSS styles
68
+ │ │ └── app.css # Main stylesheet
69
+ │ └── router.tsx # Router configuration
70
+ ├── public/ # Static assets
71
+ ├── vite.config.ts # Vite + TanStack Start configuration
72
+ ├── tsconfig.json # TypeScript configuration
73
+ └── package.json # Dependencies and scripts
74
+ ```
75
+
76
+ ## Available Scripts
77
+
78
+ - `npm run dev` - Start Convex and Vite development servers
79
+ - `npm run dev:vite` - Start only the Vite development server
80
+ - `npm run dev:convex` - Start only the Convex development server
81
+ - `npm run build` - Build for production
82
+ - `npm run preview` - Preview production build
83
+ - `npm run typecheck` - Run TypeScript type checking
84
+
85
+ ## Features
86
+
87
+ - **Dashboard** - Overview of CMS status and quick navigation
88
+ - **Content Management** - Browse and manage content entries
89
+ - **Media Library** - Upload and organize media assets
90
+ - **Content Types** - Define content schemas with custom fields
91
+ - **Settings** - Configure CMS features and preferences
92
+
93
+ ## Technology Stack
94
+
95
+ - [TanStack Start](https://tanstack.com/start) - Full-stack React framework
96
+ - [TanStack Router](https://tanstack.com/router) - Type-safe routing
97
+ - [Convex](https://convex.dev) - Real-time backend platform
98
+ - [React 19](https://react.dev) - UI library
99
+ - [Vite 7](https://vitejs.dev) - Build tool
@@ -0,0 +1,22 @@
1
+ import type { ReactNode } from "react";
2
+ import { Sidebar } from "./Sidebar";
3
+ import { Header } from "./Header";
4
+ import { useAdminConfig } from "~/contexts";
5
+
6
+ interface AdminLayoutProps {
7
+ children: ReactNode;
8
+ }
9
+
10
+ export function AdminLayout({ children }: AdminLayoutProps) {
11
+ const { layout } = useAdminConfig();
12
+
13
+ return (
14
+ <div className="flex min-h-screen bg-background">
15
+ <Sidebar />
16
+ <div className="flex flex-1 flex-col" style={{ marginLeft: layout.sidebarWidth }}>
17
+ <Header />
18
+ <main className="flex-1 overflow-auto p-6">{children}</main>
19
+ </div>
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,81 @@
1
+ import { AlertTriangle } from 'lucide-react'
2
+ import { CmsDialog } from '~/components/cmsds/CmsDialog'
3
+ import { CmsButton } from '~/components/cmsds/CmsButton'
4
+
5
+ interface BreakingChangesWarningDialogProps {
6
+ isOpen: boolean
7
+ onClose: () => void
8
+ breakingChanges: string[]
9
+ onForceUpdate: () => void
10
+ onCancel: () => void
11
+ isLoading: boolean
12
+ }
13
+
14
+ export function BreakingChangesWarningDialog({
15
+ isOpen,
16
+ onClose,
17
+ breakingChanges,
18
+ onForceUpdate,
19
+ onCancel,
20
+ isLoading,
21
+ }: BreakingChangesWarningDialogProps) {
22
+ const handleCancel = () => {
23
+ onCancel()
24
+ onClose()
25
+ }
26
+
27
+ return (
28
+ <CmsDialog
29
+ open={isOpen}
30
+ onOpenChange={(open) => !open && !isLoading && handleCancel()}
31
+ title="Breaking Changes Detected"
32
+ size="lg"
33
+ footer={
34
+ <>
35
+ <CmsButton variant="outline" onClick={handleCancel} disabled={isLoading}>
36
+ Cancel
37
+ </CmsButton>
38
+ <CmsButton variant="danger" onClick={onForceUpdate} loading={isLoading}>
39
+ Force Update
40
+ </CmsButton>
41
+ </>
42
+ }
43
+ >
44
+ <div className="space-y-4">
45
+ <div className="flex items-start gap-3 rounded-lg border border-amber-200 bg-amber-50 p-3">
46
+ <AlertTriangle className="mt-0.5 size-5 shrink-0 text-amber-600" />
47
+ <div className="space-y-1">
48
+ <p className="text-sm font-medium text-amber-800">
49
+ These changes may affect existing content
50
+ </p>
51
+ <p className="text-sm text-amber-700">
52
+ The following changes could cause data loss or validation errors for existing entries.
53
+ Review carefully before proceeding.
54
+ </p>
55
+ </div>
56
+ </div>
57
+
58
+ <div className="space-y-2">
59
+ <p className="text-sm font-medium text-foreground">
60
+ {breakingChanges.length} breaking change{breakingChanges.length !== 1 ? 's' : ''} detected:
61
+ </p>
62
+ <ul className="space-y-2">
63
+ {breakingChanges.map((change, index) => (
64
+ <li
65
+ key={index}
66
+ className="flex items-start gap-2 rounded-md border bg-muted/30 px-3 py-2 text-sm"
67
+ >
68
+ <span className="mt-0.5 size-1.5 shrink-0 rounded-full bg-amber-500" />
69
+ <span className="text-muted-foreground">{change}</span>
70
+ </li>
71
+ ))}
72
+ </ul>
73
+ </div>
74
+
75
+ <p className="text-xs text-muted-foreground">
76
+ Click "Force Update" to apply these changes anyway, or "Cancel" to go back and modify your changes.
77
+ </p>
78
+ </div>
79
+ </CmsDialog>
80
+ )
81
+ }
@@ -0,0 +1,190 @@
1
+ import { useState, useCallback } from 'react'
2
+ import { useMutation } from 'convex/react'
3
+ import { api } from '../../convex/_generated/api'
4
+ import { BulkOperationModal } from './BulkOperationModal'
5
+ import { CmsButton } from '~/components/cmsds/CmsButton'
6
+ import { Badge } from '~/components/ui/badge'
7
+ import { X } from 'lucide-react'
8
+
9
+ type BulkAction = 'publish' | 'unpublish' | 'delete' | 'archive'
10
+
11
+ interface BulkActionBarProps {
12
+ selectedIds: string[]
13
+ onClearSelection: () => void
14
+ onOperationComplete?: () => void
15
+ }
16
+
17
+ export function BulkActionBar({
18
+ selectedIds,
19
+ onClearSelection,
20
+ onOperationComplete,
21
+ }: BulkActionBarProps) {
22
+ const [activeAction, setActiveAction] = useState<BulkAction | null>(null)
23
+ const [isProcessing, setIsProcessing] = useState(false)
24
+ const [result, setResult] = useState<{
25
+ succeeded: number
26
+ failed: number
27
+ errors?: string[]
28
+ } | null>(null)
29
+
30
+ const bulkPublish = useMutation(api.bulkOperations.bulkPublish)
31
+ const bulkUnpublish = useMutation(api.bulkOperations.bulkUnpublish)
32
+ const bulkDelete = useMutation(api.bulkOperations.bulkDelete)
33
+ const bulkUpdate = useMutation(api.bulkOperations.bulkUpdate)
34
+
35
+ const handleAction = useCallback((action: BulkAction) => {
36
+ setActiveAction(action)
37
+ setResult(null)
38
+ }, [])
39
+
40
+ const handleConfirm = useCallback(async () => {
41
+ if (!activeAction || selectedIds.length === 0) return
42
+
43
+ setIsProcessing(true)
44
+ setResult(null)
45
+
46
+ try {
47
+ let response: {
48
+ succeeded: number
49
+ failed: number
50
+ errors?: { id: string; error: string }[]
51
+ }
52
+
53
+ switch (activeAction) {
54
+ case 'publish':
55
+ response = await bulkPublish({
56
+ ids: selectedIds,
57
+ changeDescription: 'Bulk published from admin',
58
+ })
59
+ break
60
+ case 'unpublish':
61
+ response = await bulkUnpublish({
62
+ ids: selectedIds,
63
+ })
64
+ break
65
+ case 'delete':
66
+ response = await bulkDelete({
67
+ ids: selectedIds,
68
+ hardDelete: false,
69
+ })
70
+ break
71
+ case 'archive':
72
+ response = await bulkUpdate({
73
+ ids: selectedIds,
74
+ status: 'archived',
75
+ })
76
+ break
77
+ default:
78
+ throw new Error(`Unknown action: ${activeAction}`)
79
+ }
80
+
81
+ setResult({
82
+ succeeded: response.succeeded,
83
+ failed: response.failed,
84
+ errors: response.errors?.map((e) => `${e.id}: ${e.error}`),
85
+ })
86
+
87
+ if (response.failed === 0) {
88
+ setTimeout(() => {
89
+ setActiveAction(null)
90
+ onClearSelection()
91
+ onOperationComplete?.()
92
+ }, 1500)
93
+ }
94
+ } catch (error) {
95
+ const message = error instanceof Error ? error.message : 'Operation failed'
96
+ setResult({
97
+ succeeded: 0,
98
+ failed: selectedIds.length,
99
+ errors: [message],
100
+ })
101
+ } finally {
102
+ setIsProcessing(false)
103
+ }
104
+ }, [
105
+ activeAction,
106
+ selectedIds,
107
+ bulkPublish,
108
+ bulkUnpublish,
109
+ bulkDelete,
110
+ bulkUpdate,
111
+ onClearSelection,
112
+ onOperationComplete,
113
+ ])
114
+
115
+ const handleCancel = useCallback(() => {
116
+ setActiveAction(null)
117
+ setResult(null)
118
+ }, [])
119
+
120
+ if (selectedIds.length === 0) {
121
+ return null
122
+ }
123
+
124
+ return (
125
+ <>
126
+ <div className="fixed inset-x-0 bottom-0 z-50 border-t bg-background/95 px-6 py-3 shadow-lg backdrop-blur supports-[backdrop-filter]:bg-background/80">
127
+ <div className="mx-auto flex max-w-7xl items-center justify-between">
128
+ <div className="flex items-center gap-3">
129
+ <Badge variant="secondary" className="text-sm font-semibold">
130
+ {selectedIds.length}
131
+ </Badge>
132
+ <span className="text-sm text-muted-foreground">
133
+ {selectedIds.length === 1 ? 'item' : 'items'} selected
134
+ </span>
135
+ <button
136
+ type="button"
137
+ onClick={onClearSelection}
138
+ className="flex items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-foreground"
139
+ >
140
+ <X className="size-3" />
141
+ Clear
142
+ </button>
143
+ </div>
144
+
145
+ <div className="flex items-center gap-2">
146
+ <CmsButton
147
+ variant="success"
148
+ size="sm"
149
+ onClick={() => handleAction('publish')}
150
+ >
151
+ Publish
152
+ </CmsButton>
153
+ <CmsButton
154
+ variant="warning"
155
+ size="sm"
156
+ onClick={() => handleAction('unpublish')}
157
+ >
158
+ Unpublish
159
+ </CmsButton>
160
+ <CmsButton
161
+ variant="secondary"
162
+ size="sm"
163
+ onClick={() => handleAction('archive')}
164
+ >
165
+ Archive
166
+ </CmsButton>
167
+ <CmsButton
168
+ variant="danger"
169
+ size="sm"
170
+ onClick={() => handleAction('delete')}
171
+ >
172
+ Delete
173
+ </CmsButton>
174
+ </div>
175
+ </div>
176
+ </div>
177
+
178
+ {activeAction && (
179
+ <BulkOperationModal
180
+ action={activeAction}
181
+ count={selectedIds.length}
182
+ isProcessing={isProcessing}
183
+ result={result}
184
+ onConfirm={handleConfirm}
185
+ onCancel={handleCancel}
186
+ />
187
+ )}
188
+ </>
189
+ )
190
+ }