Skip to content

Commit 714b2fc

Browse files
committed
Add editable fields and publish button to AI Leela generator
1 parent 631d25d commit 714b2fc

1 file changed

Lines changed: 147 additions & 41 deletions

File tree

  • web/src/app/admin/leela/generate

web/src/app/admin/leela/generate/page.tsx

Lines changed: 147 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,43 @@
11
'use client';
22

33
import { 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

68
interface 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

1823
export 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

Comments
 (0)