convex-cms 0.0.5-alpha.4 → 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 (145) hide show
  1. package/README.md +3 -3
  2. package/admin/src/components/Sidebar.tsx +150 -58
  3. package/admin/src/components/ui/collapsible.tsx +7 -0
  4. package/admin/src/embed/components/EmbedSidebar.tsx +163 -68
  5. package/admin-dist/nitro.json +1 -1
  6. package/admin-dist/public/assets/{CmsEmptyState-CkqBIab3.js → CmsEmptyState-Do_erIgn.js} +1 -1
  7. package/admin-dist/public/assets/{CmsPageHeader-CUtl5MMG.js → CmsPageHeader-qDwPGi48.js} +1 -1
  8. package/admin-dist/public/assets/{CmsStatusBadge-CUYFgEe-.js → CmsStatusBadge-Dd9uToHE.js} +1 -1
  9. package/admin-dist/public/assets/{CmsSurface-CsJfAVa3.js → CmsSurface-DBy5Lumx.js} +1 -1
  10. package/admin-dist/public/assets/{CmsToolbar-CnfbcxeP.js → CmsToolbar-D1-Y-7SK.js} +1 -1
  11. package/admin-dist/public/assets/ContentEntryEditor-CWBiIx52.js +4 -0
  12. package/admin-dist/public/assets/{TaxonomyFilter-CWCxC5HZ.js → TaxonomyFilter-CdYQawxb.js} +1 -1
  13. package/admin-dist/public/assets/_contentTypeId-D9VMP6Gs.js +1 -0
  14. package/admin-dist/public/assets/_entryId-2FlCfqE7.js +1 -0
  15. package/admin-dist/public/assets/{alert-CF1BSzGR.js → alert-GxZx0y5c.js} +1 -1
  16. package/admin-dist/public/assets/{badge-CmuOIVKp.js → badge-BAlGIjop.js} +1 -1
  17. package/admin-dist/public/assets/{circle-check-big-BKDVG6DU.js → circle-check-big-CpLxAvEj.js} +1 -1
  18. package/admin-dist/public/assets/{command-XJxnF2Sd.js → command-di7XCqcv.js} +1 -1
  19. package/admin-dist/public/assets/content-D8zELsDG.js +1 -0
  20. package/admin-dist/public/assets/{content-types-CrNEm8Hf.js → content-types-BmzD0krT.js} +2 -2
  21. package/admin-dist/public/assets/globals-BvFfH-v9.css +1 -0
  22. package/admin-dist/public/assets/{index-C7xOwudI.js → index-zqfj4T_v.js} +1 -1
  23. package/admin-dist/public/assets/{label-CHCnXeBk.js → label-B6PPtKR5.js} +1 -1
  24. package/admin-dist/public/assets/{link-2-Bb34judH.js → link-2-W2fVnVOf.js} +1 -1
  25. package/admin-dist/public/assets/{list-9Pzt48ld.js → list-F8O0lZXC.js} +1 -1
  26. package/admin-dist/public/assets/main-dZT72bAG.js +97 -0
  27. package/admin-dist/public/assets/media-CETueFbV.js +1 -0
  28. package/admin-dist/public/assets/new._contentTypeId-BV2-TyyR.js +1 -0
  29. package/admin-dist/public/assets/{plus-Ceef7DHk.js → plus-AABQIF0N.js} +1 -1
  30. package/admin-dist/public/assets/{rotate-ccw-7k7-4VUq.js → rotate-ccw-BZpZtw0N.js} +1 -1
  31. package/admin-dist/public/assets/{scroll-area-CC6wujnp.js → scroll-area-CDfk-zrz.js} +1 -1
  32. package/admin-dist/public/assets/{search-DwoUV2pv.js → search-BvgYr-c9.js} +1 -1
  33. package/admin-dist/public/assets/{select-hOZTp8aC.js → select-BuiHcMzS.js} +1 -1
  34. package/admin-dist/public/assets/settings-DBxbYDvn.js +1 -0
  35. package/admin-dist/public/assets/{switch-jX2pDaNU.js → switch-DiJvolcs.js} +1 -1
  36. package/admin-dist/public/assets/tabs-Cgz6G_Xy.js +1 -0
  37. package/admin-dist/public/assets/{tanstack-adapter-B-Glm4kH.js → tanstack-adapter-BknsSgra.js} +1 -1
  38. package/admin-dist/public/assets/taxonomies-DOErsLl5.js +1 -0
  39. package/admin-dist/public/assets/{textarea-B6SfBmr0.js → textarea-CgggMxUX.js} +1 -1
  40. package/admin-dist/public/assets/{trash-BOCnIznD.js → trash-BU4ANuaW.js} +1 -1
  41. package/admin-dist/public/assets/{triangle-alert-CXFIO_Gu.js → triangle-alert-lvCbwp0s.js} +1 -1
  42. package/admin-dist/public/assets/{useBreadcrumbLabel-_6qBagc3.js → useBreadcrumbLabel-D00rvqjw.js} +1 -1
  43. package/admin-dist/public/assets/{usePermissions-M1ijZ7a6.js → usePermissions-D7tQowaF.js} +1 -1
  44. package/admin-dist/server/_chunks/_libs/@radix-ui/react-collapsible.mjs +144 -0
  45. package/admin-dist/server/_chunks/_libs/@radix-ui/react-slot.mjs +21 -21
  46. package/admin-dist/server/_libs/lucide-react.mjs +131 -124
  47. package/admin-dist/server/_ssr/{CmsButton-DOiTVKQq.mjs → CmsButton-DbzfJru_.mjs} +1 -1
  48. package/admin-dist/server/_ssr/{CmsEmptyState-fbnGt3LD.mjs → CmsEmptyState-CuvcXr3Z.mjs} +3 -3
  49. package/admin-dist/server/_ssr/{CmsPageHeader-DHRrdOZa.mjs → CmsPageHeader-ClNPU7Up.mjs} +1 -1
  50. package/admin-dist/server/_ssr/{CmsStatusBadge-s7obWbKZ.mjs → CmsStatusBadge-CojMbrY7.mjs} +2 -2
  51. package/admin-dist/server/_ssr/{CmsSurface-rFoYjb62.mjs → CmsSurface-Dcv440rp.mjs} +1 -1
  52. package/admin-dist/server/_ssr/{CmsToolbar-zTE45z2q.mjs → CmsToolbar-BKv1nL6u.mjs} +2 -2
  53. package/admin-dist/server/_ssr/{ContentEntryEditor-BLoEjT_m.mjs → ContentEntryEditor-weiXSBdZ.mjs} +35 -51
  54. package/admin-dist/server/_ssr/{TaxonomyFilter-XAtaJC2z.mjs → TaxonomyFilter-BPQ57Mwk.mjs} +7 -7
  55. package/admin-dist/server/_ssr/{_contentTypeId-Csl4822C.mjs → _contentTypeId-DyyauLOs.mjs} +24 -23
  56. package/admin-dist/server/_ssr/{_entryId-D8alLFBx.mjs → _entryId-9Cafwxmw.mjs} +22 -21
  57. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-Dk-FIYPN.mjs +4 -0
  58. package/admin-dist/server/_ssr/{command-C0Di14--.mjs → command-CEf8YBxY.mjs} +1 -1
  59. package/admin-dist/server/_ssr/{content-CT-FPsmV.mjs → content-ZFWVzO25.mjs} +20 -19
  60. package/admin-dist/server/_ssr/{content-types-C8cBFdzE.mjs → content-types-D25lUE-j.mjs} +18 -17
  61. package/admin-dist/server/_ssr/{index-BJtcrEc-.mjs → index-BlSIlH4Z.mjs} +10 -9
  62. package/admin-dist/server/_ssr/index.mjs +2 -2
  63. package/admin-dist/server/_ssr/{label-qn2Afwl4.mjs → label-PblVvdRv.mjs} +1 -1
  64. package/admin-dist/server/_ssr/{media-qv5IAsMZ.mjs → media-CD2_NUMw.mjs} +683 -314
  65. package/admin-dist/server/_ssr/{new._contentTypeId-DdGyrhqs.mjs → new._contentTypeId-dmZy6PBX.mjs} +20 -19
  66. package/admin-dist/server/_ssr/{router-nSVkxb6Y.mjs → router-x6Ab8T4s.mjs} +109 -43
  67. package/admin-dist/server/_ssr/{scroll-area-BCinP455.mjs → scroll-area-BH_1K-WT.mjs} +1 -1
  68. package/admin-dist/server/_ssr/{select-BKQlQScw.mjs → select-CrfEkFJw.mjs} +2 -2
  69. package/admin-dist/server/_ssr/{settings-BCr2KQlk.mjs → settings-DVdsoWoh.mjs} +158 -105
  70. package/admin-dist/server/_ssr/{switch-BaOi42fE.mjs → switch-DX_X8vZl.mjs} +1 -1
  71. package/admin-dist/server/_ssr/{tabs-DYXEi9kq.mjs → tabs-4FWM0sn8.mjs} +3 -3
  72. package/admin-dist/server/_ssr/{tanstack-adapter-Bsz8kha-.mjs → tanstack-adapter-D3ZcKtbY.mjs} +1 -1
  73. package/admin-dist/server/_ssr/{taxonomies-CueMHTbE.mjs → taxonomies-BHFfO9Yr.mjs} +21 -20
  74. package/admin-dist/server/_ssr/{textarea-CI0Jqx2x.mjs → textarea-CZVaroMc.mjs} +1 -1
  75. package/admin-dist/server/_ssr/{trash-DE6W8GoX.mjs → trash-9tUB2KwI.mjs} +14 -13
  76. package/admin-dist/server/_ssr/{useBreadcrumbLabel-B5Yi72lM.mjs → useBreadcrumbLabel-DVme3DSb.mjs} +1 -1
  77. package/admin-dist/server/_ssr/{usePermissions-C3nZ-Izm.mjs → usePermissions-zAQj-ruE.mjs} +1 -1
  78. package/admin-dist/server/index.mjs +188 -188
  79. package/dist/cli/commands/init.d.ts +12 -2
  80. package/dist/cli/commands/init.d.ts.map +1 -1
  81. package/dist/cli/commands/init.js +136 -138
  82. package/dist/cli/commands/init.js.map +1 -1
  83. package/dist/cli/index.js +2 -1
  84. package/dist/cli/index.js.map +1 -1
  85. package/dist/cli/templates/admin.d.ts +10 -0
  86. package/dist/cli/templates/admin.d.ts.map +1 -0
  87. package/dist/cli/templates/admin.js +212 -0
  88. package/dist/cli/templates/admin.js.map +1 -0
  89. package/dist/cli/templates/cmsClient.d.ts +7 -0
  90. package/dist/cli/templates/cmsClient.d.ts.map +1 -0
  91. package/dist/cli/templates/cmsClient.js +36 -0
  92. package/dist/cli/templates/cmsClient.js.map +1 -0
  93. package/dist/cli/templates/cmsConfig.d.ts +7 -0
  94. package/dist/cli/templates/cmsConfig.d.ts.map +1 -0
  95. package/dist/cli/templates/cmsConfig.js +86 -0
  96. package/dist/cli/templates/cmsConfig.js.map +1 -0
  97. package/dist/cli/templates/index.d.ts +10 -0
  98. package/dist/cli/templates/index.d.ts.map +1 -0
  99. package/dist/cli/templates/index.js +10 -0
  100. package/dist/cli/templates/index.js.map +1 -0
  101. package/dist/cli/templates/schemas/blog.d.ts +8 -0
  102. package/dist/cli/templates/schemas/blog.d.ts.map +1 -0
  103. package/dist/cli/templates/schemas/blog.js +103 -0
  104. package/dist/cli/templates/schemas/blog.js.map +1 -0
  105. package/dist/cli/templates/schemas/docs.d.ts +8 -0
  106. package/dist/cli/templates/schemas/docs.d.ts.map +1 -0
  107. package/dist/cli/templates/schemas/docs.js +110 -0
  108. package/dist/cli/templates/schemas/docs.js.map +1 -0
  109. package/dist/cli/templates/schemas/index.d.ts +11 -0
  110. package/dist/cli/templates/schemas/index.d.ts.map +1 -0
  111. package/dist/cli/templates/schemas/index.js +13 -0
  112. package/dist/cli/templates/schemas/index.js.map +1 -0
  113. package/dist/cli/templates/schemas/landing.d.ts +8 -0
  114. package/dist/cli/templates/schemas/landing.d.ts.map +1 -0
  115. package/dist/cli/templates/schemas/landing.js +135 -0
  116. package/dist/cli/templates/schemas/landing.js.map +1 -0
  117. package/dist/cli/utils/fileUtils.d.ts +21 -0
  118. package/dist/cli/utils/fileUtils.d.ts.map +1 -0
  119. package/dist/cli/utils/fileUtils.js +95 -0
  120. package/dist/cli/utils/fileUtils.js.map +1 -0
  121. package/dist/cli/utils/prompts.d.ts +25 -0
  122. package/dist/cli/utils/prompts.d.ts.map +1 -0
  123. package/dist/cli/utils/prompts.js +87 -0
  124. package/dist/cli/utils/prompts.js.map +1 -0
  125. package/dist/client/agentTools.d.ts +1 -1427
  126. package/dist/client/agentTools.d.ts.map +1 -1
  127. package/dist/component/contentTypeMutations.d.ts +1 -1
  128. package/dist/test.d.ts +5 -0
  129. package/dist/test.d.ts.map +1 -1
  130. package/dist/test.js +0 -1
  131. package/dist/test.js.map +1 -1
  132. package/package.json +28 -28
  133. package/admin/README.md +0 -99
  134. package/admin-dist/public/assets/ContentEntryEditor-BU220CCy.js +0 -4
  135. package/admin-dist/public/assets/_contentTypeId-DK8cskRt.js +0 -1
  136. package/admin-dist/public/assets/_entryId-CuVMExbb.js +0 -1
  137. package/admin-dist/public/assets/content-QBUxdxbS.js +0 -1
  138. package/admin-dist/public/assets/globals-B7Wsfh_v.css +0 -1
  139. package/admin-dist/public/assets/main-CjQ2VI9L.js +0 -97
  140. package/admin-dist/public/assets/media-Dc5PWt2Q.js +0 -1
  141. package/admin-dist/public/assets/new._contentTypeId-C_I4YxIa.js +0 -1
  142. package/admin-dist/public/assets/settings-t2PbCZh4.js +0 -1
  143. package/admin-dist/public/assets/tabs-q4EbZk7c.js +0 -1
  144. package/admin-dist/public/assets/taxonomies-kyk5P4ZW.js +0 -1
  145. package/admin-dist/server/_ssr/_tanstack-start-manifest_v-BffZedId.mjs +0 -4
