convex-cms 0.0.6 → 0.0.7-alpha.0

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 (296) hide show
  1. package/README.md +59 -97
  2. package/admin/src/components/ContentEntryEditor.tsx +2 -2
  3. package/admin/src/components/ContentTypeFormModal.tsx +121 -81
  4. package/admin/src/components/fields/ReferenceField.tsx +9 -9
  5. package/admin/src/pages/ContentPage.tsx +11 -19
  6. package/admin/src/pages/ContentTypesPage.tsx +62 -16
  7. package/admin/src/routes/entries/$entryId.tsx +1 -1
  8. package/admin/src/routes/entries/type/$contentTypeId.tsx +1 -1
  9. package/admin-dist/nitro.json +1 -1
  10. package/admin-dist/public/assets/CmsEmptyState-CXVkI3FZ.js +1 -0
  11. package/admin-dist/public/assets/CmsPageHeader-DU9fD34s.js +1 -0
  12. package/admin-dist/public/assets/{CmsStatusBadge-Dd9uToHE.js → CmsStatusBadge-nZ9TeLBL.js} +1 -1
  13. package/admin-dist/public/assets/{CmsSurface-DBy5Lumx.js → CmsSurface-DF7OcKg_.js} +1 -1
  14. package/admin-dist/public/assets/CmsToolbar-5S8FQrSx.js +1 -0
  15. package/admin-dist/public/assets/ContentEntryEditor-BDb44eTo.js +4 -0
  16. package/admin-dist/public/assets/TaxonomyFilter-DEN2Q9Lo.js +1 -0
  17. package/admin-dist/public/assets/_contentTypeId-Ba5iowxH.js +1 -0
  18. package/admin-dist/public/assets/_entryId-OY3sLz6O.js +1 -0
  19. package/admin-dist/public/assets/alert-BbW1Q9CR.js +1 -0
  20. package/admin-dist/public/assets/badge-DdM8Eua8.js +1 -0
  21. package/admin-dist/public/assets/{circle-check-big-CpLxAvEj.js → circle-check-big-B7eCOM8r.js} +1 -1
  22. package/admin-dist/public/assets/command-BIjzeKOv.js +1 -0
  23. package/admin-dist/public/assets/content-BV3YeSSW.js +1 -0
  24. package/admin-dist/public/assets/content-types-Bm4b2tf8.js +1 -0
  25. package/admin-dist/public/assets/globals-D41WzvyZ.css +1 -0
  26. package/admin-dist/public/assets/index-DnJ5Twlv.js +1 -0
  27. package/admin-dist/public/assets/main-BZB1uYTH.js +102 -0
  28. package/admin-dist/public/assets/media-BIMN5jXt.js +1 -0
  29. package/admin-dist/public/assets/new._contentTypeId-DTWb8ZDl.js +1 -0
  30. package/admin-dist/public/assets/pencil-BDQ1ZWRw.js +1 -0
  31. package/admin-dist/public/assets/{rotate-ccw-BZpZtw0N.js → rotate-ccw-BWblSIsl.js} +1 -1
  32. package/admin-dist/public/assets/scroll-area-BoaB6x8v.js +1 -0
  33. package/admin-dist/public/assets/{search-BvgYr-c9.js → search-CYMIpd39.js} +1 -1
  34. package/admin-dist/public/assets/settings-DaNDUtr5.js +1 -0
  35. package/admin-dist/public/assets/switch-DN7TOCa5.js +1 -0
  36. package/admin-dist/public/assets/tabs-RN__emeJ.js +1 -0
  37. package/admin-dist/public/assets/tanstack-adapter-DQcKErwf.js +1 -0
  38. package/admin-dist/public/assets/taxonomies-DylY9HE1.js +1 -0
  39. package/admin-dist/public/assets/trash-Dp_a2mpb.js +1 -0
  40. package/admin-dist/public/assets/{useBreadcrumbLabel-D00rvqjw.js → useBreadcrumbLabel-BQ9dJI6T.js} +1 -1
  41. package/admin-dist/public/assets/usePermissions-WUBNg_Id.js +1 -0
  42. package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +2 -2
  43. package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +1 -1
  44. package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +1 -1
  45. package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +2 -2
  46. package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +2 -2
  47. package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +1 -1
  48. package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +1 -1
  49. package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +1 -1
  50. package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +6 -72
  51. package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1 -1
  52. package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +1 -1
  53. package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +20 -435
  54. package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +30 -3
  55. package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +0 -376
  56. package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +168 -383
  57. package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +451 -1195
  58. package/admin-dist/server/_chunks/_libs/react-dom.mjs +5 -5
  59. package/admin-dist/server/_chunks/_libs/react.mjs +2 -2
  60. package/admin-dist/server/_libs/cmdk.mjs +1 -1
  61. package/admin-dist/server/_libs/convex.mjs +3 -3
  62. package/admin-dist/server/_libs/cookie-es.mjs +1 -58
  63. package/admin-dist/server/_libs/date-fns.mjs +1 -1
  64. package/admin-dist/server/_libs/lucide-react.mjs +110 -103
  65. package/admin-dist/server/_libs/seroval-plugins.mjs +1 -58
  66. package/admin-dist/server/_libs/seroval.mjs +1 -1765
  67. package/admin-dist/server/_libs/zod.mjs +1 -1
  68. package/admin-dist/server/_ssr/CmsEmptyState-DYh_PPQE.mjs +38 -0
  69. package/admin-dist/server/_ssr/{CmsPageHeader-ClNPU7Up.mjs → CmsPageHeader-BcniLh49.mjs} +1 -1
  70. package/admin-dist/server/_ssr/{CmsStatusBadge-CojMbrY7.mjs → CmsStatusBadge-BShWDxwE.mjs} +2 -2
  71. package/admin-dist/server/_ssr/{CmsSurface-Dcv440rp.mjs → CmsSurface-CHEv-Kba.mjs} +1 -1
  72. package/admin-dist/server/_ssr/{CmsToolbar-BKv1nL6u.mjs → CmsToolbar-Dqqb216_.mjs} +2 -3
  73. package/admin-dist/server/_ssr/{ContentEntryEditor-weiXSBdZ.mjs → ContentEntryEditor-DOIAyWME.mjs} +18 -21
  74. package/admin-dist/server/_ssr/{TaxonomyFilter-BPQ57Mwk.mjs → TaxonomyFilter-BfsPAZ-Y.mjs} +4 -5
  75. package/admin-dist/server/_ssr/{_contentTypeId-DyyauLOs.mjs → _contentTypeId-CPjmri90.mjs} +34 -43
  76. package/admin-dist/server/_ssr/{_entryId-9Cafwxmw.mjs → _entryId-D0yu8HuP.mjs} +35 -47
  77. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-DgCpSt_y.mjs +4 -0
  78. package/admin-dist/server/_ssr/badge-Cp61aQNc.mjs +39 -0
  79. package/admin-dist/server/_ssr/{command-CEf8YBxY.mjs → command-BfjE1WJf.mjs} +2 -2
  80. package/admin-dist/server/_ssr/{config.server-D7JHDcDv.mjs → config.server-BOr7Jxr4.mjs} +5 -14
  81. package/admin-dist/server/_ssr/{content-ZFWVzO25.mjs → content-DrODe6sA.mjs} +40 -49
  82. package/admin-dist/server/_ssr/content-types-BPgMwxiT.mjs +459 -0
  83. package/admin-dist/server/_ssr/{index-BlSIlH4Z.mjs → index-BTHmIC9W.mjs} +26 -28
  84. package/admin-dist/server/_ssr/index.mjs +3459 -62
  85. package/admin-dist/server/_ssr/{media-CD2_NUMw.mjs → media-DkvBfmD9.mjs} +31 -43
  86. package/admin-dist/server/_ssr/{new._contentTypeId-dmZy6PBX.mjs → new._contentTypeId-Co_73sDJ.mjs} +33 -45
  87. package/admin-dist/server/_ssr/router-CaDgRHfQ.mjs +3031 -0
  88. package/admin-dist/server/_ssr/{scroll-area-BH_1K-WT.mjs → scroll-area-D3v-O_jk.mjs} +1 -1
  89. package/admin-dist/server/_ssr/{settings-DVdsoWoh.mjs → settings-MaEXh2Hz.mjs} +30 -39
  90. package/admin-dist/server/_ssr/{switch-DX_X8vZl.mjs → switch-DmbR03dm.mjs} +1 -1
  91. package/admin-dist/server/_ssr/{tabs-4FWM0sn8.mjs → tabs-5oFlAGLz.mjs} +2 -3
  92. package/admin-dist/server/_ssr/{tanstack-adapter-D3ZcKtbY.mjs → tanstack-adapter-DNaUioIZ.mjs} +1 -1
  93. package/admin-dist/server/_ssr/{taxonomies-BHFfO9Yr.mjs → taxonomies-D3xMK23a.mjs} +30 -39
  94. package/admin-dist/server/_ssr/{trash-9tUB2KwI.mjs → trash-CNw1mtF1.mjs} +30 -39
  95. package/admin-dist/server/_ssr/{useBreadcrumbLabel-DVme3DSb.mjs → useBreadcrumbLabel-BQGjOTcy.mjs} +1 -1
  96. package/admin-dist/server/_ssr/{usePermissions-zAQj-ruE.mjs → usePermissions-D0qtvmNi.mjs} +1 -1
  97. package/admin-dist/server/index.mjs +162 -204
  98. package/dist/cli/templates/cmsClient.d.ts +1 -1
  99. package/dist/cli/templates/cmsClient.d.ts.map +1 -1
  100. package/dist/cli/templates/cmsClient.js +30 -11
  101. package/dist/cli/templates/cmsClient.js.map +1 -1
  102. package/dist/cli/templates/cmsConfig.d.ts +1 -1
  103. package/dist/cli/templates/cmsConfig.d.ts.map +1 -1
  104. package/dist/cli/templates/cmsConfig.js +3 -3
  105. package/dist/client/admin/contentLock.d.ts +4 -4
  106. package/dist/client/admin/contentLock.d.ts.map +1 -1
  107. package/dist/client/admin/contentLock.js +1 -1
  108. package/dist/client/admin/contentLock.js.map +1 -1
  109. package/dist/client/admin/contentTypes.d.ts +303 -290
  110. package/dist/client/admin/contentTypes.d.ts.map +1 -1
  111. package/dist/client/admin/contentTypes.js +101 -9
  112. package/dist/client/admin/contentTypes.js.map +1 -1
  113. package/dist/client/admin/entries.d.ts +18 -19
  114. package/dist/client/admin/entries.d.ts.map +1 -1
  115. package/dist/client/admin/entries.js +6 -8
  116. package/dist/client/admin/entries.js.map +1 -1
  117. package/dist/client/admin/index.d.ts +690 -642
  118. package/dist/client/admin/index.d.ts.map +1 -1
  119. package/dist/client/admin/index.js +29 -5
  120. package/dist/client/admin/index.js.map +1 -1
  121. package/dist/client/admin/media.d.ts.map +1 -1
  122. package/dist/client/admin/taxonomies.d.ts.map +1 -1
  123. package/dist/client/admin/trash.d.ts +3 -4
  124. package/dist/client/admin/trash.d.ts.map +1 -1
  125. package/dist/client/admin/types.d.ts +181 -4
  126. package/dist/client/admin/types.d.ts.map +1 -1
  127. package/dist/client/admin/validators.d.ts +2009 -25
  128. package/dist/client/admin/validators.d.ts.map +1 -1
  129. package/dist/client/admin/validators.js +15 -4
  130. package/dist/client/admin/validators.js.map +1 -1
  131. package/dist/client/admin/versions.d.ts +1 -1
  132. package/dist/client/admin/versions.d.ts.map +1 -1
  133. package/dist/client/agentTools.d.ts +1 -4
  134. package/dist/client/agentTools.d.ts.map +1 -1
  135. package/dist/client/agentTools.js +9 -21
  136. package/dist/client/agentTools.js.map +1 -1
  137. package/dist/client/config.d.ts +10 -0
  138. package/dist/client/config.d.ts.map +1 -1
  139. package/dist/client/config.js +1 -0
  140. package/dist/client/config.js.map +1 -1
  141. package/dist/client/defineContent.d.ts +338 -0
  142. package/dist/client/defineContent.d.ts.map +1 -0
  143. package/dist/client/defineContent.js +368 -0
  144. package/dist/client/defineContent.js.map +1 -0
  145. package/dist/client/index.d.ts +2 -0
  146. package/dist/client/index.d.ts.map +1 -1
  147. package/dist/client/index.js +2 -0
  148. package/dist/client/index.js.map +1 -1
  149. package/dist/client/queryBuilder.d.ts +0 -15
  150. package/dist/client/queryBuilder.d.ts.map +1 -1
  151. package/dist/client/queryBuilder.js +0 -23
  152. package/dist/client/queryBuilder.js.map +1 -1
  153. package/dist/client/registry.d.ts +77 -0
  154. package/dist/client/registry.d.ts.map +1 -0
  155. package/dist/client/registry.js +95 -0
  156. package/dist/client/registry.js.map +1 -0
  157. package/dist/client/schema/defineContentType.d.ts +36 -24
  158. package/dist/client/schema/defineContentType.d.ts.map +1 -1
  159. package/dist/client/schema/defineContentType.js +77 -48
  160. package/dist/client/schema/defineContentType.js.map +1 -1
  161. package/dist/client/schema/typedClient.d.ts.map +1 -1
  162. package/dist/client/schema/typedClient.js +2 -10
  163. package/dist/client/schema/typedClient.js.map +1 -1
  164. package/dist/client/schema/types.d.ts +16 -9
  165. package/dist/client/schema/types.d.ts.map +1 -1
  166. package/dist/client/schema/types.js.map +1 -1
  167. package/dist/client/utils/toSlug.d.ts +60 -0
  168. package/dist/client/utils/toSlug.d.ts.map +1 -0
  169. package/dist/client/utils/toSlug.js +31 -0
  170. package/dist/client/utils/toSlug.js.map +1 -0
  171. package/dist/client/wrapper.d.ts +2 -2
  172. package/dist/client/wrapper.d.ts.map +1 -1
  173. package/dist/client/wrapper.js +22 -30
  174. package/dist/client/wrapper.js.map +1 -1
  175. package/dist/component/_generated/component.d.ts +24 -28
  176. package/dist/component/_generated/component.d.ts.map +1 -1
  177. package/dist/component/authorizationHooks.d.ts +1 -1
  178. package/dist/component/authorizationHooks.d.ts.map +1 -1
  179. package/dist/component/authorizationHooks.js +2 -2
  180. package/dist/component/authorizationHooks.js.map +1 -1
  181. package/dist/component/bulkOperations.d.ts.map +1 -1
  182. package/dist/component/bulkOperations.js +7 -4
  183. package/dist/component/bulkOperations.js.map +1 -1
  184. package/dist/component/contentEntries.d.ts +18 -56
  185. package/dist/component/contentEntries.d.ts.map +1 -1
  186. package/dist/component/contentEntries.js +45 -137
  187. package/dist/component/contentEntries.js.map +1 -1
  188. package/dist/component/contentEntryMutations.d.ts +14 -14
  189. package/dist/component/contentEntryMutations.d.ts.map +1 -1
  190. package/dist/component/contentEntryMutations.js +40 -43
  191. package/dist/component/contentEntryMutations.js.map +1 -1
  192. package/dist/component/contentEntryValidation.d.ts +3 -3
  193. package/dist/component/contentEntryValidation.js +6 -9
  194. package/dist/component/contentEntryValidation.js.map +1 -1
  195. package/dist/component/contentLock.d.ts +7 -7
  196. package/dist/component/contentLock.js +3 -3
  197. package/dist/component/contentLock.js.map +1 -1
  198. package/dist/component/contentTypeMigration.d.ts +1 -1
  199. package/dist/component/contentTypeMigration.js +2 -2
  200. package/dist/component/contentTypeMigration.js.map +1 -1
  201. package/dist/component/contentTypeMutations.js +2 -2
  202. package/dist/component/contentTypeMutations.js.map +1 -1
  203. package/dist/component/convex.config.d.ts.map +1 -1
  204. package/dist/component/convex.config.js +1 -1
  205. package/dist/component/convex.config.js.map +1 -1
  206. package/dist/component/eventEmitter.d.ts +0 -1
  207. package/dist/component/eventEmitter.d.ts.map +1 -1
  208. package/dist/component/eventEmitter.js.map +1 -1
  209. package/dist/component/exportImport.d.ts +37 -37
  210. package/dist/component/exportImport.d.ts.map +1 -1
  211. package/dist/component/exportImport.js +34 -34
  212. package/dist/component/exportImport.js.map +1 -1
  213. package/dist/component/lib/deepReferenceResolver.d.ts +2 -2
  214. package/dist/component/lib/deepReferenceResolver.d.ts.map +1 -1
  215. package/dist/component/lib/deepReferenceResolver.js +13 -8
  216. package/dist/component/lib/deepReferenceResolver.js.map +1 -1
  217. package/dist/component/lib/ragContentChunker.d.ts +3 -3
  218. package/dist/component/lib/ragContentChunker.d.ts.map +1 -1
  219. package/dist/component/lib/ragContentChunker.js +4 -4
  220. package/dist/component/lib/ragContentChunker.js.map +1 -1
  221. package/dist/component/lib/referenceResolver.d.ts.map +1 -1
  222. package/dist/component/lib/referenceResolver.js +10 -17
  223. package/dist/component/lib/referenceResolver.js.map +1 -1
  224. package/dist/component/mediaAssetMutations.js +4 -4
  225. package/dist/component/mediaAssetMutations.js.map +1 -1
  226. package/dist/component/ragContentIndexer.d.ts +2 -2
  227. package/dist/component/ragContentIndexer.d.ts.map +1 -1
  228. package/dist/component/ragContentIndexer.js +44 -48
  229. package/dist/component/ragContentIndexer.js.map +1 -1
  230. package/dist/component/roles.d.ts +3 -3
  231. package/dist/component/scheduledPublish.d.ts +4 -4
  232. package/dist/component/scheduledPublish.js +3 -3
  233. package/dist/component/scheduledPublish.js.map +1 -1
  234. package/dist/component/schema.d.ts +18 -18
  235. package/dist/component/schema.js +5 -5
  236. package/dist/component/schema.js.map +1 -1
  237. package/dist/component/trash.d.ts +6 -9
  238. package/dist/component/trash.d.ts.map +1 -1
  239. package/dist/component/trash.js +12 -36
  240. package/dist/component/trash.js.map +1 -1
  241. package/dist/component/userContext.d.ts +1 -2
  242. package/dist/component/userContext.d.ts.map +1 -1
  243. package/dist/component/userContext.js +1 -2
  244. package/dist/component/userContext.js.map +1 -1
  245. package/dist/component/validators.d.ts +27 -33
  246. package/dist/component/validators.d.ts.map +1 -1
  247. package/dist/component/validators.js +3 -5
  248. package/dist/component/validators.js.map +1 -1
  249. package/dist/component/versionMutations.d.ts +1 -1
  250. package/dist/component/webhookTrigger.d.ts +14 -14
  251. package/dist/test.d.ts +30 -30
  252. package/dist/test.d.ts.map +1 -1
  253. package/dist/test.js +24 -24
  254. package/dist/test.js.map +1 -1
  255. package/package.json +1 -1
  256. package/admin-dist/public/assets/CmsEmptyState-Do_erIgn.js +0 -5
  257. package/admin-dist/public/assets/CmsPageHeader-qDwPGi48.js +0 -1
  258. package/admin-dist/public/assets/CmsToolbar-D1-Y-7SK.js +0 -1
  259. package/admin-dist/public/assets/ContentEntryEditor-CWBiIx52.js +0 -4
  260. package/admin-dist/public/assets/TaxonomyFilter-CdYQawxb.js +0 -1
  261. package/admin-dist/public/assets/_contentTypeId-D9VMP6Gs.js +0 -1
  262. package/admin-dist/public/assets/_entryId-2FlCfqE7.js +0 -1
  263. package/admin-dist/public/assets/alert-GxZx0y5c.js +0 -1
  264. package/admin-dist/public/assets/badge-BAlGIjop.js +0 -1
  265. package/admin-dist/public/assets/command-di7XCqcv.js +0 -1
  266. package/admin-dist/public/assets/content-D8zELsDG.js +0 -1
  267. package/admin-dist/public/assets/content-types-BmzD0krT.js +0 -2
  268. package/admin-dist/public/assets/globals-BvFfH-v9.css +0 -1
  269. package/admin-dist/public/assets/index-zqfj4T_v.js +0 -1
  270. package/admin-dist/public/assets/label-B6PPtKR5.js +0 -1
  271. package/admin-dist/public/assets/link-2-W2fVnVOf.js +0 -1
  272. package/admin-dist/public/assets/list-F8O0lZXC.js +0 -1
  273. package/admin-dist/public/assets/main-dZT72bAG.js +0 -97
  274. package/admin-dist/public/assets/media-CETueFbV.js +0 -1
  275. package/admin-dist/public/assets/new._contentTypeId-BV2-TyyR.js +0 -1
  276. package/admin-dist/public/assets/plus-AABQIF0N.js +0 -1
  277. package/admin-dist/public/assets/scroll-area-CDfk-zrz.js +0 -1
  278. package/admin-dist/public/assets/select-BuiHcMzS.js +0 -1
  279. package/admin-dist/public/assets/settings-DBxbYDvn.js +0 -1
  280. package/admin-dist/public/assets/switch-DiJvolcs.js +0 -1
  281. package/admin-dist/public/assets/tabs-Cgz6G_Xy.js +0 -1
  282. package/admin-dist/public/assets/tanstack-adapter-BknsSgra.js +0 -1
  283. package/admin-dist/public/assets/taxonomies-DOErsLl5.js +0 -1
  284. package/admin-dist/public/assets/textarea-CgggMxUX.js +0 -1
  285. package/admin-dist/public/assets/trash-BU4ANuaW.js +0 -1
  286. package/admin-dist/public/assets/triangle-alert-lvCbwp0s.js +0 -1
  287. package/admin-dist/public/assets/usePermissions-D7tQowaF.js +0 -1
  288. package/admin-dist/server/_libs/h3-v2.mjs +0 -277
  289. package/admin-dist/server/_ssr/CmsButton-DbzfJru_.mjs +0 -125
  290. package/admin-dist/server/_ssr/CmsEmptyState-CuvcXr3Z.mjs +0 -290
  291. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dk-FIYPN.mjs +0 -4
  292. package/admin-dist/server/_ssr/content-types-D25lUE-j.mjs +0 -1312
  293. package/admin-dist/server/_ssr/label-PblVvdRv.mjs +0 -22
  294. package/admin-dist/server/_ssr/router-x6Ab8T4s.mjs +0 -1622
  295. package/admin-dist/server/_ssr/select-CrfEkFJw.mjs +0 -142
  296. package/admin-dist/server/_ssr/textarea-CZVaroMc.mjs +0 -18
