convex-cms 0.0.5-alpha.0 → 0.0.5-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (323) hide show
  1. package/README.md +95 -144
  2. package/admin/README.md +99 -0
  3. package/admin/src/components/AdminLayout.tsx +22 -0
  4. package/admin/src/components/BreakingChangesWarningDialog.tsx +81 -0
  5. package/admin/src/components/BulkActionBar.tsx +190 -0
  6. package/admin/src/components/BulkOperationModal.tsx +177 -0
  7. package/admin/src/components/ContentEntryEditor.tsx +1104 -0
  8. package/admin/src/components/ContentTypeFormModal.tsx +1012 -0
  9. package/admin/src/components/ErrorBoundary.tsx +83 -0
  10. package/admin/src/components/ErrorState.tsx +147 -0
  11. package/admin/src/components/Header.tsx +294 -0
  12. package/admin/src/components/RouteGuard.tsx +264 -0
  13. package/admin/src/components/Sidebar.tsx +90 -0
  14. package/admin/src/components/TaxonomyEditor.tsx +348 -0
  15. package/admin/src/components/TermTree.tsx +533 -0
  16. package/admin/src/components/UploadDropzone.tsx +383 -0
  17. package/admin/src/components/VersionCompare.tsx +250 -0
  18. package/admin/src/components/VersionHistory.tsx +279 -0
  19. package/admin/src/components/VersionRollbackModal.tsx +79 -0
  20. package/admin/src/components/cmsds/CmsButton.tsx +101 -0
  21. package/admin/src/components/cmsds/CmsDialog.tsx +139 -0
  22. package/admin/src/components/cmsds/CmsDropdown.tsx +62 -0
  23. package/admin/src/components/cmsds/CmsEmptyState.tsx +54 -0
  24. package/admin/src/components/cmsds/CmsField.tsx +47 -0
  25. package/admin/src/components/cmsds/CmsPageHeader.tsx +35 -0
  26. package/admin/src/components/cmsds/CmsStatusBadge.tsx +153 -0
  27. package/admin/src/components/cmsds/CmsSurface.tsx +52 -0
  28. package/admin/src/components/cmsds/CmsTable.tsx +164 -0
  29. package/admin/src/components/cmsds/CmsToolbar.tsx +58 -0
  30. package/admin/src/components/cmsds/index.ts +10 -0
  31. package/admin/src/components/fields/BooleanField.tsx +74 -0
  32. package/admin/src/components/fields/CategoryField.tsx +394 -0
  33. package/admin/src/components/fields/DateField.tsx +173 -0
  34. package/admin/src/components/fields/DefaultFieldRenderer.tsx +74 -0
  35. package/admin/src/components/fields/FieldRenderer.tsx +180 -0
  36. package/admin/src/components/fields/FieldWrapper.tsx +57 -0
  37. package/admin/src/components/fields/JsonField.tsx +172 -0
  38. package/admin/src/components/fields/MediaField.tsx +367 -0
  39. package/admin/src/components/fields/MultiSelectField.tsx +118 -0
  40. package/admin/src/components/fields/NumberField.tsx +77 -0
  41. package/admin/src/components/fields/ReferenceField.tsx +386 -0
  42. package/admin/src/components/fields/RichTextField.tsx +171 -0
  43. package/admin/src/components/fields/SelectField.tsx +62 -0
  44. package/admin/src/components/fields/TagField.tsx +325 -0
  45. package/admin/src/components/fields/TextAreaField.tsx +68 -0
  46. package/admin/src/components/fields/TextField.tsx +56 -0
  47. package/admin/src/components/fields/index.ts +54 -0
  48. package/admin/src/components/fields/registry.ts +64 -0
  49. package/admin/src/components/fields/types.ts +217 -0
  50. package/admin/src/components/filters/TaxonomyFilter.tsx +254 -0
  51. package/admin/src/components/filters/index.ts +1 -0
  52. package/admin/src/components/index.ts +8 -0
  53. package/admin/src/components/media/MediaAssetActions.tsx +115 -0
  54. package/admin/src/components/media/MediaAssetEditDialog.tsx +217 -0
  55. package/admin/src/components/media/MediaBulkActionBar.tsx +51 -0
  56. package/admin/src/components/media/MediaFolderActions.tsx +69 -0
  57. package/admin/src/components/media/MediaFolderEditDialog.tsx +126 -0
  58. package/admin/src/components/media/MediaMoveModal.tsx +179 -0
  59. package/admin/src/components/media/MediaPreviewModal.tsx +370 -0
  60. package/admin/src/components/media/MediaTaxonomyPicker.tsx +304 -0
  61. package/admin/src/components/media/MediaTrashBulkActionBar.tsx +59 -0
  62. package/admin/src/components/ui/accordion.tsx +64 -0
  63. package/admin/src/components/ui/alert-dialog.tsx +155 -0
  64. package/admin/src/components/ui/alert.tsx +66 -0
  65. package/admin/src/components/ui/avatar.tsx +53 -0
  66. package/admin/src/components/ui/badge.tsx +46 -0
  67. package/admin/src/components/ui/breadcrumb.tsx +109 -0
  68. package/admin/src/components/ui/button.tsx +62 -0
  69. package/admin/src/components/ui/calendar.tsx +220 -0
  70. package/admin/src/components/ui/card.tsx +92 -0
  71. package/admin/src/components/ui/checkbox.tsx +30 -0
  72. package/admin/src/components/ui/command.tsx +182 -0
  73. package/admin/src/components/ui/dialog.tsx +143 -0
  74. package/admin/src/components/ui/dropdown-menu.tsx +257 -0
  75. package/admin/src/components/ui/form.tsx +167 -0
  76. package/admin/src/components/ui/input.tsx +21 -0
  77. package/admin/src/components/ui/label.tsx +24 -0
  78. package/admin/src/components/ui/popover.tsx +46 -0
  79. package/admin/src/components/ui/scroll-area.tsx +56 -0
  80. package/admin/src/components/ui/select.tsx +190 -0
  81. package/admin/src/components/ui/separator.tsx +26 -0
  82. package/admin/src/components/ui/sheet.tsx +137 -0
  83. package/admin/src/components/ui/sidebar.tsx +724 -0
  84. package/admin/src/components/ui/skeleton.tsx +13 -0
  85. package/admin/src/components/ui/sonner.tsx +38 -0
  86. package/admin/src/components/ui/switch.tsx +31 -0
  87. package/admin/src/components/ui/table.tsx +114 -0
  88. package/admin/src/components/ui/tabs.tsx +66 -0
  89. package/admin/src/components/ui/textarea.tsx +18 -0
  90. package/admin/src/components/ui/tooltip.tsx +61 -0
  91. package/admin/src/contexts/AdminConfigContext.tsx +30 -0
  92. package/admin/src/contexts/AuthContext.tsx +330 -0
  93. package/admin/src/contexts/BreadcrumbContext.tsx +49 -0
  94. package/admin/src/contexts/SettingsConfigContext.tsx +57 -0
  95. package/admin/src/contexts/ThemeContext.tsx +91 -0
  96. package/admin/src/contexts/index.ts +20 -0
  97. package/admin/src/embed/components/EmbedHeader.tsx +103 -0
  98. package/admin/src/embed/components/EmbedLayout.tsx +29 -0
  99. package/admin/src/embed/components/EmbedSidebar.tsx +119 -0
  100. package/admin/src/embed/components/index.ts +3 -0
  101. package/admin/src/embed/contexts/ApiContext.tsx +32 -0
  102. package/admin/src/embed/index.tsx +184 -0
  103. package/admin/src/embed/navigation.tsx +202 -0
  104. package/admin/src/embed/pages/Content.tsx +19 -0
  105. package/admin/src/embed/pages/ContentTypes.tsx +19 -0
  106. package/admin/src/embed/pages/Dashboard.tsx +19 -0
  107. package/admin/src/embed/pages/Media.tsx +19 -0
  108. package/admin/src/embed/pages/Settings.tsx +22 -0
  109. package/admin/src/embed/pages/Taxonomies.tsx +22 -0
  110. package/admin/src/embed/pages/Trash.tsx +22 -0
  111. package/admin/src/embed/pages/index.ts +7 -0
  112. package/admin/src/embed/types.ts +24 -0
  113. package/admin/src/hooks/index.ts +2 -0
  114. package/admin/src/hooks/use-mobile.ts +19 -0
  115. package/admin/src/hooks/useBreadcrumbLabel.ts +15 -0
  116. package/admin/src/hooks/usePermissions.ts +211 -0
  117. package/admin/src/lib/admin-config.ts +111 -0
  118. package/admin/src/lib/cn.ts +6 -0
  119. package/admin/src/lib/config.server.ts +56 -0
  120. package/admin/src/lib/convex.ts +26 -0
  121. package/admin/src/lib/embed-adapter.ts +80 -0
  122. package/admin/src/lib/icons.tsx +96 -0
  123. package/admin/src/lib/loadAdminConfig.ts +92 -0
  124. package/admin/src/lib/motion.ts +29 -0
  125. package/admin/src/lib/navigation.ts +43 -0
  126. package/admin/src/lib/tanstack-adapter.ts +82 -0
  127. package/admin/src/pages/ContentPage.tsx +337 -0
  128. package/admin/src/pages/ContentTypesPage.tsx +457 -0
  129. package/admin/src/pages/DashboardPage.tsx +163 -0
  130. package/admin/src/pages/MediaPage.tsx +34 -0
  131. package/admin/src/pages/SettingsPage.tsx +486 -0
  132. package/admin/src/pages/TaxonomiesPage.tsx +289 -0
  133. package/admin/src/pages/TrashPage.tsx +421 -0
  134. package/admin/src/pages/index.ts +14 -0
  135. package/admin/src/routeTree.gen.ts +262 -0
  136. package/admin/src/router.tsx +22 -0
  137. package/admin/src/routes/__root.tsx +250 -0
  138. package/admin/src/routes/content-types.tsx +20 -0
  139. package/admin/src/routes/content.tsx +20 -0
  140. package/admin/src/routes/entries/$entryId.tsx +107 -0
  141. package/admin/src/routes/entries/new.$contentTypeId.tsx +69 -0
  142. package/admin/src/routes/entries/type/$contentTypeId.tsx +503 -0
  143. package/admin/src/routes/index.tsx +20 -0
  144. package/admin/src/routes/media.tsx +1095 -0
  145. package/admin/src/routes/settings.tsx +20 -0
  146. package/admin/src/routes/taxonomies.tsx +20 -0
  147. package/admin/src/routes/trash.tsx +20 -0
  148. package/admin/src/styles/globals.css +69 -0
  149. package/admin/src/styles/tailwind-config.css +74 -0
  150. package/admin/src/styles/theme.css +73 -0
  151. package/admin/src/types/index.ts +221 -0
  152. package/admin/src/utils/errorParsing.ts +163 -0
  153. package/admin/src/utils/index.ts +5 -0
  154. package/admin/src/vite-env.d.ts +14 -0
  155. package/admin/tailwind.preset.cjs +102 -0
  156. package/admin-dist/nitro.json +1 -1
  157. package/admin-dist/public/assets/{CmsEmptyState-CiMQwSQV.js → CmsEmptyState-CkqBIab3.js} +1 -1
  158. package/admin-dist/public/assets/{CmsPageHeader-ohOq0luT.js → CmsPageHeader-CUtl5MMG.js} +1 -1
  159. package/admin-dist/public/assets/{CmsStatusBadge-BdNf0V9v.js → CmsStatusBadge-CUYFgEe-.js} +1 -1
  160. package/admin-dist/public/assets/{CmsSurface-CWup6Jh7.js → CmsSurface-CsJfAVa3.js} +1 -1
  161. package/admin-dist/public/assets/{CmsToolbar-cEBlCHa3.js → CmsToolbar-CnfbcxeP.js} +1 -1
  162. package/admin-dist/public/assets/{ContentEntryEditor-BY5ypfUs.js → ContentEntryEditor-BU220CCy.js} +1 -1
  163. package/admin-dist/public/assets/TaxonomyFilter-CWCxC5HZ.js +1 -0
  164. package/admin-dist/public/assets/_contentTypeId-DK8cskRt.js +1 -0
  165. package/admin-dist/public/assets/{_entryId-BpSmrfAm.js → _entryId-CuVMExbb.js} +1 -1
  166. package/admin-dist/public/assets/{alert-Bf2l8kxw.js → alert-CF1BSzGR.js} +1 -1
  167. package/admin-dist/public/assets/{badge-qPrc4AUM.js → badge-CmuOIVKp.js} +1 -1
  168. package/admin-dist/public/assets/{circle-check-big-Dgozy3vV.js → circle-check-big-BKDVG6DU.js} +1 -1
  169. package/admin-dist/public/assets/{command-QOmNhlb0.js → command-XJxnF2Sd.js} +1 -1
  170. package/admin-dist/public/assets/content-QBUxdxbS.js +1 -0
  171. package/admin-dist/public/assets/content-types-CrNEm8Hf.js +2 -0
  172. package/admin-dist/public/assets/globals-B7Wsfh_v.css +1 -0
  173. package/admin-dist/public/assets/index-C7xOwudI.js +1 -0
  174. package/admin-dist/public/assets/{label-DCsUdvFh.js → label-CHCnXeBk.js} +1 -1
  175. package/admin-dist/public/assets/{link-2-Czw1N61H.js → link-2-Bb34judH.js} +1 -1
  176. package/admin-dist/public/assets/{list-DtCsXj8-.js → list-9Pzt48ld.js} +1 -1
  177. package/admin-dist/public/assets/{main-CXgkZMhe.js → main-CjQ2VI9L.js} +3 -3
  178. package/admin-dist/public/assets/media-Dc5PWt2Q.js +1 -0
  179. package/admin-dist/public/assets/{new._contentTypeId-CoTDxKzf.js → new._contentTypeId-C_I4YxIa.js} +1 -1
  180. package/admin-dist/public/assets/{plus-xCFJK0RC.js → plus-Ceef7DHk.js} +1 -1
  181. package/admin-dist/public/assets/{rotate-ccw-DIqK63wY.js → rotate-ccw-7k7-4VUq.js} +1 -1
  182. package/admin-dist/public/assets/{scroll-area-B-yrE66a.js → scroll-area-CC6wujnp.js} +1 -1
  183. package/admin-dist/public/assets/{search-CbCbboeU.js → search-DwoUV2pv.js} +1 -1
  184. package/admin-dist/public/assets/{select-Co3TZFJb.js → select-hOZTp8aC.js} +1 -1
  185. package/admin-dist/public/assets/{settings-BspTTv_o.js → settings-t2PbCZh4.js} +1 -1
  186. package/admin-dist/public/assets/{switch-CfavASmR.js → switch-jX2pDaNU.js} +1 -1
  187. package/admin-dist/public/assets/{tabs-CN5s5u2W.js → tabs-q4EbZk7c.js} +1 -1
  188. package/admin-dist/public/assets/{tanstack-adapter-npeE3RdY.js → tanstack-adapter-B-Glm4kH.js} +1 -1
  189. package/admin-dist/public/assets/taxonomies-kyk5P4ZW.js +1 -0
  190. package/admin-dist/public/assets/{textarea-BJ0XFZpT.js → textarea-B6SfBmr0.js} +1 -1
  191. package/admin-dist/public/assets/trash-BOCnIznD.js +1 -0
  192. package/admin-dist/public/assets/{triangle-alert-BZRcqsUg.js → triangle-alert-CXFIO_Gu.js} +1 -1
  193. package/admin-dist/public/assets/{useBreadcrumbLabel-DwZlwvFF.js → useBreadcrumbLabel-_6qBagc3.js} +1 -1
  194. package/admin-dist/public/assets/{usePermissions-C1JQhfqb.js → usePermissions-M1ijZ7a6.js} +1 -1
  195. package/admin-dist/server/_ssr/{CmsButton-B45JAKR1.mjs → CmsButton-DOiTVKQq.mjs} +1 -1
  196. package/admin-dist/server/_ssr/{CmsEmptyState-D_BQFAVR.mjs → CmsEmptyState-fbnGt3LD.mjs} +2 -2
  197. package/admin-dist/server/_ssr/{CmsPageHeader-CrUZA59A.mjs → CmsPageHeader-DHRrdOZa.mjs} +1 -1
  198. package/admin-dist/server/_ssr/{CmsStatusBadge-B-sj6yaj.mjs → CmsStatusBadge-s7obWbKZ.mjs} +2 -2
  199. package/admin-dist/server/_ssr/{CmsSurface-DKJZhpjk.mjs → CmsSurface-rFoYjb62.mjs} +1 -1
  200. package/admin-dist/server/_ssr/{CmsToolbar-ByaW5iXf.mjs → CmsToolbar-zTE45z2q.mjs} +2 -2
  201. package/admin-dist/server/_ssr/{ContentEntryEditor-D3_Jb1dq.mjs → ContentEntryEditor-BLoEjT_m.mjs} +12 -12
  202. package/admin-dist/server/_ssr/{TaxonomyFilter-BRJkuCtA.mjs → TaxonomyFilter-XAtaJC2z.mjs} +5 -5
  203. package/admin-dist/server/_ssr/{_contentTypeId-B9kA6CaM.mjs → _contentTypeId-Csl4822C.mjs} +13 -13
  204. package/admin-dist/server/_ssr/{_entryId-BddcMkZN.mjs → _entryId-D8alLFBx.mjs} +15 -15
  205. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BffZedId.mjs +4 -0
  206. package/admin-dist/server/_ssr/{command-CGtVr8Gb.mjs → command-C0Di14--.mjs} +1 -1
  207. package/admin-dist/server/_ssr/{content-D1tbeOd0.mjs → content-CT-FPsmV.mjs} +12 -55
  208. package/admin-dist/server/_ssr/{content-types-BZqY_BER.mjs → content-types-C8cBFdzE.mjs} +15 -46
  209. package/admin-dist/server/_ssr/{index-BIdq4xaC.mjs → index-BJtcrEc-.mjs} +5 -5
  210. package/admin-dist/server/_ssr/index.mjs +2 -2
  211. package/admin-dist/server/_ssr/{label-T-QNKAr6.mjs → label-qn2Afwl4.mjs} +1 -1
  212. package/admin-dist/server/_ssr/{media-C-xqjBrl.mjs → media-qv5IAsMZ.mjs} +14 -14
  213. package/admin-dist/server/_ssr/{new._contentTypeId-DWic9cRq.mjs → new._contentTypeId-DdGyrhqs.mjs} +13 -13
  214. package/admin-dist/server/_ssr/{router-D1BMAMJT.mjs → router-nSVkxb6Y.mjs} +11 -11
  215. package/admin-dist/server/_ssr/{scroll-area-C0pic_WA.mjs → scroll-area-BCinP455.mjs} +1 -1
  216. package/admin-dist/server/_ssr/{select-CqmuN2F6.mjs → select-BKQlQScw.mjs} +1 -1
  217. package/admin-dist/server/_ssr/{settings-CAkncGGV.mjs → settings-BCr2KQlk.mjs} +55 -40
  218. package/admin-dist/server/_ssr/{switch-CgmuJkT9.mjs → switch-BaOi42fE.mjs} +1 -1
  219. package/admin-dist/server/_ssr/{tabs-CnMj0aRy.mjs → tabs-DYXEi9kq.mjs} +2 -2
  220. package/admin-dist/server/_ssr/{tanstack-adapter-BXZrMauE.mjs → tanstack-adapter-Bsz8kha-.mjs} +1 -1
  221. package/admin-dist/server/_ssr/{taxonomies-thl3BfVm.mjs → taxonomies-CueMHTbE.mjs} +30 -19
  222. package/admin-dist/server/_ssr/{textarea-4K5OJgeh.mjs → textarea-CI0Jqx2x.mjs} +1 -1
  223. package/admin-dist/server/_ssr/{trash-B40Gx5zP.mjs → trash-DE6W8GoX.mjs} +20 -17
  224. package/admin-dist/server/_ssr/{useBreadcrumbLabel-rn-fL4zV.mjs → useBreadcrumbLabel-B5Yi72lM.mjs} +1 -1
  225. package/admin-dist/server/_ssr/{usePermissions-CKeM6_Vw.mjs → usePermissions-C3nZ-Izm.mjs} +1 -1
  226. package/admin-dist/server/index.mjs +187 -194
  227. package/dist/client/admin/bulk.d.ts +79 -0
  228. package/dist/client/admin/bulk.d.ts.map +1 -0
  229. package/dist/client/admin/bulk.js +72 -0
  230. package/dist/client/admin/bulk.js.map +1 -0
  231. package/dist/client/admin/contentLock.d.ts +118 -0
  232. package/dist/client/admin/contentLock.d.ts.map +1 -0
  233. package/dist/client/admin/contentLock.js +81 -0
  234. package/dist/client/admin/contentLock.js.map +1 -0
  235. package/dist/client/{adminApi.d.ts → admin/contentTypes.d.ts} +39 -1134
  236. package/dist/client/admin/contentTypes.d.ts.map +1 -0
  237. package/dist/client/admin/contentTypes.js +122 -0
  238. package/dist/client/admin/contentTypes.js.map +1 -0
  239. package/dist/client/admin/dashboard.d.ts +16 -0
  240. package/dist/client/admin/dashboard.d.ts.map +1 -0
  241. package/dist/client/admin/dashboard.js +33 -0
  242. package/dist/client/admin/dashboard.js.map +1 -0
  243. package/dist/client/admin/entries.d.ts +358 -0
  244. package/dist/client/admin/entries.d.ts.map +1 -0
  245. package/dist/client/admin/entries.js +220 -0
  246. package/dist/client/admin/entries.js.map +1 -0
  247. package/dist/client/admin/index.d.ts +6568 -0
  248. package/dist/client/admin/index.d.ts.map +1 -0
  249. package/dist/client/admin/index.js +305 -0
  250. package/dist/client/admin/index.js.map +1 -0
  251. package/dist/client/admin/media.d.ts +1038 -0
  252. package/dist/client/admin/media.d.ts.map +1 -0
  253. package/dist/client/admin/media.js +489 -0
  254. package/dist/client/admin/media.js.map +1 -0
  255. package/dist/client/admin/taxonomies.d.ts +339 -0
  256. package/dist/client/admin/taxonomies.d.ts.map +1 -0
  257. package/dist/client/admin/taxonomies.js +364 -0
  258. package/dist/client/admin/taxonomies.js.map +1 -0
  259. package/dist/client/admin/trash.d.ts +91 -0
  260. package/dist/client/admin/trash.d.ts.map +1 -0
  261. package/dist/client/admin/trash.js +71 -0
  262. package/dist/client/admin/trash.js.map +1 -0
  263. package/dist/client/admin/types.d.ts +320 -0
  264. package/dist/client/admin/types.d.ts.map +1 -0
  265. package/dist/client/admin/types.js +7 -0
  266. package/dist/client/admin/types.js.map +1 -0
  267. package/dist/client/admin/validators.d.ts +3886 -0
  268. package/dist/client/admin/validators.d.ts.map +1 -0
  269. package/dist/client/admin/validators.js +322 -0
  270. package/dist/client/admin/validators.js.map +1 -0
  271. package/dist/client/admin/versions.d.ts +106 -0
  272. package/dist/client/admin/versions.d.ts.map +1 -0
  273. package/dist/client/admin/versions.js +57 -0
  274. package/dist/client/admin/versions.js.map +1 -0
  275. package/dist/client/adminApiTypes.d.ts +27 -0
  276. package/dist/client/adminApiTypes.d.ts.map +1 -0
  277. package/dist/client/adminApiTypes.js +12 -0
  278. package/dist/client/adminApiTypes.js.map +1 -0
  279. package/dist/client/{admin-config.d.ts → adminConfig.d.ts} +2 -2
  280. package/dist/client/adminConfig.d.ts.map +1 -0
  281. package/dist/client/{admin-config.js → adminConfig.js} +1 -1
  282. package/dist/client/adminConfig.js.map +1 -0
  283. package/dist/client/agentTools.d.ts +4 -4
  284. package/dist/client/index.d.ts +2 -2
  285. package/dist/client/index.d.ts.map +1 -1
  286. package/dist/client/index.js +15 -2
  287. package/dist/client/index.js.map +1 -1
  288. package/dist/component/contentEntries.d.ts +4 -4
  289. package/dist/component/contentEntryMutations.d.ts +46 -0
  290. package/dist/component/contentEntryMutations.d.ts.map +1 -1
  291. package/dist/component/contentEntryMutations.js +1 -1
  292. package/dist/component/contentEntryMutations.js.map +1 -1
  293. package/dist/component/contentTypeMigration.d.ts +1 -1
  294. package/dist/component/contentTypeMutations.d.ts +22 -0
  295. package/dist/component/contentTypeMutations.d.ts.map +1 -1
  296. package/dist/component/contentTypeMutations.js +1 -1
  297. package/dist/component/contentTypeMutations.js.map +1 -1
  298. package/dist/component/mediaAssetMutations.d.ts +47 -0
  299. package/dist/component/mediaAssetMutations.d.ts.map +1 -1
  300. package/dist/component/mediaAssetMutations.js +1 -1
  301. package/dist/component/mediaAssetMutations.js.map +1 -1
  302. package/dist/component/schema.d.ts +9 -0
  303. package/dist/component/schema.d.ts.map +1 -1
  304. package/dist/component/schema.js +1 -1
  305. package/dist/component/schema.js.map +1 -1
  306. package/package.json +85 -3
  307. package/admin-dist/public/assets/ErrorState-C4nJ-ml4.js +0 -1
  308. package/admin-dist/public/assets/TaxonomyFilter-BgE_SR_O.js +0 -1
  309. package/admin-dist/public/assets/_contentTypeId-DtZectcC.js +0 -1
  310. package/admin-dist/public/assets/content-OEBGlxg1.js +0 -1
  311. package/admin-dist/public/assets/content-types-CjQliqVV.js +0 -2
  312. package/admin-dist/public/assets/globals-hAmgC66w.css +0 -1
  313. package/admin-dist/public/assets/index-BH_ECMhv.js +0 -1
  314. package/admin-dist/public/assets/media-DTJ3-ViE.js +0 -1
  315. package/admin-dist/public/assets/taxonomies-CgG46fIF.js +0 -1
  316. package/admin-dist/public/assets/trash-B3daldm5.js +0 -1
  317. package/admin-dist/server/_ssr/ErrorState-cI-bKLez.mjs +0 -89
  318. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dd7AmelK.mjs +0 -4
  319. package/dist/client/admin-config.d.ts.map +0 -1
  320. package/dist/client/admin-config.js.map +0 -1
  321. package/dist/client/adminApi.d.ts.map +0 -1
  322. package/dist/client/adminApi.js +0 -736
  323. package/dist/client/adminApi.js.map +0 -1
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Route Guard Component
3
+ *
4
+ * Protects routes by checking authentication and permissions.
5
+ * Shows appropriate UI for loading, unauthenticated, and unauthorized states.
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * // Protect a route requiring authentication
10
+ * <RouteGuard>
11
+ * <ProtectedContent />
12
+ * </RouteGuard>
13
+ *
14
+ * // Protect a route requiring specific permissions
15
+ * <RouteGuard
16
+ * requiredPermission={{ resource: 'settings', action: 'manage' }}
17
+ * >
18
+ * <SettingsPage />
19
+ * </RouteGuard>
20
+ *
21
+ * // Require a specific role
22
+ * <RouteGuard requiredRole="admin">
23
+ * <AdminOnlyContent />
24
+ * </RouteGuard>
25
+ * ```
26
+ */
27
+
28
+ import type { ReactNode } from 'react';
29
+ import { useAuth, type PermissionCheck } from '../contexts/AuthContext';
30
+ import type { RoleName } from '../../../src/component/roles';
31
+ import { Loader2 } from 'lucide-react';
32
+
33
+ // =============================================================================
34
+ // Types
35
+ // =============================================================================
36
+
37
+ export interface RouteGuardProps {
38
+ children: ReactNode;
39
+ /**
40
+ * Permission required to access this route.
41
+ * If not specified, only authentication is required.
42
+ */
43
+ requiredPermission?: PermissionCheck;
44
+ /**
45
+ * Role required to access this route.
46
+ * If not specified, any authenticated user with a role can access.
47
+ */
48
+ requiredRole?: RoleName | string;
49
+ /**
50
+ * Custom loading component.
51
+ */
52
+ loadingComponent?: ReactNode;
53
+ /**
54
+ * Custom unauthenticated component.
55
+ */
56
+ unauthenticatedComponent?: ReactNode;
57
+ /**
58
+ * Custom unauthorized component.
59
+ */
60
+ unauthorizedComponent?: ReactNode;
61
+ /**
62
+ * Callback when user is unauthenticated.
63
+ */
64
+ onUnauthenticated?: () => void;
65
+ /**
66
+ * Callback when user is unauthorized.
67
+ */
68
+ onUnauthorized?: () => void;
69
+ }
70
+
71
+ // =============================================================================
72
+ // Default Components
73
+ // =============================================================================
74
+
75
+ function DefaultLoading() {
76
+ return (
77
+ <div className="route-guard route-guard--loading bg-background flex items-center justify-center min-h-screen">
78
+ <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
79
+ </div>
80
+ );
81
+ }
82
+
83
+ function DefaultUnauthenticated() {
84
+ return (
85
+ <div className="route-guard route-guard--unauthenticated">
86
+ <div className="route-guard-icon">
87
+ <svg
88
+ xmlns="http://www.w3.org/2000/svg"
89
+ width="48"
90
+ height="48"
91
+ viewBox="0 0 24 24"
92
+ fill="none"
93
+ stroke="currentColor"
94
+ strokeWidth="2"
95
+ strokeLinecap="round"
96
+ strokeLinejoin="round"
97
+ >
98
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />
99
+ <path d="M7 11V7a5 5 0 0 1 10 0v4" />
100
+ </svg>
101
+ </div>
102
+ <h2>Authentication Required</h2>
103
+ <p>Please log in to access the admin panel.</p>
104
+ </div>
105
+ );
106
+ }
107
+
108
+ function DefaultUnauthorized({
109
+ requiredRole,
110
+ requiredPermission,
111
+ }: {
112
+ requiredRole?: RoleName | string;
113
+ requiredPermission?: PermissionCheck;
114
+ }) {
115
+ return (
116
+ <div className="route-guard route-guard--unauthorized">
117
+ <div className="route-guard-icon">
118
+ <svg
119
+ xmlns="http://www.w3.org/2000/svg"
120
+ width="48"
121
+ height="48"
122
+ viewBox="0 0 24 24"
123
+ fill="none"
124
+ stroke="currentColor"
125
+ strokeWidth="2"
126
+ strokeLinecap="round"
127
+ strokeLinejoin="round"
128
+ >
129
+ <circle cx="12" cy="12" r="10" />
130
+ <line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
131
+ </svg>
132
+ </div>
133
+ <h2>Access Denied</h2>
134
+ <p>You don't have permission to access this page.</p>
135
+ {requiredRole && (
136
+ <p className="route-guard-detail">
137
+ Required role: <strong>{requiredRole}</strong>
138
+ </p>
139
+ )}
140
+ {requiredPermission && (
141
+ <p className="route-guard-detail">
142
+ Required permission:{' '}
143
+ <strong>
144
+ {requiredPermission.action} on {requiredPermission.resource}
145
+ </strong>
146
+ </p>
147
+ )}
148
+ </div>
149
+ );
150
+ }
151
+
152
+ function DefaultError({ error }: { error: string }) {
153
+ return (
154
+ <div className="route-guard route-guard--error">
155
+ <div className="route-guard-icon">
156
+ <svg
157
+ xmlns="http://www.w3.org/2000/svg"
158
+ width="48"
159
+ height="48"
160
+ viewBox="0 0 24 24"
161
+ fill="none"
162
+ stroke="currentColor"
163
+ strokeWidth="2"
164
+ strokeLinecap="round"
165
+ strokeLinejoin="round"
166
+ >
167
+ <circle cx="12" cy="12" r="10" />
168
+ <line x1="12" y1="8" x2="12" y2="12" />
169
+ <line x1="12" y1="16" x2="12.01" y2="16" />
170
+ </svg>
171
+ </div>
172
+ <h2>Authentication Error</h2>
173
+ <p>{error}</p>
174
+ </div>
175
+ );
176
+ }
177
+
178
+ // =============================================================================
179
+ // Component
180
+ // =============================================================================
181
+
182
+ /**
183
+ * Route Guard Component
184
+ *
185
+ * Wraps protected content and handles authentication/authorization checks.
186
+ */
187
+ export function RouteGuard({
188
+ children,
189
+ requiredPermission,
190
+ requiredRole,
191
+ loadingComponent,
192
+ unauthenticatedComponent,
193
+ unauthorizedComponent,
194
+ onUnauthenticated,
195
+ onUnauthorized,
196
+ }: RouteGuardProps) {
197
+ const { authState, role, checkPermission, error } = useAuth();
198
+
199
+ // Show loading state
200
+ if (authState === 'loading') {
201
+ return <>{loadingComponent ?? <DefaultLoading />}</>;
202
+ }
203
+
204
+ // Show error state
205
+ if (authState === 'error') {
206
+ return <DefaultError error={error ?? 'An error occurred'} />;
207
+ }
208
+
209
+ // Handle unauthenticated state
210
+ if (authState === 'unauthenticated') {
211
+ onUnauthenticated?.();
212
+ return <>{unauthenticatedComponent ?? <DefaultUnauthenticated />}</>;
213
+ }
214
+
215
+ // Check if user has any role (must be assigned to CMS)
216
+ if (!role) {
217
+ onUnauthorized?.();
218
+ return (
219
+ <>
220
+ {unauthorizedComponent ?? (
221
+ <DefaultUnauthorized
222
+ requiredRole={requiredRole}
223
+ requiredPermission={requiredPermission}
224
+ />
225
+ )}
226
+ </>
227
+ );
228
+ }
229
+
230
+ // Check required role
231
+ if (requiredRole && role !== requiredRole) {
232
+ onUnauthorized?.();
233
+ return (
234
+ <>
235
+ {unauthorizedComponent ?? (
236
+ <DefaultUnauthorized
237
+ requiredRole={requiredRole}
238
+ requiredPermission={requiredPermission}
239
+ />
240
+ )}
241
+ </>
242
+ );
243
+ }
244
+
245
+ // Check required permission
246
+ if (requiredPermission && !checkPermission(requiredPermission)) {
247
+ onUnauthorized?.();
248
+ return (
249
+ <>
250
+ {unauthorizedComponent ?? (
251
+ <DefaultUnauthorized
252
+ requiredRole={requiredRole}
253
+ requiredPermission={requiredPermission}
254
+ />
255
+ )}
256
+ </>
257
+ );
258
+ }
259
+
260
+ // User is authorized, render children
261
+ return <>{children}</>;
262
+ }
263
+
264
+ export default RouteGuard;
@@ -0,0 +1,90 @@
1
+ import { Link, useRouterState } from "@tanstack/react-router";
2
+ import { Layers } from "lucide-react";
3
+ import { cn } from "~/lib/cn";
4
+ import { useAdminConfig } from "~/contexts";
5
+ import { Icon } from "~/lib/icons";
6
+ import type { NavItem } from "~/lib/admin-config";
7
+
8
+ export function Sidebar() {
9
+ const routerState = useRouterState();
10
+ const currentPath = routerState.location.pathname;
11
+ const config = useAdminConfig();
12
+ const { navItems, branding, layout } = config;
13
+
14
+ const isActive = (to: string, exact?: boolean) => {
15
+ if (exact) {
16
+ return currentPath === to;
17
+ }
18
+ return currentPath.startsWith(to);
19
+ };
20
+
21
+ const renderNavItem = (item: NavItem) => (
22
+ <Link
23
+ key={item.id}
24
+ to={item.path}
25
+ className={cn(
26
+ "flex items-center gap-3 rounded-md px-2 py-2 text-sm font-medium transition-colors",
27
+ isActive(item.path, item.exact)
28
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
29
+ : "text-sidebar-foreground hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground"
30
+ )}
31
+ >
32
+ <Icon name={item.icon} className="size-5" />
33
+ <span className="flex-1">{item.label}</span>
34
+ {item.badge && (
35
+ <span className="rounded-full bg-sidebar-primary px-2 py-0.5 text-xs text-sidebar-primary-foreground">
36
+ {item.badge}
37
+ </span>
38
+ )}
39
+ </Link>
40
+ );
41
+
42
+ const sidebarWidth = layout.sidebarWidth;
43
+
44
+ return (
45
+ <aside
46
+ className="fixed inset-y-0 left-0 z-50 flex flex-col border-r border-sidebar-border bg-sidebar"
47
+ style={{ width: sidebarWidth }}
48
+ >
49
+ <div className="flex h-14 items-center gap-2 border-b border-sidebar-border px-4">
50
+ <Link to="/" className="flex items-center gap-2 font-semibold text-sidebar-foreground">
51
+ {branding.logo ? (
52
+ <img src={branding.logo} alt={branding.appName} className="size-8" />
53
+ ) : (
54
+ <div className="flex size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
55
+ <Layers className="size-4" />
56
+ </div>
57
+ )}
58
+ <span className="text-base">{branding.appName}</span>
59
+ </Link>
60
+ </div>
61
+
62
+ <nav className="flex-1 space-y-6 overflow-y-auto p-4">
63
+ {navItems.main.length > 0 && (
64
+ <div className="space-y-1">
65
+ <span className="px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60">
66
+ Main
67
+ </span>
68
+ <div className="space-y-1 pt-2">{navItems.main.map(renderNavItem)}</div>
69
+ </div>
70
+ )}
71
+
72
+ {navItems.config.length > 0 && (
73
+ <div className="space-y-1">
74
+ <span className="px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60">
75
+ Configuration
76
+ </span>
77
+ <div className="space-y-1 pt-2">{navItems.config.map(renderNavItem)}</div>
78
+ </div>
79
+ )}
80
+ </nav>
81
+
82
+ <div className="border-t border-sidebar-border p-4">
83
+ <div className="flex items-center justify-between text-xs text-sidebar-foreground/60">
84
+ <span>Version</span>
85
+ <span className="font-mono">0.1.0</span>
86
+ </div>
87
+ </div>
88
+ </aside>
89
+ );
90
+ }
@@ -0,0 +1,348 @@
1
+ import { useState, useCallback, useEffect } from 'react'
2
+ import { useMutation } from 'convex/react'
3
+ import { api } from '../../convex/_generated/api'
4
+ import { CmsDialog } from '~/components/cmsds/CmsDialog'
5
+ import { CmsButton } from '~/components/cmsds/CmsButton'
6
+ import { Input } from '~/components/ui/input'
7
+ import { Label } from '~/components/ui/label'
8
+ import { Textarea } from '~/components/ui/textarea'
9
+ import { Checkbox } from '~/components/ui/checkbox'
10
+
11
+ interface Taxonomy {
12
+ _id: string
13
+ name: string
14
+ displayName: string
15
+ description?: string
16
+ isHierarchical: boolean
17
+ allowInlineCreation: boolean
18
+ isActive: boolean
19
+ icon?: string
20
+ sortOrder?: number
21
+ }
22
+
23
+ interface TaxonomyEditorProps {
24
+ taxonomy?: Taxonomy | null
25
+ onSave: () => void
26
+ onCancel: () => void
27
+ }
28
+
29
+ export function TaxonomyEditor({
30
+ taxonomy,
31
+ onSave,
32
+ onCancel,
33
+ }: TaxonomyEditorProps) {
34
+ const isEditing = !!taxonomy
35
+
36
+ const [formData, setFormData] = useState({
37
+ name: taxonomy?.name ?? '',
38
+ displayName: taxonomy?.displayName ?? '',
39
+ description: taxonomy?.description ?? '',
40
+ isHierarchical: taxonomy?.isHierarchical ?? false,
41
+ allowInlineCreation: taxonomy?.allowInlineCreation ?? true,
42
+ isActive: taxonomy?.isActive ?? true,
43
+ icon: taxonomy?.icon ?? '',
44
+ sortOrder: taxonomy?.sortOrder ?? 0,
45
+ })
46
+
47
+ const [errors, setErrors] = useState<Record<string, string>>({})
48
+ const [isSubmitting, setIsSubmitting] = useState(false)
49
+ const [submitError, setSubmitError] = useState<string | null>(null)
50
+ const [slugManuallyEdited, setSlugManuallyEdited] = useState(false)
51
+
52
+ const createTaxonomy = useMutation(api.taxonomies.createTaxonomy)
53
+ const updateTaxonomy = useMutation(api.taxonomies.updateTaxonomy)
54
+
55
+ useEffect(() => {
56
+ if (taxonomy) {
57
+ setFormData({
58
+ name: taxonomy.name,
59
+ displayName: taxonomy.displayName,
60
+ description: taxonomy.description ?? '',
61
+ isHierarchical: taxonomy.isHierarchical,
62
+ allowInlineCreation: taxonomy.allowInlineCreation,
63
+ isActive: taxonomy.isActive,
64
+ icon: taxonomy.icon ?? '',
65
+ sortOrder: taxonomy.sortOrder ?? 0,
66
+ })
67
+ }
68
+ }, [taxonomy])
69
+
70
+ const handleChange = useCallback(
71
+ (field: keyof typeof formData, value: string | boolean | number) => {
72
+ setFormData((prev) => ({ ...prev, [field]: value }))
73
+ setErrors((prev) => {
74
+ const { [field]: _, ...rest } = prev
75
+ return rest
76
+ })
77
+ },
78
+ []
79
+ )
80
+
81
+ const generateSlug = useCallback((name: string) => {
82
+ return name
83
+ .toLowerCase()
84
+ .replace(/[^a-z0-9]+/g, '-')
85
+ .replace(/^-|-$/g, '')
86
+ }, [])
87
+
88
+ const handleDisplayNameChange = useCallback(
89
+ (value: string) => {
90
+ handleChange('displayName', value)
91
+ if (!isEditing && !slugManuallyEdited) {
92
+ handleChange('name', generateSlug(value))
93
+ }
94
+ },
95
+ [handleChange, isEditing, slugManuallyEdited, generateSlug]
96
+ )
97
+
98
+ const validate = useCallback(() => {
99
+ const newErrors: Record<string, string> = {}
100
+
101
+ if (!formData.name.trim()) {
102
+ newErrors.name = 'Slug is required'
103
+ } else if (!/^[a-z][a-z0-9-]*$/.test(formData.name)) {
104
+ newErrors.name =
105
+ 'Slug must start with a letter and contain only lowercase letters, numbers, and hyphens'
106
+ }
107
+
108
+ if (!formData.displayName.trim()) {
109
+ newErrors.displayName = 'Display name is required'
110
+ }
111
+
112
+ setErrors(newErrors)
113
+ return Object.keys(newErrors).length === 0
114
+ }, [formData])
115
+
116
+ const handleSubmit = useCallback(
117
+ async (e: React.FormEvent) => {
118
+ e.preventDefault()
119
+
120
+ if (!validate()) return
121
+
122
+ setIsSubmitting(true)
123
+ setSubmitError(null)
124
+
125
+ try {
126
+ if (isEditing && taxonomy) {
127
+ await updateTaxonomy({
128
+ id: taxonomy._id,
129
+ displayName: formData.displayName,
130
+ description: formData.description || undefined,
131
+ allowInlineCreation: formData.allowInlineCreation,
132
+ icon: formData.icon || undefined,
133
+ sortOrder: formData.sortOrder,
134
+ isActive: formData.isActive,
135
+ })
136
+ } else {
137
+ await createTaxonomy({
138
+ name: formData.name,
139
+ displayName: formData.displayName,
140
+ description: formData.description || undefined,
141
+ isHierarchical: formData.isHierarchical,
142
+ allowInlineCreation: formData.allowInlineCreation,
143
+ icon: formData.icon || undefined,
144
+ sortOrder: formData.sortOrder,
145
+ })
146
+ }
147
+ onSave()
148
+ } catch (error) {
149
+ const message =
150
+ error instanceof Error ? error.message : 'Failed to save taxonomy'
151
+ setSubmitError(message)
152
+ } finally {
153
+ setIsSubmitting(false)
154
+ }
155
+ },
156
+ [
157
+ validate,
158
+ isEditing,
159
+ taxonomy,
160
+ formData,
161
+ createTaxonomy,
162
+ updateTaxonomy,
163
+ onSave,
164
+ ]
165
+ )
166
+
167
+ return (
168
+ <CmsDialog
169
+ open={true}
170
+ onOpenChange={(open) => !open && !isSubmitting && onCancel()}
171
+ title={isEditing ? 'Edit Taxonomy' : 'Create Taxonomy'}
172
+ size="lg"
173
+ footer={
174
+ <>
175
+ <CmsButton variant="outline" onClick={onCancel} disabled={isSubmitting}>
176
+ Cancel
177
+ </CmsButton>
178
+ <CmsButton
179
+ variant="primary"
180
+ onClick={handleSubmit}
181
+ loading={isSubmitting}
182
+ >
183
+ {isEditing ? 'Save Changes' : 'Create Taxonomy'}
184
+ </CmsButton>
185
+ </>
186
+ }
187
+ >
188
+ <form onSubmit={handleSubmit} className="space-y-4">
189
+ {submitError && (
190
+ <div className="rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800">
191
+ {submitError}
192
+ </div>
193
+ )}
194
+
195
+ <div className="space-y-2">
196
+ <Label htmlFor="displayName">
197
+ Display Name <span className="text-destructive">*</span>
198
+ </Label>
199
+ <Input
200
+ id="displayName"
201
+ value={formData.displayName}
202
+ onChange={(e) => handleDisplayNameChange(e.target.value)}
203
+ placeholder="e.g., Categories, Tags"
204
+ disabled={isSubmitting}
205
+ className={errors.displayName ? 'border-destructive' : ''}
206
+ />
207
+ {errors.displayName && (
208
+ <p className="text-xs text-destructive">{errors.displayName}</p>
209
+ )}
210
+ </div>
211
+
212
+ <div className="space-y-2">
213
+ <Label htmlFor="name">
214
+ Slug <span className="text-destructive">*</span>
215
+ </Label>
216
+ <Input
217
+ id="name"
218
+ value={formData.name}
219
+ onChange={(e) => {
220
+ handleChange('name', e.target.value.toLowerCase())
221
+ setSlugManuallyEdited(true)
222
+ }}
223
+ placeholder="e.g., categories, tags"
224
+ disabled={isSubmitting || isEditing}
225
+ className={errors.name ? 'border-destructive' : ''}
226
+ />
227
+ {errors.name && (
228
+ <p className="text-xs text-destructive">{errors.name}</p>
229
+ )}
230
+ {isEditing && (
231
+ <p className="text-xs text-muted-foreground">
232
+ Slug cannot be changed after creation
233
+ </p>
234
+ )}
235
+ </div>
236
+
237
+ <div className="space-y-2">
238
+ <Label htmlFor="description">Description</Label>
239
+ <Textarea
240
+ id="description"
241
+ value={formData.description}
242
+ onChange={(e) => handleChange('description', e.target.value)}
243
+ placeholder="Optional description"
244
+ rows={2}
245
+ disabled={isSubmitting}
246
+ />
247
+ </div>
248
+
249
+ <div className="grid grid-cols-2 gap-4">
250
+ <div className="space-y-2">
251
+ <Label htmlFor="icon">Icon</Label>
252
+ <Input
253
+ id="icon"
254
+ value={formData.icon}
255
+ onChange={(e) => handleChange('icon', e.target.value)}
256
+ placeholder="e.g., 🏷️ or folder"
257
+ disabled={isSubmitting}
258
+ />
259
+ <p className="text-xs text-muted-foreground">Emoji or icon name</p>
260
+ </div>
261
+
262
+ <div className="space-y-2">
263
+ <Label htmlFor="sortOrder">Sort Order</Label>
264
+ <Input
265
+ id="sortOrder"
266
+ type="number"
267
+ value={formData.sortOrder}
268
+ onChange={(e) =>
269
+ handleChange('sortOrder', parseInt(e.target.value) || 0)
270
+ }
271
+ disabled={isSubmitting}
272
+ />
273
+ </div>
274
+ </div>
275
+
276
+ <div className="space-y-4 pt-2">
277
+ <div className="flex items-start gap-3">
278
+ <Checkbox
279
+ id="isHierarchical"
280
+ checked={formData.isHierarchical}
281
+ onCheckedChange={(checked) =>
282
+ handleChange('isHierarchical', checked as boolean)
283
+ }
284
+ disabled={isSubmitting || isEditing}
285
+ />
286
+ <div className="space-y-0.5">
287
+ <Label
288
+ htmlFor="isHierarchical"
289
+ className="cursor-pointer font-medium"
290
+ >
291
+ Hierarchical
292
+ </Label>
293
+ <p className="text-xs text-muted-foreground">
294
+ Terms can have parent-child relationships (like categories)
295
+ </p>
296
+ {isEditing && (
297
+ <p className="text-xs text-amber-600">
298
+ Hierarchy type cannot be changed after creation
299
+ </p>
300
+ )}
301
+ </div>
302
+ </div>
303
+
304
+ <div className="flex items-start gap-3">
305
+ <Checkbox
306
+ id="allowInlineCreation"
307
+ checked={formData.allowInlineCreation}
308
+ onCheckedChange={(checked) =>
309
+ handleChange('allowInlineCreation', checked as boolean)
310
+ }
311
+ disabled={isSubmitting}
312
+ />
313
+ <div className="space-y-0.5">
314
+ <Label
315
+ htmlFor="allowInlineCreation"
316
+ className="cursor-pointer font-medium"
317
+ >
318
+ Allow inline creation
319
+ </Label>
320
+ <p className="text-xs text-muted-foreground">
321
+ Users can create new terms while editing content
322
+ </p>
323
+ </div>
324
+ </div>
325
+
326
+ <div className="flex items-start gap-3">
327
+ <Checkbox
328
+ id="isActive"
329
+ checked={formData.isActive}
330
+ onCheckedChange={(checked) =>
331
+ handleChange('isActive', checked as boolean)
332
+ }
333
+ disabled={isSubmitting}
334
+ />
335
+ <div className="space-y-0.5">
336
+ <Label htmlFor="isActive" className="cursor-pointer font-medium">
337
+ Active
338
+ </Label>
339
+ <p className="text-xs text-muted-foreground">
340
+ Inactive taxonomies are hidden from content editors
341
+ </p>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ </form>
346
+ </CmsDialog>
347
+ )
348
+ }