@actuate-media/cms-admin 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +148 -2
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/components/posts-badges.test.d.ts +2 -0
- package/dist/__tests__/components/posts-badges.test.d.ts.map +1 -0
- package/dist/__tests__/components/posts-badges.test.js +68 -0
- package/dist/__tests__/components/posts-badges.test.js.map +1 -0
- package/dist/__tests__/layout/sidebar-posts.test.d.ts +2 -0
- package/dist/__tests__/layout/sidebar-posts.test.d.ts.map +1 -0
- package/dist/__tests__/layout/sidebar-posts.test.js +162 -0
- package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -0
- package/dist/__tests__/lib/page-editor-service.test.d.ts +2 -0
- package/dist/__tests__/lib/page-editor-service.test.d.ts.map +1 -0
- package/dist/__tests__/lib/page-editor-service.test.js +137 -0
- package/dist/__tests__/lib/page-editor-service.test.js.map +1 -0
- package/dist/__tests__/lib/pages-service.test.d.ts +2 -0
- package/dist/__tests__/lib/pages-service.test.d.ts.map +1 -0
- package/dist/__tests__/lib/pages-service.test.js +140 -0
- package/dist/__tests__/lib/pages-service.test.js.map +1 -0
- package/dist/__tests__/lib/post-editor-service.test.d.ts +2 -0
- package/dist/__tests__/lib/post-editor-service.test.d.ts.map +1 -0
- package/dist/__tests__/lib/post-editor-service.test.js +185 -0
- package/dist/__tests__/lib/post-editor-service.test.js.map +1 -0
- package/dist/__tests__/lib/posts-service.test.d.ts +2 -0
- package/dist/__tests__/lib/posts-service.test.d.ts.map +1 -0
- package/dist/__tests__/lib/posts-service.test.js +244 -0
- package/dist/__tests__/lib/posts-service.test.js.map +1 -0
- package/dist/__tests__/views/posts-list-view.test.d.ts +2 -0
- package/dist/__tests__/views/posts-list-view.test.d.ts.map +1 -0
- package/dist/__tests__/views/posts-list-view.test.js +98 -0
- package/dist/__tests__/views/posts-list-view.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/components/pages/tag-badges.d.ts +21 -0
- package/dist/components/pages/tag-badges.d.ts.map +1 -0
- package/dist/components/pages/tag-badges.js +56 -0
- package/dist/components/pages/tag-badges.js.map +1 -0
- package/dist/components/posts/badges.d.ts +25 -0
- package/dist/components/posts/badges.d.ts.map +1 -0
- package/dist/components/posts/badges.js +123 -0
- package/dist/components/posts/badges.js.map +1 -0
- package/dist/components/posts/icons.d.ts +12 -0
- package/dist/components/posts/icons.d.ts.map +1 -0
- package/dist/components/posts/icons.js +26 -0
- package/dist/components/posts/icons.js.map +1 -0
- package/dist/layout/Header.d.ts +14 -1
- package/dist/layout/Header.d.ts.map +1 -1
- package/dist/layout/Header.js +10 -3
- package/dist/layout/Header.js.map +1 -1
- package/dist/layout/Layout.d.ts.map +1 -1
- package/dist/layout/Layout.js +78 -2
- package/dist/layout/Layout.js.map +1 -1
- package/dist/layout/Sidebar.d.ts +16 -0
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +176 -49
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/lib/page-editor-service.d.ts +45 -0
- package/dist/lib/page-editor-service.d.ts.map +1 -0
- package/dist/lib/page-editor-service.js +153 -0
- package/dist/lib/page-editor-service.js.map +1 -0
- package/dist/lib/pages-service.d.ts +143 -0
- package/dist/lib/pages-service.d.ts.map +1 -0
- package/dist/lib/pages-service.js +444 -0
- package/dist/lib/pages-service.js.map +1 -0
- package/dist/lib/post-editor-service.d.ts +57 -0
- package/dist/lib/post-editor-service.d.ts.map +1 -0
- package/dist/lib/post-editor-service.js +234 -0
- package/dist/lib/post-editor-service.js.map +1 -0
- package/dist/lib/posts-service.d.ts +73 -0
- package/dist/lib/posts-service.d.ts.map +1 -0
- package/dist/lib/posts-service.js +245 -0
- package/dist/lib/posts-service.js.map +1 -0
- package/dist/lib/preview-link.d.ts +32 -0
- package/dist/lib/preview-link.d.ts.map +1 -0
- package/dist/lib/preview-link.js +40 -0
- package/dist/lib/preview-link.js.map +1 -0
- package/dist/router/index.d.ts +1 -0
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +9 -1
- package/dist/router/index.js.map +1 -1
- package/dist/views/Dashboard.d.ts.map +1 -1
- package/dist/views/Dashboard.js +147 -62
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/Pages/PageTagManager.d.ts +14 -0
- package/dist/views/Pages/PageTagManager.d.ts.map +1 -0
- package/dist/views/Pages/PageTagManager.js +145 -0
- package/dist/views/Pages/PageTagManager.js.map +1 -0
- package/dist/views/Pages/PagesListView.d.ts +18 -0
- package/dist/views/Pages/PagesListView.d.ts.map +1 -0
- package/dist/views/Pages/PagesListView.js +287 -0
- package/dist/views/Pages/PagesListView.js.map +1 -0
- package/dist/views/Posts/NewPostPicker.d.ts +16 -0
- package/dist/views/Posts/NewPostPicker.d.ts.map +1 -0
- package/dist/views/Posts/NewPostPicker.js +59 -0
- package/dist/views/Posts/NewPostPicker.js.map +1 -0
- package/dist/views/Posts/NewPostTypeInstructions.d.ts +18 -0
- package/dist/views/Posts/NewPostTypeInstructions.d.ts.map +1 -0
- package/dist/views/Posts/NewPostTypeInstructions.js +147 -0
- package/dist/views/Posts/NewPostTypeInstructions.js.map +1 -0
- package/dist/views/Posts/PostTypesView.d.ts +26 -0
- package/dist/views/Posts/PostTypesView.d.ts.map +1 -0
- package/dist/views/Posts/PostTypesView.js +52 -0
- package/dist/views/Posts/PostTypesView.js.map +1 -0
- package/dist/views/Posts/PostsListView.d.ts +25 -0
- package/dist/views/Posts/PostsListView.d.ts.map +1 -0
- package/dist/views/Posts/PostsListView.js +239 -0
- package/dist/views/Posts/PostsListView.js.map +1 -0
- package/dist/views/Posts/post-type-presets.d.ts +32 -0
- package/dist/views/Posts/post-type-presets.d.ts.map +1 -0
- package/dist/views/Posts/post-type-presets.js +72 -0
- package/dist/views/Posts/post-type-presets.js.map +1 -0
- package/dist/views/page-builder/Widgets.d.ts +13 -0
- package/dist/views/page-builder/Widgets.d.ts.map +1 -0
- package/dist/views/page-builder/Widgets.js +15 -0
- package/dist/views/page-builder/Widgets.js.map +1 -0
- package/dist/views/page-editor/AddSectionModal.d.ts +20 -0
- package/dist/views/page-editor/AddSectionModal.d.ts.map +1 -0
- package/dist/views/page-editor/AddSectionModal.js +22 -0
- package/dist/views/page-editor/AddSectionModal.js.map +1 -0
- package/dist/views/page-editor/EditorCanvas.d.ts +18 -0
- package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -0
- package/dist/views/page-editor/EditorCanvas.js +64 -0
- package/dist/views/page-editor/EditorCanvas.js.map +1 -0
- package/dist/views/page-editor/EditorTopBar.d.ts +30 -0
- package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -0
- package/dist/views/page-editor/EditorTopBar.js +42 -0
- package/dist/views/page-editor/EditorTopBar.js.map +1 -0
- package/dist/views/page-editor/PagePreview.d.ts +13 -0
- package/dist/views/page-editor/PagePreview.d.ts.map +1 -0
- package/dist/views/page-editor/PagePreview.js +37 -0
- package/dist/views/page-editor/PagePreview.js.map +1 -0
- package/dist/views/page-editor/PageSectionEditor.d.ts +16 -0
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -0
- package/dist/views/page-editor/PageSectionEditor.js +272 -0
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -0
- package/dist/views/page-editor/PageSettingsModal.d.ts +12 -0
- package/dist/views/page-editor/PageSettingsModal.d.ts.map +1 -0
- package/dist/views/page-editor/PageSettingsModal.js +52 -0
- package/dist/views/page-editor/PageSettingsModal.js.map +1 -0
- package/dist/views/page-editor/SectionInspector.d.ts +17 -0
- package/dist/views/page-editor/SectionInspector.d.ts.map +1 -0
- package/dist/views/page-editor/SectionInspector.js +98 -0
- package/dist/views/page-editor/SectionInspector.js.map +1 -0
- package/dist/views/page-editor/SectionsPanel.d.ts +15 -0
- package/dist/views/page-editor/SectionsPanel.d.ts.map +1 -0
- package/dist/views/page-editor/SectionsPanel.js +43 -0
- package/dist/views/page-editor/SectionsPanel.js.map +1 -0
- package/dist/views/page-editor/ValidationSummary.d.ts +9 -0
- package/dist/views/page-editor/ValidationSummary.d.ts.map +1 -0
- package/dist/views/page-editor/ValidationSummary.js +10 -0
- package/dist/views/page-editor/ValidationSummary.js.map +1 -0
- package/dist/views/page-editor/section-icons.d.ts +4 -0
- package/dist/views/page-editor/section-icons.d.ts.map +1 -0
- package/dist/views/page-editor/section-icons.js +20 -0
- package/dist/views/page-editor/section-icons.js.map +1 -0
- package/dist/views/page-editor/section-types.d.ts +11 -0
- package/dist/views/page-editor/section-types.d.ts.map +1 -0
- package/dist/views/page-editor/section-types.js +10 -0
- package/dist/views/page-editor/section-types.js.map +1 -0
- package/dist/views/page-editor/sections/ArticleBodySection.d.ts +13 -0
- package/dist/views/page-editor/sections/ArticleBodySection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/ArticleBodySection.js +23 -0
- package/dist/views/page-editor/sections/ArticleBodySection.js.map +1 -0
- package/dist/views/page-editor/sections/AuthorBioSection.d.ts +8 -0
- package/dist/views/page-editor/sections/AuthorBioSection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/AuthorBioSection.js +21 -0
- package/dist/views/page-editor/sections/AuthorBioSection.js.map +1 -0
- package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts +5 -0
- package/dist/views/page-editor/sections/ByTheNumbersSection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/ByTheNumbersSection.js +10 -0
- package/dist/views/page-editor/sections/ByTheNumbersSection.js.map +1 -0
- package/dist/views/page-editor/sections/CtaBannerSection.d.ts +5 -0
- package/dist/views/page-editor/sections/CtaBannerSection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/CtaBannerSection.js +10 -0
- package/dist/views/page-editor/sections/CtaBannerSection.js.map +1 -0
- package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts +5 -0
- package/dist/views/page-editor/sections/FeatureHighlightSection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/FeatureHighlightSection.js +18 -0
- package/dist/views/page-editor/sections/FeatureHighlightSection.js.map +1 -0
- package/dist/views/page-editor/sections/GallerySection.d.ts +6 -0
- package/dist/views/page-editor/sections/GallerySection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/GallerySection.js +17 -0
- package/dist/views/page-editor/sections/GallerySection.js.map +1 -0
- package/dist/views/page-editor/sections/HeroBannerSection.d.ts +5 -0
- package/dist/views/page-editor/sections/HeroBannerSection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/HeroBannerSection.js +11 -0
- package/dist/views/page-editor/sections/HeroBannerSection.js.map +1 -0
- package/dist/views/page-editor/sections/MissionSection.d.ts +6 -0
- package/dist/views/page-editor/sections/MissionSection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/MissionSection.js +12 -0
- package/dist/views/page-editor/sections/MissionSection.js.map +1 -0
- package/dist/views/page-editor/sections/PullQuoteSection.d.ts +6 -0
- package/dist/views/page-editor/sections/PullQuoteSection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/PullQuoteSection.js +13 -0
- package/dist/views/page-editor/sections/PullQuoteSection.js.map +1 -0
- package/dist/views/page-editor/sections/RelatedPostsSection.d.ts +12 -0
- package/dist/views/page-editor/sections/RelatedPostsSection.d.ts.map +1 -0
- package/dist/views/page-editor/sections/RelatedPostsSection.js +23 -0
- package/dist/views/page-editor/sections/RelatedPostsSection.js.map +1 -0
- package/dist/views/page-editor/sections/SectionRenderer.d.ts +22 -0
- package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -0
- package/dist/views/page-editor/sections/SectionRenderer.js +52 -0
- package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -0
- package/dist/views/page-editor/sections/index.d.ts +14 -0
- package/dist/views/page-editor/sections/index.d.ts.map +1 -0
- package/dist/views/page-editor/sections/index.js +12 -0
- package/dist/views/page-editor/sections/index.js.map +1 -0
- package/dist/views/page-editor/sections/parts.d.ts +55 -0
- package/dist/views/page-editor/sections/parts.d.ts.map +1 -0
- package/dist/views/page-editor/sections/parts.js +60 -0
- package/dist/views/page-editor/sections/parts.js.map +1 -0
- package/dist/views/page-editor/viewports.d.ts +14 -0
- package/dist/views/page-editor/viewports.d.ts.map +1 -0
- package/dist/views/page-editor/viewports.js +12 -0
- package/dist/views/page-editor/viewports.js.map +1 -0
- package/dist/views/post-editor/PostEditorCanvas.d.ts +21 -0
- package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -0
- package/dist/views/post-editor/PostEditorCanvas.js +58 -0
- package/dist/views/post-editor/PostEditorCanvas.js.map +1 -0
- package/dist/views/post-editor/PostFieldsModal.d.ts +12 -0
- package/dist/views/post-editor/PostFieldsModal.d.ts.map +1 -0
- package/dist/views/post-editor/PostFieldsModal.js +53 -0
- package/dist/views/post-editor/PostFieldsModal.js.map +1 -0
- package/dist/views/post-editor/PostHeader.d.ts +15 -0
- package/dist/views/post-editor/PostHeader.d.ts.map +1 -0
- package/dist/views/post-editor/PostHeader.js +27 -0
- package/dist/views/post-editor/PostHeader.js.map +1 -0
- package/dist/views/post-editor/PostHeaderPanel.d.ts +11 -0
- package/dist/views/post-editor/PostHeaderPanel.d.ts.map +1 -0
- package/dist/views/post-editor/PostHeaderPanel.js +21 -0
- package/dist/views/post-editor/PostHeaderPanel.js.map +1 -0
- package/dist/views/post-editor/PostPreview.d.ts +14 -0
- package/dist/views/post-editor/PostPreview.d.ts.map +1 -0
- package/dist/views/post-editor/PostPreview.js +53 -0
- package/dist/views/post-editor/PostPreview.js.map +1 -0
- package/dist/views/post-editor/PostSectionEditor.d.ts +17 -0
- package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -0
- package/dist/views/post-editor/PostSectionEditor.js +290 -0
- package/dist/views/post-editor/PostSectionEditor.js.map +1 -0
- package/dist/views/post-editor/PostTemplateEditor.d.ts +9 -0
- package/dist/views/post-editor/PostTemplateEditor.d.ts.map +1 -0
- package/dist/views/post-editor/PostTemplateEditor.js +144 -0
- package/dist/views/post-editor/PostTemplateEditor.js.map +1 -0
- package/package.json +2 -2
- package/src/AdminRoot.tsx +213 -2
- package/src/__tests__/components/posts-badges.test.tsx +77 -0
- package/src/__tests__/layout/sidebar-posts.test.ts +179 -0
- package/src/__tests__/lib/page-editor-service.test.ts +173 -0
- package/src/__tests__/lib/pages-service.test.ts +157 -0
- package/src/__tests__/lib/post-editor-service.test.ts +215 -0
- package/src/__tests__/lib/posts-service.test.ts +279 -0
- package/src/__tests__/views/posts-list-view.test.ts +110 -0
- package/src/components/pages/tag-badges.tsx +104 -0
- package/src/components/posts/badges.tsx +170 -0
- package/src/components/posts/icons.ts +40 -0
- package/src/layout/Header.tsx +90 -43
- package/src/layout/Layout.tsx +166 -1
- package/src/layout/Sidebar.tsx +287 -101
- package/src/lib/page-editor-service.ts +233 -0
- package/src/lib/pages-service.ts +599 -0
- package/src/lib/post-editor-service.ts +342 -0
- package/src/lib/posts-service.ts +366 -0
- package/src/lib/preview-link.ts +63 -0
- package/src/router/index.ts +10 -1
- package/src/views/Dashboard.tsx +270 -131
- package/src/views/Pages/PageTagManager.tsx +436 -0
- package/src/views/Pages/PagesListView.tsx +944 -0
- package/src/views/Posts/NewPostPicker.tsx +147 -0
- package/src/views/Posts/NewPostTypeInstructions.tsx +344 -0
- package/src/views/Posts/PostTypesView.tsx +194 -0
- package/src/views/Posts/PostsListView.tsx +869 -0
- package/src/views/Posts/post-type-presets.ts +105 -0
- package/src/views/page-builder/Widgets.tsx +44 -0
- package/src/views/page-editor/AddSectionModal.tsx +56 -0
- package/src/views/page-editor/EditorCanvas.tsx +127 -0
- package/src/views/page-editor/EditorTopBar.tsx +233 -0
- package/src/views/page-editor/PagePreview.tsx +96 -0
- package/src/views/page-editor/PageSectionEditor.tsx +476 -0
- package/src/views/page-editor/PageSettingsModal.tsx +154 -0
- package/src/views/page-editor/SectionInspector.tsx +599 -0
- package/src/views/page-editor/SectionsPanel.tsx +270 -0
- package/src/views/page-editor/ValidationSummary.tsx +53 -0
- package/src/views/page-editor/section-icons.ts +33 -0
- package/src/views/page-editor/section-types.ts +27 -0
- package/src/views/page-editor/sections/ArticleBodySection.tsx +57 -0
- package/src/views/page-editor/sections/AuthorBioSection.tsx +61 -0
- package/src/views/page-editor/sections/ByTheNumbersSection.tsx +41 -0
- package/src/views/page-editor/sections/CtaBannerSection.tsx +29 -0
- package/src/views/page-editor/sections/FeatureHighlightSection.tsx +63 -0
- package/src/views/page-editor/sections/GallerySection.tsx +54 -0
- package/src/views/page-editor/sections/HeroBannerSection.tsx +44 -0
- package/src/views/page-editor/sections/MissionSection.tsx +22 -0
- package/src/views/page-editor/sections/PullQuoteSection.tsx +27 -0
- package/src/views/page-editor/sections/RelatedPostsSection.tsx +55 -0
- package/src/views/page-editor/sections/SectionRenderer.tsx +102 -0
- package/src/views/page-editor/sections/index.ts +13 -0
- package/src/views/page-editor/sections/parts.tsx +113 -0
- package/src/views/page-editor/viewports.ts +25 -0
- package/src/views/post-editor/PostEditorCanvas.tsx +123 -0
- package/src/views/post-editor/PostFieldsModal.tsx +196 -0
- package/src/views/post-editor/PostHeader.tsx +87 -0
- package/src/views/post-editor/PostHeaderPanel.tsx +107 -0
- package/src/views/post-editor/PostPreview.tsx +114 -0
- package/src/views/post-editor/PostSectionEditor.tsx +506 -0
- package/src/views/post-editor/PostTemplateEditor.tsx +347 -0
package/src/layout/Sidebar.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import type { ReactNode } from 'react'
|
|
3
4
|
import {
|
|
4
5
|
LayoutDashboard,
|
|
5
6
|
FileText,
|
|
@@ -7,7 +8,6 @@ import {
|
|
|
7
8
|
Image,
|
|
8
9
|
Settings,
|
|
9
10
|
ChevronLeft,
|
|
10
|
-
ChevronRight,
|
|
11
11
|
ClipboardList,
|
|
12
12
|
Users,
|
|
13
13
|
Search as SearchIcon,
|
|
@@ -19,10 +19,14 @@ import {
|
|
|
19
19
|
PanelTop,
|
|
20
20
|
PanelBottom,
|
|
21
21
|
Layers,
|
|
22
|
+
LayoutGrid,
|
|
22
23
|
Code2,
|
|
23
|
-
LayoutTemplate,
|
|
24
24
|
Library,
|
|
25
25
|
KeyRound,
|
|
26
|
+
ListTree,
|
|
27
|
+
Menu as MenuIcon,
|
|
28
|
+
PanelLeftClose,
|
|
29
|
+
PanelLeftOpen,
|
|
26
30
|
} from 'lucide-react'
|
|
27
31
|
import type { LucideIcon } from 'lucide-react'
|
|
28
32
|
import { ActuateBrandLogo } from '../assets/actuate-logo.js'
|
|
@@ -48,11 +52,6 @@ function ActuateMark({ className }: { className?: string }) {
|
|
|
48
52
|
)
|
|
49
53
|
}
|
|
50
54
|
|
|
51
|
-
/**
|
|
52
|
-
* Full Actuate Media lockup. Inline SVG with a transparent background so it
|
|
53
|
-
* sits naturally on whatever surface the sidebar uses (light or dark theme,
|
|
54
|
-
* custom branding background, etc.).
|
|
55
|
-
*/
|
|
56
55
|
function ActuateWordmark({ className }: { className?: string }) {
|
|
57
56
|
return <ActuateBrandLogo className={className} />
|
|
58
57
|
}
|
|
@@ -65,6 +64,9 @@ const ICON_MAP: Record<string, LucideIcon> = {
|
|
|
65
64
|
book: BookOpen,
|
|
66
65
|
help: HelpCircle,
|
|
67
66
|
newspaper: Newspaper,
|
|
67
|
+
navigation: ListTree,
|
|
68
|
+
menu: MenuIcon,
|
|
69
|
+
'list-tree': ListTree,
|
|
68
70
|
PanelTop: PanelTop,
|
|
69
71
|
PanelBottom: PanelBottom,
|
|
70
72
|
Layers: Layers,
|
|
@@ -91,9 +93,7 @@ function BrandLogo({ config, collapsed }: { config?: any; collapsed: boolean })
|
|
|
91
93
|
className="h-8 w-auto shrink-0 object-contain"
|
|
92
94
|
/>
|
|
93
95
|
{brandName && (
|
|
94
|
-
<span className="text-sidebar-foreground truncate text-sm font-
|
|
95
|
-
{brandName}
|
|
96
|
-
</span>
|
|
96
|
+
<span className="text-sidebar-foreground truncate text-sm font-medium">{brandName}</span>
|
|
97
97
|
)}
|
|
98
98
|
</div>
|
|
99
99
|
)
|
|
@@ -103,7 +103,7 @@ function BrandLogo({ config, collapsed }: { config?: any; collapsed: boolean })
|
|
|
103
103
|
return (
|
|
104
104
|
<div className="flex min-w-0 items-center gap-2.5">
|
|
105
105
|
<ActuateMark className="h-7 w-7 shrink-0" />
|
|
106
|
-
<span className="text-sidebar-foreground truncate text-sm font-
|
|
106
|
+
<span className="text-sidebar-foreground truncate text-sm font-medium">{brandName}</span>
|
|
107
107
|
</div>
|
|
108
108
|
)
|
|
109
109
|
}
|
|
@@ -111,16 +111,54 @@ function BrandLogo({ config, collapsed }: { config?: any; collapsed: boolean })
|
|
|
111
111
|
return <ActuateWordmark className="h-9" />
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Children of the Posts parent. Same shape regardless of how many
|
|
116
|
+
* post-type collections are registered — Post Types are accessed via
|
|
117
|
+
* the `/posts/types` cards page, not as individual sidebar entries.
|
|
118
|
+
*/
|
|
119
|
+
const POSTS_CHILDREN: NavItem[] = [
|
|
120
|
+
{ path: '/posts', label: 'All Posts', icon: FileText },
|
|
121
|
+
{ path: '/posts/types', label: 'Post Types', icon: Layers },
|
|
122
|
+
]
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Children of the Templates parent. Templates is its own top-level item
|
|
126
|
+
* (page building tools) — Saved Sections are reusable content blocks and
|
|
127
|
+
* Widgets are embeddable components. These are deliberately NOT children
|
|
128
|
+
* of Pages: Pages is content, Templates/Sections/Widgets are structure.
|
|
129
|
+
*/
|
|
130
|
+
const TEMPLATES_CHILDREN: NavItem[] = [
|
|
131
|
+
{ path: '/saved-sections', label: 'Saved Sections', icon: Library },
|
|
132
|
+
{ path: '/widgets', label: 'Widgets', icon: LayoutGrid },
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Nav tree for the default (config-less) install. Dashboard sits on its
|
|
137
|
+
* own at the top, then a CONTENT group holding Pages (no children) above
|
|
138
|
+
* Posts (with All Posts + Post Types) and Templates (with Saved Sections
|
|
139
|
+
* + Widgets), followed by Media / Forms / SEO / Script Tags, then a
|
|
140
|
+
* SETTINGS group (Users / API Keys / Settings). The group ordering is
|
|
141
|
+
* meaningful — the renderer emits a section header the first time it
|
|
142
|
+
* sees a new group, so "CONTENT" renders once, above Pages.
|
|
143
|
+
*/
|
|
144
|
+
const defaultNavItems: NavItem[] = [
|
|
115
145
|
{ path: '/', label: 'Dashboard', icon: LayoutDashboard },
|
|
116
|
-
{ path: '/pages', label: 'Pages', icon: File },
|
|
117
|
-
{ path: '/posts', label: 'Posts', icon: FileText },
|
|
118
|
-
{
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
146
|
+
{ path: '/pages', label: 'Pages', icon: File, group: 'Content' },
|
|
147
|
+
{ path: '/posts', label: 'Posts', icon: FileText, group: 'Content', children: POSTS_CHILDREN },
|
|
148
|
+
{
|
|
149
|
+
path: '/page-templates',
|
|
150
|
+
label: 'Templates',
|
|
151
|
+
icon: Layers,
|
|
152
|
+
group: 'Content',
|
|
153
|
+
children: TEMPLATES_CHILDREN,
|
|
154
|
+
},
|
|
155
|
+
{ path: '/media', label: 'Media', icon: Image, group: 'Content' },
|
|
156
|
+
{ path: '/forms', label: 'Forms', icon: ClipboardList, group: 'Content' },
|
|
157
|
+
{ path: '/seo', label: 'SEO', icon: SearchIcon, group: 'Content' },
|
|
158
|
+
{ path: '/script-tags', label: 'Script Tags', icon: Code2, group: 'Content' },
|
|
159
|
+
{ path: '/users', label: 'Users', icon: Users, group: 'Settings' },
|
|
160
|
+
{ path: '/api-keys', label: 'API Keys', icon: KeyRound, group: 'Settings' },
|
|
161
|
+
{ path: '/settings', label: 'Settings', icon: Settings, group: 'Settings' },
|
|
124
162
|
]
|
|
125
163
|
|
|
126
164
|
export interface SidebarProps {
|
|
@@ -142,73 +180,155 @@ export function Sidebar({
|
|
|
142
180
|
|
|
143
181
|
return (
|
|
144
182
|
<aside
|
|
145
|
-
className={`bg-sidebar border-sidebar-border h-full border-r transition-all duration-200 ${
|
|
183
|
+
className={`bg-sidebar border-sidebar-border flex h-full flex-col border-r transition-all duration-200 ${
|
|
146
184
|
collapsed ? 'w-20' : 'w-64'
|
|
147
185
|
}`}
|
|
148
186
|
>
|
|
187
|
+
{/* Brand header — no toggle button here anymore; it now lives in the footer. */}
|
|
149
188
|
<div
|
|
150
|
-
className={`border-sidebar-border flex h-14 items-center border-b px-4 ${
|
|
151
|
-
collapsed ? 'justify-center' : '
|
|
189
|
+
className={`border-sidebar-border flex h-14 shrink-0 items-center border-b px-4 ${
|
|
190
|
+
collapsed ? 'justify-center' : ''
|
|
152
191
|
}`}
|
|
153
192
|
>
|
|
154
|
-
|
|
155
|
-
|
|
193
|
+
<BrandLogo config={config} collapsed={collapsed} />
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<nav className="flex-1 space-y-1 overflow-y-auto p-3" aria-label="Primary">
|
|
197
|
+
{renderNavTree(navItems, { collapsed, currentPath, onNavigate })}
|
|
198
|
+
</nav>
|
|
199
|
+
|
|
200
|
+
{/* Footer collapse control. Sits at the bottom of the sidebar so it
|
|
201
|
+
mirrors the reference design and stays out of the way during
|
|
202
|
+
everyday nav. Hidden below md: the mobile sheet has its own
|
|
203
|
+
dismiss affordance in <AdminShell>. */}
|
|
204
|
+
<div className="border-sidebar-border hidden shrink-0 border-t p-3 md:block">
|
|
156
205
|
<button
|
|
157
206
|
onClick={onToggleCollapse}
|
|
158
|
-
className=
|
|
207
|
+
className={`text-sidebar-foreground/80 hover:bg-sidebar-accent hover:text-sidebar-foreground flex w-full items-center gap-2.5 rounded-lg px-3 py-2 text-sm transition-colors ${
|
|
208
|
+
collapsed ? 'justify-center' : ''
|
|
209
|
+
}`}
|
|
159
210
|
aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
211
|
+
title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
160
212
|
>
|
|
161
213
|
{collapsed ? (
|
|
162
|
-
<
|
|
214
|
+
<PanelLeftOpen className="h-4 w-4 shrink-0" aria-hidden />
|
|
163
215
|
) : (
|
|
164
|
-
|
|
216
|
+
<>
|
|
217
|
+
<PanelLeftClose className="h-4 w-4 shrink-0" aria-hidden />
|
|
218
|
+
<span className="font-medium">Collapse sidebar</span>
|
|
219
|
+
</>
|
|
165
220
|
)}
|
|
166
221
|
</button>
|
|
167
222
|
</div>
|
|
168
|
-
|
|
169
|
-
<nav className="space-y-1 p-3">
|
|
170
|
-
{navItems.map((item, idx) => {
|
|
171
|
-
const Icon = item.icon
|
|
172
|
-
const isActive =
|
|
173
|
-
currentPath === item.path || (item.path !== '/' && currentPath.startsWith(item.path))
|
|
174
|
-
|
|
175
|
-
const prevGroup = idx > 0 ? navItems[idx - 1]?.group : undefined
|
|
176
|
-
const showGroupLabel = item.group && item.group !== prevGroup
|
|
177
|
-
|
|
178
|
-
return (
|
|
179
|
-
<div key={item.path}>
|
|
180
|
-
{showGroupLabel && !collapsed && (
|
|
181
|
-
<div className="px-3 pt-3 pb-1">
|
|
182
|
-
<span className="text-sidebar-foreground/50 text-[10px] font-semibold tracking-wider uppercase">
|
|
183
|
-
{item.group}
|
|
184
|
-
</span>
|
|
185
|
-
</div>
|
|
186
|
-
)}
|
|
187
|
-
{showGroupLabel && collapsed && (
|
|
188
|
-
<div className="flex justify-center pt-2 pb-1">
|
|
189
|
-
<span className="border-sidebar-foreground/20 w-4 border-t" />
|
|
190
|
-
</div>
|
|
191
|
-
)}
|
|
192
|
-
<button
|
|
193
|
-
onClick={() => onNavigate(item.path)}
|
|
194
|
-
className={`flex w-full items-center gap-3 rounded-lg px-3 py-2.5 text-left transition-colors ${
|
|
195
|
-
isActive
|
|
196
|
-
? 'bg-sidebar-accent text-sidebar-primary'
|
|
197
|
-
: 'text-sidebar-foreground hover:bg-sidebar-accent'
|
|
198
|
-
} ${collapsed ? 'justify-center' : ''}`}
|
|
199
|
-
title={collapsed ? item.label : ''}
|
|
200
|
-
>
|
|
201
|
-
<Icon className="h-5 w-5 shrink-0" />
|
|
202
|
-
{!collapsed && <span className="text-sm font-medium">{item.label}</span>}
|
|
203
|
-
</button>
|
|
204
|
-
</div>
|
|
205
|
-
)
|
|
206
|
-
})}
|
|
207
|
-
</nav>
|
|
208
223
|
</aside>
|
|
209
224
|
)
|
|
210
225
|
}
|
|
211
226
|
|
|
227
|
+
// ─── Rendering ──────────────────────────────────────────────────────────────
|
|
228
|
+
|
|
229
|
+
interface NavRenderContext {
|
|
230
|
+
collapsed: boolean
|
|
231
|
+
currentPath: string
|
|
232
|
+
onNavigate: (path: string) => void
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function isPathActive(currentPath: string, itemPath: string): boolean {
|
|
236
|
+
if (itemPath === '/') return currentPath === '/'
|
|
237
|
+
|
|
238
|
+
// Special case: /posts is a prefix of /posts/types — without this
|
|
239
|
+
// guard, "All Posts" (/posts) would stay highlighted while the user
|
|
240
|
+
// is on /posts/types or /posts/types/new. Treat /posts as exact-only
|
|
241
|
+
// when a sibling /posts/* route exists.
|
|
242
|
+
if (itemPath === '/posts') {
|
|
243
|
+
return currentPath === '/posts' || currentPath === '/posts/new'
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Use boundary match so /pages doesn't match /pages-something. Exact, or
|
|
247
|
+
// followed by a `/` separator.
|
|
248
|
+
return currentPath === itemPath || currentPath.startsWith(itemPath + '/')
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function renderNavTree(items: NavItem[], ctx: NavRenderContext): ReactNode {
|
|
252
|
+
const nodes: ReactNode[] = []
|
|
253
|
+
let lastGroup: string | undefined = undefined
|
|
254
|
+
|
|
255
|
+
for (const item of items) {
|
|
256
|
+
if (item.group && item.group !== lastGroup) {
|
|
257
|
+
nodes.push(
|
|
258
|
+
ctx.collapsed ? (
|
|
259
|
+
<div key={`g-${item.group}`} className="flex justify-center pt-3 pb-1">
|
|
260
|
+
<span className="border-sidebar-foreground/20 w-4 border-t" aria-hidden />
|
|
261
|
+
</div>
|
|
262
|
+
) : (
|
|
263
|
+
<div key={`g-${item.group}`} className="px-3 pt-4 pb-1">
|
|
264
|
+
<span className="text-sidebar-foreground/55 text-[10px] font-semibold tracking-wider uppercase">
|
|
265
|
+
{item.group}
|
|
266
|
+
</span>
|
|
267
|
+
</div>
|
|
268
|
+
),
|
|
269
|
+
)
|
|
270
|
+
lastGroup = item.group
|
|
271
|
+
} else if (!item.group) {
|
|
272
|
+
lastGroup = undefined
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Keys are namespaced because a parent and its first child can share
|
|
276
|
+
// the same path (e.g. the Posts parent and its "All Posts" child are
|
|
277
|
+
// both `/posts`). Using the raw path as the key collided and made
|
|
278
|
+
// React warn about — and risk dropping — duplicate siblings.
|
|
279
|
+
nodes.push(<NavLink key={`item:${item.path}`} item={item} ctx={ctx} />)
|
|
280
|
+
if (item.children && item.children.length > 0 && !ctx.collapsed) {
|
|
281
|
+
for (const child of item.children) {
|
|
282
|
+
nodes.push(
|
|
283
|
+
<NavLink key={`child:${item.path}:${child.path}`} item={child} ctx={ctx} nested />,
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return nodes
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function NavLink({
|
|
293
|
+
item,
|
|
294
|
+
ctx,
|
|
295
|
+
nested = false,
|
|
296
|
+
}: {
|
|
297
|
+
item: NavItem
|
|
298
|
+
ctx: NavRenderContext
|
|
299
|
+
nested?: boolean
|
|
300
|
+
}) {
|
|
301
|
+
const Icon = item.icon
|
|
302
|
+
const isActive = isPathActive(ctx.currentPath, item.path)
|
|
303
|
+
|
|
304
|
+
return (
|
|
305
|
+
<button
|
|
306
|
+
onClick={() => ctx.onNavigate(item.path)}
|
|
307
|
+
className={`flex w-full items-center gap-3 rounded-lg text-left transition-colors ${
|
|
308
|
+
nested ? 'py-1.5 pr-3 pl-9' : 'px-3 py-2.5'
|
|
309
|
+
} ${
|
|
310
|
+
isActive
|
|
311
|
+
? 'bg-sidebar-accent text-sidebar-primary'
|
|
312
|
+
: 'text-sidebar-foreground hover:bg-sidebar-accent'
|
|
313
|
+
} ${ctx.collapsed ? 'justify-center px-3' : ''}`}
|
|
314
|
+
title={ctx.collapsed ? item.label : ''}
|
|
315
|
+
aria-current={isActive ? 'page' : undefined}
|
|
316
|
+
>
|
|
317
|
+
{!nested && <Icon className="h-5 w-5 shrink-0" aria-hidden />}
|
|
318
|
+
{nested && !ctx.collapsed && (
|
|
319
|
+
<Icon className="text-sidebar-foreground/60 h-4 w-4 shrink-0" aria-hidden />
|
|
320
|
+
)}
|
|
321
|
+
{!ctx.collapsed && (
|
|
322
|
+
<span className={`truncate font-medium ${nested ? 'text-[13px]' : 'text-sm'}`}>
|
|
323
|
+
{item.label}
|
|
324
|
+
</span>
|
|
325
|
+
)}
|
|
326
|
+
</button>
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ─── Config-driven nav builder ──────────────────────────────────────────────
|
|
331
|
+
|
|
212
332
|
function resolveIcon(collection: any): LucideIcon {
|
|
213
333
|
const mapped = collection.admin?.icon ? ICON_MAP[collection.admin.icon] : undefined
|
|
214
334
|
if (mapped) return mapped
|
|
@@ -219,28 +339,50 @@ export interface NavItem {
|
|
|
219
339
|
path: string
|
|
220
340
|
label: string
|
|
221
341
|
icon: LucideIcon
|
|
342
|
+
/**
|
|
343
|
+
* Section header. Items sharing the same `group` are grouped together
|
|
344
|
+
* under a header rendered on first encounter. Items without a `group`
|
|
345
|
+
* render at the top of the nav in document order.
|
|
346
|
+
*/
|
|
222
347
|
group?: string
|
|
348
|
+
/** Indented children rendered immediately after this item. */
|
|
349
|
+
children?: NavItem[]
|
|
223
350
|
}
|
|
224
351
|
|
|
225
|
-
|
|
352
|
+
/**
|
|
353
|
+
* Build the sidebar nav tree from the CMS config. If no collections are
|
|
354
|
+
* configured, returns the curated `defaultNavItems` tree. When
|
|
355
|
+
* collections ARE configured, page-type collections inherit the
|
|
356
|
+
* Pages / Saved Sections / Templates child block, and every static
|
|
357
|
+
* destination (Media, Forms, SEO, Script Tags, Users, API Keys,
|
|
358
|
+
* Settings) is anchored under the right Content / Settings group.
|
|
359
|
+
*/
|
|
360
|
+
export function buildNavItems(config: any): NavItem[] {
|
|
226
361
|
if (!config?.collections) return defaultNavItems
|
|
227
362
|
|
|
228
363
|
const raw = config.collections
|
|
229
364
|
const collectionsList: any[] = Array.isArray(raw) ? raw : Object.values(raw)
|
|
230
|
-
|
|
231
365
|
const visible = collectionsList.filter((c) => !c.admin?.hidden)
|
|
232
366
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
const
|
|
367
|
+
// Treat `admin.group: 'Posts'` (case-insensitive) as a synonym for
|
|
368
|
+
// `type: 'post'` so consumers can tag arbitrary collections into
|
|
369
|
+
// the Posts area without changing the discriminator they already
|
|
370
|
+
// use elsewhere (sitemaps, schema.org, etc.).
|
|
371
|
+
const isPostsGroup = (c: any) =>
|
|
372
|
+
typeof c.admin?.group === 'string' && c.admin.group.toLowerCase() === 'posts'
|
|
373
|
+
const pages = visible.filter((c) => c.type === 'page' && !c.admin?.group)
|
|
374
|
+
const posts = visible.filter((c) => c.type === 'post' || isPostsGroup(c))
|
|
375
|
+
const otherUngrouped = visible.filter(
|
|
376
|
+
(c) => c.type !== 'page' && c.type !== 'post' && !c.admin?.group,
|
|
377
|
+
)
|
|
378
|
+
// Anything explicitly grouped that isn't a posts collection.
|
|
379
|
+
const grouped = visible.filter((c) => c.admin?.group && !isPostsGroup(c) && c.type !== 'post')
|
|
240
380
|
|
|
241
381
|
const items: NavItem[] = [{ path: '/', label: 'Dashboard', icon: LayoutDashboard }]
|
|
242
382
|
|
|
243
|
-
|
|
383
|
+
// Ungrouped collections that are neither page- nor post-type stay at
|
|
384
|
+
// the top level, directly under Dashboard, before the CONTENT group.
|
|
385
|
+
for (const collection of otherUngrouped) {
|
|
244
386
|
items.push({
|
|
245
387
|
label: collection.labels?.plural ?? collection.slug,
|
|
246
388
|
path: `/${collection.slug}`,
|
|
@@ -248,34 +390,78 @@ function buildNavItems(config: any): NavItem[] {
|
|
|
248
390
|
})
|
|
249
391
|
}
|
|
250
392
|
|
|
251
|
-
|
|
393
|
+
// CONTENT group. Everything below shares `group: 'Content'`, so the
|
|
394
|
+
// renderer emits the "CONTENT" header once — above Pages. Order:
|
|
395
|
+
// Pages → Posts → static content destinations (Media / Forms / SEO /
|
|
396
|
+
// Script Tags).
|
|
397
|
+
|
|
398
|
+
// Pages-style collections render as plain Content entries (no nested
|
|
399
|
+
// children). Saved Sections / Widgets now live under the dedicated
|
|
400
|
+
// Templates parent below, not under Pages.
|
|
401
|
+
pages.forEach((collection) => {
|
|
402
|
+
items.push({
|
|
403
|
+
label: collection.labels?.plural ?? collection.slug,
|
|
404
|
+
path: `/${collection.slug}`,
|
|
405
|
+
icon: resolveIcon(collection),
|
|
406
|
+
group: 'Content',
|
|
407
|
+
})
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
// Posts area — collapses every post-type collection under a single
|
|
411
|
+
// "Posts" parent. The aggregated table at /posts and the type cards
|
|
412
|
+
// at /posts/types are the entry points; individual post-type
|
|
413
|
+
// collections are not enumerated in the sidebar to avoid the
|
|
414
|
+
// duplicate-nav problem when a project has many types.
|
|
415
|
+
if (posts.length > 0) {
|
|
416
|
+
items.push({
|
|
417
|
+
path: '/posts',
|
|
418
|
+
label: 'Posts',
|
|
419
|
+
icon: FileText,
|
|
420
|
+
group: 'Content',
|
|
421
|
+
children: POSTS_CHILDREN,
|
|
422
|
+
})
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Templates — its own parent holding Saved Sections + Widgets (page
|
|
426
|
+
// building structure, distinct from Pages content).
|
|
427
|
+
items.push({
|
|
428
|
+
path: '/page-templates',
|
|
429
|
+
label: 'Templates',
|
|
430
|
+
icon: Layers,
|
|
431
|
+
group: 'Content',
|
|
432
|
+
children: TEMPLATES_CHILDREN,
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
// Standard Content destinations (always present — they map to CMS
|
|
436
|
+
// features, not collections).
|
|
437
|
+
items.push(
|
|
438
|
+
{ path: '/media', label: 'Media', icon: Image, group: 'Content' },
|
|
439
|
+
{ path: '/forms', label: 'Forms', icon: ClipboardList, group: 'Content' },
|
|
440
|
+
{ path: '/seo', label: 'SEO', icon: SearchIcon, group: 'Content' },
|
|
441
|
+
{ path: '/script-tags', label: 'Script Tags', icon: Code2, group: 'Content' },
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
// Config-supplied custom groups land after the standard Content block
|
|
445
|
+
// and before Settings. Each renders under its own header.
|
|
446
|
+
const groupBuckets = new Map<string, NavItem[]>()
|
|
252
447
|
for (const col of grouped) {
|
|
253
448
|
const group = col.admin.group as string
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
icon: resolveIcon(collection),
|
|
263
|
-
group: groupName,
|
|
264
|
-
})
|
|
265
|
-
}
|
|
449
|
+
const bucket = groupBuckets.get(group) ?? []
|
|
450
|
+
bucket.push({
|
|
451
|
+
label: col.labels?.plural ?? col.slug,
|
|
452
|
+
path: `/${col.slug}`,
|
|
453
|
+
icon: resolveIcon(col),
|
|
454
|
+
group,
|
|
455
|
+
})
|
|
456
|
+
groupBuckets.set(group, bucket)
|
|
266
457
|
}
|
|
458
|
+
for (const bucket of groupBuckets.values()) items.push(...bucket)
|
|
267
459
|
|
|
460
|
+
// Settings destinations — always last.
|
|
268
461
|
items.push(
|
|
269
|
-
{ path: '/
|
|
270
|
-
{ path: '/
|
|
271
|
-
{ path: '/
|
|
272
|
-
{ path: '/media', label: 'Media', icon: Image },
|
|
273
|
-
{ path: '/forms', label: 'Forms', icon: ClipboardList },
|
|
274
|
-
{ path: '/seo', label: 'SEO', icon: SearchIcon },
|
|
275
|
-
{ path: '/script-tags', label: 'Script Tags', icon: Code2 },
|
|
276
|
-
{ path: '/users', label: 'Users', icon: Users },
|
|
277
|
-
{ path: '/api-keys', label: 'API Keys', icon: KeyRound },
|
|
278
|
-
{ path: '/settings', label: 'Settings', icon: Settings },
|
|
462
|
+
{ path: '/users', label: 'Users', icon: Users, group: 'Settings' },
|
|
463
|
+
{ path: '/api-keys', label: 'API Keys', icon: KeyRound, group: 'Settings' },
|
|
464
|
+
{ path: '/settings', label: 'Settings', icon: Settings, group: 'Settings' },
|
|
279
465
|
)
|
|
280
466
|
|
|
281
467
|
return items
|