@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
 
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'
@@ -80,6 +80,32 @@ export function PageTemplates({ onNavigate }: PageTemplatesProps) {
80
80
  }
81
81
  }
82
82
 
83
+ async function duplicateTemplate(template: PageTemplate) {
84
+ // The list payload carries the full tree, so no extra fetch is needed.
85
+ // Duplicating a built-in is the supported way to customize one (built-ins
86
+ // themselves are locked against edits).
87
+ if (!template.tree) {
88
+ toast.error('Template has no layout to duplicate')
89
+ return
90
+ }
91
+ const res = await cmsApi('/page-templates', {
92
+ method: 'POST',
93
+ body: JSON.stringify({
94
+ name: `${template.name ?? 'Untitled template'} (copy)`,
95
+ description: template.description ?? null,
96
+ category: categoryOf(template),
97
+ tree: template.tree,
98
+ thumbnail: template.thumbnail ?? null,
99
+ }),
100
+ })
101
+ if (res.error) {
102
+ toast.error(res.error)
103
+ } else {
104
+ toast.success('Template duplicated')
105
+ refetch()
106
+ }
107
+ }
108
+
83
109
  return (
84
110
  <div className="p-4 pr-8">
85
111
  {error && (
@@ -103,14 +129,15 @@ export function PageTemplates({ onNavigate }: PageTemplatesProps) {
103
129
  {templates.length} saved template{templates.length === 1 ? '' : 's'}
104
130
  </p>
105
131
  </div>
106
- <button
132
+ <Button
107
133
  type="button"
134
+ variant="outline"
135
+ size="md"
108
136
  onClick={refetch}
109
- 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)]"
137
+ leftIcon={<RefreshCw size={16} aria-hidden />}
110
138
  >
111
- <RefreshCw className="h-4 w-4" aria-hidden />
112
139
  Refresh
113
- </button>
140
+ </Button>
114
141
  </div>
115
142
 
116
143
  {!loading && templates.length > 0 && (
@@ -166,6 +193,8 @@ export function PageTemplates({ onNavigate }: PageTemplatesProps) {
166
193
  <TemplateCard
167
194
  key={template.id}
168
195
  template={template}
196
+ onUse={() => onNavigate?.(`/page-builder/new?template=${template.id}`)}
197
+ onDuplicate={() => void duplicateTemplate(template)}
169
198
  onEdit={() => setEditTarget(template)}
170
199
  onDelete={() => setDeleteTarget(template)}
171
200
  />
@@ -197,10 +226,14 @@ export function PageTemplates({ onNavigate }: PageTemplatesProps) {
197
226
 
198
227
  function TemplateCard({
199
228
  template,
229
+ onUse,
230
+ onDuplicate,
200
231
  onEdit,
201
232
  onDelete,
202
233
  }: {
203
234
  template: PageTemplate
235
+ onUse: () => void
236
+ onDuplicate: () => void
204
237
  onEdit: () => void
205
238
  onDelete: () => void
206
239
  }) {
@@ -237,16 +270,24 @@ function TemplateCard({
237
270
  {sections} section{sections === 1 ? '' : 's'}
238
271
  {updated ? ` · ${updated}` : ''}
239
272
  </span>
240
- {!template.builtIn && (
241
- <RowActions className="absolute top-1/2 right-0 -translate-y-1/2">
242
- <RowActionButton aria-label={`Edit ${name}`} onClick={onEdit}>
243
- <Pencil size={14} aria-hidden />
244
- </RowActionButton>
245
- <RowActionButton aria-label={`Delete ${name}`} danger onClick={onDelete}>
246
- <Trash2 size={14} aria-hidden />
247
- </RowActionButton>
248
- </RowActions>
249
- )}
273
+ <RowActions className="absolute top-1/2 right-0 -translate-y-1/2">
274
+ <RowActionButton aria-label={`Create page from ${name}`} onClick={onUse}>
275
+ <FilePlus size={14} aria-hidden />
276
+ </RowActionButton>
277
+ <RowActionButton aria-label={`Duplicate ${name}`} onClick={onDuplicate}>
278
+ <Copy size={14} aria-hidden />
279
+ </RowActionButton>
280
+ {!template.builtIn && (
281
+ <>
282
+ <RowActionButton aria-label={`Edit ${name}`} onClick={onEdit}>
283
+ <Pencil size={14} aria-hidden />
284
+ </RowActionButton>
285
+ <RowActionButton aria-label={`Delete ${name}`} danger onClick={onDelete}>
286
+ <Trash2 size={14} aria-hidden />
287
+ </RowActionButton>
288
+ </>
289
+ )}
290
+ </RowActions>
250
291
  </div>
251
292
  </div>
252
293
  </li>