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

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 (231) hide show
  1. package/README.md +47 -12
  2. package/admin/src/components/BulkActionBar.tsx +4 -4
  3. package/admin/src/components/ContentEntryEditor.tsx +8 -28
  4. package/admin/src/components/ContentTypeFormModal.tsx +2 -2
  5. package/admin/src/components/Sidebar.tsx +150 -58
  6. package/admin/src/components/TaxonomyEditor.tsx +2 -2
  7. package/admin/src/components/TermTree.tsx +5 -5
  8. package/admin/src/components/VersionCompare.tsx +1 -1
  9. package/admin/src/components/VersionHistory.tsx +2 -2
  10. package/admin/src/components/fields/CategoryField.tsx +1 -1
  11. package/admin/src/components/fields/MediaField.tsx +4 -4
  12. package/admin/src/components/fields/ReferenceField.tsx +4 -4
  13. package/admin/src/components/fields/TagField.tsx +3 -3
  14. package/admin/src/components/filters/TaxonomyFilter.tsx +2 -2
  15. package/admin/src/components/media/MediaAssetEditDialog.tsx +2 -2
  16. package/admin/src/components/media/MediaFolderEditDialog.tsx +1 -1
  17. package/admin/src/components/media/MediaMoveModal.tsx +2 -2
  18. package/admin/src/components/media/MediaTaxonomyPicker.tsx +4 -4
  19. package/admin/src/components/ui/collapsible.tsx +7 -0
  20. package/admin/src/contexts/SettingsConfigContext.tsx +2 -2
  21. package/admin/src/embed/components/EmbedSidebar.tsx +163 -68
  22. package/admin/src/pages/MediaPage.tsx +1102 -8
  23. package/admin/src/pages/SettingsPage.tsx +171 -108
  24. package/admin/src/routes/entries/$entryId.tsx +2 -2
  25. package/admin/src/routes/entries/new.$contentTypeId.tsx +1 -1
  26. package/admin/src/routes/entries/type/$contentTypeId.tsx +6 -6
  27. package/admin/src/routes/media.tsx +23 -1094
  28. package/admin-dist/nitro.json +1 -1
  29. package/admin-dist/public/assets/{CmsEmptyState-CkqBIab3.js → CmsEmptyState-Do_erIgn.js} +1 -1
  30. package/admin-dist/public/assets/{CmsPageHeader-CUtl5MMG.js → CmsPageHeader-qDwPGi48.js} +1 -1
  31. package/admin-dist/public/assets/{CmsStatusBadge-CUYFgEe-.js → CmsStatusBadge-Dd9uToHE.js} +1 -1
  32. package/admin-dist/public/assets/{CmsSurface-CsJfAVa3.js → CmsSurface-DBy5Lumx.js} +1 -1
  33. package/admin-dist/public/assets/{CmsToolbar-CnfbcxeP.js → CmsToolbar-D1-Y-7SK.js} +1 -1
  34. package/admin-dist/public/assets/ContentEntryEditor-CWBiIx52.js +4 -0
  35. package/admin-dist/public/assets/{TaxonomyFilter-CWCxC5HZ.js → TaxonomyFilter-CdYQawxb.js} +1 -1
  36. package/admin-dist/public/assets/_contentTypeId-D9VMP6Gs.js +1 -0
  37. package/admin-dist/public/assets/_entryId-2FlCfqE7.js +1 -0
  38. package/admin-dist/public/assets/{alert-CF1BSzGR.js → alert-GxZx0y5c.js} +1 -1
  39. package/admin-dist/public/assets/{badge-CmuOIVKp.js → badge-BAlGIjop.js} +1 -1
  40. package/admin-dist/public/assets/{circle-check-big-BKDVG6DU.js → circle-check-big-CpLxAvEj.js} +1 -1
  41. package/admin-dist/public/assets/{command-XJxnF2Sd.js → command-di7XCqcv.js} +1 -1
  42. package/admin-dist/public/assets/content-D8zELsDG.js +1 -0
  43. package/admin-dist/public/assets/{content-types-CrNEm8Hf.js → content-types-BmzD0krT.js} +2 -2
  44. package/admin-dist/public/assets/globals-BvFfH-v9.css +1 -0
  45. package/admin-dist/public/assets/{index-C7xOwudI.js → index-zqfj4T_v.js} +1 -1
  46. package/admin-dist/public/assets/{label-CHCnXeBk.js → label-B6PPtKR5.js} +1 -1
  47. package/admin-dist/public/assets/{link-2-Bb34judH.js → link-2-W2fVnVOf.js} +1 -1
  48. package/admin-dist/public/assets/{list-9Pzt48ld.js → list-F8O0lZXC.js} +1 -1
  49. package/admin-dist/public/assets/main-dZT72bAG.js +97 -0
  50. package/admin-dist/public/assets/media-CETueFbV.js +1 -0
  51. package/admin-dist/public/assets/new._contentTypeId-BV2-TyyR.js +1 -0
  52. package/admin-dist/public/assets/{plus-Ceef7DHk.js → plus-AABQIF0N.js} +1 -1
  53. package/admin-dist/public/assets/{rotate-ccw-7k7-4VUq.js → rotate-ccw-BZpZtw0N.js} +1 -1
  54. package/admin-dist/public/assets/{scroll-area-CC6wujnp.js → scroll-area-CDfk-zrz.js} +1 -1
  55. package/admin-dist/public/assets/{search-DwoUV2pv.js → search-BvgYr-c9.js} +1 -1
  56. package/admin-dist/public/assets/{select-hOZTp8aC.js → select-BuiHcMzS.js} +1 -1
  57. package/admin-dist/public/assets/settings-DBxbYDvn.js +1 -0
  58. package/admin-dist/public/assets/{switch-jX2pDaNU.js → switch-DiJvolcs.js} +1 -1
  59. package/admin-dist/public/assets/tabs-Cgz6G_Xy.js +1 -0
  60. package/admin-dist/public/assets/{tanstack-adapter-B-Glm4kH.js → tanstack-adapter-BknsSgra.js} +1 -1
  61. package/admin-dist/public/assets/taxonomies-DOErsLl5.js +1 -0
  62. package/admin-dist/public/assets/{textarea-B6SfBmr0.js → textarea-CgggMxUX.js} +1 -1
  63. package/admin-dist/public/assets/{trash-BOCnIznD.js → trash-BU4ANuaW.js} +1 -1
  64. package/admin-dist/public/assets/{triangle-alert-CXFIO_Gu.js → triangle-alert-lvCbwp0s.js} +1 -1
  65. package/admin-dist/public/assets/{useBreadcrumbLabel-_6qBagc3.js → useBreadcrumbLabel-D00rvqjw.js} +1 -1
  66. package/admin-dist/public/assets/{usePermissions-M1ijZ7a6.js → usePermissions-D7tQowaF.js} +1 -1
  67. package/admin-dist/server/_chunks/_libs/@radix-ui/react-collapsible.mjs +144 -0
  68. package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +21 -21
  69. package/admin-dist/server/_libs/lucide-react.mjs +131 -124
  70. package/admin-dist/server/_ssr/{CmsButton-DOiTVKQq.mjs → CmsButton-DbzfJru_.mjs} +1 -1
  71. package/admin-dist/server/_ssr/{CmsEmptyState-fbnGt3LD.mjs → CmsEmptyState-CuvcXr3Z.mjs} +3 -3
  72. package/admin-dist/server/_ssr/{CmsPageHeader-DHRrdOZa.mjs → CmsPageHeader-ClNPU7Up.mjs} +1 -1
  73. package/admin-dist/server/_ssr/{CmsStatusBadge-s7obWbKZ.mjs → CmsStatusBadge-CojMbrY7.mjs} +2 -2
  74. package/admin-dist/server/_ssr/{CmsSurface-rFoYjb62.mjs → CmsSurface-Dcv440rp.mjs} +1 -1
  75. package/admin-dist/server/_ssr/{CmsToolbar-zTE45z2q.mjs → CmsToolbar-BKv1nL6u.mjs} +2 -2
  76. package/admin-dist/server/_ssr/{ContentEntryEditor-BLoEjT_m.mjs → ContentEntryEditor-weiXSBdZ.mjs} +35 -51
  77. package/admin-dist/server/_ssr/{TaxonomyFilter-XAtaJC2z.mjs → TaxonomyFilter-BPQ57Mwk.mjs} +7 -7
  78. package/admin-dist/server/_ssr/{_contentTypeId-Csl4822C.mjs → _contentTypeId-DyyauLOs.mjs} +24 -23
  79. package/admin-dist/server/_ssr/{_entryId-D8alLFBx.mjs → _entryId-9Cafwxmw.mjs} +22 -21
  80. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dk-FIYPN.mjs +4 -0
  81. package/admin-dist/server/_ssr/{command-C0Di14--.mjs → command-CEf8YBxY.mjs} +1 -1
  82. package/admin-dist/server/_ssr/{content-CT-FPsmV.mjs → content-ZFWVzO25.mjs} +20 -19
  83. package/admin-dist/server/_ssr/{content-types-C8cBFdzE.mjs → content-types-D25lUE-j.mjs} +18 -17
  84. package/admin-dist/server/_ssr/{index-BJtcrEc-.mjs → index-BlSIlH4Z.mjs} +10 -9
  85. package/admin-dist/server/_ssr/index.mjs +2 -2
  86. package/admin-dist/server/_ssr/{label-qn2Afwl4.mjs → label-PblVvdRv.mjs} +1 -1
  87. package/admin-dist/server/_ssr/{media-qv5IAsMZ.mjs → media-CD2_NUMw.mjs} +683 -314
  88. package/admin-dist/server/_ssr/{new._contentTypeId-DdGyrhqs.mjs → new._contentTypeId-dmZy6PBX.mjs} +20 -19
  89. package/admin-dist/server/_ssr/{router-nSVkxb6Y.mjs → router-x6Ab8T4s.mjs} +109 -43
  90. package/admin-dist/server/_ssr/{scroll-area-BCinP455.mjs → scroll-area-BH_1K-WT.mjs} +1 -1
  91. package/admin-dist/server/_ssr/{select-BKQlQScw.mjs → select-CrfEkFJw.mjs} +2 -2
  92. package/admin-dist/server/_ssr/{settings-BCr2KQlk.mjs → settings-DVdsoWoh.mjs} +158 -105
  93. package/admin-dist/server/_ssr/{switch-BaOi42fE.mjs → switch-DX_X8vZl.mjs} +1 -1
  94. package/admin-dist/server/_ssr/{tabs-DYXEi9kq.mjs → tabs-4FWM0sn8.mjs} +3 -3
  95. package/admin-dist/server/_ssr/{tanstack-adapter-Bsz8kha-.mjs → tanstack-adapter-D3ZcKtbY.mjs} +1 -1
  96. package/admin-dist/server/_ssr/{taxonomies-CueMHTbE.mjs → taxonomies-BHFfO9Yr.mjs} +21 -20
  97. package/admin-dist/server/_ssr/{textarea-CI0Jqx2x.mjs → textarea-CZVaroMc.mjs} +1 -1
  98. package/admin-dist/server/_ssr/{trash-DE6W8GoX.mjs → trash-9tUB2KwI.mjs} +14 -13
  99. package/admin-dist/server/_ssr/{useBreadcrumbLabel-B5Yi72lM.mjs → useBreadcrumbLabel-DVme3DSb.mjs} +1 -1
  100. package/admin-dist/server/_ssr/{usePermissions-C3nZ-Izm.mjs → usePermissions-zAQj-ruE.mjs} +1 -1
  101. package/admin-dist/server/index.mjs +188 -188
  102. package/dist/cli/commands/init.d.ts +12 -2
  103. package/dist/cli/commands/init.d.ts.map +1 -1
  104. package/dist/cli/commands/init.js +136 -138
  105. package/dist/cli/commands/init.js.map +1 -1
  106. package/dist/cli/index.js +2 -1
  107. package/dist/cli/index.js.map +1 -1
  108. package/dist/cli/templates/admin.d.ts +10 -0
  109. package/dist/cli/templates/admin.d.ts.map +1 -0
  110. package/dist/cli/templates/admin.js +212 -0
  111. package/dist/cli/templates/admin.js.map +1 -0
  112. package/dist/cli/templates/cmsClient.d.ts +7 -0
  113. package/dist/cli/templates/cmsClient.d.ts.map +1 -0
  114. package/dist/cli/templates/cmsClient.js +36 -0
  115. package/dist/cli/templates/cmsClient.js.map +1 -0
  116. package/dist/cli/templates/cmsConfig.d.ts +7 -0
  117. package/dist/cli/templates/cmsConfig.d.ts.map +1 -0
  118. package/dist/cli/templates/cmsConfig.js +86 -0
  119. package/dist/cli/templates/cmsConfig.js.map +1 -0
  120. package/dist/cli/templates/index.d.ts +10 -0
  121. package/dist/cli/templates/index.d.ts.map +1 -0
  122. package/dist/cli/templates/index.js +10 -0
  123. package/dist/cli/templates/index.js.map +1 -0
  124. package/dist/cli/templates/schemas/blog.d.ts +8 -0
  125. package/dist/cli/templates/schemas/blog.d.ts.map +1 -0
  126. package/dist/cli/templates/schemas/blog.js +103 -0
  127. package/dist/cli/templates/schemas/blog.js.map +1 -0
  128. package/dist/cli/templates/schemas/docs.d.ts +8 -0
  129. package/dist/cli/templates/schemas/docs.d.ts.map +1 -0
  130. package/dist/cli/templates/schemas/docs.js +110 -0
  131. package/dist/cli/templates/schemas/docs.js.map +1 -0
  132. package/dist/cli/templates/schemas/index.d.ts +11 -0
  133. package/dist/cli/templates/schemas/index.d.ts.map +1 -0
  134. package/dist/cli/templates/schemas/index.js +13 -0
  135. package/dist/cli/templates/schemas/index.js.map +1 -0
  136. package/dist/cli/templates/schemas/landing.d.ts +8 -0
  137. package/dist/cli/templates/schemas/landing.d.ts.map +1 -0
  138. package/dist/cli/templates/schemas/landing.js +135 -0
  139. package/dist/cli/templates/schemas/landing.js.map +1 -0
  140. package/dist/cli/utils/fileUtils.d.ts +21 -0
  141. package/dist/cli/utils/fileUtils.d.ts.map +1 -0
  142. package/dist/cli/utils/fileUtils.js +95 -0
  143. package/dist/cli/utils/fileUtils.js.map +1 -0
  144. package/dist/cli/utils/prompts.d.ts +25 -0
  145. package/dist/cli/utils/prompts.d.ts.map +1 -0
  146. package/dist/cli/utils/prompts.js +87 -0
  147. package/dist/cli/utils/prompts.js.map +1 -0
  148. package/dist/client/admin/index.d.ts +75 -2
  149. package/dist/client/admin/index.d.ts.map +1 -1
  150. package/dist/client/admin/index.js +17 -1
  151. package/dist/client/admin/index.js.map +1 -1
  152. package/dist/client/admin/media.d.ts.map +1 -1
  153. package/dist/client/admin/media.js +3 -3
  154. package/dist/client/admin/media.js.map +1 -1
  155. package/dist/client/admin/settings.d.ts +50 -0
  156. package/dist/client/admin/settings.d.ts.map +1 -0
  157. package/dist/client/admin/settings.js +89 -0
  158. package/dist/client/admin/settings.js.map +1 -0
  159. package/dist/client/admin/taxonomies.d.ts.map +1 -1
  160. package/dist/client/admin/trash.d.ts.map +1 -1
  161. package/dist/client/admin/types.d.ts +40 -0
  162. package/dist/client/admin/types.d.ts.map +1 -1
  163. package/dist/client/admin/validators.d.ts +243 -3
  164. package/dist/client/admin/validators.d.ts.map +1 -1
  165. package/dist/client/admin/validators.js +18 -0
  166. package/dist/client/admin/validators.js.map +1 -1
  167. package/dist/client/adminConfig.d.ts +1 -1
  168. package/dist/client/agentTools.d.ts +1 -1427
  169. package/dist/client/agentTools.d.ts.map +1 -1
  170. package/dist/client/config.d.ts +145 -0
  171. package/dist/client/config.d.ts.map +1 -0
  172. package/dist/client/config.js +132 -0
  173. package/dist/client/config.js.map +1 -0
  174. package/dist/client/index.d.ts +3 -1
  175. package/dist/client/index.d.ts.map +1 -1
  176. package/dist/client/index.js +13 -6
  177. package/dist/client/index.js.map +1 -1
  178. package/dist/client/schema/defineContentType.js +1 -1
  179. package/dist/client/schema/defineContentType.js.map +1 -1
  180. package/dist/client/wrapper.d.ts.map +1 -1
  181. package/dist/client/wrapper.js +0 -4
  182. package/dist/client/wrapper.js.map +1 -1
  183. package/dist/component/_generated/api.d.ts +2 -0
  184. package/dist/component/_generated/api.d.ts.map +1 -1
  185. package/dist/component/_generated/api.js.map +1 -1
  186. package/dist/component/_generated/component.d.ts +42 -0
  187. package/dist/component/_generated/component.d.ts.map +1 -1
  188. package/dist/component/contentEntries.d.ts.map +1 -1
  189. package/dist/component/contentEntries.js +0 -2
  190. package/dist/component/contentEntries.js.map +1 -1
  191. package/dist/component/contentEntryMutations.d.ts.map +1 -1
  192. package/dist/component/contentEntryMutations.js +0 -1
  193. package/dist/component/contentEntryMutations.js.map +1 -1
  194. package/dist/component/contentLock.d.ts.map +1 -1
  195. package/dist/component/contentLock.js +0 -2
  196. package/dist/component/contentLock.js.map +1 -1
  197. package/dist/component/contentTypeMutations.d.ts +1 -1
  198. package/dist/component/index.d.ts +2 -1
  199. package/dist/component/index.d.ts.map +1 -1
  200. package/dist/component/index.js +3 -1
  201. package/dist/component/index.js.map +1 -1
  202. package/dist/component/schema.d.ts +18 -1
  203. package/dist/component/schema.d.ts.map +1 -1
  204. package/dist/component/schema.js +6 -1
  205. package/dist/component/schema.js.map +1 -1
  206. package/dist/component/settings.d.ts +60 -0
  207. package/dist/component/settings.d.ts.map +1 -0
  208. package/dist/component/settings.js +126 -0
  209. package/dist/component/settings.js.map +1 -0
  210. package/dist/component/validators.d.ts +36 -0
  211. package/dist/component/validators.d.ts.map +1 -1
  212. package/dist/component/validators.js +15 -0
  213. package/dist/component/validators.js.map +1 -1
  214. package/dist/test.d.ts +16 -2
  215. package/dist/test.d.ts.map +1 -1
  216. package/dist/test.js +2 -6
  217. package/dist/test.js.map +1 -1
  218. package/package.json +31 -22
  219. package/admin/README.md +0 -99
  220. package/admin-dist/public/assets/ContentEntryEditor-BU220CCy.js +0 -4
  221. package/admin-dist/public/assets/_contentTypeId-DK8cskRt.js +0 -1
  222. package/admin-dist/public/assets/_entryId-CuVMExbb.js +0 -1
  223. package/admin-dist/public/assets/content-QBUxdxbS.js +0 -1
  224. package/admin-dist/public/assets/globals-B7Wsfh_v.css +0 -1
  225. package/admin-dist/public/assets/main-CjQ2VI9L.js +0 -97
  226. package/admin-dist/public/assets/media-Dc5PWt2Q.js +0 -1
  227. package/admin-dist/public/assets/new._contentTypeId-C_I4YxIa.js +0 -1
  228. package/admin-dist/public/assets/settings-t2PbCZh4.js +0 -1
  229. package/admin-dist/public/assets/tabs-q4EbZk7c.js +0 -1
  230. package/admin-dist/public/assets/taxonomies-kyk5P4ZW.js +0 -1
  231. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BffZedId.mjs +0 -4
