@prenta/admin 0.98.0 → 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.
- package/CHANGELOG.md +12 -0
- package/dist/AdminRoot.d.ts.map +1 -1
- package/dist/AdminRoot.js +17 -28
- package/dist/AdminRoot.js.map +1 -1
- package/dist/__tests__/components/seo-score-bands.test.js +0 -10
- package/dist/__tests__/components/seo-score-bands.test.js.map +1 -1
- package/dist/__tests__/layout/sidebar-posts.test.js +6 -11
- package/dist/__tests__/layout/sidebar-posts.test.js.map +1 -1
- package/dist/__tests__/layout/sidebar-submenu.render.test.js +3 -2
- package/dist/__tests__/layout/sidebar-submenu.render.test.js.map +1 -1
- package/dist/__tests__/lib/page-audit-input.test.js +77 -1
- package/dist/__tests__/lib/page-audit-input.test.js.map +1 -1
- package/dist/__tests__/lib/page-editor-service-api.test.js +26 -23
- package/dist/__tests__/lib/page-editor-service-api.test.js.map +1 -1
- package/dist/__tests__/views/page-section-editor.test.js +0 -1
- package/dist/__tests__/views/page-section-editor.test.js.map +1 -1
- package/dist/__tests__/views/page-tab.render.test.js +0 -1
- package/dist/__tests__/views/page-tab.render.test.js.map +1 -1
- package/dist/index.d.ts +1 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -3
- package/dist/index.js.map +1 -1
- package/dist/layout/Sidebar.d.ts +3 -4
- package/dist/layout/Sidebar.d.ts.map +1 -1
- package/dist/layout/Sidebar.js +7 -38
- package/dist/layout/Sidebar.js.map +1 -1
- package/dist/lib/page-audit-input.d.ts +14 -1
- package/dist/lib/page-audit-input.d.ts.map +1 -1
- package/dist/lib/page-audit-input.js +19 -0
- package/dist/lib/page-audit-input.js.map +1 -1
- package/dist/lib/page-editor-service.d.ts +0 -7
- package/dist/lib/page-editor-service.d.ts.map +1 -1
- package/dist/lib/page-editor-service.js +9 -9
- package/dist/lib/page-editor-service.js.map +1 -1
- package/dist/prenta-admin.css +1 -1
- package/dist/views/PageEditor.d.ts +2 -2
- package/dist/views/PageEditor.js +2 -2
- package/dist/views/page-editor/PageSectionEditor.d.ts.map +1 -1
- package/dist/views/page-editor/PageSectionEditor.js +9 -15
- package/dist/views/page-editor/PageSectionEditor.js.map +1 -1
- package/package.json +4 -4
- package/src/AdminRoot.tsx +18 -68
- package/src/__tests__/components/seo-score-bands.test.ts +0 -10
- package/src/__tests__/layout/sidebar-posts.test.ts +6 -11
- package/src/__tests__/layout/sidebar-submenu.render.test.tsx +3 -2
- package/src/__tests__/lib/page-audit-input.test.ts +91 -1
- package/src/__tests__/lib/page-editor-service-api.test.ts +27 -25
- package/src/__tests__/views/page-section-editor.test.tsx +0 -1
- package/src/__tests__/views/page-tab.render.test.tsx +0 -1
- package/src/index.ts +1 -11
- package/src/layout/Sidebar.tsx +6 -41
- package/src/lib/page-audit-input.ts +36 -1
- package/src/lib/page-editor-service.ts +9 -16
- package/src/views/PageEditor.tsx +2 -2
- package/src/views/page-editor/PageSectionEditor.tsx +8 -37
- package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-block-renderer.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-block-renderer.render.test.js +0 -48
- package/dist/__tests__/views/page-builder-block-renderer.render.test.js.map +0 -1
- package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-form-picker.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-form-picker.render.test.js +0 -44
- package/dist/__tests__/views/page-builder-form-picker.render.test.js.map +0 -1
- package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-sections-panel.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-sections-panel.render.test.js +0 -121
- package/dist/__tests__/views/page-builder-sections-panel.render.test.js.map +0 -1
- package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-template-seed.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-template-seed.render.test.js +0 -48
- package/dist/__tests__/views/page-builder-template-seed.render.test.js.map +0 -1
- package/dist/__tests__/views/page-templates.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-templates.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-templates.render.test.js +0 -126
- package/dist/__tests__/views/page-templates.render.test.js.map +0 -1
- package/dist/hooks/useBuilderState.d.ts +0 -49
- package/dist/hooks/useBuilderState.d.ts.map +0 -1
- package/dist/hooks/useBuilderState.js +0 -238
- package/dist/hooks/useBuilderState.js.map +0 -1
- package/dist/views/page-builder/AIBlockAssist.d.ts +0 -9
- package/dist/views/page-builder/AIBlockAssist.d.ts.map +0 -1
- package/dist/views/page-builder/AIBlockAssist.js +0 -40
- package/dist/views/page-builder/AIBlockAssist.js.map +0 -1
- package/dist/views/page-builder/AIGenerateDialog.d.ts +0 -8
- package/dist/views/page-builder/AIGenerateDialog.d.ts.map +0 -1
- package/dist/views/page-builder/AIGenerateDialog.js +0 -173
- package/dist/views/page-builder/AIGenerateDialog.js.map +0 -1
- package/dist/views/page-builder/BlockEditor.d.ts +0 -11
- package/dist/views/page-builder/BlockEditor.d.ts.map +0 -1
- package/dist/views/page-builder/BlockEditor.js +0 -159
- package/dist/views/page-builder/BlockEditor.js.map +0 -1
- package/dist/views/page-builder/BlockPicker.d.ts +0 -7
- package/dist/views/page-builder/BlockPicker.d.ts.map +0 -1
- package/dist/views/page-builder/BlockPicker.js +0 -103
- package/dist/views/page-builder/BlockPicker.js.map +0 -1
- package/dist/views/page-builder/BottomBar.d.ts +0 -9
- package/dist/views/page-builder/BottomBar.d.ts.map +0 -1
- package/dist/views/page-builder/BottomBar.js +0 -13
- package/dist/views/page-builder/BottomBar.js.map +0 -1
- package/dist/views/page-builder/BuilderSectionsPanel.d.ts +0 -20
- package/dist/views/page-builder/BuilderSectionsPanel.d.ts.map +0 -1
- package/dist/views/page-builder/BuilderSectionsPanel.js +0 -105
- package/dist/views/page-builder/BuilderSectionsPanel.js.map +0 -1
- package/dist/views/page-builder/BuilderToolbar.d.ts +0 -21
- package/dist/views/page-builder/BuilderToolbar.d.ts.map +0 -1
- package/dist/views/page-builder/BuilderToolbar.js +0 -18
- package/dist/views/page-builder/BuilderToolbar.js.map +0 -1
- package/dist/views/page-builder/ContextPanel.d.ts +0 -20
- package/dist/views/page-builder/ContextPanel.d.ts.map +0 -1
- package/dist/views/page-builder/ContextPanel.js +0 -43
- package/dist/views/page-builder/ContextPanel.js.map +0 -1
- package/dist/views/page-builder/DesignScore.d.ts +0 -6
- package/dist/views/page-builder/DesignScore.d.ts.map +0 -1
- package/dist/views/page-builder/DesignScore.js +0 -93
- package/dist/views/page-builder/DesignScore.js.map +0 -1
- package/dist/views/page-builder/NodeSettings.d.ts +0 -12
- package/dist/views/page-builder/NodeSettings.d.ts.map +0 -1
- package/dist/views/page-builder/NodeSettings.js +0 -80
- package/dist/views/page-builder/NodeSettings.js.map +0 -1
- package/dist/views/page-builder/PageBuilder.d.ts +0 -10
- package/dist/views/page-builder/PageBuilder.d.ts.map +0 -1
- package/dist/views/page-builder/PageBuilder.js +0 -222
- package/dist/views/page-builder/PageBuilder.js.map +0 -1
- package/dist/views/page-builder/PageSettings.d.ts +0 -7
- package/dist/views/page-builder/PageSettings.d.ts.map +0 -1
- package/dist/views/page-builder/PageSettings.js +0 -27
- package/dist/views/page-builder/PageSettings.js.map +0 -1
- package/dist/views/page-builder/PageTemplates.d.ts +0 -5
- package/dist/views/page-builder/PageTemplates.d.ts.map +0 -1
- package/dist/views/page-builder/PageTemplates.js +0 -146
- package/dist/views/page-builder/PageTemplates.js.map +0 -1
- package/dist/views/page-builder/RelationshipField.d.ts +0 -15
- package/dist/views/page-builder/RelationshipField.d.ts.map +0 -1
- package/dist/views/page-builder/RelationshipField.js +0 -65
- package/dist/views/page-builder/RelationshipField.js.map +0 -1
- package/dist/views/page-builder/SEOPanel.d.ts +0 -10
- package/dist/views/page-builder/SEOPanel.d.ts.map +0 -1
- package/dist/views/page-builder/SEOPanel.js +0 -103
- package/dist/views/page-builder/SEOPanel.js.map +0 -1
- package/dist/views/page-builder/SavedSections.d.ts +0 -6
- package/dist/views/page-builder/SavedSections.d.ts.map +0 -1
- package/dist/views/page-builder/SavedSections.js +0 -141
- package/dist/views/page-builder/SavedSections.js.map +0 -1
- package/dist/views/page-builder/TemplatePicker.d.ts +0 -7
- package/dist/views/page-builder/TemplatePicker.d.ts.map +0 -1
- package/dist/views/page-builder/TemplatePicker.js +0 -68
- package/dist/views/page-builder/TemplatePicker.js.map +0 -1
- package/dist/views/page-builder/Widgets.d.ts +0 -13
- package/dist/views/page-builder/Widgets.d.ts.map +0 -1
- package/dist/views/page-builder/Widgets.js +0 -15
- package/dist/views/page-builder/Widgets.js.map +0 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/CTAPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/CTAPreview.js +0 -19
- package/dist/views/page-builder/block-renderers/CTAPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/CardsPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/CardsPreview.js +0 -22
- package/dist/views/page-builder/block-renderers/CardsPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/CodePreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/CodePreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/CodePreview.js +0 -12
- package/dist/views/page-builder/block-renderers/CodePreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/FAQPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/FAQPreview.js +0 -27
- package/dist/views/page-builder/block-renderers/FAQPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts +0 -6
- package/dist/views/page-builder/block-renderers/FallbackPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/FallbackPreview.js +0 -7
- package/dist/views/page-builder/block-renderers/FallbackPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/FormPreview.d.ts +0 -9
- package/dist/views/page-builder/block-renderers/FormPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/FormPreview.js +0 -52
- package/dist/views/page-builder/block-renderers/FormPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/GalleryPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/GalleryPreview.js +0 -19
- package/dist/views/page-builder/block-renderers/GalleryPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/HeroPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/HeroPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/HeroPreview.js +0 -19
- package/dist/views/page-builder/block-renderers/HeroPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/ImagePreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/ImagePreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/ImagePreview.js +0 -17
- package/dist/views/page-builder/block-renderers/ImagePreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/TextPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/TextPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/TextPreview.js +0 -22
- package/dist/views/page-builder/block-renderers/TextPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/VideoPreview.d.ts +0 -3
- package/dist/views/page-builder/block-renderers/VideoPreview.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/VideoPreview.js +0 -18
- package/dist/views/page-builder/block-renderers/VideoPreview.js.map +0 -1
- package/dist/views/page-builder/block-renderers/index.d.ts +0 -9
- package/dist/views/page-builder/block-renderers/index.d.ts.map +0 -1
- package/dist/views/page-builder/block-renderers/index.js +0 -25
- package/dist/views/page-builder/block-renderers/index.js.map +0 -1
- package/dist/views/page-builder/canvas/BlockRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/BlockRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/BlockRenderer.js +0 -20
- package/dist/views/page-builder/canvas/BlockRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.d.ts +0 -10
- package/dist/views/page-builder/canvas/BuilderCanvas.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/BuilderCanvas.js +0 -26
- package/dist/views/page-builder/canvas/BuilderCanvas.js.map +0 -1
- package/dist/views/page-builder/canvas/ColumnRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/ColumnRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/ColumnRenderer.js +0 -32
- package/dist/views/page-builder/canvas/ColumnRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/ContainerRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/ContainerRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/ContainerRenderer.js +0 -29
- package/dist/views/page-builder/canvas/ContainerRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/RowRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/RowRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/RowRenderer.js +0 -28
- package/dist/views/page-builder/canvas/RowRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/SectionRenderer.d.ts +0 -8
- package/dist/views/page-builder/canvas/SectionRenderer.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/SectionRenderer.js +0 -50
- package/dist/views/page-builder/canvas/SectionRenderer.js.map +0 -1
- package/dist/views/page-builder/canvas/index.d.ts +0 -3
- package/dist/views/page-builder/canvas/index.d.ts.map +0 -1
- package/dist/views/page-builder/canvas/index.js +0 -2
- package/dist/views/page-builder/canvas/index.js.map +0 -1
- package/src/__tests__/views/page-builder-block-renderer.render.test.tsx +0 -66
- package/src/__tests__/views/page-builder-form-picker.render.test.tsx +0 -54
- package/src/__tests__/views/page-builder-sections-panel.render.test.tsx +0 -138
- package/src/__tests__/views/page-builder-template-seed.render.test.tsx +0 -59
- package/src/__tests__/views/page-templates.render.test.tsx +0 -182
- package/src/hooks/useBuilderState.ts +0 -361
- package/src/views/page-builder/AIBlockAssist.tsx +0 -64
- package/src/views/page-builder/AIGenerateDialog.tsx +0 -593
- package/src/views/page-builder/BlockEditor.tsx +0 -523
- package/src/views/page-builder/BlockPicker.tsx +0 -324
- package/src/views/page-builder/BottomBar.tsx +0 -65
- package/src/views/page-builder/BuilderSectionsPanel.tsx +0 -304
- package/src/views/page-builder/BuilderToolbar.tsx +0 -221
- package/src/views/page-builder/ContextPanel.tsx +0 -145
- package/src/views/page-builder/DesignScore.tsx +0 -251
- package/src/views/page-builder/NodeSettings.tsx +0 -500
- package/src/views/page-builder/PageBuilder.tsx +0 -405
- package/src/views/page-builder/PageSettings.tsx +0 -162
- package/src/views/page-builder/PageTemplates.tsx +0 -435
- package/src/views/page-builder/RelationshipField.tsx +0 -113
- package/src/views/page-builder/SEOPanel.tsx +0 -464
- package/src/views/page-builder/SavedSections.tsx +0 -480
- package/src/views/page-builder/TemplatePicker.tsx +0 -197
- package/src/views/page-builder/Widgets.tsx +0 -44
- package/src/views/page-builder/block-renderers/CTAPreview.tsx +0 -79
- package/src/views/page-builder/block-renderers/CardsPreview.tsx +0 -69
- package/src/views/page-builder/block-renderers/CodePreview.tsx +0 -46
- package/src/views/page-builder/block-renderers/FAQPreview.tsx +0 -87
- package/src/views/page-builder/block-renderers/FallbackPreview.tsx +0 -18
- package/src/views/page-builder/block-renderers/FormPreview.tsx +0 -117
- package/src/views/page-builder/block-renderers/GalleryPreview.tsx +0 -87
- package/src/views/page-builder/block-renderers/HeroPreview.tsx +0 -90
- package/src/views/page-builder/block-renderers/ImagePreview.tsx +0 -54
- package/src/views/page-builder/block-renderers/TextPreview.tsx +0 -81
- package/src/views/page-builder/block-renderers/VideoPreview.tsx +0 -73
- package/src/views/page-builder/block-renderers/index.ts +0 -34
- package/src/views/page-builder/canvas/BlockRenderer.tsx +0 -56
- package/src/views/page-builder/canvas/BuilderCanvas.tsx +0 -87
- package/src/views/page-builder/canvas/ColumnRenderer.tsx +0 -82
- package/src/views/page-builder/canvas/ContainerRenderer.tsx +0 -67
- package/src/views/page-builder/canvas/RowRenderer.tsx +0 -68
- package/src/views/page-builder/canvas/SectionRenderer.tsx +0 -93
- package/src/views/page-builder/canvas/index.ts +0 -2
|
@@ -1,464 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState, useMemo } from 'react'
|
|
4
|
-
import { analyzeSEO } from '@prenta/core/page-builder'
|
|
5
|
-
import type {
|
|
6
|
-
PageNode,
|
|
7
|
-
BuilderSEOAnalysis,
|
|
8
|
-
BuilderSEOCheck,
|
|
9
|
-
BuilderReadabilityResult,
|
|
10
|
-
} from '@prenta/core'
|
|
11
|
-
import type { PageSettings } from '../../hooks/useBuilderState.js'
|
|
12
|
-
import {
|
|
13
|
-
CheckCircle2,
|
|
14
|
-
AlertCircle,
|
|
15
|
-
XCircle,
|
|
16
|
-
ChevronDown,
|
|
17
|
-
ChevronUp,
|
|
18
|
-
Search,
|
|
19
|
-
Globe,
|
|
20
|
-
Type,
|
|
21
|
-
FileText,
|
|
22
|
-
Clock,
|
|
23
|
-
BarChart3,
|
|
24
|
-
Target,
|
|
25
|
-
Eye,
|
|
26
|
-
} from 'lucide-react'
|
|
27
|
-
|
|
28
|
-
export interface BuilderSEOPanelProps {
|
|
29
|
-
tree: PageNode
|
|
30
|
-
pageSettings: PageSettings
|
|
31
|
-
onPageSettingsChange: (settings: Partial<PageSettings>) => void
|
|
32
|
-
selectedNodeId: string | null
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function getScoreColor(score: number): string {
|
|
36
|
-
if (score >= 70) return 'text-success'
|
|
37
|
-
if (score >= 40) return 'text-warning'
|
|
38
|
-
return 'text-destructive'
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function getRingStrokeColor(score: number): string {
|
|
42
|
-
if (score >= 70) return 'rgb(34, 197, 94)'
|
|
43
|
-
if (score >= 40) return 'rgb(245, 158, 11)'
|
|
44
|
-
return 'rgb(239, 68, 68)'
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function getScoreLabel(score: number): string {
|
|
48
|
-
if (score >= 80) return 'Excellent'
|
|
49
|
-
if (score >= 70) return 'Good'
|
|
50
|
-
if (score >= 50) return 'Needs Work'
|
|
51
|
-
return 'Poor'
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function StatusIcon({ status }: { status: BuilderSEOCheck['status'] }) {
|
|
55
|
-
switch (status) {
|
|
56
|
-
case 'good':
|
|
57
|
-
return <CheckCircle2 size={14} className="text-success shrink-0" />
|
|
58
|
-
case 'warning':
|
|
59
|
-
return <AlertCircle size={14} className="text-warning shrink-0" />
|
|
60
|
-
case 'error':
|
|
61
|
-
return <XCircle size={14} className="text-destructive shrink-0" />
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function ScoreRing({ score }: { score: number }) {
|
|
66
|
-
const radius = 32
|
|
67
|
-
const strokeWidth = 6
|
|
68
|
-
const circumference = 2 * Math.PI * radius
|
|
69
|
-
const progress = Math.min(score, 100) / 100
|
|
70
|
-
const dashOffset = circumference * (1 - progress)
|
|
71
|
-
const size = (radius + strokeWidth) * 2
|
|
72
|
-
|
|
73
|
-
return (
|
|
74
|
-
<div className="relative">
|
|
75
|
-
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className="-rotate-90">
|
|
76
|
-
<circle
|
|
77
|
-
cx={size / 2}
|
|
78
|
-
cy={size / 2}
|
|
79
|
-
r={radius}
|
|
80
|
-
fill="none"
|
|
81
|
-
stroke="var(--border)"
|
|
82
|
-
strokeWidth={strokeWidth}
|
|
83
|
-
/>
|
|
84
|
-
<circle
|
|
85
|
-
cx={size / 2}
|
|
86
|
-
cy={size / 2}
|
|
87
|
-
r={radius}
|
|
88
|
-
fill="none"
|
|
89
|
-
stroke={getRingStrokeColor(score)}
|
|
90
|
-
strokeWidth={strokeWidth}
|
|
91
|
-
strokeDasharray={circumference}
|
|
92
|
-
strokeDashoffset={dashOffset}
|
|
93
|
-
strokeLinecap="round"
|
|
94
|
-
className="transition-all duration-700 ease-out"
|
|
95
|
-
/>
|
|
96
|
-
</svg>
|
|
97
|
-
<div className="absolute inset-0 flex items-center justify-center">
|
|
98
|
-
<span className={`text-lg font-medium ${getScoreColor(score)}`}>{score}</span>
|
|
99
|
-
</div>
|
|
100
|
-
</div>
|
|
101
|
-
)
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function ScoreSummary({ checks }: { checks: BuilderSEOCheck[] }) {
|
|
105
|
-
const passed = checks.filter((c) => c.status === 'good').length
|
|
106
|
-
const warnings = checks.filter((c) => c.status === 'warning').length
|
|
107
|
-
const errors = checks.filter((c) => c.status === 'error').length
|
|
108
|
-
|
|
109
|
-
return (
|
|
110
|
-
<div className="flex flex-col gap-1">
|
|
111
|
-
<div className="flex items-center gap-1.5">
|
|
112
|
-
<CheckCircle2 size={12} className="text-success" />
|
|
113
|
-
<span className="text-muted-foreground text-xs">{passed} passed</span>
|
|
114
|
-
</div>
|
|
115
|
-
<div className="flex items-center gap-1.5">
|
|
116
|
-
<AlertCircle size={12} className="text-warning" />
|
|
117
|
-
<span className="text-muted-foreground text-xs">{warnings} warnings</span>
|
|
118
|
-
</div>
|
|
119
|
-
<div className="flex items-center gap-1.5">
|
|
120
|
-
<XCircle size={12} className="text-destructive" />
|
|
121
|
-
<span className="text-muted-foreground text-xs">{errors} issues</span>
|
|
122
|
-
</div>
|
|
123
|
-
</div>
|
|
124
|
-
)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function CollapsibleSection({
|
|
128
|
-
title,
|
|
129
|
-
icon: Icon,
|
|
130
|
-
expanded,
|
|
131
|
-
onToggle,
|
|
132
|
-
children,
|
|
133
|
-
}: {
|
|
134
|
-
title: string
|
|
135
|
-
icon: typeof Search
|
|
136
|
-
expanded: boolean
|
|
137
|
-
onToggle: () => void
|
|
138
|
-
children: React.ReactNode
|
|
139
|
-
}) {
|
|
140
|
-
return (
|
|
141
|
-
<div className="border-border border-b last:border-b-0">
|
|
142
|
-
<button
|
|
143
|
-
type="button"
|
|
144
|
-
className="hover:bg-muted/50 flex w-full items-center justify-between px-4 py-3 text-left transition-colors"
|
|
145
|
-
onClick={onToggle}
|
|
146
|
-
aria-expanded={expanded}
|
|
147
|
-
>
|
|
148
|
-
<div className="flex items-center gap-2">
|
|
149
|
-
<Icon size={14} className="text-muted-foreground" />
|
|
150
|
-
<span className="text-foreground text-xs font-medium">{title}</span>
|
|
151
|
-
</div>
|
|
152
|
-
{expanded ? (
|
|
153
|
-
<ChevronUp size={14} className="text-muted-foreground" />
|
|
154
|
-
) : (
|
|
155
|
-
<ChevronDown size={14} className="text-muted-foreground" />
|
|
156
|
-
)}
|
|
157
|
-
</button>
|
|
158
|
-
{expanded && <div className="px-4 pb-4">{children}</div>}
|
|
159
|
-
</div>
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function SEOChecksList({ checks }: { checks: BuilderSEOCheck[] }) {
|
|
164
|
-
return (
|
|
165
|
-
<div className="flex flex-col gap-2">
|
|
166
|
-
{checks.map((check) => (
|
|
167
|
-
<div key={check.id} className="flex items-start gap-2">
|
|
168
|
-
<StatusIcon status={check.status} />
|
|
169
|
-
<div className="flex flex-col">
|
|
170
|
-
<span className="text-foreground text-xs font-medium">{check.label}</span>
|
|
171
|
-
<span className="text-muted-foreground text-xs">{check.detail}</span>
|
|
172
|
-
</div>
|
|
173
|
-
</div>
|
|
174
|
-
))}
|
|
175
|
-
</div>
|
|
176
|
-
)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function ReadabilityGrid({ readability }: { readability: BuilderReadabilityResult }) {
|
|
180
|
-
const fleschLabel =
|
|
181
|
-
readability.fleschScore >= 60
|
|
182
|
-
? 'Easy'
|
|
183
|
-
: readability.fleschScore >= 30
|
|
184
|
-
? 'Moderate'
|
|
185
|
-
: 'Difficult'
|
|
186
|
-
|
|
187
|
-
return (
|
|
188
|
-
<div className="grid grid-cols-2 gap-3">
|
|
189
|
-
<div className="bg-muted/50 flex flex-col gap-1 rounded-md p-3">
|
|
190
|
-
<div className="flex items-center gap-1.5">
|
|
191
|
-
<BarChart3 size={12} className="text-muted-foreground" />
|
|
192
|
-
<span className="text-muted-foreground text-xs">Flesch Score</span>
|
|
193
|
-
</div>
|
|
194
|
-
<span className={`text-sm font-medium ${getScoreColor(readability.fleschScore)}`}>
|
|
195
|
-
{readability.fleschScore}
|
|
196
|
-
</span>
|
|
197
|
-
<span className="text-muted-foreground text-xs">{fleschLabel}</span>
|
|
198
|
-
</div>
|
|
199
|
-
|
|
200
|
-
<div className="bg-muted/50 flex flex-col gap-1 rounded-md p-3">
|
|
201
|
-
<div className="flex items-center gap-1.5">
|
|
202
|
-
<Type size={12} className="text-muted-foreground" />
|
|
203
|
-
<span className="text-muted-foreground text-xs">Word Count</span>
|
|
204
|
-
</div>
|
|
205
|
-
<span className="text-foreground text-sm font-medium">{readability.wordCount}</span>
|
|
206
|
-
<span className="text-muted-foreground text-xs">
|
|
207
|
-
{readability.wordCount >= 300 ? 'Good length' : 'Short'}
|
|
208
|
-
</span>
|
|
209
|
-
</div>
|
|
210
|
-
|
|
211
|
-
<div className="bg-muted/50 flex flex-col gap-1 rounded-md p-3">
|
|
212
|
-
<div className="flex items-center gap-1.5">
|
|
213
|
-
<FileText size={12} className="text-muted-foreground" />
|
|
214
|
-
<span className="text-muted-foreground text-xs">Avg Sentence</span>
|
|
215
|
-
</div>
|
|
216
|
-
<span className="text-foreground text-sm font-medium">
|
|
217
|
-
{readability.avgSentenceLength} words
|
|
218
|
-
</span>
|
|
219
|
-
<span className="text-muted-foreground text-xs">
|
|
220
|
-
{readability.avgSentenceLength <= 20 ? 'Good' : 'Long'}
|
|
221
|
-
</span>
|
|
222
|
-
</div>
|
|
223
|
-
|
|
224
|
-
<div className="bg-muted/50 flex flex-col gap-1 rounded-md p-3">
|
|
225
|
-
<div className="flex items-center gap-1.5">
|
|
226
|
-
<Clock size={12} className="text-muted-foreground" />
|
|
227
|
-
<span className="text-muted-foreground text-xs">Reading Time</span>
|
|
228
|
-
</div>
|
|
229
|
-
<span className="text-foreground text-sm font-medium">{readability.readingTime} min</span>
|
|
230
|
-
<span className="text-muted-foreground text-xs">~{readability.wordCount} words</span>
|
|
231
|
-
</div>
|
|
232
|
-
</div>
|
|
233
|
-
)
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
function PerBlockHints({ hints }: { hints: BuilderSEOCheck[] }) {
|
|
237
|
-
return (
|
|
238
|
-
<div className="flex flex-col gap-2">
|
|
239
|
-
{hints.map((hint, index) => (
|
|
240
|
-
<div key={`${hint.id}-${index}`} className="flex items-start gap-2">
|
|
241
|
-
<StatusIcon status={hint.status} />
|
|
242
|
-
<div className="flex flex-col">
|
|
243
|
-
<span className="text-foreground text-xs font-medium">{hint.label}</span>
|
|
244
|
-
<span className="text-muted-foreground text-xs">{hint.detail}</span>
|
|
245
|
-
</div>
|
|
246
|
-
</div>
|
|
247
|
-
))}
|
|
248
|
-
</div>
|
|
249
|
-
)
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function BasicSEOFields({
|
|
253
|
-
pageSettings,
|
|
254
|
-
onPageSettingsChange,
|
|
255
|
-
}: {
|
|
256
|
-
pageSettings: PageSettings
|
|
257
|
-
onPageSettingsChange: (settings: Partial<PageSettings>) => void
|
|
258
|
-
}) {
|
|
259
|
-
const metaTitleLength = (pageSettings.metaTitle || '').length
|
|
260
|
-
const metaDescLength = (pageSettings.metaDescription || '').length
|
|
261
|
-
|
|
262
|
-
return (
|
|
263
|
-
<div className="flex flex-col gap-4">
|
|
264
|
-
<div className="flex flex-col gap-1.5">
|
|
265
|
-
<div className="flex items-center justify-between">
|
|
266
|
-
<label className="text-foreground text-xs font-medium">Meta Title</label>
|
|
267
|
-
<span
|
|
268
|
-
className={`text-xs ${metaTitleLength > 60 ? 'text-destructive' : metaTitleLength > 0 ? 'text-muted-foreground' : 'text-muted-foreground'}`}
|
|
269
|
-
>
|
|
270
|
-
{metaTitleLength}/60
|
|
271
|
-
</span>
|
|
272
|
-
</div>
|
|
273
|
-
<input
|
|
274
|
-
type="text"
|
|
275
|
-
value={pageSettings.metaTitle || ''}
|
|
276
|
-
onChange={(e) => onPageSettingsChange({ metaTitle: e.target.value })}
|
|
277
|
-
placeholder="Page title for search engines"
|
|
278
|
-
className="bg-input-background border-border text-foreground placeholder:text-muted-foreground focus:ring-primary w-full rounded-md border px-3 py-2 text-sm focus:ring-1 focus:outline-none"
|
|
279
|
-
/>
|
|
280
|
-
</div>
|
|
281
|
-
|
|
282
|
-
<div className="flex flex-col gap-1.5">
|
|
283
|
-
<div className="flex items-center justify-between">
|
|
284
|
-
<label className="text-foreground text-xs font-medium">Meta Description</label>
|
|
285
|
-
<span
|
|
286
|
-
className={`text-xs ${metaDescLength > 160 ? 'text-destructive' : metaDescLength > 0 ? 'text-muted-foreground' : 'text-muted-foreground'}`}
|
|
287
|
-
>
|
|
288
|
-
{metaDescLength}/160
|
|
289
|
-
</span>
|
|
290
|
-
</div>
|
|
291
|
-
<textarea
|
|
292
|
-
value={pageSettings.metaDescription || ''}
|
|
293
|
-
onChange={(e) => onPageSettingsChange({ metaDescription: e.target.value })}
|
|
294
|
-
placeholder="Brief description of the page content"
|
|
295
|
-
rows={3}
|
|
296
|
-
className="bg-input-background border-border text-foreground placeholder:text-muted-foreground focus:ring-primary w-full resize-none rounded-md border px-3 py-2 text-sm focus:ring-1 focus:outline-none"
|
|
297
|
-
/>
|
|
298
|
-
</div>
|
|
299
|
-
|
|
300
|
-
<div className="flex flex-col gap-1.5">
|
|
301
|
-
<label className="text-foreground text-xs font-medium">Focus Keyphrase</label>
|
|
302
|
-
<input
|
|
303
|
-
type="text"
|
|
304
|
-
value={pageSettings.focusKeyphrase || ''}
|
|
305
|
-
onChange={(e) => onPageSettingsChange({ focusKeyphrase: e.target.value })}
|
|
306
|
-
placeholder="Primary keyword or phrase"
|
|
307
|
-
className="bg-input-background border-border text-foreground placeholder:text-muted-foreground focus:ring-primary w-full rounded-md border px-3 py-2 text-sm focus:ring-1 focus:outline-none"
|
|
308
|
-
/>
|
|
309
|
-
</div>
|
|
310
|
-
</div>
|
|
311
|
-
)
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function SearchPreview({ pageSettings }: { pageSettings: PageSettings }) {
|
|
315
|
-
const title = pageSettings.metaTitle || pageSettings.title || 'Untitled Page'
|
|
316
|
-
const description = pageSettings.metaDescription || 'No description set'
|
|
317
|
-
const slug = pageSettings.slug || 'page'
|
|
318
|
-
|
|
319
|
-
return (
|
|
320
|
-
<div className="bg-background border-border rounded-md border p-4">
|
|
321
|
-
<p className="text-muted-foreground mb-2 text-xs">Google Search Preview</p>
|
|
322
|
-
<div className="flex flex-col gap-1">
|
|
323
|
-
<p className="text-primary truncate text-sm">{title}</p>
|
|
324
|
-
<p className="text-success truncate text-xs">example.com/{slug}</p>
|
|
325
|
-
<p className="text-muted-foreground line-clamp-2 text-xs">{description}</p>
|
|
326
|
-
</div>
|
|
327
|
-
</div>
|
|
328
|
-
)
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function SocialPreview({ pageSettings }: { pageSettings: PageSettings }) {
|
|
332
|
-
const title = pageSettings.metaTitle || pageSettings.title || 'Untitled Page'
|
|
333
|
-
const description = pageSettings.metaDescription || 'No description set'
|
|
334
|
-
|
|
335
|
-
return (
|
|
336
|
-
<div className="bg-background border-border overflow-hidden rounded-md border">
|
|
337
|
-
<p className="text-muted-foreground px-4 pt-3 pb-2 text-xs">Open Graph Preview</p>
|
|
338
|
-
{pageSettings.ogImage && (
|
|
339
|
-
<div className="bg-muted flex h-32 w-full items-center justify-center overflow-hidden">
|
|
340
|
-
<img src={pageSettings.ogImage} alt="OG preview" className="h-full w-full object-cover" />
|
|
341
|
-
</div>
|
|
342
|
-
)}
|
|
343
|
-
{!pageSettings.ogImage && (
|
|
344
|
-
<div className="bg-muted flex h-32 w-full items-center justify-center">
|
|
345
|
-
<Globe size={24} className="text-muted-foreground" />
|
|
346
|
-
</div>
|
|
347
|
-
)}
|
|
348
|
-
<div className="flex flex-col gap-1 p-3">
|
|
349
|
-
<p className="text-muted-foreground text-xs uppercase">example.com</p>
|
|
350
|
-
<p className="text-foreground truncate text-sm font-medium">{title}</p>
|
|
351
|
-
<p className="text-muted-foreground line-clamp-2 text-xs">{description}</p>
|
|
352
|
-
</div>
|
|
353
|
-
</div>
|
|
354
|
-
)
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
export function BuilderSEOPanel({
|
|
358
|
-
tree,
|
|
359
|
-
pageSettings,
|
|
360
|
-
onPageSettingsChange,
|
|
361
|
-
selectedNodeId,
|
|
362
|
-
}: BuilderSEOPanelProps) {
|
|
363
|
-
const analysis = useMemo<BuilderSEOAnalysis>(
|
|
364
|
-
() => analyzeSEO(tree, pageSettings as any),
|
|
365
|
-
[tree, pageSettings],
|
|
366
|
-
)
|
|
367
|
-
|
|
368
|
-
const [expandedSections, setExpandedSections] = useState<string[]>(['checks'])
|
|
369
|
-
|
|
370
|
-
const toggleSection = (section: string) => {
|
|
371
|
-
setExpandedSections((prev) =>
|
|
372
|
-
prev.includes(section) ? prev.filter((s) => s !== section) : [...prev, section],
|
|
373
|
-
)
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const blockHints = useMemo(() => {
|
|
377
|
-
if (!selectedNodeId) return []
|
|
378
|
-
return analysis.perBlockHints.get(selectedNodeId) || []
|
|
379
|
-
}, [analysis.perBlockHints, selectedNodeId])
|
|
380
|
-
|
|
381
|
-
if (!tree.children || tree.children.length === 0) {
|
|
382
|
-
return (
|
|
383
|
-
<div className="flex min-h-[200px] flex-col items-center justify-center p-6 text-center">
|
|
384
|
-
<Search size={32} className="text-muted-foreground mb-3" />
|
|
385
|
-
<p className="text-foreground mb-1 text-sm font-medium">SEO Analysis</p>
|
|
386
|
-
<p className="text-muted-foreground text-xs">
|
|
387
|
-
Add content to your page to see SEO analysis
|
|
388
|
-
</p>
|
|
389
|
-
</div>
|
|
390
|
-
)
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
return (
|
|
394
|
-
<div className="flex flex-col">
|
|
395
|
-
<div className="border-border flex items-center gap-4 border-b px-4 py-5">
|
|
396
|
-
<ScoreRing score={analysis.score} />
|
|
397
|
-
<div className="flex flex-col gap-1">
|
|
398
|
-
<span className="text-foreground text-xs font-medium">
|
|
399
|
-
{getScoreLabel(analysis.score)}
|
|
400
|
-
</span>
|
|
401
|
-
<ScoreSummary checks={analysis.checks} />
|
|
402
|
-
</div>
|
|
403
|
-
</div>
|
|
404
|
-
|
|
405
|
-
<div className="flex flex-col">
|
|
406
|
-
<CollapsibleSection
|
|
407
|
-
title="SEO Checks"
|
|
408
|
-
icon={Search}
|
|
409
|
-
expanded={expandedSections.includes('checks')}
|
|
410
|
-
onToggle={() => toggleSection('checks')}
|
|
411
|
-
>
|
|
412
|
-
<SEOChecksList checks={analysis.checks} />
|
|
413
|
-
</CollapsibleSection>
|
|
414
|
-
|
|
415
|
-
<CollapsibleSection
|
|
416
|
-
title="Readability"
|
|
417
|
-
icon={BarChart3}
|
|
418
|
-
expanded={expandedSections.includes('readability')}
|
|
419
|
-
onToggle={() => toggleSection('readability')}
|
|
420
|
-
>
|
|
421
|
-
<ReadabilityGrid readability={analysis.readability} />
|
|
422
|
-
</CollapsibleSection>
|
|
423
|
-
|
|
424
|
-
{blockHints.length > 0 && (
|
|
425
|
-
<CollapsibleSection
|
|
426
|
-
title="Block Hints"
|
|
427
|
-
icon={Target}
|
|
428
|
-
expanded={expandedSections.includes('blockHints')}
|
|
429
|
-
onToggle={() => toggleSection('blockHints')}
|
|
430
|
-
>
|
|
431
|
-
<PerBlockHints hints={blockHints} />
|
|
432
|
-
</CollapsibleSection>
|
|
433
|
-
)}
|
|
434
|
-
|
|
435
|
-
<CollapsibleSection
|
|
436
|
-
title="Basic SEO"
|
|
437
|
-
icon={FileText}
|
|
438
|
-
expanded={expandedSections.includes('basicSeo')}
|
|
439
|
-
onToggle={() => toggleSection('basicSeo')}
|
|
440
|
-
>
|
|
441
|
-
<BasicSEOFields pageSettings={pageSettings} onPageSettingsChange={onPageSettingsChange} />
|
|
442
|
-
</CollapsibleSection>
|
|
443
|
-
|
|
444
|
-
<CollapsibleSection
|
|
445
|
-
title="Search Preview"
|
|
446
|
-
icon={Eye}
|
|
447
|
-
expanded={expandedSections.includes('searchPreview')}
|
|
448
|
-
onToggle={() => toggleSection('searchPreview')}
|
|
449
|
-
>
|
|
450
|
-
<SearchPreview pageSettings={pageSettings} />
|
|
451
|
-
</CollapsibleSection>
|
|
452
|
-
|
|
453
|
-
<CollapsibleSection
|
|
454
|
-
title="Social Preview"
|
|
455
|
-
icon={Globe}
|
|
456
|
-
expanded={expandedSections.includes('socialPreview')}
|
|
457
|
-
onToggle={() => toggleSection('socialPreview')}
|
|
458
|
-
>
|
|
459
|
-
<SocialPreview pageSettings={pageSettings} />
|
|
460
|
-
</CollapsibleSection>
|
|
461
|
-
</div>
|
|
462
|
-
</div>
|
|
463
|
-
)
|
|
464
|
-
}
|