@actuate-media/cms-admin 0.10.0 → 0.11.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 +8 -5
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/layout/primitives.test.d.ts +2 -0
- package/dist/__tests__/layout/primitives.test.d.ts.map +1 -0
- package/dist/__tests__/layout/primitives.test.js +34 -0
- package/dist/__tests__/layout/primitives.test.js.map +1 -0
- package/dist/__tests__/lib/cv.test.d.ts +2 -0
- package/dist/__tests__/lib/cv.test.d.ts.map +1 -0
- package/dist/__tests__/lib/cv.test.js +66 -0
- package/dist/__tests__/lib/cv.test.js.map +1 -0
- package/dist/actuate-admin.css +1 -1
- package/dist/assets/actuate-logo.d.ts +36 -0
- package/dist/assets/actuate-logo.d.ts.map +1 -0
- package/dist/assets/actuate-logo.js +15 -0
- package/dist/assets/actuate-logo.js.map +1 -0
- package/dist/components/Breadcrumbs.js +2 -2
- package/dist/components/CommandPalette.js +10 -10
- package/dist/components/ContentOverviewChart.js +3 -3
- package/dist/components/ErrorBoundary.js +1 -1
- package/dist/components/FocalPointPicker.js +2 -2
- package/dist/components/FolderTree.js +20 -20
- package/dist/components/LivePreview.js +3 -3
- package/dist/components/LocaleSwitcher.js +1 -1
- package/dist/components/MediaPickerModal.js +4 -4
- package/dist/components/PresenceIndicator.js +1 -1
- package/dist/components/SEOConfigPanel.d.ts +2 -0
- package/dist/components/SEOConfigPanel.d.ts.map +1 -0
- package/dist/components/SEOConfigPanel.js +174 -0
- package/dist/components/SEOConfigPanel.js.map +1 -0
- package/dist/components/SEOPanel.js +9 -9
- package/dist/components/SEOPerformance.js +2 -2
- package/dist/components/SchedulePublishDialog.js +1 -1
- package/dist/components/SharePreviewLinkDialog.js +1 -1
- package/dist/components/TipTapEditor.js +5 -5
- package/dist/components/VersionHistory.js +2 -2
- package/dist/components/ui/Badge.d.ts +33 -3
- package/dist/components/ui/Badge.d.ts.map +1 -1
- package/dist/components/ui/Badge.js +42 -8
- package/dist/components/ui/Badge.js.map +1 -1
- package/dist/components/ui/Button.d.ts +19 -8
- package/dist/components/ui/Button.d.ts.map +1 -1
- package/dist/components/ui/Button.js +35 -14
- package/dist/components/ui/Button.js.map +1 -1
- package/dist/components/ui/Card.d.ts +26 -0
- package/dist/components/ui/Card.d.ts.map +1 -0
- package/dist/components/ui/Card.js +45 -0
- package/dist/components/ui/Card.js.map +1 -0
- package/dist/components/ui/DataTable.js +1 -1
- package/dist/components/ui/Input.d.ts +15 -0
- package/dist/components/ui/Input.d.ts.map +1 -0
- package/dist/components/ui/Input.js +23 -0
- package/dist/components/ui/Input.js.map +1 -0
- package/dist/components/ui/SearchInput.js +1 -1
- package/dist/components/ui/Select.d.ts +16 -0
- package/dist/components/ui/Select.d.ts.map +1 -0
- package/dist/components/ui/Select.js +25 -0
- package/dist/components/ui/Select.js.map +1 -0
- package/dist/components/ui/Toast.js +1 -1
- package/dist/components/ui/index.d.ts +10 -4
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +5 -2
- package/dist/components/ui/index.js.map +1 -1
- package/dist/fields/BlockBuilderField.js +3 -3
- package/dist/fields/DateField.js +1 -1
- package/dist/fields/RelationshipField.js +3 -3
- package/dist/fields/TextField.js +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/layout/Header.js +1 -1
- package/dist/layout/Layout.d.ts +14 -0
- package/dist/layout/Layout.d.ts.map +1 -1
- package/dist/layout/Layout.js +17 -11
- package/dist/layout/Layout.js.map +1 -1
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +21 -11
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/layout/primitives/AdminShell.d.ts +43 -0
- package/dist/layout/primitives/AdminShell.d.ts.map +1 -0
- package/dist/layout/primitives/AdminShell.js +51 -0
- package/dist/layout/primitives/AdminShell.js.map +1 -0
- package/dist/layout/primitives/Box.d.ts +19 -0
- package/dist/layout/primitives/Box.d.ts.map +1 -0
- package/dist/layout/primitives/Box.js +12 -0
- package/dist/layout/primitives/Box.js.map +1 -0
- package/dist/layout/primitives/Cluster.d.ts +27 -0
- package/dist/layout/primitives/Cluster.d.ts.map +1 -0
- package/dist/layout/primitives/Cluster.js +37 -0
- package/dist/layout/primitives/Cluster.js.map +1 -0
- package/dist/layout/primitives/Grid.d.ts +45 -0
- package/dist/layout/primitives/Grid.d.ts.map +1 -0
- package/dist/layout/primitives/Grid.js +59 -0
- package/dist/layout/primitives/Grid.js.map +1 -0
- package/dist/layout/primitives/PageContainer.d.ts +36 -0
- package/dist/layout/primitives/PageContainer.d.ts.map +1 -0
- package/dist/layout/primitives/PageContainer.js +41 -0
- package/dist/layout/primitives/PageContainer.js.map +1 -0
- package/dist/layout/primitives/Split.d.ts +34 -0
- package/dist/layout/primitives/Split.d.ts.map +1 -0
- package/dist/layout/primitives/Split.js +27 -0
- package/dist/layout/primitives/Split.js.map +1 -0
- package/dist/layout/primitives/Stack.d.ts +23 -0
- package/dist/layout/primitives/Stack.d.ts.map +1 -0
- package/dist/layout/primitives/Stack.js +34 -0
- package/dist/layout/primitives/Stack.js.map +1 -0
- package/dist/layout/primitives/index.d.ts +30 -0
- package/dist/layout/primitives/index.d.ts.map +1 -0
- package/dist/layout/primitives/index.js +22 -0
- package/dist/layout/primitives/index.js.map +1 -0
- package/dist/layout/primitives/tokens.d.ts +48 -0
- package/dist/layout/primitives/tokens.d.ts.map +1 -0
- package/dist/layout/primitives/tokens.js +54 -0
- package/dist/layout/primitives/tokens.js.map +1 -0
- package/dist/lib/cv.d.ts +53 -0
- package/dist/lib/cv.d.ts.map +1 -0
- package/dist/lib/cv.js +39 -0
- package/dist/lib/cv.js.map +1 -0
- package/dist/views/ApiKeys.js +7 -7
- package/dist/views/CollectionList.js +8 -8
- package/dist/views/Dashboard.d.ts.map +1 -1
- package/dist/views/Dashboard.js +333 -78
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/DocumentEdit.js +3 -3
- package/dist/views/ForgotPassword.js +2 -2
- package/dist/views/FormEditor.js +5 -5
- package/dist/views/FormSubmissions.js +6 -6
- package/dist/views/Forms.js +2 -2
- package/dist/views/Login.d.ts +16 -1
- package/dist/views/Login.d.ts.map +1 -1
- package/dist/views/Login.js +17 -7
- package/dist/views/Login.js.map +1 -1
- package/dist/views/MediaBrowser.js +16 -16
- package/dist/views/PageEditor.js +2 -2
- package/dist/views/Pages.js +10 -10
- package/dist/views/PostEditor.js +2 -2
- package/dist/views/Posts.js +4 -4
- package/dist/views/Redirects.js +4 -4
- package/dist/views/ResetPassword.js +2 -2
- package/dist/views/SEO.js +6 -6
- package/dist/views/ScriptTagEditor.js +4 -4
- package/dist/views/ScriptTags.js +2 -2
- package/dist/views/Settings.d.ts.map +1 -1
- package/dist/views/Settings.js +9 -8
- package/dist/views/Settings.js.map +1 -1
- package/dist/views/SetupWizard.js +2 -2
- package/dist/views/Users.js +4 -4
- package/dist/views/page-builder/AIBlockAssist.js +1 -1
- package/dist/views/page-builder/AIGenerateDialog.js +10 -10
- package/dist/views/page-builder/BlockEditor.js +10 -10
- package/dist/views/page-builder/BlockPicker.js +4 -4
- package/dist/views/page-builder/BottomBar.js +1 -1
- package/dist/views/page-builder/BuilderToolbar.js +2 -2
- package/dist/views/page-builder/ContextPanel.js +2 -2
- package/dist/views/page-builder/DesignScore.js +9 -9
- package/dist/views/page-builder/NodeSettings.js +8 -8
- package/dist/views/page-builder/PageBuilder.js +3 -3
- package/dist/views/page-builder/PageSettings.js +1 -1
- package/dist/views/page-builder/PageTemplates.js +2 -2
- package/dist/views/page-builder/SEOPanel.js +13 -13
- package/dist/views/page-builder/SavedSections.js +5 -5
- package/dist/views/page-builder/TemplatePicker.js +2 -2
- package/dist/views/page-builder/block-renderers/CTAPreview.js +5 -5
- package/dist/views/page-builder/block-renderers/CardsPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/CodePreview.js +1 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/FallbackPreview.js +1 -1
- package/dist/views/page-builder/block-renderers/FormPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +5 -5
- package/dist/views/page-builder/block-renderers/HeroPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/ImagePreview.js +3 -3
- package/dist/views/page-builder/block-renderers/TextPreview.js +3 -3
- package/dist/views/page-builder/block-renderers/VideoPreview.js +4 -4
- package/dist/views/page-builder/canvas/BlockRenderer.js +1 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.js +3 -3
- package/dist/views/page-builder/canvas/ColumnRenderer.js +2 -2
- package/dist/views/page-builder/canvas/ContainerRenderer.js +2 -2
- package/dist/views/page-builder/canvas/RowRenderer.js +2 -2
- package/dist/views/page-builder/canvas/SectionRenderer.js +2 -2
- package/package.json +6 -2
- package/src/AdminRoot.tsx +21 -11
- package/src/__tests__/layout/primitives.test.ts +37 -0
- package/src/__tests__/lib/cv.test.ts +74 -0
- package/src/assets/actuate-logo.tsx +72 -0
- package/src/components/Breadcrumbs.tsx +6 -6
- package/src/components/CommandPalette.tsx +34 -34
- package/src/components/ContentOverviewChart.tsx +3 -3
- package/src/components/ErrorBoundary.tsx +3 -3
- package/src/components/FocalPointPicker.tsx +4 -4
- package/src/components/FolderTree.tsx +38 -38
- package/src/components/LivePreview.tsx +16 -16
- package/src/components/LocaleSwitcher.tsx +7 -7
- package/src/components/MediaPickerModal.tsx +21 -21
- package/src/components/PresenceIndicator.tsx +2 -2
- package/src/components/SEOConfigPanel.tsx +582 -0
- package/src/components/SEOPanel.tsx +46 -46
- package/src/components/SEOPerformance.tsx +21 -21
- package/src/components/SchedulePublishDialog.tsx +4 -4
- package/src/components/SharePreviewLinkDialog.tsx +1 -1
- package/src/components/TipTapEditor.tsx +33 -33
- package/src/components/VersionHistory.tsx +16 -16
- package/src/components/ui/Badge.tsx +66 -14
- package/src/components/ui/Button.tsx +70 -33
- package/src/components/ui/Card.tsx +101 -0
- package/src/components/ui/DataTable.tsx +1 -1
- package/src/components/ui/Input.tsx +35 -0
- package/src/components/ui/SearchInput.tsx +4 -4
- package/src/components/ui/Select.tsx +56 -0
- package/src/components/ui/Toast.tsx +1 -1
- package/src/components/ui/index.ts +18 -4
- package/src/fields/BlockBuilderField.tsx +3 -3
- package/src/fields/DateField.tsx +1 -1
- package/src/fields/RelationshipField.tsx +10 -10
- package/src/fields/TextField.tsx +1 -1
- package/src/index.ts +28 -0
- package/src/layout/Header.tsx +28 -28
- package/src/layout/Layout.tsx +39 -46
- package/src/layout/Sidebar.tsx +37 -64
- package/src/layout/primitives/AdminShell.tsx +118 -0
- package/src/layout/primitives/Box.tsx +30 -0
- package/src/layout/primitives/Cluster.tsx +74 -0
- package/src/layout/primitives/Grid.tsx +120 -0
- package/src/layout/primitives/PageContainer.tsx +96 -0
- package/src/layout/primitives/Split.tsx +73 -0
- package/src/layout/primitives/Stack.tsx +67 -0
- package/src/layout/primitives/index.ts +36 -0
- package/src/layout/primitives/tokens.ts +76 -0
- package/src/lib/cv.ts +96 -0
- package/src/styles/build-input.css +1 -1
- package/src/views/ApiKeys.tsx +57 -57
- package/src/views/CollectionList.tsx +30 -30
- package/src/views/Dashboard.tsx +737 -186
- package/src/views/DocumentEdit.tsx +9 -9
- package/src/views/ForgotPassword.tsx +18 -18
- package/src/views/FormEditor.tsx +75 -75
- package/src/views/FormSubmissions.tsx +76 -76
- package/src/views/Forms.tsx +27 -27
- package/src/views/Login.tsx +65 -25
- package/src/views/MediaBrowser.tsx +127 -127
- package/src/views/PageEditor.tsx +25 -25
- package/src/views/Pages.tsx +59 -59
- package/src/views/PostEditor.tsx +37 -37
- package/src/views/Posts.tsx +48 -48
- package/src/views/Redirects.tsx +21 -21
- package/src/views/ResetPassword.tsx +28 -28
- package/src/views/SEO.tsx +144 -144
- package/src/views/ScriptTagEditor.tsx +24 -24
- package/src/views/ScriptTags.tsx +10 -10
- package/src/views/Settings.tsx +88 -80
- package/src/views/SetupWizard.tsx +28 -28
- package/src/views/Users.tsx +20 -20
- package/src/views/page-builder/AIBlockAssist.tsx +1 -1
- package/src/views/page-builder/AIGenerateDialog.tsx +63 -63
- package/src/views/page-builder/BlockEditor.tsx +26 -26
- package/src/views/page-builder/BlockPicker.tsx +22 -22
- package/src/views/page-builder/BottomBar.tsx +8 -8
- package/src/views/page-builder/BuilderToolbar.tsx +17 -17
- package/src/views/page-builder/ContextPanel.tsx +3 -3
- package/src/views/page-builder/DesignScore.tsx +21 -21
- package/src/views/page-builder/NodeSettings.tsx +27 -27
- package/src/views/page-builder/PageBuilder.tsx +11 -11
- package/src/views/page-builder/PageSettings.tsx +4 -4
- package/src/views/page-builder/PageTemplates.tsx +18 -18
- package/src/views/page-builder/SEOPanel.tsx +53 -53
- package/src/views/page-builder/SavedSections.tsx +37 -37
- package/src/views/page-builder/TemplatePicker.tsx +17 -17
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +13 -13
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +5 -5
- package/src/views/page-builder/block-renderers/CodePreview.tsx +6 -6
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +13 -13
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +3 -3
- package/src/views/page-builder/block-renderers/FormPreview.tsx +20 -20
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +8 -8
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +16 -16
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +4 -4
- package/src/views/page-builder/block-renderers/TextPreview.tsx +14 -14
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +12 -12
- package/src/views/page-builder/canvas/BlockRenderer.tsx +4 -4
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +6 -6
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +3 -3
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +2 -2
- package/src/views/page-builder/canvas/RowRenderer.tsx +2 -2
- package/src/views/page-builder/canvas/SectionRenderer.tsx +2 -2
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { forwardRef, type InputHTMLAttributes } from 'react'
|
|
2
|
+
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
3
|
+
|
|
4
|
+
const input = cv(
|
|
5
|
+
'focus-visible:ring-offset-background flex w-full rounded-[var(--radius)] border bg-[var(--background)] text-[var(--foreground)] file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-[var(--muted-foreground)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
6
|
+
{
|
|
7
|
+
variants: {
|
|
8
|
+
size: {
|
|
9
|
+
sm: 'h-8 px-2.5 text-xs',
|
|
10
|
+
md: 'h-9 px-3 text-sm',
|
|
11
|
+
lg: 'h-10 px-4 text-base',
|
|
12
|
+
},
|
|
13
|
+
state: {
|
|
14
|
+
default: 'border-[var(--border)]',
|
|
15
|
+
invalid: 'border-[var(--destructive)] focus-visible:ring-[var(--destructive)]',
|
|
16
|
+
valid: 'border-emerald-500 focus-visible:ring-emerald-500',
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
defaultVariants: { size: 'md', state: 'default' },
|
|
20
|
+
},
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
export type InputVariants = VariantProps<typeof input>
|
|
24
|
+
|
|
25
|
+
export interface InputProps
|
|
26
|
+
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'size'>, InputVariants {}
|
|
27
|
+
|
|
28
|
+
export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
|
|
29
|
+
{ size, state, className, ...rest },
|
|
30
|
+
ref,
|
|
31
|
+
) {
|
|
32
|
+
return <input ref={ref} className={input({ size, state, class: className })} {...rest} />
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
export { input as inputVariants }
|
|
@@ -10,7 +10,7 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
10
10
|
return (
|
|
11
11
|
<div className="relative flex-1">
|
|
12
12
|
<svg
|
|
13
|
-
className="absolute
|
|
13
|
+
className="absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-[var(--muted-foreground)]"
|
|
14
14
|
fill="none"
|
|
15
15
|
viewBox="0 0 24 24"
|
|
16
16
|
stroke="currentColor"
|
|
@@ -27,12 +27,12 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
27
27
|
value={value}
|
|
28
28
|
onChange={(e) => onChange(e.target.value)}
|
|
29
29
|
placeholder={placeholder}
|
|
30
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] py-2 pl-9
|
|
30
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] py-2 pr-8 pl-9 text-sm outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
31
31
|
/>
|
|
32
32
|
{value && (
|
|
33
33
|
<button
|
|
34
34
|
onClick={() => onChange('')}
|
|
35
|
-
className="absolute
|
|
35
|
+
className="absolute top-1/2 right-2 -translate-y-1/2 rounded p-0.5 text-[var(--muted-foreground)] hover:text-[var(--foreground)]"
|
|
36
36
|
aria-label="Clear search"
|
|
37
37
|
>
|
|
38
38
|
<svg
|
|
@@ -46,7 +46,7 @@ export function SearchInput({ value, onChange, placeholder = 'Search...' }: Sear
|
|
|
46
46
|
</svg>
|
|
47
47
|
</button>
|
|
48
48
|
)}
|
|
49
|
-
<kbd className="pointer-events-none absolute
|
|
49
|
+
<kbd className="pointer-events-none absolute top-1/2 right-8 hidden -translate-y-1/2 rounded border border-[var(--border)] px-1.5 py-0.5 text-[10px] text-[var(--muted-foreground)] sm:inline-block">
|
|
50
50
|
⌘K
|
|
51
51
|
</kbd>
|
|
52
52
|
</div>
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { forwardRef, type SelectHTMLAttributes, type ReactNode } from 'react'
|
|
2
|
+
import { cv, type VariantProps } from '../../lib/cv.js'
|
|
3
|
+
|
|
4
|
+
const select = cv(
|
|
5
|
+
// Shares geometry with Input so they line up in forms.
|
|
6
|
+
'focus-visible:ring-offset-background flex w-full appearance-none rounded-[var(--radius)] border bg-[var(--background)] pr-8 text-[var(--foreground)] focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50',
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
size: {
|
|
10
|
+
sm: 'h-8 pr-7 pl-2.5 text-xs',
|
|
11
|
+
md: 'h-9 pr-8 pl-3 text-sm',
|
|
12
|
+
lg: 'h-10 pr-9 pl-4 text-base',
|
|
13
|
+
},
|
|
14
|
+
state: {
|
|
15
|
+
default: 'border-[var(--border)]',
|
|
16
|
+
invalid: 'border-[var(--destructive)] focus-visible:ring-[var(--destructive)]',
|
|
17
|
+
valid: 'border-emerald-500 focus-visible:ring-emerald-500',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
defaultVariants: { size: 'md', state: 'default' },
|
|
21
|
+
},
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
export type SelectVariants = VariantProps<typeof select>
|
|
25
|
+
|
|
26
|
+
export interface SelectProps
|
|
27
|
+
extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'size'>, SelectVariants {
|
|
28
|
+
children?: ReactNode
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(
|
|
32
|
+
{ size, state, className, children, ...rest },
|
|
33
|
+
ref,
|
|
34
|
+
) {
|
|
35
|
+
return (
|
|
36
|
+
<div className="relative">
|
|
37
|
+
<select ref={ref} className={select({ size, state, class: className })} {...rest}>
|
|
38
|
+
{children}
|
|
39
|
+
</select>
|
|
40
|
+
<svg
|
|
41
|
+
aria-hidden
|
|
42
|
+
className="pointer-events-none absolute top-1/2 right-2 h-4 w-4 -translate-y-1/2 text-[var(--muted-foreground)]"
|
|
43
|
+
viewBox="0 0 20 20"
|
|
44
|
+
fill="currentColor"
|
|
45
|
+
>
|
|
46
|
+
<path
|
|
47
|
+
fillRule="evenodd"
|
|
48
|
+
d="M5.23 7.21a.75.75 0 011.06.02L10 11.06l3.71-3.83a.75.75 0 111.08 1.04l-4.25 4.39a.75.75 0 01-1.08 0L5.21 8.27a.75.75 0 01.02-1.06z"
|
|
49
|
+
clipRule="evenodd"
|
|
50
|
+
/>
|
|
51
|
+
</svg>
|
|
52
|
+
</div>
|
|
53
|
+
)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
export { select as selectVariants }
|
|
@@ -45,7 +45,7 @@ export function ToastContainer({ toasts, onDismiss }: ToastContainerProps) {
|
|
|
45
45
|
if (toasts.length === 0) return null
|
|
46
46
|
|
|
47
47
|
return (
|
|
48
|
-
<div className="fixed
|
|
48
|
+
<div className="fixed right-4 bottom-4 z-[100] flex flex-col gap-2">
|
|
49
49
|
{toasts.map((toast) => (
|
|
50
50
|
<div
|
|
51
51
|
key={toast.id}
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
export { Button } from './Button.js'
|
|
2
|
-
export type { ButtonProps } from './Button.js'
|
|
3
|
-
export { Badge } from './Badge.js'
|
|
4
|
-
export type { BadgeProps } from './Badge.js'
|
|
1
|
+
export { Button, buttonVariants } from './Button.js'
|
|
2
|
+
export type { ButtonProps, ButtonVariants } from './Button.js'
|
|
3
|
+
export { Badge, badgeVariants, STATUS_TONE, STATUS_LABEL } from './Badge.js'
|
|
4
|
+
export type { BadgeProps, BadgeVariants, DocumentStatus } from './Badge.js'
|
|
5
|
+
export {
|
|
6
|
+
Card,
|
|
7
|
+
CardHeader,
|
|
8
|
+
CardTitle,
|
|
9
|
+
CardDescription,
|
|
10
|
+
CardContent,
|
|
11
|
+
CardFooter,
|
|
12
|
+
cardVariants,
|
|
13
|
+
} from './Card.js'
|
|
14
|
+
export type { CardProps, CardVariants } from './Card.js'
|
|
15
|
+
export { Input, inputVariants } from './Input.js'
|
|
16
|
+
export type { InputProps, InputVariants } from './Input.js'
|
|
17
|
+
export { Select, selectVariants } from './Select.js'
|
|
18
|
+
export type { SelectProps, SelectVariants } from './Select.js'
|
|
5
19
|
export { Avatar } from './Avatar.js'
|
|
6
20
|
export type { AvatarProps } from './Avatar.js'
|
|
7
21
|
export { EmptyState } from './EmptyState.js'
|
|
@@ -74,7 +74,7 @@ function BlockField({ field, value, onChange }: BlockFieldProps) {
|
|
|
74
74
|
<select
|
|
75
75
|
value={(value as string) ?? ''}
|
|
76
76
|
onChange={(e) => onChange(e.target.value)}
|
|
77
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm outline-none
|
|
77
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm transition-colors outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
78
78
|
>
|
|
79
79
|
<option value="">Select...</option>
|
|
80
80
|
{field.options.map((opt) => (
|
|
@@ -98,7 +98,7 @@ function BlockField({ field, value, onChange }: BlockFieldProps) {
|
|
|
98
98
|
value={(value as string) ?? ''}
|
|
99
99
|
onChange={(e) => onChange(e.target.value)}
|
|
100
100
|
rows={4}
|
|
101
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm outline-none
|
|
101
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm transition-colors outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
102
102
|
/>
|
|
103
103
|
</div>
|
|
104
104
|
)
|
|
@@ -134,7 +134,7 @@ function BlockField({ field, value, onChange }: BlockFieldProps) {
|
|
|
134
134
|
value={(value as string) ?? ''}
|
|
135
135
|
onChange={(e) => onChange(e.target.value)}
|
|
136
136
|
required={field.required}
|
|
137
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm outline-none
|
|
137
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] px-3 py-2 text-sm transition-colors outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
138
138
|
/>
|
|
139
139
|
</div>
|
|
140
140
|
)
|
package/src/fields/DateField.tsx
CHANGED
|
@@ -25,7 +25,7 @@ export function DateField({ label, value = '', onChange, required, helpText }: D
|
|
|
25
25
|
/>
|
|
26
26
|
<button
|
|
27
27
|
type="button"
|
|
28
|
-
className="absolute
|
|
28
|
+
className="absolute top-1/2 right-2 -translate-y-1/2 text-[var(--muted-foreground)]"
|
|
29
29
|
aria-label="Open calendar"
|
|
30
30
|
>
|
|
31
31
|
<svg
|
|
@@ -158,12 +158,12 @@ export function RelationshipField({
|
|
|
158
158
|
onClick={() => handleToggle(opt.id)}
|
|
159
159
|
className={`flex w-full items-center gap-2 px-3 py-2 text-sm hover:bg-[var(--accent)] ${isSelected ? 'font-medium' : ''}`}
|
|
160
160
|
>
|
|
161
|
-
<div className="
|
|
161
|
+
<div className="min-w-0 flex-1 text-left">
|
|
162
162
|
<div className="flex items-center gap-2">
|
|
163
163
|
<span className="truncate">{getDocTitle(opt)}</span>
|
|
164
164
|
{isSelected && <span className="shrink-0 text-[var(--primary)]">✓</span>}
|
|
165
165
|
</div>
|
|
166
|
-
<div className="flex items-center gap-2
|
|
166
|
+
<div className="mt-0.5 flex items-center gap-2">
|
|
167
167
|
<span className="text-[10px] text-[var(--muted-foreground)]">{collection}</span>
|
|
168
168
|
{opt.updatedAt && (
|
|
169
169
|
<span className="text-[10px] text-[var(--muted-foreground)]">
|
|
@@ -201,7 +201,7 @@ export function RelationshipField({
|
|
|
201
201
|
onClick={() => handleRemove(item.id)}
|
|
202
202
|
className="hover:text-[var(--destructive)]"
|
|
203
203
|
>
|
|
204
|
-
<X className="
|
|
204
|
+
<X className="h-3 w-3" />
|
|
205
205
|
</button>
|
|
206
206
|
</span>
|
|
207
207
|
))}
|
|
@@ -209,26 +209,26 @@ export function RelationshipField({
|
|
|
209
209
|
)}
|
|
210
210
|
|
|
211
211
|
<div className="relative">
|
|
212
|
-
<Search className="absolute left-2.5
|
|
212
|
+
<Search className="pointer-events-none absolute top-1/2 left-2.5 h-4 w-4 -translate-y-1/2 text-[var(--muted-foreground)]" />
|
|
213
213
|
<input
|
|
214
214
|
type="text"
|
|
215
215
|
value={searchTerm}
|
|
216
216
|
onChange={(e) => handleSearch(e.target.value)}
|
|
217
217
|
onFocus={() => setOpen(true)}
|
|
218
218
|
placeholder={`Search ${relationTo}...`}
|
|
219
|
-
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)]
|
|
219
|
+
className="w-full rounded-md border border-[var(--border)] bg-[var(--input-background)] py-2 pr-3 pl-8 text-sm outline-none focus:ring-2 focus:ring-[var(--ring)]"
|
|
220
220
|
/>
|
|
221
221
|
{loading && (
|
|
222
|
-
<Loader2 className="absolute right-2.5
|
|
222
|
+
<Loader2 className="absolute top-1/2 right-2.5 h-4 w-4 -translate-y-1/2 animate-spin text-[var(--muted-foreground)]" />
|
|
223
223
|
)}
|
|
224
224
|
|
|
225
225
|
{open && (
|
|
226
|
-
<ul className="absolute z-50 mt-1
|
|
226
|
+
<ul className="absolute z-50 mt-1 max-h-60 w-full overflow-y-auto rounded-md border border-[var(--border)] bg-[var(--popover)] py-1 shadow-lg">
|
|
227
227
|
{unselectedItems.map((opt) => (
|
|
228
228
|
<li key={opt.id}>{renderOption(opt, false)}</li>
|
|
229
229
|
))}
|
|
230
230
|
{selectedItems.length > 0 && unselectedItems.length > 0 && (
|
|
231
|
-
<li className="border-t border-[var(--border)]
|
|
231
|
+
<li className="my-1 border-t border-[var(--border)]" />
|
|
232
232
|
)}
|
|
233
233
|
{selectedItems.map((opt) => (
|
|
234
234
|
<li key={opt.id}>{renderOption(opt, true)}</li>
|
|
@@ -236,7 +236,7 @@ export function RelationshipField({
|
|
|
236
236
|
{options.length === 0 && !loading && (
|
|
237
237
|
<li className="px-3 py-2 text-sm text-[var(--muted-foreground)]">No results</li>
|
|
238
238
|
)}
|
|
239
|
-
<li className="border-t border-[var(--border)]
|
|
239
|
+
<li className="mt-1 border-t border-[var(--border)]">
|
|
240
240
|
<button
|
|
241
241
|
type="button"
|
|
242
242
|
onClick={() => {
|
|
@@ -245,7 +245,7 @@ export function RelationshipField({
|
|
|
245
245
|
}}
|
|
246
246
|
className="flex w-full items-center gap-2 px-3 py-2 text-sm text-[var(--primary)] hover:bg-[var(--accent)]"
|
|
247
247
|
>
|
|
248
|
-
<Plus className="
|
|
248
|
+
<Plus className="h-3.5 w-3.5" />
|
|
249
249
|
Create New
|
|
250
250
|
</button>
|
|
251
251
|
</li>
|
package/src/fields/TextField.tsx
CHANGED
|
@@ -43,7 +43,7 @@ export function TextField({
|
|
|
43
43
|
onChange={(e) => onChange(e.target.value)}
|
|
44
44
|
required={required}
|
|
45
45
|
maxLength={maxLength}
|
|
46
|
-
className={`w-full rounded-md border bg-[var(--input-background)] px-3 py-2 text-sm outline-none
|
|
46
|
+
className={`w-full rounded-md border bg-[var(--input-background)] px-3 py-2 text-sm transition-colors outline-none focus:ring-2 focus:ring-[var(--ring)] ${
|
|
47
47
|
hasError ? 'border-[var(--destructive)]' : 'border-[var(--border)]'
|
|
48
48
|
}`}
|
|
49
49
|
/>
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,34 @@ export type { SidebarProps } from './layout/Sidebar.js'
|
|
|
8
8
|
export { Header } from './layout/Header.js'
|
|
9
9
|
export type { HeaderProps } from './layout/Header.js'
|
|
10
10
|
|
|
11
|
+
// Layout primitives — the sanctioned way to compose admin views.
|
|
12
|
+
// Always prefer these to hand-rolled flex/grid utilities.
|
|
13
|
+
export {
|
|
14
|
+
AdminShell,
|
|
15
|
+
PageContainer,
|
|
16
|
+
Stack,
|
|
17
|
+
Cluster,
|
|
18
|
+
Grid,
|
|
19
|
+
Split,
|
|
20
|
+
Box,
|
|
21
|
+
tokens,
|
|
22
|
+
} from './layout/primitives/index.js'
|
|
23
|
+
export type {
|
|
24
|
+
AdminShellProps,
|
|
25
|
+
PageContainerProps,
|
|
26
|
+
StackProps,
|
|
27
|
+
StackSpace,
|
|
28
|
+
ClusterProps,
|
|
29
|
+
ClusterAlign,
|
|
30
|
+
ClusterJustify,
|
|
31
|
+
GridProps,
|
|
32
|
+
GridResponsive,
|
|
33
|
+
SplitProps,
|
|
34
|
+
BoxProps,
|
|
35
|
+
SpaceToken,
|
|
36
|
+
RadiusToken,
|
|
37
|
+
} from './layout/primitives/index.js'
|
|
38
|
+
|
|
11
39
|
export { Dashboard } from './views/Dashboard.js'
|
|
12
40
|
export { Posts } from './views/Posts.js'
|
|
13
41
|
export { Pages } from './views/Pages.js'
|
package/src/layout/Header.tsx
CHANGED
|
@@ -24,100 +24,100 @@ export function Header({ onToggleSidebar, session, onNavigate }: HeaderProps) {
|
|
|
24
24
|
|
|
25
25
|
return (
|
|
26
26
|
<>
|
|
27
|
-
<header className="
|
|
27
|
+
<header className="border-border bg-background flex h-14 items-center justify-between gap-4 border-b px-4">
|
|
28
28
|
<button
|
|
29
29
|
onClick={onToggleSidebar}
|
|
30
|
-
className="
|
|
30
|
+
className="hover:bg-accent rounded-lg p-2 transition-colors md:hidden"
|
|
31
31
|
aria-label="Toggle sidebar"
|
|
32
32
|
>
|
|
33
|
-
<Menu className="
|
|
33
|
+
<Menu className="text-foreground h-5 w-5" strokeWidth={2} />
|
|
34
34
|
</button>
|
|
35
35
|
|
|
36
|
-
<div className="flex items-center
|
|
37
|
-
<span className="text-lg font-semibold
|
|
36
|
+
<div className="flex items-center md:hidden">
|
|
37
|
+
<span className="text-foreground text-lg font-semibold">Actuate</span>
|
|
38
38
|
</div>
|
|
39
39
|
|
|
40
40
|
<div className="flex-1" />
|
|
41
41
|
|
|
42
42
|
<div className="flex items-center gap-2 sm:gap-3">
|
|
43
|
-
<div className="hidden md:block
|
|
44
|
-
<Search className="
|
|
43
|
+
<div className="relative hidden md:block">
|
|
44
|
+
<Search className="text-muted-foreground pointer-events-none absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2" />
|
|
45
45
|
<input
|
|
46
46
|
type="text"
|
|
47
47
|
placeholder="Search... (⌘K)"
|
|
48
48
|
value={searchQuery}
|
|
49
49
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
50
50
|
onFocus={() => setShowCommandPalette(true)}
|
|
51
|
-
className="
|
|
51
|
+
className="border-border bg-input-background text-foreground focus:ring-ring w-64 rounded-lg border py-1.5 pr-3 pl-9 text-sm focus:border-transparent focus:ring-2 focus:outline-none"
|
|
52
52
|
/>
|
|
53
53
|
</div>
|
|
54
54
|
|
|
55
55
|
<button
|
|
56
56
|
onClick={() => setShowCommandPalette(true)}
|
|
57
|
-
className="
|
|
57
|
+
className="hover:bg-accent rounded-lg p-2 transition-colors md:hidden"
|
|
58
58
|
aria-label="Search"
|
|
59
59
|
>
|
|
60
|
-
<Search className="
|
|
60
|
+
<Search className="text-muted-foreground h-5 w-5" />
|
|
61
61
|
</button>
|
|
62
62
|
|
|
63
63
|
<LocaleSwitcher />
|
|
64
64
|
|
|
65
65
|
<button
|
|
66
66
|
onClick={toggleTheme}
|
|
67
|
-
className="
|
|
67
|
+
className="hover:bg-accent rounded-lg p-2 transition-colors"
|
|
68
68
|
aria-label="Toggle theme"
|
|
69
69
|
>
|
|
70
70
|
{resolvedTheme === 'dark' ? (
|
|
71
|
-
<Sun className="
|
|
71
|
+
<Sun className="text-muted-foreground h-5 w-5" />
|
|
72
72
|
) : (
|
|
73
|
-
<Moon className="
|
|
73
|
+
<Moon className="text-muted-foreground h-5 w-5" />
|
|
74
74
|
)}
|
|
75
75
|
</button>
|
|
76
76
|
|
|
77
77
|
<button
|
|
78
|
-
className="
|
|
78
|
+
className="hover:bg-accent relative rounded-lg p-2 transition-colors"
|
|
79
79
|
aria-label="Notifications"
|
|
80
80
|
>
|
|
81
|
-
<Bell className="
|
|
82
|
-
<span className="absolute top-1.5 right-1.5
|
|
81
|
+
<Bell className="text-muted-foreground h-5 w-5" />
|
|
82
|
+
<span className="bg-destructive absolute top-1.5 right-1.5 h-2 w-2 rounded-full" />
|
|
83
83
|
</button>
|
|
84
84
|
|
|
85
85
|
<DropdownMenu.Root>
|
|
86
86
|
<DropdownMenu.Trigger asChild>
|
|
87
|
-
<button className="flex items-center gap-2 p-1.5
|
|
88
|
-
<div className="
|
|
89
|
-
<span className="text-
|
|
87
|
+
<button className="hover:bg-accent flex items-center gap-2 rounded-lg p-1.5 transition-colors">
|
|
88
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-full bg-linear-to-br from-blue-500 to-purple-600">
|
|
89
|
+
<span className="text-sm font-medium text-white">
|
|
90
90
|
{session?.user?.name?.charAt(0)?.toUpperCase() ?? 'A'}
|
|
91
91
|
</span>
|
|
92
92
|
</div>
|
|
93
|
-
<ChevronDown className="
|
|
93
|
+
<ChevronDown className="text-muted-foreground hidden h-4 w-4 sm:block" />
|
|
94
94
|
</button>
|
|
95
95
|
</DropdownMenu.Trigger>
|
|
96
96
|
|
|
97
97
|
<DropdownMenu.Portal>
|
|
98
98
|
<DropdownMenu.Content
|
|
99
|
-
className="
|
|
99
|
+
className="bg-popover text-popover-foreground border-border z-50 min-w-[200px] rounded-lg border p-1 shadow-lg"
|
|
100
100
|
align="end"
|
|
101
101
|
sideOffset={5}
|
|
102
102
|
>
|
|
103
|
-
<div className="px-3 py-2
|
|
103
|
+
<div className="border-border border-b px-3 py-2">
|
|
104
104
|
<p className="text-sm font-medium">{session?.user?.name ?? 'Admin User'}</p>
|
|
105
|
-
<p className="text-
|
|
105
|
+
<p className="text-muted-foreground text-xs">
|
|
106
106
|
{session?.user?.email ?? 'admin@example.com'}
|
|
107
107
|
</p>
|
|
108
108
|
</div>
|
|
109
|
-
<DropdownMenu.Item className="flex items-center gap-2 px-3 py-2 text-sm
|
|
110
|
-
<User className="
|
|
109
|
+
<DropdownMenu.Item className="hover:bg-accent flex cursor-pointer items-center gap-2 rounded px-3 py-2 text-sm outline-none">
|
|
110
|
+
<User className="h-4 w-4" />
|
|
111
111
|
Profile
|
|
112
112
|
</DropdownMenu.Item>
|
|
113
113
|
<DropdownMenu.Item
|
|
114
|
-
className="flex items-center gap-2 px-3 py-2 text-sm
|
|
114
|
+
className="hover:bg-accent flex cursor-pointer items-center gap-2 rounded px-3 py-2 text-sm outline-none"
|
|
115
115
|
onSelect={() => onNavigate('/settings')}
|
|
116
116
|
>
|
|
117
117
|
Settings
|
|
118
118
|
</DropdownMenu.Item>
|
|
119
|
-
<DropdownMenu.Separator className="
|
|
120
|
-
<DropdownMenu.Item className="flex items-center gap-2 px-3 py-2 text-sm
|
|
119
|
+
<DropdownMenu.Separator className="bg-border my-1 h-px" />
|
|
120
|
+
<DropdownMenu.Item className="text-destructive hover:bg-accent flex cursor-pointer items-center gap-2 rounded px-3 py-2 text-sm outline-none">
|
|
121
121
|
Logout
|
|
122
122
|
</DropdownMenu.Item>
|
|
123
123
|
</DropdownMenu.Content>
|
package/src/layout/Layout.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { useState, useEffect, type ReactNode } from 'react'
|
|
|
4
4
|
import { Sidebar } from './Sidebar.js'
|
|
5
5
|
import { Header } from './Header.js'
|
|
6
6
|
import { Breadcrumbs } from '../components/Breadcrumbs.js'
|
|
7
|
+
import { AdminShell } from './primitives/AdminShell.js'
|
|
7
8
|
import { Toaster } from 'sonner'
|
|
8
9
|
|
|
9
10
|
export interface LayoutProps {
|
|
@@ -14,6 +15,20 @@ export interface LayoutProps {
|
|
|
14
15
|
children: ReactNode
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Layout — thin shell that wires Sidebar/Header/Breadcrumbs into the
|
|
20
|
+
* `<AdminShell>` primitive.
|
|
21
|
+
*
|
|
22
|
+
* `AdminShell` (in `./primitives/AdminShell.tsx`) owns the actual layout
|
|
23
|
+
* algorithm (CSS Grid on desktop, fixed slide-in overlay on mobile,
|
|
24
|
+
* JS-driven `matchMedia` breakpoint) so that every layout decision lives
|
|
25
|
+
* in one place. Per-product chrome (Sidebar, Header, Breadcrumbs)
|
|
26
|
+
* composes on top of it.
|
|
27
|
+
*
|
|
28
|
+
* DO NOT re-implement layout logic in this file — extend `AdminShell`
|
|
29
|
+
* instead. The previous flex + `fixed`↔`static` toggle caused recurring
|
|
30
|
+
* sidebar-overlap bugs; the grid-based primitive is the contract.
|
|
31
|
+
*/
|
|
17
32
|
export function Layout({ config, session, currentPath, onNavigate, children }: LayoutProps) {
|
|
18
33
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(false)
|
|
19
34
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false)
|
|
@@ -22,54 +37,32 @@ export function Layout({ config, session, currentPath, onNavigate, children }: L
|
|
|
22
37
|
setMobileSidebarOpen(false)
|
|
23
38
|
}, [currentPath])
|
|
24
39
|
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
const handleResize = () => {
|
|
27
|
-
if (window.innerWidth < 1024) {
|
|
28
|
-
setMobileSidebarOpen(false)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
window.addEventListener('resize', handleResize)
|
|
32
|
-
return () => window.removeEventListener('resize', handleResize)
|
|
33
|
-
}, [])
|
|
34
|
-
|
|
35
40
|
return (
|
|
36
|
-
|
|
41
|
+
<>
|
|
37
42
|
<Toaster position="bottom-right" />
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
43
|
+
<AdminShell
|
|
44
|
+
mobileSidebarOpen={mobileSidebarOpen}
|
|
45
|
+
onMobileSidebarClose={() => setMobileSidebarOpen(false)}
|
|
46
|
+
sidebar={
|
|
47
|
+
<Sidebar
|
|
48
|
+
collapsed={sidebarCollapsed}
|
|
49
|
+
onToggleCollapse={() => setSidebarCollapsed(!sidebarCollapsed)}
|
|
50
|
+
currentPath={currentPath}
|
|
51
|
+
onNavigate={onNavigate}
|
|
52
|
+
config={config}
|
|
53
|
+
/>
|
|
54
|
+
}
|
|
55
|
+
header={
|
|
56
|
+
<Header
|
|
57
|
+
onToggleSidebar={() => setMobileSidebarOpen(!mobileSidebarOpen)}
|
|
58
|
+
session={session}
|
|
59
|
+
onNavigate={onNavigate}
|
|
60
|
+
/>
|
|
61
|
+
}
|
|
62
|
+
breadcrumbs={<Breadcrumbs currentPath={currentPath} onNavigate={onNavigate} />}
|
|
50
63
|
>
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
currentPath={currentPath}
|
|
55
|
-
onNavigate={onNavigate}
|
|
56
|
-
config={config}
|
|
57
|
-
/>
|
|
58
|
-
</div>
|
|
59
|
-
|
|
60
|
-
<div className="flex-1 flex flex-col overflow-hidden">
|
|
61
|
-
<Header
|
|
62
|
-
onToggleSidebar={() => setMobileSidebarOpen(!mobileSidebarOpen)}
|
|
63
|
-
session={session}
|
|
64
|
-
onNavigate={onNavigate}
|
|
65
|
-
/>
|
|
66
|
-
|
|
67
|
-
<Breadcrumbs currentPath={currentPath} onNavigate={onNavigate} />
|
|
68
|
-
|
|
69
|
-
<main className="flex-1 overflow-auto bg-background">
|
|
70
|
-
<div className="h-full">{children}</div>
|
|
71
|
-
</main>
|
|
72
|
-
</div>
|
|
73
|
-
</div>
|
|
64
|
+
<div className="h-full">{children}</div>
|
|
65
|
+
</AdminShell>
|
|
66
|
+
</>
|
|
74
67
|
)
|
|
75
68
|
}
|