@actuate-media/cms-admin 0.69.1 → 0.70.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 (86) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +4 -1
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/lib/pages-tree.test.js +40 -1
  6. package/dist/__tests__/lib/pages-tree.test.js.map +1 -1
  7. package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts +2 -0
  8. package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts.map +1 -0
  9. package/dist/__tests__/views/page-builder-template-seed.render.test.js +48 -0
  10. package/dist/__tests__/views/page-builder-template-seed.render.test.js.map +1 -0
  11. package/dist/__tests__/views/page-templates.render.test.js +21 -0
  12. package/dist/__tests__/views/page-templates.render.test.js.map +1 -1
  13. package/dist/actuate-admin.css +1 -1
  14. package/dist/components/TipTapEditor.d.ts.map +1 -1
  15. package/dist/components/TipTapEditor.js +11 -9
  16. package/dist/components/TipTapEditor.js.map +1 -1
  17. package/dist/index.d.ts +3 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/lib/pages-service.d.ts +7 -0
  22. package/dist/lib/pages-service.d.ts.map +1 -1
  23. package/dist/lib/pages-service.js +11 -0
  24. package/dist/lib/pages-service.js.map +1 -1
  25. package/dist/lib/pages-tree.d.ts +14 -0
  26. package/dist/lib/pages-tree.d.ts.map +1 -1
  27. package/dist/lib/pages-tree.js +51 -0
  28. package/dist/lib/pages-tree.js.map +1 -1
  29. package/dist/views/FormEditor.js +5 -7
  30. package/dist/views/FormEditor.js.map +1 -1
  31. package/dist/views/HiddenLoginNotFound.d.ts +6 -0
  32. package/dist/views/HiddenLoginNotFound.d.ts.map +1 -1
  33. package/dist/views/HiddenLoginNotFound.js +6 -0
  34. package/dist/views/HiddenLoginNotFound.js.map +1 -1
  35. package/dist/views/MediaBrowser.d.ts.map +1 -1
  36. package/dist/views/MediaBrowser.js +2 -1
  37. package/dist/views/MediaBrowser.js.map +1 -1
  38. package/dist/views/PageEditor.d.ts +4 -0
  39. package/dist/views/PageEditor.d.ts.map +1 -1
  40. package/dist/views/PageEditor.js +4 -0
  41. package/dist/views/PageEditor.js.map +1 -1
  42. package/dist/views/Pages/PagesListView.d.ts.map +1 -1
  43. package/dist/views/Pages/PagesListView.js +40 -5
  44. package/dist/views/Pages/PagesListView.js.map +1 -1
  45. package/dist/views/Pages/PagesTreeTable.d.ts +11 -1
  46. package/dist/views/Pages/PagesTreeTable.d.ts.map +1 -1
  47. package/dist/views/Pages/PagesTreeTable.js +40 -9
  48. package/dist/views/Pages/PagesTreeTable.js.map +1 -1
  49. package/dist/views/Pages.d.ts +4 -0
  50. package/dist/views/Pages.d.ts.map +1 -1
  51. package/dist/views/Pages.js +4 -0
  52. package/dist/views/Pages.js.map +1 -1
  53. package/dist/views/Redirects.d.ts +4 -0
  54. package/dist/views/Redirects.d.ts.map +1 -1
  55. package/dist/views/Redirects.js +4 -0
  56. package/dist/views/Redirects.js.map +1 -1
  57. package/dist/views/ScriptTags.d.ts.map +1 -1
  58. package/dist/views/ScriptTags.js +2 -1
  59. package/dist/views/ScriptTags.js.map +1 -1
  60. package/dist/views/page-builder/PageBuilder.d.ts +3 -1
  61. package/dist/views/page-builder/PageBuilder.d.ts.map +1 -1
  62. package/dist/views/page-builder/PageBuilder.js +24 -2
  63. package/dist/views/page-builder/PageBuilder.js.map +1 -1
  64. package/dist/views/page-builder/PageTemplates.d.ts.map +1 -1
  65. package/dist/views/page-builder/PageTemplates.js +30 -4
  66. package/dist/views/page-builder/PageTemplates.js.map +1 -1
  67. package/package.json +1 -1
  68. package/src/AdminRoot.tsx +11 -1
  69. package/src/__tests__/lib/pages-tree.test.ts +49 -0
  70. package/src/__tests__/views/page-builder-template-seed.render.test.tsx +59 -0
  71. package/src/__tests__/views/page-templates.render.test.tsx +40 -0
  72. package/src/components/TipTapEditor.tsx +11 -9
  73. package/src/index.ts +3 -0
  74. package/src/lib/pages-service.ts +15 -0
  75. package/src/lib/pages-tree.ts +54 -0
  76. package/src/views/FormEditor.tsx +10 -10
  77. package/src/views/HiddenLoginNotFound.tsx +6 -0
  78. package/src/views/MediaBrowser.tsx +8 -9
  79. package/src/views/PageEditor.tsx +4 -0
  80. package/src/views/Pages/PagesListView.tsx +54 -9
  81. package/src/views/Pages/PagesTreeTable.tsx +203 -111
  82. package/src/views/Pages.tsx +4 -0
  83. package/src/views/Redirects.tsx +4 -0
  84. package/src/views/ScriptTags.tsx +12 -8
  85. package/src/views/page-builder/PageBuilder.tsx +30 -2
  86. package/src/views/page-builder/PageTemplates.tsx +56 -15
