convex-cms 0.0.6 → 0.0.7
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.
- package/README.md +68 -101
- package/admin/src/components/BreakingChangesWarningDialog.tsx +5 -5
- package/admin/src/components/BulkOperationModal.tsx +14 -14
- package/admin/src/components/ContentEntryEditor.tsx +8 -8
- package/admin/src/components/ContentTypeFormModal.tsx +122 -82
- package/admin/src/components/Header.tsx +5 -2
- package/admin/src/components/SchemaDriftWarning.tsx +126 -0
- package/admin/src/components/TaxonomyEditor.tsx +2 -2
- package/admin/src/components/TermTree.tsx +3 -3
- package/admin/src/components/UploadDropzone.tsx +7 -7
- package/admin/src/components/VersionCompare.tsx +13 -13
- package/admin/src/components/VersionHistory.tsx +2 -2
- package/admin/src/components/VersionRollbackModal.tsx +5 -5
- package/admin/src/components/cmsds/CmsButton.tsx +2 -2
- package/admin/src/components/cmsds/CmsDialog.tsx +4 -1
- package/admin/src/components/cmsds/CmsStatusBadge.tsx +5 -5
- package/admin/src/components/fields/JsonField.tsx +1 -1
- package/admin/src/components/fields/ReferenceField.tsx +9 -9
- package/admin/src/components/fields/TagField.tsx +1 -1
- package/admin/src/contexts/SettingsConfigContext.tsx +10 -3
- package/admin/src/embed/index.tsx +29 -9
- package/admin/src/embed/pages/ContentTypeEntries.tsx +25 -0
- package/admin/src/embed/pages/Entry.tsx +114 -0
- package/admin/src/embed/pages/Media.tsx +3 -1
- package/admin/src/embed/pages/NewEntry.tsx +83 -0
- package/admin/src/embed/pages/index.ts +3 -0
- package/admin/src/pages/ContentPage.tsx +27 -20
- package/admin/src/pages/ContentTypeEntriesPage.tsx +466 -0
- package/admin/src/pages/ContentTypesPage.tsx +65 -19
- package/admin/src/pages/DashboardPage.tsx +3 -0
- package/admin/src/pages/SettingsPage.tsx +4 -4
- package/admin/src/pages/index.ts +1 -0
- package/admin/src/routes/__root.tsx +10 -10
- package/admin/src/routes/entries/$entryId.tsx +1 -1
- package/admin/src/routes/entries/type/$contentTypeId.tsx +1 -1
- package/admin/src/styles/globals.css +31 -5
- package/admin/src/styles/tailwind-config.css +25 -0
- package/admin/src/styles/theme.css +50 -0
- package/admin-dist/nitro.json +1 -1
- package/admin-dist/public/assets/CmsEmptyState-6-PLaXtD.js +1 -0
- package/admin-dist/public/assets/CmsPageHeader-SoF4Epu9.js +1 -0
- package/admin-dist/public/assets/CmsStatusBadge-D7kYaohx.js +1 -0
- package/admin-dist/public/assets/{CmsSurface-DBy5Lumx.js → CmsSurface-BvksBm6W.js} +1 -1
- package/admin-dist/public/assets/CmsToolbar-DlZPMe2B.js +1 -0
- package/admin-dist/public/assets/ContentEntryEditor-C6n9xLS9.js +4 -0
- package/admin-dist/public/assets/TaxonomyFilter-CFX1_g8s.js +1 -0
- package/admin-dist/public/assets/_contentTypeId-DTv8UoTp.js +1 -0
- package/admin-dist/public/assets/_entryId-D3lr5Dvy.js +1 -0
- package/admin-dist/public/assets/alert-BAHTL6ao.js +1 -0
- package/admin-dist/public/assets/badge-oJv4Eai8.js +1 -0
- package/admin-dist/public/assets/{circle-check-big-CpLxAvEj.js → circle-check-big-3OHxNDhO.js} +1 -1
- package/admin-dist/public/assets/command-DwgQs69u.js +1 -0
- package/admin-dist/public/assets/content-CKQ4QwW2.js +1 -0
- package/admin-dist/public/assets/content-types-BrttaLpc.js +1 -0
- package/admin-dist/public/assets/globals-CoCRjt0K.css +1 -0
- package/admin-dist/public/assets/index-DOkgTSx0.js +1 -0
- package/admin-dist/public/assets/main-DV6oxWnU.js +102 -0
- package/admin-dist/public/assets/media-B2i-mCbx.js +1 -0
- package/admin-dist/public/assets/new._contentTypeId-VF63rpic.js +1 -0
- package/admin-dist/public/assets/pencil-CX1CiTDD.js +1 -0
- package/admin-dist/public/assets/refresh-cw-Cm-YOeFI.js +1 -0
- package/admin-dist/public/assets/{rotate-ccw-BZpZtw0N.js → rotate-ccw-B45JsL5f.js} +1 -1
- package/admin-dist/public/assets/scroll-area-b3A1HHR7.js +1 -0
- package/admin-dist/public/assets/{search-BvgYr-c9.js → search-DKKh_DdH.js} +1 -1
- package/admin-dist/public/assets/settings-CGVDEV1r.js +1 -0
- package/admin-dist/public/assets/switch-BTMY8Qnk.js +1 -0
- package/admin-dist/public/assets/tabs-DUQwUoIb.js +1 -0
- package/admin-dist/public/assets/tanstack-adapter-f7AHmQ5L.js +1 -0
- package/admin-dist/public/assets/taxonomies-DvMppdiD.js +1 -0
- package/admin-dist/public/assets/trash-D7e0uKd9.js +1 -0
- package/admin-dist/public/assets/{useBreadcrumbLabel-D00rvqjw.js → useBreadcrumbLabel-CF2KYwsw.js} +1 -1
- package/admin-dist/public/assets/usePermissions-DWBImEOW.js +1 -0
- package/admin-dist/server/_chunks/_libs/@date-fns/tz.mjs +2 -2
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-avatar.mjs +1 -1
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-collection.mjs +1 -1
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-context.mjs +2 -2
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-dialog.mjs +2 -2
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-label.mjs +1 -1
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-menu.mjs +1 -1
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-popover.mjs +1 -1
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-primitive.mjs +6 -72
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-select.mjs +1 -1
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-separator.mjs +1 -1
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +20 -435
- package/admin-dist/server/_chunks/_libs/@radix-ui/react-visually-hidden.mjs +30 -3
- package/admin-dist/server/_chunks/_libs/@tanstack/history.mjs +0 -376
- package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +168 -383
- package/admin-dist/server/_chunks/_libs/@tanstack/router-core.mjs +451 -1195
- package/admin-dist/server/_chunks/_libs/react-dom.mjs +5 -5
- package/admin-dist/server/_chunks/_libs/react.mjs +2 -2
- package/admin-dist/server/_libs/cmdk.mjs +1 -1
- package/admin-dist/server/_libs/convex.mjs +3 -3
- package/admin-dist/server/_libs/cookie-es.mjs +1 -58
- package/admin-dist/server/_libs/date-fns.mjs +1 -1
- package/admin-dist/server/_libs/lucide-react.mjs +117 -103
- package/admin-dist/server/_libs/seroval-plugins.mjs +1 -58
- package/admin-dist/server/_libs/seroval.mjs +1 -1765
- package/admin-dist/server/_libs/zod.mjs +1 -1
- package/admin-dist/server/_ssr/CmsEmptyState-BM8DghTl.mjs +38 -0
- package/admin-dist/server/_ssr/{CmsPageHeader-ClNPU7Up.mjs → CmsPageHeader-BHUmrIWD.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsStatusBadge-CojMbrY7.mjs → CmsStatusBadge-D0Zb0oRl.mjs} +7 -7
- package/admin-dist/server/_ssr/{CmsSurface-Dcv440rp.mjs → CmsSurface-B2eBr-47.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsToolbar-BKv1nL6u.mjs → CmsToolbar-BCrwg7OL.mjs} +2 -3
- package/admin-dist/server/_ssr/{ContentEntryEditor-weiXSBdZ.mjs → ContentEntryEditor-Cjfm0uhr.mjs} +46 -49
- package/admin-dist/server/_ssr/{TaxonomyFilter-BPQ57Mwk.mjs → TaxonomyFilter-C4pD0kfM.mjs} +4 -5
- package/admin-dist/server/_ssr/{_contentTypeId-DyyauLOs.mjs → _contentTypeId-CiDiX-p7.mjs} +34 -43
- package/admin-dist/server/_ssr/{_entryId-9Cafwxmw.mjs → _entryId-9GxatOkL.mjs} +35 -47
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-CC7UrHKE.mjs +4 -0
- package/admin-dist/server/_ssr/badge-EI998zba.mjs +39 -0
- package/admin-dist/server/_ssr/{command-CEf8YBxY.mjs → command-BLAWQhUw.mjs} +2 -2
- package/admin-dist/server/_ssr/{config.server-D7JHDcDv.mjs → config.server-BOr7Jxr4.mjs} +5 -14
- package/admin-dist/server/_ssr/{content-ZFWVzO25.mjs → content-BHX39L4D.mjs} +63 -63
- package/admin-dist/server/_ssr/content-types-DCzrBhTH.mjs +459 -0
- package/admin-dist/server/_ssr/{index-BlSIlH4Z.mjs → index-DwM_5VNP.mjs} +114 -30
- package/admin-dist/server/_ssr/index.mjs +3459 -62
- package/admin-dist/server/_ssr/{media-CD2_NUMw.mjs → media-CbzgTRRQ.mjs} +31 -43
- package/admin-dist/server/_ssr/{new._contentTypeId-dmZy6PBX.mjs → new._contentTypeId-6Ph-Gtlw.mjs} +33 -45
- package/admin-dist/server/_ssr/router-vd1nySeP.mjs +3041 -0
- package/admin-dist/server/_ssr/{scroll-area-BH_1K-WT.mjs → scroll-area--B9snFTJ.mjs} +1 -1
- package/admin-dist/server/_ssr/{settings-DVdsoWoh.mjs → settings-DlTO2JSj.mjs} +34 -43
- package/admin-dist/server/_ssr/{switch-DX_X8vZl.mjs → switch-C05NgNW0.mjs} +1 -1
- package/admin-dist/server/_ssr/{tabs-4FWM0sn8.mjs → tabs-DAk2J5xy.mjs} +9 -10
- package/admin-dist/server/_ssr/{tanstack-adapter-D3ZcKtbY.mjs → tanstack-adapter-DWbaPByn.mjs} +15 -1
- package/admin-dist/server/_ssr/{taxonomies-BHFfO9Yr.mjs → taxonomies-B8nqce6u.mjs} +35 -44
- package/admin-dist/server/_ssr/{trash-9tUB2KwI.mjs → trash-zdlZgpTo.mjs} +30 -39
- package/admin-dist/server/_ssr/{useBreadcrumbLabel-DVme3DSb.mjs → useBreadcrumbLabel-DpEKyG1h.mjs} +1 -1
- package/admin-dist/server/_ssr/{usePermissions-zAQj-ruE.mjs → usePermissions-olYRd9S9.mjs} +1 -1
- package/admin-dist/server/index.mjs +168 -203
- package/dist/cli/templates/cmsClient.d.ts +1 -1
- package/dist/cli/templates/cmsClient.d.ts.map +1 -1
- package/dist/cli/templates/cmsClient.js +30 -11
- package/dist/cli/templates/cmsClient.js.map +1 -1
- package/dist/cli/templates/cmsConfig.d.ts +1 -1
- package/dist/cli/templates/cmsConfig.d.ts.map +1 -1
- package/dist/cli/templates/cmsConfig.js +3 -3
- package/dist/client/admin/contentLock.d.ts +4 -4
- package/dist/client/admin/contentLock.d.ts.map +1 -1
- package/dist/client/admin/contentLock.js +1 -1
- package/dist/client/admin/contentLock.js.map +1 -1
- package/dist/client/admin/contentTypes.d.ts +328 -290
- package/dist/client/admin/contentTypes.d.ts.map +1 -1
- package/dist/client/admin/contentTypes.js +307 -9
- package/dist/client/admin/contentTypes.js.map +1 -1
- package/dist/client/admin/entries.d.ts +18 -19
- package/dist/client/admin/entries.d.ts.map +1 -1
- package/dist/client/admin/entries.js +33 -8
- package/dist/client/admin/entries.js.map +1 -1
- package/dist/client/admin/index.d.ts +694 -642
- package/dist/client/admin/index.d.ts.map +1 -1
- package/dist/client/admin/index.js +45 -5
- package/dist/client/admin/index.js.map +1 -1
- package/dist/client/admin/media.d.ts.map +1 -1
- package/dist/client/admin/taxonomies.d.ts.map +1 -1
- package/dist/client/admin/trash.d.ts +3 -4
- package/dist/client/admin/trash.d.ts.map +1 -1
- package/dist/client/admin/types.d.ts +185 -4
- package/dist/client/admin/types.d.ts.map +1 -1
- package/dist/client/admin/validators.d.ts +2009 -25
- package/dist/client/admin/validators.d.ts.map +1 -1
- package/dist/client/admin/validators.js +15 -4
- package/dist/client/admin/validators.js.map +1 -1
- package/dist/client/admin/versions.d.ts +1 -1
- package/dist/client/admin/versions.d.ts.map +1 -1
- package/dist/client/agentTools.d.ts +1 -4
- package/dist/client/agentTools.d.ts.map +1 -1
- package/dist/client/agentTools.js +9 -21
- package/dist/client/agentTools.js.map +1 -1
- package/dist/client/config.d.ts +10 -0
- package/dist/client/config.d.ts.map +1 -1
- package/dist/client/config.js +1 -0
- package/dist/client/config.js.map +1 -1
- package/dist/client/defineContent.d.ts +338 -0
- package/dist/client/defineContent.d.ts.map +1 -0
- package/dist/client/defineContent.js +368 -0
- package/dist/client/defineContent.js.map +1 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/queryBuilder.d.ts +0 -15
- package/dist/client/queryBuilder.d.ts.map +1 -1
- package/dist/client/queryBuilder.js +0 -23
- package/dist/client/queryBuilder.js.map +1 -1
- package/dist/client/registry.d.ts +77 -0
- package/dist/client/registry.d.ts.map +1 -0
- package/dist/client/registry.js +95 -0
- package/dist/client/registry.js.map +1 -0
- package/dist/client/schema/defineContentType.d.ts +36 -24
- package/dist/client/schema/defineContentType.d.ts.map +1 -1
- package/dist/client/schema/defineContentType.js +176 -128
- package/dist/client/schema/defineContentType.js.map +1 -1
- package/dist/client/schema/typedClient.d.ts.map +1 -1
- package/dist/client/schema/typedClient.js +2 -10
- package/dist/client/schema/typedClient.js.map +1 -1
- package/dist/client/schema/types.d.ts +16 -9
- package/dist/client/schema/types.d.ts.map +1 -1
- package/dist/client/schema/types.js.map +1 -1
- package/dist/client/utils/toSlug.d.ts +60 -0
- package/dist/client/utils/toSlug.d.ts.map +1 -0
- package/dist/client/utils/toSlug.js +31 -0
- package/dist/client/utils/toSlug.js.map +1 -0
- package/dist/client/wrapper.d.ts +2 -2
- package/dist/client/wrapper.d.ts.map +1 -1
- package/dist/client/wrapper.js +22 -30
- package/dist/client/wrapper.js.map +1 -1
- package/dist/component/_generated/component.d.ts +24 -28
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/authorizationHooks.d.ts +1 -1
- package/dist/component/authorizationHooks.d.ts.map +1 -1
- package/dist/component/authorizationHooks.js +2 -2
- package/dist/component/authorizationHooks.js.map +1 -1
- package/dist/component/bulkOperations.d.ts.map +1 -1
- package/dist/component/bulkOperations.js +7 -4
- package/dist/component/bulkOperations.js.map +1 -1
- package/dist/component/contentEntries.d.ts +18 -56
- package/dist/component/contentEntries.d.ts.map +1 -1
- package/dist/component/contentEntries.js +45 -137
- package/dist/component/contentEntries.js.map +1 -1
- package/dist/component/contentEntryMutations.d.ts +14 -14
- package/dist/component/contentEntryMutations.d.ts.map +1 -1
- package/dist/component/contentEntryMutations.js +40 -43
- package/dist/component/contentEntryMutations.js.map +1 -1
- package/dist/component/contentEntryValidation.d.ts +3 -3
- package/dist/component/contentEntryValidation.js +6 -9
- package/dist/component/contentEntryValidation.js.map +1 -1
- package/dist/component/contentLock.d.ts +7 -7
- package/dist/component/contentLock.js +3 -3
- package/dist/component/contentLock.js.map +1 -1
- package/dist/component/contentTypeMigration.d.ts +1 -1
- package/dist/component/contentTypeMigration.js +2 -2
- package/dist/component/contentTypeMigration.js.map +1 -1
- package/dist/component/contentTypeMutations.d.ts.map +1 -1
- package/dist/component/contentTypeMutations.js +7 -6
- package/dist/component/contentTypeMutations.js.map +1 -1
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/convex.config.js +1 -1
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/eventEmitter.d.ts +0 -1
- package/dist/component/eventEmitter.d.ts.map +1 -1
- package/dist/component/eventEmitter.js.map +1 -1
- package/dist/component/exportImport.d.ts +37 -37
- package/dist/component/exportImport.d.ts.map +1 -1
- package/dist/component/exportImport.js +34 -34
- package/dist/component/exportImport.js.map +1 -1
- package/dist/component/lib/deepReferenceResolver.d.ts +2 -2
- package/dist/component/lib/deepReferenceResolver.d.ts.map +1 -1
- package/dist/component/lib/deepReferenceResolver.js +13 -8
- package/dist/component/lib/deepReferenceResolver.js.map +1 -1
- package/dist/component/lib/ragContentChunker.d.ts +3 -3
- package/dist/component/lib/ragContentChunker.d.ts.map +1 -1
- package/dist/component/lib/ragContentChunker.js +4 -4
- package/dist/component/lib/ragContentChunker.js.map +1 -1
- package/dist/component/lib/referenceResolver.d.ts.map +1 -1
- package/dist/component/lib/referenceResolver.js +10 -17
- package/dist/component/lib/referenceResolver.js.map +1 -1
- package/dist/component/mediaAssetMutations.js +4 -4
- package/dist/component/mediaAssetMutations.js.map +1 -1
- package/dist/component/ragContentIndexer.d.ts +2 -2
- package/dist/component/ragContentIndexer.d.ts.map +1 -1
- package/dist/component/ragContentIndexer.js +44 -48
- package/dist/component/ragContentIndexer.js.map +1 -1
- package/dist/component/roles.d.ts +3 -3
- package/dist/component/scheduledPublish.d.ts +4 -4
- package/dist/component/scheduledPublish.js +3 -3
- package/dist/component/scheduledPublish.js.map +1 -1
- package/dist/component/schema.d.ts +18 -18
- package/dist/component/schema.js +5 -5
- package/dist/component/schema.js.map +1 -1
- package/dist/component/trash.d.ts +6 -9
- package/dist/component/trash.d.ts.map +1 -1
- package/dist/component/trash.js +12 -36
- package/dist/component/trash.js.map +1 -1
- package/dist/component/userContext.d.ts +1 -2
- package/dist/component/userContext.d.ts.map +1 -1
- package/dist/component/userContext.js +1 -2
- package/dist/component/userContext.js.map +1 -1
- package/dist/component/validators.d.ts +27 -33
- package/dist/component/validators.d.ts.map +1 -1
- package/dist/component/validators.js +3 -5
- package/dist/component/validators.js.map +1 -1
- package/dist/component/versionMutations.d.ts +1 -1
- package/dist/component/webhookTrigger.d.ts +14 -14
- package/dist/test.d.ts +30 -30
- package/dist/test.d.ts.map +1 -1
- package/dist/test.js +24 -24
- package/dist/test.js.map +1 -1
- package/package.json +1 -1
- package/admin-dist/public/assets/CmsEmptyState-Do_erIgn.js +0 -5
- package/admin-dist/public/assets/CmsPageHeader-qDwPGi48.js +0 -1
- package/admin-dist/public/assets/CmsStatusBadge-Dd9uToHE.js +0 -1
- package/admin-dist/public/assets/CmsToolbar-D1-Y-7SK.js +0 -1
- package/admin-dist/public/assets/ContentEntryEditor-CWBiIx52.js +0 -4
- package/admin-dist/public/assets/TaxonomyFilter-CdYQawxb.js +0 -1
- package/admin-dist/public/assets/_contentTypeId-D9VMP6Gs.js +0 -1
- package/admin-dist/public/assets/_entryId-2FlCfqE7.js +0 -1
- package/admin-dist/public/assets/alert-GxZx0y5c.js +0 -1
- package/admin-dist/public/assets/badge-BAlGIjop.js +0 -1
- package/admin-dist/public/assets/command-di7XCqcv.js +0 -1
- package/admin-dist/public/assets/content-D8zELsDG.js +0 -1
- package/admin-dist/public/assets/content-types-BmzD0krT.js +0 -2
- package/admin-dist/public/assets/globals-BvFfH-v9.css +0 -1
- package/admin-dist/public/assets/index-zqfj4T_v.js +0 -1
- package/admin-dist/public/assets/label-B6PPtKR5.js +0 -1
- package/admin-dist/public/assets/link-2-W2fVnVOf.js +0 -1
- package/admin-dist/public/assets/list-F8O0lZXC.js +0 -1
- package/admin-dist/public/assets/main-dZT72bAG.js +0 -97
- package/admin-dist/public/assets/media-CETueFbV.js +0 -1
- package/admin-dist/public/assets/new._contentTypeId-BV2-TyyR.js +0 -1
- package/admin-dist/public/assets/plus-AABQIF0N.js +0 -1
- package/admin-dist/public/assets/scroll-area-CDfk-zrz.js +0 -1
- package/admin-dist/public/assets/select-BuiHcMzS.js +0 -1
- package/admin-dist/public/assets/settings-DBxbYDvn.js +0 -1
- package/admin-dist/public/assets/switch-DiJvolcs.js +0 -1
- package/admin-dist/public/assets/tabs-Cgz6G_Xy.js +0 -1
- package/admin-dist/public/assets/tanstack-adapter-BknsSgra.js +0 -1
- package/admin-dist/public/assets/taxonomies-DOErsLl5.js +0 -1
- package/admin-dist/public/assets/textarea-CgggMxUX.js +0 -1
- package/admin-dist/public/assets/trash-BU4ANuaW.js +0 -1
- package/admin-dist/public/assets/triangle-alert-lvCbwp0s.js +0 -1
- package/admin-dist/public/assets/usePermissions-D7tQowaF.js +0 -1
- package/admin-dist/server/_libs/h3-v2.mjs +0 -277
- package/admin-dist/server/_ssr/CmsButton-DbzfJru_.mjs +0 -125
- package/admin-dist/server/_ssr/CmsEmptyState-CuvcXr3Z.mjs +0 -290
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dk-FIYPN.mjs +0 -4
- package/admin-dist/server/_ssr/content-types-D25lUE-j.mjs +0 -1312
- package/admin-dist/server/_ssr/label-PblVvdRv.mjs +0 -22
- package/admin-dist/server/_ssr/router-x6Ab8T4s.mjs +0 -1622
- package/admin-dist/server/_ssr/select-CrfEkFJw.mjs +0 -142
- 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
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/convex-cms)
|
|
4
|
+
[](https://opensource.org/licenses/Apache-2.0)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
> **Alpha (v0.0.7)** Actively developed. APIs may change. [Report issues](https://github.com/obkaro/convex-cms/issues).
|
|
6
7
|
|
|
7
|
-
|
|
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
|
|
|
@@ -95,14 +20,14 @@ pnpm add convex-cms
|
|
|
95
20
|
```typescript
|
|
96
21
|
// convex/convex.config.ts
|
|
97
22
|
import { defineApp } from "convex/server";
|
|
98
|
-
import
|
|
23
|
+
import cms from "convex-cms/convex.config";
|
|
99
24
|
|
|
100
25
|
const app = defineApp();
|
|
101
|
-
app.use(
|
|
26
|
+
app.use(cms);
|
|
102
27
|
export default app;
|
|
103
28
|
```
|
|
104
29
|
|
|
105
|
-
### 3.
|
|
30
|
+
### 3. Initialize
|
|
106
31
|
|
|
107
32
|
**For Admin UI:** Run `pnpm convex-cms init` then `pnpm convex-cms admin`
|
|
108
33
|
→ [Full Admin UI Setup](./docs/guides/admin-ui-setup.md)
|
|
@@ -110,35 +35,77 @@ 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
|
|
|
113
|
-
##
|
|
38
|
+
## Why Convex CMS?
|
|
39
|
+
|
|
40
|
+
If you're building on Convex and need content management without the overhead of all the CMS plumbing, this is the most integrated option.
|
|
41
|
+
|
|
42
|
+
### What you get:
|
|
43
|
+
|
|
44
|
+
- **Typesafe admin API** Admin APIs exported directly from your backend for use in your React queries and mutations
|
|
45
|
+
- **Built in admin UI** A well designed admin UI that you can view and edit content from
|
|
46
|
+
- **Embeddable content manager** Ability to embed and serve the prebuilt UI as part of you React application
|
|
47
|
+
- **Data independence** CMS that lives in your own convex deployment, extendable and customizable with your convex functions
|
|
48
|
+
- **Agent-native.** Pre-built tools useful for AI agent integration with `@convex-dev/agent`
|
|
49
|
+
|
|
50
|
+
## Features
|
|
51
|
+
|
|
52
|
+
| Feature | What it does |
|
|
53
|
+
|---------|--------------|
|
|
54
|
+
| **Code-first config** | Define content types in TypeScript with full type inference |
|
|
55
|
+
| **UI-defined config** | Create and modify content types through the admin interface |
|
|
56
|
+
| **CMS Client** | Programmatic access for custom queries and mutations |
|
|
57
|
+
| **Admin API** | Pre-built functions that power the admin UI |
|
|
58
|
+
| **CLI Admin UI** | Run for local development, content entry, and management |
|
|
59
|
+
| **Embedded Admin UI** | Ship the admin interface as part of your React app |
|
|
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. Pre-built tools for AI-driven workflows.
|
|
74
|
+
|
|
75
|
+
*Any combination of these features works together seamlessly. Pick what fits your workflow.*
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## Batteries Included
|
|
79
|
+
|
|
80
|
+
Leverage included features or extend and customize within your own convex functions to your desire.
|
|
114
81
|
|
|
115
82
|
### Core Content
|
|
116
|
-
- **13 field types
|
|
117
|
-
- **Publishing workflows
|
|
118
|
-
- **Content versioning
|
|
119
|
-
- **Scheduled publishing
|
|
83
|
+
- **13 field types.** text, richText, number, boolean, date, datetime, select, multiSelect, reference, media, json, tags, category
|
|
84
|
+
- **Publishing workflows.** Draft → scheduled → published with version history
|
|
85
|
+
- **Content versioning.** Snapshots, comparison, and rollback
|
|
86
|
+
- **Scheduled publishing.** Convex scheduler integration for future publish dates
|
|
120
87
|
|
|
121
88
|
### Media Management
|
|
122
|
-
- **File uploads
|
|
123
|
-
- **Image variants
|
|
124
|
-
- **Metadata & tagging
|
|
89
|
+
- **File uploads.** Direct to Convex storage with folder organization
|
|
90
|
+
- **Image variants.** Automatic resizing and format conversion
|
|
91
|
+
- **Metadata & tagging.** Alt text, descriptions, taxonomy support
|
|
125
92
|
|
|
126
93
|
### Organization
|
|
127
|
-
- **Taxonomies
|
|
128
|
-
- **Content locking
|
|
129
|
-
- **Soft delete & trash
|
|
94
|
+
- **Taxonomies.** Hierarchical categories and flat tags
|
|
95
|
+
- **Content locking.** Prevent concurrent edit conflicts
|
|
96
|
+
- **Soft delete & trash.** Configurable retention with restore
|
|
130
97
|
|
|
131
98
|
### Integration
|
|
132
|
-
- **RBAC
|
|
133
|
-
- **Multi-locale
|
|
134
|
-
- **Webhooks
|
|
135
|
-
- **Event system
|
|
136
|
-
- **Agent tools
|
|
137
|
-
- **Query builder
|
|
99
|
+
- **RBAC.** 4 built-in roles + custom roles with fine-grained permissions
|
|
100
|
+
- **Multi-locale.** Content localization with fallback chains
|
|
101
|
+
- **Webhooks.** Event-driven integration with external systems
|
|
102
|
+
- **Event system.** All mutations emit events for async processing
|
|
103
|
+
- **Agent tools.** 23 pre-built tools with Zod schemas for AI integration
|
|
104
|
+
- **Query builder.** Fluent API for complex content queries
|
|
138
105
|
|
|
139
106
|
### Admin UI
|
|
140
|
-
- **Pre-built React interface
|
|
141
|
-
- **Visual content editing
|
|
107
|
+
- **Pre-built React interface.** CLI mode for development, embeddable for production
|
|
108
|
+
- **Visual content editing.** Rich text, media picker, reference selector
|
|
142
109
|
|
|
143
110
|
## Admin UI Modes
|
|
144
111
|
|
|
@@ -42,13 +42,13 @@ export function BreakingChangesWarningDialog({
|
|
|
42
42
|
}
|
|
43
43
|
>
|
|
44
44
|
<div className="space-y-4">
|
|
45
|
-
<div className="flex items-start gap-3 rounded-lg border
|
|
46
|
-
<AlertTriangle className="mt-0.5 size-5 shrink-0 text-
|
|
45
|
+
<div className="diff-modified flex items-start gap-3 rounded-lg border p-3">
|
|
46
|
+
<AlertTriangle className="mt-0.5 size-5 shrink-0 text-diff-modified" />
|
|
47
47
|
<div className="space-y-1">
|
|
48
|
-
<p className="text-sm font-medium text-
|
|
48
|
+
<p className="text-sm font-medium text-diff-modified">
|
|
49
49
|
These changes may affect existing content
|
|
50
50
|
</p>
|
|
51
|
-
<p className="text-sm text-
|
|
51
|
+
<p className="text-sm text-diff-modified-foreground">
|
|
52
52
|
The following changes could cause data loss or validation errors for existing entries.
|
|
53
53
|
Review carefully before proceeding.
|
|
54
54
|
</p>
|
|
@@ -65,7 +65,7 @@ export function BreakingChangesWarningDialog({
|
|
|
65
65
|
key={index}
|
|
66
66
|
className="flex items-start gap-2 rounded-md border bg-muted/30 px-3 py-2 text-sm"
|
|
67
67
|
>
|
|
68
|
-
<span className="mt-0.5 size-1.5 shrink-0 rounded-full bg-
|
|
68
|
+
<span className="mt-0.5 size-1.5 shrink-0 rounded-full bg-warning" />
|
|
69
69
|
<span className="text-muted-foreground">{change}</span>
|
|
70
70
|
</li>
|
|
71
71
|
))}
|
|
@@ -102,7 +102,7 @@ export function BulkOperationModal({
|
|
|
102
102
|
<div className="space-y-4">
|
|
103
103
|
{result.failed === 0 ? (
|
|
104
104
|
<div className="flex flex-col items-center gap-3 py-4 text-center">
|
|
105
|
-
<div className="flex size-12 items-center justify-center rounded-full bg-
|
|
105
|
+
<div className="flex size-12 items-center justify-center rounded-full bg-diff-added-bg text-diff-added">
|
|
106
106
|
<CheckCircle className="size-6" />
|
|
107
107
|
</div>
|
|
108
108
|
<p className="text-sm text-muted-foreground">
|
|
@@ -116,33 +116,33 @@ export function BulkOperationModal({
|
|
|
116
116
|
) : (
|
|
117
117
|
<div className="space-y-4">
|
|
118
118
|
<div className="grid grid-cols-2 gap-4">
|
|
119
|
-
<div className="rounded-lg border
|
|
120
|
-
<p className="text-2xl font-semibold text-
|
|
119
|
+
<div className="diff-added rounded-lg border p-3 text-center">
|
|
120
|
+
<p className="text-2xl font-semibold text-diff-added">
|
|
121
121
|
{result.succeeded}
|
|
122
122
|
</p>
|
|
123
|
-
<p className="text-xs text-
|
|
123
|
+
<p className="text-xs text-diff-added-foreground">Succeeded</p>
|
|
124
124
|
</div>
|
|
125
|
-
<div className="rounded-lg border
|
|
126
|
-
<p className="text-2xl font-semibold text-
|
|
125
|
+
<div className="diff-removed rounded-lg border p-3 text-center">
|
|
126
|
+
<p className="text-2xl font-semibold text-diff-removed">
|
|
127
127
|
{result.failed}
|
|
128
128
|
</p>
|
|
129
|
-
<p className="text-xs text-
|
|
129
|
+
<p className="text-xs text-diff-removed-foreground">Failed</p>
|
|
130
130
|
</div>
|
|
131
131
|
</div>
|
|
132
132
|
|
|
133
133
|
{result.errors && result.errors.length > 0 && (
|
|
134
|
-
<div className="rounded-lg border
|
|
135
|
-
<p className="mb-2 text-sm font-medium text-
|
|
134
|
+
<div className="diff-modified rounded-lg border p-3">
|
|
135
|
+
<p className="mb-2 text-sm font-medium text-diff-modified">
|
|
136
136
|
Errors:
|
|
137
137
|
</p>
|
|
138
|
-
<ul className="space-y-1 text-xs text-
|
|
138
|
+
<ul className="space-y-1 text-xs text-diff-modified-foreground">
|
|
139
139
|
{result.errors.slice(0, 5).map((error, index) => (
|
|
140
140
|
<li key={index} className="truncate">
|
|
141
141
|
• {error}
|
|
142
142
|
</li>
|
|
143
143
|
))}
|
|
144
144
|
{result.errors.length > 5 && (
|
|
145
|
-
<li className="text-
|
|
145
|
+
<li className="text-diff-modified-foreground/80">
|
|
146
146
|
...and {result.errors.length - 5} more errors
|
|
147
147
|
</li>
|
|
148
148
|
)}
|
|
@@ -163,9 +163,9 @@ export function BulkOperationModal({
|
|
|
163
163
|
</p>
|
|
164
164
|
<p className="text-sm text-muted-foreground">{config.description}</p>
|
|
165
165
|
{config.warning && (
|
|
166
|
-
<div className="flex items-start gap-2 rounded-lg border
|
|
167
|
-
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-
|
|
168
|
-
<p className="text-sm
|
|
166
|
+
<div className="diff-modified flex items-start gap-2 rounded-lg border p-3">
|
|
167
|
+
<AlertTriangle className="mt-0.5 size-4 shrink-0 text-diff-modified" />
|
|
168
|
+
<p className="text-sm">
|
|
169
169
|
<span className="font-medium">Note:</span> {config.warning}
|
|
170
170
|
</p>
|
|
171
171
|
</div>
|
|
@@ -95,7 +95,7 @@ export interface ContentType {
|
|
|
95
95
|
|
|
96
96
|
export interface ContentEntry {
|
|
97
97
|
_id: string
|
|
98
|
-
|
|
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
|
-
|
|
648
|
+
contentTypeName: contentType.name,
|
|
649
649
|
data: dataForBackend,
|
|
650
650
|
})) as ContentEntry
|
|
651
651
|
}
|
|
@@ -725,8 +725,8 @@ export function ContentEntryEditor({
|
|
|
725
725
|
className={cn(
|
|
726
726
|
'flex items-center gap-1.5 text-sm',
|
|
727
727
|
autosaveStatus === 'saving' && 'text-muted-foreground',
|
|
728
|
-
autosaveStatus === 'saved' && 'text-
|
|
729
|
-
autosaveStatus === 'error' && 'text-
|
|
728
|
+
autosaveStatus === 'saved' && 'text-success',
|
|
729
|
+
autosaveStatus === 'error' && 'text-destructive'
|
|
730
730
|
)}
|
|
731
731
|
data-testid="autosave-status"
|
|
732
732
|
>
|
|
@@ -755,7 +755,7 @@ export function ContentEntryEditor({
|
|
|
755
755
|
)}
|
|
756
756
|
|
|
757
757
|
{isDirty && (
|
|
758
|
-
<span className="text-sm text-
|
|
758
|
+
<span className="text-sm text-warning">Unsaved changes</span>
|
|
759
759
|
)}
|
|
760
760
|
</div>
|
|
761
761
|
</div>
|
|
@@ -763,7 +763,7 @@ export function ContentEntryEditor({
|
|
|
763
763
|
{/* Success/Error Messages */}
|
|
764
764
|
{saveSuccess && (
|
|
765
765
|
<div
|
|
766
|
-
className="flex items-center gap-2 rounded-lg border
|
|
766
|
+
className="diff-added flex items-center gap-2 rounded-lg border px-4 py-3 text-sm"
|
|
767
767
|
role="status"
|
|
768
768
|
>
|
|
769
769
|
<CheckCircle className="size-4" />
|
|
@@ -773,7 +773,7 @@ export function ContentEntryEditor({
|
|
|
773
773
|
|
|
774
774
|
{(submitError || publishError) && (
|
|
775
775
|
<div
|
|
776
|
-
className="rounded-lg border
|
|
776
|
+
className="diff-removed rounded-lg border px-4 py-3 text-sm"
|
|
777
777
|
role="alert"
|
|
778
778
|
>
|
|
779
779
|
<span className="font-medium">Error:</span> {submitError || publishError}
|
|
@@ -886,7 +886,7 @@ export function ContentEntryEditor({
|
|
|
886
886
|
entry.status === 'scheduled' &&
|
|
887
887
|
entry.scheduledPublishAt && (
|
|
888
888
|
<span
|
|
889
|
-
className="flex items-center gap-1 text-xs text-
|
|
889
|
+
className="flex items-center gap-1 text-xs text-info"
|
|
890
890
|
data-testid="scheduled-time"
|
|
891
891
|
>
|
|
892
892
|
<Clock className="size-3" />
|
|
@@ -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={
|
|
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
|
-
|
|
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
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
|
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
|
-
|
|
640
|
-
|
|
673
|
+
if (!isReadOnly) {
|
|
674
|
+
setActiveFieldIndex(index);
|
|
675
|
+
setShowFieldEditor(true);
|
|
676
|
+
}
|
|
641
677
|
}}
|
|
642
678
|
data-testid={`field-item-${index}`}
|
|
643
679
|
>
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
e
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
<
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
e
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
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
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
e
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
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"
|
|
@@ -900,7 +940,7 @@ export function ContentTypeFormModal({
|
|
|
900
940
|
|
|
901
941
|
{submitError && (
|
|
902
942
|
<div
|
|
903
|
-
className="rounded-lg border
|
|
943
|
+
className="diff-removed rounded-lg border px-3 py-2 text-sm"
|
|
904
944
|
role="alert"
|
|
905
945
|
data-testid="submit-error"
|
|
906
946
|
>
|