package/README.md CHANGED
@@ -1,86 +1,11 @@
1
1
  # Convex CMS
2
2
 
3
- > **Alpha Status (v0.0.5)** — Actively developed. APIs may change. [Report issues](https://github.com/obkaro/convex-cms/issues).
3
+ [![npm version](https://badge.fury.io/js/convex-cms.svg)](https://www.npmjs.com/package/convex-cms)
4
+ [![License: Apache-2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
4
5
 
5
- A headless CMS built as a [Convex Component](https://docs.convex.dev/components) — content management that runs inside your Convex app.
6
+ > **Alpha (v0.0.6)** Actively developed. APIs may change. [Report issues](https://github.com/obkaro/convex-cms/issues).
6
7
 
7
- ## Why Convex CMS?
8
-
9
- If you're building on Convex and need content management, this is the most integrated option:
10
-
11
- - **Zero infrastructure** — Runs entirely within your Convex deployment
12
- - **True real-time** — Content updates via Convex subscriptions, not polling
13
- - **Type-safe** — Code-first schemas with full TypeScript inference
14
- - **Component isolation** — Separate database tables, versioned independently
15
- - **Agent-native** — 23 pre-built tools for AI agent integration via `@convex-dev/agent`
16
-
17
- ## Choose Your Path
18
-
19
- ### Need an Admin Interface?
20
-
21
- Use **`defineAdminAPI`** — one line creates all the backend functions for a working admin UI.
22
-
23
- ```
24
- Your App Admin UI
25
- │ │
26
- └── convex/admin.ts ────────────┘
27
- defineAdminAPI()
28
-
29
- ├── listContentTypes
30
- ├── getEntry
31
- ├── publishEntry
32
- └── ... (60+ functions across 11 domains)
33
-
34
-
35
- CMS Component
36
- ```
37
-
38
- → **[Admin UI Setup Guide](./docs/guides/admin-ui-setup.md)**
39
-
40
- ### Building Custom Content Logic?
41
-
42
- Use **`createCmsClient`** — full programmatic control with typed methods in your Convex functions.
43
-
44
- ```
45
- Your Convex Functions
46
-
47
- └── cms.contentEntries.list(ctx, { status: "published" })
48
- cms.contentTypes.create(ctx, { name: "blog", ... })
49
- cms.mediaAssets.upload(ctx, { ... })
50
-
51
-
52
- CMS Component
53
- ```
54
-
55
- → **[Getting Started Guide](./docs/guides/getting-started.md)**
56
-
57
- ### Want Full Type Safety?
58
-
59
- Use **code-first schemas** — define content types with Convex validators, get TypeScript inference.
60
-
61
- ```typescript
62
- const blogPost = defineContentType({
63
- name: "blog_post",
64
- validator: v.object({
65
- title: v.string(),
66
- content: v.string(),
67
- }),
68
- });
69
-
70
- // TypeScript knows entry.data.title is string
71
- const entry = await cms.typedContentEntries.get<"blog_post">(ctx, id);
72
- ```
73
-
74
- → **[Code-First Schema Reference](./docs/api/code-first-schema.md)**
75
-
76
- ### Need Both?
77
-
78
- **Most apps use both.** This is the typical setup:
79
-
80
- - `defineAdminAPI` powers the admin interface for content editors
81
- - `createCmsClient` gives you typed methods for custom queries on your frontend
82
-
83
- They work together through the same CMS component.
8
+ A headless CMS built as a [Convex Component](https://docs.convex.dev/components). Content management that runs inside your Convex app.
84
9
 
85
10
  ## Quick Start
86
11
 
@@ -110,35 +35,72 @@ export default app;
110
35
  **For Custom Functions:** Create a CMS client and use it in your functions
111
36
  → [Full Getting Started Guide](./docs/guides/getting-started.md)
112
37
 
38
+ ## Why Convex CMS?
39
+
40
+ If you're building on Convex and need content management, this is the most integrated option:
41
+
42
+ - **Zero infrastructure.** Runs entirely within your Convex deployment
43
+ - **True real-time.** Content updates via Convex subscriptions, not polling
44
+ - **Type-safe.** Code-first schemas with full TypeScript inference
45
+ - **Component isolation.** Separate database tables, versioned independently
46
+ - **Agent-native.** 23 pre-built tools for AI agent integration via `@convex-dev/agent`
47
+
48
+ ## Features
49
+
50
+ | Feature | What it does |
51
+ |---------|--------------|
52
+ | **Code-first config** | Define content types in TypeScript with full type inference |
53
+ | **UI-defined config** | Create and modify content types through the admin interface |
54
+ | **CMS Client** | Programmatic access via `createCmsClient` for custom queries and mutations |
55
+ | **Admin API** | Pre-built functions via `defineAdminAPI` that power the admin UI |
56
+ | **CLI Admin UI** | Run `npx convex-cms admin` for local development |
57
+ | **Embedded Admin UI** | Ship the admin interface as part of your React app |
58
+
59
+ Any combination of these features works together seamlessly. Pick what fits your workflow.
60
+
61
+ ## In Practice
62
+
63
+ **Full control over the editorial experience?**
64
+ Code-first config + CMS Client. You define the schema in TypeScript and build exactly the UI you want.
65
+
66
+ **Ship fast with a ready-made admin?**
67
+ Code-first config + Admin API + Embedded Admin UI. Type-safe schemas with a working admin interface out of the box.
68
+
69
+ **Content team needs to modify schemas without deploys?**
70
+ UI-defined config + Admin API + Embedded Admin UI. Non-developers can add fields and content types.
71
+
72
+ **Automated content pipelines?**
73
+ CMS Client + agent tools. 23 pre-built tools for AI-driven workflows.
74
+
113
75
  ## What's Included
114
76
 
115
77
  ### Core Content
116
- - **13 field types** text, richText, number, boolean, date, datetime, select, multiSelect, reference, media, json, tags, category
117
- - **Publishing workflows** draft → scheduled → published with version history
118
- - **Content versioning** Snapshots, comparison, and rollback
119
- - **Scheduled publishing** Convex scheduler integration for future publish dates
78
+ - **13 field types.** text, richText, number, boolean, date, datetime, select, multiSelect, reference, media, json, tags, category
79
+ - **Publishing workflows.** Draft → scheduled → published with version history
80
+ - **Content versioning.** Snapshots, comparison, and rollback
81
+ - **Scheduled publishing.** Convex scheduler integration for future publish dates
120
82
 
121
83
  ### Media Management
122
- - **File uploads** Direct to Convex storage with folder organization
123
- - **Image variants** Automatic resizing and format conversion
124
- - **Metadata & tagging** Alt text, descriptions, taxonomy support
84
+ - **File uploads.** Direct to Convex storage with folder organization
85
+ - **Image variants.** Automatic resizing and format conversion
86
+ - **Metadata & tagging.** Alt text, descriptions, taxonomy support
125
87
 
126
88
  ### Organization
127
- - **Taxonomies** Hierarchical categories and flat tags
128
- - **Content locking** Prevent concurrent edit conflicts
129
- - **Soft delete & trash** Configurable retention with restore
89
+ - **Taxonomies.** Hierarchical categories and flat tags
90
+ - **Content locking.** Prevent concurrent edit conflicts
91
+ - **Soft delete & trash.** Configurable retention with restore
130
92
 
131
93
  ### Integration
132
- - **RBAC** 4 built-in roles + custom roles with fine-grained permissions
133
- - **Multi-locale** Content localization with fallback chains
134
- - **Webhooks** Event-driven integration with external systems
135
- - **Event system** All mutations emit events for async processing
136
- - **Agent tools** 23 pre-built tools with Zod schemas for AI integration
137
- - **Query builder** Fluent API for complex content queries
94
+ - **RBAC.** 4 built-in roles + custom roles with fine-grained permissions
95
+ - **Multi-locale.** Content localization with fallback chains
96
+ - **Webhooks.** Event-driven integration with external systems
97
+ - **Event system.** All mutations emit events for async processing
98
+ - **Agent tools.** 23 pre-built tools with Zod schemas for AI integration
99
+ - **Query builder.** Fluent API for complex content queries
138
100
 
139
101
  ### Admin UI
140
- - **Pre-built React interface** CLI mode for development, embeddable for production
141
- - **Visual content editing** Rich text, media picker, reference selector
102
+ - **Pre-built React interface.** CLI mode for development, embeddable for production
103
+ - **Visual content editing.** Rich text, media picker, reference selector
142
104
 
143
105
  ## Admin UI Modes
144
106
 
@@ -95,7 +95,7 @@ export interface ContentType {
95
95
 
96
96
  export interface ContentEntry {
97
97
  _id: string
98
- contentTypeId: string
98
+ contentTypeName: string
99
99
  slug: string
100
100
  status: 'draft' | 'published' | 'scheduled' | 'archived'
101
101
  data: Record<string, unknown>
@@ -645,7 +645,7 @@ export function ContentEntryEditor({
645
645
  })) as ContentEntry
646
646
  } else {
647
647
  savedEntry = (await createEntry({
648
- contentTypeId: contentType._id,
648
+ contentTypeName: contentType.name,
649
649
  data: dataForBackend,
650
650
  })) as ContentEntry
651
651
  }
@@ -32,6 +32,7 @@ import {
32
32
  List,
33
33
  Tag,
34
34
  FolderOpen,
35
+ Code2,
35
36
  } from "lucide-react";
36
37
  import { cn } from "~/lib/cn";
37
38
  import { BreakingChangesWarningDialog } from "./BreakingChangesWarningDialog";
@@ -139,7 +140,7 @@ interface ContentTypeFormModalProps {
139
140
  onClose: () => void;
140
141
  onCreated?: (contentType: unknown) => void;
141
142
  onUpdated?: (contentType: unknown) => void;
142
- contentType?: ContentType | null;
143
+ contentType?: (ContentType & { source?: "code" | "database" }) | null;
143
144
  }
144
145
 
145
146
  function generateMachineName(displayName: string): string {
@@ -164,6 +165,8 @@ export function ContentTypeFormModal({
164
165
  contentType,
165
166
  }: ContentTypeFormModalProps) {
166
167
  const isEditing = !!contentType;
168
+ const isCodeDefined = contentType?.source === "code";
169
+ const isReadOnly = isCodeDefined;
167
170
 
168
171
  const [displayName, setDisplayName] = useState("");
169
172
  const [machineName, setMachineName] = useState("");
@@ -512,34 +515,62 @@ export function ContentTypeFormModal({
512
515
  <CmsDialog
513
516
  open={isOpen}
514
517
  onOpenChange={(open) => !open && handleClose()}
515
- title={isEditing ? "Edit Content Type" : "Create Content Type"}
518
+ title={
519
+ isCodeDefined
520
+ ? "View Content Type"
521
+ : isEditing
522
+ ? "Edit Content Type"
523
+ : "Create Content Type"
524
+ }
516
525
  size="2xl"
517
526
  footer={
518
- <>
519
- <CmsButton
520
- variant="outline"
521
- onClick={handleClose}
522
- disabled={isSubmitting}
523
- >
524
- Cancel
525
- </CmsButton>
526
- <CmsButton
527
- variant="primary"
528
- onClick={handleSubmit}
529
- disabled={validationErrors.length > 0}
530
- loading={isSubmitting}
531
- data-testid={
532
- isEditing
533
- ? "update-content-type-submit"
534
- : "create-content-type-submit"
535
- }
536
- >
537
- {isEditing ? "Save Changes" : "Create Content Type"}
527
+ isReadOnly ? (
528
+ <CmsButton variant="outline" onClick={handleClose}>
529
+ Close
538
530
  </CmsButton>
539
- </>
531
+ ) : (
532
+ <>
533
+ <CmsButton
534
+ variant="outline"
535
+ onClick={handleClose}
536
+ disabled={isSubmitting}
537
+ >
538
+ Cancel
539
+ </CmsButton>
540
+ <CmsButton
541
+ variant="primary"
542
+ onClick={handleSubmit}
543
+ disabled={validationErrors.length > 0}
544
+ loading={isSubmitting}
545
+ data-testid={
546
+ isEditing
547
+ ? "update-content-type-submit"
548
+ : "create-content-type-submit"
549
+ }
550
+ >
551
+ {isEditing ? "Save Changes" : "Create Content Type"}
552
+ </CmsButton>
553
+ </>
554
+ )
540
555
  }
541
556
  >
542
557
  <form onSubmit={handleSubmit} className="space-y-6">
558
+ {isCodeDefined && (
559
+ <div className="flex items-start gap-3 rounded-lg border border-violet-200 bg-violet-50 p-3">
560
+ <Code2 className="mt-0.5 size-5 shrink-0 text-violet-600" />
561
+ <div className="space-y-1">
562
+ <p className="text-sm font-medium text-violet-900">
563
+ Managed by Code
564
+ </p>
565
+ <p className="text-sm text-violet-700">
566
+ This content type is defined in your codebase and cannot be
567
+ edited through the admin interface. To make changes, update
568
+ the definition in your code.
569
+ </p>
570
+ </div>
571
+ </div>
572
+ )}
573
+
543
574
  {/* Basic Info Section */}
544
575
  <div className="space-y-4">
545
576
  <h4 className="text-sm font-semibold text-foreground">
@@ -555,8 +586,8 @@ export function ContentTypeFormModal({
555
586
  value={displayName}
556
587
  onChange={(e) => handleDisplayNameChange(e.target.value)}
557
588
  placeholder="e.g., Blog Post"
558
- disabled={isSubmitting}
559
- autoFocus
589
+ disabled={isSubmitting || isReadOnly}
590
+ autoFocus={!isReadOnly}
560
591
  data-testid="display-name-input"
561
592
  />
562
593
  </div>
@@ -570,7 +601,7 @@ export function ContentTypeFormModal({
570
601
  value={machineName}
571
602
  onChange={(e) => handleMachineNameChange(e.target.value)}
572
603
  placeholder="e.g., blog_post"
573
- disabled={isSubmitting || isEditing}
604
+ disabled={isSubmitting || isEditing || isReadOnly}
574
605
  className={cn(
575
606
  !isValidMachineName(machineName) &&
576
607
  machineName &&
@@ -592,7 +623,7 @@ export function ContentTypeFormModal({
592
623
  value={description}
593
624
  onChange={(e) => setDescription(e.target.value)}
594
625
  placeholder="Optional description of this content type"
595
- disabled={isSubmitting}
626
+ disabled={isSubmitting || isReadOnly}
596
627
  rows={2}
597
628
  />
598
629
  </div>
@@ -602,7 +633,7 @@ export function ContentTypeFormModal({
602
633
  id="singleton"
603
634
  checked={singleton}
604
635
  onCheckedChange={(checked) => setSingleton(checked as boolean)}
605
- disabled={isSubmitting}
636
+ disabled={isSubmitting || isReadOnly}
606
637
  />
607
638
  <Label htmlFor="singleton" className="cursor-pointer">
608
639
  Singleton (only one entry allowed)
@@ -614,17 +645,19 @@ export function ContentTypeFormModal({
614
645
  <div className="space-y-4">
615
646
  <div className="flex items-center justify-between">
616
647
  <h4 className="text-sm font-semibold text-foreground">Fields</h4>
617
- <CmsButton
618
- type="button"
619
- variant="secondary"
620
- size="sm"
621
- onClick={addField}
622
- disabled={isSubmitting}
623
- data-testid="add-field-button"
624
- >
625
- <Plus className="size-3.5" />
626
- Add Field
627
- </CmsButton>
648
+ {!isReadOnly && (
649
+ <CmsButton
650
+ type="button"
651
+ variant="secondary"
652
+ size="sm"
653
+ onClick={addField}
654
+ disabled={isSubmitting}
655
+ data-testid="add-field-button"
656
+ >
657
+ <Plus className="size-3.5" />
658
+ Add Field
659
+ </CmsButton>
660
+ )}
628
661
  </div>
629
662
 
630
663
  <div className="space-y-2">
@@ -632,41 +665,46 @@ export function ContentTypeFormModal({
632
665
  <div
633
666
  key={index}
634
667
  className={cn(
635
- "flex cursor-pointer items-center gap-2 rounded-lg border p-2 transition-colors hover:bg-muted/50",
668
+ "flex items-center gap-2 rounded-lg border p-2 transition-colors",
669
+ !isReadOnly && "cursor-pointer hover:bg-muted/50",
636
670
  activeFieldIndex === index && "border-primary bg-primary/5",
637
671
  )}
638
672
  onClick={() => {
639
- setActiveFieldIndex(index);
640
- setShowFieldEditor(true);
673
+ if (!isReadOnly) {
674
+ setActiveFieldIndex(index);
675
+ setShowFieldEditor(true);
676
+ }
641
677
  }}
642
678
  data-testid={`field-item-${index}`}
643
679
  >
644
- <div className="flex flex-col gap-0.5">
645
- {index > 0 && (
646
- <button
647
- type="button"
648
- onClick={(e) => {
649
- e.stopPropagation();
650
- moveField(index, index - 1);
651
- }}
652
- className="rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground"
653
- >
654
- <ChevronUp className="size-3" />
655
- </button>
656
- )}
657
- {index < fields.length - 1 && (
658
- <button
659
- type="button"
660
- onClick={(e) => {
661
- e.stopPropagation();
662
- moveField(index, index + 1);
663
- }}
664
- className="rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground"
665
- >
666
- <ChevronDown className="size-3" />
667
- </button>
668
- )}
669
- </div>
680
+ {!isReadOnly && (
681
+ <div className="flex flex-col gap-0.5">
682
+ {index > 0 && (
683
+ <button
684
+ type="button"
685
+ onClick={(e) => {
686
+ e.stopPropagation();
687
+ moveField(index, index - 1);
688
+ }}
689
+ className="rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground"
690
+ >
691
+ <ChevronUp className="size-3" />
692
+ </button>
693
+ )}
694
+ {index < fields.length - 1 && (
695
+ <button
696
+ type="button"
697
+ onClick={(e) => {
698
+ e.stopPropagation();
699
+ moveField(index, index + 1);
700
+ }}
701
+ className="rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground"
702
+ >
703
+ <ChevronDown className="size-3" />
704
+ </button>
705
+ )}
706
+ </div>
707
+ )}
670
708
 
671
709
  <div className="flex size-8 items-center justify-center rounded bg-muted text-muted-foreground">
672
710
  {FIELD_TYPE_INFO[field.type].icon}
@@ -682,23 +720,25 @@ export function ContentTypeFormModal({
682
720
  </p>
683
721
  </div>
684
722
 
685
- <button
686
- type="button"
687
- onClick={(e) => {
688
- e.stopPropagation();
689
- removeField(index);
690
- }}
691
- disabled={isSubmitting || fields.length === 1}
692
- className="rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive disabled:opacity-50"
693
- >
694
- <X className="size-4" />
695
- </button>
723
+ {!isReadOnly && (
724
+ <button
725
+ type="button"
726
+ onClick={(e) => {
727
+ e.stopPropagation();
728
+ removeField(index);
729
+ }}
730
+ disabled={isSubmitting || fields.length === 1}
731
+ className="rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive disabled:opacity-50"
732
+ >
733
+ <X className="size-4" />
734
+ </button>
735
+ )}
696
736
  </div>
697
737
  ))}
698
738
  </div>
699
739
 
700
- {/* Field Editor Panel */}
701
- {showFieldEditor && activeField && activeFieldIndex !== null && (
740
+ {/* Field Editor Panel - hidden in read-only mode */}
741
+ {!isReadOnly && showFieldEditor && activeField && activeFieldIndex !== null && (
702
742
  <div
703
743
  className="rounded-lg border bg-muted/30 p-4"
704
744
  data-testid="field-editor"
@@ -113,7 +113,7 @@ export function ReferenceField({
113
113
  api.admin.listEntries,
114
114
  showPicker
115
115
  ? {
116
- contentTypeId: contentTypeFilter || undefined,
116
+ contentTypeName: contentTypeFilter || undefined,
117
117
  search: searchQuery || undefined,
118
118
  paginationOpts: { numItems: 50, cursor: null },
119
119
  }
@@ -124,15 +124,15 @@ export function ReferenceField({
124
124
  if (!entriesResult?.page) return []
125
125
  if (allowedContentTypes.length === 0) return entriesResult.page
126
126
 
127
- const allowedIds = filteredContentTypes.map((ct) => ct._id)
127
+ const allowedNames = filteredContentTypes.map((ct) => ct.name)
128
128
  return entriesResult.page.filter((entry) =>
129
- allowedIds.includes(entry.contentTypeId)
129
+ allowedNames.includes(entry.contentTypeName)
130
130
  )
131
131
  }, [entriesResult?.page, allowedContentTypes, filteredContentTypes])
132
132
 
133
- const getContentTypeName = useCallback(
134
- (contentTypeId: string) => {
135
- const ct = contentTypes?.page?.find((c) => c._id === contentTypeId)
133
+ const getContentTypeDisplayName = useCallback(
134
+ (contentTypeName: string) => {
135
+ const ct = contentTypes?.page?.find((c) => c.name === contentTypeName)
136
136
  return ct?.displayName || ct?.name || 'Unknown'
137
137
  },
138
138
  [contentTypes?.page]
@@ -177,7 +177,7 @@ export function ReferenceField({
177
177
  data?: Record<string, unknown>
178
178
  slug?: string
179
179
  status: string
180
- contentTypeId: string
180
+ contentTypeName: string
181
181
  },
182
182
  showRemove = true
183
183
  ) => (
@@ -194,7 +194,7 @@ export function ReferenceField({
194
194
  </p>
195
195
  <div className="flex items-center gap-2">
196
196
  <span className="text-xs text-muted-foreground">
197
- {getContentTypeName(entry.contentTypeId)}
197
+ {getContentTypeDisplayName(entry.contentTypeName)}
198
198
  </span>
199
199
  <CmsStatusBadge status={entry.status as ContentStatus} />
200
200
  </div>
@@ -329,7 +329,7 @@ export function ReferenceField({
329
329
  </p>
330
330
  <div className="flex items-center gap-2">
331
331
  <span className="text-xs text-muted-foreground">
332
- {getContentTypeName(entry.contentTypeId)}
332
+ {getContentTypeDisplayName(entry.contentTypeName)}
333
333
  </span>
334
334
  <Badge
335
335
  variant="secondary"
@@ -39,19 +39,11 @@ import { CmsAdminApi } from "~/embed/contexts/ApiContext";
39
39
 
40
40
  type ContentType = {
41
41
  _id: string;
42
+ name: string;
42
43
  displayName: string;
43
44
  titleField?: string;
44
45
  };
45
46
 
46
- type Entry = {
47
- _id: string;
48
- contentTypeId: string;
49
- slug?: string;
50
- status: string;
51
- data: Record<string, unknown>;
52
- _creationTime: number;
53
- };
54
-
55
47
  export interface ContentPageProps {
56
48
  api: CmsAdminApi
57
49
  navigation: AdminNavigation;
@@ -70,12 +62,12 @@ export function ContentPage({ api, navigation }: ContentPageProps) {
70
62
  const isLoadingContentTypes = contentTypesResult === undefined;
71
63
 
72
64
  const entriesResult = useQuery(api.listEntries, {
73
- contentTypeId: selectedTypeId || undefined,
65
+ contentTypeName: selectedTypeId || undefined,
74
66
  status: selectedStatus || undefined,
75
67
  search: searchQuery.trim() || undefined,
76
68
  paginationOpts: { numItems: 50, cursor: null },
77
69
  });
78
- const entries: Entry[] = entriesResult?.page ?? [];
70
+ const entries = entriesResult?.page ?? [];
79
71
  const isLoadingEntries = entriesResult === undefined;
80
72
 
81
73
  const isLoading = isLoadingContentTypes || isLoadingEntries;
@@ -84,16 +76,16 @@ export function ContentPage({ api, navigation }: ContentPageProps) {
84
76
  navigation.navigateToNewEntry(contentTypeId);
85
77
  };
86
78
 
87
- const getContentTypeName = (contentTypeId: string) => {
88
- const type = contentTypes.find((t) => t._id === contentTypeId);
89
- return type?.displayName ?? "Unknown";
79
+ const getContentTypeDisplayName = (contentTypeName: string) => {
80
+ const type = contentTypes.find((t) => t.name === contentTypeName);
81
+ return type?.displayName ?? contentTypeName;
90
82
  };
91
83
 
92
84
  const getEntryTitle = (
93
85
  entry: { data: Record<string, unknown> },
94
- contentTypeId: string
86
+ contentTypeName: string
95
87
  ) => {
96
- const type = contentTypes.find((t) => t._id === contentTypeId);
88
+ const type = contentTypes.find((t) => t.name === contentTypeName);
97
89
  const titleField = type?.titleField ?? "title";
98
90
  const title = entry.data[titleField];
99
91
  return typeof title === "string" && title ? title : "Untitled";
@@ -291,7 +283,7 @@ export function ContentPage({ api, navigation }: ContentPageProps) {
291
283
  onCheckedChange={(checked) =>
292
284
  handleSelectItem(entry._id, checked as boolean)
293
285
  }
294
- aria-label={`Select ${getEntryTitle(entry, entry.contentTypeId)}`}
286
+ aria-label={`Select ${getEntryTitle(entry, entry.contentTypeName)}`}
295
287
  />
296
288
  </td>
297
289
  <td className="p-3">
@@ -301,7 +293,7 @@ export function ContentPage({ api, navigation }: ContentPageProps) {
301
293
  className="block text-left"
302
294
  >
303
295
  <span className="font-medium text-foreground hover:text-primary">
304
- {getEntryTitle(entry, entry.contentTypeId)}
296
+ {getEntryTitle(entry, entry.contentTypeName)}
305
297
  </span>
306
298
  <span className="block text-xs text-muted-foreground">
307
299
  {entry.slug}
@@ -309,7 +301,7 @@ export function ContentPage({ api, navigation }: ContentPageProps) {
309
301
  </button>
310
302
  </td>
311
303
  <td className="p-3 text-sm text-muted-foreground">
312
- {getContentTypeName(entry.contentTypeId)}
304
+ {getContentTypeDisplayName(entry.contentTypeName)}
313
305
  </td>
314
306
  <td className="p-3">
315
307
  <CmsStatusBadge status={entry.status as ContentStatus} />