@@ -1,7 +1,7 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
3
  import { useMemo, useState } from 'react';
4
- import { AlertTriangle, Layers, Pencil, RefreshCw, Trash2 } from 'lucide-react';
4
+ import { AlertTriangle, Copy, FilePlus, Layers, Pencil, RefreshCw, Trash2 } from 'lucide-react';
5
5
  import { toast } from 'sonner';
6
6
  import { cmsApi } from '../../lib/api.js';
7
7
  import { useApiData } from '../../lib/useApiData.js';
@@ -56,17 +56,43 @@ export function PageTemplates({ onNavigate }) {
56
56
  refetch();
57
57
  }
58
58
  }
59
- return (_jsxs("div", { className: "p-4 pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-[var(--r)] border border-[var(--bdr)] bg-[var(--card)] p-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0 text-[var(--muted)]", "aria-hidden": true }), _jsx("span", { className: "flex-1 text-sm text-[var(--txt)]", children: error }), _jsx("button", { type: "button", onClick: refetch, className: "rounded-md border border-[var(--bdr)] px-3 py-1 text-sm text-[var(--txt)] hover:bg-[var(--acc-l)]", children: "Retry" })] })), _jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-foreground mb-1", children: "Page Templates" }), _jsxs("p", { className: "text-sm text-[var(--sub)]", children: [templates.length, " saved template", templates.length === 1 ? '' : 's'] })] }), _jsxs("button", { type: "button", onClick: refetch, className: "inline-flex items-center gap-2 rounded-md border border-[var(--bdr)] px-3 py-2 text-sm font-medium text-[var(--txt)] transition-colors duration-[var(--motion-fast)] hover:border-[var(--acc-m)] hover:bg-[var(--acc-l)] hover:text-[var(--acc)]", children: [_jsx(RefreshCw, { className: "h-4 w-4", "aria-hidden": true }), "Refresh"] })] }), !loading && templates.length > 0 && (_jsxs("div", { className: "mb-4 flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "max-w-xs min-w-52 flex-1", children: _jsx(SearchInput, { value: search, onChange: setSearch, placeholder: "Search templates\u2026" }) }), _jsxs("div", { className: "flex gap-1.5", role: "group", "aria-label": "Filter templates by category", children: [_jsx(FilterPill, { active: category === 'all', onClick: () => setCategory('all'), children: "All" }), Object.keys(CATEGORIES).map((cat) => (_jsx(FilterPill, { active: category === cat, onClick: () => setCategory(cat), children: CATEGORIES[cat].label }, cat)))] }), _jsxs("span", { className: "ml-auto text-[12.5px] whitespace-nowrap text-[var(--muted)]", children: [filtered.length, " of ", templates.length] })] })), loading ? (_jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3", role: "status", "aria-live": "polite", "aria-label": "Loading page templates", children: Array.from({ length: 6 }, (_, i) => (_jsx(Skeleton, { variant: "card" }, i))) })) : templates.length === 0 ? (_jsx(EmptyState, { icon: _jsx(Layers, { size: 24, "aria-hidden": true }), title: "No page templates yet", description: "Built-in templates are seeded by the CMS when the templates API is available.", actionLabel: "View Saved Sections", onAction: () => onNavigate?.('/saved-sections') })) : filtered.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-[var(--muted)]", children: "No templates match your search." })) : (_jsx("ul", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3", "aria-label": "Page templates", children: filtered.map((template) => (_jsx(TemplateCard, { template: template, onEdit: () => setEditTarget(template), onDelete: () => setDeleteTarget(template) }, template.id))) })), _jsx(TemplateEditModal, { template: editTarget, onClose: () => setEditTarget(null), onSaved: () => {
59
+ async function duplicateTemplate(template) {
60
+ // The list payload carries the full tree, so no extra fetch is needed.
61
+ // Duplicating a built-in is the supported way to customize one (built-ins
62
+ // themselves are locked against edits).
63
+ if (!template.tree) {
64
+ toast.error('Template has no layout to duplicate');
65
+ return;
66
+ }
67
+ const res = await cmsApi('/page-templates', {
68
+ method: 'POST',
69
+ body: JSON.stringify({
70
+ name: `${template.name ?? 'Untitled template'} (copy)`,
71
+ description: template.description ?? null,
72
+ category: categoryOf(template),
73
+ tree: template.tree,
74
+ thumbnail: template.thumbnail ?? null,
75
+ }),
76
+ });
77
+ if (res.error) {
78
+ toast.error(res.error);
79
+ }
80
+ else {
81
+ toast.success('Template duplicated');
82
+ refetch();
83
+ }
84
+ }
85
+ return (_jsxs("div", { className: "p-4 pr-8", children: [error && (_jsxs("div", { className: "mb-4 flex items-center gap-3 rounded-[var(--r)] border border-[var(--bdr)] bg-[var(--card)] p-3", children: [_jsx(AlertTriangle, { className: "h-5 w-5 shrink-0 text-[var(--muted)]", "aria-hidden": true }), _jsx("span", { className: "flex-1 text-sm text-[var(--txt)]", children: error }), _jsx("button", { type: "button", onClick: refetch, className: "rounded-md border border-[var(--bdr)] px-3 py-1 text-sm text-[var(--txt)] hover:bg-[var(--acc-l)]", children: "Retry" })] })), _jsxs("div", { className: "mb-4 flex items-center justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "text-foreground mb-1", children: "Page Templates" }), _jsxs("p", { className: "text-sm text-[var(--sub)]", children: [templates.length, " saved template", templates.length === 1 ? '' : 's'] })] }), _jsx(Button, { type: "button", variant: "outline", size: "md", onClick: refetch, leftIcon: _jsx(RefreshCw, { size: 16, "aria-hidden": true }), children: "Refresh" })] }), !loading && templates.length > 0 && (_jsxs("div", { className: "mb-4 flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "max-w-xs min-w-52 flex-1", children: _jsx(SearchInput, { value: search, onChange: setSearch, placeholder: "Search templates\u2026" }) }), _jsxs("div", { className: "flex gap-1.5", role: "group", "aria-label": "Filter templates by category", children: [_jsx(FilterPill, { active: category === 'all', onClick: () => setCategory('all'), children: "All" }), Object.keys(CATEGORIES).map((cat) => (_jsx(FilterPill, { active: category === cat, onClick: () => setCategory(cat), children: CATEGORIES[cat].label }, cat)))] }), _jsxs("span", { className: "ml-auto text-[12.5px] whitespace-nowrap text-[var(--muted)]", children: [filtered.length, " of ", templates.length] })] })), loading ? (_jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3", role: "status", "aria-live": "polite", "aria-label": "Loading page templates", children: Array.from({ length: 6 }, (_, i) => (_jsx(Skeleton, { variant: "card" }, i))) })) : templates.length === 0 ? (_jsx(EmptyState, { icon: _jsx(Layers, { size: 24, "aria-hidden": true }), title: "No page templates yet", description: "Built-in templates are seeded by the CMS when the templates API is available.", actionLabel: "View Saved Sections", onAction: () => onNavigate?.('/saved-sections') })) : filtered.length === 0 ? (_jsx("p", { className: "py-12 text-center text-sm text-[var(--muted)]", children: "No templates match your search." })) : (_jsx("ul", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3", "aria-label": "Page templates", children: filtered.map((template) => (_jsx(TemplateCard, { template: template, onUse: () => onNavigate?.(`/page-builder/new?template=${template.id}`), onDuplicate: () => void duplicateTemplate(template), onEdit: () => setEditTarget(template), onDelete: () => setDeleteTarget(template) }, template.id))) })), _jsx(TemplateEditModal, { template: editTarget, onClose: () => setEditTarget(null), onSaved: () => {
60
86
  setEditTarget(null);
61
87
  refetch();
62
88
  } }), _jsx(ConfirmDialog, { open: deleteTarget !== null, onClose: () => setDeleteTarget(null), onConfirm: () => void confirmDelete(), title: "Delete template?", description: `"${deleteTarget?.name ?? 'This template'}" will be permanently removed. Pages already created from it are not affected.`, confirmLabel: "Delete", destructive: true })] }));
