@prenta/admin 1.0.0 → 1.1.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 +6 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +5 -56
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/components/breakpoint-controls.render.test.js +4 -4
- package/dist/__tests__/components/breakpoint-controls.render.test.js.map +1 -1
- package/dist/__tests__/lib/open-public-site-tab.test.d.ts +2 -0
- package/dist/__tests__/lib/open-public-site-tab.test.d.ts.map +1 -0
- package/dist/__tests__/lib/open-public-site-tab.test.js +64 -0
- package/dist/__tests__/lib/open-public-site-tab.test.js.map +1 -0
- package/dist/__tests__/lib/view-site-chrome.test.d.ts +2 -0
- package/dist/__tests__/lib/view-site-chrome.test.d.ts.map +1 -0
- package/dist/__tests__/lib/view-site-chrome.test.js +59 -0
- package/dist/__tests__/lib/view-site-chrome.test.js.map +1 -0
- package/dist/__tests__/router/admin-routes.test.js +4 -0
- package/dist/__tests__/router/admin-routes.test.js.map +1 -1
- package/dist/__tests__/views/canvas-surface-shared.test.js +0 -16
- package/dist/__tests__/views/canvas-surface-shared.test.js.map +1 -1
- package/dist/__tests__/views/editor-top-bar.render.test.js +44 -22
- package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
- package/dist/__tests__/views/page-section-editor.test.js +30 -9
- package/dist/__tests__/views/page-section-editor.test.js.map +1 -1
- package/dist/__tests__/views/post-section-editor.test.js +23 -0
- package/dist/__tests__/views/post-section-editor.test.js.map +1 -1
- package/dist/components/BreakpointPicker.d.ts +3 -2
- package/dist/components/BreakpointPicker.d.ts.map +1 -1
- package/dist/components/BreakpointPicker.js +5 -3
- package/dist/components/BreakpointPicker.js.map +1 -1
- package/dist/components/CanvasEditTip.js +1 -1
- package/dist/components/CanvasEditTip.js.map +1 -1
- package/dist/lib/editor-preview.d.ts +4 -1
- package/dist/lib/editor-preview.d.ts.map +1 -1
- package/dist/lib/editor-preview.js +4 -1
- package/dist/lib/editor-preview.js.map +1 -1
- package/dist/lib/open-public-site-tab.d.ts +14 -0
- package/dist/lib/open-public-site-tab.d.ts.map +1 -0
- package/dist/lib/open-public-site-tab.js +46 -0
- package/dist/lib/open-public-site-tab.js.map +1 -0
- package/dist/lib/view-site-chrome.d.ts +16 -0
- package/dist/lib/view-site-chrome.d.ts.map +1 -0
- package/dist/lib/view-site-chrome.js +15 -0
- package/dist/lib/view-site-chrome.js.map +1 -0
- package/dist/router/admin-routes.d.ts.map +1 -1
- package/dist/router/admin-routes.js +0 -2
- package/dist/router/admin-routes.js.map +1 -1
- package/dist/views/page-editor/EditorCanvas.d.ts +1 -2
- package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
- package/dist/views/page-editor/EditorCanvas.js +2 -2
- package/dist/views/page-editor/EditorCanvas.js.map +1 -1
- package/dist/views/page-editor/EditorTopBar.d.ts +13 -17
- package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
- package/dist/views/page-editor/EditorTopBar.js +19 -21
- package/dist/views/page-editor/EditorTopBar.js.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.d.ts +1 -7
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.js +29 -95
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/dist/views/page-editor/StructureNotice.d.ts +2 -5
- package/dist/views/page-editor/StructureNotice.d.ts.map +1 -1
- package/dist/views/page-editor/StructureNotice.js +6 -10
- package/dist/views/page-editor/StructureNotice.js.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.d.ts +1 -3
- package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.js +3 -3
- package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.d.ts +1 -15
- package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.js +30 -88
- package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
- package/package.json +4 -4
- package/src/AdminRoot.tsx +0 -70
- package/src/__tests__/components/breakpoint-controls.render.test.tsx +6 -4
- package/src/__tests__/lib/open-public-site-tab.test.ts +74 -0
- package/src/__tests__/lib/view-site-chrome.test.ts +65 -0
- package/src/__tests__/router/admin-routes.test.ts +5 -0
- package/src/__tests__/views/canvas-surface-shared.test.ts +0 -19
- package/src/__tests__/views/editor-top-bar.render.test.tsx +85 -22
- package/src/__tests__/views/page-section-editor.test.tsx +42 -9
- package/src/__tests__/views/post-section-editor.test.tsx +35 -0
- package/src/components/BreakpointPicker.tsx +6 -3
- package/src/components/CanvasEditTip.tsx +2 -2
- package/src/lib/editor-preview.ts +4 -1
- package/src/lib/open-public-site-tab.ts +57 -0
- package/src/lib/view-site-chrome.ts +34 -0
- package/src/router/admin-routes.ts +0 -2
- package/src/styles/theme.css +4 -4
- package/src/views/page-editor/EditorCanvas.tsx +1 -6
- package/src/views/page-editor/EditorTopBar.tsx +81 -82
- package/src/views/page-editor/PageSectionEditor.tsx +77 -157
- package/src/views/page-editor/StructureNotice.tsx +7 -42
- package/src/views/post-editor/PostEditorCanvas.tsx +2 -8
- package/src/views/post-editor/PostSectionEditor.tsx +66 -138
- package/dist/__tests__/views/use-site-preview.render.test.d.ts +0 -2
- package/dist/__tests__/views/use-site-preview.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/use-site-preview.render.test.js +0 -120
- package/dist/__tests__/views/use-site-preview.render.test.js.map +0 -1
- package/dist/views/page-editor/PagePreview.d.ts +0 -13
- package/dist/views/page-editor/PagePreview.d.ts.map +0 -1
- package/dist/views/page-editor/PagePreview.js +0 -46
- package/dist/views/page-editor/PagePreview.js.map +0 -1
- package/dist/views/page-editor/SitePreviewFrame.d.ts +0 -43
- package/dist/views/page-editor/SitePreviewFrame.d.ts.map +0 -1
- package/dist/views/page-editor/SitePreviewFrame.js +0 -165
- package/dist/views/page-editor/SitePreviewFrame.js.map +0 -1
- package/dist/views/page-editor/useSitePreview.d.ts +0 -47
- package/dist/views/page-editor/useSitePreview.d.ts.map +0 -1
- package/dist/views/page-editor/useSitePreview.js +0 -96
- package/dist/views/page-editor/useSitePreview.js.map +0 -1
- package/dist/views/post-editor/PostPreview.d.ts +0 -14
- package/dist/views/post-editor/PostPreview.d.ts.map +0 -1
- package/dist/views/post-editor/PostPreview.js +0 -63
- package/dist/views/post-editor/PostPreview.js.map +0 -1
- package/src/__tests__/views/use-site-preview.render.test.tsx +0 -164
- package/src/views/page-editor/PagePreview.tsx +0 -105
- package/src/views/page-editor/SitePreviewFrame.tsx +0 -250
- package/src/views/page-editor/useSitePreview.ts +0 -158
- package/src/views/post-editor/PostPreview.tsx +0 -125
|
@@ -24,8 +24,6 @@ interface PostEditorCanvasProps {
|
|
|
24
24
|
onAddSection?: () => void
|
|
25
25
|
/** Section types rendered as placeholders — shows the "Structure view" notice. */
|
|
26
26
|
structuralTypes?: string[]
|
|
27
|
-
/** Site preview toggle is available — show WYSIWYG nudge on the canvas. */
|
|
28
|
-
sitePreviewAvailable?: boolean
|
|
29
27
|
}
|
|
30
28
|
|
|
31
29
|
/**
|
|
@@ -53,7 +51,6 @@ export function PostEditorCanvas({
|
|
|
53
51
|
onCanvasElement,
|
|
54
52
|
onAddSection,
|
|
55
53
|
structuralTypes = [],
|
|
56
|
-
sitePreviewAvailable = false,
|
|
57
54
|
}: PostEditorCanvasProps) {
|
|
58
55
|
const registerRef = useSectionRefs(selectedId)
|
|
59
56
|
|
|
@@ -67,10 +64,7 @@ export function PostEditorCanvas({
|
|
|
67
64
|
chrome={
|
|
68
65
|
<>
|
|
69
66
|
{sections.length > 0 && <CanvasEditTip />}
|
|
70
|
-
<StructureNotice
|
|
71
|
-
structuralTypes={structuralTypes}
|
|
72
|
-
sitePreviewAvailable={sitePreviewAvailable}
|
|
73
|
-
/>
|
|
67
|
+
<StructureNotice structuralTypes={structuralTypes} />
|
|
74
68
|
</>
|
|
75
69
|
}
|
|
76
70
|
>
|
|
@@ -109,7 +103,7 @@ function EmptyBody({ onAddSection }: { onAddSection?: () => void }) {
|
|
|
109
103
|
</span>
|
|
110
104
|
<p className="text-muted-foreground text-sm">
|
|
111
105
|
{onAddSection
|
|
112
|
-
? 'The header is set by the template. Add body sections to build out this post. Hover sections for a light outline, click to open its editor in the section list
|
|
106
|
+
? 'The header is set by the template. Add body sections to build out this post. Hover sections for a light outline, click to open its editor in the section list.'
|
|
113
107
|
: 'This post’s template renders no body sections. Layout is design-owned — sections come from the template in code. Pick a different template in Post settings, or edit the template itself.'}
|
|
114
108
|
</p>
|
|
115
109
|
{onAddSection && (
|
|
@@ -28,12 +28,10 @@ import {
|
|
|
28
28
|
type ValidationResult,
|
|
29
29
|
} from '../../lib/post-editor-service.js'
|
|
30
30
|
import { MediaPickerModal } from '../../components/MediaPickerModal.js'
|
|
31
|
+
import { SharePreviewLinkDialog } from '../../components/SharePreviewLinkDialog.js'
|
|
31
32
|
import { defaultPostHeader } from '@prenta/core/page-sections'
|
|
32
|
-
import {
|
|
33
|
-
|
|
34
|
-
buildPublicUrl,
|
|
35
|
-
createPreviewToken,
|
|
36
|
-
} from '../../lib/preview-link.js'
|
|
33
|
+
import { openPublicSiteTab } from '../../lib/open-public-site-tab.js'
|
|
34
|
+
import type { ViewSiteIntent } from '../../lib/view-site-chrome.js'
|
|
37
35
|
import { usePublicSiteUrl } from '../../hooks/usePublicSiteUrl.js'
|
|
38
36
|
import {
|
|
39
37
|
hasCanvasRenderer,
|
|
@@ -42,12 +40,9 @@ import {
|
|
|
42
40
|
} from '../page-editor/sections/index.js'
|
|
43
41
|
import { DEFAULT_VIEWPORT, type ViewportId } from '../page-editor/viewports.js'
|
|
44
42
|
import { EditorTopBar, type SaveState } from '../page-editor/EditorTopBar.js'
|
|
45
|
-
import { SitePreviewFrame } from '../page-editor/SitePreviewFrame.js'
|
|
46
|
-
import { useSitePreview } from '../page-editor/useSitePreview.js'
|
|
47
43
|
import { SectionsList } from '../page-editor/SectionsPanel.js'
|
|
48
44
|
import { SectionEditForm } from '../page-editor/SectionEditForm.js'
|
|
49
45
|
import { ValidationSummary } from '../page-editor/ValidationSummary.js'
|
|
50
|
-
import { resolveDefaultSitePreview } from '../../lib/editor-preview.js'
|
|
51
46
|
import { buildEditorContentHtml, computeLiveSeoScore } from '../../lib/editor-seo-content.js'
|
|
52
47
|
import { snapshotBaseline, type ReflowBaseline } from '../../lib/reflow-baseline.js'
|
|
53
48
|
import { ReflowContext } from '../page-editor/reflow-context.js'
|
|
@@ -68,7 +63,6 @@ interface EditorConfig {
|
|
|
68
63
|
| undefined
|
|
69
64
|
>
|
|
70
65
|
seo?: { siteUrl?: string }
|
|
71
|
-
admin?: { editor?: { defaultSitePreview?: boolean | { pages?: boolean; posts?: boolean } } }
|
|
72
66
|
}
|
|
73
67
|
|
|
74
68
|
/** The slice of the admin session this editor reads. */
|
|
@@ -83,12 +77,6 @@ interface PostSectionEditorProps {
|
|
|
83
77
|
config?: EditorConfig
|
|
84
78
|
session?: EditorSession
|
|
85
79
|
onNavigate: (path: string) => void
|
|
86
|
-
/**
|
|
87
|
-
* Open a preview route in a new browser tab. `prepare` runs before the tab
|
|
88
|
-
* navigates (used to save an unsaved draft first); returning `false` cancels
|
|
89
|
-
* the open. Falls back to in-tab navigation when not provided.
|
|
90
|
-
*/
|
|
91
|
-
onOpenPreview?: (path: string, prepare?: () => Promise<boolean | void> | boolean | void) => void
|
|
92
80
|
}
|
|
93
81
|
|
|
94
82
|
function deriveCan(session: EditorSession | undefined): {
|
|
@@ -111,7 +99,6 @@ export function PostSectionEditor({
|
|
|
111
99
|
config,
|
|
112
100
|
session,
|
|
113
101
|
onNavigate,
|
|
114
|
-
onOpenPreview,
|
|
115
102
|
}: PostSectionEditorProps) {
|
|
116
103
|
const [post, setPost] = useState<EditorPost | null>(null)
|
|
117
104
|
const [header, setHeader] = useState<PostHeaderConfig>(defaultPostHeader())
|
|
@@ -151,6 +138,7 @@ export function PostSectionEditor({
|
|
|
151
138
|
const [historyOpen, setHistoryOpen] = useState(false)
|
|
152
139
|
const [confirmPublish, setConfirmPublish] = useState(false)
|
|
153
140
|
const [scheduleOpen, setScheduleOpen] = useState(false)
|
|
141
|
+
const [shareOpen, setShareOpen] = useState(false)
|
|
154
142
|
const [leaveTarget, setLeaveTarget] = useState<string | null>(null)
|
|
155
143
|
const [validation, setValidation] = useState<ValidationResult | null>(null)
|
|
156
144
|
|
|
@@ -177,6 +165,8 @@ export function PostSectionEditor({
|
|
|
177
165
|
return col?.labels?.singular ?? postType
|
|
178
166
|
}, [config, postType])
|
|
179
167
|
|
|
168
|
+
const urlPrefix = config?.collections?.[postType]?.urlPrefix ?? postType
|
|
169
|
+
|
|
180
170
|
// A misconfigured post-type collection (missing the fields the editor
|
|
181
171
|
// writes) would silently drop content on save — detect it up front.
|
|
182
172
|
// Memoized to a string/null so the load effect can depend on it without
|
|
@@ -390,21 +380,6 @@ export function PostSectionEditor({
|
|
|
390
380
|
}
|
|
391
381
|
}, [post, documentId, postType, onNavigate])
|
|
392
382
|
|
|
393
|
-
// Inline branded preview — Next builder canvas (same origin as admin).
|
|
394
|
-
// "View on site" still uses publicSiteUrl for the Astro/public apex.
|
|
395
|
-
const sitePreview = useSitePreview({
|
|
396
|
-
collection: postType,
|
|
397
|
-
documentId: post?.id,
|
|
398
|
-
slug: post?.slug,
|
|
399
|
-
urlPrefix: config?.collections?.[postType]?.urlPrefix ?? postType,
|
|
400
|
-
dirty,
|
|
401
|
-
saveState,
|
|
402
|
-
saveDraft: handleSaveDraft,
|
|
403
|
-
defaultActive: resolveDefaultSitePreview(config?.admin?.editor, 'posts'),
|
|
404
|
-
})
|
|
405
|
-
|
|
406
|
-
const sitePreviewAvailable = Boolean(post?.id && post?.slug)
|
|
407
|
-
|
|
408
383
|
const requestPublish = useCallback(() => {
|
|
409
384
|
if (!post) return
|
|
410
385
|
const result = validatePost(post, true)
|
|
@@ -555,78 +530,37 @@ export function PostSectionEditor({
|
|
|
555
530
|
[dirty, onNavigate],
|
|
556
531
|
)
|
|
557
532
|
|
|
558
|
-
const
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
533
|
+
const handleViewOnSite = useCallback(
|
|
534
|
+
(intent: ViewSiteIntent) => {
|
|
535
|
+
if (!post?.id || !post.slug) {
|
|
536
|
+
toast.error('Save the post before viewing it on the site')
|
|
537
|
+
return
|
|
538
|
+
}
|
|
539
|
+
void openPublicSiteTab({
|
|
540
|
+
intent,
|
|
541
|
+
siteUrl: publicSiteUrl,
|
|
542
|
+
collection: postType,
|
|
543
|
+
documentId: post.id,
|
|
544
|
+
slug: post.slug,
|
|
545
|
+
urlPrefix,
|
|
546
|
+
data: { path: (post as { path?: string }).path },
|
|
547
|
+
dirty,
|
|
548
|
+
saveDraft: handleSaveDraft,
|
|
549
|
+
}).catch((err) => {
|
|
550
|
+
toast.error(err instanceof Error ? err.message : 'Failed to open the site')
|
|
551
|
+
})
|
|
552
|
+
},
|
|
553
|
+
[post, postType, urlPrefix, dirty, handleSaveDraft, publicSiteUrl],
|
|
554
|
+
)
|
|
572
555
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
return
|
|
579
|
-
}
|
|
580
|
-
// Published → open the LIVE post directly (no preview token, no forced
|
|
581
|
-
// save). Pending edits stay previewable via the eye + globe.
|
|
582
|
-
if (post.status === 'PUBLISHED') {
|
|
583
|
-
window.open(
|
|
584
|
-
buildPublicUrl({
|
|
585
|
-
siteUrl: publicSiteUrl,
|
|
586
|
-
urlPrefix: config?.collections?.[postType]?.urlPrefix ?? postType,
|
|
587
|
-
slug: post.slug,
|
|
588
|
-
data: { path: (post as { path?: string }).path },
|
|
589
|
-
}),
|
|
590
|
-
'_blank',
|
|
591
|
-
'noopener',
|
|
592
|
-
)
|
|
593
|
-
return
|
|
594
|
-
}
|
|
595
|
-
const tab = window.open('about:blank', '_blank')
|
|
596
|
-
if (!tab) {
|
|
597
|
-
toast.error('Enable pop-ups for this site to open the preview in a new tab.')
|
|
598
|
-
return
|
|
556
|
+
const handleShare = useCallback(async () => {
|
|
557
|
+
if (!post?.id) return
|
|
558
|
+
if (dirty) {
|
|
559
|
+
const ok = await handleSaveDraft()
|
|
560
|
+
if (!ok) return
|
|
599
561
|
}
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
if (dirty) {
|
|
603
|
-
const ok = await handleSaveDraft()
|
|
604
|
-
if (!ok) {
|
|
605
|
-
tab.close()
|
|
606
|
-
return
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
const { token } = await createPreviewToken(postType, post.id!)
|
|
610
|
-
const urlPrefix = config?.collections?.[postType]?.urlPrefix ?? postType
|
|
611
|
-
const target = buildPublicPreviewUrl({
|
|
612
|
-
siteUrl: publicSiteUrl,
|
|
613
|
-
urlPrefix,
|
|
614
|
-
slug: post.slug,
|
|
615
|
-
data: { path: (post as { path?: string }).path },
|
|
616
|
-
token,
|
|
617
|
-
})
|
|
618
|
-
try {
|
|
619
|
-
tab.opener = null
|
|
620
|
-
} catch {
|
|
621
|
-
/* same-origin guard */
|
|
622
|
-
}
|
|
623
|
-
tab.location.replace(target)
|
|
624
|
-
} catch (err) {
|
|
625
|
-
tab.close()
|
|
626
|
-
toast.error(err instanceof Error ? err.message : 'Failed to open site preview')
|
|
627
|
-
}
|
|
628
|
-
})()
|
|
629
|
-
}, [post, postType, dirty, config, handleSaveDraft, publicSiteUrl])
|
|
562
|
+
setShareOpen(true)
|
|
563
|
+
}, [post, dirty, handleSaveDraft])
|
|
630
564
|
|
|
631
565
|
// ─── Render states ───────────────────────────────────────────────────
|
|
632
566
|
if (loading) {
|
|
@@ -693,16 +627,13 @@ export function PostSectionEditor({
|
|
|
693
627
|
viewport={viewport}
|
|
694
628
|
backLabel="Posts"
|
|
695
629
|
onViewport={setViewport}
|
|
696
|
-
{
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
{}
|
|
700
|
-
: { zoom, canvasScale, onZoom: setZoom })}
|
|
630
|
+
zoom={zoom}
|
|
631
|
+
canvasScale={canvasScale}
|
|
632
|
+
onZoom={setZoom}
|
|
701
633
|
onBack={() => guardedNavigate('/posts')}
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
onToggleSitePreview={post.id && post.slug ? sitePreview.toggle : undefined}
|
|
634
|
+
onViewOnSite={post.id && post.slug ? handleViewOnSite : undefined}
|
|
635
|
+
onShare={post.id ? handleShare : undefined}
|
|
636
|
+
hasPublicSiteUrl={Boolean(publicSiteUrl)}
|
|
706
637
|
onHistory={post.id ? () => setHistoryOpen(true) : undefined}
|
|
707
638
|
onOpenSettings={() => setFieldsOpen(true)}
|
|
708
639
|
onOpenSeo={handleToggleSeo}
|
|
@@ -801,34 +732,18 @@ export function PostSectionEditor({
|
|
|
801
732
|
</EditorRail>
|
|
802
733
|
|
|
803
734
|
<div className="flex-1 overflow-hidden">
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
/>
|
|
817
|
-
) : (
|
|
818
|
-
<PostEditorCanvas
|
|
819
|
-
sections={post.sections}
|
|
820
|
-
header={header}
|
|
821
|
-
context={context}
|
|
822
|
-
selectedId={selectedId}
|
|
823
|
-
viewport={viewport}
|
|
824
|
-
zoom={zoom}
|
|
825
|
-
onScaleChange={setCanvasScale}
|
|
826
|
-
onCanvasElement={setCanvasEl}
|
|
827
|
-
onSelect={handleSelectSection}
|
|
828
|
-
structuralTypes={structuralTypes}
|
|
829
|
-
sitePreviewAvailable={sitePreviewAvailable && !sitePreview.active}
|
|
830
|
-
/>
|
|
831
|
-
)}
|
|
735
|
+
<PostEditorCanvas
|
|
736
|
+
sections={post.sections}
|
|
737
|
+
header={header}
|
|
738
|
+
context={context}
|
|
739
|
+
selectedId={selectedId}
|
|
740
|
+
viewport={viewport}
|
|
741
|
+
zoom={zoom}
|
|
742
|
+
onScaleChange={setCanvasScale}
|
|
743
|
+
onCanvasElement={setCanvasEl}
|
|
744
|
+
onSelect={handleSelectSection}
|
|
745
|
+
structuralTypes={structuralTypes}
|
|
746
|
+
/>
|
|
832
747
|
</div>
|
|
833
748
|
</div>
|
|
834
749
|
</ReflowContext.Provider>
|
|
@@ -874,6 +789,19 @@ export function PostSectionEditor({
|
|
|
874
789
|
/>
|
|
875
790
|
)}
|
|
876
791
|
|
|
792
|
+
{post.id && (
|
|
793
|
+
<SharePreviewLinkDialog
|
|
794
|
+
collectionSlug={postType}
|
|
795
|
+
documentId={post.id}
|
|
796
|
+
documentSlug={post.slug}
|
|
797
|
+
documentPath={(post as { path?: string }).path}
|
|
798
|
+
siteUrl={publicSiteUrl}
|
|
799
|
+
urlPrefix={urlPrefix}
|
|
800
|
+
open={shareOpen}
|
|
801
|
+
onClose={() => setShareOpen(false)}
|
|
802
|
+
/>
|
|
803
|
+
)}
|
|
804
|
+
|
|
877
805
|
<ConfirmDialog
|
|
878
806
|
open={leaveTarget !== null}
|
|
879
807
|
onClose={() => setLeaveTarget(null)}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-site-preview.render.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/views/use-site-preview.render.test.tsx"],"names":[],"mappings":""}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// @vitest-environment happy-dom
|
|
3
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
5
|
-
/**
|
|
6
|
-
* useSitePreview — drives the inline branded preview (iframe of the public
|
|
7
|
-
* site rendering a draft). Pins: token minted + URL built on activation,
|
|
8
|
-
* unsaved drafts are persisted first (a failed save cancels activation), and
|
|
9
|
-
* the frame reloads (reloadKey bump) after each completed save.
|
|
10
|
-
*/
|
|
11
|
-
const createPreviewToken = vi.fn(async () => ({
|
|
12
|
-
token: 'tok-123',
|
|
13
|
-
expiresAt: '2026-06-19T00:00:00Z',
|
|
14
|
-
}));
|
|
15
|
-
vi.mock('../../lib/preview-link.js', () => ({
|
|
16
|
-
createPreviewToken: (...args) => createPreviewToken(...args),
|
|
17
|
-
buildPublicPreviewUrl: ({ siteUrl, urlPrefix, slug, path, token }) => {
|
|
18
|
-
const p = path === '/' || path === '' ? '/' : path || `${urlPrefix ? `/${urlPrefix}` : ''}/${slug}`;
|
|
19
|
-
return p === '/'
|
|
20
|
-
? `${siteUrl}/?preview=${token}`
|
|
21
|
-
: `${siteUrl}${p.startsWith('/') ? p : `/${p}`}?preview=${token}`;
|
|
22
|
-
},
|
|
23
|
-
resolveSiteUrl: (u) => u ?? 'https://fallback.test',
|
|
24
|
-
}));
|
|
25
|
-
vi.mock('sonner', () => ({
|
|
26
|
-
toast: { error: vi.fn(), success: vi.fn() },
|
|
27
|
-
}));
|
|
28
|
-
const { useSitePreview } = await import('../../views/page-editor/useSitePreview.js');
|
|
29
|
-
const { toast } = await import('sonner');
|
|
30
|
-
function Harness({ documentId = 'doc-1', slug = 'about', dirty = false, saveState = 'idle', saveDraft = vi.fn(async () => true), defaultActive = false, }) {
|
|
31
|
-
const sp = useSitePreview({
|
|
32
|
-
collection: 'pages',
|
|
33
|
-
documentId,
|
|
34
|
-
slug,
|
|
35
|
-
urlPrefix: '',
|
|
36
|
-
// Canvas defaults to window.location.origin (Next builder / CMS host).
|
|
37
|
-
dirty,
|
|
38
|
-
saveState,
|
|
39
|
-
saveDraft,
|
|
40
|
-
defaultActive,
|
|
41
|
-
});
|
|
42
|
-
return (_jsxs("div", { children: [_jsx("button", { type: "button", onClick: sp.toggle, children: "toggle" }), _jsx("output", { "data-testid": "state", children: JSON.stringify({ active: sp.active, url: sp.url, reloadKey: sp.reloadKey }) })] }));
|
|
43
|
-
}
|
|
44
|
-
function state() {
|
|
45
|
-
return JSON.parse(screen.getByTestId('state').textContent ?? '{}');
|
|
46
|
-
}
|
|
47
|
-
beforeEach(() => {
|
|
48
|
-
createPreviewToken.mockClear();
|
|
49
|
-
vi.mocked(toast.error).mockClear();
|
|
50
|
-
});
|
|
51
|
-
describe('useSitePreview', () => {
|
|
52
|
-
it('mints a token and builds the canvas draft URL on activation', async () => {
|
|
53
|
-
render(_jsx(Harness, {}));
|
|
54
|
-
fireEvent.click(screen.getByText('toggle'));
|
|
55
|
-
expect(state().active).toBe(true);
|
|
56
|
-
await waitFor(() => {
|
|
57
|
-
expect(state().url).toBe(`${window.location.origin}/about?preview=tok-123`);
|
|
58
|
-
});
|
|
59
|
-
expect(createPreviewToken).toHaveBeenCalledWith('pages', 'doc-1');
|
|
60
|
-
});
|
|
61
|
-
it('saves a dirty draft before minting; a failed save cancels activation', async () => {
|
|
62
|
-
const saveDraft = vi.fn(async () => false);
|
|
63
|
-
render(_jsx(Harness, { dirty: true, saveDraft: saveDraft }));
|
|
64
|
-
fireEvent.click(screen.getByText('toggle'));
|
|
65
|
-
await waitFor(() => expect(saveDraft).toHaveBeenCalled());
|
|
66
|
-
await waitFor(() => expect(state().active).toBe(false));
|
|
67
|
-
expect(createPreviewToken).not.toHaveBeenCalled();
|
|
68
|
-
});
|
|
69
|
-
it('refuses to activate for unsaved documents', () => {
|
|
70
|
-
render(_jsx(Harness, { documentId: null }));
|
|
71
|
-
fireEvent.click(screen.getByText('toggle'));
|
|
72
|
-
expect(state().active).toBe(false);
|
|
73
|
-
expect(toast.error).toHaveBeenCalled();
|
|
74
|
-
});
|
|
75
|
-
it('toggles back to the canvas on second click', async () => {
|
|
76
|
-
render(_jsx(Harness, {}));
|
|
77
|
-
fireEvent.click(screen.getByText('toggle'));
|
|
78
|
-
await waitFor(() => expect(state().url).not.toBeNull());
|
|
79
|
-
fireEvent.click(screen.getByText('toggle'));
|
|
80
|
-
expect(state().active).toBe(false);
|
|
81
|
-
});
|
|
82
|
-
it('auto-activates when defaultActive is true', async () => {
|
|
83
|
-
render(_jsx(Harness, { defaultActive: true }));
|
|
84
|
-
await waitFor(() => {
|
|
85
|
-
expect(state().active).toBe(true);
|
|
86
|
-
expect(state().url).toBe(`${window.location.origin}/about?preview=tok-123`);
|
|
87
|
-
});
|
|
88
|
-
expect(createPreviewToken).toHaveBeenCalledWith('pages', 'doc-1');
|
|
89
|
-
});
|
|
90
|
-
it('honors an explicit canvasUrl override', async () => {
|
|
91
|
-
function CanvasHarness() {
|
|
92
|
-
const sp = useSitePreview({
|
|
93
|
-
collection: 'pages',
|
|
94
|
-
documentId: 'doc-1',
|
|
95
|
-
slug: 'about',
|
|
96
|
-
urlPrefix: '',
|
|
97
|
-
canvasUrl: 'https://cms.test',
|
|
98
|
-
dirty: false,
|
|
99
|
-
saveState: 'idle',
|
|
100
|
-
saveDraft: vi.fn(async () => true),
|
|
101
|
-
});
|
|
102
|
-
return (_jsx("button", { type: "button", onClick: sp.toggle, children: sp.url ?? 'none' }));
|
|
103
|
-
}
|
|
104
|
-
render(_jsx(CanvasHarness, {}));
|
|
105
|
-
fireEvent.click(screen.getByRole('button'));
|
|
106
|
-
await waitFor(() => {
|
|
107
|
-
expect(screen.getByRole('button').textContent).toBe('https://cms.test/about?preview=tok-123');
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
it('bumps reloadKey after a save completes while the preview is open', async () => {
|
|
111
|
-
const { rerender } = render(_jsx(Harness, { saveState: "idle" }));
|
|
112
|
-
fireEvent.click(screen.getByText('toggle'));
|
|
113
|
-
await waitFor(() => expect(state().url).not.toBeNull());
|
|
114
|
-
expect(state().reloadKey).toBe(0);
|
|
115
|
-
rerender(_jsx(Harness, { saveState: "saving" }));
|
|
116
|
-
rerender(_jsx(Harness, { saveState: "saved" }));
|
|
117
|
-
await waitFor(() => expect(state().reloadKey).toBe(1));
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
//# sourceMappingURL=use-site-preview.render.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"use-site-preview.render.test.js","sourceRoot":"","sources":["../../../src/__tests__/views/use-site-preview.render.test.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
interface PagePreviewProps {
|
|
2
|
-
documentId: string;
|
|
3
|
-
onNavigate: (path: string) => void;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Clean, chrome-free render of a page using the SAME section renderers as
|
|
7
|
-
* the editor canvas — so the preview reflects what will actually ship.
|
|
8
|
-
* Only visible sections are rendered (hidden sections are omitted, exactly
|
|
9
|
-
* as the public page would behave).
|
|
10
|
-
*/
|
|
11
|
-
export declare function PagePreview({ documentId, onNavigate }: PagePreviewProps): import("react/jsx-runtime").JSX.Element;
|
|
12
|
-
export {};
|
|
13
|
-
//# sourceMappingURL=PagePreview.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PagePreview.d.ts","sourceRoot":"","sources":["../../../src/views/page-editor/PagePreview.tsx"],"names":[],"mappings":"AAOA,UAAU,gBAAgB;IACxB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,gBAAgB,2CAsFvE"}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
import { AlertTriangle, Loader2, Pencil, X } from 'lucide-react';
|
|
5
|
-
import { fetchPageForEditor } from '../../lib/page-editor-service.js';
|
|
6
|
-
import { SectionRenderer } from './sections/index.js';
|
|
7
|
-
/**
|
|
8
|
-
* Clean, chrome-free render of a page using the SAME section renderers as
|
|
9
|
-
* the editor canvas — so the preview reflects what will actually ship.
|
|
10
|
-
* Only visible sections are rendered (hidden sections are omitted, exactly
|
|
11
|
-
* as the public page would behave).
|
|
12
|
-
*/
|
|
13
|
-
export function PagePreview({ documentId, onNavigate }) {
|
|
14
|
-
const [page, setPage] = useState(null);
|
|
15
|
-
const [loading, setLoading] = useState(true);
|
|
16
|
-
const [error, setError] = useState(null);
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
let cancelled = false;
|
|
19
|
-
setLoading(true);
|
|
20
|
-
fetchPageForEditor(documentId)
|
|
21
|
-
.then((p) => !cancelled && setPage(p))
|
|
22
|
-
.catch((err) => !cancelled && setError(err instanceof Error ? err.message : 'Failed to load'))
|
|
23
|
-
.finally(() => !cancelled && setLoading(false));
|
|
24
|
-
return () => {
|
|
25
|
-
cancelled = true;
|
|
26
|
-
};
|
|
27
|
-
}, [documentId]);
|
|
28
|
-
if (loading) {
|
|
29
|
-
return (_jsxs("div", { className: "bg-background flex h-full items-center justify-center", children: [_jsx(Loader2, { className: "text-muted-foreground h-6 w-6 animate-spin", "aria-hidden": true }), _jsx("span", { className: "sr-only", children: "Loading preview\u2026" })] }));
|
|
30
|
-
}
|
|
31
|
-
if (error || !page) {
|
|
32
|
-
return (_jsx("div", { className: "bg-background flex h-full items-center justify-center p-6", children: _jsxs("div", { className: "border-destructive/30 bg-destructive/5 flex max-w-md items-center gap-3 rounded-lg border p-4", children: [_jsx(AlertTriangle, { className: "text-destructive shrink-0", size: 20, "aria-hidden": true }), _jsx("p", { className: "text-foreground text-sm font-medium", children: error ?? 'Page not found.' })] }) }));
|
|
33
|
-
}
|
|
34
|
-
const visible = page.sections.filter((s) => s.visible);
|
|
35
|
-
return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "bg-card border-border flex h-12 shrink-0 items-center justify-between border-b px-4", children: [_jsxs("button", { type: "button", onClick: () => {
|
|
36
|
-
// Opened via the editor's "Preview" (script-opened tab) → close
|
|
37
|
-
// it. Opened by navigating to the URL directly (no opener, so
|
|
38
|
-
// `window.close()` is a no-op) → fall back to the editor so the
|
|
39
|
-
// button is never dead.
|
|
40
|
-
if (window.opener)
|
|
41
|
-
window.close();
|
|
42
|
-
else
|
|
43
|
-
onNavigate(`/pages/${documentId}/edit`);
|
|
44
|
-
}, className: "text-muted-foreground hover:text-foreground inline-flex items-center gap-1.5 text-sm", "aria-label": "Close preview tab", children: [_jsx(X, { className: "h-4 w-4", "aria-hidden": true }), "Close"] }), _jsxs("span", { className: "text-muted-foreground text-xs", children: ["Preview \u00B7 ", page.title || 'Untitled', " ", page.path] }), _jsxs("button", { type: "button", onClick: () => onNavigate(`/pages/${documentId}/edit`), className: "bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-sm font-medium", children: [_jsx(Pencil, { className: "h-3.5 w-3.5", "aria-hidden": true }), "Open editor"] })] }), _jsx("div", { className: "prenta-canvas bg-card flex-1 overflow-auto", children: visible.length === 0 ? (_jsx("div", { className: "text-muted-foreground flex h-full items-center justify-center text-sm", children: "This page has no visible sections yet." })) : (visible.map((section) => _jsx(SectionRenderer, { section: section }, section.id))) })] }));
|
|
45
|
-
}
|
|
46
|
-
//# sourceMappingURL=PagePreview.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"PagePreview.js","sourceRoot":"","sources":["../../../src/views/page-editor/PagePreview.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type PreviewSectionPayload } from '../../lib/preview-bridge.js';
|
|
2
|
-
import { type ViewportId } from './viewports.js';
|
|
3
|
-
export interface CanvasEditPayload {
|
|
4
|
-
sectionId: string;
|
|
5
|
-
fieldPath: string;
|
|
6
|
-
value: string;
|
|
7
|
-
}
|
|
8
|
-
export interface CanvasMediaRequestPayload {
|
|
9
|
-
sectionId: string;
|
|
10
|
-
fieldPath: string;
|
|
11
|
-
}
|
|
12
|
-
interface SitePreviewFrameProps {
|
|
13
|
-
/** Canvas draft URL; null while the preview token is still minting. */
|
|
14
|
-
src: string | null;
|
|
15
|
-
viewport: ViewportId;
|
|
16
|
-
/** Changing this remounts the iframe so it reloads the persisted draft. */
|
|
17
|
-
reloadKey: number;
|
|
18
|
-
title?: string;
|
|
19
|
-
/** Currently selected section in the editor — pushed to the canvas as a highlight. */
|
|
20
|
-
selectedId?: string | null;
|
|
21
|
-
/** Live editor sections — debounced push after canvas ready (Phase 3). */
|
|
22
|
-
sections?: PreviewSectionPayload[];
|
|
23
|
-
/**
|
|
24
|
-
* Increment after a canvas-originated text edit so the next sections push is
|
|
25
|
-
* skipped (avoids remounting a focused contenteditable).
|
|
26
|
-
*/
|
|
27
|
-
suppressSectionsEcho?: number;
|
|
28
|
-
/** Canvas → admin: section (and optional field) clicked in the real design. */
|
|
29
|
-
onSelectSection?: (sectionId: string | null, fieldPath?: string) => void;
|
|
30
|
-
/** Canvas → admin: inline text edit (Stage 3). */
|
|
31
|
-
onCanvasEdit?: (edit: CanvasEditPayload) => void;
|
|
32
|
-
/** Canvas → admin: open media picker for a field (Stage 3 wave B). */
|
|
33
|
-
onCanvasRequestMedia?: (request: CanvasMediaRequestPayload) => void;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Inline branded preview surface — Next builder route in an iframe at the
|
|
37
|
-
* selected viewport's *true* CSS width, CSS-`transform: scale()`'d to fit
|
|
38
|
-
* (never crop). Stage 3: mode / edit / request-media + suppress-echo for
|
|
39
|
-
* canvas-originated text edits.
|
|
40
|
-
*/
|
|
41
|
-
export declare function SitePreviewFrame({ src, viewport, reloadKey, title, selectedId, sections, suppressSectionsEcho, onSelectSection, onCanvasEdit, onCanvasRequestMedia, }: SitePreviewFrameProps): import("react/jsx-runtime").JSX.Element;
|
|
42
|
-
export {};
|
|
43
|
-
//# sourceMappingURL=SitePreviewFrame.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SitePreviewFrame.d.ts","sourceRoot":"","sources":["../../../src/views/page-editor/SitePreviewFrame.tsx"],"names":[],"mappings":"AAKA,OAAO,EAUL,KAAK,qBAAqB,EAE3B,MAAM,6BAA6B,CAAA;AACpC,OAAO,EAAe,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAE7D,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,UAAU,qBAAqB;IAC7B,uEAAuE;IACvE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAClB,QAAQ,EAAE,UAAU,CAAA;IACpB,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,sFAAsF;IACtF,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,qBAAqB,EAAE,CAAA;IAClC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,+EAA+E;IAC/E,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;IACxE,kDAAkD;IAClD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAChD,sEAAsE;IACtE,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,yBAAyB,KAAK,IAAI,CAAA;CACpE;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,GAAG,EACH,QAAQ,EACR,SAAS,EACT,KAAsB,EACtB,UAAiB,EACjB,QAAQ,EACR,oBAAwB,EACxB,eAAe,EACf,YAAY,EACZ,oBAAoB,GACrB,EAAE,qBAAqB,2CAiLvB"}
|