@prenta/admin 0.98.1 → 1.0.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 (267) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/AdminRoot.d.ts.map +1 -1
  3. package/dist/AdminRoot.js +17 -28
  4. package/dist/AdminRoot.js.map +1 -1
  5. package/dist/__tests__/components/seo-score-bands.test.js +0 -3
  6. package/dist/__tests__/components/seo-score-bands.test.js.map +1 -1
  7. package/dist/__tests__/layout/sidebar-posts.test.js +6 -11
  8. package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -1
  9. package/dist/__tests__/layout/sidebar-submenu.render.test.js +3 -2
  10. package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -1
  11. package/dist/__tests__/lib/page-editor-service-api.test.js +26 -23
  12. package/dist/__tests__/lib/page-editor-service-api.test.js.map +1 -1
  13. package/dist/__tests__/views/page-section-editor.test.js +0 -1
  14. package/dist/__tests__/views/page-section-editor.test.js.map +1 -1
  15. package/dist/__tests__/views/page-tab.render.test.js +0 -1
  16. package/dist/__tests__/views/page-tab.render.test.js.map +1 -1
  17. package/dist/index.d.ts +1 -4
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/layout/Sidebar.d.ts +3 -4
  22. package/dist/layout/Sidebar.d.ts.map +1 -1
  23. package/dist/layout/Sidebar.js +7 -38
  24. package/dist/layout/Sidebar.js.map +1 -1
  25. package/dist/lib/page-editor-service.d.ts +0 -7
  26. package/dist/lib/page-editor-service.d.ts.map +1 -1
  27. package/dist/lib/page-editor-service.js +9 -9
  28. package/dist/lib/page-editor-service.js.map +1 -1
  29. package/dist/prenta-admin.css +1 -1
  30. package/dist/views/PageEditor.d.ts +2 -2
  31. package/dist/views/PageEditor.js +2 -2
  32. package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
  33. package/dist/views/page-editor/PageSectionEditor.js +9 -15
  34. package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
  35. package/package.json +4 -4
  36. package/src/AdminRoot.tsx +18 -68
  37. package/src/__tests__/components/seo-score-bands.test.ts +0 -3
  38. package/src/__tests__/layout/sidebar-posts.test.ts +6 -11
  39. package/src/__tests__/layout/sidebar-submenu.render.test.tsx +3 -2
  40. package/src/__tests__/lib/page-editor-service-api.test.ts +27 -25
  41. package/src/__tests__/views/page-section-editor.test.tsx +0 -1
  42. package/src/__tests__/views/page-tab.render.test.tsx +0 -1
  43. package/src/index.ts +1 -11
  44. package/src/layout/Sidebar.tsx +6 -41
  45. package/src/lib/page-editor-service.ts +9 -16
  46. package/src/views/PageEditor.tsx +2 -2
  47. package/src/views/page-editor/PageSectionEditor.tsx +8 -37
  48. package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts +0 -2
  49. package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts.map +0 -1
  50. package/dist/__tests__/views/page-builder-block-renderer.render.test.js +0 -48
  51. package/dist/__tests__/views/page-builder-block-renderer.render.test.js.map +0 -1
  52. package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts +0 -2
  53. package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts.map +0 -1
  54. package/dist/__tests__/views/page-builder-form-picker.render.test.js +0 -44
  55. package/dist/__tests__/views/page-builder-form-picker.render.test.js.map +0 -1
  56. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts +0 -2
  57. package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts.map +0 -1
  58. package/dist/__tests__/views/page-builder-sections-panel.render.test.js +0 -121
  59. package/dist/__tests__/views/page-builder-sections-panel.render.test.js.map +0 -1
  60. package/dist/__tests__/views/page-builder-seo-panel.render.test.d.ts +0 -2
  61. package/dist/__tests__/views/page-builder-seo-panel.render.test.d.ts.map +0 -1
  62. package/dist/__tests__/views/page-builder-seo-panel.render.test.js +0 -65
  63. package/dist/__tests__/views/page-builder-seo-panel.render.test.js.map +0 -1
  64. package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts +0 -2
  65. package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts.map +0 -1
  66. package/dist/__tests__/views/page-builder-template-seed.render.test.js +0 -48
  67. package/dist/__tests__/views/page-builder-template-seed.render.test.js.map +0 -1
  68. package/dist/__tests__/views/page-templates.render.test.d.ts +0 -2
  69. package/dist/__tests__/views/page-templates.render.test.d.ts.map +0 -1
  70. package/dist/__tests__/views/page-templates.render.test.js +0 -126
  71. package/dist/__tests__/views/page-templates.render.test.js.map +0 -1
  72. package/dist/hooks/useBuilderState.d.ts +0 -49
  73. package/dist/hooks/useBuilderState.d.ts.map +0 -1
  74. package/dist/hooks/useBuilderState.js +0 -238
  75. package/dist/hooks/useBuilderState.js.map +0 -1
  76. package/dist/views/page-builder/AIBlockAssist.d.ts +0 -9
  77. package/dist/views/page-builder/AIBlockAssist.d.ts.map +0 -1
  78. package/dist/views/page-builder/AIBlockAssist.js +0 -40
  79. package/dist/views/page-builder/AIBlockAssist.js.map +0 -1
  80. package/dist/views/page-builder/AIGenerateDialog.d.ts +0 -8
  81. package/dist/views/page-builder/AIGenerateDialog.d.ts.map +0 -1
  82. package/dist/views/page-builder/AIGenerateDialog.js +0 -173
  83. package/dist/views/page-builder/AIGenerateDialog.js.map +0 -1
  84. package/dist/views/page-builder/BlockEditor.d.ts +0 -11
  85. package/dist/views/page-builder/BlockEditor.d.ts.map +0 -1
  86. package/dist/views/page-builder/BlockEditor.js +0 -159
  87. package/dist/views/page-builder/BlockEditor.js.map +0 -1
  88. package/dist/views/page-builder/BlockPicker.d.ts +0 -7
  89. package/dist/views/page-builder/BlockPicker.d.ts.map +0 -1
  90. package/dist/views/page-builder/BlockPicker.js +0 -103
  91. package/dist/views/page-builder/BlockPicker.js.map +0 -1
  92. package/dist/views/page-builder/BottomBar.d.ts +0 -9
  93. package/dist/views/page-builder/BottomBar.d.ts.map +0 -1
  94. package/dist/views/page-builder/BottomBar.js +0 -13
  95. package/dist/views/page-builder/BottomBar.js.map +0 -1
  96. package/dist/views/page-builder/BuilderSectionsPanel.d.ts +0 -20
  97. package/dist/views/page-builder/BuilderSectionsPanel.d.ts.map +0 -1
  98. package/dist/views/page-builder/BuilderSectionsPanel.js +0 -105
  99. package/dist/views/page-builder/BuilderSectionsPanel.js.map +0 -1
  100. package/dist/views/page-builder/BuilderToolbar.d.ts +0 -21
  101. package/dist/views/page-builder/BuilderToolbar.d.ts.map +0 -1
  102. package/dist/views/page-builder/BuilderToolbar.js +0 -18
  103. package/dist/views/page-builder/BuilderToolbar.js.map +0 -1
  104. package/dist/views/page-builder/ContextPanel.d.ts +0 -20
  105. package/dist/views/page-builder/ContextPanel.d.ts.map +0 -1
  106. package/dist/views/page-builder/ContextPanel.js +0 -43
  107. package/dist/views/page-builder/ContextPanel.js.map +0 -1
  108. package/dist/views/page-builder/DesignScore.d.ts +0 -6
  109. package/dist/views/page-builder/DesignScore.d.ts.map +0 -1
  110. package/dist/views/page-builder/DesignScore.js +0 -93
  111. package/dist/views/page-builder/DesignScore.js.map +0 -1
  112. package/dist/views/page-builder/NodeSettings.d.ts +0 -12
  113. package/dist/views/page-builder/NodeSettings.d.ts.map +0 -1
  114. package/dist/views/page-builder/NodeSettings.js +0 -80
  115. package/dist/views/page-builder/NodeSettings.js.map +0 -1
  116. package/dist/views/page-builder/PageBuilder.d.ts +0 -10
  117. package/dist/views/page-builder/PageBuilder.d.ts.map +0 -1
  118. package/dist/views/page-builder/PageBuilder.js +0 -222
  119. package/dist/views/page-builder/PageBuilder.js.map +0 -1
  120. package/dist/views/page-builder/PageSettings.d.ts +0 -7
  121. package/dist/views/page-builder/PageSettings.d.ts.map +0 -1
  122. package/dist/views/page-builder/PageSettings.js +0 -27
  123. package/dist/views/page-builder/PageSettings.js.map +0 -1
  124. package/dist/views/page-builder/PageTemplates.d.ts +0 -5
  125. package/dist/views/page-builder/PageTemplates.d.ts.map +0 -1
  126. package/dist/views/page-builder/PageTemplates.js +0 -146
  127. package/dist/views/page-builder/PageTemplates.js.map +0 -1
  128. package/dist/views/page-builder/RelationshipField.d.ts +0 -15
  129. package/dist/views/page-builder/RelationshipField.d.ts.map +0 -1
  130. package/dist/views/page-builder/RelationshipField.js +0 -65
  131. package/dist/views/page-builder/RelationshipField.js.map +0 -1
  132. package/dist/views/page-builder/SEOPanel.d.ts +0 -9
  133. package/dist/views/page-builder/SEOPanel.d.ts.map +0 -1
  134. package/dist/views/page-builder/SEOPanel.js +0 -38
  135. package/dist/views/page-builder/SEOPanel.js.map +0 -1
  136. package/dist/views/page-builder/SavedSections.d.ts +0 -6
  137. package/dist/views/page-builder/SavedSections.d.ts.map +0 -1
  138. package/dist/views/page-builder/SavedSections.js +0 -141
  139. package/dist/views/page-builder/SavedSections.js.map +0 -1
  140. package/dist/views/page-builder/TemplatePicker.d.ts +0 -7
  141. package/dist/views/page-builder/TemplatePicker.d.ts.map +0 -1
  142. package/dist/views/page-builder/TemplatePicker.js +0 -68
  143. package/dist/views/page-builder/TemplatePicker.js.map +0 -1
  144. package/dist/views/page-builder/Widgets.d.ts +0 -13
  145. package/dist/views/page-builder/Widgets.d.ts.map +0 -1
  146. package/dist/views/page-builder/Widgets.js +0 -15
  147. package/dist/views/page-builder/Widgets.js.map +0 -1
  148. package/dist/views/page-builder/block-renderers/CTAPreview.d.ts +0 -3
  149. package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +0 -1
  150. package/dist/views/page-builder/block-renderers/CTAPreview.js +0 -19
  151. package/dist/views/page-builder/block-renderers/CTAPreview.js.map +0 -1
  152. package/dist/views/page-builder/block-renderers/CardsPreview.d.ts +0 -3
  153. package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +0 -1
  154. package/dist/views/page-builder/block-renderers/CardsPreview.js +0 -22
  155. package/dist/views/page-builder/block-renderers/CardsPreview.js.map +0 -1
  156. package/dist/views/page-builder/block-renderers/CodePreview.d.ts +0 -3
  157. package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +0 -1
  158. package/dist/views/page-builder/block-renderers/CodePreview.js +0 -12
  159. package/dist/views/page-builder/block-renderers/CodePreview.js.map +0 -1
  160. package/dist/views/page-builder/block-renderers/FAQPreview.d.ts +0 -3
  161. package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +0 -1
  162. package/dist/views/page-builder/block-renderers/FAQPreview.js +0 -27
  163. package/dist/views/page-builder/block-renderers/FAQPreview.js.map +0 -1
  164. package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts +0 -6
  165. package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +0 -1
  166. package/dist/views/page-builder/block-renderers/FallbackPreview.js +0 -7
  167. package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +0 -1
  168. package/dist/views/page-builder/block-renderers/FormPreview.d.ts +0 -9
  169. package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +0 -1
  170. package/dist/views/page-builder/block-renderers/FormPreview.js +0 -52
  171. package/dist/views/page-builder/block-renderers/FormPreview.js.map +0 -1
  172. package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts +0 -3
  173. package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +0 -1
  174. package/dist/views/page-builder/block-renderers/GalleryPreview.js +0 -19
  175. package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +0 -1
  176. package/dist/views/page-builder/block-renderers/HeroPreview.d.ts +0 -3
  177. package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +0 -1
  178. package/dist/views/page-builder/block-renderers/HeroPreview.js +0 -19
  179. package/dist/views/page-builder/block-renderers/HeroPreview.js.map +0 -1
  180. package/dist/views/page-builder/block-renderers/ImagePreview.d.ts +0 -3
  181. package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +0 -1
  182. package/dist/views/page-builder/block-renderers/ImagePreview.js +0 -17
  183. package/dist/views/page-builder/block-renderers/ImagePreview.js.map +0 -1
  184. package/dist/views/page-builder/block-renderers/TextPreview.d.ts +0 -3
  185. package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +0 -1
  186. package/dist/views/page-builder/block-renderers/TextPreview.js +0 -22
  187. package/dist/views/page-builder/block-renderers/TextPreview.js.map +0 -1
  188. package/dist/views/page-builder/block-renderers/VideoPreview.d.ts +0 -3
  189. package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +0 -1
  190. package/dist/views/page-builder/block-renderers/VideoPreview.js +0 -18
  191. package/dist/views/page-builder/block-renderers/VideoPreview.js.map +0 -1
  192. package/dist/views/page-builder/block-renderers/index.d.ts +0 -9
  193. package/dist/views/page-builder/block-renderers/index.d.ts.map +0 -1
  194. package/dist/views/page-builder/block-renderers/index.js +0 -25
  195. package/dist/views/page-builder/block-renderers/index.js.map +0 -1
  196. package/dist/views/page-builder/canvas/BlockRenderer.d.ts +0 -8
  197. package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +0 -1
  198. package/dist/views/page-builder/canvas/BlockRenderer.js +0 -20
  199. package/dist/views/page-builder/canvas/BlockRenderer.js.map +0 -1
  200. package/dist/views/page-builder/canvas/BuilderCanvas.d.ts +0 -10
  201. package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +0 -1
  202. package/dist/views/page-builder/canvas/BuilderCanvas.js +0 -26
  203. package/dist/views/page-builder/canvas/BuilderCanvas.js.map +0 -1
  204. package/dist/views/page-builder/canvas/ColumnRenderer.d.ts +0 -8
  205. package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +0 -1
  206. package/dist/views/page-builder/canvas/ColumnRenderer.js +0 -32
  207. package/dist/views/page-builder/canvas/ColumnRenderer.js.map +0 -1
  208. package/dist/views/page-builder/canvas/ContainerRenderer.d.ts +0 -8
  209. package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +0 -1
  210. package/dist/views/page-builder/canvas/ContainerRenderer.js +0 -29
  211. package/dist/views/page-builder/canvas/ContainerRenderer.js.map +0 -1
  212. package/dist/views/page-builder/canvas/RowRenderer.d.ts +0 -8
  213. package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +0 -1
  214. package/dist/views/page-builder/canvas/RowRenderer.js +0 -28
  215. package/dist/views/page-builder/canvas/RowRenderer.js.map +0 -1
  216. package/dist/views/page-builder/canvas/SectionRenderer.d.ts +0 -8
  217. package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +0 -1
  218. package/dist/views/page-builder/canvas/SectionRenderer.js +0 -50
  219. package/dist/views/page-builder/canvas/SectionRenderer.js.map +0 -1
  220. package/dist/views/page-builder/canvas/index.d.ts +0 -3
  221. package/dist/views/page-builder/canvas/index.d.ts.map +0 -1
  222. package/dist/views/page-builder/canvas/index.js +0 -2
  223. package/dist/views/page-builder/canvas/index.js.map +0 -1
  224. package/src/__tests__/views/page-builder-block-renderer.render.test.tsx +0 -66
  225. package/src/__tests__/views/page-builder-form-picker.render.test.tsx +0 -54
  226. package/src/__tests__/views/page-builder-sections-panel.render.test.tsx +0 -138
  227. package/src/__tests__/views/page-builder-seo-panel.render.test.tsx +0 -77
  228. package/src/__tests__/views/page-builder-template-seed.render.test.tsx +0 -59
  229. package/src/__tests__/views/page-templates.render.test.tsx +0 -182
  230. package/src/hooks/useBuilderState.ts +0 -361
  231. package/src/views/page-builder/AIBlockAssist.tsx +0 -64
  232. package/src/views/page-builder/AIGenerateDialog.tsx +0 -593
  233. package/src/views/page-builder/BlockEditor.tsx +0 -523
  234. package/src/views/page-builder/BlockPicker.tsx +0 -324
  235. package/src/views/page-builder/BottomBar.tsx +0 -65
  236. package/src/views/page-builder/BuilderSectionsPanel.tsx +0 -304
  237. package/src/views/page-builder/BuilderToolbar.tsx +0 -221
  238. package/src/views/page-builder/ContextPanel.tsx +0 -144
  239. package/src/views/page-builder/DesignScore.tsx +0 -251
  240. package/src/views/page-builder/NodeSettings.tsx +0 -500
  241. package/src/views/page-builder/PageBuilder.tsx +0 -405
  242. package/src/views/page-builder/PageSettings.tsx +0 -162
  243. package/src/views/page-builder/PageTemplates.tsx +0 -435
  244. package/src/views/page-builder/RelationshipField.tsx +0 -113
  245. package/src/views/page-builder/SEOPanel.tsx +0 -222
  246. package/src/views/page-builder/SavedSections.tsx +0 -480
  247. package/src/views/page-builder/TemplatePicker.tsx +0 -197
  248. package/src/views/page-builder/Widgets.tsx +0 -44
  249. package/src/views/page-builder/block-renderers/CTAPreview.tsx +0 -79
  250. package/src/views/page-builder/block-renderers/CardsPreview.tsx +0 -69
  251. package/src/views/page-builder/block-renderers/CodePreview.tsx +0 -46
  252. package/src/views/page-builder/block-renderers/FAQPreview.tsx +0 -87
  253. package/src/views/page-builder/block-renderers/FallbackPreview.tsx +0 -18
  254. package/src/views/page-builder/block-renderers/FormPreview.tsx +0 -117
  255. package/src/views/page-builder/block-renderers/GalleryPreview.tsx +0 -87
  256. package/src/views/page-builder/block-renderers/HeroPreview.tsx +0 -90
  257. package/src/views/page-builder/block-renderers/ImagePreview.tsx +0 -54
  258. package/src/views/page-builder/block-renderers/TextPreview.tsx +0 -81
  259. package/src/views/page-builder/block-renderers/VideoPreview.tsx +0 -73
  260. package/src/views/page-builder/block-renderers/index.ts +0 -34
  261. package/src/views/page-builder/canvas/BlockRenderer.tsx +0 -56
  262. package/src/views/page-builder/canvas/BuilderCanvas.tsx +0 -87
  263. package/src/views/page-builder/canvas/ColumnRenderer.tsx +0 -82
  264. package/src/views/page-builder/canvas/ContainerRenderer.tsx +0 -67
  265. package/src/views/page-builder/canvas/RowRenderer.tsx +0 -68
  266. package/src/views/page-builder/canvas/SectionRenderer.tsx +0 -93
  267. package/src/views/page-builder/canvas/index.ts +0 -2
