convex-cms 0.0.3 → 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 +107 -60
- 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-CRswfTzk.js → CmsEmptyState-CkqBIab3.js} +2 -2
- package/admin-dist/public/assets/{CmsPageHeader-CirpXndm.js → CmsPageHeader-CUtl5MMG.js} +1 -1
- package/admin-dist/public/assets/{CmsStatusBadge-CbEUpQu-.js → CmsStatusBadge-CUYFgEe-.js} +1 -1
- package/admin-dist/public/assets/CmsSurface-CsJfAVa3.js +1 -0
- package/admin-dist/public/assets/{CmsToolbar-BI2nZOXp.js → CmsToolbar-CnfbcxeP.js} +1 -1
- package/admin-dist/public/assets/{ContentEntryEditor-CBeCyK_m.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-CKU_glsK.js → _entryId-CuVMExbb.js} +1 -1
- package/admin-dist/public/assets/alert-CF1BSzGR.js +1 -0
- package/admin-dist/public/assets/{badge-hvUOzpVZ.js → badge-CmuOIVKp.js} +1 -1
- package/admin-dist/public/assets/{circle-check-big-CF_pR17r.js → circle-check-big-BKDVG6DU.js} +1 -1
- package/admin-dist/public/assets/{command-DU82cJlt.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-KNtpL71g.js → label-CHCnXeBk.js} +1 -1
- package/admin-dist/public/assets/{link-2-Bw2aI4V4.js → link-2-Bb34judH.js} +1 -1
- package/admin-dist/public/assets/{list-sYepHjt_.js → list-9Pzt48ld.js} +1 -1
- package/admin-dist/public/assets/{main-CKj5yfEi.js → main-CjQ2VI9L.js} +3 -3
- package/admin-dist/public/assets/media-Dc5PWt2Q.js +1 -0
- package/admin-dist/public/assets/{new._contentTypeId-C3LstjNs.js → new._contentTypeId-C_I4YxIa.js} +1 -1
- package/admin-dist/public/assets/{plus-DUn8v_Xf.js → plus-Ceef7DHk.js} +1 -1
- package/admin-dist/public/assets/{rotate-ccw-DJEoHcRI.js → rotate-ccw-7k7-4VUq.js} +1 -1
- package/admin-dist/public/assets/scroll-area-CC6wujnp.js +1 -0
- package/admin-dist/public/assets/{search-MuAUDJKR.js → search-DwoUV2pv.js} +1 -1
- package/admin-dist/public/assets/select-hOZTp8aC.js +1 -0
- package/admin-dist/public/assets/settings-t2PbCZh4.js +1 -0
- package/admin-dist/public/assets/switch-jX2pDaNU.js +1 -0
- package/admin-dist/public/assets/tabs-q4EbZk7c.js +1 -0
- package/admin-dist/public/assets/tanstack-adapter-B-Glm4kH.js +1 -0
- package/admin-dist/public/assets/taxonomies-kyk5P4ZW.js +1 -0
- package/admin-dist/public/assets/{textarea-BTy7nwzR.js → textarea-B6SfBmr0.js} +1 -1
- package/admin-dist/public/assets/trash-BOCnIznD.js +1 -0
- package/admin-dist/public/assets/{triangle-alert-E52Vfeuh.js → triangle-alert-CXFIO_Gu.js} +1 -1
- package/admin-dist/public/assets/useBreadcrumbLabel-_6qBagc3.js +1 -0
- package/admin-dist/public/assets/{usePermissions-Basjs9BT.js → usePermissions-M1ijZ7a6.js} +1 -1
- package/admin-dist/server/_chunks/_libs/@tanstack/react-router.mjs +7 -0
- package/admin-dist/server/_ssr/{badge-6BsP37vG.mjs → CmsButton-DOiTVKQq.mjs} +33 -33
- package/admin-dist/server/_ssr/{CmsEmptyState-DU7-7-mV.mjs → CmsEmptyState-fbnGt3LD.mjs} +2 -2
- package/admin-dist/server/_ssr/{CmsPageHeader-CseW0AHm.mjs → CmsPageHeader-DHRrdOZa.mjs} +1 -1
- package/admin-dist/server/_ssr/{CmsStatusBadge-B_pi4KCp.mjs → CmsStatusBadge-s7obWbKZ.mjs} +2 -2
- package/admin-dist/server/_ssr/CmsSurface-rFoYjb62.mjs +44 -0
- package/admin-dist/server/_ssr/{CmsToolbar-X75ex6ek.mjs → CmsToolbar-zTE45z2q.mjs} +2 -2
- package/admin-dist/server/_ssr/{ContentEntryEditor-CepusRsA.mjs → ContentEntryEditor-BLoEjT_m.mjs} +12 -12
- package/admin-dist/server/_ssr/{TaxonomyFilter-Bwrq0-cz.mjs → TaxonomyFilter-XAtaJC2z.mjs} +5 -5
- package/admin-dist/server/_ssr/{_contentTypeId-BqYKEcLr.mjs → _contentTypeId-Csl4822C.mjs} +13 -13
- package/admin-dist/server/_ssr/{_entryId-CRfnqeDf.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-fy8epIKf.mjs → command-C0Di14--.mjs} +1 -1
- package/admin-dist/server/_ssr/{content-B5RhL7uW.mjs → content-CT-FPsmV.mjs} +170 -98
- package/admin-dist/server/_ssr/{content-types-BIOqCQYN.mjs → content-types-C8cBFdzE.mjs} +260 -115
- package/admin-dist/server/_ssr/{index-DHSHDPt1.mjs → index-BJtcrEc-.mjs} +88 -17
- package/admin-dist/server/_ssr/index.mjs +2 -2
- package/admin-dist/server/_ssr/{label-C8Dko1j7.mjs → label-qn2Afwl4.mjs} +1 -1
- package/admin-dist/server/_ssr/{media-CSx3XttC.mjs → media-qv5IAsMZ.mjs} +43 -43
- package/admin-dist/server/_ssr/{new._contentTypeId-DzanEZQM.mjs → new._contentTypeId-DdGyrhqs.mjs} +13 -13
- package/admin-dist/server/_ssr/{router-DDWcF-kt.mjs → router-nSVkxb6Y.mjs} +11 -11
- package/admin-dist/server/_ssr/{scroll-area-bjPYwhXN.mjs → scroll-area-BCinP455.mjs} +1 -1
- package/admin-dist/server/_ssr/{select-BUhDDf4T.mjs → select-BKQlQScw.mjs} +1 -1
- package/admin-dist/server/_ssr/{settings-DAsxnw2q.mjs → settings-BCr2KQlk.mjs} +236 -139
- package/admin-dist/server/_ssr/{switch-BgyRtQ1Z.mjs → switch-BaOi42fE.mjs} +1 -1
- package/admin-dist/server/_ssr/{tabs-DzMdRB1A.mjs → tabs-DYXEi9kq.mjs} +5 -3
- package/admin-dist/server/_ssr/tanstack-adapter-Bsz8kha-.mjs +119 -0
- package/admin-dist/server/_ssr/{taxonomies-C8j8g5Q5.mjs → taxonomies-CueMHTbE.mjs} +184 -73
- package/admin-dist/server/_ssr/{textarea-9jNeYJSc.mjs → textarea-CI0Jqx2x.mjs} +1 -1
- package/admin-dist/server/_ssr/{trash-DYMxwhZB.mjs → trash-DE6W8GoX.mjs} +211 -88
- package/admin-dist/server/_ssr/{useBreadcrumbLabel-FNSAr2Ha.mjs → useBreadcrumbLabel-B5Yi72lM.mjs} +1 -1
- package/admin-dist/server/_ssr/{usePermissions-BJGGahrJ.mjs → usePermissions-C3nZ-Izm.mjs} +1 -1
- package/admin-dist/server/index.mjs +189 -182
- package/dist/cli/commands/init.d.ts +6 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +156 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/index.js +6 -0
- package/dist/cli/index.js.map +1 -1
- 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/admin/contentTypes.d.ts +1204 -0
- 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} +4 -4
- package/dist/client/adminConfig.d.ts.map +1 -0
- package/dist/client/{admin-config.js → adminConfig.js} +3 -3
- package/dist/client/adminConfig.js.map +1 -0
- package/dist/client/agentTools.d.ts +11 -21
- package/dist/client/agentTools.d.ts.map +1 -1
- package/dist/client/agentTools.js +4 -4
- package/dist/client/index.d.ts +6 -6
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +19 -6
- package/dist/client/index.js.map +1 -1
- package/dist/client/schema/codegen.d.ts +2 -2
- package/dist/client/schema/codegen.d.ts.map +1 -1
- package/dist/client/schema/codegen.js +3 -3
- package/dist/client/schema/codegen.js.map +1 -1
- package/dist/client/schema/defineContentType.d.ts +3 -3
- package/dist/client/schema/defineContentType.js +3 -3
- package/dist/client/schema/index.d.ts +7 -7
- package/dist/client/schema/index.d.ts.map +1 -1
- package/dist/client/schema/index.js +5 -5
- package/dist/client/schema/index.js.map +1 -1
- package/dist/client/schema/schemaDrift.d.ts +1 -1
- package/dist/client/schema/schemaDrift.js +1 -1
- package/dist/client/schema/typedClient.d.ts +2 -2
- package/dist/client/schema/typedClient.js +2 -2
- package/dist/client/schema/types.d.ts +1 -1
- package/dist/client/schema/types.js +1 -1
- package/dist/client/wrapper.d.ts +108 -65
- package/dist/client/wrapper.d.ts.map +1 -1
- package/dist/client/wrapper.js +22 -22
- package/dist/client/wrapper.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/convex.config.d.ts +2 -2
- package/dist/component/convex.config.js +2 -2
- package/dist/component/index.d.ts +1 -1
- package/dist/component/index.js +1 -1
- package/dist/component/lib/ragContentChunker.d.ts +1 -1
- package/dist/component/lib/ragContentChunker.js +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/roles.d.ts +1 -1
- package/dist/component/roles.js +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/dist/react/index.d.ts +2 -2
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +13 -7
- package/dist/react/index.js.map +1 -1
- package/dist/test.d.ts +2 -2
- package/dist/test.js +2 -2
- package/package.json +115 -13
- package/admin-dist/public/assets/ErrorState-BIVaWmom.js +0 -1
- package/admin-dist/public/assets/TaxonomyFilter-ChaY6Y_x.js +0 -1
- package/admin-dist/public/assets/_contentTypeId-DQ8k_Rvw.js +0 -1
- package/admin-dist/public/assets/alert-BXjTqrwQ.js +0 -1
- package/admin-dist/public/assets/content-_LXl3pp7.js +0 -1
- package/admin-dist/public/assets/content-types-KjxaXGxY.js +0 -2
- package/admin-dist/public/assets/globals-CS6BZ0zp.css +0 -1
- package/admin-dist/public/assets/index-DNGIZHL-.js +0 -1
- package/admin-dist/public/assets/media-Bkrkffm7.js +0 -1
- package/admin-dist/public/assets/scroll-area-DfIlT0in.js +0 -1
- package/admin-dist/public/assets/select-BD29IXCI.js +0 -1
- package/admin-dist/public/assets/settings-DmMyn_6A.js +0 -1
- package/admin-dist/public/assets/switch-h3Rrnl5i.js +0 -1
- package/admin-dist/public/assets/tabs-imc8h-Dp.js +0 -1
- package/admin-dist/public/assets/taxonomies-dAsrT65H.js +0 -1
- package/admin-dist/public/assets/trash-SAWKZZHv.js +0 -1
- package/admin-dist/public/assets/useBreadcrumbLabel-BECBMCzM.js +0 -1
- package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +0 -89
- package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BwDlABVk.mjs +0 -4
- package/admin-dist/server/_ssr/alert-CVt45UUP.mjs +0 -92
- 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 +0 -2273
- package/dist/client/adminApi.d.ts.map +0 -1
- package/dist/client/adminApi.js +0 -716
- package/dist/client/adminApi.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,26 +1,84 @@
|
|
|
1
1
|
# Convex CMS
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A headless CMS built as a [Convex Component](https://docs.convex.dev/components) — content management that runs inside your Convex app.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Choose Your Path
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
- **Flexible content modeling** - Define content types with 13 field types
|
|
9
|
-
- **Publishing workflows** - Draft, publish, schedule, and version content
|
|
10
|
-
- **Media management** - Upload, organize, and serve media with variants
|
|
11
|
-
- **Role-based access control** - Fine-grained permissions with custom roles
|
|
12
|
-
- **Multi-locale support** - Content localization with fallback chains
|
|
13
|
-
- **Admin UI** - Ready-to-use React admin interface
|
|
7
|
+
### Need an Admin Interface?
|
|
14
8
|
|
|
15
|
-
|
|
9
|
+
Use **`defineAdminAPI`** — one line creates all the backend functions for a working admin UI.
|
|
16
10
|
|
|
17
|
-
```bash
|
|
18
|
-
npm install convex-cms
|
|
19
11
|
```
|
|
12
|
+
Your App Admin UI
|
|
13
|
+
│ │
|
|
14
|
+
└── convex/admin.ts ────────────┘
|
|
15
|
+
defineAdminAPI()
|
|
16
|
+
│
|
|
17
|
+
├── listContentTypes
|
|
18
|
+
├── getEntry
|
|
19
|
+
├── publishEntry
|
|
20
|
+
└── ... (30+ functions)
|
|
21
|
+
│
|
|
22
|
+
▼
|
|
23
|
+
CMS Component
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
→ **[Admin UI Setup Guide](./docs/guides/admin-ui-setup.md)**
|
|
27
|
+
|
|
28
|
+
### Building Custom Content Logic?
|
|
29
|
+
|
|
30
|
+
Use **`createCmsClient`** — full programmatic control with typed methods in your Convex functions.
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
Your Convex Functions
|
|
34
|
+
│
|
|
35
|
+
└── cms.contentEntries.list(ctx, { status: "published" })
|
|
36
|
+
cms.contentTypes.create(ctx, { name: "blog", ... })
|
|
37
|
+
cms.mediaAssets.upload(ctx, { ... })
|
|
38
|
+
│
|
|
39
|
+
▼
|
|
40
|
+
CMS Component
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
→ **[Getting Started Guide](./docs/guides/getting-started.md)**
|
|
44
|
+
|
|
45
|
+
### Want Full Type Safety?
|
|
46
|
+
|
|
47
|
+
Use **code-first schemas** — define content types with Convex validators, get TypeScript inference.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
const blogPost = defineContentType({
|
|
51
|
+
name: "blog_post",
|
|
52
|
+
validator: v.object({
|
|
53
|
+
title: v.string(),
|
|
54
|
+
content: v.string(),
|
|
55
|
+
}),
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// TypeScript knows entry.data.title is string
|
|
59
|
+
const entry = await cms.typedContentEntries.get<"blog_post">(ctx, id);
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
→ **[Code-First Schema Reference](./docs/api/code-first-schema.md)**
|
|
63
|
+
|
|
64
|
+
### Need Both?
|
|
65
|
+
|
|
66
|
+
**Most apps use both.** This is the typical setup:
|
|
67
|
+
|
|
68
|
+
- `defineAdminAPI` powers the admin interface for content editors
|
|
69
|
+
- `createCmsClient` gives you typed methods for custom queries on your frontend
|
|
70
|
+
|
|
71
|
+
They work together through the same CMS component.
|
|
20
72
|
|
|
21
73
|
## Quick Start
|
|
22
74
|
|
|
23
|
-
### 1.
|
|
75
|
+
### 1. Install
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm install convex-cms
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2. Add the Component
|
|
24
82
|
|
|
25
83
|
```typescript
|
|
26
84
|
// convex/convex.config.ts
|
|
@@ -32,62 +90,51 @@ app.use(convexCms);
|
|
|
32
90
|
export default app;
|
|
33
91
|
```
|
|
34
92
|
|
|
35
|
-
###
|
|
93
|
+
### 3. Choose Your Setup
|
|
36
94
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
import { createCmsClient } from "convex-cms";
|
|
40
|
-
import { components } from "./_generated/api";
|
|
95
|
+
**For Admin UI:** Run `npx convex-cms init` then `npx convex-cms admin`
|
|
96
|
+
→ [Full Admin UI Setup](./docs/guides/admin-ui-setup.md)
|
|
41
97
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
permissiveMode: true, // For development only
|
|
45
|
-
});
|
|
46
|
-
```
|
|
98
|
+
**For Custom Functions:** Create a CMS client and use it in your functions
|
|
99
|
+
→ [Full Getting Started Guide](./docs/guides/getting-started.md)
|
|
47
100
|
|
|
48
|
-
|
|
101
|
+
## What's Included
|
|
49
102
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
handler: async (ctx) => {
|
|
58
|
-
const result = await cms.contentEntries.list(ctx, {
|
|
59
|
-
status: "published",
|
|
60
|
-
});
|
|
61
|
-
return result.items;
|
|
62
|
-
},
|
|
63
|
-
});
|
|
103
|
+
- **13 field types** — text, richText, media, reference, select, and more
|
|
104
|
+
- **Publishing workflows** — draft → scheduled → published with version history
|
|
105
|
+
- **Media management** — uploads, folders, variants, and metadata
|
|
106
|
+
- **RBAC** — 4 built-in roles + custom roles with fine-grained permissions
|
|
107
|
+
- **Multi-locale** — content localization with fallback chains
|
|
108
|
+
- **Admin UI** — pre-built React interface (CLI or embeddable)
|
|
109
|
+
- **Agent tools** — Zod schemas for AI integration
|
|
64
110
|
|
|
65
|
-
|
|
66
|
-
args: { title: v.string(), content: v.string() },
|
|
67
|
-
handler: async (ctx, args) => {
|
|
68
|
-
return await cms.contentEntries.create(ctx, {
|
|
69
|
-
contentTypeId: blogTypeId,
|
|
70
|
-
data: args,
|
|
71
|
-
});
|
|
72
|
-
},
|
|
73
|
-
});
|
|
74
|
-
```
|
|
111
|
+
## Admin UI Modes
|
|
75
112
|
|
|
76
|
-
|
|
113
|
+
| Mode | Command | Best For |
|
|
114
|
+
|------|---------|----------|
|
|
115
|
+
| **CLI** | `npx convex-cms admin` | Development |
|
|
116
|
+
| **Embed** | `<CmsAdmin api={api.admin} />` | Production |
|
|
77
117
|
|
|
78
|
-
|
|
79
|
-
- [Content Modeling](./docs/guides/content-modeling.md)
|
|
80
|
-
- [Media Management](./docs/guides/media.md)
|
|
81
|
-
- [Authorization](./docs/guides/authorization.md)
|
|
82
|
-
- [API Reference](./docs/api/client-api.md)
|
|
118
|
+
Both modes call the same functions from your `convex/admin.ts`.
|
|
83
119
|
|
|
84
|
-
##
|
|
85
|
-
|
|
86
|
-
Convex CMS includes a ready-to-use admin interface:
|
|
120
|
+
## Documentation
|
|
87
121
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
122
|
+
| Guide | Description |
|
|
123
|
+
|-------|-------------|
|
|
124
|
+
| [Admin UI Setup](./docs/guides/admin-ui-setup.md) | CLI and embed modes, auth integration |
|
|
125
|
+
| [Getting Started](./docs/guides/getting-started.md) | Programmatic usage with createCmsClient |
|
|
126
|
+
| [Integration Patterns](./docs/guides/integration-patterns.md) | Common setups and when to use each |
|
|
127
|
+
| [Content Modeling](./docs/guides/content-modeling.md) | Content types and field definitions |
|
|
128
|
+
| [Authorization](./docs/guides/authorization.md) | Roles, permissions, and custom auth |
|
|
129
|
+
| [Media Management](./docs/guides/media.md) | Uploads, folders, and variants |
|
|
130
|
+
|
|
131
|
+
| Reference | Description |
|
|
132
|
+
|-----------|-------------|
|
|
133
|
+
| [Client API](./docs/api/client-api.md) | createCmsClient methods |
|
|
134
|
+
| [Admin API](./docs/api/admin-api.md) | defineAdminAPI functions |
|
|
135
|
+
| [Code-First Schema](./docs/api/code-first-schema.md) | TypeScript-first content types |
|
|
136
|
+
| [Field Types](./docs/api/field-types.md) | All 13 field types |
|
|
137
|
+
| [Configuration](./docs/api/configuration.md) | All config options |
|
|
91
138
|
|
|
92
139
|
## Requirements
|
|
93
140
|
|
package/admin/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Convex CMS Admin UI
|
|
2
|
+
|
|
3
|
+
The Admin UI for Convex CMS, built with TanStack Start and React.
|
|
4
|
+
|
|
5
|
+
## Getting Started
|
|
6
|
+
|
|
7
|
+
### Prerequisites
|
|
8
|
+
|
|
9
|
+
- Node.js 18+
|
|
10
|
+
- A Convex account and project
|
|
11
|
+
|
|
12
|
+
### Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
cd admin
|
|
16
|
+
npm install
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### Development
|
|
20
|
+
|
|
21
|
+
1. Start the Convex development server:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx convex dev
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This will generate the Convex API types and provide a `VITE_CONVEX_URL`.
|
|
28
|
+
|
|
29
|
+
2. Create a `.env` file with your Convex URL:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cp .env.example .env
|
|
33
|
+
# Edit .env with your VITE_CONVEX_URL from step 1
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
3. Start the development server:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm run dev:vite
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or run both Convex and Vite together:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm run dev
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
4. Open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
49
|
+
|
|
50
|
+
## Project Structure
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
admin/
|
|
54
|
+
├── convex/ # Convex configuration
|
|
55
|
+
│ └── convex.config.ts # CMS component integration
|
|
56
|
+
├── src/
|
|
57
|
+
│ ├── routes/ # TanStack Router routes
|
|
58
|
+
│ │ ├── __root.tsx # Root layout with Convex provider
|
|
59
|
+
│ │ ├── index.tsx # Dashboard page
|
|
60
|
+
│ │ ├── content.tsx # Content entries list
|
|
61
|
+
│ │ ├── media.tsx # Media library
|
|
62
|
+
│ │ ├── content-types.tsx # Content type management
|
|
63
|
+
│ │ └── settings.tsx # CMS settings
|
|
64
|
+
│ ├── components/ # Reusable React components
|
|
65
|
+
│ ├── lib/ # Utilities and helpers
|
|
66
|
+
│ │ └── convex.ts # Convex client utilities
|
|
67
|
+
│ ├── styles/ # CSS styles
|
|
68
|
+
│ │ └── app.css # Main stylesheet
|
|
69
|
+
│ └── router.tsx # Router configuration
|
|
70
|
+
├── public/ # Static assets
|
|
71
|
+
├── vite.config.ts # Vite + TanStack Start configuration
|
|
72
|
+
├── tsconfig.json # TypeScript configuration
|
|
73
|
+
└── package.json # Dependencies and scripts
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Available Scripts
|
|
77
|
+
|
|
78
|
+
- `npm run dev` - Start Convex and Vite development servers
|
|
79
|
+
- `npm run dev:vite` - Start only the Vite development server
|
|
80
|
+
- `npm run dev:convex` - Start only the Convex development server
|
|
81
|
+
- `npm run build` - Build for production
|
|
82
|
+
- `npm run preview` - Preview production build
|
|
83
|
+
- `npm run typecheck` - Run TypeScript type checking
|
|
84
|
+
|
|
85
|
+
## Features
|
|
86
|
+
|
|
87
|
+
- **Dashboard** - Overview of CMS status and quick navigation
|
|
88
|
+
- **Content Management** - Browse and manage content entries
|
|
89
|
+
- **Media Library** - Upload and organize media assets
|
|
90
|
+
- **Content Types** - Define content schemas with custom fields
|
|
91
|
+
- **Settings** - Configure CMS features and preferences
|
|
92
|
+
|
|
93
|
+
## Technology Stack
|
|
94
|
+
|
|
95
|
+
- [TanStack Start](https://tanstack.com/start) - Full-stack React framework
|
|
96
|
+
- [TanStack Router](https://tanstack.com/router) - Type-safe routing
|
|
97
|
+
- [Convex](https://convex.dev) - Real-time backend platform
|
|
98
|
+
- [React 19](https://react.dev) - UI library
|
|
99
|
+
- [Vite 7](https://vitejs.dev) - Build tool
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import { Sidebar } from "./Sidebar";
|
|
3
|
+
import { Header } from "./Header";
|
|
4
|
+
import { useAdminConfig } from "~/contexts";
|
|
5
|
+
|
|
6
|
+
interface AdminLayoutProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function AdminLayout({ children }: AdminLayoutProps) {
|
|
11
|
+
const { layout } = useAdminConfig();
|
|
12
|
+
|
|
13
|
+
return (
|
|
14
|
+
<div className="flex min-h-screen bg-background">
|
|
15
|
+
<Sidebar />
|
|
16
|
+
<div className="flex flex-1 flex-col" style={{ marginLeft: layout.sidebarWidth }}>
|
|
17
|
+
<Header />
|
|
18
|
+
<main className="flex-1 overflow-auto p-6">{children}</main>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AlertTriangle } from 'lucide-react'
|
|
2
|
+
import { CmsDialog } from '~/components/cmsds/CmsDialog'
|
|
3
|
+
import { CmsButton } from '~/components/cmsds/CmsButton'
|
|
4
|
+
|
|
5
|
+
interface BreakingChangesWarningDialogProps {
|
|
6
|
+
isOpen: boolean
|
|
7
|
+
onClose: () => void
|
|
8
|
+
breakingChanges: string[]
|
|
9
|
+
onForceUpdate: () => void
|
|
10
|
+
onCancel: () => void
|
|
11
|
+
isLoading: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function BreakingChangesWarningDialog({
|
|
15
|
+
isOpen,
|
|
16
|
+
onClose,
|
|
17
|
+
breakingChanges,
|
|
18
|
+
onForceUpdate,
|
|
19
|
+
onCancel,
|
|
20
|
+
isLoading,
|
|
21
|
+
}: BreakingChangesWarningDialogProps) {
|
|
22
|
+
const handleCancel = () => {
|
|
23
|
+
onCancel()
|
|
24
|
+
onClose()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<CmsDialog
|
|
29
|
+
open={isOpen}
|
|
30
|
+
onOpenChange={(open) => !open && !isLoading && handleCancel()}
|
|
31
|
+
title="Breaking Changes Detected"
|
|
32
|
+
size="lg"
|
|
33
|
+
footer={
|
|
34
|
+
<>
|
|
35
|
+
<CmsButton variant="outline" onClick={handleCancel} disabled={isLoading}>
|
|
36
|
+
Cancel
|
|
37
|
+
</CmsButton>
|
|
38
|
+
<CmsButton variant="danger" onClick={onForceUpdate} loading={isLoading}>
|
|
39
|
+
Force Update
|
|
40
|
+
</CmsButton>
|
|
41
|
+
</>
|
|
42
|
+
}
|
|
43
|
+
>
|
|
44
|
+
<div className="space-y-4">
|
|
45
|
+
<div className="flex items-start gap-3 rounded-lg border border-amber-200 bg-amber-50 p-3">
|
|
46
|
+
<AlertTriangle className="mt-0.5 size-5 shrink-0 text-amber-600" />
|
|
47
|
+
<div className="space-y-1">
|
|
48
|
+
<p className="text-sm font-medium text-amber-800">
|
|
49
|
+
These changes may affect existing content
|
|
50
|
+
</p>
|
|
51
|
+
<p className="text-sm text-amber-700">
|
|
52
|
+
The following changes could cause data loss or validation errors for existing entries.
|
|
53
|
+
Review carefully before proceeding.
|
|
54
|
+
</p>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<div className="space-y-2">
|
|
59
|
+
<p className="text-sm font-medium text-foreground">
|
|
60
|
+
{breakingChanges.length} breaking change{breakingChanges.length !== 1 ? 's' : ''} detected:
|
|
61
|
+
</p>
|
|
62
|
+
<ul className="space-y-2">
|
|
63
|
+
{breakingChanges.map((change, index) => (
|
|
64
|
+
<li
|
|
65
|
+
key={index}
|
|
66
|
+
className="flex items-start gap-2 rounded-md border bg-muted/30 px-3 py-2 text-sm"
|
|
67
|
+
>
|
|
68
|
+
<span className="mt-0.5 size-1.5 shrink-0 rounded-full bg-amber-500" />
|
|
69
|
+
<span className="text-muted-foreground">{change}</span>
|
|
70
|
+
</li>
|
|
71
|
+
))}
|
|
72
|
+
</ul>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<p className="text-xs text-muted-foreground">
|
|
76
|
+
Click "Force Update" to apply these changes anyway, or "Cancel" to go back and modify your changes.
|
|
77
|
+
</p>
|
|
78
|
+
</div>
|
|
79
|
+
</CmsDialog>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react'
|
|
2
|
+
import { useMutation } from 'convex/react'
|
|
3
|
+
import { api } from '../../convex/_generated/api'
|
|
4
|
+
import { BulkOperationModal } from './BulkOperationModal'
|
|
5
|
+
import { CmsButton } from '~/components/cmsds/CmsButton'
|
|
6
|
+
import { Badge } from '~/components/ui/badge'
|
|
7
|
+
import { X } from 'lucide-react'
|
|
8
|
+
|
|
9
|
+
type BulkAction = 'publish' | 'unpublish' | 'delete' | 'archive'
|
|
10
|
+
|
|
11
|
+
interface BulkActionBarProps {
|
|
12
|
+
selectedIds: string[]
|
|
13
|
+
onClearSelection: () => void
|
|
14
|
+
onOperationComplete?: () => void
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function BulkActionBar({
|
|
18
|
+
selectedIds,
|
|
19
|
+
onClearSelection,
|
|
20
|
+
onOperationComplete,
|
|
21
|
+
}: BulkActionBarProps) {
|
|
22
|
+
const [activeAction, setActiveAction] = useState<BulkAction | null>(null)
|
|
23
|
+
const [isProcessing, setIsProcessing] = useState(false)
|
|
24
|
+
const [result, setResult] = useState<{
|
|
25
|
+
succeeded: number
|
|
26
|
+
failed: number
|
|
27
|
+
errors?: string[]
|
|
28
|
+
} | null>(null)
|
|
29
|
+
|
|
30
|
+
const bulkPublish = useMutation(api.bulkOperations.bulkPublish)
|
|
31
|
+
const bulkUnpublish = useMutation(api.bulkOperations.bulkUnpublish)
|
|
32
|
+
const bulkDelete = useMutation(api.bulkOperations.bulkDelete)
|
|
33
|
+
const bulkUpdate = useMutation(api.bulkOperations.bulkUpdate)
|
|
34
|
+
|
|
35
|
+
const handleAction = useCallback((action: BulkAction) => {
|
|
36
|
+
setActiveAction(action)
|
|
37
|
+
setResult(null)
|
|
38
|
+
}, [])
|
|
39
|
+
|
|
40
|
+
const handleConfirm = useCallback(async () => {
|
|
41
|
+
if (!activeAction || selectedIds.length === 0) return
|
|
42
|
+
|
|
43
|
+
setIsProcessing(true)
|
|
44
|
+
setResult(null)
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
let response: {
|
|
48
|
+
succeeded: number
|
|
49
|
+
failed: number
|
|
50
|
+
errors?: { id: string; error: string }[]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
switch (activeAction) {
|
|
54
|
+
case 'publish':
|
|
55
|
+
response = await bulkPublish({
|
|
56
|
+
ids: selectedIds,
|
|
57
|
+
changeDescription: 'Bulk published from admin',
|
|
58
|
+
})
|
|
59
|
+
break
|
|
60
|
+
case 'unpublish':
|
|
61
|
+
response = await bulkUnpublish({
|
|
62
|
+
ids: selectedIds,
|
|
63
|
+
})
|
|
64
|
+
break
|
|
65
|
+
case 'delete':
|
|
66
|
+
response = await bulkDelete({
|
|
67
|
+
ids: selectedIds,
|
|
68
|
+
hardDelete: false,
|
|
69
|
+
})
|
|
70
|
+
break
|
|
71
|
+
case 'archive':
|
|
72
|
+
response = await bulkUpdate({
|
|
73
|
+
ids: selectedIds,
|
|
74
|
+
status: 'archived',
|
|
75
|
+
})
|
|
76
|
+
break
|
|
77
|
+
default:
|
|
78
|
+
throw new Error(`Unknown action: ${activeAction}`)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
setResult({
|
|
82
|
+
succeeded: response.succeeded,
|
|
83
|
+
failed: response.failed,
|
|
84
|
+
errors: response.errors?.map((e) => `${e.id}: ${e.error}`),
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
if (response.failed === 0) {
|
|
88
|
+
setTimeout(() => {
|
|
89
|
+
setActiveAction(null)
|
|
90
|
+
onClearSelection()
|
|
91
|
+
onOperationComplete?.()
|
|
92
|
+
}, 1500)
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
const message = error instanceof Error ? error.message : 'Operation failed'
|
|
96
|
+
setResult({
|
|
97
|
+
succeeded: 0,
|
|
98
|
+
failed: selectedIds.length,
|
|
99
|
+
errors: [message],
|
|
100
|
+
})
|
|
101
|
+
} finally {
|
|
102
|
+
setIsProcessing(false)
|
|
103
|
+
}
|
|
104
|
+
}, [
|
|
105
|
+
activeAction,
|
|
106
|
+
selectedIds,
|
|
107
|
+
bulkPublish,
|
|
108
|
+
bulkUnpublish,
|
|
109
|
+
bulkDelete,
|
|
110
|
+
bulkUpdate,
|
|
111
|
+
onClearSelection,
|
|
112
|
+
onOperationComplete,
|
|
113
|
+
])
|
|
114
|
+
|
|
115
|
+
const handleCancel = useCallback(() => {
|
|
116
|
+
setActiveAction(null)
|
|
117
|
+
setResult(null)
|
|
118
|
+
}, [])
|
|
119
|
+
|
|
120
|
+
if (selectedIds.length === 0) {
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return (
|
|
125
|
+
<>
|
|
126
|
+
<div className="fixed inset-x-0 bottom-0 z-50 border-t bg-background/95 px-6 py-3 shadow-lg backdrop-blur supports-[backdrop-filter]:bg-background/80">
|
|
127
|
+
<div className="mx-auto flex max-w-7xl items-center justify-between">
|
|
128
|
+
<div className="flex items-center gap-3">
|
|
129
|
+
<Badge variant="secondary" className="text-sm font-semibold">
|
|
130
|
+
{selectedIds.length}
|
|
131
|
+
</Badge>
|
|
132
|
+
<span className="text-sm text-muted-foreground">
|
|
133
|
+
{selectedIds.length === 1 ? 'item' : 'items'} selected
|
|
134
|
+
</span>
|
|
135
|
+
<button
|
|
136
|
+
type="button"
|
|
137
|
+
onClick={onClearSelection}
|
|
138
|
+
className="flex items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
|
139
|
+
>
|
|
140
|
+
<X className="size-3" />
|
|
141
|
+
Clear
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div className="flex items-center gap-2">
|
|
146
|
+
<CmsButton
|
|
147
|
+
variant="success"
|
|
148
|
+
size="sm"
|
|
149
|
+
onClick={() => handleAction('publish')}
|
|
150
|
+
>
|
|
151
|
+
Publish
|
|
152
|
+
</CmsButton>
|
|
153
|
+
<CmsButton
|
|
154
|
+
variant="warning"
|
|
155
|
+
size="sm"
|
|
156
|
+
onClick={() => handleAction('unpublish')}
|
|
157
|
+
>
|
|
158
|
+
Unpublish
|
|
159
|
+
</CmsButton>
|
|
160
|
+
<CmsButton
|
|
161
|
+
variant="secondary"
|
|
162
|
+
size="sm"
|
|
163
|
+
onClick={() => handleAction('archive')}
|
|
164
|
+
>
|
|
165
|
+
Archive
|
|
166
|
+
</CmsButton>
|
|
167
|
+
<CmsButton
|
|
168
|
+
variant="danger"
|
|
169
|
+
size="sm"
|
|
170
|
+
onClick={() => handleAction('delete')}
|
|
171
|
+
>
|
|
172
|
+
Delete
|
|
173
|
+
</CmsButton>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
|
|
178
|
+
{activeAction && (
|
|
179
|
+
<BulkOperationModal
|
|
180
|
+
action={activeAction}
|
|
181
|
+
count={selectedIds.length}
|
|
182
|
+
isProcessing={isProcessing}
|
|
183
|
+
result={result}
|
|
184
|
+
onConfirm={handleConfirm}
|
|
185
|
+
onCancel={handleCancel}
|
|
186
|
+
/>
|
|
187
|
+
)}
|
|
188
|
+
</>
|
|
189
|
+
)
|
|
190
|
+
}
|