package/README.md CHANGED
@@ -87,7 +87,7 @@ They work together through the same CMS component.
87
87
  ### 1. Install
88
88
 
89
89
  ```bash
90
- npm install convex-cms
90
+ pnpm add convex-cms
91
91
  ```
92
92
 
93
93
  ### 2. Add the Component
@@ -104,7 +104,7 @@ export default app;
104
104
 
105
105
  ### 3. Choose Your Setup
106
106
 
107
- **For Admin UI:** Run `npx convex-cms init` then `npx convex-cms admin`
107
+ **For Admin UI:** Run `pnpm convex-cms init` then `pnpm convex-cms admin`
108
108
  → [Full Admin UI Setup](./docs/guides/admin-ui-setup.md)
109
109
 
110
110
  **For Custom Functions:** Create a CMS client and use it in your functions
@@ -144,7 +144,7 @@ export default app;
144
144
 
145
145
  | Mode | Command | Best For |
146
146
  |------|---------|----------|
147
- | **CLI** | `npx convex-cms admin` | Development |
147
+ | **CLI** | `pnpm convex-cms admin` | Development |
148
148
  | **Embed** | `<CmsAdmin api={api.admin} />` | Production |
149
149
 
150
150
  Both modes call the same functions from your `convex/admin.ts`.