63
89
  }
64
- function TemplateCard({ template, onEdit, onDelete, }) {
90
+ function TemplateCard({ template, onUse, onDuplicate, onEdit, onDelete, }) {
65
91
  const cat = CATEGORIES[categoryOf(template)];
66
92
  const sections = sectionCountOf(template);
67
93
  const name = template.name ?? 'Untitled template';
68
94
  const updated = template.updatedAt ? new Date(template.updatedAt).toLocaleDateString() : null;
69
- return (_jsxs("li", { className: "group relative flex flex-col overflow-hidden rounded-[var(--r)] border border-[var(--bdr)] bg-[var(--card)] shadow-[var(--shd)] transition-[border-color,box-shadow,transform] duration-[var(--motion-fast)] focus-within:border-[var(--acc-m)] focus-within:shadow-[var(--shd2)] hover:-translate-y-0.5 hover:border-[var(--acc-m)] hover:shadow-[var(--shd2)] motion-reduce:hover:translate-y-0", children: [_jsx(TemplatePreview, { template: template, sections: sections }), _jsxs("div", { className: "flex flex-1 flex-col p-4 pb-0", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("h2", { className: "text-[15px] leading-snug font-medium text-[var(--txt)]", children: name }), template.builtIn && (_jsx("span", { className: "shrink-0 rounded-full border border-[var(--bdr)] bg-[var(--bg)] px-2 py-0.5 text-[10.5px] font-medium whitespace-nowrap text-[var(--sub)]", children: "Built-in" }))] }), _jsx("p", { className: "mt-2 min-h-10 text-[12.5px] leading-relaxed text-[var(--sub)]", children: template.description ?? 'No description provided.' }), _jsxs("div", { className: "relative mt-3 flex h-[46px] items-center justify-between border-t border-[var(--bdr)] py-2.5", children: [_jsxs("span", { className: `inline-flex items-center gap-1.5 text-[11.5px] font-medium ${cat.text}`, children: [_jsx("span", { "aria-hidden": true, className: `h-[7px] w-[7px] shrink-0 rounded-full ${cat.dot}` }), cat.label] }), _jsxs("span", { className: "text-[11.5px] text-[var(--muted)] transition-opacity duration-[var(--motion-fast)] group-focus-within:opacity-0 group-hover:opacity-0", children: [sections, " section", sections === 1 ? '' : 's', updated ? ` · ${updated}` : ''] }), !template.builtIn && (_jsxs(RowActions, { className: "absolute top-1/2 right-0 -translate-y-1/2", children: [_jsx(RowActionButton, { "aria-label": `Edit ${name}`, onClick: onEdit, children: _jsx(Pencil, { size: 14, "aria-hidden": true }) }), _jsx(RowActionButton, { "aria-label": `Delete ${name}`, danger: true, onClick: onDelete, children: _jsx(Trash2, { size: 14, "aria-hidden": true }) })] }))] })] })] }));
95
+ return (_jsxs("li", { className: "group relative flex flex-col overflow-hidden rounded-[var(--r)] border border-[var(--bdr)] bg-[var(--card)] shadow-[var(--shd)] transition-[border-color,box-shadow,transform] duration-[var(--motion-fast)] focus-within:border-[var(--acc-m)] focus-within:shadow-[var(--shd2)] hover:-translate-y-0.5 hover:border-[var(--acc-m)] hover:shadow-[var(--shd2)] motion-reduce:hover:translate-y-0", children: [_jsx(TemplatePreview, { template: template, sections: sections }), _jsxs("div", { className: "flex flex-1 flex-col p-4 pb-0", children: [_jsxs("div", { className: "flex items-start justify-between gap-3", children: [_jsx("h2", { className: "text-[15px] leading-snug font-medium text-[var(--txt)]", children: name }), template.builtIn && (_jsx("span", { className: "shrink-0 rounded-full border border-[var(--bdr)] bg-[var(--bg)] px-2 py-0.5 text-[10.5px] font-medium whitespace-nowrap text-[var(--sub)]", children: "Built-in" }))] }), _jsx("p", { className: "mt-2 min-h-10 text-[12.5px] leading-relaxed text-[var(--sub)]", children: template.description ?? 'No description provided.' }), _jsxs("div", { className: "relative mt-3 flex h-[46px] items-center justify-between border-t border-[var(--bdr)] py-2.5", children: [_jsxs("span", { className: `inline-flex items-center gap-1.5 text-[11.5px] font-medium ${cat.text}`, children: [_jsx("span", { "aria-hidden": true, className: `h-[7px] w-[7px] shrink-0 rounded-full ${cat.dot}` }), cat.label] }), _jsxs("span", { className: "text-[11.5px] text-[var(--muted)] transition-opacity duration-[var(--motion-fast)] group-focus-within:opacity-0 group-hover:opacity-0", children: [sections, " section", sections === 1 ? '' : 's', updated ? ` · ${updated}` : ''] }), _jsxs(RowActions, { className: "absolute top-1/2 right-0 -translate-y-1/2", children: [_jsx(RowActionButton, { "aria-label": `Create page from ${name}`, onClick: onUse, children: _jsx(FilePlus, { size: 14, "aria-hidden": true }) }), _jsx(RowActionButton, { "aria-label": `Duplicate ${name}`, onClick: onDuplicate, children: _jsx(Copy, { size: 14, "aria-hidden": true }) }), !template.builtIn && (_jsxs(_Fragment, { children: [_jsx(RowActionButton, { "aria-label": `Edit ${name}`, onClick: onEdit, children: _jsx(Pencil, { size: 14, "aria-hidden": true }) }), _jsx(RowActionButton, { "aria-label": `Delete ${name}`, danger: true, onClick: onDelete, children: _jsx(Trash2, { size: 14, "aria-hidden": true }) })] }))] })] })] })] }));
70
96
  }