@@ -1,50 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState } from 'react';
4
- import { ContainerRenderer } from './ContainerRenderer.js';
5
- import { RowRenderer } from './RowRenderer.js';
6
- export function SectionRenderer({ node, selectedNodeId, onSelectNode }) {
7
- const [hovered, setHovered] = useState(false);
8
- const isSelected = selectedNodeId === node.id;
9
- const handleClick = (e) => {
10
- e.stopPropagation();
11
- onSelectNode(node.id);
12
- };
13
- const backgroundStyle = {};
14
- if (node.settings.background) {
15
- backgroundStyle.backgroundColor = node.settings.background;
16
- }
17
- if (node.settings.backgroundImage) {
18
- backgroundStyle.backgroundImage = `url(${node.settings.backgroundImage})`;
19
- backgroundStyle.backgroundSize = 'cover';
20
- backgroundStyle.backgroundPosition = 'center';
21
- }
22
- if (node.settings.backgroundGradient) {
23
- backgroundStyle.backgroundImage = node.settings.backgroundGradient;
24
- }
25
- if (node.settings.paddingTop) {
26
- backgroundStyle.paddingTop = node.settings.paddingTop;
27
- }
28
- if (node.settings.paddingBottom) {
29
- backgroundStyle.paddingBottom = node.settings.paddingBottom;
30
- }
31
- if (node.settings.marginTop) {
32
- backgroundStyle.marginTop = node.settings.marginTop;
33
- }
34
- if (node.settings.marginBottom) {
35
- backgroundStyle.marginBottom = node.settings.marginBottom;
36
- }
37
- return (_jsxs("div", { "data-node-id": node.id, className: `relative w-full transition-shadow ${isSelected ? 'ring-primary ring-2 ring-offset-2' : hovered ? 'ring-primary/50 ring-1' : ''}`, style: backgroundStyle, onClick: handleClick, onMouseEnter: (e) => {
38
- e.stopPropagation();
39
- setHovered(true);
40
- }, onMouseLeave: () => setHovered(false), children: [(hovered || isSelected) && (_jsx("span", { className: "bg-primary text-primary-foreground absolute -top-2 -left-1 z-10 rounded px-1.5 py-0.5 text-xs font-medium", children: "Section" })), node.children.map((child) => {
41
- if (child.type === 'container') {
42
- return (_jsx(ContainerRenderer, { node: child, selectedNodeId: selectedNodeId, onSelectNode: onSelectNode }, child.id));
43
- }
44
- if (child.type === 'row') {
45
- return (_jsx(RowRenderer, { node: child, selectedNodeId: selectedNodeId, onSelectNode: onSelectNode }, child.id));
46
- }
47
- return null;
48
- })] }));
49
- }
50
- //# sourceMappingURL=SectionRenderer.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SectionRenderer.js","sourceRoot":"","sources":["../../../../src/views/page-builder/canvas/SectionRenderer.tsx"],"names":[],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- export { BuilderCanvas } from './BuilderCanvas.js';
2
- export type { BuilderCanvasProps } from './BuilderCanvas.js';
3
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/views/page-builder/canvas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAClD,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA"}
@@ -1,2 +0,0 @@
1
- export { BuilderCanvas } from './BuilderCanvas.js';
2
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/views/page-builder/canvas/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA"}
@@ -1,66 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
3
- import { fireEvent, render, screen, waitFor } from '@testing-library/react'
4
- import type { BlockNode } from '@prenta/core'
5
-
6
- const cmsApi = vi.fn()
7
- vi.mock('../../lib/api.js', () => ({ cmsApi: (...args: unknown[]) => cmsApi(...args) }))
8
-
9
- const { BlockRenderer } = await import('../../views/page-builder/canvas/BlockRenderer.js')
10
-
11
- function formNode(over: Partial<BlockNode> = {}): BlockNode {
12
- return {
13
- id: 'node-1',
14
- type: 'block',
15
- settings: { blockType: 'form', variant: 'contact' },
16
- data: { formId: 'f1' },
17
- ...over,
18
- } as BlockNode
19
- }
20
-
21
- beforeEach(() => {
22
- cmsApi.mockReset()
23
- cmsApi.mockResolvedValue({
24
- data: {
25
- name: 'Contact Us',
26
- fields: [
27
- { key: 'name', label: 'Full Name', type: 'text' },
28
- { key: 'message', label: 'Message', type: 'textarea' },
29
- ],
30
- },
31
- status: 200,
32
- })
33
- })
34
-
35
- describe('canvas BlockRenderer', () => {
36
- it('renders the live form preview with the linked form name and fields', async () => {
37
- render(<BlockRenderer node={formNode()} selectedNodeId={null} onSelectNode={vi.fn()} />)
38
- await waitFor(() => expect(screen.getByText('Contact Us')).toBeTruthy())
39
- expect(cmsApi).toHaveBeenCalledWith('/forms/f1')
40
- expect(screen.getByText('Full Name')).toBeTruthy()
41
- })
42
-
43
- it('selects the node on click', () => {
44
- const onSelectNode = vi.fn()
45
- const { container } = render(
46
- <BlockRenderer
47
- node={formNode({ data: {} })}
48
- selectedNodeId={null}
49
- onSelectNode={onSelectNode}
50
- />,
51
- )
52
- fireEvent.click(container.querySelector('[data-node-id="node-1"]')!)
53
- expect(onSelectNode).toHaveBeenCalledWith('node-1')
54
- })
55
-
56
- it('falls back to a placeholder for unknown block types', () => {
57
- render(
58
- <BlockRenderer
59
- node={formNode({ settings: { blockType: 'widget' }, data: {} })}
60
- selectedNodeId={null}
61
- onSelectNode={vi.fn()}
62
- />,
63
- )
64
- expect(screen.getByText('No preview available')).toBeTruthy()
65
- })
66
- })
@@ -1,54 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
3
- import { fireEvent, render, screen, waitFor } from '@testing-library/react'
4
- import type { FieldDefinition } from '@prenta/core'
5
-
6
- const cmsApi = vi.fn()
7
- vi.mock('../../lib/api.js', () => ({ cmsApi: (...args: unknown[]) => cmsApi(...args) }))
8
-
9
- const { RelationshipField } = await import('../../views/page-builder/RelationshipField.js')
10
-
11
- const formField: FieldDefinition = {
12
- type: 'relationship',
13
- label: 'Form',
14
- relationTo: 'forms',
15
- } as FieldDefinition
16
-
17
- beforeEach(() => {
18
- cmsApi.mockReset()
19
- })
20
-
21
- describe('RelationshipField (form block picker)', () => {
22
- it('loads forms and lets the editor pick one by id', async () => {
23
- cmsApi.mockResolvedValue({
24
- data: [
25
- { id: 'f1', name: 'Contact Us', slug: 'contact-us' },
26
- { id: 'f2', name: 'Event RSVP', slug: 'event-rsvp' },
27
- ],
28
- status: 200,
29
- })
30
- const onChange = vi.fn()
31
- render(<RelationshipField definition={formField} value="" onChange={onChange} label="Form" />)
32
-
33
- await waitFor(() => expect(screen.getByText('Contact Us')).toBeTruthy())
34
- expect(cmsApi).toHaveBeenCalledWith('/forms')
35
- expect(screen.getByText('Event RSVP')).toBeTruthy()
36
-
37
- fireEvent.change(screen.getByLabelText('Form'), { target: { value: 'f2' } })
38
- expect(onChange).toHaveBeenCalledWith('f2')
39
- })
40
-
41
- it('shows an empty hint when the collection has no items', async () => {
42
- cmsApi.mockResolvedValue({ data: [], status: 200 })
43
- render(<RelationshipField definition={formField} value="" onChange={vi.fn()} label="Form" />)
44
- await waitFor(() => expect(screen.getByText('No forms found.')).toBeTruthy())
45
- })
46
-
47
- it('degrades to a plain id input when the collection cannot be loaded', async () => {
48
- cmsApi.mockResolvedValue({ error: 'boom', status: 500 })
49
- render(<RelationshipField definition={formField} value="f9" onChange={vi.fn()} label="Form" />)
50
- await waitFor(() =>
51
- expect((screen.getByPlaceholderText('Enter forms id…') as HTMLInputElement).value).toBe('f9'),
52
- )
53
- })
54
- })
@@ -1,138 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
3
- import { fireEvent, render, screen } from '@testing-library/react'
4
- import type { PageNode } from '@prenta/core'
5
-
6
- const { BuilderSectionsPanel } = await import('../../views/page-builder/BuilderSectionsPanel.js')
7
-
8
- /** Minimal builder tree: a hero section plus an empty, hidden section. */
9
- function makeTree(): PageNode {
10
- return {
11
- id: 'page1',
12
- type: 'page',
13
- children: [
14
- {
15
- id: 'sec1',
16
- type: 'section',
17
- settings: {},
18
- children: [
19
- {
20
- id: 'cont1',
21
- type: 'container',
22
- settings: {},
23
- children: [
24
- {
25
- id: 'row1',
26
- type: 'row',
27
- settings: {},
28
- children: [
29
- {
30
- id: 'col1',
31
- type: 'column',
32
- settings: { width: 12 },
33
- children: [
34
- {
35
- id: 'blk1',
36
- type: 'block',
37
- settings: { blockType: 'hero', variant: 'centered' },
38
- data: {},
39
- },
40
- ],
41
- },
42
- ],
43
- },
44
- ],
45
- },
46
- ],
47
- },
48
- { id: 'sec2', type: 'section', settings: { visibility: 'hidden' }, children: [] },
49
- ],
50
- }
51
- }
52
-
53
- function renderPanel(over: Partial<Parameters<typeof BuilderSectionsPanel>[0]> = {}) {
54
- const props = {
55
- tree: makeTree(),
56
- selectedNodeId: null,
57
- collapsed: false,
58
- onToggleCollapsed: vi.fn(),
59
- onSelect: vi.fn(),
60
- onReorder: vi.fn(),
61
- onAddSection: vi.fn(),
62
- ...over,
63
- }
64
- render(<BuilderSectionsPanel {...props} />)
65
- return props
66
- }
67
-
68
- beforeEach(() => {
69
- vi.clearAllMocks()
70
- })
71
-
72
- describe('BuilderSectionsPanel', () => {
73
- it('lists the page sections in order with derived names and block counts', () => {
74
- renderPanel()
75
-
76
- // First section is named after its first block's catalog label.
77
- expect(screen.getByText('Hero')).toBeTruthy()
78
- expect(screen.getByText('1 block')).toBeTruthy()
79
- // Empty section falls back to a positional name.
80
- expect(screen.getByText('Section 2')).toBeTruthy()
81
- expect(screen.getByText('Empty section')).toBeTruthy()
82
- // Count chip in the header.
83
- expect(screen.getByText('2')).toBeTruthy()
84
- // Panel is an accessible nav landmark.
85
- expect(screen.getByRole('navigation', { name: 'Page sections' })).toBeTruthy()
86
- })
87
-
88
- it('fires the selection callback when a section row is clicked', () => {
89
- const props = renderPanel()
90
-
91
- // `/^Hero/` excludes the drag handle ("Drag to reorder Hero").
92
- fireEvent.click(screen.getByRole('button', { name: /^Hero/ }))
93
- expect(props.onSelect).toHaveBeenCalledWith('sec1')
94
- })
95
-
96
- it('marks the selected section with aria-current', () => {
97
- renderPanel({ selectedNodeId: 'sec1' })
98
-
99
- const row = screen.getByRole('button', { name: /^Hero/ })
100
- expect(row.getAttribute('aria-current')).toBe('true')
101
- })
102
-
103
- it('exposes keyboard-accessible drag handles per section', () => {
104
- renderPanel()
105
- expect(screen.getByRole('button', { name: 'Drag to reorder Hero' })).toBeTruthy()
106
- expect(screen.getByRole('button', { name: 'Drag to reorder Section 2' })).toBeTruthy()
107
- })
108
-
109
- it('fires the add-section callback from the bottom affordance', () => {
110
- const props = renderPanel()
111
-
112
- fireEvent.click(screen.getByRole('button', { name: 'Add Section' }))
113
- expect(props.onAddSection).toHaveBeenCalledTimes(1)
114
- })
115
-
116
- it('collapses to a rail with an expand toggle', () => {
117
- const props = renderPanel({ collapsed: true })
118
-
119
- // Section rows are gone; only the expand affordance remains.
120
- expect(screen.queryByText('Hero')).toBeNull()
121
- const toggle = screen.getByRole('button', { name: 'Expand sections panel' })
122
- expect(toggle.getAttribute('aria-expanded')).toBe('false')
123
-
124
- fireEvent.click(toggle)
125
- expect(props.onToggleCollapsed).toHaveBeenCalledTimes(1)
126
- })
127
-
128
- it('shows a collapse toggle with aria-expanded when open', () => {
129
- renderPanel()
130
- const toggle = screen.getByRole('button', { name: 'Collapse sections panel' })
131
- expect(toggle.getAttribute('aria-expanded')).toBe('true')
132
- })
133
-
134
- it('shows an empty message when the page has no sections', () => {
135
- renderPanel({ tree: { id: 'page1', type: 'page', children: [] } })
136
- expect(screen.getByText(/No sections yet/)).toBeTruthy()
137
- })
138
- })
@@ -1,77 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { describe, expect, it, vi } from 'vitest'
3
- import { render, screen } from '@testing-library/react'
4
- import type { PageNode } from '@prenta/core'
5
- import { BuilderSEOPanel } from '../../views/page-builder/SEOPanel.js'
6
-
7
- const tree = {
8
- id: 'page-1',
9
- type: 'page',
10
- children: [
11
- {
12
- id: 'sec-1',
13
- type: 'section',
14
- settings: {},
15
- children: [
16
- {
17
- id: 'row-1',
18
- type: 'row',
19
- settings: {},
20
- children: [
21
- {
22
- id: 'col-1',
23
- type: 'column',
24
- settings: { width: 12 },
25
- children: [
26
- {
27
- id: 'blk-hero',
28
- type: 'block',
29
- settings: { blockType: 'hero' },
30
- data: {
31
- title: 'Welcome to Our Service',
32
- image: { url: '/hero.webp', alt: 'Hero banner' },
33
- },
34
- },
35
- ],
36
- },
37
- ],
38
- },
39
- ],
40
- },
41
- ],
42
- } as PageNode
43
-
44
- const pageSettings = {
45
- title: 'Home',
46
- slug: 'home',
47
- metaTitle: 'Welcome to Our Service',
48
- metaDescription: 'The public description.',
49
- }
50
-
51
- describe('BuilderSEOPanel', () => {
52
- it('grades the live tree with the shared page audit panel and keeps meta fields', () => {
53
- render(
54
- <BuilderSEOPanel tree={tree} pageSettings={pageSettings} onPageSettingsChange={vi.fn()} />,
55
- )
56
-
57
- expect(screen.getByRole('img', { name: /SEO score:/i })).toBeTruthy()
58
- expect(screen.getByRole('img', { name: /GEO score:/i })).toBeTruthy()
59
- expect(screen.getByText('SEO checks')).toBeTruthy()
60
- expect(screen.getByText('GEO checks')).toBeTruthy()
61
- expect(screen.getByLabelText('Meta Title')).toBeTruthy()
62
- expect(screen.queryByText('Readability')).toBeNull()
63
- expect(screen.queryByText('Block Hints')).toBeNull()
64
- })
65
-
66
- it('keeps the empty-tree copy when the canvas has no children', () => {
67
- render(
68
- <BuilderSEOPanel
69
- tree={{ id: 'page-1', type: 'page', children: [] } as PageNode}
70
- pageSettings={pageSettings}
71
- onPageSettingsChange={vi.fn()}
72
- />,
73
- )
74
- expect(screen.getByText('Add content to your page to see SEO analysis')).toBeTruthy()
75
- expect(screen.queryByRole('img', { name: /SEO score:/i })).toBeNull()
76
- })
77
- })
@@ -1,59 +0,0 @@
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
- })
@@ -1,182 +0,0 @@
1
- // @vitest-environment happy-dom
2
- import { beforeEach, describe, expect, it, vi } from 'vitest'
3
- import { fireEvent, 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', () => ({ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn() } }))
8
-
9
- const { PageTemplates } = await import('../../views/page-builder/PageTemplates.js')
10
-
11
- function section(id: string) {
12
- return { id, type: 'section', settings: {}, children: [] }
13
- }
14
-
15
- const TEMPLATES = [
16
- {
17
- id: 'builtin-landing',
18
- name: 'Landing Page',
19
- description: 'Hero, features cards, CTA, testimonials, and final CTA',
20
- category: 'landing',
21
- builtIn: true,
22
- updatedAt: '2026-05-29T00:00:00.000Z',
23
- thumbnail: null,
24
- tree: { id: 'p1', type: 'page', children: [section('a'), section('b'), section('c')] },
25
- },
26
- {
27
- id: 'custom-1',
28
- name: 'Team Member',
29
- description: 'Avatar hero, full bio, and contact links',
30
- category: 'content',
31
- builtIn: false,
32
- updatedAt: '2026-06-10T00:00:00.000Z',
33
- thumbnail: null,
34
- tree: { id: 'p2', type: 'page', children: [section('a')] },
35
- },
36
- ]
37
-
38
- beforeEach(() => {
39
- cmsApi.mockReset()
40
- cmsApi.mockImplementation(async (endpoint: string, options?: RequestInit) => {
41
- if (endpoint === '/page-templates' && !options?.method) {
42
- return { data: TEMPLATES, status: 200 }
43
- }
44
- return { data: {}, status: 200 }
45
- })
46
- })
47
-
48
- describe('PageTemplates card grid', () => {
49
- it('renders a card per template with name, category, and section-count meta', async () => {
50
- render(<PageTemplates onNavigate={() => {}} />)
51
-
52
- expect(await screen.findByText('Landing Page')).toBeTruthy()
53
- expect(screen.getByText('Team Member')).toBeTruthy()
54
- // Section counts derived from each template's tree.
55
- expect(screen.getByText(/3 sections/)).toBeTruthy()
56
- expect(screen.getByText(/1 section ·/)).toBeTruthy()
57
- // Category labels from the design's palette.
58
- expect(screen.getByText('Landing Page')).toBeTruthy()
59
- expect(screen.getByText('Built-in')).toBeTruthy()
60
- })
61
-
62
- it('hides edit/delete actions on built-in templates (server rejects them)', async () => {
63
- render(<PageTemplates onNavigate={() => {}} />)
64
- await screen.findByText('Landing Page')
65
-
66
- expect(screen.queryByRole('button', { name: 'Edit Landing Page' })).toBeNull()
67
- expect(screen.queryByRole('button', { name: 'Delete Landing Page' })).toBeNull()
68
- expect(screen.getByRole('button', { name: 'Edit Team Member' })).toBeTruthy()
69
- expect(screen.getByRole('button', { name: 'Delete Team Member' })).toBeTruthy()
70
- })
71
-
72
- it('deletes a custom template only after the confirm dialog is accepted', async () => {
73
- render(<PageTemplates onNavigate={() => {}} />)
74
- await screen.findByText('Team Member')
75
-
76
- fireEvent.click(screen.getByRole('button', { name: 'Delete Team Member' }))
77
-
78
- // Confirm dialog is up; nothing deleted yet.
79
- expect(await screen.findByText('Delete template?')).toBeTruthy()
80
- expect(cmsApi).not.toHaveBeenCalledWith(
81
- '/page-templates/custom-1',
82
- expect.objectContaining({ method: 'DELETE' }),
83
- )
84
-
85
- fireEvent.click(screen.getByRole('button', { name: 'Delete' }))
86
-
87
- await waitFor(() =>
88
- expect(cmsApi).toHaveBeenCalledWith(
89
- '/page-templates/custom-1',
90
- expect.objectContaining({ method: 'DELETE' }),
91
- ),
92
- )
93
- })
94
-
95
- it('opens the edit modal seeded with the template and saves via PUT', async () => {
96
- render(<PageTemplates onNavigate={() => {}} />)
97
- await screen.findByText('Team Member')
98
-
99
- fireEvent.click(screen.getByRole('button', { name: 'Edit Team Member' }))
100
-
101
- const nameInput = (await screen.findByPlaceholderText('Template name')) as HTMLInputElement
102
- expect(nameInput.value).toBe('Team Member')
103
-
104
- fireEvent.change(nameInput, { target: { value: 'Team Bio' } })
105
- fireEvent.click(screen.getByRole('button', { name: 'Save changes' }))
106
-
107
- await waitFor(() =>
108
- expect(cmsApi).toHaveBeenCalledWith(
109
- '/page-templates/custom-1',
110
- expect.objectContaining({ method: 'PUT' }),
111
- ),
112
- )
113
- const putCall = cmsApi.mock.calls.find(
114
- (c) => (c[1] as RequestInit | undefined)?.method === 'PUT',
115
- )!
116
- expect(JSON.parse((putCall[1] as RequestInit).body as string)).toMatchObject({
117
- name: 'Team Bio',
118
- category: 'content',
119
- })
120
- })
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
-
162
- it('filters cards by search text', async () => {
163
- render(<PageTemplates onNavigate={() => {}} />)
164
- await screen.findByText('Team Member')
165
-
166
- fireEvent.change(screen.getByPlaceholderText('Search templates…'), {
167
- target: { value: 'landing' },
168
- })
169
-
170
- await waitFor(() => {
171
- expect(screen.queryByText('Team Member')).toBeNull()
172
- expect(screen.getByText('Landing Page')).toBeTruthy()
173
- })
174
- })
175
-
176
- it('shows the empty state when no templates exist', async () => {
177
- cmsApi.mockResolvedValue({ data: [], status: 200 })
178
- render(<PageTemplates onNavigate={() => {}} />)
179
-
180
- expect(await screen.findByText('No page templates yet')).toBeTruthy()
181
- })
182
- })