@actuate-media/cms-admin 0.73.0 → 0.74.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/CHANGELOG.md +16 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +3 -2
- package/dist/AdminRoot.js.map +1 -1
- package/dist/LoginRoot.d.ts.map +1 -1
- package/dist/LoginRoot.js +2 -1
- package/dist/LoginRoot.js.map +1 -1
- package/dist/__tests__/components/theme-provider-accent.test.d.ts +2 -0
- package/dist/__tests__/components/theme-provider-accent.test.d.ts.map +1 -0
- package/dist/__tests__/components/theme-provider-accent.test.js +64 -0
- package/dist/__tests__/components/theme-provider-accent.test.js.map +1 -0
- package/dist/__tests__/views/portal-container.test.js +13 -0
- package/dist/__tests__/views/portal-container.test.js.map +1 -1
- package/dist/__tests__/views/redirects-pane.render.test.js +6 -0
- package/dist/__tests__/views/redirects-pane.render.test.js.map +1 -1
- package/dist/__tests__/views/seo-content-pane.render.test.js +13 -4
- package/dist/__tests__/views/seo-content-pane.render.test.js.map +1 -1
- package/dist/actuate-admin.css +1 -1
- package/dist/components/CoauthorResultPopover.js +1 -1
- package/dist/components/EditorSeoAside.d.ts +20 -0
- package/dist/components/EditorSeoAside.d.ts.map +1 -0
- package/dist/components/EditorSeoAside.js +30 -0
- package/dist/components/EditorSeoAside.js.map +1 -0
- package/dist/components/MediaPickerModal.js +1 -1
- package/dist/components/SEOConfigPanel.js +1 -1
- package/dist/components/SchedulePublishDialog.js +1 -1
- package/dist/components/SharePreviewLinkDialog.js +1 -1
- package/dist/components/ThemeProvider.d.ts.map +1 -1
- package/dist/components/ThemeProvider.js +2 -1
- package/dist/components/ThemeProvider.js.map +1 -1
- package/dist/components/VersionHistory.js +1 -1
- package/dist/components/seo/SeoEditorPane.d.ts.map +1 -1
- package/dist/components/seo/SeoEditorPane.js +28 -8
- package/dist/components/seo/SeoEditorPane.js.map +1 -1
- package/dist/components/seo/SeoIssueFixPanel.d.ts.map +1 -1
- package/dist/components/seo/SeoIssueFixPanel.js +13 -3
- package/dist/components/seo/SeoIssueFixPanel.js.map +1 -1
- package/dist/components/seo/primitives.d.ts.map +1 -1
- package/dist/components/seo/primitives.js +3 -3
- package/dist/components/seo/primitives.js.map +1 -1
- package/dist/components/ui/Card.js +5 -5
- package/dist/components/ui/Card.js.map +1 -1
- package/dist/components/ui/InspectorPane.js +3 -3
- package/dist/components/ui/InspectorPane.js.map +1 -1
- package/dist/components/ui/PanelSection.d.ts +16 -0
- package/dist/components/ui/PanelSection.d.ts.map +1 -0
- package/dist/components/ui/PanelSection.js +15 -0
- package/dist/components/ui/PanelSection.js.map +1 -0
- package/dist/lib/admin-root-element.d.ts +9 -0
- package/dist/lib/admin-root-element.d.ts.map +1 -0
- package/dist/lib/admin-root-element.js +14 -0
- package/dist/lib/admin-root-element.js.map +1 -0
- package/dist/lib/portal-container.d.ts.map +1 -1
- package/dist/lib/portal-container.js +2 -3
- package/dist/lib/portal-container.js.map +1 -1
- package/dist/lib/seo-service.d.ts +89 -0
- package/dist/lib/seo-service.d.ts.map +1 -1
- package/dist/lib/seo-service.js +50 -2
- package/dist/lib/seo-service.js.map +1 -1
- package/dist/views/ApiKeys.js +2 -2
- package/dist/views/Dashboard.d.ts.map +1 -1
- package/dist/views/Dashboard.js +12 -14
- package/dist/views/Dashboard.js.map +1 -1
- package/dist/views/MediaBrowser.d.ts.map +1 -1
- package/dist/views/MediaBrowser.js +35 -6
- package/dist/views/MediaBrowser.js.map +1 -1
- package/dist/views/PageEditor.js +1 -1
- package/dist/views/Posts/PostTypeEditor.js +3 -3
- package/dist/views/SEO.d.ts.map +1 -1
- package/dist/views/SEO.js +4 -2
- package/dist/views/SEO.js.map +1 -1
- package/dist/views/ScriptTags.js +1 -1
- package/dist/views/ScriptTags.js.map +1 -1
- package/dist/views/Users.js +1 -1
- package/dist/views/page-editor/EditorTopBar.d.ts +4 -1
- package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
- package/dist/views/page-editor/EditorTopBar.js +5 -3
- package/dist/views/page-editor/EditorTopBar.js.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.js +24 -1
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/dist/views/page-editor/SectionInspector.js +2 -2
- package/dist/views/page-editor/SectionsPanel.js +1 -1
- package/dist/views/post-editor/PostHeaderPanel.js +1 -1
- package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.js +24 -1
- package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
- package/dist/views/seo/LinksTab.d.ts +4 -0
- package/dist/views/seo/LinksTab.d.ts.map +1 -0
- package/dist/views/seo/LinksTab.js +111 -0
- package/dist/views/seo/LinksTab.js.map +1 -0
- package/dist/views/seo/OverviewTab.js +1 -1
- package/dist/views/seo/OverviewTab.js.map +1 -1
- package/dist/views/seo/RedirectsTab.d.ts.map +1 -1
- package/dist/views/seo/RedirectsTab.js +39 -6
- package/dist/views/seo/RedirectsTab.js.map +1 -1
- package/dist/views/seo/TechnicalTab.js +2 -2
- package/dist/views/settings/AISettingsTab.js +1 -1
- package/dist/views/settings/AiFeaturesCard.js +1 -1
- package/dist/views/settings/AiFeaturesCard.js.map +1 -1
- package/dist/views/settings/AppearanceTab.js +1 -1
- package/dist/views/settings/BrandVoiceCard.js +1 -1
- package/dist/views/settings/EmailSettingsTab.js +2 -2
- package/dist/views/settings/GeneralSettingsTab.js +1 -1
- package/dist/views/settings/SecurityStatusCard.js +1 -1
- package/dist/views/settings/SeoSettingsTab.js +4 -4
- package/dist/views/settings/UpdatesTab.js +2 -2
- package/dist/views/settings/components.js +3 -3
- package/dist/views/settings/components.js.map +1 -1
- package/package.json +2 -2
- package/src/AdminRoot.tsx +3 -2
- package/src/LoginRoot.tsx +2 -1
- package/src/__tests__/components/theme-provider-accent.test.tsx +84 -0
- package/src/__tests__/views/portal-container.test.tsx +14 -0
- package/src/__tests__/views/redirects-pane.render.test.tsx +6 -0
- package/src/__tests__/views/seo-content-pane.render.test.tsx +14 -4
- package/src/components/CoauthorResultPopover.tsx +1 -1
- package/src/components/EditorSeoAside.tsx +149 -0
- package/src/components/MediaPickerModal.tsx +1 -1
- package/src/components/SEOConfigPanel.tsx +2 -2
- package/src/components/SchedulePublishDialog.tsx +1 -1
- package/src/components/SharePreviewLinkDialog.tsx +1 -1
- package/src/components/ThemeProvider.tsx +3 -1
- package/src/components/VersionHistory.tsx +1 -1
- package/src/components/seo/SeoEditorPane.tsx +41 -12
- package/src/components/seo/SeoIssueFixPanel.tsx +16 -4
- package/src/components/seo/primitives.tsx +7 -3
- package/src/components/ui/Card.tsx +5 -5
- package/src/components/ui/InspectorPane.tsx +3 -3
- package/src/components/ui/PanelSection.tsx +53 -0
- package/src/lib/admin-root-element.ts +15 -0
- package/src/lib/portal-container.ts +3 -2
- package/src/lib/seo-service.ts +143 -2
- package/src/views/ApiKeys.tsx +2 -2
- package/src/views/Dashboard.tsx +40 -53
- package/src/views/MediaBrowser.tsx +62 -12
- package/src/views/PageEditor.tsx +1 -1
- package/src/views/Posts/PostTypeEditor.tsx +3 -3
- package/src/views/SEO.tsx +6 -0
- package/src/views/ScriptTags.tsx +2 -2
- package/src/views/Users.tsx +1 -1
- package/src/views/page-editor/EditorTopBar.tsx +24 -0
- package/src/views/page-editor/PageSectionEditor.tsx +43 -3
- package/src/views/page-editor/SectionInspector.tsx +2 -2
- package/src/views/page-editor/SectionsPanel.tsx +1 -1
- package/src/views/post-editor/PostHeaderPanel.tsx +1 -1
- package/src/views/post-editor/PostSectionEditor.tsx +43 -3
- package/src/views/seo/LinksTab.tsx +462 -0
- package/src/views/seo/OverviewTab.tsx +1 -1
- package/src/views/seo/RedirectsTab.tsx +97 -4
- package/src/views/seo/TechnicalTab.tsx +2 -2
- package/src/views/settings/AISettingsTab.tsx +3 -3
- package/src/views/settings/AiFeaturesCard.tsx +1 -1
- package/src/views/settings/AppearanceTab.tsx +1 -1
- package/src/views/settings/BrandVoiceCard.tsx +2 -2
- package/src/views/settings/EmailSettingsTab.tsx +2 -2
- package/src/views/settings/GeneralSettingsTab.tsx +3 -3
- package/src/views/settings/SecurityStatusCard.tsx +1 -1
- package/src/views/settings/SeoSettingsTab.tsx +6 -6
- package/src/views/settings/UpdatesTab.tsx +3 -3
- package/src/views/settings/components.tsx +3 -3
package/src/views/SEO.tsx
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
ClipboardCheck,
|
|
16
16
|
RefreshCw,
|
|
17
17
|
Bot,
|
|
18
|
+
Link2,
|
|
18
19
|
} from 'lucide-react'
|
|
19
20
|
import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
20
21
|
import { toast } from 'sonner'
|
|
@@ -30,6 +31,7 @@ import { ContentTab } from './seo/ContentTab.js'
|
|
|
30
31
|
import { TechnicalTab } from './seo/TechnicalTab.js'
|
|
31
32
|
import { RedirectsTab } from './seo/RedirectsTab.js'
|
|
32
33
|
import { AuditTab } from './seo/AuditTab.js'
|
|
34
|
+
import { LinksTab } from './seo/LinksTab.js'
|
|
33
35
|
|
|
34
36
|
export interface SEOProps {
|
|
35
37
|
onNavigate?: (path: string) => void
|
|
@@ -39,6 +41,7 @@ export interface SEOProps {
|
|
|
39
41
|
const TABS = [
|
|
40
42
|
{ id: 'overview', label: 'Overview', icon: LayoutDashboard },
|
|
41
43
|
{ id: 'content', label: 'Content SEO', icon: FileText },
|
|
44
|
+
{ id: 'links', label: 'Links', icon: Link2 },
|
|
42
45
|
{ id: 'technical', label: 'Technical', icon: Settings2 },
|
|
43
46
|
{ id: 'redirects', label: 'Redirects', icon: ArrowRightLeft },
|
|
44
47
|
{ id: 'audit', label: 'Audit', icon: ClipboardCheck },
|
|
@@ -212,6 +215,9 @@ export function SEO({ onNavigate, initialTab = 'overview' }: SEOProps) {
|
|
|
212
215
|
/>
|
|
213
216
|
)}
|
|
214
217
|
</Tabs.Content>
|
|
218
|
+
<Tabs.Content value="links" tabIndex={-1}>
|
|
219
|
+
{activeTab === 'links' && <LinksTab onNavigate={onNavigate} />}
|
|
220
|
+
</Tabs.Content>
|
|
215
221
|
<Tabs.Content value="technical" tabIndex={-1}>
|
|
216
222
|
{activeTab === 'technical' && <TechnicalTab />}
|
|
217
223
|
</Tabs.Content>
|
package/src/views/ScriptTags.tsx
CHANGED
|
@@ -236,7 +236,7 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
236
236
|
<div className="flex h-full min-h-0 flex-col">
|
|
237
237
|
<div className="p-3 pr-6 pb-0 sm:p-4 sm:pr-8 sm:pb-0">
|
|
238
238
|
{error && (
|
|
239
|
-
<div className="border-destructive/30 bg-destructive/10 mb-4 flex items-center gap-3 rounded-lg border p-
|
|
239
|
+
<div className="border-destructive/30 bg-destructive/10 mb-4 flex items-center gap-3 rounded-lg border p-4">
|
|
240
240
|
<AlertTriangle className="text-destructive h-5 w-5 shrink-0" />
|
|
241
241
|
<span className="text-destructive flex-1 text-sm">{error}</span>
|
|
242
242
|
<button
|
|
@@ -334,7 +334,7 @@ export function ScriptTags({ initialSelectedId, initialNew }: ScriptTagsProps) {
|
|
|
334
334
|
</div>
|
|
335
335
|
|
|
336
336
|
<SplitPaneLayout>
|
|
337
|
-
<SplitPaneList className="p-
|
|
337
|
+
<SplitPaneList className="p-4 pr-8 sm:pr-8">
|
|
338
338
|
{tags.length === 0 && !error ? (
|
|
339
339
|
<div className="rounded-lg border border-[var(--bdr)] bg-[var(--card)] p-12 text-center">
|
|
340
340
|
<Code2 className="mx-auto mb-3 h-10 w-10 text-[var(--sub)]" />
|
package/src/views/Users.tsx
CHANGED
|
@@ -982,7 +982,7 @@ export function Users({ embedded = false, currentPath }: UsersProps = {}) {
|
|
|
982
982
|
|
|
983
983
|
{/* Role filter pills */}
|
|
984
984
|
<div
|
|
985
|
-
className="border-border flex flex-wrap gap-2 border-b px-
|
|
985
|
+
className="border-border flex flex-wrap gap-2 border-b px-6 py-4"
|
|
986
986
|
role="group"
|
|
987
987
|
aria-label="Filter by role"
|
|
988
988
|
>
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Loader2,
|
|
11
11
|
type LucideIcon,
|
|
12
12
|
Monitor,
|
|
13
|
+
Search,
|
|
13
14
|
Settings2,
|
|
14
15
|
Smartphone,
|
|
15
16
|
Tablet,
|
|
@@ -57,6 +58,9 @@ interface EditorTopBarProps {
|
|
|
57
58
|
*/
|
|
58
59
|
onToggleSitePreview?: () => void
|
|
59
60
|
onOpenSettings: () => void
|
|
61
|
+
/** Open the document SEO panel (meta, social, schema, analysis). */
|
|
62
|
+
onOpenSeo?: () => void
|
|
63
|
+
seoOpen?: boolean
|
|
60
64
|
onSaveDraft: () => void
|
|
61
65
|
onPublish: () => void
|
|
62
66
|
/**
|
|
@@ -102,6 +106,8 @@ export function EditorTopBar({
|
|
|
102
106
|
sitePreview = false,
|
|
103
107
|
onToggleSitePreview,
|
|
104
108
|
onOpenSettings,
|
|
109
|
+
onOpenSeo,
|
|
110
|
+
seoOpen = false,
|
|
105
111
|
onSaveDraft,
|
|
106
112
|
onPublish,
|
|
107
113
|
structuralTypes = [],
|
|
@@ -192,6 +198,24 @@ export function EditorTopBar({
|
|
|
192
198
|
<Settings2 className="h-4 w-4" aria-hidden />
|
|
193
199
|
</button>
|
|
194
200
|
|
|
201
|
+
{onOpenSeo && (
|
|
202
|
+
<button
|
|
203
|
+
type="button"
|
|
204
|
+
onClick={onOpenSeo}
|
|
205
|
+
aria-pressed={seoOpen}
|
|
206
|
+
aria-label="SEO"
|
|
207
|
+
title="SEO — meta tags, social preview, schema, and analysis"
|
|
208
|
+
className={`inline-flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-sm font-medium transition-colors ${
|
|
209
|
+
seoOpen
|
|
210
|
+
? 'bg-primary text-primary-foreground'
|
|
211
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-accent'
|
|
212
|
+
}`}
|
|
213
|
+
>
|
|
214
|
+
<Search className="h-4 w-4 shrink-0" aria-hidden />
|
|
215
|
+
<span className="hidden sm:inline">SEO</span>
|
|
216
|
+
</button>
|
|
217
|
+
)}
|
|
218
|
+
|
|
195
219
|
{onHistory && (
|
|
196
220
|
<button
|
|
197
221
|
type="button"
|
|
@@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
|
4
4
|
import { AlertTriangle, Blocks, Loader2, Lock } from 'lucide-react'
|
|
5
5
|
import { toast } from 'sonner'
|
|
6
6
|
import { ConfirmDialog } from '../../components/ui/ConfirmDialog.js'
|
|
7
|
+
import { EditorSeoAside } from '../../components/EditorSeoAside.js'
|
|
7
8
|
import { ErrorBoundary } from '../../components/ErrorBoundary.js'
|
|
8
9
|
import { VersionHistory } from '../../components/VersionHistory.js'
|
|
9
10
|
import {
|
|
@@ -97,6 +98,7 @@ export function PageSectionEditor({
|
|
|
97
98
|
|
|
98
99
|
const [addOpen, setAddOpen] = useState(false)
|
|
99
100
|
const [settingsOpen, setSettingsOpen] = useState(false)
|
|
101
|
+
const [seoOpen, setSeoOpen] = useState(false)
|
|
100
102
|
const [historyOpen, setHistoryOpen] = useState(false)
|
|
101
103
|
const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null)
|
|
102
104
|
const [confirmPublish, setConfirmPublish] = useState(false)
|
|
@@ -336,6 +338,7 @@ export function PageSectionEditor({
|
|
|
336
338
|
.then((p) => {
|
|
337
339
|
setPage(p)
|
|
338
340
|
setSelectedId(null)
|
|
341
|
+
setSeoOpen(false)
|
|
339
342
|
setDirty(false)
|
|
340
343
|
setSaveState('idle')
|
|
341
344
|
setValidation(null)
|
|
@@ -345,6 +348,27 @@ export function PageSectionEditor({
|
|
|
345
348
|
})
|
|
346
349
|
}, [page?.id])
|
|
347
350
|
|
|
351
|
+
const handleSeoSaved = useCallback(() => {
|
|
352
|
+
if (!page?.id) return
|
|
353
|
+
fetchPageForEditor(page.id)
|
|
354
|
+
.then((p) => setPage(p))
|
|
355
|
+
.catch(() => {
|
|
356
|
+
/* SEO API saved; editor meta will sync on next full load */
|
|
357
|
+
})
|
|
358
|
+
}, [page?.id])
|
|
359
|
+
|
|
360
|
+
const handleToggleSeo = useCallback(() => {
|
|
361
|
+
setSeoOpen((open) => {
|
|
362
|
+
if (!open) setSelectedId(null)
|
|
363
|
+
return !open
|
|
364
|
+
})
|
|
365
|
+
}, [])
|
|
366
|
+
|
|
367
|
+
const handleSelectSection = useCallback((id: string | null) => {
|
|
368
|
+
setSeoOpen(false)
|
|
369
|
+
setSelectedId(id)
|
|
370
|
+
}, [])
|
|
371
|
+
|
|
348
372
|
// ─── Navigation with unsaved guard ───────────────────────────────────
|
|
349
373
|
const guardedNavigate = useCallback(
|
|
350
374
|
(path: string) => {
|
|
@@ -492,6 +516,8 @@ export function PageSectionEditor({
|
|
|
492
516
|
onToggleSitePreview={page.id && page.slug ? sitePreview.toggle : undefined}
|
|
493
517
|
onHistory={page.id ? () => setHistoryOpen(true) : undefined}
|
|
494
518
|
onOpenSettings={() => setSettingsOpen(true)}
|
|
519
|
+
onOpenSeo={handleToggleSeo}
|
|
520
|
+
seoOpen={seoOpen}
|
|
495
521
|
onSaveDraft={handleSaveDraft}
|
|
496
522
|
onPublish={requestPublish}
|
|
497
523
|
structuralTypes={structuralTypes}
|
|
@@ -550,7 +576,7 @@ export function PageSectionEditor({
|
|
|
550
576
|
sections={page.sections}
|
|
551
577
|
selectedId={selectedId}
|
|
552
578
|
canEdit={effectiveCanEdit}
|
|
553
|
-
onSelect={
|
|
579
|
+
onSelect={handleSelectSection}
|
|
554
580
|
onReorder={handleReorder}
|
|
555
581
|
onToggleVisibility={handleToggleVisibility}
|
|
556
582
|
onDuplicate={handleDuplicate}
|
|
@@ -571,7 +597,7 @@ export function PageSectionEditor({
|
|
|
571
597
|
sections={page.sections}
|
|
572
598
|
selectedId={selectedId}
|
|
573
599
|
viewport={viewport}
|
|
574
|
-
onSelect={
|
|
600
|
+
onSelect={handleSelectSection}
|
|
575
601
|
onAddSection={() => setAddOpen(true)}
|
|
576
602
|
structuralTypes={structuralTypes}
|
|
577
603
|
sitePreviewAvailable={sitePreviewAvailable && !sitePreview.active}
|
|
@@ -579,7 +605,7 @@ export function PageSectionEditor({
|
|
|
579
605
|
)}
|
|
580
606
|
</div>
|
|
581
607
|
|
|
582
|
-
{selected && (
|
|
608
|
+
{selected && !seoOpen && (
|
|
583
609
|
<SectionInspector
|
|
584
610
|
section={selected}
|
|
585
611
|
canEdit={effectiveCanEdit}
|
|
@@ -590,6 +616,20 @@ export function PageSectionEditor({
|
|
|
590
616
|
onToggleVisibility={() => handleToggleVisibility(selected.id)}
|
|
591
617
|
/>
|
|
592
618
|
)}
|
|
619
|
+
|
|
620
|
+
{seoOpen && (
|
|
621
|
+
<EditorSeoAside
|
|
622
|
+
entityType="page"
|
|
623
|
+
entityId={page.id}
|
|
624
|
+
title={page.title}
|
|
625
|
+
seoTitle={page.seoTitle}
|
|
626
|
+
seoDescription={page.seoDescription}
|
|
627
|
+
canEdit={effectiveCanEdit}
|
|
628
|
+
onClose={() => setSeoOpen(false)}
|
|
629
|
+
onDraftChange={handlePageSettings}
|
|
630
|
+
onSaved={handleSeoSaved}
|
|
631
|
+
/>
|
|
632
|
+
)}
|
|
593
633
|
</div>
|
|
594
634
|
</div>
|
|
595
635
|
|
|
@@ -109,7 +109,7 @@ export function SectionInspector({
|
|
|
109
109
|
aria-label={`${section.name} (read-only)`}
|
|
110
110
|
className={INSPECTOR_SHELL}
|
|
111
111
|
>
|
|
112
|
-
<div className="border-border flex items-center justify-between border-b px-
|
|
112
|
+
<div className="border-border flex items-center justify-between border-b px-6 py-4">
|
|
113
113
|
<div className="min-w-0">
|
|
114
114
|
<h2 className="text-foreground truncate text-sm font-medium">{section.name}</h2>
|
|
115
115
|
<p className="text-muted-foreground text-xs">Managed outside the editor</p>
|
|
@@ -142,7 +142,7 @@ export function SectionInspector({
|
|
|
142
142
|
|
|
143
143
|
return (
|
|
144
144
|
<ResizableAside role="dialog" aria-label={`Edit ${section.name}`} className={INSPECTOR_SHELL}>
|
|
145
|
-
<div className="border-border flex items-center justify-between border-b px-
|
|
145
|
+
<div className="border-border flex items-center justify-between border-b px-6 py-4">
|
|
146
146
|
<div className="min-w-0">
|
|
147
147
|
<h2 className="text-foreground truncate text-sm font-medium">{section.name}</h2>
|
|
148
148
|
<p className="text-muted-foreground text-xs">{def?.name ?? 'Section'}</p>
|
|
@@ -78,7 +78,7 @@ export function SectionsPanel({
|
|
|
78
78
|
className="bg-card border-border flex h-full w-72 shrink-0 flex-col border-r"
|
|
79
79
|
aria-label="Page sections"
|
|
80
80
|
>
|
|
81
|
-
<div className="border-border flex items-center justify-between border-b px-
|
|
81
|
+
<div className="border-border flex items-center justify-between border-b px-6 py-4">
|
|
82
82
|
<div className="flex items-center gap-2">
|
|
83
83
|
<h2 className="text-foreground text-sm font-medium">Sections</h2>
|
|
84
84
|
<span className="bg-muted text-muted-foreground rounded-full px-2 py-0.5 text-xs font-medium tabular-nums">
|
|
@@ -33,7 +33,7 @@ export function PostHeaderPanel({ header, canEdit, onClose, onChange }: PostHead
|
|
|
33
33
|
aria-label="Edit post header"
|
|
34
34
|
className="bg-card border-border flex h-full flex-col border-l"
|
|
35
35
|
>
|
|
36
|
-
<div className="border-border flex items-center justify-between border-b px-
|
|
36
|
+
<div className="border-border flex items-center justify-between border-b px-6 py-4">
|
|
37
37
|
<div className="min-w-0">
|
|
38
38
|
<h2 className="text-foreground truncate text-sm font-medium">Post header</h2>
|
|
39
39
|
<p className="text-muted-foreground text-xs">Layout for every post of this type</p>
|
|
@@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
|
|
|
4
4
|
import { AlertTriangle, Loader2, Lock } from 'lucide-react'
|
|
5
5
|
import { toast } from 'sonner'
|
|
6
6
|
import { ConfirmDialog } from '../../components/ui/ConfirmDialog.js'
|
|
7
|
+
import { EditorSeoAside } from '../../components/EditorSeoAside.js'
|
|
7
8
|
import { ErrorBoundary } from '../../components/ErrorBoundary.js'
|
|
8
9
|
import { VersionHistory } from '../../components/VersionHistory.js'
|
|
9
10
|
import {
|
|
@@ -122,6 +123,7 @@ export function PostSectionEditor({
|
|
|
122
123
|
|
|
123
124
|
const [addOpen, setAddOpen] = useState(false)
|
|
124
125
|
const [fieldsOpen, setFieldsOpen] = useState(false)
|
|
126
|
+
const [seoOpen, setSeoOpen] = useState(false)
|
|
125
127
|
const [historyOpen, setHistoryOpen] = useState(false)
|
|
126
128
|
const [deleteTargetId, setDeleteTargetId] = useState<string | null>(null)
|
|
127
129
|
const [confirmPublish, setConfirmPublish] = useState(false)
|
|
@@ -386,6 +388,7 @@ export function PostSectionEditor({
|
|
|
386
388
|
.then((p) => {
|
|
387
389
|
setPost(p)
|
|
388
390
|
setSelectedId(null)
|
|
391
|
+
setSeoOpen(false)
|
|
389
392
|
setDirty(false)
|
|
390
393
|
setSaveState('idle')
|
|
391
394
|
setValidation(null)
|
|
@@ -395,6 +398,27 @@ export function PostSectionEditor({
|
|
|
395
398
|
})
|
|
396
399
|
}, [postType, post?.id])
|
|
397
400
|
|
|
401
|
+
const handleSeoSaved = useCallback(() => {
|
|
402
|
+
if (!post?.id) return
|
|
403
|
+
fetchPostForEditor(postType, post.id)
|
|
404
|
+
.then((p) => setPost(p))
|
|
405
|
+
.catch(() => {
|
|
406
|
+
/* SEO API saved; editor meta will sync on next full load */
|
|
407
|
+
})
|
|
408
|
+
}, [postType, post?.id])
|
|
409
|
+
|
|
410
|
+
const handleToggleSeo = useCallback(() => {
|
|
411
|
+
setSeoOpen((open) => {
|
|
412
|
+
if (!open) setSelectedId(null)
|
|
413
|
+
return !open
|
|
414
|
+
})
|
|
415
|
+
}, [])
|
|
416
|
+
|
|
417
|
+
const handleSelectSection = useCallback((id: string | null) => {
|
|
418
|
+
setSeoOpen(false)
|
|
419
|
+
setSelectedId(id)
|
|
420
|
+
}, [])
|
|
421
|
+
|
|
398
422
|
const guardedNavigate = useCallback(
|
|
399
423
|
(path: string) => {
|
|
400
424
|
if (dirty) setLeaveTarget(path)
|
|
@@ -534,6 +558,8 @@ export function PostSectionEditor({
|
|
|
534
558
|
onToggleSitePreview={post.id && post.slug ? sitePreview.toggle : undefined}
|
|
535
559
|
onHistory={post.id ? () => setHistoryOpen(true) : undefined}
|
|
536
560
|
onOpenSettings={() => setFieldsOpen(true)}
|
|
561
|
+
onOpenSeo={handleToggleSeo}
|
|
562
|
+
seoOpen={seoOpen}
|
|
537
563
|
onSaveDraft={handleSaveDraft}
|
|
538
564
|
onPublish={requestPublish}
|
|
539
565
|
structuralTypes={structuralTypes}
|
|
@@ -569,7 +595,7 @@ export function PostSectionEditor({
|
|
|
569
595
|
sections={post.sections}
|
|
570
596
|
selectedId={selectedId}
|
|
571
597
|
canEdit={canEdit}
|
|
572
|
-
onSelect={
|
|
598
|
+
onSelect={handleSelectSection}
|
|
573
599
|
onReorder={handleReorder}
|
|
574
600
|
onToggleVisibility={handleToggleVisibility}
|
|
575
601
|
onDuplicate={handleDuplicate}
|
|
@@ -592,7 +618,7 @@ export function PostSectionEditor({
|
|
|
592
618
|
context={context}
|
|
593
619
|
selectedId={selectedId}
|
|
594
620
|
viewport={viewport}
|
|
595
|
-
onSelect={
|
|
621
|
+
onSelect={handleSelectSection}
|
|
596
622
|
onAddSection={() => setAddOpen(true)}
|
|
597
623
|
structuralTypes={structuralTypes}
|
|
598
624
|
sitePreviewAvailable={sitePreviewAvailable && !sitePreview.active}
|
|
@@ -600,7 +626,7 @@ export function PostSectionEditor({
|
|
|
600
626
|
)}
|
|
601
627
|
</div>
|
|
602
628
|
|
|
603
|
-
{selected && (
|
|
629
|
+
{selected && !seoOpen && (
|
|
604
630
|
<SectionInspector
|
|
605
631
|
section={selected}
|
|
606
632
|
canEdit={canEdit}
|
|
@@ -613,6 +639,20 @@ export function PostSectionEditor({
|
|
|
613
639
|
onPostBodyChange={handlePostBodyChange}
|
|
614
640
|
/>
|
|
615
641
|
)}
|
|
642
|
+
|
|
643
|
+
{seoOpen && (
|
|
644
|
+
<EditorSeoAside
|
|
645
|
+
entityType="post"
|
|
646
|
+
entityId={post.id}
|
|
647
|
+
title={post.title}
|
|
648
|
+
seoTitle={post.seoTitle}
|
|
649
|
+
seoDescription={post.seoDescription}
|
|
650
|
+
canEdit={canEdit}
|
|
651
|
+
onClose={() => setSeoOpen(false)}
|
|
652
|
+
onDraftChange={handleFields}
|
|
653
|
+
onSaved={handleSeoSaved}
|
|
654
|
+
/>
|
|
655
|
+
)}
|
|
616
656
|
</div>
|
|
617
657
|
</div>
|
|
618
658
|
|