@@ -1,8 +1,17 @@
1
+ import { useState } from "react";
1
2
  import { Link, useRouterState } from "@tanstack/react-router";
2
- import { Layers } from "lucide-react";
3
+ import { useQuery } from "convex/react";
4
+ import { Layers, ChevronDown } from "lucide-react";
3
5
  import { cn } from "~/lib/cn";
4
6
  import { useAdminConfig } from "~/contexts";
5
7
  import { Icon } from "~/lib/icons";
8
+ import { api } from "../../convex/_generated/api";
9
+ import {
10
+ Collapsible,
11
+ CollapsibleTrigger,
12
+ CollapsibleContent,
13
+ } from "~/components/ui/collapsible";
14
+ import { ContentTypeFormModal } from "~/components/ContentTypeFormModal";
6
15
  import type { NavItem } from "~/lib/admin-config";
7
16
 
8
17
  export function Sidebar() {
@@ -11,6 +20,13 @@ export function Sidebar() {
11
20
  const config = useAdminConfig();
12
21
  const { navItems, branding, layout } = config;
13
22
 
23
+ const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
24
+
25
+ const contentTypesResult = useQuery(api.admin.listContentTypes, {
26
+ isActive: true,
27
+ });
28
+ const contentTypes = contentTypesResult?.page ?? [];
29
+
14
30
  const isActive = (to: string, exact?: boolean) => {
15
31
  if (exact) {
16
32
  return currentPath === to;
@@ -18,73 +34,149 @@ export function Sidebar() {
18
34
  return currentPath.startsWith(to);
19
35
  };
20
36
 
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>
37
+ const isContentActive =
38
+ currentPath === "/content" ||
39
+ currentPath.startsWith("/entries/type/") ||
40
+ currentPath.startsWith("/entries/new/") ||
41
+ currentPath.startsWith("/entries/");
42
+
43
+ const renderNavItem = (item: NavItem) => {
44
+ if (item.id === "content") {
45
+ return renderContentMenu(item);
46
+ }
47
+
48
+ return (
49
+ <Link
50
+ key={item.id}
51
+ to={item.path}
52
+ className={cn(
53
+ "flex items-center gap-3 rounded-md px-2 py-2 text-sm font-medium transition-colors",
54
+ isActive(item.path, item.exact)
55
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
56
+ : "text-sidebar-foreground hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground"
57
+ )}
58
+ >
59
+ <Icon name={item.icon} className="size-5" />
60
+ <span className="flex-1">{item.label}</span>
61
+ {item.badge && (
62
+ <span className="rounded-full bg-sidebar-primary px-2 py-0.5 text-xs text-sidebar-primary-foreground">
63
+ {item.badge}
64
+ </span>
65
+ )}
66
+ </Link>
67
+ );
68
+ };
69
+
70
+ const renderContentMenu = (item: NavItem) => (
71
+ <Collapsible key={item.id} defaultOpen={isContentActive}>
72
+ <CollapsibleTrigger
73
+ className={cn(
74
+ "flex w-full items-center gap-3 rounded-md px-2 py-2 text-sm font-medium transition-colors",
75
+ isContentActive
76
+ ? "bg-sidebar-accent text-sidebar-accent-foreground"
77
+ : "text-sidebar-foreground hover:bg-sidebar-accent/50 hover:text-sidebar-accent-foreground",
78
+ "group"
79
+ )}
80
+ >
81
+ <Icon name={item.icon} className="size-5" />
82
+ <span className="flex-1 text-left">{item.label}</span>
83
+ <ChevronDown className="size-4 transition-transform duration-200 group-data-[state=open]:rotate-180" />
84
+ </CollapsibleTrigger>
85
+ <CollapsibleContent>
86
+ <div className="ml-5 mt-1 space-y-1 border-l border-sidebar-border pl-3">
87
+ <Link
88
+ to="/content"
89
+ className={cn(
90
+ "flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors",
91
+ currentPath === "/content"
92
+ ? "bg-sidebar-accent/60 text-sidebar-accent-foreground"
93
+ : "text-sidebar-foreground/80 hover:bg-sidebar-accent/30 hover:text-sidebar-accent-foreground"
94
+ )}
95
+ >
96
+ All Entries
97
+ </Link>
98
+ {contentTypes.map((type) => (
99
+ <Link
100
+ key={type._id}
101
+ to="/entries/type/$contentTypeId"
102
+ params={{ contentTypeId: type._id }}
103
+ className={cn(
104
+ "flex items-center gap-2 rounded-md px-2 py-1.5 text-sm transition-colors",
105
+ currentPath === `/entries/type/${type._id}`
106
+ ? "bg-sidebar-accent/60 text-sidebar-accent-foreground"
107
+ : "text-sidebar-foreground/80 hover:bg-sidebar-accent/30 hover:text-sidebar-accent-foreground"
108
+ )}
109
+ >
110
+ {type.displayName}
111
+ </Link>
112
+ ))}
113
+ {contentTypes.length === 0 && contentTypesResult !== undefined && (
114
+ <button
115
+ type="button"
116
+ onClick={() => setIsCreateModalOpen(true)}
117
+ 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"
118
+ >
119
+ + Create content type
120
+ </button>
121
+ )}
122
+ </div>
123
+ </CollapsibleContent>
124
+ </Collapsible>
40
125
  );
41
126
 
42
127
  const sidebarWidth = layout.sidebarWidth;
43
128
 
44
129
  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" />
130
+ <>
131
+ <aside
132
+ className="fixed inset-y-0 left-0 z-50 flex flex-col border-r border-sidebar-border bg-sidebar"
133
+ style={{ width: sidebarWidth }}
134
+ >
135
+ <div className="flex h-14 items-center gap-2 border-b border-sidebar-border px-4">
136
+ <Link to="/" className="flex items-center gap-2 font-semibold text-sidebar-foreground">
137
+ {branding.logo ? (
138
+ <img src={branding.logo} alt={branding.appName} className="size-8" />
139
+ ) : (
140
+ <div className="flex size-8 items-center justify-center rounded-lg bg-sidebar-primary text-sidebar-primary-foreground">
141
+ <Layers className="size-4" />
142
+ </div>
143
+ )}
144
+ <span className="text-base">{branding.appName}</span>
145
+ </Link>
146
+ </div>
147
+
148
+ <nav className="flex-1 space-y-6 overflow-y-auto p-4">
149
+ {navItems.main.length > 0 && (
150
+ <div className="space-y-1">
151
+ <span className="px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60">
152
+ Main
153
+ </span>
154
+ <div className="space-y-1 pt-2">{navItems.main.map(renderNavItem)}</div>
56
155
  </div>
57
156
  )}
58
- <span className="text-base">{branding.appName}</span>
59
- </Link>
60
- </div>
61
157
 
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
- )}
158
+ {navItems.config.length > 0 && (
159
+ <div className="space-y-1">
160
+ <span className="px-2 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/60">
161
+ Configuration
162
+ </span>
163
+ <div className="space-y-1 pt-2">{navItems.config.map(renderNavItem)}</div>
164
+ </div>
165
+ )}
166
+ </nav>
71
167
 
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>
168
+ <div className="border-t border-sidebar-border p-4">
169
+ <div className="flex items-center justify-between text-xs text-sidebar-foreground/60">
170
+ <span>Version</span>
171
+ <span className="font-mono">0.1.0</span>
78
172
  </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
173
  </div>
87
- </div>
88
- </aside>
174
+ </aside>
175
+
176
+ <ContentTypeFormModal
177
+ isOpen={isCreateModalOpen}
178
+ onClose={() => setIsCreateModalOpen(false)}
179
+ />
180
+ </>
89
181
  );
90
182
  }
