@prenta/admin 0.93.0 → 0.95.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 +152 -0
- package/dist/__tests__/lib/page-editor-service-api.test.js +37 -0
- package/dist/__tests__/lib/page-editor-service-api.test.js.map +1 -1
- package/dist/__tests__/lib/version-diff.test.d.ts +2 -0
- package/dist/__tests__/lib/version-diff.test.d.ts.map +1 -0
- package/dist/__tests__/lib/version-diff.test.js +230 -0
- package/dist/__tests__/lib/version-diff.test.js.map +1 -0
- package/dist/__tests__/lib/version-history.test.d.ts +2 -0
- package/dist/__tests__/lib/version-history.test.d.ts.map +1 -0
- package/dist/__tests__/lib/version-history.test.js +125 -0
- package/dist/__tests__/lib/version-history.test.js.map +1 -0
- package/dist/__tests__/styles/responsive-layer.test.d.ts +2 -0
- package/dist/__tests__/styles/responsive-layer.test.d.ts.map +1 -0
- package/dist/__tests__/styles/responsive-layer.test.js +127 -0
- package/dist/__tests__/styles/responsive-layer.test.js.map +1 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.d.ts +2 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.js +70 -0
- package/dist/__tests__/views/canvas-review-mode.render.test.js.map +1 -0
- package/dist/__tests__/views/editor-top-bar.render.test.js +29 -0
- package/dist/__tests__/views/editor-top-bar.render.test.js.map +1 -1
- package/dist/__tests__/views/section-edit-form.render.test.d.ts +2 -0
- package/dist/__tests__/views/section-edit-form.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/section-edit-form.render.test.js +124 -0
- package/dist/__tests__/views/section-edit-form.render.test.js.map +1 -0
- package/dist/__tests__/views/section-inspector-media.test.js +1 -1
- package/dist/__tests__/views/section-inspector-media.test.js.map +1 -1
- package/dist/__tests__/views/section-inspector-repeater.render.test.js +2 -1
- package/dist/__tests__/views/section-inspector-repeater.render.test.js.map +1 -1
- package/dist/__tests__/views/version-history-panel.render.test.d.ts +2 -0
- package/dist/__tests__/views/version-history-panel.render.test.d.ts.map +1 -0
- package/dist/__tests__/views/version-history-panel.render.test.js +153 -0
- package/dist/__tests__/views/version-history-panel.render.test.js.map +1 -0
- package/dist/components/CanvasEditTip.js +1 -1
- package/dist/components/CanvasEditTip.js.map +1 -1
- package/dist/components/EditorRail.js +3 -3
- package/dist/components/EditorRail.js.map +1 -1
- package/dist/components/page-tab/DirectiveSwitch.d.ts.map +1 -1
- package/dist/components/page-tab/DirectiveSwitch.js +5 -1
- package/dist/components/page-tab/DirectiveSwitch.js.map +1 -1
- package/dist/layout/primitives/AdminShell.d.ts.map +1 -1
- package/dist/layout/primitives/AdminShell.js +2 -2
- package/dist/layout/primitives/AdminShell.js.map +1 -1
- package/dist/lib/version-diff.d.ts +50 -0
- package/dist/lib/version-diff.d.ts.map +1 -0
- package/dist/lib/version-diff.js +294 -0
- package/dist/lib/version-diff.js.map +1 -0
- package/dist/lib/version-history.d.ts +73 -0
- package/dist/lib/version-history.d.ts.map +1 -0
- package/dist/lib/version-history.js +126 -0
- package/dist/lib/version-history.js.map +1 -0
- package/dist/prenta-admin.css +1 -1
- package/dist/views/page-editor/EditorCanvas.d.ts +14 -1
- package/dist/views/page-editor/EditorCanvas.d.ts.map +1 -1
- package/dist/views/page-editor/EditorCanvas.js +3 -3
- package/dist/views/page-editor/EditorCanvas.js.map +1 -1
- package/dist/views/page-editor/EditorTopBar.d.ts +8 -2
- package/dist/views/page-editor/EditorTopBar.d.ts.map +1 -1
- package/dist/views/page-editor/EditorTopBar.js +8 -6
- 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 +129 -40
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/dist/views/page-editor/ReviewBar.d.ts +21 -0
- package/dist/views/page-editor/ReviewBar.d.ts.map +1 -0
- package/dist/views/page-editor/ReviewBar.js +22 -0
- package/dist/views/page-editor/ReviewBar.js.map +1 -0
- package/dist/views/page-editor/SectionEditForm.d.ts +42 -0
- package/dist/views/page-editor/SectionEditForm.d.ts.map +1 -0
- package/dist/views/page-editor/SectionEditForm.js +48 -0
- package/dist/views/page-editor/SectionEditForm.js.map +1 -0
- package/dist/views/page-editor/SectionInspector.d.ts +0 -20
- package/dist/views/page-editor/SectionInspector.d.ts.map +1 -1
- package/dist/views/page-editor/SectionInspector.js +6 -224
- package/dist/views/page-editor/SectionInspector.js.map +1 -1
- package/dist/views/page-editor/SectionsPanel.d.ts +10 -1
- package/dist/views/page-editor/SectionsPanel.d.ts.map +1 -1
- package/dist/views/page-editor/SectionsPanel.js +11 -5
- package/dist/views/page-editor/SectionsPanel.js.map +1 -1
- package/dist/views/page-editor/VersionHistoryPanel.d.ts +29 -0
- package/dist/views/page-editor/VersionHistoryPanel.d.ts.map +1 -0
- package/dist/views/page-editor/VersionHistoryPanel.js +81 -0
- package/dist/views/page-editor/VersionHistoryPanel.js.map +1 -0
- package/dist/views/page-editor/section-fields.d.ts +43 -0
- package/dist/views/page-editor/section-fields.d.ts.map +1 -0
- package/dist/views/page-editor/section-fields.js +236 -0
- package/dist/views/page-editor/section-fields.js.map +1 -0
- package/dist/views/page-editor/sections/ArticleBodySection.js +1 -1
- package/dist/views/page-editor/sections/ArticleBodySection.js.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.d.ts +13 -1
- package/dist/views/page-editor/sections/SectionRenderer.d.ts.map +1 -1
- package/dist/views/page-editor/sections/SectionRenderer.js +23 -8
- package/dist/views/page-editor/sections/SectionRenderer.js.map +1 -1
- package/dist/views/post-editor/PostEditorCanvas.js +1 -1
- package/dist/views/post-editor/PostEditorCanvas.js.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.d.ts.map +1 -1
- package/dist/views/post-editor/PostSectionEditor.js +11 -9
- package/dist/views/post-editor/PostSectionEditor.js.map +1 -1
- package/package.json +3 -3
- package/src/__tests__/lib/page-editor-service-api.test.ts +44 -0
- package/src/__tests__/lib/version-diff.test.ts +278 -0
- package/src/__tests__/lib/version-history.test.ts +166 -0
- package/src/__tests__/styles/responsive-layer.test.ts +152 -0
- package/src/__tests__/views/canvas-review-mode.render.test.tsx +94 -0
- package/src/__tests__/views/editor-top-bar.render.test.tsx +42 -0
- package/src/__tests__/views/section-edit-form.render.test.tsx +162 -0
- package/src/__tests__/views/section-inspector-media.test.ts +1 -1
- package/src/__tests__/views/section-inspector-repeater.render.test.tsx +2 -1
- package/src/__tests__/views/version-history-panel.render.test.tsx +195 -0
- package/src/components/CanvasEditTip.tsx +3 -3
- package/src/components/EditorRail.tsx +5 -5
- package/src/components/page-tab/DirectiveSwitch.tsx +4 -1
- package/src/layout/primitives/AdminShell.tsx +13 -1
- package/src/lib/version-diff.ts +376 -0
- package/src/lib/version-history.ts +192 -0
- package/src/styles/responsive.css +281 -0
- package/src/styles/theme.css +8 -0
- package/src/views/page-editor/EditorCanvas.tsx +34 -7
- package/src/views/page-editor/EditorTopBar.tsx +20 -8
- package/src/views/page-editor/PageSectionEditor.tsx +182 -65
- package/src/views/page-editor/ReviewBar.tsx +76 -0
- package/src/views/page-editor/SectionEditForm.tsx +161 -0
- package/src/views/page-editor/SectionInspector.tsx +9 -553
- package/src/views/page-editor/SectionsPanel.tsx +72 -16
- package/src/views/page-editor/VersionHistoryPanel.tsx +302 -0
- package/src/views/page-editor/section-fields.tsx +563 -0
- package/src/views/page-editor/sections/ArticleBodySection.tsx +1 -1
- package/src/views/page-editor/sections/SectionRenderer.tsx +53 -12
- package/src/views/post-editor/PostEditorCanvas.tsx +1 -1
- package/src/views/post-editor/PostSectionEditor.tsx +18 -26
|
@@ -27,7 +27,7 @@ import { StructureNotice } from './StructureNotice.js';
|
|
|
27
27
|
* show/hide — none of which resize the observed elements. The observer
|
|
28
28
|
* exists for later window resizes; its first callback is not a substitute.
|
|
29
29
|
*/
|
|
30
|
-
export function EditorCanvas({ sections, selectedId, breakpoint, zoom = 'fit', onSelect, onScaleChange, onCanvasElement, structuralTypes = [], sitePreviewAvailable = false, }) {
|
|
30
|
+
export function EditorCanvas({ sections, selectedId, breakpoint, zoom = 'fit', onSelect, onScaleChange, onCanvasElement, structuralTypes = [], sitePreviewAvailable = false, reviewing = false, changedSectionIds, reviewBar, }) {
|
|
31
31
|
const bp = getBreakpoint(breakpoint);
|
|
32
32
|
const sentinelRef = useRef(null);
|
|
33
33
|
const surfaceRef = useRef(null);
|
|
@@ -72,7 +72,7 @@ export function EditorCanvas({ sections, selectedId, breakpoint, zoom = 'fit', o
|
|
|
72
72
|
useEffect(() => {
|
|
73
73
|
onScaleChange?.(scale);
|
|
74
74
|
}, [scale, onScaleChange]);
|
|
75
|
-
return (_jsxs("div", { className: "bg-muted h-full overflow-auto px-6 pt-4.5 pb-10 text-center", children: [_jsx("div", { ref: sentinelRef, "aria-hidden": true, className: "h-0" }), sections.length === 0 ? (_jsx(EmptyCanvas, {})) : (_jsxs(_Fragment, { children: [_jsx(CanvasEditTip, {}), _jsx(StructureNotice, { structuralTypes: structuralTypes, sitePreviewAvailable: sitePreviewAvailable }), _jsx(CanvasMetaLine, { breakpoint: bp, pageHeight: pageHeight, scale: scale }), _jsx("div", { className: "mx-auto block", style: {
|
|
75
|
+
return (_jsxs("div", { className: "prenta-canvas-scroll bg-muted h-full overflow-auto px-6 pt-4.5 pb-10 text-center", children: [_jsx("div", { ref: sentinelRef, "aria-hidden": true, className: "h-0" }), sections.length === 0 ? (_jsx(EmptyCanvas, {})) : (_jsxs(_Fragment, { children: [reviewing ? (reviewBar) : (_jsxs(_Fragment, { children: [_jsx(CanvasEditTip, {}), _jsx(StructureNotice, { structuralTypes: structuralTypes, sitePreviewAvailable: sitePreviewAvailable })] })), _jsx(CanvasMetaLine, { breakpoint: bp, pageHeight: pageHeight, scale: scale }), _jsx("div", { className: "prenta-canvas-fit mx-auto block", style: {
|
|
76
76
|
width: Math.round(bp.width * scale),
|
|
77
77
|
height: pageHeight ? Math.round(pageHeight * scale) : undefined,
|
|
78
78
|
}, children: _jsx("div", { ref: (el) => {
|
|
@@ -86,7 +86,7 @@ export function EditorCanvas({ sections, selectedId, breakpoint, zoom = 'fit', o
|
|
|
86
86
|
width: bp.width,
|
|
87
87
|
transform: `scale(${scale})`,
|
|
88
88
|
transformOrigin: 'top left',
|
|
89
|
-
}, children: sections.map((section) => (_jsx(SectionRenderer, { section: section, editable: true, selected: section.id === selectedId, onSelect: onSelect, registerRef: registerRef }, section.id))) }) })] }))] }));
|
|
89
|
+
}, children: sections.map((section) => (_jsx(SectionRenderer, { section: section, editable: true, reviewing: reviewing, changed: changedSectionIds?.has(section.id) ?? false, selected: section.id === selectedId, onSelect: onSelect, registerRef: registerRef }, section.id))) }) })] }))] }));
|
|
90
90
|
}
|
|
91
91
|
/**
|
|
92
92
|
* Design-owned pages always ship the template's sections, so an empty canvas
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorCanvas.js","sourceRoot":"","sources":["../../../src/views/page-editor/EditorCanvas.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AACjF,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAA;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAgC,MAAM,0BAA0B,CAAA;AAEhG,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;
|
|
1
|
+
{"version":3,"file":"EditorCanvas.js","sourceRoot":"","sources":["../../../src/views/page-editor/EditorCanvas.tsx"],"names":[],"mappings":"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"}
|
|
@@ -32,10 +32,16 @@ interface EditorTopBarProps {
|
|
|
32
32
|
*/
|
|
33
33
|
onViewOnSite?: () => void;
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
35
|
+
* Toggle the version history panel. When omitted the History button is
|
|
36
36
|
* hidden — e.g. for unsaved documents that have no versions yet.
|
|
37
37
|
*/
|
|
38
38
|
onHistory?: () => void;
|
|
39
|
+
/**
|
|
40
|
+
* Whether history is showing. History takes over the rail rather than
|
|
41
|
+
* opening a dialog, so the button needs a pressed state — without one there
|
|
42
|
+
* is nothing in the chrome saying which mode the editor is in.
|
|
43
|
+
*/
|
|
44
|
+
historyOpen?: boolean;
|
|
39
45
|
/** Whether the inline site preview (iframe of the public site) is showing. */
|
|
40
46
|
sitePreview?: boolean;
|
|
41
47
|
/**
|
|
@@ -72,6 +78,6 @@ interface EditorTopBarProps {
|
|
|
72
78
|
*/
|
|
73
79
|
structuralTypes?: string[];
|
|
74
80
|
}
|
|
75
|
-
export declare function EditorTopBar({ title, status, dirty, saveState, publishing, canEdit, canPublish, publishReady, publishDisabledReason, viewport, zoom, canvasScale, onZoom, backLabel, onViewport, onBack, onPreview, onViewOnSite, onHistory, sitePreview, onToggleSitePreview, onOpenSettings, onOpenSeo, seoOpen, seoScore, seoScoreBand, onSaveDraft, onPublish, onSchedule, scheduleReady, scheduledAt, scheduledUnpublishAt, structuralTypes, }: EditorTopBarProps): import("react/jsx-runtime").JSX.Element;
|
|
81
|
+
export declare function EditorTopBar({ title, status, dirty, saveState, publishing, canEdit, canPublish, publishReady, publishDisabledReason, viewport, zoom, canvasScale, onZoom, backLabel, onViewport, onBack, onPreview, onViewOnSite, onHistory, historyOpen, sitePreview, onToggleSitePreview, onOpenSettings, onOpenSeo, seoOpen, seoScore, seoScoreBand, onSaveDraft, onPublish, onSchedule, scheduleReady, scheduledAt, scheduledUnpublishAt, structuralTypes, }: EditorTopBarProps): import("react/jsx-runtime").JSX.Element;
|
|
76
82
|
export {};
|
|
77
83
|
//# sourceMappingURL=EditorTopBar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorTopBar.d.ts","sourceRoot":"","sources":["../../../src/views/page-editor/EditorTopBar.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAA;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AACpD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAA;AAE7D,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;IACpB,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,6EAA6E;IAC7E,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,EAAE,UAAU,CAAA;IACpB,mFAAmF;IACnF,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAA;IACnC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAA;IAChC,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wDAAwD;IACxD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IACvC,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,qFAAqF;IACrF,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AASD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,KAAK,EACL,SAAS,EACT,UAAU,EACV,OAAO,EACP,UAAU,EACV,YAAmB,EACnB,qBAAqB,EACrB,QAAQ,EACR,IAAI,EACJ,WAAW,EACX,MAAM,EACN,SAAmB,EACnB,UAAU,EACV,MAAM,EACN,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,OAAe,EACf,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,SAAS,EACT,UAAU,EACV,aAAoB,EACpB,WAAkB,EAClB,oBAA2B,EAC3B,eAAoB,GACrB,EAAE,iBAAiB,
|
|
1
|
+
{"version":3,"file":"EditorTopBar.d.ts","sourceRoot":"","sources":["../../../src/views/page-editor/EditorTopBar.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kCAAkC,CAAA;AAClE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAA;AACpD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAEhD,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAA;AAE7D,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,UAAU,CAAA;IAClB,KAAK,EAAE,OAAO,CAAA;IACd,SAAS,EAAE,SAAS,CAAA;IACpB,UAAU,EAAE,OAAO,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,UAAU,EAAE,OAAO,CAAA;IACnB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,6EAA6E;IAC7E,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,EAAE,UAAU,CAAA;IACpB,mFAAmF;IACnF,IAAI,CAAC,EAAE,IAAI,CAAA;IACX,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC7B,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,CAAA;IACnC,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,IAAI,CAAA;IAChC,cAAc,EAAE,MAAM,IAAI,CAAA;IAC1B,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,wDAAwD;IACxD;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IACvC,WAAW,EAAE,MAAM,IAAI,CAAA;IACvB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,MAAM,IAAI,CAAA;IACvB,qFAAqF;IACrF,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAC3B;AASD,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,KAAK,EACL,SAAS,EACT,UAAU,EACV,OAAO,EACP,UAAU,EACV,YAAmB,EACnB,qBAAqB,EACrB,QAAQ,EACR,IAAI,EACJ,WAAW,EACX,MAAM,EACN,SAAmB,EACnB,UAAU,EACV,MAAM,EACN,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAmB,EACnB,WAAmB,EACnB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,OAAe,EACf,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,SAAS,EACT,UAAU,EACV,aAAoB,EACpB,WAAkB,EAClB,oBAA2B,EAC3B,eAAoB,GACrB,EAAE,iBAAiB,2CA6OnB"}
|
|
@@ -10,7 +10,7 @@ const STATUS_TEXT = {
|
|
|
10
10
|
SCHEDULED: { label: 'Scheduled', className: 'text-warning dark:text-amber-400' },
|
|
11
11
|
ARCHIVED: { label: 'Archived', className: 'text-muted-foreground' },
|
|
12
12
|
};
|
|
13
|
-
export function EditorTopBar({ title, status, dirty, saveState, publishing, canEdit, canPublish, publishReady = true, publishDisabledReason, viewport, zoom, canvasScale, onZoom, backLabel = 'Pages', onViewport, onBack, onPreview, onViewOnSite, onHistory, sitePreview = false, onToggleSitePreview, onOpenSettings, onOpenSeo, seoOpen = false, seoScore, seoScoreBand, onSaveDraft, onPublish, onSchedule, scheduleReady = true, scheduledAt = null, scheduledUnpublishAt = null, structuralTypes = [], }) {
|
|
13
|
+
export function EditorTopBar({ title, status, dirty, saveState, publishing, canEdit, canPublish, publishReady = true, publishDisabledReason, viewport, zoom, canvasScale, onZoom, backLabel = 'Pages', onViewport, onBack, onPreview, onViewOnSite, onHistory, historyOpen = false, sitePreview = false, onToggleSitePreview, onOpenSettings, onOpenSeo, seoOpen = false, seoScore, seoScoreBand, onSaveDraft, onPublish, onSchedule, scheduleReady = true, scheduledAt = null, scheduledUnpublishAt = null, structuralTypes = [], }) {
|
|
14
14
|
const statusInfo = STATUS_TEXT[status];
|
|
15
15
|
// With custom section types on the page the canvas (and the eye preview,
|
|
16
16
|
// which reuses the admin renderers) only shows structure — the real design
|
|
@@ -49,20 +49,22 @@ export function EditorTopBar({ title, status, dirty, saveState, publishing, canE
|
|
|
49
49
|
: scheduledAt
|
|
50
50
|
? `Reschedule — currently publishes ${formatScheduleLabel(scheduledAt)}`
|
|
51
51
|
: 'Schedule a future publish date';
|
|
52
|
-
return (_jsxs("header", { className: "bg-card border-border flex h-14 shrink-0 items-center justify-between gap-4 border-b px-4", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex min-w-0 items-center gap-1.5 text-sm", children: [_jsx("button", { type: "button", onClick: onBack, className: "text-muted-foreground hover:text-foreground shrink-0", children: backLabel }), _jsx("span", { className: "text-muted-foreground", "aria-hidden": true, children: "/" }), _jsx("span", { className: "text-foreground truncate font-medium", children: title || 'Untitled page' })] }), _jsx("span", { className: `ml-2 shrink-0 text-xs font-medium ${statusInfo.className}`, "aria-label": `Status: ${statusInfo.label}`, children: statusInfo.label }), scheduledAt && (_jsxs("span", { className: "text-muted-foreground ml-1 hidden shrink-0 text-xs sm:inline", title: scheduledUnpublishAt
|
|
52
|
+
return (_jsxs("header", { className: "prenta-editor-header bg-card border-border flex h-14 shrink-0 items-center justify-between gap-4 border-b px-4", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsxs("nav", { "aria-label": "Breadcrumb", className: "flex min-w-0 items-center gap-1.5 text-sm", children: [_jsx("button", { type: "button", onClick: onBack, className: "text-muted-foreground hover:text-foreground shrink-0", children: backLabel }), _jsx("span", { className: "text-muted-foreground", "aria-hidden": true, children: "/" }), _jsx("span", { className: "text-foreground truncate font-medium", children: title || 'Untitled page' })] }), _jsx("span", { className: `ml-2 shrink-0 text-xs font-medium ${statusInfo.className}`, "aria-label": `Status: ${statusInfo.label}`, children: statusInfo.label }), scheduledAt && (_jsxs("span", { className: "text-muted-foreground ml-1 hidden shrink-0 text-xs sm:inline", title: scheduledUnpublishAt
|
|
53
53
|
? `Publishes ${formatScheduleLabel(scheduledAt)} · Unpublishes ${formatScheduleLabel(scheduledUnpublishAt)}`
|
|
54
|
-
: `Publishes ${formatScheduleLabel(scheduledAt)}`, children: ["\u00B7 ", formatScheduleLabel(scheduledAt)] })), _jsx(SaveStatus, { state: saveState, dirty: dirty })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "hidden items-center gap-1.5 md:flex", children: [_jsx(BreakpointPicker, { value: viewport, onChange: onViewport }), zoom !== undefined && onZoom && (_jsx(ZoomChip, { zoom: zoom, scale: canvasScale ?? 1, onChange: onZoom }))] }), _jsx("button", { type: "button", onClick: onOpenSettings, "aria-label": "Page settings", title: "Page settings", className: "text-muted-foreground hover:text-foreground hover:bg-accent rounded-lg p-2", children: _jsx(Settings2, { className: "h-4 w-4", "aria-hidden": true }) }), onOpenSeo && (_jsxs("button", { type: "button", onClick: onOpenSeo, "aria-pressed": seoOpen, "aria-label": seoScore === undefined ? 'SEO' : `SEO, score ${seoScore} out of 100`, title: seoScore === undefined
|
|
54
|
+
: `Publishes ${formatScheduleLabel(scheduledAt)}`, children: ["\u00B7 ", formatScheduleLabel(scheduledAt)] })), _jsx(SaveStatus, { state: saveState, dirty: dirty })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "hidden items-center gap-1.5 md:flex", children: [_jsx(BreakpointPicker, { value: viewport, onChange: onViewport }), zoom !== undefined && onZoom && (_jsx(ZoomChip, { zoom: zoom, scale: canvasScale ?? 1, onChange: onZoom }))] }), _jsx("button", { type: "button", onClick: onOpenSettings, "aria-label": "Page settings", title: "Page settings", className: "prenta-touch-target text-muted-foreground hover:text-foreground hover:bg-accent rounded-lg p-2", children: _jsx(Settings2, { className: "h-4 w-4", "aria-hidden": true }) }), onOpenSeo && (_jsxs("button", { type: "button", onClick: onOpenSeo, "aria-pressed": seoOpen, "aria-label": seoScore === undefined ? 'SEO' : `SEO, score ${seoScore} out of 100`, title: seoScore === undefined
|
|
55
55
|
? 'SEO — meta tags, social preview, schema, and analysis.'
|
|
56
56
|
: `SEO — score ${seoScore}/100. Meta tags, social preview, schema, and analysis.`, className: `inline-flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-sm font-medium transition-colors ${seoOpen
|
|
57
57
|
? 'bg-primary text-primary-foreground'
|
|
58
|
-
: 'text-muted-foreground hover:text-foreground hover:bg-accent'}`, children: [seoScore !== undefined && (_jsx(EditorSeoScoreBadge, { score: seoScore, band: seoScoreBand, inverted: seoOpen })), _jsx("span", { className: "hidden sm:inline", children: "SEO" })] })), onHistory && (_jsx("button", { type: "button", onClick: onHistory, "aria-
|
|
58
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-accent'}`, children: [seoScore !== undefined && (_jsx(EditorSeoScoreBadge, { score: seoScore, band: seoScoreBand, inverted: seoOpen })), _jsx("span", { className: "hidden sm:inline", children: "SEO" })] })), onHistory && (_jsx("button", { type: "button", onClick: onHistory, "aria-pressed": historyOpen, "aria-label": "Version history", title: historyOpen ? 'Close version history' : 'Version history', className: `prenta-touch-target rounded-lg p-2 transition-colors ${historyOpen
|
|
59
|
+
? 'bg-primary text-primary-foreground'
|
|
60
|
+
: 'text-muted-foreground hover:text-foreground hover:bg-accent'}`, children: _jsx(History, { className: "h-4 w-4", "aria-hidden": true }) })), _jsx("button", { type: "button", onClick: onPreview, "aria-label": previewLabel, title: previewLabel, className: "prenta-touch-target text-muted-foreground hover:text-foreground hover:bg-accent rounded-lg p-2", children: _jsx(Eye, { className: "h-4 w-4", "aria-hidden": true }) }), onToggleSitePreview && (_jsx("button", { type: "button", onClick: onToggleSitePreview, "aria-pressed": sitePreview, "aria-label": sitePreview ? 'Back to editor canvas' : 'Inline site preview', title: sitePreview
|
|
59
61
|
? 'Back to editor canvas'
|
|
60
|
-
: 'Inline site preview — render the draft through your public site, in place', className: `rounded-lg p-2 transition-colors ${sitePreview
|
|
62
|
+
: 'Inline site preview — render the draft through your public site, in place', className: `prenta-touch-target rounded-lg p-2 transition-colors ${sitePreview
|
|
61
63
|
? 'bg-primary text-primary-foreground'
|
|
62
64
|
: 'text-muted-foreground hover:text-foreground hover:bg-accent'}`, children: _jsx(Globe, { className: "h-4 w-4", "aria-hidden": true }) })), onViewOnSite &&
|
|
63
65
|
(structural ? (_jsxs("button", { type: "button", onClick: onViewOnSite, title: isLive
|
|
64
66
|
? 'Open the live published page in a new tab'
|
|
65
|
-
: 'Open the draft on your public site — the real design preview', className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5", "aria-hidden": true }), isLive ? 'View live' : 'View on site'] })) : (_jsx("button", { type: "button", onClick: onViewOnSite, "aria-label": isLive ? 'View live page' : 'View draft on site', title: isLive ? 'View live page' : 'View draft on site', className: "text-muted-foreground hover:text-foreground hover:bg-accent rounded-lg p-2", children: _jsx(ExternalLink, { className: "h-4 w-4", "aria-hidden": true }) }))), _jsx("button", { type: "button", onClick: onSaveDraft, disabled: !canEdit || saveState === 'saving' || (!dirty && saveState !== 'error'), className: "border-border text-foreground hover:bg-accent rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: saveState === 'saving' ? 'Saving…' : 'Save Draft' }), onSchedule && (_jsxs("button", { type: "button", onClick: onSchedule, disabled: scheduleBlocked, title: scheduleTitle, "aria-label": scheduledAt ? 'Reschedule publish' : 'Schedule publish', className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: [_jsx(Calendar, { className: "h-3.5 w-3.5", "aria-hidden": true }), _jsx("span", { className: "hidden sm:inline", children: scheduledAt ? 'Reschedule' : 'Schedule' })] })), _jsxs("button", { type: "button", onClick: onPublish, disabled: publishBlocked, title: publishTitle, "aria-disabled": publishBlocked, className: "bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: [publishing && _jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": true }), "Publish"] })] })] }));
|
|
67
|
+
: 'Open the draft on your public site — the real design preview', className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors", children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5", "aria-hidden": true }), isLive ? 'View live' : 'View on site'] })) : (_jsx("button", { type: "button", onClick: onViewOnSite, "aria-label": isLive ? 'View live page' : 'View draft on site', title: isLive ? 'View live page' : 'View draft on site', className: "prenta-touch-target text-muted-foreground hover:text-foreground hover:bg-accent rounded-lg p-2", children: _jsx(ExternalLink, { className: "h-4 w-4", "aria-hidden": true }) }))), _jsx("button", { type: "button", onClick: onSaveDraft, disabled: !canEdit || saveState === 'saving' || (!dirty && saveState !== 'error'), className: "border-border text-foreground hover:bg-accent rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: saveState === 'saving' ? 'Saving…' : 'Save Draft' }), onSchedule && (_jsxs("button", { type: "button", onClick: onSchedule, disabled: scheduleBlocked, title: scheduleTitle, "aria-label": scheduledAt ? 'Reschedule publish' : 'Schedule publish', className: "border-border text-foreground hover:bg-accent inline-flex items-center gap-1.5 rounded-lg border px-3 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: [_jsx(Calendar, { className: "h-3.5 w-3.5", "aria-hidden": true }), _jsx("span", { className: "hidden sm:inline", children: scheduledAt ? 'Reschedule' : 'Schedule' })] })), _jsxs("button", { type: "button", onClick: onPublish, disabled: publishBlocked, title: publishTitle, "aria-disabled": publishBlocked, className: "bg-primary text-primary-foreground hover:bg-primary/90 inline-flex items-center gap-1.5 rounded-lg px-3.5 py-1.5 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50", children: [publishing && _jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin", "aria-hidden": true }), "Publish"] })] })] }));
|
|
66
68
|
}
|
|
67
69
|
function SaveStatus({ state, dirty }) {
|
|
68
70
|
if (state === 'saving') {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EditorTopBar.js","sourceRoot":"","sources":["../../../src/views/page-editor/EditorTopBar.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EACL,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,GAAG,EACH,KAAK,EACL,OAAO,EACP,OAAO,EACP,SAAS,EACT,aAAa,GACd,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAA;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"EditorTopBar.js","sourceRoot":"","sources":["../../../src/views/page-editor/EditorTopBar.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAO,EACL,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,GAAG,EACH,KAAK,EACL,OAAO,EACP,OAAO,EACP,SAAS,EACT,aAAa,GACd,MAAM,cAAc,CAAA;AACrB,OAAO,EAAE,mBAAmB,EAAE,MAAM,6CAA6C,CAAA;AACjF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAA;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAoFvD,MAAM,WAAW,GAA6D;IAC5E,KAAK,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,kCAAkC,EAAE;IACxE,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,wCAAwC,EAAE;IACtF,SAAS,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,kCAAkC,EAAE;IAChF,QAAQ,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,uBAAuB,EAAE;CACpE,CAAA;AAED,MAAM,UAAU,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,KAAK,EACL,SAAS,EACT,UAAU,EACV,OAAO,EACP,UAAU,EACV,YAAY,GAAG,IAAI,EACnB,qBAAqB,EACrB,QAAQ,EACR,IAAI,EACJ,WAAW,EACX,MAAM,EACN,SAAS,GAAG,OAAO,EACnB,UAAU,EACV,MAAM,EACN,SAAS,EACT,YAAY,EACZ,SAAS,EACT,WAAW,GAAG,KAAK,EACnB,WAAW,GAAG,KAAK,EACnB,mBAAmB,EACnB,cAAc,EACd,SAAS,EACT,OAAO,GAAG,KAAK,EACf,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,SAAS,EACT,UAAU,EACV,aAAa,GAAG,IAAI,EACpB,WAAW,GAAG,IAAI,EAClB,oBAAoB,GAAG,IAAI,EAC3B,eAAe,GAAG,EAAE,GACF;IAClB,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAA;IACtC,yEAAyE;IACzE,2EAA2E;IAC3E,6EAA6E;IAC7E,MAAM,UAAU,GAAG,eAAe,CAAC,MAAM,GAAG,CAAC,CAAA;IAC7C,MAAM,YAAY,GAAG,UAAU;QAC7B,CAAC,CAAC,uDAAuD;QACzD,CAAC,CAAC,cAAc,CAAA;IAClB,4EAA4E;IAC5E,yEAAyE;IACzE,MAAM,MAAM,GAAG,MAAM,KAAK,WAAW,CAAA;IAErC,MAAM,cAAc,GAAG,CAAC,UAAU,IAAI,CAAC,YAAY,IAAI,UAAU,IAAI,SAAS,KAAK,QAAQ,CAAA;IAC3F,MAAM,YAAY,GAAG,UAAU;QAC7B,CAAC,CAAC,aAAa;QACf,CAAC,CAAC,SAAS,KAAK,QAAQ;YACtB,CAAC,CAAC,qCAAqC;YACvC,CAAC,CAAC,UAAU,IAAI,CAAC,YAAY,IAAI,qBAAqB;gBACpD,CAAC,CAAC,qBAAqB;gBACvB,CAAC,CAAC,CAAC,UAAU;oBACX,CAAC,CAAC,uCAAuC;oBACzC,CAAC,CAAC,SAAS,CAAA;IAEnB,MAAM,eAAe,GACnB,CAAC,UAAU;QACX,CAAC,UAAU;QACX,CAAC,YAAY;QACb,CAAC,aAAa;QACd,UAAU;QACV,SAAS,KAAK,QAAQ,CAAA;IACxB,MAAM,aAAa,GACjB,SAAS,KAAK,QAAQ;QACpB,CAAC,CAAC,qCAAqC;QACvC,CAAC,CAAC,CAAC,aAAa;YACd,CAAC,CAAC,wBAAwB;YAC1B,CAAC,CAAC,UAAU,IAAI,CAAC,YAAY,IAAI,qBAAqB;gBACpD,CAAC,CAAC,qBAAqB;gBACvB,CAAC,CAAC,CAAC,UAAU;oBACX,CAAC,CAAC,wCAAwC;oBAC1C,CAAC,CAAC,WAAW;wBACX,CAAC,CAAC,oCAAoC,mBAAmB,CAAC,WAAW,CAAC,EAAE;wBACxE,CAAC,CAAC,gCAAgC,CAAA;IAE9C,OAAO,CACL,kBAAQ,SAAS,EAAC,gHAAgH,aAEhI,eAAK,SAAS,EAAC,iCAAiC,aAC9C,6BAAgB,YAAY,EAAC,SAAS,EAAC,2CAA2C,aAChF,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAC,sDAAsD,YAE/D,SAAS,GACH,EACT,eAAM,SAAS,EAAC,uBAAuB,uCAEhC,EACP,eAAM,SAAS,EAAC,sCAAsC,YAAE,KAAK,IAAI,eAAe,GAAQ,IACpF,EACN,eACE,SAAS,EAAE,qCAAqC,UAAU,CAAC,SAAS,EAAE,gBAC1D,WAAW,UAAU,CAAC,KAAK,EAAE,YAExC,UAAU,CAAC,KAAK,GACZ,EACN,WAAW,IAAI,CACd,gBACE,SAAS,EAAC,8DAA8D,EACxE,KAAK,EACH,oBAAoB;4BAClB,CAAC,CAAC,aAAa,mBAAmB,CAAC,WAAW,CAAC,kBAAkB,mBAAmB,CAAC,oBAAoB,CAAC,EAAE;4BAC5G,CAAC,CAAC,aAAa,mBAAmB,CAAC,WAAW,CAAC,EAAE,wBAGlD,mBAAmB,CAAC,WAAW,CAAC,IAC9B,CACR,EACD,KAAC,UAAU,IAAC,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,GAAI,IAC1C,EAGN,eAAK,SAAS,EAAC,yBAAyB,aACtC,eAAK,SAAS,EAAC,qCAAqC,aAClD,KAAC,gBAAgB,IAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,GAAI,EAC1D,IAAI,KAAK,SAAS,IAAI,MAAM,IAAI,CAC/B,KAAC,QAAQ,IAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,IAAI,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAI,CACpE,IACG,EAEN,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,cAAc,gBACZ,eAAe,EAC1B,KAAK,EAAC,eAAe,EACrB,SAAS,EAAC,gGAAgG,YAE1G,KAAC,SAAS,IAAC,SAAS,EAAC,SAAS,wBAAe,GACtC,EAER,SAAS,IAAI,CACZ,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,SAAS,kBACJ,OAAO,gBACT,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,QAAQ,aAAa,EAChF,KAAK,EACH,QAAQ,KAAK,SAAS;4BACpB,CAAC,CAAC,wDAAwD;4BAC1D,CAAC,CAAC,eAAe,QAAQ,wDAAwD,EAErF,SAAS,EAAE,iGACT,OAAO;4BACL,CAAC,CAAC,oCAAoC;4BACtC,CAAC,CAAC,6DACN,EAAE,aAED,QAAQ,KAAK,SAAS,IAAI,CACzB,KAAC,mBAAmB,IAAC,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,GAAI,CAChF,EACD,eAAM,SAAS,EAAC,kBAAkB,oBAAW,IACtC,CACV,EAEA,SAAS,IAAI,CACZ,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,SAAS,kBACJ,WAAW,gBACd,iBAAiB,EAC5B,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,iBAAiB,EAChE,SAAS,EAAE,wDACT,WAAW;4BACT,CAAC,CAAC,oCAAoC;4BACtC,CAAC,CAAC,6DACN,EAAE,YAEF,KAAC,OAAO,IAAC,SAAS,EAAC,SAAS,wBAAe,GACpC,CACV,EAED,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,SAAS,gBACN,YAAY,EACxB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAC,gGAAgG,YAE1G,KAAC,GAAG,IAAC,SAAS,EAAC,SAAS,wBAAe,GAChC,EAER,mBAAmB,IAAI,CACtB,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,mBAAmB,kBACd,WAAW,gBACb,WAAW,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,qBAAqB,EACzE,KAAK,EACH,WAAW;4BACT,CAAC,CAAC,uBAAuB;4BACzB,CAAC,CAAC,2EAA2E,EAEjF,SAAS,EAAE,wDACT,WAAW;4BACT,CAAC,CAAC,oCAAoC;4BACtC,CAAC,CAAC,6DACN,EAAE,YAEF,KAAC,KAAK,IAAC,SAAS,EAAC,SAAS,wBAAe,GAClC,CACV,EAEA,YAAY;wBACX,CAAC,UAAU,CAAC,CAAC,CAAC,CACZ,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,YAAY,EACrB,KAAK,EACH,MAAM;gCACJ,CAAC,CAAC,2CAA2C;gCAC7C,CAAC,CAAC,8DAA8D,EAEpE,SAAS,EAAC,oJAAoJ,aAE9J,KAAC,YAAY,IAAC,SAAS,EAAC,aAAa,wBAAe,EACnD,MAAM,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,cAAc,IAC/B,CACV,CAAC,CAAC,CAAC,CACF,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,YAAY,gBACT,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,oBAAoB,EAC5D,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,oBAAoB,EACvD,SAAS,EAAC,gGAAgG,YAE1G,KAAC,YAAY,IAAC,SAAS,EAAC,SAAS,wBAAe,GACzC,CACV,CAAC,EAEJ,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,WAAW,EACpB,QAAQ,EAAE,CAAC,OAAO,IAAI,SAAS,KAAK,QAAQ,IAAI,CAAC,CAAC,KAAK,IAAI,SAAS,KAAK,OAAO,CAAC,EACjF,SAAS,EAAC,mKAAmK,YAE5K,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,GAC3C,EAER,UAAU,IAAI,CACb,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,eAAe,EACzB,KAAK,EAAE,aAAa,gBACR,WAAW,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,kBAAkB,EACnE,SAAS,EAAC,oMAAoM,aAE9M,KAAC,QAAQ,IAAC,SAAS,EAAC,aAAa,wBAAe,EAChD,eAAM,SAAS,EAAC,kBAAkB,YAAE,WAAW,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,GAAQ,IAC5E,CACV,EAED,kBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,SAAS,EAClB,QAAQ,EAAE,cAAc,EACxB,KAAK,EAAE,YAAY,mBACJ,cAAc,EAC7B,SAAS,EAAC,wMAAwM,aAEjN,UAAU,IAAI,KAAC,OAAO,IAAC,SAAS,EAAC,0BAA0B,wBAAe,eAEpE,IACL,IACC,CACV,CAAA;AACH,CAAC;AAED,SAAS,UAAU,CAAC,EAAE,KAAK,EAAE,KAAK,EAAwC;IACxE,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvB,OAAO,CACL,gBACE,SAAS,EAAC,mEAAmE,eACnE,QAAQ,aAElB,KAAC,OAAO,IAAC,SAAS,EAAC,sBAAsB,wBAAe,oBAEnD,CACR,CAAA;IACH,CAAC;IACD,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,OAAO,CACL,gBACE,SAAS,EAAC,8DAA8D,eAC9D,QAAQ,aAElB,KAAC,aAAa,IAAC,SAAS,EAAC,SAAS,wBAAe,sBAE5C,CACR,CAAA;IACH,CAAC;IACD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CACL,eAAM,SAAS,EAAC,oCAAoC,eAAW,QAAQ,gCAEhE,CACR,CAAA;IACH,CAAC;IACD,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;QACtB,OAAO,CACL,gBACE,SAAS,EAAC,mEAAmE,eACnE,QAAQ,aAElB,KAAC,KAAK,IAAC,SAAS,EAAC,SAAS,wBAAe,aAEpC,CACR,CAAA;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAW;IACtC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAA;IACvB,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAAE,OAAO,GAAG,CAAA;IACzC,OAAO,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE;QACjC,KAAK,EAAE,OAAO;QACd,GAAG,EAAE,SAAS;QACd,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,SAAS;KAClB,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PageSectionEditor.d.ts","sourceRoot":"","sources":["../../../src/views/page-editor/PageSectionEditor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"PageSectionEditor.d.ts","sourceRoot":"","sources":["../../../src/views/page-editor/PageSectionEditor.tsx"],"names":[],"mappings":"AAsEA,UAAU,sBAAsB;IAC9B,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,MAAM,CAAC,EAAE,GAAG,CAAA;IACZ,OAAO,CAAC,EAAE,GAAG,CAAA;IACb,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAClC;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,KAAK,IAAI,CAAA;CACjG;AA6CD,wBAAgB,iBAAiB,CAAC,EAChC,UAAU,EACV,eAAe,EACf,MAAM,EACN,OAAO,EACP,UAAU,EACV,aAAa,GACd,EAAE,sBAAsB,2CA2kCxB"}
|
|
@@ -10,8 +10,11 @@ import { SeoEditorPane } from '../../components/seo/SeoEditorPane.js';
|
|
|
10
10
|
import { EditorRail, RailHeader } from '../../components/EditorRail.js';
|
|
11
11
|
import { EditorRailTabs } from '../../components/EditorRailTabs.js';
|
|
12
12
|
import { ErrorBoundary } from '../../components/ErrorBoundary.js';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { VersionHistoryPanel } from './VersionHistoryPanel.js';
|
|
14
|
+
import { ReviewBar } from './ReviewBar.js';
|
|
15
|
+
import { fetchPageVersions, restorePageVersion, } from '../../lib/version-history.js';
|
|
16
|
+
import { changedSectionIds, diffVersions } from '../../lib/version-diff.js';
|
|
17
|
+
import { emptyPage, fetchPageForEditor, missingPageFields, publishPage, PAGES_COLLECTION, savePageDraft, toggleSectionVisibility, updateSectionContent, updateSectionContentAtPath, updateSectionMeta, validatePage, } from '../../lib/page-editor-service.js';
|
|
15
18
|
import { MediaPickerModal } from '../../components/MediaPickerModal.js';
|
|
16
19
|
import { buildPublicPreviewUrl, buildPublicUrl, createPreviewToken, } from '../../lib/preview-link.js';
|
|
17
20
|
import { usePublicSiteUrl } from '../../hooks/usePublicSiteUrl.js';
|
|
@@ -31,7 +34,7 @@ import { EditorCanvas } from './EditorCanvas.js';
|
|
|
31
34
|
import { ReflowContext } from './reflow-context.js';
|
|
32
35
|
import { SitePreviewFrame } from './SitePreviewFrame.js';
|
|
33
36
|
import { useSitePreview } from './useSitePreview.js';
|
|
34
|
-
import {
|
|
37
|
+
import { SectionEditForm } from './SectionEditForm.js';
|
|
35
38
|
import { PageSettingsModal } from './PageSettingsModal.js';
|
|
36
39
|
import { ValidationSummary } from './ValidationSummary.js';
|
|
37
40
|
import { resolveDefaultSitePreview } from '../../lib/editor-preview.js';
|
|
@@ -154,6 +157,12 @@ export function PageSectionEditor({ documentId, initialParentId, config, session
|
|
|
154
157
|
/** Active rail tab. Replaces the old boolean `seoOpen` right-hand aside. */
|
|
155
158
|
const [railTab, setRailTab] = useState('sections');
|
|
156
159
|
const [historyOpen, setHistoryOpen] = useState(false);
|
|
160
|
+
const [versions, setVersions] = useState([]);
|
|
161
|
+
const [versionsLoading, setVersionsLoading] = useState(false);
|
|
162
|
+
const [versionsError, setVersionsError] = useState(null);
|
|
163
|
+
/** Index into `versions`; 0 is the newest, which is the current content. */
|
|
164
|
+
const [versionIndex, setVersionIndex] = useState(0);
|
|
165
|
+
const [restoring, setRestoring] = useState(false);
|
|
157
166
|
const [confirmPublish, setConfirmPublish] = useState(false);
|
|
158
167
|
const [scheduleOpen, setScheduleOpen] = useState(false);
|
|
159
168
|
const [leaveTarget, setLeaveTarget] = useState(null);
|
|
@@ -232,6 +241,96 @@ export function PageSectionEditor({ documentId, initialParentId, config, session
|
|
|
232
241
|
},
|
|
233
242
|
], [page?.sections.length, audit]);
|
|
234
243
|
const activeRailTab = railTabs.find((tab) => tab.id === railTab) ?? railTabs[0];
|
|
244
|
+
// ─── Version history ─────────────────────────────────────────────────
|
|
245
|
+
// History is a MODE, not a dialog: it overlays the rail and drives the
|
|
246
|
+
// canvas, so the version being reviewed is editor state rather than
|
|
247
|
+
// something owned by a panel that can be unmounted.
|
|
248
|
+
const loadVersions = useCallback(async (pageId) => {
|
|
249
|
+
setVersionsLoading(true);
|
|
250
|
+
setVersionsError(null);
|
|
251
|
+
try {
|
|
252
|
+
setVersions(await fetchPageVersions(PAGES_COLLECTION, pageId));
|
|
253
|
+
}
|
|
254
|
+
catch (err) {
|
|
255
|
+
setVersions([]);
|
|
256
|
+
setVersionsError(err instanceof Error ? err.message : 'Failed to load version history');
|
|
257
|
+
}
|
|
258
|
+
finally {
|
|
259
|
+
setVersionsLoading(false);
|
|
260
|
+
}
|
|
261
|
+
}, []);
|
|
262
|
+
const openHistory = useCallback(() => {
|
|
263
|
+
if (!page?.id)
|
|
264
|
+
return;
|
|
265
|
+
setHistoryOpen(true);
|
|
266
|
+
// Always land on "current" so opening history never silently swaps the
|
|
267
|
+
// canvas out from under the editor.
|
|
268
|
+
setVersionIndex(0);
|
|
269
|
+
void loadVersions(page.id);
|
|
270
|
+
}, [page?.id, loadVersions]);
|
|
271
|
+
const closeHistory = useCallback(() => {
|
|
272
|
+
setHistoryOpen(false);
|
|
273
|
+
setVersionIndex(0);
|
|
274
|
+
}, []);
|
|
275
|
+
/**
|
|
276
|
+
* Index 0 is the current content, which the canvas is already showing — so
|
|
277
|
+
* selecting it is "back to current", not a review. Only an older index puts
|
|
278
|
+
* the editor into review mode.
|
|
279
|
+
*/
|
|
280
|
+
const reviewing = historyOpen && versionIndex > 0;
|
|
281
|
+
const reviewVersion = reviewing ? (versions[versionIndex] ?? null) : null;
|
|
282
|
+
// What that version's save CHANGED — diffed against the one before it, which
|
|
283
|
+
// is the question the outlines on the canvas answer.
|
|
284
|
+
const reviewChanges = useMemo(() => reviewVersion
|
|
285
|
+
? diffVersions(reviewVersion.sections, versions[versionIndex + 1]?.sections)
|
|
286
|
+
: [], [reviewVersion, versions, versionIndex]);
|
|
287
|
+
const reviewChangedIds = useMemo(() => changedSectionIds(reviewChanges), [reviewChanges]);
|
|
288
|
+
const handleRestoreVersion = useCallback(async (version) => {
|
|
289
|
+
if (!page?.id)
|
|
290
|
+
return;
|
|
291
|
+
setRestoring(true);
|
|
292
|
+
try {
|
|
293
|
+
await restorePageVersion(PAGES_COLLECTION, page.id, version.id);
|
|
294
|
+
// Re-read rather than trusting the snapshot we already hold: the
|
|
295
|
+
// restore goes through the normal update path, so hooks and derived
|
|
296
|
+
// fields may have altered what actually landed.
|
|
297
|
+
const restored = await fetchPageForEditor(page.id);
|
|
298
|
+
acceptPage(restored);
|
|
299
|
+
setSelectedId(null);
|
|
300
|
+
setDirty(false);
|
|
301
|
+
setSaveState('idle');
|
|
302
|
+
setValidation(null);
|
|
303
|
+
closeHistory();
|
|
304
|
+
void loadVersions(page.id);
|
|
305
|
+
toast.success('Version restored');
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
toast.error(err instanceof Error ? err.message : 'Failed to restore version');
|
|
309
|
+
}
|
|
310
|
+
finally {
|
|
311
|
+
setRestoring(false);
|
|
312
|
+
}
|
|
313
|
+
}, [page?.id, acceptPage, closeHistory, loadVersions]);
|
|
314
|
+
// ─── Edit permission ─────────────────────────────────────────────────
|
|
315
|
+
/**
|
|
316
|
+
* ADR 0002: a Page-Builder-owned page (it has a `_layout`) compiles its
|
|
317
|
+
* sections from the builder layout on every save, so editing them here would
|
|
318
|
+
* be overwritten. The server independently refuses such edits with 409.
|
|
319
|
+
*/
|
|
320
|
+
const readOnly = page?.builderOwned ?? false;
|
|
321
|
+
/**
|
|
322
|
+
* The single gate every writing surface reads.
|
|
323
|
+
*
|
|
324
|
+
* Hoisted above the callbacks rather than derived at render time, because
|
|
325
|
+
* the two that were left behind were the ones that mattered: the site
|
|
326
|
+
* preview's in-place edit handlers close over `canEdit` (the raw role) and
|
|
327
|
+
* so kept accepting edits while a past version was under review — and while
|
|
328
|
+
* a builder-owned page was supposedly read-only. The iframe is a second
|
|
329
|
+
* editing surface, and a lock that only covers the rail is not a lock.
|
|
330
|
+
*/
|
|
331
|
+
const effectiveCanEdit = canEdit && !readOnly && !reviewing;
|
|
332
|
+
/** Restoring is not editing: it is allowed precisely while reviewing. */
|
|
333
|
+
const canRestoreVersion = canEdit && !readOnly;
|
|
235
334
|
// ─── Load ──────────────────────────────────────────────────────────
|
|
236
335
|
useEffect(() => {
|
|
237
336
|
let cancelled = false;
|
|
@@ -289,11 +388,12 @@ export function PageSectionEditor({ documentId, initialParentId, config, session
|
|
|
289
388
|
return;
|
|
290
389
|
mutateSections((s) => updateSectionContent(s, selectedId, patch));
|
|
291
390
|
}, [selectedId, mutateSections]);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
391
|
+
// NOTE: there is deliberately no `handleSettingsChange` here any more.
|
|
392
|
+
// `settings` holds background, spacing, anchor and CSS class — the template's
|
|
393
|
+
// business, not the editor's (docs/design-owned-sections.md). The values
|
|
394
|
+
// still round-trip through save untouched and the renderers still read them;
|
|
395
|
+
// this editor simply no longer offers to change them. The post TEMPLATE
|
|
396
|
+
// editor keeps the full set, because that is the surface that authors one.
|
|
297
397
|
const handleMetaChange = useCallback((patch) => {
|
|
298
398
|
if (!selectedId)
|
|
299
399
|
return;
|
|
@@ -489,24 +589,6 @@ export function PageSectionEditor({ documentId, initialParentId, config, session
|
|
|
489
589
|
toast.success('Page moved to trash');
|
|
490
590
|
onNavigate('/pages');
|
|
491
591
|
}, [page?.id, onNavigate]);
|
|
492
|
-
// After a version restore the server has already written the restored
|
|
493
|
-
// data — refetch so the editor reflects it instead of the stale state.
|
|
494
|
-
const handleRestored = useCallback(() => {
|
|
495
|
-
if (!page?.id)
|
|
496
|
-
return;
|
|
497
|
-
fetchPageForEditor(page.id)
|
|
498
|
-
.then((p) => {
|
|
499
|
-
acceptPage(p);
|
|
500
|
-
setSelectedId(null);
|
|
501
|
-
setRailTab('sections');
|
|
502
|
-
setDirty(false);
|
|
503
|
-
setSaveState('idle');
|
|
504
|
-
setValidation(null);
|
|
505
|
-
})
|
|
506
|
-
.catch(() => {
|
|
507
|
-
toast.error('Version restored — refresh the page to see it.');
|
|
508
|
-
});
|
|
509
|
-
}, [page?.id]);
|
|
510
592
|
const handleSeoSaved = useCallback(() => {
|
|
511
593
|
reloadAudit();
|
|
512
594
|
if (!page?.id)
|
|
@@ -534,19 +616,22 @@ export function PageSectionEditor({ documentId, initialParentId, config, session
|
|
|
534
616
|
setSelectedId(id);
|
|
535
617
|
setPendingFocusPath(fieldPath ?? null);
|
|
536
618
|
}, []);
|
|
619
|
+
// `effectiveCanEdit`, not `canEdit`: the site preview is a second editing
|
|
620
|
+
// surface, so a role check alone let it write through both the version-review
|
|
621
|
+
// lock and the builder-owned lock the rest of the editor honours.
|
|
537
622
|
const handleCanvasEdit = useCallback((edit) => {
|
|
538
|
-
if (!
|
|
623
|
+
if (!effectiveCanEdit)
|
|
539
624
|
return;
|
|
540
625
|
setSuppressSectionsEcho((n) => n + 1);
|
|
541
626
|
mutateSections((s) => updateSectionContentAtPath(s, edit.sectionId, edit.fieldPath, edit.value));
|
|
542
627
|
setSelectedId(edit.sectionId);
|
|
543
|
-
}, [
|
|
628
|
+
}, [effectiveCanEdit, mutateSections]);
|
|
544
629
|
const handleCanvasRequestMedia = useCallback((request) => {
|
|
545
|
-
if (!
|
|
630
|
+
if (!effectiveCanEdit)
|
|
546
631
|
return;
|
|
547
632
|
setSelectedId(request.sectionId);
|
|
548
633
|
setCanvasMediaTarget(request);
|
|
549
|
-
}, [
|
|
634
|
+
}, [effectiveCanEdit]);
|
|
550
635
|
// After a canvas field click, focus the matching inspector input once painted.
|
|
551
636
|
useEffect(() => {
|
|
552
637
|
if (!pendingFocusPath || !selectedId)
|
|
@@ -646,13 +731,11 @@ export function PageSectionEditor({ documentId, initialParentId, config, session
|
|
|
646
731
|
if (loadError || !page) {
|
|
647
732
|
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 }), _jsxs("div", { className: "min-w-0", children: [_jsx("p", { className: "text-foreground text-sm font-medium", children: "Failed to load page" }), _jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: loadError ?? 'Page not found.' }), _jsx("button", { type: "button", onClick: () => onNavigate('/pages'), className: "text-primary mt-2 text-sm font-medium hover:underline", children: "Back to Pages" })] })] }) }));
|
|
648
733
|
}
|
|
649
|
-
|
|
650
|
-
//
|
|
651
|
-
//
|
|
652
|
-
//
|
|
653
|
-
|
|
654
|
-
const readOnly = page.builderOwned;
|
|
655
|
-
const effectiveCanEdit = canEdit && !readOnly;
|
|
734
|
+
// `readOnly` / `effectiveCanEdit` / `canRestoreVersion` are computed above,
|
|
735
|
+
// with the hooks, so the callbacks defined there share this one gate.
|
|
736
|
+
// The canvas shows the reviewed snapshot, but `page.sections` stays the
|
|
737
|
+
// editor's own content throughout — nothing about review mode writes to it.
|
|
738
|
+
const canvasSections = reviewVersion ? reviewVersion.sections : page.sections;
|
|
656
739
|
// Sections whose type has no registered definition (seed/code-owned, e.g.
|
|
657
740
|
// `opal-*`). They are preserved verbatim on save but can't be edited here.
|
|
658
741
|
const unmanagedTypes = Array.from(new Set(page.sections.filter((s) => s.unmanaged).map((s) => s.sectionType)));
|
|
@@ -667,17 +750,23 @@ export function PageSectionEditor({ documentId, initialParentId, config, session
|
|
|
667
750
|
? // The site preview is an iframe of the real front end, not a
|
|
668
751
|
// scaled canvas — a zoom percentage there would be a lie.
|
|
669
752
|
{}
|
|
670
|
-
: { zoom, canvasScale, onZoom: setZoom }), onBack: () => guardedNavigate('/pages'), onPreview: handlePreview, onViewOnSite: page.id ? handleViewOnSite : undefined, sitePreview: sitePreview.active, onToggleSitePreview: page.id && page.slug ? sitePreview.toggle : undefined, onHistory: page.id ? () =>
|
|
753
|
+
: { zoom, canvasScale, onZoom: setZoom }), onBack: () => guardedNavigate('/pages'), onPreview: handlePreview, onViewOnSite: page.id ? handleViewOnSite : undefined, sitePreview: sitePreview.active, onToggleSitePreview: page.id && page.slug ? sitePreview.toggle : undefined, historyOpen: historyOpen, onHistory: page.id ? () => (historyOpen ? closeHistory() : openHistory()) : undefined, onOpenSettings: () => setSettingsOpen(true), onOpenSeo: handleToggleSeo, seoOpen: railTab === 'seo',
|
|
671
754
|
// The audit's SEO score, banded by the audit's own scale. Previously
|
|
672
755
|
// this chip showed `computeLiveSeoScore` — a different model — so the
|
|
673
756
|
// header read 53 while the rings beside it read 0 and 57.
|
|
674
|
-
seoScore: audit?.seo.score ?? undefined, seoScoreBand: typeof audit?.seo.score === 'number' ? scoreBand(audit.seo.score) : undefined, onSaveDraft: handleSaveDraft, onPublish: requestPublish, onSchedule: page.id ? requestSchedule : undefined, scheduleReady: Boolean(page.id), scheduledAt: page.scheduledAt, scheduledUnpublishAt: page.scheduledUnpublishAt, structuralTypes: structuralTypes }), readOnly && (_jsxs("div", { role: "status", className: "border-border bg-accent/40 flex items-center gap-3 border-b px-4 py-2.5", children: [_jsx(Blocks, { className: "text-muted-foreground shrink-0", size: 16, "aria-hidden": true }), _jsxs("p", { className: "text-muted-foreground flex-1 text-sm", children: ["This page is built in the", ' ', _jsx("span", { className: "text-foreground font-medium", children: "Page Builder" }), ". Its sections are generated from the builder layout and are read-only here."] }), page.id && (_jsx("button", { type: "button", onClick: () => guardedNavigate(`/page-builder/${page.id}`), className: "bg-primary text-primary-foreground hover:bg-primary/90 shrink-0 rounded-lg px-3 py-1.5 text-sm font-medium transition-colors", children: "Open in Page Builder" }))] })), unmanagedTypes.length > 0 && (_jsxs("div", { role: "status", className: "border-border bg-muted/50 flex items-start gap-3 border-b px-4 py-2.5", children: [_jsx(Lock, { className: "text-muted-foreground mt-0.5 shrink-0", size: 16, "aria-hidden": true }), _jsxs("p", { className: "text-muted-foreground flex-1 text-sm", children: ["This page has", ' ', _jsxs("span", { className: "text-foreground font-medium", children: [unmanagedTypes.length, " section type", unmanagedTypes.length > 1 ? 's' : ''] }), ' ', "managed outside the editor (", unmanagedTypes.join(', '), "). They are preserved on save but edited in your seed script or code, not here."] })] })), validation && (_jsx(ValidationSummary, { errors: validation.errors, warnings: validation.warnings, onDismiss: () => setValidation(null) })), _jsx(ReflowContext.Provider, { value: reflowContext, children: _jsxs("div", { className: "relative flex flex-1 overflow-hidden", children: [_jsx(EditorRail, { collapsedLabel: activeRailTab.label, children: ({ width, dragging, collapse }) => (_jsxs(_Fragment, { children: [_jsx(EditorRailTabs, { tabs: railTabs, active: railTab, onChange: setRailTab }), _jsx(RailHeader, { title: activeRailTab.title, width: width, dragging: dragging, onCollapse: collapse }), _jsx("div", { role: "tabpanel", id: `rail-panel-${railTab}`, "aria-labelledby": `rail-tab-${railTab}`, className: "flex min-h-0 flex-1 flex-col", children: railTab === 'sections' ? (_jsx(SectionsList, { sections: page.sections, selectedId: selectedId, canEdit: effectiveCanEdit, onSelect: handleSelectSection, onToggleVisibility: handleToggleVisibility
|
|
757
|
+
seoScore: audit?.seo.score ?? undefined, seoScoreBand: typeof audit?.seo.score === 'number' ? scoreBand(audit.seo.score) : undefined, onSaveDraft: handleSaveDraft, onPublish: requestPublish, onSchedule: page.id ? requestSchedule : undefined, scheduleReady: Boolean(page.id), scheduledAt: page.scheduledAt, scheduledUnpublishAt: page.scheduledUnpublishAt, structuralTypes: structuralTypes }), readOnly && (_jsxs("div", { role: "status", className: "border-border bg-accent/40 flex items-center gap-3 border-b px-4 py-2.5", children: [_jsx(Blocks, { className: "text-muted-foreground shrink-0", size: 16, "aria-hidden": true }), _jsxs("p", { className: "text-muted-foreground flex-1 text-sm", children: ["This page is built in the", ' ', _jsx("span", { className: "text-foreground font-medium", children: "Page Builder" }), ". Its sections are generated from the builder layout and are read-only here."] }), page.id && (_jsx("button", { type: "button", onClick: () => guardedNavigate(`/page-builder/${page.id}`), className: "bg-primary text-primary-foreground hover:bg-primary/90 shrink-0 rounded-lg px-3 py-1.5 text-sm font-medium transition-colors", children: "Open in Page Builder" }))] })), unmanagedTypes.length > 0 && (_jsxs("div", { role: "status", className: "border-border bg-muted/50 flex items-start gap-3 border-b px-4 py-2.5", children: [_jsx(Lock, { className: "text-muted-foreground mt-0.5 shrink-0", size: 16, "aria-hidden": true }), _jsxs("p", { className: "text-muted-foreground flex-1 text-sm", children: ["This page has", ' ', _jsxs("span", { className: "text-foreground font-medium", children: [unmanagedTypes.length, " section type", unmanagedTypes.length > 1 ? 's' : ''] }), ' ', "managed outside the editor (", unmanagedTypes.join(', '), "). They are preserved on save but edited in your seed script or code, not here."] })] })), validation && (_jsx(ValidationSummary, { errors: validation.errors, warnings: validation.warnings, onDismiss: () => setValidation(null) })), _jsx(ReflowContext.Provider, { value: reflowContext, children: _jsxs("div", { className: "prenta-editor-body relative flex flex-1 overflow-hidden", children: [_jsx(EditorRail, { collapsedLabel: activeRailTab.label, children: ({ width, dragging, collapse }) => (_jsxs(_Fragment, { children: [_jsx(EditorRailTabs, { tabs: railTabs, active: railTab, onChange: setRailTab }), _jsx(RailHeader, { title: activeRailTab.title, width: width, dragging: dragging, onCollapse: collapse }), _jsx("div", { role: "tabpanel", id: `rail-panel-${railTab}`, "aria-labelledby": `rail-tab-${railTab}`, className: "flex min-h-0 flex-1 flex-col", children: railTab === 'sections' ? (_jsx(SectionsList, { sections: page.sections, selectedId: selectedId, canEdit: effectiveCanEdit, onSelect: handleSelectSection, onToggleVisibility: handleToggleVisibility,
|
|
758
|
+
// The editor opens inside the row it belongs to. While
|
|
759
|
+
// reviewing a past version there is nothing to edit, so
|
|
760
|
+
// rows stay closed rather than opening a dead form.
|
|
761
|
+
renderEditor: reviewing
|
|
762
|
+
? undefined
|
|
763
|
+
: (section) => (_jsx(SectionEditForm, { section: section, canEdit: effectiveCanEdit, onContentChange: handleContentChange, onMetaChange: handleMetaChange })) })) : railTab === 'page' ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: _jsx(PageTab, { page: page, canEdit: effectiveCanEdit, publishedPath: publishedPath, siteUrl: publicSiteUrl, directives: directives, onDirectivesChange: (next) => {
|
|
675
764
|
setDirectives(next);
|
|
676
765
|
setDirty(true);
|
|
677
766
|
}, redirectOnPublish: redirectOnPublish, onRedirectOnPublishChange: setRedirectOnPublish, canCreateRedirect: canCreateRedirect, onPageChange: handlePageSettings, onDuplicate: handleDuplicate, onTrash: handleTrash, schemaType: seoRecord?.structuredDataType ?? undefined, brandName: config?.seo?.siteName ?? undefined }) })) : (_jsx(SeoEditorPane, { entityType: "page", entityId: page.id, slug: page.slug, auditSections: page.sections, onAuditChange: setLiveAudit, contentHtml: seoContentHtml, draftMeta: {
|
|
678
767
|
seoTitle: page.seoTitle,
|
|
679
768
|
seoDescription: page.seoDescription,
|
|
680
|
-
}, onDraftChange: handlePageSettings, onClose: () => setRailTab('sections'), onSaved: handleSeoSaved })) })] })) }), _jsx("div", { className: "flex-1 overflow-hidden", children: sitePreview.active ? (_jsx(SitePreviewFrame, { src: sitePreview.url, viewport: viewport, reloadKey: sitePreview.reloadKey, title: `Site preview: ${page.title || 'Untitled page'}`, selectedId: selectedId, sections: page.sections, suppressSectionsEcho: suppressSectionsEcho, onSelectSection: handleSelectSection, onCanvasEdit: handleCanvasEdit, onCanvasRequestMedia: handleCanvasRequestMedia })) : (_jsx(EditorCanvas, { sections:
|
|
769
|
+
}, onDraftChange: handlePageSettings, onClose: () => setRailTab('sections'), onSaved: handleSeoSaved })) }), historyOpen && (_jsx(VersionHistoryPanel, { versions: versions, loading: versionsLoading, error: versionsError, selectedIndex: versionIndex, onSelect: setVersionIndex, onClose: closeHistory, onRestore: handleRestoreVersion, restoring: restoring, canRestore: canRestoreVersion }))] })) }), _jsx("div", { className: "flex-1 overflow-hidden", children: sitePreview.active ? (_jsx(SitePreviewFrame, { src: sitePreview.url, viewport: viewport, reloadKey: sitePreview.reloadKey, title: `Site preview: ${page.title || 'Untitled page'}`, selectedId: selectedId, sections: page.sections, suppressSectionsEcho: suppressSectionsEcho, onSelectSection: handleSelectSection, onCanvasEdit: handleCanvasEdit, onCanvasRequestMedia: handleCanvasRequestMedia })) : (_jsx(EditorCanvas, { sections: canvasSections, selectedId: selectedId, breakpoint: viewport, zoom: zoom, onScaleChange: setCanvasScale, onCanvasElement: setCanvasEl, onSelect: handleSelectSection, structuralTypes: structuralTypes, sitePreviewAvailable: sitePreviewAvailable && !sitePreview.active, reviewing: reviewing, changedSectionIds: reviewChangedIds, reviewBar: reviewVersion ? (_jsx(ReviewBar, { version: reviewVersion, changedCount: reviewChangedIds.size, onRestore: () => void handleRestoreVersion(reviewVersion), onBackToCurrent: () => setVersionIndex(0), restoring: restoring, canRestore: canRestoreVersion })) : null })) })] }) })] }), _jsx(PageSettingsModal, { open: settingsOpen, page: page, canEdit: effectiveCanEdit, onClose: () => setSettingsOpen(false), onSave: handlePageSettings }), _jsx(ConfirmDialog, { open: confirmPublish, onClose: () => setConfirmPublish(false), onConfirm: doPublish, title: "Publish this page?", description: "The current content will go live and replace the published version.", confirmLabel: "Publish" }), page.id && (_jsx(SchedulePublishDialog, { collectionSlug: PAGES_COLLECTION, documentId: page.id, scheduledAt: page.scheduledAt, scheduledUnpublishAt: page.scheduledUnpublishAt, open: scheduleOpen, onClose: () => setScheduleOpen(false), onScheduled: handleScheduled })), _jsx(ConfirmDialog, { open: leaveTarget !== null, onClose: () => setLeaveTarget(null), onConfirm: () => {
|
|
681
770
|
const target = leaveTarget;
|
|
682
771
|
setLeaveTarget(null);
|
|
683
772
|
setDirty(false);
|