@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,197 +0,0 @@
1
- 'use client'
2
-
3
- import { useState, useEffect, useCallback } from 'react'
4
- import {
5
- X,
6
- Loader2,
7
- AlertTriangle,
8
- Layout,
9
- FileText,
10
- MapPin,
11
- Phone,
12
- BookOpen,
13
- File,
14
- } from 'lucide-react'
15
- import type { LucideIcon } from 'lucide-react'
16
- import { cmsApi } from '../../lib/api.js'
17
- import { createEmptyPage } from '@prenta/core/page-builder'
18
- import type { PageNode } from '@prenta/core'
19
-
20
- export interface TemplatePickerProps {
21
- onSelect: (templateTree: PageNode) => void
22
- onClose: () => void
23
- }
24
-
25
- interface PageTemplate {
26
- id: string
27
- name: string
28
- description: string
29
- category: string
30
- tree: PageNode
31
- }
32
-
33
- const categoryIcons: Record<string, LucideIcon> = {
34
- landing: Layout,
35
- blog: FileText,
36
- contact: MapPin,
37
- portfolio: BookOpen,
38
- mobile: Phone,
39
- }
40
-
41
- const categoryColors: Record<string, string> = {
42
- landing: 'bg-primary/10 text-primary',
43
- blog: 'bg-accent text-foreground',
44
- contact: 'bg-primary/10 text-primary',
45
- portfolio: 'bg-accent text-foreground',
46
- mobile: 'bg-primary/10 text-primary',
47
- }
48
-
49
- function resolveIcon(category: string): LucideIcon {
50
- return categoryIcons[category] ?? File
51
- }
52
-
53
- function resolveBadgeColor(category: string): string {
54
- return categoryColors[category] ?? 'bg-muted text-muted-foreground'
55
- }
56
-
57
- export function TemplatePicker({ onSelect, onClose }: TemplatePickerProps) {
58
- const [templates, setTemplates] = useState<PageTemplate[]>([])
59
- const [loading, setLoading] = useState(true)
60
- const [error, setError] = useState<string | null>(null)
61
-
62
- const fetchTemplates = useCallback(async () => {
63
- setLoading(true)
64
- setError(null)
65
- const res = await cmsApi<PageTemplate[]>('/page-templates')
66
- if (res.error) {
67
- setError(res.error)
68
- } else {
69
- setTemplates(res.data ?? [])
70
- }
71
- setLoading(false)
72
- }, [])
73
-
74
- useEffect(() => {
75
- fetchTemplates()
76
- }, [fetchTemplates])
77
-
78
- useEffect(() => {
79
- function handleKeyDown(e: KeyboardEvent) {
80
- if (e.key === 'Escape') onClose()
81
- }
82
- document.addEventListener('keydown', handleKeyDown)
83
- return () => document.removeEventListener('keydown', handleKeyDown)
84
- }, [onClose])
85
-
86
- function handleBlankPage() {
87
- onSelect(createEmptyPage())
88
- onClose()
89
- }
90
-
91
- function handleTemplateSelect(template: PageTemplate) {
92
- onSelect(template.tree)
93
- onClose()
94
- }
95
-
96
- return (
97
- <div
98
- className="fixed inset-0 z-50 flex items-center justify-center"
99
- role="dialog"
100
- aria-modal="true"
101
- aria-label="Choose a template"
102
- >
103
- <div
104
- className="motion-safe:animate-in motion-safe:fade-in-0 absolute inset-0 bg-black/50"
105
- onClick={onClose}
106
- aria-hidden="true"
107
- />
108
-
109
- <div className="bg-card border-border relative z-10 flex max-h-[80vh] w-full max-w-2xl flex-col overflow-hidden rounded-xl border shadow-2xl">
110
- <div className="flex items-center justify-between px-5 pt-5 pb-3">
111
- <h2 className="text-foreground text-lg font-medium">Choose a Template</h2>
112
- <button
113
- type="button"
114
- onClick={onClose}
115
- className="text-muted-foreground hover:text-foreground hover:bg-accent rounded-md p-1.5 transition-colors"
116
- aria-label="Close"
117
- >
118
- <X size={18} />
119
- </button>
120
- </div>
121
-
122
- <div className="flex-1 overflow-y-auto px-5 pb-5">
123
- {loading && (
124
- <div className="flex items-center justify-center py-16">
125
- <Loader2 className="text-muted-foreground h-6 w-6 animate-spin" />
126
- </div>
127
- )}
128
-
129
- {error && !loading && (
130
- <div className="flex flex-col items-center justify-center py-12 text-center">
131
- <AlertTriangle size={24} className="text-destructive mb-2" />
132
- <p className="text-muted-foreground mb-3 text-sm">{error}</p>
133
- <button
134
- type="button"
135
- onClick={fetchTemplates}
136
- className="text-foreground border-border hover:bg-accent rounded-md border px-3 py-1.5 text-sm font-medium transition-colors"
137
- >
138
- Retry
139
- </button>
140
- </div>
141
- )}
142
-
143
- {!loading && !error && (
144
- <div
145
- className="grid grid-cols-2 gap-3 md:grid-cols-3"
146
- role="list"
147
- aria-label="Available templates"
148
- >
149
- <button
150
- type="button"
151
- role="listitem"
152
- onClick={handleBlankPage}
153
- className="border-border hover:border-primary bg-card group focus:ring-ring flex cursor-pointer flex-col items-center gap-2 rounded-lg border p-4 text-left transition-colors focus:ring-2 focus:outline-none"
154
- >
155
- <div className="bg-muted text-muted-foreground group-hover:bg-primary/10 group-hover:text-primary flex h-10 w-10 items-center justify-center rounded-md transition-colors">
156
- <File size={20} />
157
- </div>
158
- <p className="text-foreground text-center text-sm font-medium">Blank Page</p>
159
- <p className="text-muted-foreground text-center text-xs">Start from scratch</p>
160
- </button>
161
-
162
- {templates.map((template) => {
163
- const Icon = resolveIcon(template.category)
164
- const badgeColor = resolveBadgeColor(template.category)
165
-
166
- return (
167
- <button
168
- key={template.id}
169
- type="button"
170
- role="listitem"
171
- onClick={() => handleTemplateSelect(template)}
172
- className="border-border hover:border-primary bg-card group focus:ring-ring flex cursor-pointer flex-col items-center gap-2 rounded-lg border p-4 text-left transition-colors focus:ring-2 focus:outline-none"
173
- >
174
- <div className="bg-accent text-foreground group-hover:bg-primary/10 group-hover:text-primary flex h-10 w-10 items-center justify-center rounded-md transition-colors">
175
- <Icon size={20} />
176
- </div>
177
- <p className="text-foreground w-full truncate text-center text-sm font-medium">
178
- {template.name}
179
- </p>
180
- {template.description && (
181
- <p className="text-muted-foreground line-clamp-2 text-center text-xs">
182
- {template.description}
183
- </p>
184
- )}
185
- <span className={`inline-block rounded-full px-2 py-0.5 text-xs ${badgeColor}`}>
186
- {template.category}
187
- </span>
188
- </button>
189
- )
190
- })}
191
- </div>
192
- )}
193
- </div>
194
- </div>
195
- </div>
196
- )
197
- }
@@ -1,44 +0,0 @@
1
- 'use client'
2
-
3
- import { LayoutGrid } from 'lucide-react'
4
-
5
- export interface WidgetsProps {
6
- onNavigate?: (path: string) => void
7
- }
8
-
9
- /**
10
- * /widgets — placeholder for the embeddable Widgets library.
11
- *
12
- * Widgets are reusable, embeddable components (forms, CTAs, banners)
13
- * that can be dropped into pages and saved sections. The management UI
14
- * ships in a later release; this view establishes the route and nav
15
- * entry with an honest empty state rather than a dead link.
16
- */
17
- export function Widgets({ onNavigate }: WidgetsProps) {
18
- return (
19
- <div className="p-4 pr-8">
20
- <div className="mb-4">
21
- <h1 className="text-foreground mb-1">Widgets</h1>
22
- <p className="text-muted-foreground text-sm">
23
- Reusable, embeddable components for pages and saved sections.
24
- </p>
25
- </div>
26
-
27
- <div className="border-border bg-card rounded-lg border p-8 text-center">
28
- <LayoutGrid className="text-muted-foreground mx-auto mb-3 h-8 w-8" aria-hidden />
29
- <h2 className="text-foreground mb-1 text-lg font-medium">Widgets are coming soon</h2>
30
- <p className="text-muted-foreground mx-auto mb-4 max-w-md text-sm">
31
- The widget library lets you build embeddable components once and reuse them across pages.
32
- In the meantime, use Saved Sections for reusable content blocks.
33
- </p>
34
- <button
35
- type="button"
36
- onClick={() => onNavigate?.('/saved-sections')}
37
- className="bg-primary text-primary-foreground rounded-md px-4 py-2 text-sm font-medium hover:opacity-90"
38
- >
39
- View Saved Sections
40
- </button>
41
- </div>
42
- </div>
43
- )
44
- }
@@ -1,79 +0,0 @@
1
- 'use client'
2
-
3
- import type { BlockPreviewProps } from './index.js'
4
-
5
- export function CTAPreview({ data, variant = 'banner' }: BlockPreviewProps) {
6
- const heading = (data.heading as string) || ''
7
- const body = (data.body as string) || ''
8
- const buttonText = (data.buttonText as string) || ''
9
-
10
- const button = (
11
- <span className="bg-primary text-primary-foreground inline-block rounded-md px-4 py-2 text-sm">
12
- {buttonText || 'Click Here'}
13
- </span>
14
- )
15
-
16
- if (variant === 'inline') {
17
- return (
18
- <div className="border-border flex items-center justify-between gap-4 rounded-md border p-4">
19
- <div className="flex-1">
20
- <p className="text-foreground text-sm font-medium">
21
- {heading || <span className="text-muted-foreground">CTA Heading</span>}
22
- </p>
23
- {body && <p className="text-muted-foreground mt-1 text-xs">{body}</p>}
24
- </div>
25
- {button}
26
- </div>
27
- )
28
- }
29
-
30
- if (variant === 'floating') {
31
- return (
32
- <div className="border-border bg-card mx-auto max-w-md rounded-lg border p-6 shadow-md">
33
- <div className="text-center">
34
- <h3 className="text-foreground text-lg font-medium">
35
- {heading || <span className="text-muted-foreground">CTA Heading</span>}
36
- </h3>
37
- {(body || !heading) && (
38
- <p className="text-muted-foreground mt-2 text-sm">
39
- {body || 'Supporting text for the call to action'}
40
- </p>
41
- )}
42
- <div className="mt-4">{button}</div>
43
- </div>
44
- </div>
45
- )
46
- }
47
-
48
- if (variant === 'split') {
49
- return (
50
- <div className="border-border flex items-center gap-6 rounded-md border p-6">
51
- <div className="flex-1">
52
- <h3 className="text-foreground text-lg font-medium">
53
- {heading || <span className="text-muted-foreground">CTA Heading</span>}
54
- </h3>
55
- {(body || !heading) && (
56
- <p className="text-muted-foreground mt-2 text-sm">
57
- {body || 'Supporting text for the call to action'}
58
- </p>
59
- )}
60
- </div>
61
- <div className="shrink-0">{button}</div>
62
- </div>
63
- )
64
- }
65
-
66
- return (
67
- <div className="border-border bg-accent rounded-md border p-6 text-center">
68
- <h3 className="text-foreground text-lg font-medium">
69
- {heading || <span className="text-muted-foreground">CTA Heading</span>}
70
- </h3>
71
- {(body || !heading) && (
72
- <p className="text-muted-foreground mt-2 text-sm">
73
- {body || 'Supporting text for the call to action'}
74
- </p>
75
- )}
76
- <div className="mt-4">{button}</div>
77
- </div>
78
- )
79
- }
@@ -1,69 +0,0 @@
1
- 'use client'
2
-
3
- import { Image } from 'lucide-react'
4
- import type { BlockPreviewProps } from './index.js'
5
-
6
- interface CardItem {
7
- title?: string
8
- description?: string
9
- image?: string
10
- link?: string
11
- icon?: string
12
- }
13
-
14
- export function CardsPreview({ data, variant = 'grid-3' }: BlockPreviewProps) {
15
- const items = (data.items as CardItem[]) || []
16
-
17
- const placeholderItems: CardItem[] =
18
- items.length > 0
19
- ? items
20
- : [
21
- { title: 'Card Title', description: 'Card description text' },
22
- { title: 'Card Title', description: 'Card description text' },
23
- { title: 'Card Title', description: 'Card description text' },
24
- ]
25
-
26
- const gridClass =
27
- variant === 'grid-2'
28
- ? 'grid grid-cols-2 gap-3'
29
- : variant === 'grid-4'
30
- ? 'grid grid-cols-4 gap-3'
31
- : variant === 'horizontal'
32
- ? 'flex gap-3 overflow-hidden'
33
- : 'grid grid-cols-3 gap-3'
34
-
35
- return (
36
- <div className="border-border rounded-md border p-4">
37
- <div className={gridClass}>
38
- {placeholderItems.map((item, i) => (
39
- <div
40
- key={i}
41
- className={`border-border bg-card flex flex-col overflow-hidden rounded-md border ${variant === 'horizontal' ? 'min-w-40' : ''}`}
42
- >
43
- <div className="bg-muted flex h-20 items-center justify-center">
44
- {item.image ? (
45
- <img
46
- src={item.image}
47
- alt={item.title || ''}
48
- className="h-full w-full object-cover"
49
- />
50
- ) : (
51
- <Image size={20} className="text-muted-foreground" />
52
- )}
53
- </div>
54
- <div className="p-3">
55
- <p className="text-foreground text-sm font-medium">
56
- {item.title || <span className="text-muted-foreground">Card Title</span>}
57
- </p>
58
- {item.description && (
59
- <p className="text-muted-foreground mt-1 line-clamp-2 text-xs">
60
- {item.description}
61
- </p>
62
- )}
63
- </div>
64
- </div>
65
- ))}
66
- </div>
67
- </div>
68
- )
69
- }
@@ -1,46 +0,0 @@
1
- 'use client'
2
-
3
- import { Code, Globe, Terminal } from 'lucide-react'
4
- import type { BlockPreviewProps } from './index.js'
5
-
6
- export function CodePreview({ data, variant = 'embed' }: BlockPreviewProps) {
7
- const content = (data.content as string) || ''
8
- const language = (data.language as string) || ''
9
- const sandboxed = data.sandboxed as boolean | undefined
10
-
11
- const truncated = content.length > 200 ? content.slice(0, 200) + '…' : content
12
-
13
- const icon =
14
- variant === 'html' ? (
15
- <Globe size={14} className="text-muted-foreground" />
16
- ) : variant === 'script' ? (
17
- <Terminal size={14} className="text-muted-foreground" />
18
- ) : (
19
- <Code size={14} className="text-muted-foreground" />
20
- )
21
-
22
- return (
23
- <div className="border-border overflow-hidden rounded-md border">
24
- <div className="border-border bg-card flex items-center gap-2 border-b px-3 py-2">
25
- {icon}
26
- <span className="text-muted-foreground text-xs">{language || variant}</span>
27
- {sandboxed && (
28
- <span className="bg-muted text-muted-foreground ml-auto rounded px-1.5 py-0.5 text-xs">
29
- sandboxed
30
- </span>
31
- )}
32
- </div>
33
- <div className="bg-card p-4">
34
- {truncated ? (
35
- <pre className="text-foreground overflow-hidden font-mono text-xs whitespace-pre-wrap">
36
- {truncated}
37
- </pre>
38
- ) : (
39
- <p className="text-muted-foreground font-mono text-xs italic">
40
- // Code content goes here…
41
- </p>
42
- )}
43
- </div>
44
- </div>
45
- )
46
- }
@@ -1,87 +0,0 @@
1
- 'use client'
2
-
3
- import { ChevronDown } from 'lucide-react'
4
- import type { BlockPreviewProps } from './index.js'
5
-
6
- interface FAQItem {
7
- question?: string
8
- answer?: string
9
- }
10
-
11
- export function FAQPreview({ data, variant = 'accordion' }: BlockPreviewProps) {
12
- const items = (data.items as FAQItem[]) || []
13
-
14
- const placeholderItems: FAQItem[] =
15
- items.length > 0
16
- ? items
17
- : [
18
- { question: 'What is this?', answer: 'Answer to the first question goes here.' },
19
- { question: 'How does it work?', answer: 'Answer to the second question goes here.' },
20
- {
21
- question: 'Where can I learn more?',
22
- answer: 'Answer to the third question goes here.',
23
- },
24
- ]
25
-
26
- if (variant === 'two-column') {
27
- const mid = Math.ceil(placeholderItems.length / 2)
28
- const col1 = placeholderItems.slice(0, mid)
29
- const col2 = placeholderItems.slice(mid)
30
-
31
- return (
32
- <div className="border-border rounded-md border p-4">
33
- <div className="grid grid-cols-2 gap-4">
34
- {[col1, col2].map((column, colIdx) => (
35
- <div key={colIdx} className="flex flex-col gap-3">
36
- {column.map((item, i) => (
37
- <div key={i} className="border-border rounded-md border p-3">
38
- <p className="text-foreground text-sm font-medium">
39
- {item.question || 'Question?'}
40
- </p>
41
- <p className="text-muted-foreground mt-1 line-clamp-2 text-xs">
42
- {item.answer || 'Answer text…'}
43
- </p>
44
- </div>
45
- ))}
46
- </div>
47
- ))}
48
- </div>
49
- </div>
50
- )
51
- }
52
-
53
- if (variant === 'list') {
54
- return (
55
- <div className="border-border rounded-md border p-4">
56
- <div className="flex flex-col gap-3">
57
- {placeholderItems.map((item, i) => (
58
- <div key={i} className="border-border border-b pb-3 last:border-b-0 last:pb-0">
59
- <p className="text-foreground text-sm font-medium">{item.question || 'Question?'}</p>
60
- <p className="text-muted-foreground mt-1 line-clamp-2 text-xs">
61
- {item.answer || 'Answer text…'}
62
- </p>
63
- </div>
64
- ))}
65
- </div>
66
- </div>
67
- )
68
- }
69
-
70
- return (
71
- <div className="border-border rounded-md border p-4">
72
- <div className="flex flex-col gap-2">
73
- {placeholderItems.map((item, i) => (
74
- <div key={i} className="border-border rounded-md border p-3">
75
- <div className="flex items-center justify-between">
76
- <p className="text-foreground text-sm font-medium">{item.question || 'Question?'}</p>
77
- <ChevronDown size={14} className="text-muted-foreground shrink-0" />
78
- </div>
79
- {i === 0 && (
80
- <p className="text-muted-foreground mt-2 text-xs">{item.answer || 'Answer text…'}</p>
81
- )}
82
- </div>
83
- ))}
84
- </div>
85
- </div>
86
- )
87
- }
@@ -1,18 +0,0 @@
1
- 'use client'
2
-
3
- import { Box } from 'lucide-react'
4
-
5
- export interface FallbackPreviewProps {
6
- blockType: string
7
- data: Record<string, unknown>
8
- }
9
-
10
- export function FallbackPreview({ blockType }: FallbackPreviewProps) {
11
- return (
12
- <div className="border-border bg-muted/50 flex flex-col items-center justify-center gap-2 rounded-md border border-dashed p-8">
13
- <Box size={24} className="text-muted-foreground" />
14
- <p className="text-muted-foreground text-sm font-medium">{blockType}</p>
15
- <p className="text-muted-foreground text-xs">No preview available</p>
16
- </div>
17
- )
18
- }
@@ -1,117 +0,0 @@
1
- 'use client'
2
-
3
- import { useEffect, useState } from 'react'
4
- import { FileText, Mail, Send } from 'lucide-react'
5
- import { cmsApi } from '../../../lib/api.js'
6
- import type { BlockPreviewProps } from './index.js'
7
-
8
- interface PreviewField {
9
- key: string
10
- label: string
11
- type: string
12
- }
13
-
14
- interface PreviewForm {
15
- name: string
16
- fields: PreviewField[]
17
- }
18
-
19
- /**
20
- * Canvas preview for the form block. When a form is linked we fetch its real
21
- * name + fields so the editor sees what will render; otherwise we fall back to
22
- * a generic skeleton. Network failures degrade to the skeleton rather than
23
- * breaking the canvas.
24
- */
25
- export function FormPreview({ data, variant = 'contact' }: BlockPreviewProps) {
26
- const formId = (data.formId as string) || ''
27
- const [form, setForm] = useState<PreviewForm | null>(null)
28
- const [loading, setLoading] = useState(false)
29
-
30
- useEffect(() => {
31
- if (!formId) {
32
- setForm(null)
33
- return
34
- }
35
- let cancelled = false
36
- setLoading(true)
37
- cmsApi<{ name?: string; fields?: PreviewField[] }>(`/forms/${encodeURIComponent(formId)}`)
38
- .then((res) => {
39
- if (cancelled) return
40
- if (res.data?.name) {
41
- setForm({ name: res.data.name, fields: res.data.fields ?? [] })
42
- } else {
43
- setForm(null)
44
- }
45
- })
46
- .finally(() => {
47
- if (!cancelled) setLoading(false)
48
- })
49
- return () => {
50
- cancelled = true
51
- }
52
- }, [formId])
53
-
54
- if (!formId) {
55
- return (
56
- <div className="border-border bg-muted/30 rounded-md border border-dashed p-6 text-center">
57
- <FileText size={20} className="text-muted-foreground mx-auto" />
58
- <p className="text-muted-foreground mt-2 text-sm">No form selected</p>
59
- <p className="text-muted-foreground/70 text-xs">Pick a form in the block settings</p>
60
- </div>
61
- )
62
- }
63
-
64
- const heading = form?.name ?? (loading ? 'Loading form…' : 'Contact Form')
65
- const fields = form?.fields ?? []
66
-
67
- if (variant === 'newsletter') {
68
- return (
69
- <div className="border-border rounded-md border p-6 text-center">
70
- <Mail size={24} className="text-muted-foreground mx-auto" />
71
- <p className="text-foreground mt-3 text-sm font-medium">
72
- {form?.name ?? 'Newsletter Signup'}
73
- </p>
74
- <div className="mx-auto mt-4 flex max-w-xs gap-2">
75
- <div className="border-border bg-input-background h-9 flex-1 rounded-md border" />
76
- <div className="bg-primary flex h-9 items-center rounded-md px-3">
77
- <Send size={14} className="text-primary-foreground" />
78
- </div>
79
- </div>
80
- </div>
81
- )
82
- }
83
-
84
- return (
85
- <div className="border-border rounded-md border p-5">
86
- <div className="flex items-center gap-2">
87
- <FileText size={16} className="text-muted-foreground" />
88
- <p className="text-foreground text-sm font-medium">{heading}</p>
89
- </div>
90
- <div className="mt-3 flex flex-col gap-2">
91
- {fields.length > 0 ? (
92
- fields.slice(0, 5).map((field) => (
93
- <div key={field.key}>
94
- <p className="text-muted-foreground mb-1 text-xs">{field.label || field.key}</p>
95
- <div
96
- className={`border-border bg-input-background rounded-md border ${
97
- field.type === 'textarea' ? 'h-16' : 'h-8'
98
- }`}
99
- />
100
- </div>
101
- ))
102
- ) : (
103
- <>
104
- <div className="border-border bg-input-background h-8 rounded-md border" />
105
- <div className="border-border bg-input-background h-8 rounded-md border" />
106
- <div className="border-border bg-input-background h-20 rounded-md border" />
107
- </>
108
- )}
109
- </div>
110
- <div className="mt-3">
111
- <span className="bg-primary text-primary-foreground inline-block rounded-md px-4 py-2 text-sm">
112
- {variant === 'custom' ? 'Submit' : 'Send Message'}
113
- </span>
114
- </div>
115
- </div>
116
- )
117
- }