71
97
  /**
72
98
  * Card preview area. Uses the stored thumbnail when one exists; otherwise
@@ -1 +1 @@
1
- {"version":3,"file":"PageTemplates.js","sourceRoot":"","sources":["../../../src/views/page-builder/PageTemplates.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PageTemplates.js","sourceRoot":"","sources":["../../../src/views/page-builder/PageTemplates.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actuate-media/cms-admin",
3
- "version": "0.69.1",
3
+ "version": "0.70.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/actuate-media/actuatecms.git",
package/src/AdminRoot.tsx CHANGED
@@ -627,7 +627,17 @@ function AdminShell({
627
627
  // declared as `type: 'page'` through the Page Builder by default.
628
628
  const pageBuilderNew = matchRoute('/page-builder/new')
629
629
  if (pageBuilderNew) {
630
- return <PageBuilder collectionSlug="pages" config={config} onNavigate={navigate} />
630
+ // "Use template" on the Templates grid seeds the new page from a
631
+ // saved template (`/page-builder/new?template=<id>`).
632
+ const builderQuery = parseRouteQuery(currentPath)
633
+ return (
634
+ <PageBuilder
635
+ templateId={builderQuery.get('template') ?? undefined}
636
+ collectionSlug="pages"
637
+ config={config}
638
+ onNavigate={navigate}
639
+ />
640
+ )
631
641
  }
632
642
 
633
643
  const pageBuilderEdit = matchRoute('/page-builder/:id')
@@ -1,6 +1,8 @@
1
1
  import { describe, expect, it } from 'vitest'
2
2
  import {
3
3
  buildPageTree,
4
+ checkReparent,
5
+ collectDescendantIds,
4
6
  defaultExpandedIds,
5
7
  flattenPageTree,
6
8
  type PageTreeNode,
@@ -195,3 +197,50 @@ describe('defaultExpandedIds', () => {
195
197
  expect([...defaultExpandedIds(roots)]).toEqual(['about'])
196
198
  })
197
199
  })
200
+
201
+ describe('collectDescendantIds', () => {
202
+ it('returns every id in the subtree, excluding the page itself', () => {
203
+ const roots = buildPageTree([
204
+ page({ id: 'about', title: 'About Us', path: '/about' }),
205
+ page({ id: 'team', title: 'Our Team', path: '/about/team' }),
206
+ page({ id: 'deep', title: 'Deep', path: '/about/team/deep' }),
207
+ page({ id: 'blog', title: 'Blog', path: '/blog' }),
208
+ ])
209
+ expect([...collectDescendantIds(roots, 'about')].sort()).toEqual(['deep', 'team'])
210
+ expect([...collectDescendantIds(roots, 'blog')]).toEqual([])
211
+ expect([...collectDescendantIds(roots, 'missing')]).toEqual([])
212
+ })
213
+ })
214
+
215
+ describe('checkReparent', () => {
216
+ const roots = () =>
217
+ buildPageTree([
218
+ page({ id: 'about', title: 'About Us', path: '/about' }),
219
+ page({ id: 'team', title: 'Our Team', path: '/about/team' }),
220
+ page({ id: 'deep', title: 'Deep', path: '/about/team/deep' }),
221
+ page({ id: 'blog', title: 'Blog', path: '/blog' }),
222
+ ])
223
+
224
+ it('allows moving a page under an unrelated page', () => {
225
+ expect(checkReparent(roots(), 'blog', 'team')).toEqual({ ok: true })
226
+ })
227
+
228
+ it('allows moving a subtree up to an ancestor that is not its parent', () => {
229
+ // deep currently sits under team; moving it directly under about is legal.
230
+ expect(checkReparent(roots(), 'deep', 'about')).toEqual({ ok: true })
231
+ })
232
+
233
+ it('rejects self-drops', () => {
234
+ expect(checkReparent(roots(), 'about', 'about').ok).toBe(false)
235
+ })
236
+
237
+ it('rejects drops into the page own subtree (cycle)', () => {
238
+ expect(checkReparent(roots(), 'about', 'team').ok).toBe(false)
239
+ expect(checkReparent(roots(), 'about', 'deep').ok).toBe(false)
240
+ })
241
+
242
+ it('rejects no-op moves onto the current parent', () => {
243
+ expect(checkReparent(roots(), 'team', 'about').ok).toBe(false)
244
+ expect(checkReparent(roots(), 'deep', 'team').ok).toBe(false)
245
+ })
246
+ })
@@ -0,0 +1,59 @@
1
+ // @vitest-environment happy-dom
2
+ import { beforeEach, describe, expect, it, vi, afterEach } from 'vitest'
3
+ import { cleanup, render, screen, waitFor } from '@testing-library/react'
4
+
5
+ const cmsApi = vi.fn()
6
+ vi.mock('../../lib/api.js', () => ({ cmsApi: (...args: unknown[]) => cmsApi(...args) }))
7
+ vi.mock('sonner', () => ({
8
+ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn() },
9
+ Toaster: () => null,
10
+ }))
11
+
12
+ const { PageBuilder } = await import('../../views/page-builder/PageBuilder.js')
13
+
14
+ const TEMPLATE_TREE = {
15
+ id: 'tpl-page',
16
+ type: 'page',
17
+ children: [
18
+ {
19
+ id: 'sec1',
20
+ type: 'section',
21
+ settings: { label: 'Hero From Template' },
22
+ children: [],
23
+ },
24
+ ],
25
+ }
26
+
27
+ beforeEach(() => {
28
+ cmsApi.mockReset()
29
+ cmsApi.mockImplementation(async (endpoint: string) => {
30
+ if (endpoint === '/page-templates/tpl-1') {
31
+ return { data: { id: 'tpl-1', name: 'Landing', tree: TEMPLATE_TREE }, status: 200 }
32
+ }
33
+ return { data: {}, status: 200 }
34
+ })
35
+ })
36
+
37
+ afterEach(cleanup)
38
+
39
+ describe('PageBuilder template seeding (/page-builder/new?template=)', () => {
40
+ it('fetches the template and seeds the canvas tree for a new page', async () => {
41
+ render(
42
+ <PageBuilder templateId="tpl-1" collectionSlug="pages" config={{}} onNavigate={() => {}} />,
43
+ )
44
+
45
+ await waitFor(() => expect(cmsApi).toHaveBeenCalledWith('/page-templates/tpl-1'))
46
+ // replaceTree marks the builder dirty — the unsaved-changes dot is the
47
+ // observable proof the template tree was seeded into the canvas state.
48
+ expect(await screen.findByTitle('Unsaved changes')).toBeTruthy()
49
+ })
50
+
51
+ it('starts from a blank, clean tree when no template id is given', async () => {
52
+ render(<PageBuilder collectionSlug="pages" config={{}} onNavigate={() => {}} />)
53
+
54
+ await waitFor(() =>
55
+ expect(cmsApi).not.toHaveBeenCalledWith(expect.stringContaining('/page-templates/')),
56
+ )
57
+ expect(screen.queryByTitle('Unsaved changes')).toBeNull()
58
+ })
59
+ })
@@ -119,6 +119,46 @@ describe('PageTemplates card grid', () => {
119
119
  })
120
120
  })
121
121
 
122
+ it('routes "Use template" into the builder with the template id', async () => {
123
+ const onNavigate = vi.fn()
124
+ render(<PageTemplates onNavigate={onNavigate} />)
125
+ await screen.findByText('Landing Page')
126
+
127
+ fireEvent.click(screen.getByRole('button', { name: 'Create page from Landing Page' }))
128
+
129
+ expect(onNavigate).toHaveBeenCalledWith('/page-builder/new?template=builtin-landing')
130
+ })
131
+
132
+ it('duplicates a template (built-ins included) via POST with a "(copy)" name', async () => {
133
+ render(<PageTemplates onNavigate={() => {}} />)
134
+ await screen.findByText('Landing Page')
135
+
136
+ fireEvent.click(screen.getByRole('button', { name: 'Duplicate Landing Page' }))
137
+
138
+ await waitFor(() =>
139
+ expect(cmsApi).toHaveBeenCalledWith(
140
+ '/page-templates',
141
+ expect.objectContaining({ method: 'POST' }),
142
+ ),
143
+ )
144
+ const postCall = cmsApi.mock.calls.find(
145
+ (c) => (c[1] as RequestInit | undefined)?.method === 'POST',
146
+ )!
147
+ expect(JSON.parse((postCall[1] as RequestInit).body as string)).toMatchObject({
148
+ name: 'Landing Page (copy)',
149
+ category: 'landing',
150
+ tree: TEMPLATES[0]!.tree,
151
+ })
152
+ // The grid refetches so the copy appears.
153
+ await waitFor(() =>
154
+ expect(
155
+ cmsApi.mock.calls.filter(
156
+ (c) => c[0] === '/page-templates' && !(c[1] as RequestInit | undefined)?.method,
157
+ ).length,
158
+ ).toBeGreaterThanOrEqual(2),
159
+ )
160
+ })
161
+
122
162
  it('filters cards by search text', async () => {
123
163
  render(<PageTemplates onNavigate={() => {}} />)
124
164
  await screen.findByText('Team Member')
@@ -155,13 +155,13 @@ const editorStyles = `
155
155
  }
156
156
  .ProseMirror th,
157
157
  .ProseMirror td {
158
- border: 1px solid #d1d5db;
158
+ border: 1px solid var(--bdr);
159
159
  padding: 0.5rem;
160
160
  text-align: left;
161
161
  min-width: 80px;
162
162
  }
163
163
  .ProseMirror th {
164
- background-color: #f3f4f6;
164
+ background-color: var(--surface-muted);
165
165
  font-weight: 600;
166
166
  }
167
167
  .ProseMirror img {
@@ -170,11 +170,13 @@ const editorStyles = `
170
170
  border-radius: 0.375rem;
171
171
  }
172
172
  .ProseMirror blockquote {
173
- border-left: 3px solid #3b82f6;
173
+ border-left: 3px solid var(--acc);
174
174
  padding-left: 1rem;
175
175
  margin: 1rem 0;
176
- color: #4b5563;
176
+ color: var(--sub);
177
177
  }
178
+ /* Code blocks use a fixed dark slate theme in both light and dark modes
179
+ (standard "dark editor chrome" convention; no theme token maps to it). */
178
180
  .ProseMirror pre {
179
181
  background: #1e293b;
180
182
  color: #e2e8f0;
@@ -184,7 +186,7 @@ const editorStyles = `
184
186
  margin: 0.75rem 0;
185
187
  }
186
188
  .ProseMirror code {
187
- background: #f1f5f9;
189
+ background: var(--surface-muted);
188
190
  padding: 0.125rem 0.375rem;
189
191
  border-radius: 0.25rem;
190
192
  font-size: 0.875em;
@@ -197,18 +199,18 @@ const editorStyles = `
197
199
  }