@@ -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 };
@@ -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
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "date": "2026-01-24T17:01:45.627Z",
2
+ "date": "2026-01-25T00:44:46.445Z",
3
3
  "preset": "node-server",
4
4
  "framework": {
5
5
  "name": "nitro",
@@ -1,4 +1,4 @@
1
- import{r as i,j as o,af as ge,a8 as L,aa as A,a3 as I,a2 as D,a5 as k,ag as me,a0 as _,ah as xe,a4 as z,ai as he,aj as be,ak as ve,al as Ce,am as ye,c as b,a9 as De}from"./main-CjQ2VI9L.js";import{X as je,C as T,u as ke,a as Ne}from"./badge-CmuOIVKp.js";function Re(e){const t=_e(e),r=i.forwardRef((s,a)=>{const{children:n,...c}=s,l=i.Children.toArray(n),d=l.find(Ie);if(d){const u=d.props.children,p=l.map(f=>f===d?i.Children.count(u)>1?i.Children.only(null):i.isValidElement(u)?u.props.children:null:f);return o.jsx(t,{...c,ref:a,children:i.isValidElement(u)?i.cloneElement(u,void 0,p):null})}return o.jsx(t,{...c,ref:a,children:n})});return r.displayName=`${e}.Slot`,r}function _e(e){const t=i.forwardRef((r,s)=>{const{children:a,...n}=r;if(i.isValidElement(a)){const c=we(a),l=Pe(n,a.props);return a.type!==i.Fragment&&(l.ref=s?ge(s,c):c),i.cloneElement(a,l)}return i.Children.count(a)>1?i.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var Ee=Symbol("radix.slottable");function Ie(e){return i.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===Ee}function Pe(e,t){const r={...t};for(const s in t){const a=e[s],n=t[s];/^on[A-Z]/.test(s)?a&&n?r[s]=(...l)=>{const d=n(...l);return a(...l),d}:a&&(r[s]=a):s==="style"?r[s]={...a,...n}:s==="className"&&(r[s]=[a,n].filter(Boolean).join(" "))}return{...e,...r}}function we(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,r=t&&"isReactWarning"in t&&t.isReactWarning;return r?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,r=t&&"isReactWarning"in t&&t.isReactWarning,r?e.props.ref:e.props.ref||e.ref)}var P="Dialog",[G]=z(P),[Oe,h]=G(P),V=e=>{const{__scopeDialog:t,children:r,open:s,defaultOpen:a,onOpenChange:n,modal:c=!0}=e,l=i.useRef(null),d=i.useRef(null),[u,p]=L({prop:s,defaultProp:a??!1,onChange:n,caller:P});return o.jsx(Oe,{scope:t,triggerRef:l,contentRef:d,contentId:A(),titleId:A(),descriptionId:A(),open:u,onOpenChange:p,onOpenToggle:i.useCallback(()=>p(f=>!f),[p]),modal:c,children:r})};V.displayName=P;var H="DialogTrigger",Se=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(H,r),n=_(t,a.triggerRef);return o.jsx(D.button,{type:"button","aria-haspopup":"dialog","aria-expanded":a.open,"aria-controls":a.contentId,"data-state":W(a.open),...s,ref:n,onClick:k(e.onClick,a.onOpenToggle)})});Se.displayName=H;var M="DialogPortal",[Ae,q]=G(M,{forceMount:void 0}),K=e=>{const{__scopeDialog:t,forceMount:r,children:s,container:a}=e,n=h(M,t);return o.jsx(Ae,{scope:t,forceMount:r,children:i.Children.map(s,c=>o.jsx(I,{present:r||n.open,children:o.jsx(me,{asChild:!0,container:a,children:c})}))})};K.displayName=M;var E="DialogOverlay",U=i.forwardRef((e,t)=>{const r=q(E,e.__scopeDialog),{forceMount:s=r.forceMount,...a}=e,n=h(E,e.__scopeDialog);return n.modal?o.jsx(I,{present:s||n.open,children:o.jsx(Me,{...a,ref:t})}):null});U.displayName=E;var Te=Re("DialogOverlay.RemoveScroll"),Me=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(E,r);return o.jsx(he,{as:Te,allowPinchZoom:!0,shards:[a.contentRef],children:o.jsx(D.div,{"data-state":W(a.open),...s,ref:t,style:{pointerEvents:"auto",...s.style}})})}),N="DialogContent",X=i.forwardRef((e,t)=>{const r=q(N,e.__scopeDialog),{forceMount:s=r.forceMount,...a}=e,n=h(N,e.__scopeDialog);return o.jsx(I,{present:s||n.open,children:n.modal?o.jsx(Fe,{...a,ref:t}):o.jsx(We,{...a,ref:t})})});X.displayName=N;var Fe=i.forwardRef((e,t)=>{const r=h(N,e.__scopeDialog),s=i.useRef(null),a=_(t,r.contentRef,s);return i.useEffect(()=>{const n=s.current;if(n)return xe(n)},[]),o.jsx(Z,{...e,ref:a,trapFocus:r.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:k(e.onCloseAutoFocus,n=>{n.preventDefault(),r.triggerRef.current?.focus()}),onPointerDownOutside:k(e.onPointerDownOutside,n=>{const c=n.detail.originalEvent,l=c.button===0&&c.ctrlKey===!0;(c.button===2||l)&&n.preventDefault()}),onFocusOutside:k(e.onFocusOutside,n=>n.preventDefault())})}),We=i.forwardRef((e,t)=>{const r=h(N,e.__scopeDialog),s=i.useRef(!1),a=i.useRef(!1);return o.jsx(Z,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:n=>{e.onCloseAutoFocus?.(n),n.defaultPrevented||(s.current||r.triggerRef.current?.focus(),n.preventDefault()),s.current=!1,a.current=!1},onInteractOutside:n=>{e.onInteractOutside?.(n),n.defaultPrevented||(s.current=!0,n.detail.originalEvent.type==="pointerdown"&&(a.current=!0));const c=n.target;r.triggerRef.current?.contains(c)&&n.preventDefault(),n.detail.originalEvent.type==="focusin"&&a.current&&n.preventDefault()}})}),Z=i.forwardRef((e,t)=>{const{__scopeDialog:r,trapFocus:s,onOpenAutoFocus:a,onCloseAutoFocus:n,...c}=e,l=h(N,r),d=i.useRef(null),u=_(t,d);return be(),o.jsxs(o.Fragment,{children:[o.jsx(ve,{asChild:!0,loop:!0,trapped:s,onMountAutoFocus:a,onUnmountAutoFocus:n,children:o.jsx(Ce,{role:"dialog",id:l.contentId,"aria-describedby":l.descriptionId,"aria-labelledby":l.titleId,"data-state":W(l.open),...c,ref:u,onDismiss:()=>l.onOpenChange(!1)})}),o.jsxs(o.Fragment,{children:[o.jsx($e,{titleId:l.titleId}),o.jsx(Le,{contentRef:d,descriptionId:l.descriptionId})]})]})}),F="DialogTitle",Y=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(F,r);return o.jsx(D.h2,{id:a.titleId,...s,ref:t})});Y.displayName=F;var J="DialogDescription",Q=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(J,r);return o.jsx(D.p,{id:a.descriptionId,...s,ref:t})});Q.displayName=J;var ee="DialogClose",te=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(ee,r);return o.jsx(D.button,{type:"button",...s,ref:t,onClick:k(e.onClick,()=>a.onOpenChange(!1))})});te.displayName=ee;function W(e){return e?"open":"closed"}var ne="DialogTitleWarning",[ut,oe]=ye(ne,{contentName:N,titleName:F,docsSlug:"dialog"}),$e=({titleId:e})=>{const t=oe(ne),r=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users.
1
+ import{r as i,j as o,ah as ge,aa as L,ac as A,a5 as I,a4 as D,a7 as k,ai as me,a2 as _,aj as xe,a6 as z,ak as he,al as be,am as ve,an as Ce,ao as ye,c as b,ab as De}from"./main-dZT72bAG.js";import{X as je,C as T,u as ke,a as Ne}from"./badge-BAlGIjop.js";function Re(e){const t=_e(e),r=i.forwardRef((s,a)=>{const{children:n,...c}=s,l=i.Children.toArray(n),d=l.find(Ie);if(d){const u=d.props.children,p=l.map(f=>f===d?i.Children.count(u)>1?i.Children.only(null):i.isValidElement(u)?u.props.children:null:f);return o.jsx(t,{...c,ref:a,children:i.isValidElement(u)?i.cloneElement(u,void 0,p):null})}return o.jsx(t,{...c,ref:a,children:n})});return r.displayName=`${e}.Slot`,r}function _e(e){const t=i.forwardRef((r,s)=>{const{children:a,...n}=r;if(i.isValidElement(a)){const c=we(a),l=Pe(n,a.props);return a.type!==i.Fragment&&(l.ref=s?ge(s,c):c),i.cloneElement(a,l)}return i.Children.count(a)>1?i.Children.only(null):null});return t.displayName=`${e}.SlotClone`,t}var Ee=Symbol("radix.slottable");function Ie(e){return i.isValidElement(e)&&typeof e.type=="function"&&"__radixId"in e.type&&e.type.__radixId===Ee}function Pe(e,t){const r={...t};for(const s in t){const a=e[s],n=t[s];/^on[A-Z]/.test(s)?a&&n?r[s]=(...l)=>{const d=n(...l);return a(...l),d}:a&&(r[s]=a):s==="style"?r[s]={...a,...n}:s==="className"&&(r[s]=[a,n].filter(Boolean).join(" "))}return{...e,...r}}function we(e){let t=Object.getOwnPropertyDescriptor(e.props,"ref")?.get,r=t&&"isReactWarning"in t&&t.isReactWarning;return r?e.ref:(t=Object.getOwnPropertyDescriptor(e,"ref")?.get,r=t&&"isReactWarning"in t&&t.isReactWarning,r?e.props.ref:e.props.ref||e.ref)}var P="Dialog",[G]=z(P),[Oe,h]=G(P),V=e=>{const{__scopeDialog:t,children:r,open:s,defaultOpen:a,onOpenChange:n,modal:c=!0}=e,l=i.useRef(null),d=i.useRef(null),[u,p]=L({prop:s,defaultProp:a??!1,onChange:n,caller:P});return o.jsx(Oe,{scope:t,triggerRef:l,contentRef:d,contentId:A(),titleId:A(),descriptionId:A(),open:u,onOpenChange:p,onOpenToggle:i.useCallback(()=>p(f=>!f),[p]),modal:c,children:r})};V.displayName=P;var H="DialogTrigger",Se=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(H,r),n=_(t,a.triggerRef);return o.jsx(D.button,{type:"button","aria-haspopup":"dialog","aria-expanded":a.open,"aria-controls":a.contentId,"data-state":W(a.open),...s,ref:n,onClick:k(e.onClick,a.onOpenToggle)})});Se.displayName=H;var M="DialogPortal",[Ae,q]=G(M,{forceMount:void 0}),K=e=>{const{__scopeDialog:t,forceMount:r,children:s,container:a}=e,n=h(M,t);return o.jsx(Ae,{scope:t,forceMount:r,children:i.Children.map(s,c=>o.jsx(I,{present:r||n.open,children:o.jsx(me,{asChild:!0,container:a,children:c})}))})};K.displayName=M;var E="DialogOverlay",U=i.forwardRef((e,t)=>{const r=q(E,e.__scopeDialog),{forceMount:s=r.forceMount,...a}=e,n=h(E,e.__scopeDialog);return n.modal?o.jsx(I,{present:s||n.open,children:o.jsx(Me,{...a,ref:t})}):null});U.displayName=E;var Te=Re("DialogOverlay.RemoveScroll"),Me=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(E,r);return o.jsx(he,{as:Te,allowPinchZoom:!0,shards:[a.contentRef],children:o.jsx(D.div,{"data-state":W(a.open),...s,ref:t,style:{pointerEvents:"auto",...s.style}})})}),N="DialogContent",X=i.forwardRef((e,t)=>{const r=q(N,e.__scopeDialog),{forceMount:s=r.forceMount,...a}=e,n=h(N,e.__scopeDialog);return o.jsx(I,{present:s||n.open,children:n.modal?o.jsx(Fe,{...a,ref:t}):o.jsx(We,{...a,ref:t})})});X.displayName=N;var Fe=i.forwardRef((e,t)=>{const r=h(N,e.__scopeDialog),s=i.useRef(null),a=_(t,r.contentRef,s);return i.useEffect(()=>{const n=s.current;if(n)return xe(n)},[]),o.jsx(Z,{...e,ref:a,trapFocus:r.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:k(e.onCloseAutoFocus,n=>{n.preventDefault(),r.triggerRef.current?.focus()}),onPointerDownOutside:k(e.onPointerDownOutside,n=>{const c=n.detail.originalEvent,l=c.button===0&&c.ctrlKey===!0;(c.button===2||l)&&n.preventDefault()}),onFocusOutside:k(e.onFocusOutside,n=>n.preventDefault())})}),We=i.forwardRef((e,t)=>{const r=h(N,e.__scopeDialog),s=i.useRef(!1),a=i.useRef(!1);return o.jsx(Z,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:n=>{e.onCloseAutoFocus?.(n),n.defaultPrevented||(s.current||r.triggerRef.current?.focus(),n.preventDefault()),s.current=!1,a.current=!1},onInteractOutside:n=>{e.onInteractOutside?.(n),n.defaultPrevented||(s.current=!0,n.detail.originalEvent.type==="pointerdown"&&(a.current=!0));const c=n.target;r.triggerRef.current?.contains(c)&&n.preventDefault(),n.detail.originalEvent.type==="focusin"&&a.current&&n.preventDefault()}})}),Z=i.forwardRef((e,t)=>{const{__scopeDialog:r,trapFocus:s,onOpenAutoFocus:a,onCloseAutoFocus:n,...c}=e,l=h(N,r),d=i.useRef(null),u=_(t,d);return be(),o.jsxs(o.Fragment,{children:[o.jsx(ve,{asChild:!0,loop:!0,trapped:s,onMountAutoFocus:a,onUnmountAutoFocus:n,children:o.jsx(Ce,{role:"dialog",id:l.contentId,"aria-describedby":l.descriptionId,"aria-labelledby":l.titleId,"data-state":W(l.open),...c,ref:u,onDismiss:()=>l.onOpenChange(!1)})}),o.jsxs(o.Fragment,{children:[o.jsx($e,{titleId:l.titleId}),o.jsx(Le,{contentRef:d,descriptionId:l.descriptionId})]})]})}),F="DialogTitle",Y=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(F,r);return o.jsx(D.h2,{id:a.titleId,...s,ref:t})});Y.displayName=F;var J="DialogDescription",Q=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(J,r);return o.jsx(D.p,{id:a.descriptionId,...s,ref:t})});Q.displayName=J;var ee="DialogClose",te=i.forwardRef((e,t)=>{const{__scopeDialog:r,...s}=e,a=h(ee,r);return o.jsx(D.button,{type:"button",...s,ref:t,onClick:k(e.onClick,()=>a.onOpenChange(!1))})});te.displayName=ee;function W(e){return e?"open":"closed"}var ne="DialogTitleWarning",[ut,oe]=ye(ne,{contentName:N,titleName:F,docsSlug:"dialog"}),$e=({titleId:e})=>{const t=oe(ne),r=`\`${t.contentName}\` requires a \`${t.titleName}\` for the component to be accessible for screen reader users.
2
2
 
3
3
  If you want to hide the \`${t.titleName}\`, you can wrap it with our VisuallyHidden component.
4
4
 
@@ -1 +1 @@
1
- import{j as e,c}from"./main-CjQ2VI9L.js";function x({title:i,description:s,actions:t,breadcrumbs:a,className:l,...r}){return e.jsxs("div",{className:c("mb-6",l),...r,children:[a&&e.jsx("div",{className:"mb-2",children:a}),e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("h1",{className:"text-2xl font-semibold tracking-tight text-foreground",children:i}),s&&e.jsx("p",{className:"text-sm text-muted-foreground",children:s})]}),t&&e.jsx("div",{className:"flex items-center gap-2",children:t})]})]})}export{x as C};
1
+ import{j as e,c}from"./main-dZT72bAG.js";function x({title:i,description:s,actions:t,breadcrumbs:a,className:l,...r}){return e.jsxs("div",{className:c("mb-6",l),...r,children:[a&&e.jsx("div",{className:"mb-2",children:a}),e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{className:"space-y-1",children:[e.jsx("h1",{className:"text-2xl font-semibold tracking-tight text-foreground",children:i}),s&&e.jsx("p",{className:"text-sm text-muted-foreground",children:s})]}),t&&e.jsx("div",{className:"flex items-center gap-2",children:t})]})]})}export{x as C};
@@ -1 +1 @@
1
- import{j as e,c as l}from"./main-CjQ2VI9L.js";import{B as o}from"./badge-CmuOIVKp.js";const i={draft:{label:"Draft",className:"status-draft",icon:e.jsx("svg",{className:"size-3",fill:"currentColor",viewBox:"0 0 8 8",children:e.jsx("circle",{cx:"4",cy:"4",r:"3"})})},published:{label:"Published",className:"status-published",icon:e.jsx("svg",{className:"size-3",fill:"none",stroke:"currentColor",strokeWidth:"2",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 13l4 4L19 7"})})},scheduled:{label:"Scheduled",className:"status-scheduled",icon:e.jsxs("svg",{className:"size-3",fill:"none",stroke:"currentColor",strokeWidth:"2",viewBox:"0 0 24 24",children:[e.jsx("circle",{cx:"12",cy:"12",r:"10"}),e.jsx("path",{strokeLinecap:"round",d:"M12 6v6l4 2"})]})},archived:{label:"Archived",className:"status-archived",icon:e.jsx("svg",{className:"size-3",fill:"none",stroke:"currentColor",strokeWidth:"2",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"})})}},c={gray:"bg-muted text-muted-foreground",yellow:"bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400",blue:"bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400",green:"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400",red:"bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400",purple:"bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-400",orange:"bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-400"};function d(){return e.jsx("svg",{className:"size-3",fill:"currentColor",viewBox:"0 0 8 8",children:e.jsx("circle",{cx:"4",cy:"4",r:"3"})})}function g({status:n,customConfig:s,className:a,...t}){if(s)return e.jsxs(o,{variant:"secondary",className:l("gap-1.5 px-2 py-0.5 text-xs font-medium",c[s.color],a),...t,children:[d(),s.displayName]});const r=i[n];return r?e.jsxs(o,{variant:"secondary",className:l("gap-1.5 px-2 py-0.5 text-xs font-medium",r.className,a),...t,children:[r.icon,r.label]}):e.jsxs(o,{variant:"secondary",className:l("gap-1.5 px-2 py-0.5 text-xs font-medium",c.gray,a),...t,children:[d(),n]})}export{g as C};
1
+ import{j as e,c as l}from"./main-dZT72bAG.js";import{B as o}from"./badge-BAlGIjop.js";const i={draft:{label:"Draft",className:"status-draft",icon:e.jsx("svg",{className:"size-3",fill:"currentColor",viewBox:"0 0 8 8",children:e.jsx("circle",{cx:"4",cy:"4",r:"3"})})},published:{label:"Published",className:"status-published",icon:e.jsx("svg",{className:"size-3",fill:"none",stroke:"currentColor",strokeWidth:"2",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 13l4 4L19 7"})})},scheduled:{label:"Scheduled",className:"status-scheduled",icon:e.jsxs("svg",{className:"size-3",fill:"none",stroke:"currentColor",strokeWidth:"2",viewBox:"0 0 24 24",children:[e.jsx("circle",{cx:"12",cy:"12",r:"10"}),e.jsx("path",{strokeLinecap:"round",d:"M12 6v6l4 2"})]})},archived:{label:"Archived",className:"status-archived",icon:e.jsx("svg",{className:"size-3",fill:"none",stroke:"currentColor",strokeWidth:"2",viewBox:"0 0 24 24",children:e.jsx("path",{strokeLinecap:"round",strokeLinejoin:"round",d:"M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"})})}},c={gray:"bg-muted text-muted-foreground",yellow:"bg-yellow-100 text-yellow-800 dark:bg-yellow-900/30 dark:text-yellow-400",blue:"bg-blue-100 text-blue-800 dark:bg-blue-900/30 dark:text-blue-400",green:"bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-400",red:"bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-400",purple:"bg-purple-100 text-purple-800 dark:bg-purple-900/30 dark:text-purple-400",orange:"bg-orange-100 text-orange-800 dark:bg-orange-900/30 dark:text-orange-400"};function d(){return e.jsx("svg",{className:"size-3",fill:"currentColor",viewBox:"0 0 8 8",children:e.jsx("circle",{cx:"4",cy:"4",r:"3"})})}function g({status:n,customConfig:s,className:a,...t}){if(s)return e.jsxs(o,{variant:"secondary",className:l("gap-1.5 px-2 py-0.5 text-xs font-medium",c[s.color],a),...t,children:[d(),s.displayName]});const r=i[n];return r?e.jsxs(o,{variant:"secondary",className:l("gap-1.5 px-2 py-0.5 text-xs font-medium",r.className,a),...t,children:[r.icon,r.label]}):e.jsxs(o,{variant:"secondary",className:l("gap-1.5 px-2 py-0.5 text-xs font-medium",c.gray,a),...t,children:[d(),n]})}export{g as C};
@@ -1 +1 @@
1
- import{j as r,c as t}from"./main-CjQ2VI9L.js";const l={base:"surface-base",elevated:"surface-elevated",floating:"surface-floating"},m={none:"",sm:"p-3",md:"p-4",lg:"p-6"},u={none:"rounded-none",sm:"rounded-sm",md:"rounded-md",lg:"rounded-lg"};function f({elevation:e="base",padding:s="md",rounded:n="lg",className:a,children:d,...o}){return r.jsx("div",{className:t(l[e],m[s],u[n],a),...o,children:d})}export{f as C};
1
+ import{j as r,c as t}from"./main-dZT72bAG.js";const l={base:"surface-base",elevated:"surface-elevated",floating:"surface-floating"},m={none:"",sm:"p-3",md:"p-4",lg:"p-6"},u={none:"rounded-none",sm:"rounded-sm",md:"rounded-md",lg:"rounded-lg"};function f({elevation:e="base",padding:s="md",rounded:n="lg",className:a,children:d,...o}){return r.jsx("div",{className:t(l[e],m[s],u[n],a),...o,children:d})}export{f as C};
@@ -1 +1 @@
1
- import{j as e,c}from"./main-CjQ2VI9L.js";import{I as i}from"./CmsEmptyState-CkqBIab3.js";import{S as p}from"./search-DwoUV2pv.js";function j({left:t,right:s,search:a,filters:r,actions:l,className:m,children:n,...o}){return e.jsxs("div",{className:c("flex flex-wrap items-center justify-between gap-3 pb-4",m),...o,children:[e.jsxs("div",{className:"flex flex-1 flex-wrap items-center gap-2",children:[a&&e.jsxs("div",{className:"relative w-full max-w-xs",children:[e.jsx(p,{className:"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"}),e.jsx(i,{type:"search",placeholder:a.placeholder??"Search...",value:a.value,onChange:x=>a.onChange(x.target.value),className:"pl-9"})]}),r,t,n]}),(s||l)&&e.jsx("div",{className:"flex items-center gap-2",children:s??l})]})}export{j as C};
1
+ import{j as e,c}from"./main-dZT72bAG.js";import{I as i}from"./CmsEmptyState-Do_erIgn.js";import{S as p}from"./search-BvgYr-c9.js";function j({left:t,right:s,search:a,filters:r,actions:l,className:m,children:n,...o}){return e.jsxs("div",{className:c("flex flex-wrap items-center justify-between gap-3 pb-4",m),...o,children:[e.jsxs("div",{className:"flex flex-1 flex-wrap items-center gap-2",children:[a&&e.jsxs("div",{className:"relative w-full max-w-xs",children:[e.jsx(p,{className:"absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground"}),e.jsx(i,{type:"search",placeholder:a.placeholder??"Search...",value:a.value,onChange:x=>a.onChange(x.target.value),className:"pl-9"})]}),r,t,n]}),(s||l)&&e.jsx("div",{className:"flex items-center gap-2",children:s??l})]})}export{j as C};