11'use client' ;
22
33import { useState } from 'react' ;
4- import { Sparkles , Loader2 , Copy , Check , AlertCircle , Youtube } from 'lucide-react' ;
4+ import { Sparkles , Loader2 , Copy , Check , AlertCircle , Youtube , Send } from 'lucide-react' ;
5+ import { saveLeela } from '@/actions/content' ;
6+ import { useRouter } from 'next/navigation' ;
57
68interface GeneratedContent {
79 is_suitable ?: boolean ;
810 quality_score ?: number ;
911 rejection_reason ?: string ;
1012 rejected ?: boolean ;
1113 reason ?: string ;
14+ suggested_title ?: string ;
1215 story ?: string ;
1316 doubt ?: string ;
1417 revelation ?: string ;
1518 scriptural_refs ?: string ;
19+ keywords ?: string [ ] ;
20+ social_tags ?: string [ ] ;
1621}
1722
1823export default function GenerateLeelaPage ( ) {
24+ const router = useRouter ( ) ;
1925 const [ youtubeId , setYoutubeId ] = useState ( '' ) ;
2026 const [ title , setTitle ] = useState ( '' ) ;
2127 const [ transcript , setTranscript ] = useState ( '' ) ;
2228 const [ isGenerating , setIsGenerating ] = useState ( false ) ;
29+ const [ isPublishing , setIsPublishing ] = useState ( false ) ;
2330 const [ error , setError ] = useState < string | null > ( null ) ;
2431 const [ content , setContent ] = useState < GeneratedContent | null > ( null ) ;
2532 const [ copied , setCopied ] = useState ( false ) ;
2633
34+ // Editable fields
35+ const [ editableTitle , setEditableTitle ] = useState ( '' ) ;
36+ const [ editableStory , setEditableStory ] = useState ( '' ) ;
37+ const [ editableDoubt , setEditableDoubt ] = useState ( '' ) ;
38+ const [ editableRevelation , setEditableRevelation ] = useState ( '' ) ;
39+ const [ editableScripturalRefs , setEditableScripturalRefs ] = useState ( '' ) ;
40+
2741 const handleGenerate = async ( ) => {
2842 if ( ! transcript . trim ( ) || transcript . trim ( ) . length < 100 ) {
2943 setError ( 'Please provide a transcript (minimum 100 characters)' ) ;
@@ -52,13 +66,52 @@ export default function GenerateLeelaPage() {
5266 }
5367
5468 setContent ( data ) ;
69+
70+ // Set editable fields
71+ if ( ! data . rejected ) {
72+ setEditableTitle ( data . suggested_title || title || '' ) ;
73+ setEditableStory ( data . story || '' ) ;
74+ setEditableDoubt ( data . doubt || '' ) ;
75+ setEditableRevelation ( data . revelation || '' ) ;
76+ setEditableScripturalRefs ( data . scriptural_refs || '' ) ;
77+ }
5578 } catch ( err : any ) {
5679 setError ( err . message || 'An error occurred' ) ;
5780 } finally {
5881 setIsGenerating ( false ) ;
5982 }
6083 } ;
6184
85+ const handlePublish = async ( ) => {
86+ if ( ! content || content . rejected ) return ;
87+
88+ setIsPublishing ( true ) ;
89+ setError ( null ) ;
90+
91+ try {
92+ await saveLeela ( {
93+ title_english : editableTitle ,
94+ youtube_id : youtubeId ,
95+ transcript : transcript ,
96+ story : editableStory ,
97+ doubt : editableDoubt || null ,
98+ revelation : editableRevelation || null ,
99+ scriptural_refs : editableScripturalRefs || null ,
100+ description : editableStory . substring ( 0 , 200 ) + '...' ,
101+ keywords : content . keywords || [ ] ,
102+ social_tags : content . social_tags || [ ] ,
103+ orderId : 0 // Will be set by admin later
104+ } ) ;
105+
106+ // Redirect to admin leela list
107+ router . push ( '/admin/leela' ) ;
108+ } catch ( err : any ) {
109+ setError ( err . message || 'Failed to publish' ) ;
110+ } finally {
111+ setIsPublishing ( false ) ;
112+ }
113+ } ;
114+
62115 const handleCopy = ( ) => {
63116 if ( content ) {
64117 navigator . clipboard . writeText ( JSON . stringify ( content , null , 2 ) ) ;
@@ -187,66 +240,104 @@ export default function GenerateLeelaPage() {
187240
188241 { /* Action Bar (Only for successful results) */ }
189242 { ! content . rejected && (
190- < div className = "flex items-center justify-between p-4 bg-green-50 border border-green-200 rounded-xl text-green-700 " >
191- < div className = "flex items-center gap-2" >
192- < Check className = "w-5 h-5" />
243+ < div className = "flex flex-col md:flex-row items-stretch md:items- center gap-3 p-4 bg-green-50 border border-green-200 rounded-xl" >
244+ < div className = "flex items-center gap-2 text-green-700 flex-1 " >
245+ < Check className = "w-5 h-5 flex-shrink-0 " />
193246 < div className = "flex flex-col" >
194247 < p className = "font-semibold leading-none" > Content generated successfully!</ p >
195- < span className = "text-[10px] font-bold uppercase tracking-widest mt-1 opacity-70" > Quality Score: { content . quality_score } /10</ span >
248+ < span className = "text-[10px] font-bold uppercase tracking-widest mt-1 opacity-70" > Quality Score: { content . quality_score } /10</ span >
196249 </ div >
197250 </ div >
198- < button
199- onClick = { handleCopy }
200- className = "px-4 py-2 bg-green-100 text-green-800 font-medium rounded-lg hover:bg-green-200 transition-all flex items-center gap-2"
201- >
202- { copied ? (
203- < >
204- < Check className = "w-4 h-4" />
205- Copied!
206- </ >
207- ) : (
208- < >
209- < Copy className = "w-4 h-4" />
210- Copy JSON
211- </ >
212- ) }
213- </ button >
251+ < div className = "flex gap-2" >
252+ < button
253+ onClick = { handleCopy }
254+ className = "px-4 py-2 bg-green-100 text-green-800 font-medium rounded-lg hover:bg-green-200 transition-all flex items-center gap-2"
255+ >
256+ { copied ? (
257+ < >
258+ < Check className = "w-4 h-4" />
259+ Copied!
260+ </ >
261+ ) : (
262+ < >
263+ < Copy className = "w-4 h-4" />
264+ Copy JSON
265+ </ >
266+ ) }
267+ </ button >
268+ < button
269+ onClick = { handlePublish }
270+ disabled = { isPublishing }
271+ className = "px-6 py-2 bg-ochre text-white font-bold rounded-lg hover:bg-ochre/90 disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center gap-2"
272+ >
273+ { isPublishing ? (
274+ < >
275+ < Loader2 className = "w-4 h-4 animate-spin" />
276+ Publishing...
277+ </ >
278+ ) : (
279+ < >
280+ < Send className = "w-4 h-4" />
281+ Publish
282+ </ >
283+ ) }
284+ </ button >
285+ </ div >
214286 </ div >
215287 ) }
216288
217- { /* The Story */ }
218- { ! content . rejected && content . story && (
219- < ContentSection
289+ { /* Editable Title */ }
290+ { ! content . rejected && (
291+ < EditableSection
292+ title = "📝 Title"
293+ value = { editableTitle }
294+ onChange = { setEditableTitle }
295+ bgColor = "bg-blue-50"
296+ rows = { 2 }
297+ />
298+ ) }
299+
300+ { /* Editable Story */ }
301+ { ! content . rejected && editableStory && (
302+ < EditableSection
220303 title = "📖 The Story"
221- content = { content . story }
304+ value = { editableStory }
305+ onChange = { setEditableStory }
222306 bgColor = "bg-white"
307+ rows = { 10 }
223308 />
224309 ) }
225310
226- { /* The Doubt */ }
227- { ! content . rejected && content . doubt && (
228- < ContentSection
311+ { /* Editable Doubt */ }
312+ { ! content . rejected && editableDoubt && (
313+ < EditableSection
229314 title = "❓ The Conflict / Doubt"
230- content = { content . doubt }
315+ value = { editableDoubt }
316+ onChange = { setEditableDoubt }
231317 bgColor = "bg-orange-50"
318+ rows = { 6 }
232319 />
233320 ) }
234321
235- { /* The Revelation */ }
236- { ! content . rejected && content . revelation && (
237- < ContentSection
322+ { /* Editable Revelation */ }
323+ { ! content . rejected && editableRevelation && (
324+ < EditableSection
238325 title = "💡 The Revelation"
239- content = { content . revelation }
326+ value = { editableRevelation }
327+ onChange = { setEditableRevelation }
240328 bgColor = "bg-amber-50"
329+ rows = { 8 }
241330 />
242331 ) }
243332
244- { /* Scriptural References */ }
245- { ! content . rejected && content . scriptural_refs && (
246- < ContentSection
333+ { /* Editable Scriptural References */ }
334+ { ! content . rejected && editableScripturalRefs && (
335+ < EditableSection
247336 title = "📜 Scriptural References"
248- content = { content . scriptural_refs }
337+ value = { editableScripturalRefs }
338+ onChange = { setEditableScripturalRefs }
249339 bgColor = "bg-gray-50"
340+ rows = { 3 }
250341 />
251342 ) }
252343
@@ -268,13 +359,28 @@ export default function GenerateLeelaPage() {
268359 ) ;
269360}
270361
271- function ContentSection ( { title, content, bgColor } : { title : string ; content : string ; bgColor : string } ) {
362+ function EditableSection ( {
363+ title,
364+ value,
365+ onChange,
366+ bgColor,
367+ rows = 6
368+ } : {
369+ title : string ;
370+ value : string ;
371+ onChange : ( value : string ) => void ;
372+ bgColor : string ;
373+ rows ?: number ;
374+ } ) {
272375 return (
273376 < div className = { `${ bgColor } rounded-2xl border border-gray-100 p-6 space-y-3` } >
274377 < h2 className = "text-xl font-bold text-gray-800" > { title } </ h2 >
275- < div className = "prose prose-ochre max-w-none text-gray-700 whitespace-pre-wrap" >
276- { content }
277- </ div >
378+ < textarea
379+ value = { value }
380+ onChange = { ( e ) => onChange ( e . target . value ) }
381+ rows = { rows }
382+ className = "w-full px-4 py-3 border border-gray-200 rounded-xl focus:ring-2 focus:ring-ochre focus:border-ochre transition-all resize-y bg-white"
383+ />
278384 </ div >
279385 ) ;
280386}
0 commit comments