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
@@ -0,0 +1,457 @@
1
+ /**
2
+ * Shared Content Types Page Component
3
+ *
4
+ * Manages content type definitions with create/edit capabilities.
5
+ * Used by both CLI routes and embed pages.
6
+ */
7
+
8
+ import { useState, useMemo, useCallback } from "react";
9
+ import { useQuery } from "convex/react";
10
+ import { ContentTypeFormModal } from "~/components/ContentTypeFormModal";
11
+
12
+ interface ContentTypeField {
13
+ name: string;
14
+ label: string;
15
+ type: string;
16
+ }
17
+
18
+ interface ContentTypeWithCount {
19
+ _id: string;
20
+ name: string;
21
+ displayName: string;
22
+ description?: string;
23
+ fields: ContentTypeField[];
24
+ isActive: boolean;
25
+ singleton?: boolean;
26
+ entryCount?: number;
27
+ _creationTime: number;
28
+ }
29
+ import { CmsPageHeader } from "~/components/cmsds/CmsPageHeader";
30
+ import { CmsToolbar } from "~/components/cmsds/CmsToolbar";
31
+ import { CmsEmptyState } from "~/components/cmsds/CmsEmptyState";
32
+ import { CmsButton } from "~/components/cmsds/CmsButton";
33
+ import { Input } from "~/components/ui/input";
34
+ import { Checkbox } from "~/components/ui/checkbox";
35
+ import { Badge } from "~/components/ui/badge";
36
+ import { cn } from "~/lib/cn";
37
+ import {
38
+ Search,
39
+ Grid3X3,
40
+ List,
41
+ Plus,
42
+ FileType,
43
+ Hash,
44
+ ToggleLeft,
45
+ Calendar,
46
+ Link2,
47
+ Image,
48
+ Braces,
49
+ ChevronDown,
50
+ Tag,
51
+ FolderOpen,
52
+ AlignLeft,
53
+ Pencil,
54
+ } from "lucide-react";
55
+ import type { AdminNavigation } from "~/lib/navigation";
56
+ import { CmsAdminApi } from "~/embed/contexts/ApiContext";
57
+
58
+ type ViewMode = "grid" | "list";
59
+
60
+ const FIELD_ICONS: Record<string, React.ReactNode> = {
61
+ text: <AlignLeft className="size-3" />,
62
+ richText: <FileType className="size-3" />,
63
+ number: <Hash className="size-3" />,
64
+ boolean: <ToggleLeft className="size-3" />,
65
+ date: <Calendar className="size-3" />,
66
+ datetime: <Calendar className="size-3" />,
67
+ reference: <Link2 className="size-3" />,
68
+ media: <Image className="size-3" />,
69
+ json: <Braces className="size-3" />,
70
+ select: <ChevronDown className="size-3" />,
71
+ multiSelect: <List className="size-3" />,
72
+ tags: <Tag className="size-3" />,
73
+ category: <FolderOpen className="size-3" />,
74
+ };
75
+
76
+ export interface ContentTypesPageProps {
77
+ api: CmsAdminApi;
78
+ navigation: AdminNavigation;
79
+ }
80
+
81
+ export function ContentTypesPage({ api, navigation }: ContentTypesPageProps) {
82
+ const [searchQuery, setSearchQuery] = useState("");
83
+ const [viewMode, setViewMode] = useState<ViewMode>("grid");
84
+ const [showActiveOnly, setShowActiveOnly] = useState(true);
85
+ const [showCreateModal, setShowCreateModal] = useState(false);
86
+ const [
87
+ editingContentType,
88
+ setEditingContentType,
89
+ ] = useState<ContentTypeWithCount | null>(null);
90
+
91
+ const handleContentTypeCreated = useCallback(() => {
92
+ setShowCreateModal(false);
93
+ }, []);
94
+
95
+ const handleContentTypeUpdated = useCallback(() => {
96
+ setEditingContentType(null);
97
+ }, []);
98
+
99
+ const contentTypesResult = useQuery(api.listContentTypes, {
100
+ isActive: showActiveOnly ? true : undefined,
101
+ includeEntryCounts: true,
102
+ });
103
+
104
+ const contentTypes = (contentTypesResult?.page ??
105
+ []) as ContentTypeWithCount[];
106
+ const isLoading = contentTypesResult === undefined;
107
+
108
+ const filteredContentTypes = useMemo(() => {
109
+ if (!searchQuery.trim()) {
110
+ return contentTypes;
111
+ }
112
+
113
+ const query = searchQuery.toLowerCase();
114
+ return contentTypes.filter(
115
+ (type) =>
116
+ type.name.toLowerCase().includes(query) ||
117
+ type.displayName.toLowerCase().includes(query),
118
+ );
119
+ }, [contentTypes, searchQuery]);
120
+
121
+ const formatDate = (timestamp: number) => {
122
+ return new Date(timestamp).toLocaleDateString("en-US", {
123
+ year: "numeric",
124
+ month: "short",
125
+ day: "numeric",
126
+ });
127
+ };
128
+
129
+ const getRelativeTime = (timestamp: number) => {
130
+ const now = Date.now();
131
+ const diff = now - timestamp;
132
+ const seconds = Math.floor(diff / 1000);
133
+ const minutes = Math.floor(seconds / 60);
134
+ const hours = Math.floor(minutes / 60);
135
+ const days = Math.floor(hours / 24);
136
+
137
+ if (days > 30) {
138
+ return formatDate(timestamp);
139
+ } else if (days > 0) {
140
+ return `${days}d ago`;
141
+ } else if (hours > 0) {
142
+ return `${hours}h ago`;
143
+ } else if (minutes > 0) {
144
+ return `${minutes}m ago`;
145
+ } else {
146
+ return "Just now";
147
+ }
148
+ };
149
+
150
+ return (
151
+ <div className="space-y-6 p-6">
152
+ <CmsPageHeader
153
+ title="Content Types"
154
+ description="Define the structure of your content with custom fields and validation rules."
155
+ />
156
+
157
+ <CmsToolbar
158
+ left={
159
+ <div className="flex items-center gap-3">
160
+ <div className="relative">
161
+ <Search className="absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
162
+ <Input
163
+ type="search"
164
+ placeholder="Search content types..."
165
+ value={searchQuery}
166
+ onChange={(e) => setSearchQuery(e.target.value)}
167
+ className="w-64 pl-9"
168
+ />
169
+ </div>
170
+ <label className="flex cursor-pointer items-center gap-2 text-sm">
171
+ <Checkbox
172
+ checked={showActiveOnly}
173
+ onCheckedChange={(checked) =>
174
+ setShowActiveOnly(checked as boolean)
175
+ }
176
+ />
177
+ Active only
178
+ </label>
179
+ </div>
180
+ }
181
+ right={
182
+ <div className="flex items-center gap-2">
183
+ <div className="flex rounded-md border bg-muted/30">
184
+ <button
185
+ className={cn(
186
+ "rounded-l-md p-2 transition-colors",
187
+ viewMode === "grid"
188
+ ? "bg-background shadow-sm"
189
+ : "hover:bg-muted/50",
190
+ )}
191
+ onClick={() => setViewMode("grid")}
192
+ title="Grid view"
193
+ aria-label="Grid view"
194
+ >
195
+ <Grid3X3 className="size-4" />
196
+ </button>
197
+ <button
198
+ className={cn(
199
+ "rounded-r-md p-2 transition-colors",
200
+ viewMode === "list"
201
+ ? "bg-background shadow-sm"
202
+ : "hover:bg-muted/50",
203
+ )}
204
+ onClick={() => setViewMode("list")}
205
+ title="List view"
206
+ aria-label="List view"
207
+ >
208
+ <List className="size-4" />
209
+ </button>
210
+ </div>
211
+ <CmsButton
212
+ onClick={() => setShowCreateModal(true)}
213
+ data-testid="create-content-type-button"
214
+ >
215
+ <Plus className="size-4" />
216
+ Create Content Type
217
+ </CmsButton>
218
+ </div>
219
+ }
220
+ />
221
+
222
+ {isLoading ? (
223
+ <div className="flex flex-col items-center justify-center py-12">
224
+ <div className="size-8 animate-spin rounded-full border-2 border-muted border-t-primary" />
225
+ <p className="mt-4 text-sm text-muted-foreground">
226
+ Loading content types...
227
+ </p>
228
+ </div>
229
+ ) : filteredContentTypes.length === 0 ? (
230
+ <CmsEmptyState
231
+ icon={<FileType className="size-6" />}
232
+ title={
233
+ searchQuery
234
+ ? "No content types match your search"
235
+ : "No content types defined"
236
+ }
237
+ description={
238
+ searchQuery
239
+ ? "Try adjusting your search query or filters."
240
+ : "Content types define the schema for your content. Create one to get started."
241
+ }
242
+ />
243
+ ) : viewMode === "grid" ? (
244
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
245
+ {filteredContentTypes.map((contentType) => (
246
+ <div
247
+ key={contentType._id}
248
+ className="group flex flex-col rounded-lg border bg-card p-4 transition-all hover:border-primary/50 hover:shadow-md"
249
+ >
250
+ <div className="mb-3 flex items-center justify-between">
251
+ <div className="flex size-10 items-center justify-center rounded-md bg-muted text-muted-foreground">
252
+ <FileType className="size-5" />
253
+ </div>
254
+ <div className="flex items-center gap-1.5">
255
+ {!contentType.isActive && (
256
+ <Badge variant="secondary" className="text-xs font-normal">
257
+ Inactive
258
+ </Badge>
259
+ )}
260
+ {contentType.singleton && (
261
+ <Badge
262
+ variant="secondary"
263
+ className="border-amber-200 bg-amber-50 text-xs font-normal text-amber-700"
264
+ >
265
+ Singleton
266
+ </Badge>
267
+ )}
268
+ <button
269
+ onClick={() => setEditingContentType(contentType)}
270
+ className="rounded p-1.5 text-muted-foreground opacity-0 transition-all hover:bg-muted hover:text-foreground group-hover:opacity-100"
271
+ title="Edit content type"
272
+ aria-label="Edit content type"
273
+ >
274
+ <Pencil className="size-4" />
275
+ </button>
276
+ </div>
277
+ </div>
278
+
279
+ <button
280
+ type="button"
281
+ onClick={() =>
282
+ navigation.navigateToContentType(contentType._id)
283
+ }
284
+ className="flex flex-1 flex-col text-left"
285
+ >
286
+ <h3 className="text-base font-semibold text-foreground">
287
+ {contentType.displayName}
288
+ </h3>
289
+ <p className="mt-0.5 text-xs text-muted-foreground">
290
+ {contentType.name}
291
+ </p>
292
+
293
+ <div className="mt-3 flex items-center gap-4">
294
+ <div className="text-center">
295
+ <p className="text-lg font-semibold text-foreground">
296
+ {contentType.fields.length}
297
+ </p>
298
+ <p className="text-xs text-muted-foreground">
299
+ {contentType.fields.length === 1 ? "Field" : "Fields"}
300
+ </p>
301
+ </div>
302
+ <div className="text-center">
303
+ <p className="text-lg font-semibold text-foreground">
304
+ {contentType.entryCount ?? 0}
305
+ </p>
306
+ <p className="text-xs text-muted-foreground">
307
+ {contentType.entryCount === 1 ? "Entry" : "Entries"}
308
+ </p>
309
+ </div>
310
+ </div>
311
+
312
+ <div className="mt-3 flex flex-wrap gap-1.5">
313
+ {contentType.fields.slice(0, 4).map((field) => (
314
+ <span
315
+ key={field.name}
316
+ className="flex items-center gap-1 rounded-md bg-muted px-1.5 py-0.5 text-xs text-muted-foreground"
317
+ title={field.label}
318
+ >
319
+ {FIELD_ICONS[field.type] || (
320
+ <AlignLeft className="size-3" />
321
+ )}
322
+ <span className="truncate">{field.label}</span>
323
+ </span>
324
+ ))}
325
+ {contentType.fields.length > 4 && (
326
+ <span className="rounded-md bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
327
+ +{contentType.fields.length - 4} more
328
+ </span>
329
+ )}
330
+ </div>
331
+
332
+ <p className="mt-auto pt-3 text-xs text-muted-foreground">
333
+ Updated {getRelativeTime(contentType._creationTime)}
334
+ </p>
335
+ </button>
336
+ </div>
337
+ ))}
338
+ </div>
339
+ ) : (
340
+ <div className="rounded-lg border bg-card">
341
+ <table className="w-full">
342
+ <thead>
343
+ <tr className="border-b">
344
+ <th className="p-3 text-left text-sm font-medium text-muted-foreground">
345
+ Name
346
+ </th>
347
+ <th className="p-3 text-left text-sm font-medium text-muted-foreground">
348
+ Fields
349
+ </th>
350
+ <th className="p-3 text-left text-sm font-medium text-muted-foreground">
351
+ Entries
352
+ </th>
353
+ <th className="p-3 text-left text-sm font-medium text-muted-foreground">
354
+ Status
355
+ </th>
356
+ <th className="p-3 text-left text-sm font-medium text-muted-foreground">
357
+ Last Updated
358
+ </th>
359
+ <th className="p-3 text-left text-sm font-medium text-muted-foreground">
360
+ Actions
361
+ </th>
362
+ </tr>
363
+ </thead>
364
+ <tbody>
365
+ {filteredContentTypes.map((contentType) => (
366
+ <tr
367
+ key={contentType._id}
368
+ className="border-b last:border-0 transition-colors hover:bg-muted/50"
369
+ >
370
+ <td className="p-3">
371
+ <p className="font-medium text-foreground">
372
+ {contentType.displayName}
373
+ </p>
374
+ <p className="text-xs text-muted-foreground">
375
+ {contentType.name}
376
+ </p>
377
+ </td>
378
+ <td className="p-3 text-sm text-muted-foreground">
379
+ {contentType.fields.length}
380
+ </td>
381
+ <td className="p-3 text-sm text-muted-foreground">
382
+ {contentType.entryCount ?? 0}
383
+ </td>
384
+ <td className="p-3">
385
+ <div className="flex items-center gap-1.5">
386
+ <Badge
387
+ variant={contentType.isActive ? "default" : "secondary"}
388
+ className={cn(
389
+ "text-xs font-normal",
390
+ contentType.isActive &&
391
+ "border-emerald-200 bg-emerald-50 text-emerald-700",
392
+ )}
393
+ >
394
+ {contentType.isActive ? "Active" : "Inactive"}
395
+ </Badge>
396
+ {contentType.singleton && (
397
+ <Badge
398
+ variant="secondary"
399
+ className="border-amber-200 bg-amber-50 text-xs font-normal text-amber-700"
400
+ >
401
+ Singleton
402
+ </Badge>
403
+ )}
404
+ </div>
405
+ </td>
406
+ <td className="p-3 text-sm text-muted-foreground">
407
+ {formatDate(contentType._creationTime)}
408
+ </td>
409
+ <td className="p-3">
410
+ <div className="flex items-center gap-2">
411
+ <CmsButton
412
+ variant="outline"
413
+ size="sm"
414
+ onClick={() => setEditingContentType(contentType)}
415
+ >
416
+ <Pencil className="size-3.5" />
417
+ Edit
418
+ </CmsButton>
419
+ <CmsButton
420
+ variant="outline"
421
+ size="sm"
422
+ onClick={() =>
423
+ navigation.navigateToContentType(contentType._id)
424
+ }
425
+ >
426
+ View Entries
427
+ </CmsButton>
428
+ </div>
429
+ </td>
430
+ </tr>
431
+ ))}
432
+ </tbody>
433
+ </table>
434
+ </div>
435
+ )}
436
+
437
+ {!isLoading && filteredContentTypes.length > 0 && (
438
+ <p className="text-center text-sm text-muted-foreground">
439
+ Showing {filteredContentTypes.length} of {contentTypes.length} content
440
+ type
441
+ {contentTypes.length !== 1 ? "s" : ""}
442
+ </p>
443
+ )}
444
+
445
+ <ContentTypeFormModal
446
+ isOpen={showCreateModal || !!editingContentType}
447
+ onClose={() => {
448
+ setShowCreateModal(false);
449
+ setEditingContentType(null);
450
+ }}
451
+ contentType={editingContentType as any}
452
+ onCreated={handleContentTypeCreated}
453
+ onUpdated={handleContentTypeUpdated}
454
+ />
455
+ </div>
456
+ );
457
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Shared Dashboard Page Component
3
+ *
4
+ * Shows CMS overview with quick navigation cards and statistics.
5
+ * Used by both CLI routes and embed pages.
6
+ */
7
+
8
+ import { useQuery } from "convex/react";
9
+ import {
10
+ Card,
11
+ CardContent,
12
+ CardDescription,
13
+ CardHeader,
14
+ CardTitle,
15
+ } from "~/components/ui/card";
16
+ import { Skeleton } from "~/components/ui/skeleton";
17
+ import { CmsPageHeader } from "~/components/cmsds";
18
+ import { FileText, Image, Layers, Settings, TrendingUp } from "lucide-react";
19
+ import type { AdminNavigation } from "~/lib/navigation";
20
+ import type { CmsAdminApi } from "~/embed/contexts/ApiContext";
21
+
22
+ interface DashboardPageProps {
23
+ api: CmsAdminApi;
24
+ navigation: AdminNavigation;
25
+ }
26
+
27
+ export function DashboardPage({ api, navigation }: DashboardPageProps) {
28
+ const stats = useQuery(api.getDashboardStats, {});
29
+ const isLoading = stats === undefined;
30
+ const hasError = stats === null;
31
+
32
+ return (
33
+ <div className="space-y-8">
34
+ <CmsPageHeader
35
+ title="Dashboard"
36
+ description="Welcome to Convex CMS Admin. Manage your content, media, and publishing workflows."
37
+ />
38
+
39
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
40
+ <DashboardCard
41
+ title="Content Entries"
42
+ description="Create and manage your content"
43
+ onClick={() => navigation.navigate("/content")}
44
+ icon={<FileText className="size-5" />}
45
+ />
46
+ <DashboardCard
47
+ title="Media Library"
48
+ description="Upload and organize media assets"
49
+ onClick={() => navigation.navigate("/media")}
50
+ icon={<Image className="size-5" />}
51
+ />
52
+ <DashboardCard
53
+ title="Content Types"
54
+ description="Define content schemas and fields"
55
+ onClick={() => navigation.navigate("/content-types")}
56
+ icon={<Layers className="size-5" />}
57
+ />
58
+ <DashboardCard
59
+ title="Settings"
60
+ description="Configure CMS settings"
61
+ onClick={() => navigation.navigate("/settings")}
62
+ icon={<Settings className="size-5" />}
63
+ />
64
+ </div>
65
+
66
+ <section className="space-y-4">
67
+ <div className="flex items-center gap-2">
68
+ <TrendingUp className="size-5 text-muted-foreground" />
69
+ <h2 className="text-lg font-semibold">Quick Stats</h2>
70
+ </div>
71
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
72
+ <StatCard
73
+ label="Content Types"
74
+ value={
75
+ isLoading
76
+ ? undefined
77
+ : hasError
78
+ ? "—"
79
+ : String(stats.contentTypes)
80
+ }
81
+ isLoading={isLoading}
82
+ />
83
+ <StatCard
84
+ label="Content Entries"
85
+ value={
86
+ isLoading
87
+ ? undefined
88
+ : hasError
89
+ ? "—"
90
+ : String(stats.contentEntries)
91
+ }
92
+ isLoading={isLoading}
93
+ />
94
+ <StatCard
95
+ label="Media Assets"
96
+ value={
97
+ isLoading ? undefined : hasError ? "—" : String(stats.mediaAssets)
98
+ }
99
+ isLoading={isLoading}
100
+ />
101
+ <StatCard
102
+ label="Published"
103
+ value={
104
+ isLoading ? undefined : hasError ? "—" : String(stats.published)
105
+ }
106
+ isLoading={isLoading}
107
+ />
108
+ </div>
109
+ </section>
110
+ </div>
111
+ );
112
+ }
113
+
114
+ function DashboardCard({
115
+ title,
116
+ description,
117
+ onClick,
118
+ icon,
119
+ }: {
120
+ title: string;
121
+ description: string;
122
+ onClick: () => void;
123
+ icon: React.ReactNode;
124
+ }) {
125
+ return (
126
+ <button type="button" onClick={onClick} className="text-left">
127
+ <Card className="h-full transition-colors hover:bg-accent/50">
128
+ <CardHeader className="pb-2">
129
+ <div className="mb-2 flex size-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
130
+ {icon}
131
+ </div>
132
+ <CardTitle className="text-base">{title}</CardTitle>
133
+ </CardHeader>
134
+ <CardContent>
135
+ <CardDescription>{description}</CardDescription>
136
+ </CardContent>
137
+ </Card>
138
+ </button>
139
+ );
140
+ }
141
+
142
+ function StatCard({
143
+ label,
144
+ value,
145
+ isLoading = false,
146
+ }: {
147
+ label: string;
148
+ value?: string;
149
+ isLoading?: boolean;
150
+ }) {
151
+ return (
152
+ <Card>
153
+ <CardContent className="p-4">
154
+ {isLoading ? (
155
+ <Skeleton className="mb-1 h-8 w-16" />
156
+ ) : (
157
+ <div className="text-2xl font-bold">{value}</div>
158
+ )}
159
+ <div className="text-sm text-muted-foreground">{label}</div>
160
+ </CardContent>
161
+ </Card>
162
+ );
163
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Shared Media Page Component
3
+ *
4
+ * Manages media assets and folders.
5
+ * Used by both CLI routes and embed pages.
6
+ */
7
+
8
+ import { CmsPageHeader } from "~/components/cmsds/CmsPageHeader";
9
+ import { CmsEmptyState } from "~/components/cmsds/CmsEmptyState";
10
+ import { Image } from "lucide-react";
11
+ import type { AdminNavigation } from "~/lib/navigation";
12
+ import type { CmsAdminApi } from "~/embed/contexts/ApiContext";
13
+
14
+ export interface MediaPageProps {
15
+ api: CmsAdminApi;
16
+ navigation: AdminNavigation;
17
+ }
18
+
19
+ export function MediaPage({ api: _api, navigation: _navigation }: MediaPageProps) {
20
+ return (
21
+ <div className="space-y-6 p-6">
22
+ <CmsPageHeader
23
+ title="Media Library"
24
+ description="Upload and manage media assets like images, videos, and documents."
25
+ />
26
+
27
+ <CmsEmptyState
28
+ icon={<Image className="size-6" />}
29
+ title="Media library coming soon"
30
+ description="The media management interface is under development."
31
+ />
32
+ </div>
33
+ );
34
+ }