convex-cms 0.0.5-alpha.0 → 0.0.5-alpha.2
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 +95 -144
- package/admin/README.md +99 -0
- package/admin/src/components/AdminLayout.tsx +22 -0
- package/admin/src/components/BreakingChangesWarningDialog.tsx +81 -0
- package/admin/src/components/BulkActionBar.tsx +190 -0
- package/admin/src/components/BulkOperationModal.tsx +177 -0
- package/admin/src/components/ContentEntryEditor.tsx +1104 -0
- package/admin/src/components/ContentTypeFormModal.tsx +1012 -0
- package/admin/src/components/ErrorBoundary.tsx +83 -0
- package/admin/src/components/ErrorState.tsx +147 -0
- package/admin/src/components/Header.tsx +294 -0
- package/admin/src/components/RouteGuard.tsx +264 -0
- package/admin/src/components/Sidebar.tsx +90 -0
- package/admin/src/components/TaxonomyEditor.tsx +348 -0
- package/admin/src/components/TermTree.tsx +533 -0
- package/admin/src/components/UploadDropzone.tsx +383 -0
- package/admin/src/components/VersionCompare.tsx +250 -0
- package/admin/src/components/VersionHistory.tsx +279 -0
- package/admin/src/components/VersionRollbackModal.tsx +79 -0
- package/admin/src/components/cmsds/CmsButton.tsx +101 -0
- package/admin/src/components/cmsds/CmsDialog.tsx +139 -0
- package/admin/src/components/cmsds/CmsDropdown.tsx +62 -0
- package/admin/src/components/cmsds/CmsEmptyState.tsx +54 -0
- package/admin/src/components/cmsds/CmsField.tsx +47 -0
- package/admin/src/components/cmsds/CmsPageHeader.tsx +35 -0
- package/admin/src/components/cmsds/CmsStatusBadge.tsx +153 -0
- package/admin/src/components/cmsds/CmsSurface.tsx +52 -0
- package/admin/src/components/cmsds/CmsTable.tsx +164 -0
- package/admin/src/components/cmsds/CmsToolbar.tsx +58 -0
- package/admin/src/components/cmsds/index.ts +10 -0
- package/admin/src/components/fields/BooleanField.tsx +74 -0
- package/admin/src/components/fields/CategoryField.tsx +394 -0
- package/admin/src/components/fields/DateField.tsx +173 -0
- package/admin/src/components/fields/DefaultFieldRenderer.tsx +74 -0
- package/admin/src/components/fields/FieldRenderer.tsx +180 -0
- package/admin/src/components/fields/FieldWrapper.tsx +57 -0
- package/admin/src/components/fields/JsonField.tsx +172 -0
- package/admin/src/components/fields/MediaField.tsx +367 -0
- package/admin/src/components/fields/MultiSelectField.tsx +118 -0
- package/admin/src/components/fields/NumberField.tsx +77 -0
- package/admin/src/components/fields/ReferenceField.tsx +386 -0
- package/admin/src/components/fields/RichTextField.tsx +171 -0
- package/admin/src/components/fields/SelectField.tsx +62 -0
- package/admin/src/components/fields/TagField.tsx +325 -0
- package/admin/src/components/fields/TextAreaField.tsx +68 -0
- package/admin/src/components/fields/TextField.tsx +56 -0
- package/admin/src/components/fields/index.ts +54 -0
- package/admin/src/components/fields/registry.ts +64 -0
- package/admin/src/components/fields/types.ts +217 -0
- package/admin/src/components/filters/TaxonomyFilter.tsx +254 -0
- package/admin/src/components/filters/index.ts +1 -0
- package/admin/src/components/index.ts +8 -0
- package/admin/src/components/media/MediaAssetActions.tsx +115 -0
- package/admin/src/components/media/MediaAssetEditDialog.tsx +217 -0
- package/admin/src/components/media/MediaBulkActionBar.tsx +51 -0
- package/admin/src/components/media/MediaFolderActions.tsx +69 -0
- package/admin/src/components/media/MediaFolderEditDialog.tsx +126 -0
- package/admin/src/components/media/MediaMoveModal.tsx +179 -0
- package/admin/src/components/media/MediaPreviewModal.tsx +370 -0
- package/admin/src/components/media/MediaTaxonomyPicker.tsx +304 -0
- package/admin/src/components/media/MediaTrashBulkActionBar.tsx +59 -0
- package/admin/src/components/ui/accordion.tsx +64 -0
- package/admin/src/components/ui/alert-dialog.tsx +155 -0
- package/admin/src/components/ui/alert.tsx +66 -0
- package/admin/src/components/ui/avatar.tsx +53 -0
- package/admin/src/components/ui/badge.tsx +46 -0
- package/admin/src/components/ui/breadcrumb.tsx +109 -0
- package/admin/src/components/ui/button.tsx +62 -0
- package/admin/src/components/ui/calendar.tsx +220 -0
- package/admin/src/components/ui/card.tsx +92 -0
- package/admin/src/components/ui/checkbox.tsx +30 -0
- package/admin/src/components/ui/command.tsx +182 -0
- package/admin/src/components/ui/dialog.tsx +143 -0
- package/admin/src/components/ui/dropdown-menu.tsx +257 -0
- package/admin/src/components/ui/form.tsx +167 -0
- package/admin/src/components/ui/input.tsx +21 -0
- package/admin/src/components/ui/label.tsx +24 -0
- package/admin/src/components/ui/popover.tsx +46 -0
- package/admin/src/components/ui/scroll-area.tsx +56 -0
- package/admin/src/components/ui/select.tsx +190 -0
- package/admin/src/components/ui/separator.tsx +26 -0
- package/admin/src/components/ui/sheet.tsx +137 -0
- package/admin/src/components/ui/sidebar.tsx +724 -0
- package/admin/src/components/ui/skeleton.tsx +13 -0
- package/admin/src/components/ui/sonner.tsx +38 -0
- package/admin/src/components/ui/switch.tsx +31 -0
- package/admin/src/components/ui/table.tsx +114 -0
- package/admin/src/components/ui/tabs.tsx +66 -0
- package/admin/src/components/ui/textarea.tsx +18 -0
- package/admin/src/components/ui/tooltip.tsx +61 -0
- package/admin/src/contexts/AdminConfigContext.tsx +30 -0
- package/admin/src/contexts/AuthContext.tsx +330 -0
- package/admin/src/contexts/BreadcrumbContext.tsx +49 -0
- package/admin/src/contexts/SettingsConfigContext.tsx +57 -0
- package/admin/src/contexts/ThemeContext.tsx +91 -0
- package/admin/src/contexts/index.ts +20 -0
- package/admin/src/embed/components/EmbedHeader.tsx +103 -0
- package/admin/src/embed/components/EmbedLayout.tsx +29 -0
- package/admin/src/embed/components/EmbedSidebar.tsx +119 -0
- package/admin/src/embed/components/index.ts +3 -0
- package/admin/src/embed/contexts/ApiContext.tsx +32 -0
- package/admin/src/embed/index.tsx +184 -0
- package/admin/src/embed/navigation.tsx +202 -0
- package/admin/src/embed/pages/Content.tsx +19 -0
- package/admin/src/embed/pages/ContentTypes.tsx +19 -0
- package/admin/src/embed/pages/Dashboard.tsx +19 -0
- package/admin/src/embed/pages/Media.tsx +19 -0
- package/admin/src/embed/pages/Settings.tsx +22 -0
- package/admin/src/embed/pages/Taxonomies.tsx +22 -0
- package/admin/src/embed/pages/Trash.tsx +22 -0
- package/admin/src/embed/pages/index.ts +7 -0
- package/admin/src/embed/types.ts +24 -0
- package/admin/src/hooks/index.ts +2 -0
- package/admin/src/hooks/use-mobile.ts +19 -0
- package/admin/src/hooks/useBreadcrumbLabel.ts +15 -0
- package/admin/src/hooks/usePermissions.ts +211 -0
- package/admin/src/lib/admin-config.ts +111 -0
- package/admin/src/lib/cn.ts +6 -0
- package/admin/src/lib/config.server.ts +56 -0
- package/admin/src/lib/convex.ts +26 -0
- package/admin/src/lib/embed-adapter.ts +80 -0
- package/admin/src/lib/icons.tsx +96 -0
- package/admin/src/lib/loadAdminConfig.ts +92 -0
- package/admin/src/lib/motion.ts +29 -0
- package/admin/src/lib/navigation.ts +43 -0
- package/admin/src/lib/tanstack-adapter.ts +82 -0
- package/admin/src/pages/ContentPage.tsx +337 -0
- package/admin/src/pages/ContentTypesPage.tsx +457 -0
- package/admin/src/pages/DashboardPage.tsx +163 -0
- package/admin/src/pages/MediaPage.tsx +34 -0
- package/admin/src/pages/SettingsPage.tsx +486 -0
- package/admin/src/pages/TaxonomiesPage.tsx +289 -0
- package/admin/src/pages/TrashPage.tsx +421 -0
- package/admin/src/pages/index.ts +14 -0
- package/admin/src/routeTree.gen.ts +262 -0
- package/admin/src/router.tsx +22 -0
- package/admin/src/routes/__root.tsx +250 -0
- package/admin/src/routes/content-types.tsx +20 -0
- package/admin/src/routes/content.tsx +20 -0
- package/admin/src/routes/entries/$entryId.tsx +107 -0
- package/admin/src/routes/entries/new.$contentTypeId.tsx +69 -0
- package/admin/src/routes/entries/type/$contentTypeId.tsx +503 -0
- package/admin/src/routes/index.tsx +20 -0
- package/admin/src/routes/media.tsx +1095 -0
- package/admin/src/routes/settings.tsx +20 -0
- package/admin/src/routes/taxonomies.tsx +20 -0
- package/admin/src/routes/trash.tsx +20 -0
- package/admin/src/styles/globals.css +69 -0
- package/admin/src/styles/tailwind-config.css +74 -0
- package/admin/src/styles/theme.css +73 -0
- package/admin/src/types/index.ts +221 -0
- package/admin/src/utils/errorParsing.ts +163 -0
- package/admin/src/utils/index.ts +5 -0
- package/admin/src/vite-env.d.ts +14 -0
- package/admin/tailwind.preset.cjs +102 -0
- package/admin-dist/nitro.json +1 -1
- package/admin-dist/public/assets/{CmsEmptyState-CiMQwSQV.js → CmsEmptyState-CkqBIab3.js} +1 -1
- package/admin-dist/public/assets/{CmsPageHeader-ohOq0luT.js → CmsPageHeader-CUtl5MMG.js} +1 -1
- package/admin-dist/public/assets/{CmsStatusBadge-BdNf0V9v.js → CmsStatusBadge-CUYFgEe-.js} +1 -1
- package/admin-dist/public/assets/{CmsSurface-CWup6Jh7.js → CmsSurface-CsJfAVa3.js} +1 -1
- package/admin-dist/public/assets/{CmsToolbar-cEBlCHa3.js → CmsToolbar-CnfbcxeP.js} +1 -1
- package/admin-dist/public/assets/{ContentEntryEditor-BY5ypfUs.js → ContentEntryEditor-BU220CCy.js} +1 -1
- package/admin-dist/public/assets/TaxonomyFilter-CWCxC5HZ.js +1 -0
- package/admin-dist/public/assets/_contentTypeId-DK8cskRt.js +1 -0
- package/admin-dist/public/assets/{_entryId-BpSmrfAm.js → _entryId-CuVMExbb.js} +1 -1
- package/admin-dist/public/assets/{alert-Bf2l8kxw.js → alert-CF1BSzGR.js} +1 -1
- package/admin-dist/public/assets/{badge-qPrc4AUM.js → badge-CmuOIVKp.js} +1 -1
- package/admin-dist/public/assets/{circle-check-big-Dgozy3vV.js → circle-check-big-BKDVG6DU.js} +1 -1
- package/admin-dist/public/assets/{command-QOmNhlb0.js → command-XJxnF2Sd.js} +1 -1
- package/admin-dist/public/assets/content-QBUxdxbS.js +1 -0
- package/admin-dist/public/assets/content-types-CrNEm8Hf.js +2 -0
- package/admin-dist/public/assets/globals-B7Wsfh_v.css +1 -0
- package/admin-dist/public/assets/index-C7xOwudI.js +1 -0
- package/admin-dist/public/assets/{label-DCsUdvFh.js → label-CHCnXeBk.js} +1 -1
- package/admin-dist/public/assets/{link-2-Czw1N61H.js → link-2-Bb34judH.js} +1 -1
- package/admin-dist/public/assets/{list-DtCsXj8-.js → list-9Pzt48ld.js} +1 -1
- package/admin-dist/public/assets/{main-CXgkZMhe.js → main-CjQ2VI9L.js} +3 -3
- package/admin-dist/public/assets/media-Dc5PWt2Q.js +1 -0
- package/admin-dist/public/assets/{new._contentTypeId-CoTDxKzf.js → new._contentTypeId-C_I4YxIa.js} +1 -1
- package/admin-dist/public/assets/{plus-xCFJK0RC.js → plus-Ceef7DHk.js} +1 -1
- package/admin-dist/public/assets/{rotate-ccw-DIqK63wY.js → rotate-ccw-7k7-4VUq.js} +1 -1
- package/admin-dist/public/assets/{scroll-area-B-yrE66a.js → scroll-area-CC6wujnp.js} +1 -1
- package/admin-dist/public/assets/{search-CbCbboeU.js → search-DwoUV2pv.js} +1 -1
- package/admin-dist/public/assets/{select-Co3TZFJb.js → select-hOZTp8aC.js} +1 -1
- package/admin-dist/public/assets/{settings-BspTTv_o.js → settings-t2PbCZh4.js} +1 -1
- package/admin-dist/public/assets/{switch-CfavASmR.js → switch-jX2pDaNU.js} +1 -1
- package/admin-dist/public/assets/{tabs-CN5s5u2W.js → tabs-q4EbZk7c.js} +1 -1
- package/admin-dist/public/assets/{tanstack-adapter-npeE3RdY.js → tanstack-adapter-B-Glm4kH.js} +1 -1
- package/admin-dist/public/assets/taxonomies-kyk5P4ZW.js +1 -0
- package/admin-dist/public/assets/{textarea-BJ0XFZpT.js → textarea-B6SfBmr0.js} +1 -1
- package/admin-dist/public/assets/trash-BOCnIznD.js +1 -0
- package/admin-dist/public/assets/{triangle-alert-BZRcqsUg.js → triangle-alert-CXFIO_Gu.js} +1 -1
- package/admin-dist/public/assets/{useBreadcrumbLabel-DwZlwvFF.js → useBreadcrumbLabel-_6qBagc3.js} +1 -1
- package/admin-dist/public/assets/{usePermissions-C1JQhfqb.js → usePermissions-M1ijZ7a6.js} +1 -1
- package/admin-dist/server/_ssr/{CmsButton-B45JAKR1.mjs → CmsButton-DOiTVKQq.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsEmptyState-D_BQFAVR.mjs → CmsEmptyState-fbnGt3LD.mjs} +2 -2
- package/admin-dist/server/_ssr/{CmsPageHeader-CrUZA59A.mjs → CmsPageHeader-DHRrdOZa.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsStatusBadge-B-sj6yaj.mjs → CmsStatusBadge-s7obWbKZ.mjs} +2 -2
- package/admin-dist/server/_ssr/{CmsSurface-DKJZhpjk.mjs → CmsSurface-rFoYjb62.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsToolbar-ByaW5iXf.mjs → CmsToolbar-zTE45z2q.mjs} +2 -2
- package/admin-dist/server/_ssr/{ContentEntryEditor-D3_Jb1dq.mjs → ContentEntryEditor-BLoEjT_m.mjs} +12 -12
- package/admin-dist/server/_ssr/{TaxonomyFilter-BRJkuCtA.mjs → TaxonomyFilter-XAtaJC2z.mjs} +5 -5
- package/admin-dist/server/_ssr/{_contentTypeId-B9kA6CaM.mjs → _contentTypeId-Csl4822C.mjs} +13 -13
- package/admin-dist/server/_ssr/{_entryId-BddcMkZN.mjs → _entryId-D8alLFBx.mjs} +15 -15
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BffZedId.mjs +4 -0
- package/admin-dist/server/_ssr/{command-CGtVr8Gb.mjs → command-C0Di14--.mjs} +1 -1
- package/admin-dist/server/_ssr/{content-D1tbeOd0.mjs → content-CT-FPsmV.mjs} +12 -55
- package/admin-dist/server/_ssr/{content-types-BZqY_BER.mjs → content-types-C8cBFdzE.mjs} +15 -46
- package/admin-dist/server/_ssr/{index-BIdq4xaC.mjs → index-BJtcrEc-.mjs} +5 -5
- package/admin-dist/server/_ssr/index.mjs +2 -2
- package/admin-dist/server/_ssr/{label-T-QNKAr6.mjs → label-qn2Afwl4.mjs} +1 -1
- package/admin-dist/server/_ssr/{media-C-xqjBrl.mjs → media-qv5IAsMZ.mjs} +14 -14
- package/admin-dist/server/_ssr/{new._contentTypeId-DWic9cRq.mjs → new._contentTypeId-DdGyrhqs.mjs} +13 -13
- package/admin-dist/server/_ssr/{router-D1BMAMJT.mjs → router-nSVkxb6Y.mjs} +11 -11
- package/admin-dist/server/_ssr/{scroll-area-C0pic_WA.mjs → scroll-area-BCinP455.mjs} +1 -1
- package/admin-dist/server/_ssr/{select-CqmuN2F6.mjs → select-BKQlQScw.mjs} +1 -1
- package/admin-dist/server/_ssr/{settings-CAkncGGV.mjs → settings-BCr2KQlk.mjs} +55 -40
- package/admin-dist/server/_ssr/{switch-CgmuJkT9.mjs → switch-BaOi42fE.mjs} +1 -1
- package/admin-dist/server/_ssr/{tabs-CnMj0aRy.mjs → tabs-DYXEi9kq.mjs} +2 -2
- package/admin-dist/server/_ssr/{tanstack-adapter-BXZrMauE.mjs → tanstack-adapter-Bsz8kha-.mjs} +1 -1
- package/admin-dist/server/_ssr/{taxonomies-thl3BfVm.mjs → taxonomies-CueMHTbE.mjs} +30 -19
- package/admin-dist/server/_ssr/{textarea-4K5OJgeh.mjs → textarea-CI0Jqx2x.mjs} +1 -1
- package/admin-dist/server/_ssr/{trash-B40Gx5zP.mjs → trash-DE6W8GoX.mjs} +20 -17
- package/admin-dist/server/_ssr/{useBreadcrumbLabel-rn-fL4zV.mjs → useBreadcrumbLabel-B5Yi72lM.mjs} +1 -1
- package/admin-dist/server/_ssr/{usePermissions-CKeM6_Vw.mjs → usePermissions-C3nZ-Izm.mjs} +1 -1
- package/admin-dist/server/index.mjs +183 -190
- package/dist/client/admin/bulk.d.ts +79 -0
- package/dist/client/admin/bulk.d.ts.map +1 -0
- package/dist/client/admin/bulk.js +72 -0
- package/dist/client/admin/bulk.js.map +1 -0
- package/dist/client/admin/contentLock.d.ts +118 -0
- package/dist/client/admin/contentLock.d.ts.map +1 -0
- package/dist/client/admin/contentLock.js +81 -0
- package/dist/client/admin/contentLock.js.map +1 -0
- package/dist/client/{adminApi.d.ts → admin/contentTypes.d.ts} +39 -1134
- package/dist/client/admin/contentTypes.d.ts.map +1 -0
- package/dist/client/admin/contentTypes.js +122 -0
- package/dist/client/admin/contentTypes.js.map +1 -0
- package/dist/client/admin/dashboard.d.ts +16 -0
- package/dist/client/admin/dashboard.d.ts.map +1 -0
- package/dist/client/admin/dashboard.js +33 -0
- package/dist/client/admin/dashboard.js.map +1 -0
- package/dist/client/admin/entries.d.ts +358 -0
- package/dist/client/admin/entries.d.ts.map +1 -0
- package/dist/client/admin/entries.js +220 -0
- package/dist/client/admin/entries.js.map +1 -0
- package/dist/client/admin/index.d.ts +6568 -0
- package/dist/client/admin/index.d.ts.map +1 -0
- package/dist/client/admin/index.js +305 -0
- package/dist/client/admin/index.js.map +1 -0
- package/dist/client/admin/media.d.ts +1038 -0
- package/dist/client/admin/media.d.ts.map +1 -0
- package/dist/client/admin/media.js +489 -0
- package/dist/client/admin/media.js.map +1 -0
- package/dist/client/admin/taxonomies.d.ts +339 -0
- package/dist/client/admin/taxonomies.d.ts.map +1 -0
- package/dist/client/admin/taxonomies.js +364 -0
- package/dist/client/admin/taxonomies.js.map +1 -0
- package/dist/client/admin/trash.d.ts +91 -0
- package/dist/client/admin/trash.d.ts.map +1 -0
- package/dist/client/admin/trash.js +71 -0
- package/dist/client/admin/trash.js.map +1 -0
- package/dist/client/admin/types.d.ts +320 -0
- package/dist/client/admin/types.d.ts.map +1 -0
- package/dist/client/admin/types.js +7 -0
- package/dist/client/admin/types.js.map +1 -0
- package/dist/client/admin/validators.d.ts +3886 -0
- package/dist/client/admin/validators.d.ts.map +1 -0
- package/dist/client/admin/validators.js +322 -0
- package/dist/client/admin/validators.js.map +1 -0
- package/dist/client/admin/versions.d.ts +106 -0
- package/dist/client/admin/versions.d.ts.map +1 -0
- package/dist/client/admin/versions.js +57 -0
- package/dist/client/admin/versions.js.map +1 -0
- package/dist/client/adminApiTypes.d.ts +27 -0
- package/dist/client/adminApiTypes.d.ts.map +1 -0
- package/dist/client/adminApiTypes.js +12 -0
- package/dist/client/adminApiTypes.js.map +1 -0
- package/dist/client/{admin-config.d.ts → adminConfig.d.ts} +2 -2
- package/dist/client/adminConfig.d.ts.map +1 -0
- package/dist/client/{admin-config.js → adminConfig.js} +1 -1
- package/dist/client/adminConfig.js.map +1 -0
- package/dist/client/agentTools.d.ts +4 -4
- package/dist/client/index.d.ts +2 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +15 -2
- package/dist/client/index.js.map +1 -1
- package/dist/component/contentEntries.d.ts +4 -4
- package/dist/component/contentEntryMutations.d.ts +46 -0
- package/dist/component/contentEntryMutations.d.ts.map +1 -1
- package/dist/component/contentEntryMutations.js +1 -1
- package/dist/component/contentEntryMutations.js.map +1 -1
- package/dist/component/contentTypeMigration.d.ts +1 -1
- package/dist/component/contentTypeMutations.d.ts +22 -0
- package/dist/component/contentTypeMutations.d.ts.map +1 -1
- package/dist/component/contentTypeMutations.js +1 -1
- package/dist/component/contentTypeMutations.js.map +1 -1
- package/dist/component/mediaAssetMutations.d.ts +47 -0
- package/dist/component/mediaAssetMutations.d.ts.map +1 -1
- package/dist/component/mediaAssetMutations.js +1 -1
- package/dist/component/mediaAssetMutations.js.map +1 -1
- package/dist/component/schema.d.ts +9 -0
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +1 -1
- package/dist/component/schema.js.map +1 -1
- package/package.json +85 -3
- package/admin-dist/public/assets/ErrorState-C4nJ-ml4.js +0 -1
- package/admin-dist/public/assets/TaxonomyFilter-BgE_SR_O.js +0 -1
- package/admin-dist/public/assets/_contentTypeId-DtZectcC.js +0 -1
- package/admin-dist/public/assets/content-OEBGlxg1.js +0 -1
- package/admin-dist/public/assets/content-types-CjQliqVV.js +0 -2
- package/admin-dist/public/assets/globals-hAmgC66w.css +0 -1
- package/admin-dist/public/assets/index-BH_ECMhv.js +0 -1
- package/admin-dist/public/assets/media-DTJ3-ViE.js +0 -1
- package/admin-dist/public/assets/taxonomies-CgG46fIF.js +0 -1
- package/admin-dist/public/assets/trash-B3daldm5.js +0 -1
- package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +0 -89
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dd7AmelK.mjs +0 -4
- package/dist/client/admin-config.d.ts.map +0 -1
- package/dist/client/admin-config.js.map +0 -1
- package/dist/client/adminApi.d.ts.map +0 -1
- package/dist/client/adminApi.js +0 -736
- package/dist/client/adminApi.js.map +0 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createContext, useContext, useState, useCallback, useMemo } from 'react'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
|
|
4
|
+
interface BreadcrumbContextValue {
|
|
5
|
+
overrides: Map<string, string>
|
|
6
|
+
setOverride: (path: string, label: string) => void
|
|
7
|
+
clearOverride: (path: string) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const BreadcrumbContext = createContext<BreadcrumbContextValue | null>(null)
|
|
11
|
+
|
|
12
|
+
export function BreadcrumbProvider({ children }: { children: ReactNode }) {
|
|
13
|
+
const [overrides, setOverrides] = useState<Map<string, string>>(new Map())
|
|
14
|
+
|
|
15
|
+
const setOverride = useCallback((path: string, label: string) => {
|
|
16
|
+
setOverrides((prev) => {
|
|
17
|
+
const next = new Map(prev)
|
|
18
|
+
next.set(path, label)
|
|
19
|
+
return next
|
|
20
|
+
})
|
|
21
|
+
}, [])
|
|
22
|
+
|
|
23
|
+
const clearOverride = useCallback((path: string) => {
|
|
24
|
+
setOverrides((prev) => {
|
|
25
|
+
const next = new Map(prev)
|
|
26
|
+
next.delete(path)
|
|
27
|
+
return next
|
|
28
|
+
})
|
|
29
|
+
}, [])
|
|
30
|
+
|
|
31
|
+
const value = useMemo(
|
|
32
|
+
() => ({ overrides, setOverride, clearOverride }),
|
|
33
|
+
[overrides, setOverride, clearOverride]
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<BreadcrumbContext.Provider value={value}>
|
|
38
|
+
{children}
|
|
39
|
+
</BreadcrumbContext.Provider>
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function useBreadcrumbContext() {
|
|
44
|
+
const context = useContext(BreadcrumbContext)
|
|
45
|
+
if (!context) {
|
|
46
|
+
throw new Error('useBreadcrumbContext must be used within a BreadcrumbProvider')
|
|
47
|
+
}
|
|
48
|
+
return context
|
|
49
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useQuery } from "convex/react";
|
|
2
|
+
import { createContext, useContext, useMemo, type ReactNode } from "react";
|
|
3
|
+
import { api } from "../../convex/_generated/api";
|
|
4
|
+
import type { AdminConfig } from "~/lib/admin-config";
|
|
5
|
+
import { AdminConfigProvider } from "./AdminConfigContext";
|
|
6
|
+
|
|
7
|
+
type Settings = NonNullable<typeof api.settings.get._returnType>;
|
|
8
|
+
|
|
9
|
+
interface SettingsConfigContextValue {
|
|
10
|
+
baseConfig: AdminConfig;
|
|
11
|
+
settings: Settings | undefined;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const SettingsConfigContext = createContext<SettingsConfigContextValue | null>(null);
|
|
15
|
+
|
|
16
|
+
export function SettingsConfigProvider({
|
|
17
|
+
baseConfig,
|
|
18
|
+
children,
|
|
19
|
+
}: {
|
|
20
|
+
baseConfig: AdminConfig;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}) {
|
|
23
|
+
const settings = useQuery(api.settings.get);
|
|
24
|
+
|
|
25
|
+
const mergedConfig = useMemo((): AdminConfig => {
|
|
26
|
+
if (!settings) return baseConfig;
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
...baseConfig,
|
|
30
|
+
navigation: {
|
|
31
|
+
...baseConfig.navigation,
|
|
32
|
+
showMedia: baseConfig.navigation.showMedia && settings.features.mediaManagement,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}, [baseConfig, settings]);
|
|
36
|
+
|
|
37
|
+
const contextValue = useMemo(
|
|
38
|
+
() => ({ baseConfig, settings }),
|
|
39
|
+
[baseConfig, settings]
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<SettingsConfigContext.Provider value={contextValue}>
|
|
44
|
+
<AdminConfigProvider config={mergedConfig}>
|
|
45
|
+
{children}
|
|
46
|
+
</AdminConfigProvider>
|
|
47
|
+
</SettingsConfigContext.Provider>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function useSettingsConfig(): SettingsConfigContextValue {
|
|
52
|
+
const ctx = useContext(SettingsConfigContext);
|
|
53
|
+
if (!ctx) {
|
|
54
|
+
throw new Error("useSettingsConfig must be used within SettingsConfigProvider");
|
|
55
|
+
}
|
|
56
|
+
return ctx;
|
|
57
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useEffect,
|
|
5
|
+
useState,
|
|
6
|
+
useCallback,
|
|
7
|
+
type ReactNode,
|
|
8
|
+
} from 'react'
|
|
9
|
+
|
|
10
|
+
type Theme = 'light' | 'dark' | 'system'
|
|
11
|
+
|
|
12
|
+
interface ThemeContextValue {
|
|
13
|
+
theme: Theme
|
|
14
|
+
resolvedTheme: 'light' | 'dark'
|
|
15
|
+
setTheme: (theme: Theme) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const ThemeContext = createContext<ThemeContextValue | null>(null)
|
|
19
|
+
|
|
20
|
+
const STORAGE_KEY = 'convex-cms-theme'
|
|
21
|
+
|
|
22
|
+
function getSystemTheme(): 'light' | 'dark' {
|
|
23
|
+
if (typeof window === 'undefined') return 'light'
|
|
24
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getStoredTheme(): Theme {
|
|
28
|
+
if (typeof window === 'undefined') return 'system'
|
|
29
|
+
const stored = localStorage.getItem(STORAGE_KEY)
|
|
30
|
+
if (stored === 'light' || stored === 'dark' || stored === 'system') {
|
|
31
|
+
return stored
|
|
32
|
+
}
|
|
33
|
+
return 'system'
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function ThemeProvider({ children }: { children: ReactNode }) {
|
|
37
|
+
const [theme, setThemeState] = useState<Theme>(() => getStoredTheme())
|
|
38
|
+
const [resolvedTheme, setResolvedTheme] = useState<'light' | 'dark'>(() => {
|
|
39
|
+
const stored = getStoredTheme()
|
|
40
|
+
return stored === 'system' ? getSystemTheme() : stored
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const applyTheme = useCallback((newTheme: Theme) => {
|
|
44
|
+
const resolved = newTheme === 'system' ? getSystemTheme() : newTheme
|
|
45
|
+
setResolvedTheme(resolved)
|
|
46
|
+
|
|
47
|
+
const root = document.documentElement
|
|
48
|
+
root.classList.remove('light', 'dark')
|
|
49
|
+
root.classList.add(resolved)
|
|
50
|
+
}, [])
|
|
51
|
+
|
|
52
|
+
const setTheme = useCallback(
|
|
53
|
+
(newTheme: Theme) => {
|
|
54
|
+
setThemeState(newTheme)
|
|
55
|
+
localStorage.setItem(STORAGE_KEY, newTheme)
|
|
56
|
+
applyTheme(newTheme)
|
|
57
|
+
},
|
|
58
|
+
[applyTheme]
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
applyTheme(theme)
|
|
63
|
+
}, [theme, applyTheme])
|
|
64
|
+
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
|
|
67
|
+
|
|
68
|
+
const handleChange = () => {
|
|
69
|
+
if (theme === 'system') {
|
|
70
|
+
applyTheme('system')
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
mediaQuery.addEventListener('change', handleChange)
|
|
75
|
+
return () => mediaQuery.removeEventListener('change', handleChange)
|
|
76
|
+
}, [theme, applyTheme])
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<ThemeContext.Provider value={{ theme, resolvedTheme, setTheme }}>
|
|
80
|
+
{children}
|
|
81
|
+
</ThemeContext.Provider>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function useTheme() {
|
|
86
|
+
const context = useContext(ThemeContext)
|
|
87
|
+
if (!context) {
|
|
88
|
+
throw new Error('useTheme must be used within a ThemeProvider')
|
|
89
|
+
}
|
|
90
|
+
return context
|
|
91
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export {
|
|
2
|
+
AuthProvider,
|
|
3
|
+
useAuth,
|
|
4
|
+
type AuthUser,
|
|
5
|
+
type AuthState,
|
|
6
|
+
type AuthContextValue,
|
|
7
|
+
type AuthProviderProps,
|
|
8
|
+
type GetUserHook,
|
|
9
|
+
type GetUserRoleHook,
|
|
10
|
+
type LogoutHook,
|
|
11
|
+
type PermissionCheck,
|
|
12
|
+
} from './AuthContext';
|
|
13
|
+
|
|
14
|
+
export { ThemeProvider, useTheme } from './ThemeContext';
|
|
15
|
+
|
|
16
|
+
export { AdminConfigProvider, useAdminConfig } from './AdminConfigContext';
|
|
17
|
+
|
|
18
|
+
export { SettingsConfigProvider, useSettingsConfig } from './SettingsConfigContext';
|
|
19
|
+
|
|
20
|
+
export { BreadcrumbProvider, useBreadcrumbContext } from './BreadcrumbContext';
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embed Header
|
|
3
|
+
*
|
|
4
|
+
* A simplified header for the embedded admin without router dependencies.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { ChevronLeft, Bell, HelpCircle, User, LogOut, Moon, Sun } from "lucide-react";
|
|
8
|
+
import { useAdminConfig, useTheme, useAuth } from "../../contexts";
|
|
9
|
+
import { useEmbedNavigation } from "../navigation";
|
|
10
|
+
|
|
11
|
+
export function EmbedHeader() {
|
|
12
|
+
const { goBack, canGoBack, currentRoute } = useEmbedNavigation();
|
|
13
|
+
const { branding: _branding } = useAdminConfig();
|
|
14
|
+
const { theme, setTheme } = useTheme();
|
|
15
|
+
const { user, logout, isAuthenticated } = useAuth();
|
|
16
|
+
|
|
17
|
+
const routeTitles: Record<string, string> = {
|
|
18
|
+
dashboard: "Dashboard",
|
|
19
|
+
content: "Content",
|
|
20
|
+
"content-types": "Content Types",
|
|
21
|
+
media: "Media Library",
|
|
22
|
+
taxonomies: "Taxonomies",
|
|
23
|
+
settings: "Settings",
|
|
24
|
+
trash: "Trash",
|
|
25
|
+
entries: "Entries",
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const title = routeTitles[currentRoute.route] || "Dashboard";
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<header className="sticky top-0 z-40 flex h-14 items-center justify-between border-b border-border bg-background px-6">
|
|
32
|
+
<div className="flex items-center gap-4">
|
|
33
|
+
{canGoBack && (
|
|
34
|
+
<button
|
|
35
|
+
type="button"
|
|
36
|
+
onClick={goBack}
|
|
37
|
+
className="flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground"
|
|
38
|
+
>
|
|
39
|
+
<ChevronLeft className="size-4" />
|
|
40
|
+
Back
|
|
41
|
+
</button>
|
|
42
|
+
)}
|
|
43
|
+
<h1 className="text-lg font-semibold text-foreground">{title}</h1>
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
<div className="flex items-center gap-2">
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
|
|
50
|
+
className="flex size-9 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
51
|
+
title={`Switch to ${theme === "dark" ? "light" : "dark"} mode`}
|
|
52
|
+
>
|
|
53
|
+
{theme === "dark" ? <Sun className="size-5" /> : <Moon className="size-5" />}
|
|
54
|
+
</button>
|
|
55
|
+
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
className="flex size-9 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
59
|
+
title="Notifications"
|
|
60
|
+
>
|
|
61
|
+
<Bell className="size-5" />
|
|
62
|
+
</button>
|
|
63
|
+
|
|
64
|
+
<button
|
|
65
|
+
type="button"
|
|
66
|
+
className="flex size-9 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
67
|
+
title="Help"
|
|
68
|
+
>
|
|
69
|
+
<HelpCircle className="size-5" />
|
|
70
|
+
</button>
|
|
71
|
+
|
|
72
|
+
{isAuthenticated && user && (
|
|
73
|
+
<div className="ml-2 flex items-center gap-2 border-l border-border pl-4">
|
|
74
|
+
<div className="flex size-8 items-center justify-center rounded-full bg-primary text-primary-foreground">
|
|
75
|
+
{user.avatarUrl ? (
|
|
76
|
+
<img
|
|
77
|
+
src={user.avatarUrl}
|
|
78
|
+
alt={user.name || "User"}
|
|
79
|
+
className="size-full rounded-full object-cover"
|
|
80
|
+
/>
|
|
81
|
+
) : (
|
|
82
|
+
<User className="size-4" />
|
|
83
|
+
)}
|
|
84
|
+
</div>
|
|
85
|
+
{user.name && (
|
|
86
|
+
<span className="text-sm font-medium text-foreground">
|
|
87
|
+
{user.name}
|
|
88
|
+
</span>
|
|
89
|
+
)}
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
onClick={() => logout()}
|
|
93
|
+
className="flex size-8 items-center justify-center rounded-md text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
94
|
+
title="Log out"
|
|
95
|
+
>
|
|
96
|
+
<LogOut className="size-4" />
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
</div>
|
|
101
|
+
</header>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embed Layout
|
|
3
|
+
*
|
|
4
|
+
* A router-agnostic layout for the embedded admin that uses
|
|
5
|
+
* EmbedSidebar instead of the router-dependent Sidebar.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ReactNode } from "react";
|
|
9
|
+
import { EmbedSidebar } from "./EmbedSidebar";
|
|
10
|
+
import { EmbedHeader } from "./EmbedHeader";
|
|
11
|
+
import { useAdminConfig } from "../../contexts";
|
|
12
|
+
|
|
13
|
+
interface EmbedLayoutProps {
|
|
14
|
+
children: ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function EmbedLayout({ children }: EmbedLayoutProps) {
|
|
18
|
+
const { layout } = useAdminConfig();
|
|
19
|
+
|
|
20
|
+
return (
|
|
21
|
+
<div className="flex min-h-screen bg-background">
|
|
22
|
+
<EmbedSidebar />
|
|
23
|
+
<div className="flex flex-1 flex-col" style={{ marginLeft: layout.sidebarWidth }}>
|
|
24
|
+
<EmbedHeader />
|
|
25
|
+
<main className="flex-1 overflow-auto p-6">{children}</main>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embed Sidebar
|
|
3
|
+
*
|
|
4
|
+
* A router-agnostic version of the Sidebar component that uses
|
|
5
|
+
* the EmbedNavigation context for navigation instead of TanStack Router.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Layers } from "lucide-react";
|
|
9
|
+
import { cn } from "../../lib/cn";
|
|
10
|
+
import { useAdminConfig } from "../../contexts";
|
|
11
|
+
import { Icon } from "../../lib/icons";
|
|
12
|
+
import { useEmbedNavigation, type EmbedRoute } from "../navigation";
|
|
13
|
+
import type { NavItem } from "../../lib/admin-config";
|
|
14
|
+
|
|
15
|
+
function pathToRoute(path: string): EmbedRoute {
|
|
16
|
+
if (path === "/" || path === "") return "dashboard";
|
|
17
|
+
if (path.startsWith("/content-types")) return "content-types";
|
|
18
|
+
if (path.startsWith("/entries")) return "entries";
|
|
19
|
+
if (path.startsWith("/content")) return "content";
|
|
20
|
+
if (path.startsWith("/media")) return "media";
|
|
21
|
+
if (path.startsWith("/taxonomies")) return "taxonomies";
|
|
22
|
+
if (path.startsWith("/settings")) return "settings";
|
|
23
|
+
if (path.startsWith("/trash")) return "trash";
|
|
24
|
+
return "dashboard";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function EmbedSidebar() {
|
|
28
|
+
const { currentPath, navigate } = useEmbedNavigation();
|
|
29
|
+
const config = useAdminConfig();
|
|
30
|
+
const { navItems, branding, layout } = config;
|
|
31
|
+
|
|
32
|
+
const isActive = (path: string, exact?: boolean) => {
|
|
33
|
+
const normalizedCurrent = currentPath.replace(/^\/admin/, "");
|
|
34
|
+
if (exact) {
|
|
35
|
+
return normalizedCurrent === path;
|
|
36
|
+
}
|
|
37
|
+
return normalizedCurrent.startsWith(path);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const handleNavClick = (item: NavItem) => {
|
|
41
|
+
const route = pathToRoute(item.path);
|
|
42
|
+
navigate(route);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const renderNavItem = (item: NavItem) => (
|
|
46
|
+
<button
|
|
47
|
+
key={item.id}
|
|
48
|
+
type="button"
|
|
49
|
+
onClick={() => handleNavClick(item)}
|
|
50
|
+
className={cn(
|
|
51
|
+
"flex w-full items-center gap-3 rounded-md px-2 py-2 text-left text-sm font-medium transition-colors",
|
|
52
|
+
isActive(item.path, item.exact)
|
|
53
|
+
? "bg-sidebar-accent text-sidebar-accent-foreground"
|
|
54
|
+
: "text-sidebar-foreground hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground"
|
|
55
|
+
)}
|
|
56
|
+
>
|
|
57
|
+
<Icon name={item.icon} className="size-5" />
|
|
58
|
+
<span className="flex-1">{item.label}</span>
|
|
59
|
+
{item.badge && (
|
|
60
|
+
<span className="rounded-full bg-sidebar-primary px-2 py-0.5 text-xs text-sidebar-primary-foreground">
|
|
61
|
+
{item.badge}
|
|
62
|
+
</span>
|
|
63
|
+
)}
|
|
64
|
+
</button>
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
const sidebarWidth = layout.sidebarWidth;
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<aside
|
|
71
|
+
className="fixed inset-y-0 left-0 z-50 flex flex-col border-r border-sidebar-border bg-sidebar"
|
|
72
|
+
style={{ width: sidebarWidth }}
|
|
73
|
+
>
|
|
74
|
+
<div className="flex h-14 items-center gap-2 border-b border-sidebar-border px-4">
|
|
75
|
+
<button
|
|
76
|
+
type="button"
|
|
77
|
+
onClick={() => navigate("dashboard")}
|
|
78
|
+
className="flex items-center gap-2 font-semibold text-sidebar-foreground"
|
|
79
|
+
>
|
|
80
|
+
{branding.logo ? (
|
|
81
|
+
<img src={branding.logo} alt={branding.appName} className="size-8" />
|
|
82
|
+
) : (
|
|
83
|
+
<div className="flex size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
|
|
84
|
+
<Layers className="size-4" />
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
87
|
+
<span className="text-base">{branding.appName}</span>
|
|
88
|
+
</button>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<nav className="flex-1 space-y-6 overflow-y-auto p-4">
|
|
92
|
+
{navItems.main.length > 0 && (
|
|
93
|
+
<div className="space-y-1">
|
|
94
|
+
<span className="px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60">
|
|
95
|
+
Main
|
|
96
|
+
</span>
|
|
97
|
+
<div className="space-y-1 pt-2">{navItems.main.map(renderNavItem)}</div>
|
|
98
|
+
</div>
|
|
99
|
+
)}
|
|
100
|
+
|
|
101
|
+
{navItems.config.length > 0 && (
|
|
102
|
+
<div className="space-y-1">
|
|
103
|
+
<span className="px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60">
|
|
104
|
+
Configuration
|
|
105
|
+
</span>
|
|
106
|
+
<div className="space-y-1 pt-2">{navItems.config.map(renderNavItem)}</div>
|
|
107
|
+
</div>
|
|
108
|
+
)}
|
|
109
|
+
</nav>
|
|
110
|
+
|
|
111
|
+
<div className="border-t border-sidebar-border p-4">
|
|
112
|
+
<div className="flex items-center justify-between text-xs text-sidebar-foreground/60">
|
|
113
|
+
<span>Version</span>
|
|
114
|
+
<span className="font-mono">0.1.0</span>
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
</aside>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embed API Context
|
|
3
|
+
*
|
|
4
|
+
* Provides the Convex API to embedded CMS admin pages.
|
|
5
|
+
* The API structure matches the namespaced exports from defineAdminAPI,
|
|
6
|
+
* allowing shared page components to work with the generated api object.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { createContext, useContext, type ReactNode } from "react";
|
|
10
|
+
import { api } from "../../../convex/_generated/api";
|
|
11
|
+
|
|
12
|
+
export type CmsAdminApi = typeof api["admin"];
|
|
13
|
+
|
|
14
|
+
const ApiContext = createContext<CmsAdminApi | null>(null);
|
|
15
|
+
|
|
16
|
+
export function ApiProvider({
|
|
17
|
+
api,
|
|
18
|
+
children,
|
|
19
|
+
}: {
|
|
20
|
+
api: CmsAdminApi;
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}) {
|
|
23
|
+
return <ApiContext.Provider value={api}>{children}</ApiContext.Provider>;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function useApi(): CmsAdminApi {
|
|
27
|
+
const api = useContext(ApiContext);
|
|
28
|
+
if (!api) {
|
|
29
|
+
throw new Error("useApi must be used within ApiProvider");
|
|
30
|
+
}
|
|
31
|
+
return api;
|
|
32
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Embeddable CMS Admin Component
|
|
3
|
+
*
|
|
4
|
+
* Use this component to embed the CMS admin UI into your existing React app.
|
|
5
|
+
* Provides a fully functional admin interface with router-agnostic navigation.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { CmsAdmin } from "@convex-cms/admin/embed";
|
|
10
|
+
*
|
|
11
|
+
* function App() {
|
|
12
|
+
* return (
|
|
13
|
+
* <CmsAdmin
|
|
14
|
+
* convexUrl="https://your-deployment.convex.cloud"
|
|
15
|
+
* auth={{
|
|
16
|
+
* getUser: () => currentUser,
|
|
17
|
+
* getUserRole: (userId) => userRoles[userId] ?? null,
|
|
18
|
+
* onLogout: () => signOut(),
|
|
19
|
+
* }}
|
|
20
|
+
* config={{
|
|
21
|
+
* branding: { appName: "My CMS" },
|
|
22
|
+
* navigation: { showTaxonomies: false },
|
|
23
|
+
* }}
|
|
24
|
+
* basePath="/admin"
|
|
25
|
+
* />
|
|
26
|
+
* );
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { ConvexProvider, ConvexReactClient } from "convex/react";
|
|
32
|
+
import { useMemo, type ReactNode } from "react";
|
|
33
|
+
import { AdminConfigProvider } from "../contexts/AdminConfigContext";
|
|
34
|
+
import {
|
|
35
|
+
AuthProvider,
|
|
36
|
+
type GetUserHook,
|
|
37
|
+
type GetUserRoleHook,
|
|
38
|
+
type LogoutHook,
|
|
39
|
+
} from "../contexts/AuthContext";
|
|
40
|
+
import { ThemeProvider } from "../contexts/ThemeContext";
|
|
41
|
+
import { RouteGuard } from "../components/RouteGuard";
|
|
42
|
+
import { resolveAdminConfig } from "../lib/admin-config";
|
|
43
|
+
import type { CmsAdminProps, CmsAdminAuthConfig } from "./types";
|
|
44
|
+
import { ApiProvider } from "./contexts/ApiContext";
|
|
45
|
+
import {
|
|
46
|
+
EmbedNavigationProvider,
|
|
47
|
+
useEmbedNavigation,
|
|
48
|
+
type EmbedRoute,
|
|
49
|
+
} from "./navigation";
|
|
50
|
+
import { EmbedLayout } from "./components/EmbedLayout";
|
|
51
|
+
import {
|
|
52
|
+
EmbedDashboard,
|
|
53
|
+
EmbedContent,
|
|
54
|
+
EmbedContentTypes,
|
|
55
|
+
EmbedMedia,
|
|
56
|
+
EmbedSettings,
|
|
57
|
+
EmbedTrash,
|
|
58
|
+
EmbedTaxonomies,
|
|
59
|
+
} from "./pages";
|
|
60
|
+
|
|
61
|
+
function adaptAuthConfig(auth: CmsAdminAuthConfig): {
|
|
62
|
+
getUser: GetUserHook;
|
|
63
|
+
getUserRole: GetUserRoleHook;
|
|
64
|
+
onLogout: LogoutHook;
|
|
65
|
+
} {
|
|
66
|
+
return {
|
|
67
|
+
getUser: auth.getUser,
|
|
68
|
+
getUserRole: ({ userId }) => auth.getUserRole(userId),
|
|
69
|
+
onLogout: auth.onLogout ?? (() => {}),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function ConvexProviderWrapper({
|
|
74
|
+
convexUrl,
|
|
75
|
+
children,
|
|
76
|
+
}: {
|
|
77
|
+
convexUrl: string;
|
|
78
|
+
children: ReactNode;
|
|
79
|
+
}) {
|
|
80
|
+
const convex = useMemo(() => {
|
|
81
|
+
if (!convexUrl) return null;
|
|
82
|
+
return new ConvexReactClient(convexUrl);
|
|
83
|
+
}, [convexUrl]);
|
|
84
|
+
|
|
85
|
+
if (!convex) {
|
|
86
|
+
return (
|
|
87
|
+
<div className="flex min-h-full items-center justify-center bg-background p-6">
|
|
88
|
+
<div className="max-w-lg space-y-4 rounded-lg border border-amber-200 bg-amber-50 p-6 text-center">
|
|
89
|
+
<h2 className="text-xl font-semibold text-amber-900">
|
|
90
|
+
Convex Configuration Required
|
|
91
|
+
</h2>
|
|
92
|
+
<p className="text-sm text-amber-800">
|
|
93
|
+
Please provide a valid convexUrl prop to the CmsAdmin component.
|
|
94
|
+
</p>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return <ConvexProvider client={convex}>{children}</ConvexProvider>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function EmbedRouter() {
|
|
104
|
+
const { currentRoute } = useEmbedNavigation();
|
|
105
|
+
|
|
106
|
+
const renderPage = () => {
|
|
107
|
+
switch (currentRoute.route) {
|
|
108
|
+
case "dashboard":
|
|
109
|
+
return <EmbedDashboard />;
|
|
110
|
+
case "content":
|
|
111
|
+
return <EmbedContent />;
|
|
112
|
+
case "content-types":
|
|
113
|
+
return <EmbedContentTypes />;
|
|
114
|
+
case "media":
|
|
115
|
+
return <EmbedMedia />;
|
|
116
|
+
case "settings":
|
|
117
|
+
return <EmbedSettings />;
|
|
118
|
+
case "taxonomies":
|
|
119
|
+
return <EmbedTaxonomies />;
|
|
120
|
+
case "trash":
|
|
121
|
+
return <EmbedTrash />;
|
|
122
|
+
case "entries":
|
|
123
|
+
return <EmbedContent />;
|
|
124
|
+
default:
|
|
125
|
+
return <EmbedDashboard />;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
return <EmbedLayout>{renderPage()}</EmbedLayout>;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function CmsAdmin({
|
|
133
|
+
api,
|
|
134
|
+
convexUrl,
|
|
135
|
+
config,
|
|
136
|
+
auth,
|
|
137
|
+
basePath = "/admin",
|
|
138
|
+
className,
|
|
139
|
+
initialRoute = "dashboard",
|
|
140
|
+
onNavigate,
|
|
141
|
+
}: CmsAdminProps & {
|
|
142
|
+
initialRoute?: EmbedRoute;
|
|
143
|
+
onNavigate?: (path: string, params: Record<string, string>) => void;
|
|
144
|
+
}) {
|
|
145
|
+
const adminConfig = useMemo(() => resolveAdminConfig(config), [config]);
|
|
146
|
+
const authConfig = useMemo(() => adaptAuthConfig(auth), [auth]);
|
|
147
|
+
|
|
148
|
+
return (
|
|
149
|
+
<div className={className}>
|
|
150
|
+
<ApiProvider api={api}>
|
|
151
|
+
<ThemeProvider>
|
|
152
|
+
<AdminConfigProvider config={adminConfig}>
|
|
153
|
+
<ConvexProviderWrapper convexUrl={convexUrl}>
|
|
154
|
+
<AuthProvider
|
|
155
|
+
getUser={authConfig.getUser}
|
|
156
|
+
getUserRole={authConfig.getUserRole}
|
|
157
|
+
onLogout={authConfig.onLogout}
|
|
158
|
+
>
|
|
159
|
+
<EmbedNavigationProvider
|
|
160
|
+
initialRoute={initialRoute}
|
|
161
|
+
basePath={basePath}
|
|
162
|
+
onNavigate={onNavigate}
|
|
163
|
+
>
|
|
164
|
+
<RouteGuard>
|
|
165
|
+
<div className="min-h-screen">
|
|
166
|
+
<EmbedRouter />
|
|
167
|
+
</div>
|
|
168
|
+
</RouteGuard>
|
|
169
|
+
</EmbedNavigationProvider>
|
|
170
|
+
</AuthProvider>
|
|
171
|
+
</ConvexProviderWrapper>
|
|
172
|
+
</AdminConfigProvider>
|
|
173
|
+
</ThemeProvider>
|
|
174
|
+
</ApiProvider>
|
|
175
|
+
</div>
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export type { CmsAdminProps, CmsAdminAuthConfig, CmsAdminUser } from "./types";
|
|
180
|
+
export type { CmsAdminApi } from "./contexts/ApiContext";
|
|
181
|
+
export type { AdminConfig, NavItem } from "../lib/admin-config";
|
|
182
|
+
export { resolveAdminConfig, defineAdminConfig } from "../lib/admin-config";
|
|
183
|
+
export type { EmbedRoute, EmbedRouteState } from "./navigation";
|
|
184
|
+
export { useEmbedNavigation, useEmbedParams, useEmbedRoute } from "./navigation";
|