198
200
  .ProseMirror hr {
199
201
  border: none;
200
- border-top: 2px solid #e5e7eb;
202
+ border-top: 2px solid var(--bdr);
201
203
  margin: 1.5rem 0;
202
204
  }
203
205
  .ProseMirror p.is-editor-empty:first-child::before {
204
206
  content: attr(data-placeholder);
205
207
  float: left;
206
- color: #9ca3af;
208
+ color: var(--muted);
207
209
  pointer-events: none;
208
210
  height: 0;
209
211
  }
210
212
  .ProseMirror .selectedCell {
211
- background-color: #dbeafe;
213
+ background-color: var(--acc-l);
212
214
  }
213
215
  `
214
216
 
@@ -659,7 +661,7 @@ export function TipTapEditor({
659
661
  className={`border-border bg-card flex-1 overflow-hidden rounded-lg border ${className ?? ''}`}
660
662
  >
661
663
  {collaboration && awareness && (
662
- <div className="bg-card flex items-center justify-between gap-2 border-b border-gray-100 px-2 py-1.5">
664
+ <div className="bg-card border-border flex items-center justify-between gap-2 border-b px-2 py-1.5">
663
665
  <PresenceChips awareness={awareness} status={collabStatus} />
664
666
  <OfflineStatus connection={collabStatus} offline={offlineStatus} />
665
667
  </div>
package/src/index.ts CHANGED
@@ -50,13 +50,16 @@ export type {
50
50
  } from './layout/primitives/index.js'
51
51
 
52
52
  export { Dashboard } from './views/Dashboard.js'
53
+ /** @deprecated Superseded by `PagesListView` routing in `AdminRoot`; will be removed in the next major. */
53
54
  export { Pages } from './views/Pages.js'
55
+ /** @deprecated Superseded by the visual page builder (`PageBuilder`); will be removed in the next major. */
54
56
  export { PageEditor } from './views/PageEditor.js'
55
57
  export { MediaBrowser } from './views/MediaBrowser.js'
56
58
  export { Forms } from './views/Forms.js'
57
59
  export { FormEditor } from './views/FormEditor.js'
58
60
  export type { FormEditorProps } from './views/FormEditor.js'
59
61
  export { FormSubmissions } from './views/FormSubmissions.js'
62
+ /** @deprecated Superseded by the Redirects tab inside the `SEO` view; will be removed in the next major. */
60
63
  export { Redirects } from './views/Redirects.js'
61
64
  export { Users } from './views/Users.js'
62
65
  export { Settings } from './views/Settings.js'
@@ -520,6 +520,21 @@ export async function duplicatePage(id: string): Promise<{ id?: string; error?:
520
520
  return { id: res.data?.id }
521
521
  }
522
522
 
523
+ /**
524
+ * Reparent a page in the tree. `parentPageId: null` makes it top-level.
525
+ * Document updates merge `data`, so only the parent link changes.
526
+ */
527
+ export async function movePage(
528
+ id: string,
529
+ parentPageId: string | null,
530
+ ): Promise<{ error?: string }> {
531
+ const res = await cmsApi(`/collections/${PAGES_COLLECTION}/${encodeURIComponent(id)}`, {
532
+ method: 'PUT',
533
+ body: JSON.stringify({ data: { parentPageId } }),
534
+ })
535
+ return res.error ? { error: res.error } : {}
536
+ }
537
+
523
538
  /** Soft-delete a single page. */
524
539
  export async function deletePage(id: string): Promise<{ error?: string }> {
525
540
  const res = await cmsApi(`/collections/${PAGES_COLLECTION}/${encodeURIComponent(id)}`, {
@@ -143,3 +143,57 @@ export function flattenPageTree(nodes: PageTreeNode[], expanded: Set<string>): P
143
143
  export function defaultExpandedIds(roots: PageTreeNode[]): Set<string> {
144
144
  return new Set(roots.filter((n) => n.children.length > 0).map((n) => n.page.id))
145
145
  }
146
+
147
+ /** Ids of every page in `id`'s subtree (excluding `id` itself). */
148
+ export function collectDescendantIds(roots: PageTreeNode[], id: string): Set<string> {
149
+ const out = new Set<string>()
150
+ const collect = (node: PageTreeNode) => {
151
+ for (const child of node.children) {
152
+ out.add(child.page.id)
153
+ collect(child)
154
+ }
155
+ }
156
+ const find = (nodes: PageTreeNode[]): PageTreeNode | undefined => {
157
+ for (const n of nodes) {
158
+ if (n.page.id === id) return n
159
+ const hit = find(n.children)
160
+ if (hit) return hit
161
+ }
162
+ return undefined
163
+ }
164
+ const node = find(roots)
165
+ if (node) collect(node)
166
+ return out
167
+ }
168
+
169
+ export type ReparentCheck = { ok: true } | { ok: false; reason: string }
170
+
171
+ /**
172
+ * Validate a drag-reparent: `sourceId` becomes a child of `targetId`
173
+ * (or a root when `targetId` is null). Rejects self-drops, drops into the
174
+ * page's own subtree (would create a cycle), and no-op moves.
175
+ */
176
+ export function checkReparent(
177
+ roots: PageTreeNode[],
178
+ sourceId: string,
179
+ targetId: string | null,
180
+ ): ReparentCheck {
181
+ if (targetId === sourceId) {
182
+ return { ok: false, reason: 'A page cannot be its own parent' }
183
+ }
184
+ if (targetId !== null && collectDescendantIds(roots, sourceId).has(targetId)) {
185
+ return { ok: false, reason: 'Cannot move a page into its own sub-pages' }
186
+ }
187
+ const parentOf = (nodes: PageTreeNode[], parent: string | null): string | null | undefined => {
188
+ for (const n of nodes) {
189
+ if (n.page.id === sourceId) return parent
190
+ const hit = parentOf(n.children, n.page.id)
191
+ if (hit !== undefined) return hit
192
+ }
193
+ return undefined
194
+ }
195
+ if ((parentOf(roots, null) ?? null) === targetId) {
196
+ return { ok: false, reason: 'Page is already there' }
197
+ }
198
+ return { ok: true }
199
+ }
@@ -351,11 +351,11 @@ export function FormEditor({ formId, onNavigate }: FormEditorProps) {
351
351
  </label>
352
352
  <div className="flex gap-3">
353
353
  <label
354
- className="hover:bg-muted flex cursor-pointer items-center gap-2 rounded-lg border px-4 py-2 transition-colors"
355
- style={{
356
- borderColor: confirmation.type === 'message' ? '#2563eb' : '#d1d5db',
357
- backgroundColor: confirmation.type === 'message' ? '#eff6ff' : 'transparent',
358
- }}
354
+ className={`flex cursor-pointer items-center gap-2 rounded-lg border px-4 py-2 transition-colors ${
355
+ confirmation.type === 'message'
356
+ ? 'border-primary bg-accent'
357
+ : 'border-border hover:bg-muted bg-transparent'
358
+ }`}
359
359
  >
360
360
  <input
361
361
  type="radio"
@@ -369,11 +369,11 @@ export function FormEditor({ formId, onNavigate }: FormEditorProps) {
369
369
  <span className="text-sm">Show Message</span>
370
370
  </label>
371
371
  <label
372
- className="hover:bg-muted flex cursor-pointer items-center gap-2 rounded-lg border px-4 py-2 transition-colors"
373
- style={{
374
- borderColor: confirmation.type === 'redirect' ? '#2563eb' : '#d1d5db',
375
- backgroundColor: confirmation.type === 'redirect' ? '#eff6ff' : 'transparent',
376
- }}
372
+ className={`flex cursor-pointer items-center gap-2 rounded-lg border px-4 py-2 transition-colors ${
373
+ confirmation.type === 'redirect'
374
+ ? 'border-primary bg-accent'
375
+ : 'border-border hover:bg-muted bg-transparent'
376
+ }`}
377
377
  >
378
378
  <input
379
379
  type="radio"
@@ -5,6 +5,12 @@
5
5
  * (`config.admin.loginPath` set). Mimics the framework's default 404 page so
6
6
  * hitting `/admin` reveals nothing — no login form, no admin branding, and no
7
7
  * redirect that would leak the real login location.
8
+ *
9
+ * INTENTIONAL raw colors: although this mounts inside `.actuate-admin` (so the
10
+ * theme CSS vars are technically available), it must NOT use them. Painting
11
+ * this screen with the admin palette (or its dark-mode variant) would make it
12
+ * visibly differ from the framework's stock 404 and fingerprint the hidden
13
+ * admin to anyone probing `/admin`. Plain #fff/#000 matches the default 404.
8
14
  */
9
15
  export function HiddenLoginNotFound() {
10
16
  return (
@@ -31,6 +31,7 @@ import { sortByRelevance, type SortConfig, toggleSort } from '../lib/search.js'
31
31
  import { useApiData } from '../lib/useApiData.js'
32
32
  import { cmsApi } from '../lib/api.js'
33
33
  import { FolderTree, type FolderSelection } from '../components/FolderTree.js'
34
+ import { Button } from '../components/ui/Button.js'
34
35
  import { StatusBadge } from '../components/ui/StatusBadge.js'
35
36
  import { StatPill } from '../components/ui/StatPill.js'
36
37
  import { MediaSeoAudit, type MediaSeoAuditItem } from './MediaSeoAudit.js'
@@ -508,18 +509,16 @@ export function MediaBrowser({ onNavigate, currentPath }: MediaBrowserProps) {
508
509
  className="hidden"
509
510
  onChange={(e) => handleUploadFiles(e.target.files)}
510
511
  />
511
- <button
512
+ <Button
513
+ type="button"
514
+ variant="primary"
515
+ size="md"
512
516
  onClick={() => fileInputRef.current?.click()}
513
- disabled={uploading}
514
- className="bg-primary text-primary-foreground hover:bg-primary/90 flex items-center gap-2 rounded-lg px-4 py-2 text-sm transition-colors disabled:opacity-50"
517
+ loading={uploading}
518
+ leftIcon={<Upload size={16} aria-hidden />}
515
519
  >
516
- {uploading ? (
517
- <Loader2 className="h-4 w-4 animate-spin" />
518
- ) : (
519
- <Upload className="h-4 w-4" />
520
- )}
521
520
  {uploading ? 'Uploading...' : 'Upload Files'}
522
- </button>
521
+ </Button>
523
522
  </div>
524
523
  </div>
525
524
 
@@ -12,6 +12,10 @@ export interface PageEditorProps {
12
12
  onNavigate?: (path: string) => void
13
13
  }
14
14
 
15
+ /**
16
+ * @deprecated Superseded by the visual page builder (`PageBuilder` and the
17
+ * `views/page-editor/` section editor); will be removed in the next major.
18
+ */
15
19
  export function PageEditor({ id, onNavigate }: PageEditorProps) {
16
20
  const isNew = !id
17
21
  const { data, loading, error } = useApiData<any>(isNew ? '' : `/collections/pages/${id}`)