@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.
Files changed (117) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +5 -56
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/components/breakpoint-controls.render.test.js +4 -4
  6. package/dist/__tests__/components/breakpoint-controls.render.test.js.map +1 -1
  7. package/dist/__tests__/lib/open-public-site-tab.test.d.ts +2 -0
  8. package/dist/__tests__/lib/open-public-site-tab.test.d.ts.map +1 -0
  9. package/dist/__tests__/lib/open-public-site-tab.test.js +64 -0
  10. package/dist/__tests__/lib/open-public-site-tab.test.js.map +1 -0
  11. package/dist/__tests__/lib/view-site-chrome.test.d.ts +2 -0
  12. package/dist/__tests__/lib/view-site-chrome.test.d.ts.map +1 -0
  13. package/dist/__tests__/lib/view-site-chrome.test.js +59 -0
  14. package/dist/__tests__/lib/view-site-chrome.test.js.map +1 -0
  15. package/dist/__tests__/router/admin-routes.test.js +4 -0
  16. package/dist/__tests__/router/admin-routes.test.js.map +1 -1
  17. package/dist/__tests__/views/canvas-surface-shared.test.js +0 -16
  18. package/dist/__tests__/views/canvas-surface-shared.test.js.map +1 -1
  19. package/dist/__tests__/views/editor-top-bar.render.test.js +44 -22
  20. package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
  21. package/dist/__tests__/views/page-section-editor.test.js +30 -9
  22. package/dist/__tests__/views/page-section-editor.test.js.map +1 -1
  23. package/dist/__tests__/views/post-section-editor.test.js +23 -0
  24. package/dist/__tests__/views/post-section-editor.test.js.map +1 -1
  25. package/dist/components/BreakpointPicker.d.ts +3 -2
  26. package/dist/components/BreakpointPicker.d.ts.map +1 -1
  27. package/dist/components/BreakpointPicker.js +5 -3
  28. package/dist/components/BreakpointPicker.js.map +1 -1
  29. package/dist/components/CanvasEditTip.js +1 -1
  30. package/dist/components/CanvasEditTip.js.map +1 -1
  31. package/dist/lib/editor-preview.d.ts +4 -1
  32. package/dist/lib/editor-preview.d.ts.map +1 -1
  33. package/dist/lib/editor-preview.js +4 -1
  34. package/dist/lib/editor-preview.js.map +1 -1
  35. package/dist/lib/open-public-site-tab.d.ts +14 -0
  36. package/dist/lib/open-public-site-tab.d.ts.map +1 -0
  37. package/dist/lib/open-public-site-tab.js +46 -0
  38. package/dist/lib/open-public-site-tab.js.map +1 -0
  39. package/dist/lib/view-site-chrome.d.ts +16 -0
  40. package/dist/lib/view-site-chrome.d.ts.map +1 -0
  41. package/dist/lib/view-site-chrome.js +15 -0
  42. package/dist/lib/view-site-chrome.js.map +1 -0
  43. package/dist/router/admin-routes.d.ts.map +1 -1
  44. package/dist/router/admin-routes.js +0 -2
  45. package/dist/router/admin-routes.js.map +1 -1
  46. package/dist/views/page-editor/EditorCanvas.d.ts +1 -2
  47. package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
  48. package/dist/views/page-editor/EditorCanvas.js +2 -2
  49. package/dist/views/page-editor/EditorCanvas.js.map +1 -1
  50. package/dist/views/page-editor/EditorTopBar.d.ts +13 -17
  51. package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
  52. package/dist/views/page-editor/EditorTopBar.js +19 -21
  53. package/dist/views/page-editor/EditorTopBar.js.map +1 -1
  54. package/dist/views/page-editor/PageSectionEditor.d.ts +1 -7
  55. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
  56. package/dist/views/page-editor/PageSectionEditor.js +29 -95
  57. package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
  58. package/dist/views/page-editor/StructureNotice.d.ts +2 -5
  59. package/dist/views/page-editor/StructureNotice.d.ts.map +1 -1
  60. package/dist/views/page-editor/StructureNotice.js +6 -10
  61. package/dist/views/page-editor/StructureNotice.js.map +1 -1
  62. package/dist/views/post-editor/PostEditorCanvas.d.ts +1 -3
  63. package/dist/views/post-editor/PostEditorCanvas.d.ts.map +1 -1
  64. package/dist/views/post-editor/PostEditorCanvas.js +3 -3
  65. package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
  66. package/dist/views/post-editor/PostSectionEditor.d.ts +1 -15
  67. package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
  68. package/dist/views/post-editor/PostSectionEditor.js +30 -88
  69. package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
  70. package/package.json +4 -4
  71. package/src/AdminRoot.tsx +0 -70
  72. package/src/__tests__/components/breakpoint-controls.render.test.tsx +6 -4
  73. package/src/__tests__/lib/open-public-site-tab.test.ts +74 -0
  74. package/src/__tests__/lib/view-site-chrome.test.ts +65 -0
  75. package/src/__tests__/router/admin-routes.test.ts +5 -0
  76. package/src/__tests__/views/canvas-surface-shared.test.ts +0 -19
  77. package/src/__tests__/views/editor-top-bar.render.test.tsx +85 -22
  78. package/src/__tests__/views/page-section-editor.test.tsx +42 -9
  79. package/src/__tests__/views/post-section-editor.test.tsx +35 -0
  80. package/src/components/BreakpointPicker.tsx +6 -3
  81. package/src/components/CanvasEditTip.tsx +2 -2
  82. package/src/lib/editor-preview.ts +4 -1
  83. package/src/lib/open-public-site-tab.ts +57 -0
  84. package/src/lib/view-site-chrome.ts +34 -0
  85. package/src/router/admin-routes.ts +0 -2
  86. package/src/styles/theme.css +4 -4
  87. package/src/views/page-editor/EditorCanvas.tsx +1 -6
  88. package/src/views/page-editor/EditorTopBar.tsx +81 -82
  89. package/src/views/page-editor/PageSectionEditor.tsx +77 -157
  90. package/src/views/page-editor/StructureNotice.tsx +7 -42
  91. package/src/views/post-editor/PostEditorCanvas.tsx +2 -8
  92. package/src/views/post-editor/PostSectionEditor.tsx +66 -138
  93. package/dist/__tests__/views/use-site-preview.render.test.d.ts +0 -2
  94. package/dist/__tests__/views/use-site-preview.render.test.d.ts.map +0 -1
  95. package/dist/__tests__/views/use-site-preview.render.test.js +0 -120
  96. package/dist/__tests__/views/use-site-preview.render.test.js.map +0 -1
  97. package/dist/views/page-editor/PagePreview.d.ts +0 -13
  98. package/dist/views/page-editor/PagePreview.d.ts.map +0 -1
  99. package/dist/views/page-editor/PagePreview.js +0 -46
  100. package/dist/views/page-editor/PagePreview.js.map +0 -1
  101. package/dist/views/page-editor/SitePreviewFrame.d.ts +0 -43
  102. package/dist/views/page-editor/SitePreviewFrame.d.ts.map +0 -1
  103. package/dist/views/page-editor/SitePreviewFrame.js +0 -165
  104. package/dist/views/page-editor/SitePreviewFrame.js.map +0 -1
  105. package/dist/views/page-editor/useSitePreview.d.ts +0 -47
  106. package/dist/views/page-editor/useSitePreview.d.ts.map +0 -1
  107. package/dist/views/page-editor/useSitePreview.js +0 -96
  108. package/dist/views/page-editor/useSitePreview.js.map +0 -1
  109. package/dist/views/post-editor/PostPreview.d.ts +0 -14
  110. package/dist/views/post-editor/PostPreview.d.ts.map +0 -1
  111. package/dist/views/post-editor/PostPreview.js +0 -63
  112. package/dist/views/post-editor/PostPreview.js.map +0 -1
  113. package/src/__tests__/views/use-site-preview.render.test.tsx +0 -164
  114. package/src/views/page-editor/PagePreview.tsx +0 -105
  115. package/src/views/page-editor/SitePreviewFrame.tsx +0 -250
  116. package/src/views/page-editor/useSitePreview.ts +0 -158
  117. 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 — or use Site preview to click text and images in place.'
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
- buildPublicPreviewUrl,
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 handlePreview = useCallback(() => {
559
- if (!post?.id) {
560
- toast.error('Save the post before previewing')
561
- return
562
- }
563
- const path = `/posts/${postType}/${post.id}/preview`
564
- // New-tab preview: save first when there are unsaved edits so the preview
565
- // (which reads the persisted document) reflects what's on screen.
566
- if (onOpenPreview) {
567
- onOpenPreview(path, dirty ? handleSaveDraft : undefined)
568
- } else {
569
- guardedNavigate(path)
570
- }
571
- }, [post, postType, dirty, onOpenPreview, handleSaveDraft, guardedNavigate])
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
- // "View on site": render the draft through the consumer's real public route
574
- // (`/<urlPrefix>/<slug>?preview=<token>`) via a signed preview token.
575
- const handleViewOnSite = useCallback(() => {
576
- if (!post?.id || !post.slug) {
577
- toast.error('Save the post before viewing it on the site')
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
- void (async () => {
601
- try {
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
- {...(sitePreview.active
697
- ? // The site preview is an iframe of the real front end, not a
698
- // scaled canvas — a zoom percentage there would be a lie.
699
- {}
700
- : { zoom, canvasScale, onZoom: setZoom })}
630
+ zoom={zoom}
631
+ canvasScale={canvasScale}
632
+ onZoom={setZoom}
701
633
  onBack={() => guardedNavigate('/posts')}
702
- onPreview={handlePreview}
703
- onViewOnSite={post.id ? handleViewOnSite : undefined}
704
- sitePreview={sitePreview.active}
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
- {sitePreview.active ? (
805
- <SitePreviewFrame
806
- src={sitePreview.url}
807
- viewport={viewport}
808
- reloadKey={sitePreview.reloadKey}
809
- title={`Site preview: ${post.title || `New ${typeLabel}`}`}
810
- selectedId={selectedId}
811
- sections={post.sections}
812
- suppressSectionsEcho={suppressSectionsEcho}
813
- onSelectSection={handleSelectSection}
814
- onCanvasEdit={handleCanvasEdit}
815
- onCanvasRequestMedia={handleCanvasRequestMedia}
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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=use-site-preview.render.test.d.ts.map
@@ -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"}