@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.
- package/CHANGELOG.md +6 -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 -3
- 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-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-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 -3
- 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-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-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-seo-panel.render.test.d.ts +0 -2
- package/dist/__tests__/views/page-builder-seo-panel.render.test.d.ts.map +0 -1
- package/dist/__tests__/views/page-builder-seo-panel.render.test.js +0 -65
- package/dist/__tests__/views/page-builder-seo-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 -9
- package/dist/views/page-builder/SEOPanel.d.ts.map +0 -1
- package/dist/views/page-builder/SEOPanel.js +0 -38
- 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-seo-panel.render.test.tsx +0 -77
- 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 -144
- 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 -222
- 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,593 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { useState, useCallback } from 'react'
|
|
4
|
-
import * as Dialog from '@radix-ui/react-dialog'
|
|
5
|
-
import {
|
|
6
|
-
Sparkles,
|
|
7
|
-
X,
|
|
8
|
-
Loader2,
|
|
9
|
-
Check,
|
|
10
|
-
ChevronDown,
|
|
11
|
-
ChevronUp,
|
|
12
|
-
AlertTriangle,
|
|
13
|
-
Wand2,
|
|
14
|
-
FileText,
|
|
15
|
-
Search,
|
|
16
|
-
Accessibility,
|
|
17
|
-
ArrowRight,
|
|
18
|
-
} from 'lucide-react'
|
|
19
|
-
import { toast } from 'sonner'
|
|
20
|
-
import { cmsApi } from '../../lib/api.js'
|
|
21
|
-
import { adminPortalContainer } from '../../lib/portal-container.js'
|
|
22
|
-
import type { PageNode, GenerationStep } from '@prenta/core'
|
|
23
|
-
|
|
24
|
-
export interface AIGenerateDialogProps {
|
|
25
|
-
open: boolean
|
|
26
|
-
onClose: () => void
|
|
27
|
-
onAccept: (tree: PageNode) => void
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
interface StepInfo {
|
|
31
|
-
key: GenerationStep
|
|
32
|
-
label: string
|
|
33
|
-
icon: React.ReactNode
|
|
34
|
-
description: string
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const ALL_STEPS: StepInfo[] = [
|
|
38
|
-
{
|
|
39
|
-
key: 'structure',
|
|
40
|
-
label: 'Structure',
|
|
41
|
-
icon: <Wand2 size={14} />,
|
|
42
|
-
description: 'Generate page layout and block placement',
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
key: 'content',
|
|
46
|
-
label: 'Content',
|
|
47
|
-
icon: <FileText size={14} />,
|
|
48
|
-
description: 'Write compelling copy for every block',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
key: 'seo',
|
|
52
|
-
label: 'SEO',
|
|
53
|
-
icon: <Search size={14} />,
|
|
54
|
-
description: 'Optimize headings, meta tags, and keywords',
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
key: 'accessibility',
|
|
58
|
-
label: 'Accessibility',
|
|
59
|
-
icon: <Accessibility size={14} />,
|
|
60
|
-
description: 'Fix heading hierarchy, alt text, and ARIA',
|
|
61
|
-
},
|
|
62
|
-
]
|
|
63
|
-
|
|
64
|
-
type Tone = 'professional' | 'casual' | 'friendly' | 'technical' | 'luxury'
|
|
65
|
-
|
|
66
|
-
const TONES: { value: Tone; label: string }[] = [
|
|
67
|
-
{ value: 'professional', label: 'Professional' },
|
|
68
|
-
{ value: 'casual', label: 'Casual' },
|
|
69
|
-
{ value: 'friendly', label: 'Friendly' },
|
|
70
|
-
{ value: 'technical', label: 'Technical' },
|
|
71
|
-
{ value: 'luxury', label: 'Luxury' },
|
|
72
|
-
]
|
|
73
|
-
|
|
74
|
-
type Phase = 'prompt' | 'generating' | 'review'
|
|
75
|
-
|
|
76
|
-
interface GenerationStepResult {
|
|
77
|
-
step: string
|
|
78
|
-
metadata: {
|
|
79
|
-
tokensUsed: number
|
|
80
|
-
durationMs: number
|
|
81
|
-
changes: string[]
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
interface GenerationResultData {
|
|
86
|
-
tree: PageNode
|
|
87
|
-
steps: GenerationStepResult[]
|
|
88
|
-
totalTokensUsed: number
|
|
89
|
-
totalDurationMs: number
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function AIGenerateDialog({ open, onClose, onAccept }: AIGenerateDialogProps) {
|
|
93
|
-
const [phase, setPhase] = useState<Phase>('prompt')
|
|
94
|
-
const [prompt, setPrompt] = useState('')
|
|
95
|
-
const [tone, setTone] = useState<Tone>('professional')
|
|
96
|
-
const [enabledSteps, setEnabledSteps] = useState<Set<GenerationStep>>(
|
|
97
|
-
new Set(['structure', 'content', 'seo', 'accessibility']),
|
|
98
|
-
)
|
|
99
|
-
const [showContext, setShowContext] = useState(false)
|
|
100
|
-
const [businessName, setBusiness] = useState('')
|
|
101
|
-
const [industry, setIndustry] = useState('')
|
|
102
|
-
const [phone, setPhone] = useState('')
|
|
103
|
-
const [address, setAddress] = useState('')
|
|
104
|
-
const [services, setServices] = useState('')
|
|
105
|
-
|
|
106
|
-
const [currentStep, setCurrentStep] = useState<string>('')
|
|
107
|
-
const [completedSteps, setCompletedSteps] = useState<string[]>([])
|
|
108
|
-
const [stepMessage, setStepMessage] = useState('')
|
|
109
|
-
|
|
110
|
-
const [result, setResult] = useState<GenerationResultData | null>(null)
|
|
111
|
-
const [error, setError] = useState<string | null>(null)
|
|
112
|
-
const [expandedStep, setExpandedStep] = useState<string | null>(null)
|
|
113
|
-
|
|
114
|
-
const toggleStep = (step: GenerationStep) => {
|
|
115
|
-
const next = new Set(enabledSteps)
|
|
116
|
-
if (step === 'structure') return
|
|
117
|
-
if (next.has(step)) {
|
|
118
|
-
next.delete(step)
|
|
119
|
-
} else {
|
|
120
|
-
next.add(step)
|
|
121
|
-
}
|
|
122
|
-
setEnabledSteps(next)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const resetState = useCallback(() => {
|
|
126
|
-
setPhase('prompt')
|
|
127
|
-
setPrompt('')
|
|
128
|
-
setTone('professional')
|
|
129
|
-
setEnabledSteps(new Set(['structure', 'content', 'seo', 'accessibility']))
|
|
130
|
-
setShowContext(false)
|
|
131
|
-
setBusiness('')
|
|
132
|
-
setIndustry('')
|
|
133
|
-
setPhone('')
|
|
134
|
-
setAddress('')
|
|
135
|
-
setServices('')
|
|
136
|
-
setCurrentStep('')
|
|
137
|
-
setCompletedSteps([])
|
|
138
|
-
setStepMessage('')
|
|
139
|
-
setResult(null)
|
|
140
|
-
setError(null)
|
|
141
|
-
setExpandedStep(null)
|
|
142
|
-
}, [])
|
|
143
|
-
|
|
144
|
-
const handleClose = useCallback(() => {
|
|
145
|
-
resetState()
|
|
146
|
-
onClose()
|
|
147
|
-
}, [resetState, onClose])
|
|
148
|
-
|
|
149
|
-
const handleGenerate = useCallback(async () => {
|
|
150
|
-
if (!prompt.trim()) {
|
|
151
|
-
toast.error('Please describe the page you want to create')
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
setPhase('generating')
|
|
156
|
-
setError(null)
|
|
157
|
-
|
|
158
|
-
const steps = ALL_STEPS.filter((s) => enabledSteps.has(s.key)).map((s) => s.key)
|
|
159
|
-
|
|
160
|
-
setCurrentStep(steps[0]!)
|
|
161
|
-
setStepMessage('Starting generation...')
|
|
162
|
-
|
|
163
|
-
const context: Record<string, unknown> = {}
|
|
164
|
-
if (businessName) context.businessName = businessName
|
|
165
|
-
if (industry) context.industry = industry
|
|
166
|
-
if (phone) context.phone = phone
|
|
167
|
-
if (address) context.address = address
|
|
168
|
-
if (services)
|
|
169
|
-
context.services = services
|
|
170
|
-
.split(',')
|
|
171
|
-
.map((s) => s.trim())
|
|
172
|
-
.filter(Boolean)
|
|
173
|
-
|
|
174
|
-
try {
|
|
175
|
-
const res = await cmsApi<GenerationResultData>('/page-builder/generate', {
|
|
176
|
-
method: 'POST',
|
|
177
|
-
body: JSON.stringify({
|
|
178
|
-
prompt: prompt.trim(),
|
|
179
|
-
steps,
|
|
180
|
-
tone,
|
|
181
|
-
context: Object.keys(context).length > 0 ? context : undefined,
|
|
182
|
-
}),
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
if (res.error) {
|
|
186
|
-
setError(res.error)
|
|
187
|
-
setPhase('prompt')
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (res.data) {
|
|
192
|
-
setResult(res.data)
|
|
193
|
-
setCompletedSteps(res.data.steps.map((s) => s.step))
|
|
194
|
-
setPhase('review')
|
|
195
|
-
}
|
|
196
|
-
} catch (err) {
|
|
197
|
-
setError(err instanceof Error ? err.message : 'Generation failed')
|
|
198
|
-
setPhase('prompt')
|
|
199
|
-
}
|
|
200
|
-
}, [prompt, tone, enabledSteps, businessName, industry, phone, address, services])
|
|
201
|
-
|
|
202
|
-
const handleAccept = useCallback(() => {
|
|
203
|
-
if (result?.tree) {
|
|
204
|
-
onAccept(result.tree)
|
|
205
|
-
toast.success('AI-generated page applied')
|
|
206
|
-
handleClose()
|
|
207
|
-
}
|
|
208
|
-
}, [result, onAccept, handleClose])
|
|
209
|
-
|
|
210
|
-
return (
|
|
211
|
-
<Dialog.Root open={open} onOpenChange={(next) => !next && handleClose()}>
|
|
212
|
-
<Dialog.Portal container={adminPortalContainer()}>
|
|
213
|
-
<Dialog.Overlay className="fixed inset-0 z-50 bg-black/50" />
|
|
214
|
-
<Dialog.Content className="bg-card border-border text-card-foreground fixed top-1/2 left-1/2 z-50 flex max-h-[85vh] w-[calc(100%-2rem)] max-w-2xl -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border shadow-2xl focus:outline-none">
|
|
215
|
-
{/* Header */}
|
|
216
|
-
<div className="border-border flex items-center justify-between border-b px-6 py-4">
|
|
217
|
-
<div className="flex items-center gap-2.5">
|
|
218
|
-
<div className="bg-primary/10 rounded-lg p-1.5">
|
|
219
|
-
<Sparkles size={18} className="text-primary" aria-hidden />
|
|
220
|
-
</div>
|
|
221
|
-
<div>
|
|
222
|
-
<Dialog.Title className="text-foreground text-base font-medium">
|
|
223
|
-
AI Page Generator
|
|
224
|
-
</Dialog.Title>
|
|
225
|
-
<Dialog.Description className="text-muted-foreground text-xs">
|
|
226
|
-
Describe your page and let AI build it
|
|
227
|
-
</Dialog.Description>
|
|
228
|
-
</div>
|
|
229
|
-
</div>
|
|
230
|
-
<Dialog.Close asChild>
|
|
231
|
-
<button
|
|
232
|
-
type="button"
|
|
233
|
-
className="hover:bg-muted text-muted-foreground hover:text-foreground focus-visible:ring-ring rounded-md p-1.5 transition-colors focus-visible:ring-2 focus-visible:outline-none"
|
|
234
|
-
aria-label="Close AI page generator"
|
|
235
|
-
>
|
|
236
|
-
<X size={16} aria-hidden />
|
|
237
|
-
</button>
|
|
238
|
-
</Dialog.Close>
|
|
239
|
-
</div>
|
|
240
|
-
|
|
241
|
-
{/* Content */}
|
|
242
|
-
<div className="flex-1 overflow-y-auto p-6">
|
|
243
|
-
{phase === 'prompt' && (
|
|
244
|
-
<div className="space-y-5">
|
|
245
|
-
{error && (
|
|
246
|
-
<div className="bg-destructive/5 border-destructive/20 flex items-start gap-2.5 rounded-lg border p-3">
|
|
247
|
-
<AlertTriangle size={16} className="text-destructive mt-0.5 shrink-0" />
|
|
248
|
-
<p className="text-destructive text-sm">{error}</p>
|
|
249
|
-
</div>
|
|
250
|
-
)}
|
|
251
|
-
|
|
252
|
-
{/* Prompt */}
|
|
253
|
-
<div>
|
|
254
|
-
<label className="text-foreground mb-1.5 block text-sm font-medium">
|
|
255
|
-
Describe your page
|
|
256
|
-
</label>
|
|
257
|
-
<textarea
|
|
258
|
-
value={prompt}
|
|
259
|
-
onChange={(e) => setPrompt(e.target.value)}
|
|
260
|
-
placeholder="e.g. A landing page for a plumbing company with a hero section, service cards, testimonials, and a contact form"
|
|
261
|
-
rows={4}
|
|
262
|
-
className="bg-background border-border focus:ring-primary/40 text-foreground placeholder:text-muted-foreground w-full resize-none rounded-lg border px-3 py-2.5 text-sm focus:ring-2 focus:outline-none"
|
|
263
|
-
autoFocus
|
|
264
|
-
/>
|
|
265
|
-
</div>
|
|
266
|
-
|
|
267
|
-
{/* Tone */}
|
|
268
|
-
<div>
|
|
269
|
-
<label className="text-foreground mb-1.5 block text-sm font-medium">Tone</label>
|
|
270
|
-
<div className="flex flex-wrap gap-2">
|
|
271
|
-
{TONES.map((t) => (
|
|
272
|
-
<button
|
|
273
|
-
key={t.value}
|
|
274
|
-
onClick={() => setTone(t.value)}
|
|
275
|
-
className={`rounded-full border px-3 py-1.5 text-xs transition-colors ${
|
|
276
|
-
tone === t.value
|
|
277
|
-
? 'border-primary bg-primary/10 text-primary'
|
|
278
|
-
: 'border-border bg-background text-muted-foreground hover:border-primary/40'
|
|
279
|
-
}`}
|
|
280
|
-
>
|
|
281
|
-
{t.label}
|
|
282
|
-
</button>
|
|
283
|
-
))}
|
|
284
|
-
</div>
|
|
285
|
-
</div>
|
|
286
|
-
|
|
287
|
-
{/* Steps */}
|
|
288
|
-
<div>
|
|
289
|
-
<label className="text-foreground mb-1.5 block text-sm font-medium">
|
|
290
|
-
Generation steps
|
|
291
|
-
</label>
|
|
292
|
-
<div className="space-y-1.5">
|
|
293
|
-
{ALL_STEPS.map((step) => (
|
|
294
|
-
<label
|
|
295
|
-
key={step.key}
|
|
296
|
-
className={`flex cursor-pointer items-center gap-3 rounded-lg border p-2.5 transition-colors ${
|
|
297
|
-
enabledSteps.has(step.key)
|
|
298
|
-
? 'border-primary/30 bg-primary/5'
|
|
299
|
-
: 'border-border bg-background'
|
|
300
|
-
} ${step.key === 'structure' ? 'cursor-not-allowed opacity-70' : 'hover:border-primary/20'}`}
|
|
301
|
-
>
|
|
302
|
-
<input
|
|
303
|
-
type="checkbox"
|
|
304
|
-
checked={enabledSteps.has(step.key)}
|
|
305
|
-
onChange={() => toggleStep(step.key)}
|
|
306
|
-
disabled={step.key === 'structure'}
|
|
307
|
-
className="sr-only"
|
|
308
|
-
/>
|
|
309
|
-
<div
|
|
310
|
-
className={`flex h-5 w-5 shrink-0 items-center justify-center rounded ${
|
|
311
|
-
enabledSteps.has(step.key)
|
|
312
|
-
? 'bg-primary text-primary-foreground'
|
|
313
|
-
: 'bg-muted border-border border'
|
|
314
|
-
}`}
|
|
315
|
-
>
|
|
316
|
-
{enabledSteps.has(step.key) && <Check size={12} />}
|
|
317
|
-
</div>
|
|
318
|
-
<div className="flex min-w-0 flex-1 items-center gap-2">
|
|
319
|
-
<span className="text-muted-foreground">{step.icon}</span>
|
|
320
|
-
<div>
|
|
321
|
-
<span className="text-foreground text-sm">{step.label}</span>
|
|
322
|
-
<span className="text-muted-foreground ml-2 text-xs">
|
|
323
|
-
{step.description}
|
|
324
|
-
</span>
|
|
325
|
-
</div>
|
|
326
|
-
</div>
|
|
327
|
-
</label>
|
|
328
|
-
))}
|
|
329
|
-
</div>
|
|
330
|
-
</div>
|
|
331
|
-
|
|
332
|
-
{/* Business context (collapsible) */}
|
|
333
|
-
<div>
|
|
334
|
-
<button
|
|
335
|
-
onClick={() => setShowContext(!showContext)}
|
|
336
|
-
className="text-muted-foreground hover:text-foreground flex items-center gap-1.5 text-sm transition-colors"
|
|
337
|
-
>
|
|
338
|
-
{showContext ? <ChevronUp size={14} /> : <ChevronDown size={14} />}
|
|
339
|
-
Business context (optional)
|
|
340
|
-
</button>
|
|
341
|
-
{showContext && (
|
|
342
|
-
<div className="bg-muted/50 border-border mt-3 space-y-3 rounded-lg border p-4">
|
|
343
|
-
<div className="grid grid-cols-2 gap-3">
|
|
344
|
-
<div>
|
|
345
|
-
<label className="text-muted-foreground mb-1 block text-xs">
|
|
346
|
-
Business name
|
|
347
|
-
</label>
|
|
348
|
-
<input
|
|
349
|
-
type="text"
|
|
350
|
-
value={businessName}
|
|
351
|
-
onChange={(e) => setBusiness(e.target.value)}
|
|
352
|
-
className="bg-background border-border focus:ring-primary/40 text-foreground w-full rounded-md border px-2.5 py-1.5 text-sm focus:ring-2 focus:outline-none"
|
|
353
|
-
/>
|
|
354
|
-
</div>
|
|
355
|
-
<div>
|
|
356
|
-
<label className="text-muted-foreground mb-1 block text-xs">
|
|
357
|
-
Industry
|
|
358
|
-
</label>
|
|
359
|
-
<input
|
|
360
|
-
type="text"
|
|
361
|
-
value={industry}
|
|
362
|
-
onChange={(e) => setIndustry(e.target.value)}
|
|
363
|
-
className="bg-background border-border focus:ring-primary/40 text-foreground w-full rounded-md border px-2.5 py-1.5 text-sm focus:ring-2 focus:outline-none"
|
|
364
|
-
/>
|
|
365
|
-
</div>
|
|
366
|
-
<div>
|
|
367
|
-
<label className="text-muted-foreground mb-1 block text-xs">Phone</label>
|
|
368
|
-
<input
|
|
369
|
-
type="text"
|
|
370
|
-
value={phone}
|
|
371
|
-
onChange={(e) => setPhone(e.target.value)}
|
|
372
|
-
className="bg-background border-border focus:ring-primary/40 text-foreground w-full rounded-md border px-2.5 py-1.5 text-sm focus:ring-2 focus:outline-none"
|
|
373
|
-
/>
|
|
374
|
-
</div>
|
|
375
|
-
<div>
|
|
376
|
-
<label className="text-muted-foreground mb-1 block text-xs">
|
|
377
|
-
Address
|
|
378
|
-
</label>
|
|
379
|
-
<input
|
|
380
|
-
type="text"
|
|
381
|
-
value={address}
|
|
382
|
-
onChange={(e) => setAddress(e.target.value)}
|
|
383
|
-
className="bg-background border-border focus:ring-primary/40 text-foreground w-full rounded-md border px-2.5 py-1.5 text-sm focus:ring-2 focus:outline-none"
|
|
384
|
-
/>
|
|
385
|
-
</div>
|
|
386
|
-
</div>
|
|
387
|
-
<div>
|
|
388
|
-
<label className="text-muted-foreground mb-1 block text-xs">
|
|
389
|
-
Services (comma-separated)
|
|
390
|
-
</label>
|
|
391
|
-
<input
|
|
392
|
-
type="text"
|
|
393
|
-
value={services}
|
|
394
|
-
onChange={(e) => setServices(e.target.value)}
|
|
395
|
-
placeholder="e.g. Web design, SEO, Content marketing"
|
|
396
|
-
className="bg-background border-border focus:ring-primary/40 text-foreground placeholder:text-muted-foreground w-full rounded-md border px-2.5 py-1.5 text-sm focus:ring-2 focus:outline-none"
|
|
397
|
-
/>
|
|
398
|
-
</div>
|
|
399
|
-
</div>
|
|
400
|
-
)}
|
|
401
|
-
</div>
|
|
402
|
-
</div>
|
|
403
|
-
)}
|
|
404
|
-
|
|
405
|
-
{phase === 'generating' && (
|
|
406
|
-
<div className="space-y-6 py-8">
|
|
407
|
-
<div className="text-center">
|
|
408
|
-
<Loader2 size={32} className="text-primary mx-auto mb-3 animate-spin" />
|
|
409
|
-
<p className="text-foreground text-sm font-medium">
|
|
410
|
-
{stepMessage || 'Generating...'}
|
|
411
|
-
</p>
|
|
412
|
-
<p className="text-muted-foreground mt-1 text-xs">This may take 15-30 seconds</p>
|
|
413
|
-
</div>
|
|
414
|
-
|
|
415
|
-
<div className="space-y-2">
|
|
416
|
-
{ALL_STEPS.filter((s) => enabledSteps.has(s.key)).map((step) => {
|
|
417
|
-
const isComplete = completedSteps.includes(step.key)
|
|
418
|
-
const isCurrent = currentStep === step.key
|
|
419
|
-
|
|
420
|
-
return (
|
|
421
|
-
<div
|
|
422
|
-
key={step.key}
|
|
423
|
-
className={`flex items-center gap-3 rounded-lg border p-3 transition-colors ${
|
|
424
|
-
isComplete
|
|
425
|
-
? 'border-primary/30 bg-primary/5'
|
|
426
|
-
: isCurrent
|
|
427
|
-
? 'border-primary/50 bg-primary/10'
|
|
428
|
-
: 'border-border bg-muted/30'
|
|
429
|
-
}`}
|
|
430
|
-
>
|
|
431
|
-
<div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full">
|
|
432
|
-
{isComplete ? (
|
|
433
|
-
<div className="bg-primary flex h-6 w-6 items-center justify-center rounded-full">
|
|
434
|
-
<Check size={12} className="text-primary-foreground" />
|
|
435
|
-
</div>
|
|
436
|
-
) : isCurrent ? (
|
|
437
|
-
<Loader2 size={16} className="text-primary animate-spin" />
|
|
438
|
-
) : (
|
|
439
|
-
<div className="bg-muted border-border h-6 w-6 rounded-full border" />
|
|
440
|
-
)}
|
|
441
|
-
</div>
|
|
442
|
-
<div className="flex items-center gap-2">
|
|
443
|
-
<span className="text-muted-foreground">{step.icon}</span>
|
|
444
|
-
<span
|
|
445
|
-
className={`text-sm ${isCurrent || isComplete ? 'text-foreground' : 'text-muted-foreground'}`}
|
|
446
|
-
>
|
|
447
|
-
{step.label}
|
|
448
|
-
</span>
|
|
449
|
-
</div>
|
|
450
|
-
</div>
|
|
451
|
-
)
|
|
452
|
-
})}
|
|
453
|
-
</div>
|
|
454
|
-
</div>
|
|
455
|
-
)}
|
|
456
|
-
|
|
457
|
-
{phase === 'review' && result && (
|
|
458
|
-
<div className="space-y-5">
|
|
459
|
-
{/* Summary */}
|
|
460
|
-
<div className="bg-primary/5 border-primary/20 flex items-center gap-3 rounded-lg border p-4">
|
|
461
|
-
<div className="bg-primary/10 flex h-10 w-10 shrink-0 items-center justify-center rounded-full">
|
|
462
|
-
<Sparkles size={18} className="text-primary" />
|
|
463
|
-
</div>
|
|
464
|
-
<div>
|
|
465
|
-
<p className="text-foreground text-sm font-medium">
|
|
466
|
-
Page generated successfully
|
|
467
|
-
</p>
|
|
468
|
-
<p className="text-muted-foreground mt-0.5 text-xs">
|
|
469
|
-
{result.totalTokensUsed.toLocaleString()} tokens
|
|
470
|
-
{' \u00B7 '}
|
|
471
|
-
{(result.totalDurationMs / 1000).toFixed(1)}s{' \u00B7 '}
|
|
472
|
-
{result.steps.length} step{result.steps.length !== 1 ? 's' : ''}
|
|
473
|
-
</p>
|
|
474
|
-
</div>
|
|
475
|
-
</div>
|
|
476
|
-
|
|
477
|
-
{/* Step details */}
|
|
478
|
-
<div className="space-y-2">
|
|
479
|
-
<h3 className="text-foreground text-sm font-medium">Generation steps</h3>
|
|
480
|
-
{result.steps.map((step) => {
|
|
481
|
-
const info = ALL_STEPS.find((s) => s.key === step.step)
|
|
482
|
-
const isExpanded = expandedStep === step.step
|
|
483
|
-
|
|
484
|
-
return (
|
|
485
|
-
<div
|
|
486
|
-
key={step.step}
|
|
487
|
-
className="border-border overflow-hidden rounded-lg border"
|
|
488
|
-
>
|
|
489
|
-
<button
|
|
490
|
-
onClick={() => setExpandedStep(isExpanded ? null : step.step)}
|
|
491
|
-
className="hover:bg-muted/50 flex w-full items-center gap-3 p-3 text-left transition-colors"
|
|
492
|
-
>
|
|
493
|
-
<div className="bg-primary flex h-6 w-6 shrink-0 items-center justify-center rounded-full">
|
|
494
|
-
<Check size={12} className="text-primary-foreground" />
|
|
495
|
-
</div>
|
|
496
|
-
<span className="text-muted-foreground">{info?.icon}</span>
|
|
497
|
-
<span className="text-foreground flex-1 text-sm">{info?.label}</span>
|
|
498
|
-
<span className="text-muted-foreground text-xs">
|
|
499
|
-
{(step.metadata.durationMs / 1000).toFixed(1)}s
|
|
500
|
-
</span>
|
|
501
|
-
{isExpanded ? (
|
|
502
|
-
<ChevronUp size={14} className="text-muted-foreground" />
|
|
503
|
-
) : (
|
|
504
|
-
<ChevronDown size={14} className="text-muted-foreground" />
|
|
505
|
-
)}
|
|
506
|
-
</button>
|
|
507
|
-
{isExpanded && (
|
|
508
|
-
<div className="border-border border-t px-3 pt-2 pb-3">
|
|
509
|
-
<ul className="space-y-1">
|
|
510
|
-
{step.metadata.changes.map((change, i) => (
|
|
511
|
-
<li
|
|
512
|
-
key={i}
|
|
513
|
-
className="text-muted-foreground flex items-start gap-2 text-xs"
|
|
514
|
-
>
|
|
515
|
-
<ArrowRight size={10} className="mt-0.5 shrink-0" />
|
|
516
|
-
{change}
|
|
517
|
-
</li>
|
|
518
|
-
))}
|
|
519
|
-
</ul>
|
|
520
|
-
<p className="text-muted-foreground mt-2 text-xs">
|
|
521
|
-
Tokens: {step.metadata.tokensUsed.toLocaleString()}
|
|
522
|
-
</p>
|
|
523
|
-
</div>
|
|
524
|
-
)}
|
|
525
|
-
</div>
|
|
526
|
-
)
|
|
527
|
-
})}
|
|
528
|
-
</div>
|
|
529
|
-
|
|
530
|
-
<p className="text-muted-foreground text-xs">
|
|
531
|
-
Review the generated page in the canvas after accepting. You can always undo to
|
|
532
|
-
revert.
|
|
533
|
-
</p>
|
|
534
|
-
</div>
|
|
535
|
-
)}
|
|
536
|
-
</div>
|
|
537
|
-
|
|
538
|
-
{/* Footer */}
|
|
539
|
-
<div className="border-border flex items-center justify-end gap-3 border-t px-6 py-4">
|
|
540
|
-
{phase === 'prompt' && (
|
|
541
|
-
<>
|
|
542
|
-
<button
|
|
543
|
-
onClick={handleClose}
|
|
544
|
-
className="text-muted-foreground hover:text-foreground px-4 py-2 text-sm transition-colors"
|
|
545
|
-
>
|
|
546
|
-
Cancel
|
|
547
|
-
</button>
|
|
548
|
-
<button
|
|
549
|
-
onClick={handleGenerate}
|
|
550
|
-
disabled={!prompt.trim()}
|
|
551
|
-
className="bg-primary text-primary-foreground hover:bg-primary/90 flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors disabled:cursor-not-allowed disabled:opacity-50"
|
|
552
|
-
>
|
|
553
|
-
<Sparkles size={14} />
|
|
554
|
-
Generate Page
|
|
555
|
-
</button>
|
|
556
|
-
</>
|
|
557
|
-
)}
|
|
558
|
-
|
|
559
|
-
{phase === 'generating' && (
|
|
560
|
-
<button
|
|
561
|
-
onClick={handleClose}
|
|
562
|
-
className="text-muted-foreground hover:text-foreground px-4 py-2 text-sm transition-colors"
|
|
563
|
-
>
|
|
564
|
-
Cancel
|
|
565
|
-
</button>
|
|
566
|
-
)}
|
|
567
|
-
|
|
568
|
-
{phase === 'review' && (
|
|
569
|
-
<>
|
|
570
|
-
<button
|
|
571
|
-
onClick={() => {
|
|
572
|
-
setPhase('prompt')
|
|
573
|
-
setResult(null)
|
|
574
|
-
}}
|
|
575
|
-
className="text-muted-foreground hover:text-foreground px-4 py-2 text-sm transition-colors"
|
|
576
|
-
>
|
|
577
|
-
Regenerate
|
|
578
|
-
</button>
|
|
579
|
-
<button
|
|
580
|
-
onClick={handleAccept}
|
|
581
|
-
className="bg-primary text-primary-foreground hover:bg-primary/90 flex items-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors"
|
|
582
|
-
>
|
|
583
|
-
<Check size={14} />
|
|
584
|
-
Accept & Apply
|
|
585
|
-
</button>
|
|
586
|
-
</>
|
|
587
|
-
)}
|
|
588
|
-
</div>
|
|
589
|
-
</Dialog.Content>
|
|
590
|
-
</Dialog.Portal>
|
|
591
|
-
</Dialog.Root>
|
|
592
|
-
)
|
|
593
|
-
}
|