@@ -42,14 +42,14 @@ export function MediaTaxonomyPicker({
42
42
  const suggestionsRef = useRef<HTMLDivElement>(null)
43
43
  const containerRef = useRef<HTMLDivElement>(null)
44
44
 
45
- const currentTerms = useQuery(api.taxonomies.getTermsByMedia, {
45
+ const currentTerms = useQuery(api.admin.getTermsByMedia, {
46
46
  mediaId,
47
47
  taxonomyId,
48
48
  })
49
49
 
50
50
  const selectedTermIds = currentTerms?.map((t: TaxonomyTermDisplay) => t._id) ?? []
51
51
 
52
- const suggestionsResult = useQuery(api.taxonomies.suggestTerms, {
52
+ const suggestionsResult = useQuery(api.admin.suggestTerms, {
53
53
  taxonomyId,
54
54
  query: inputValue,
55
55
  limit: 10,
@@ -57,8 +57,8 @@ export function MediaTaxonomyPicker({
57
57
  })
58
58
  const suggestions = (suggestionsResult ?? []) as TaxonomyTermDisplay[]
59
59
 
60
- const setMediaTermsMutation = useMutation(api.taxonomies.setMediaTerms)
61
- const createTermMutation = useMutation(api.taxonomies.createTermAndAddToMedia)
60
+ const setMediaTermsMutation = useMutation(api.admin.setMediaTerms)
61
+ const createTermMutation = useMutation(api.admin.createTermAndAddToMedia)
62
62
 
63
63
  useEffect(() => {
64
64
  function handleClickOutside(event: MouseEvent) {
@@ -0,0 +1,7 @@
1
+ import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
2
+
3
+ const Collapsible = CollapsiblePrimitive.Root;
4
+ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
5
+ const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
6
+
7
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
@@ -4,7 +4,7 @@ import { api } from "../../convex/_generated/api";
4
4
  import type { AdminConfig } from "~/lib/admin-config";
5
5
  import { AdminConfigProvider } from "./AdminConfigContext";
6
6
 
7
- type Settings = NonNullable<typeof api.settings.get._returnType>;
7
+ type Settings = NonNullable<typeof api.admin.getSettings._returnType>;
8
8
 
9
9
  interface SettingsConfigContextValue {
10
10
  baseConfig: AdminConfig;
@@ -20,7 +20,7 @@ export function SettingsConfigProvider({
20
20
  baseConfig: AdminConfig;
21
21
  children: ReactNode;
22
22
  }) {
23
- const settings = useQuery(api.settings.get);
23
+ const settings = useQuery(api.admin.getSettings);
24
24
 
25
25
  const mergedConfig = useMemo((): AdminConfig => {
26
26
  if (!settings) return baseConfig;
@@ -5,11 +5,20 @@
5
5
  * the EmbedNavigation context for navigation instead of TanStack Router.
6
6
  */
7
7
 
8
- import { Layers } from "lucide-react";
8
+ import { useState } from "react";
9
+ import { useQuery } from "convex/react";
10
+ import { Layers, ChevronDown } from "lucide-react";
9
11
  import { cn } from "../../lib/cn";
10
12
  import { useAdminConfig } from "../../contexts";
11
13
  import { Icon } from "../../lib/icons";
12
14
  import { useEmbedNavigation, type EmbedRoute } from "../navigation";
15
+ import { useApi } from "../contexts/ApiContext";
16
+ import {
17
+ Collapsible,
18
+ CollapsibleTrigger,
19
+ CollapsibleContent,
20
+ } from "../../components/ui/collapsible";
21
+ import { ContentTypeFormModal } from "../../components/ContentTypeFormModal";
13
22
  import type { NavItem } from "../../lib/admin-config";
14
23
 
15
24
  function pathToRoute(path: string): EmbedRoute {
@@ -25,95 +34,181 @@ function pathToRoute(path: string): EmbedRoute {
25
34
  }
26
35
 
27
36
  export function EmbedSidebar() {
28
- const { currentPath, navigate } = useEmbedNavigation();
37
+ const { currentPath, navigate, navigateToContentType } = useEmbedNavigation();
29
38
  const config = useAdminConfig();
30
39
  const { navItems, branding, layout } = config;
40
+ const api = useApi();
41
+
42
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
43
+
44
+ const contentTypesResult = useQuery(api.listContentTypes, {
45
+ isActive: true,
46
+ });
47
+ const contentTypes = contentTypesResult?.page ?? [];
48
+
49
+ const normalizedPath = currentPath.replace(/^\/admin/, "");
31
50
 
32
51
  const isActive = (path: string, exact?: boolean) => {
33
- const normalizedCurrent = currentPath.replace(/^\/admin/, "");
34
52
  if (exact) {
35
- return normalizedCurrent === path;
53
+ return normalizedPath === path;
36
54
  }
37
- return normalizedCurrent.startsWith(path);
55
+ return normalizedPath.startsWith(path);
38
56
  };
39
57
 
58
+ const isContentActive =
59
+ normalizedPath === "/content" ||
60
+ normalizedPath.startsWith("/entries/type/") ||
61
+ normalizedPath.startsWith("/entries/new/") ||
62
+ normalizedPath.startsWith("/entries/");
63
+
40
64
  const handleNavClick = (item: NavItem) => {
41
65
  const route = pathToRoute(item.path);
42
66
  navigate(route);
43
67
  };
44
68
 
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>
69
+ const renderNavItem = (item: NavItem) => {
70
+ if (item.id === "content") {
71
+ return renderContentMenu(item);
72
+ }
73
+
74
+ return (
75
+ <button
76
+ key={item.id}
77
+ type="button"
78
+ onClick={() => handleNavClick(item)}
79
+ className={cn(
80
+ "flex w-full items-center gap-3 rounded-md px-2 py-2 text-left text-sm font-medium transition-colors",
81
+ isActive(item.path, item.exact)
82
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
83
+ : "text-sidebar-foreground hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground"
84
+ )}
85
+ >
86
+ <Icon name={item.icon} className="size-5" />
87
+ <span className="flex-1">{item.label}</span>
88
+ {item.badge && (
89
+ <span className="rounded-full bg-sidebar-primary px-2 py-0.5 text-xs text-sidebar-primary-foreground">
90
+ {item.badge}
91
+ </span>
92
+ )}
93
+ </button>
94
+ );
95
+ };
96
+
97
+ const renderContentMenu = (item: NavItem) => (
98
+ <Collapsible key={item.id} defaultOpen={isContentActive}>
99
+ <CollapsibleTrigger
100
+ className={cn(
101
+ "flex w-full items-center gap-3 rounded-md px-2 py-2 text-sm font-medium transition-colors",
102
+ isContentActive
103
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
104
+ : "text-sidebar-foreground hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
105
+ "group"
106
+ )}
107
+ >
108
+ <Icon name={item.icon} className="size-5" />
109
+ <span className="flex-1 text-left">{item.label}</span>
110
+ <ChevronDown className="size-4 transition-transform duration-200 group-data-[state=open]:rotate-180" />
111
+ </CollapsibleTrigger>
112
+ <CollapsibleContent>
113
+ <div className="ml-5 mt-1 space-y-1 border-l border-sidebar-border pl-3">
114
+ <button
115
+ type="button"
116
+ onClick={() => navigate("content")}
117
+ className={cn(
118
+ "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors",
119
+ normalizedPath === "/content"
120
+ ? "bg-sidebar-accent/60 text-sidebar-accent-foreground"
121
+ : "text-sidebar-foreground/80 hover:bg-sidebar-accent/30 hover:text-sidebar-accent-foreground"
122
+ )}
123
+ >
124
+ All Entries
125
+ </button>
126
+ {contentTypes.map((type) => (
127
+ <button
128
+ key={type._id}
129
+ type="button"
130
+ onClick={() => navigateToContentType(type._id)}
131
+ className={cn(
132
+ "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm transition-colors",
133
+ normalizedPath === `/entries/type/${type._id}`
134
+ ? "bg-sidebar-accent/60 text-sidebar-accent-foreground"
135
+ : "text-sidebar-foreground/80 hover:bg-sidebar-accent/30 hover:text-sidebar-accent-foreground"
136
+ )}
137
+ >
138
+ {type.displayName}
139
+ </button>
140
+ ))}
141
+ {contentTypes.length === 0 && contentTypesResult !== undefined && (
142
+ <button
143
+ type="button"
144
+ onClick={() => setIsCreateModalOpen(true)}
145
+ className="flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm text-sidebar-foreground/60 hover:bg-sidebar-accent/30 hover:text-sidebar-accent-foreground"
146
+ >
147
+ + Create content type
148
+ </button>
149
+ )}
150
+ </div>
151
+ </CollapsibleContent>
152
+ </Collapsible>
65
153
  );
66
154
 
67
155
  const sidebarWidth = layout.sidebarWidth;
68
156
 
69
157
  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" />
158
+ <>
159
+ <aside
160
+ className="fixed inset-y-0 left-0 z-50 flex flex-col border-r border-sidebar-border bg-sidebar"
161
+ style={{ width: sidebarWidth }}
162
+ >
163
+ <div className="flex h-14 items-center gap-2 border-b border-sidebar-border px-4">
164
+ <button
165
+ type="button"
166
+ onClick={() => navigate("dashboard")}
167
+ className="flex items-center gap-2 font-semibold text-sidebar-foreground"
168
+ >
169
+ {branding.logo ? (
170
+ <img src={branding.logo} alt={branding.appName} className="size-8" />
171
+ ) : (
172
+ <div className="flex size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
173
+ <Layers className="size-4" />
174
+ </div>
175
+ )}
176
+ <span className="text-base">{branding.appName}</span>
177
+ </button>
178
+ </div>
179
+
180
+ <nav className="flex-1 space-y-6 overflow-y-auto p-4">
181
+ {navItems.main.length > 0 && (
182
+ <div className="space-y-1">
183
+ <span className="px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60">
184
+ Main
185
+ </span>
186
+ <div className="space-y-1 pt-2">{navItems.main.map(renderNavItem)}</div>
85
187
  </div>
86
188
  )}
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
189
 
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>
190
+ {navItems.config.length > 0 && (
191
+ <div className="space-y-1">
192
+ <span className="px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60">
193
+ Configuration
194
+ </span>
195
+ <div className="space-y-1 pt-2">{navItems.config.map(renderNavItem)}</div>
196
+ </div>
197
+ )}
198
+ </nav>
110
199
 
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>
200
+ <div className="border-t border-sidebar-border p-4">
201
+ <div className="flex items-center justify-between text-xs text-sidebar-foreground/60">
202
+ <span>Version</span>
203
+ <span className="font-mono">0.1.0</span>
204
+ </div>
115
205
  </div>
116
- </div>
117
- </aside>
206
+ </aside>
207
+
208
+ <ContentTypeFormModal
209
+ isOpen={isCreateModalOpen}
210
+ onClose={() => setIsCreateModalOpen(false)}
211
+ />
212
+ </>
118
213
  );
119
214
  }