"use client"; import { useEffect, useState } from "react"; import { cmsApi, Lesson, Block, GradingCategory } from "@/lib/api"; import Link from "next/link"; import DescriptionBlock from "@/components/blocks/DescriptionBlock"; import MediaBlock from "@/components/blocks/MediaBlock"; import QuizBlock from "@/components/blocks/QuizBlock"; import FillInTheBlanksBlock from "@/components/blocks/FillInTheBlanksBlock"; import MatchingBlock from "@/components/blocks/MatchingBlock"; import OrderingBlock from "@/components/blocks/OrderingBlock"; import ShortAnswerBlock from "@/components/blocks/ShortAnswerBlock"; import DocumentBlock from "@/components/blocks/DocumentBlock"; import VideoMarkerBlock from "@/components/blocks/VideoMarkerBlock"; import { Save, X, Pencil, ChevronUp, ChevronDown, Trash2 } from "lucide-react"; export default function LessonEditor({ params }: { params: { id: string; lessonId: string } }) { const [lesson, setLesson] = useState(null); const [loading, setLoading] = useState(true); const [isSaving, setIsSaving] = useState(false); const [editMode, setEditMode] = useState(false); const [editingId, setEditingId] = useState(null); // Activity State (Blocks) const [blocks, setBlocks] = useState([]); const [summary, setSummary] = useState(""); const [isGeneratingSummary, setIsGeneratingSummary] = useState(false); const [isGeneratingQuiz, setIsGeneratingQuiz] = useState(false); const [gradingCategories, setGradingCategories] = useState([]); const [isGraded, setIsGraded] = useState(false); const [selectedCategoryId, setSelectedCategoryId] = useState(""); const [maxAttempts, setMaxAttempts] = useState(null); const [allowRetry, setAllowRetry] = useState(true); const [dueDate, setDueDate] = useState(""); const [importantDateType, setImportantDateType] = useState(""); const [editValue, setEditValue] = useState(""); // Polling for AI status useEffect(() => { let interval: NodeJS.Timeout; if (lesson && (lesson.transcription_status === 'queued' || lesson.transcription_status === 'processing')) { interval = setInterval(async () => { try { const updated = await cmsApi.getLesson(params.lessonId); setLesson(updated); // If it finished, update local states if (updated.transcription_status === 'completed') { if (updated.transcription) { // Automatically update summary if available? No, wait for manual trigger or auto-trigger? // For now just update lesson } } } catch (err) { console.error("Polling failed", err); } }, 3000); } return () => { if (interval) clearInterval(interval); }; }, [lesson, lesson?.transcription_status, params.lessonId]); useEffect(() => { const loadData = async () => { try { // Use cmsApi for consistency const lessonData = await cmsApi.getLesson(params.lessonId); setLesson(lessonData); setSummary(lessonData.summary || ""); setIsGraded(lessonData.is_graded || false); setSelectedCategoryId(lessonData.grading_category_id || ""); setMaxAttempts(lessonData.max_attempts); setAllowRetry(lessonData.allow_retry); setDueDate(lessonData.due_date ? new Date(lessonData.due_date).toISOString().split('T')[0] : ""); setImportantDateType(lessonData.important_date_type || ""); if (lessonData.metadata?.blocks) { setBlocks(lessonData.metadata.blocks); } else { setBlocks([ { id: 'initial-desc', type: 'description', content: `Welcome to ${lessonData.title}. Please follow the instructions below.` } ]); } // Load grading categories const categories = await cmsApi.getGradingCategories(params.id); setGradingCategories(categories); } catch { console.error("Failed to load lesson or categories"); } finally { setLoading(false); } }; loadData(); }, [params.id, params.lessonId]); const handleSaveLessonTitle = async () => { if (!lesson || !editValue) return; try { const updated = await cmsApi.updateLesson(lesson.id, { title: editValue }); setLesson(updated); setEditingId(null); } catch { alert("Failed to update title"); } }; const handleSave = async () => { if (!lesson) return; setIsSaving(true); try { // Sync content_url for video/audio lessons from the first media block let content_url = lesson.content_url; if (lesson.content_type === 'video' || lesson.content_type === 'audio') { const mediaBlock = blocks.find(b => b.type === 'media'); if (mediaBlock && mediaBlock.url) { content_url = mediaBlock.url; } } const updated = await cmsApi.updateLesson(lesson.id, { metadata: { ...lesson.metadata, blocks }, content_url, summary, is_graded: isGraded, grading_category_id: selectedCategoryId || null, max_attempts: maxAttempts, allow_retry: allowRetry, due_date: dueDate ? new Date(dueDate).toISOString() : undefined, important_date_type: (importantDateType || undefined) as 'exam' | 'assignment' | 'milestone' | 'live-session' | undefined }); setLesson(updated); setEditMode(false); } catch { alert("Failed to save activity."); } finally { setIsSaving(false); } }; const addBlock = (type: 'description' | 'media' | 'quiz' | 'fill-in-the-blanks' | 'matching' | 'ordering' | 'short-answer' | 'document' | 'video_marker') => { const newBlock: Block = { id: Math.random().toString(36).substr(2, 9), type, ...(type === 'description' && { content: "" }), ...(type === 'media' && { url: "", media_type: 'video' as const, config: { maxPlays: 0 } }), ...(type === 'quiz' && { quiz_data: { questions: [] } }), ...(type === 'fill-in-the-blanks' && { content: "Type your text here with [[blanks]]." }), ...(type === 'matching' && { pairs: [{ left: "Item 1", right: "Match 1" }] }), ...(type === 'ordering' && { items: ["Item A", "Item B"] }), ...(type === 'short-answer' && { prompt: "Question?", correctAnswers: ["Answer"] }), ...(type === 'document' && { url: "", title: "" }), ...(type === 'video_marker' && { url: "", title: "Video Interactivo", markers: [] }), }; setBlocks([...blocks, newBlock]); }; const removeBlock = (id: string) => { setBlocks(blocks.filter(b => b.id !== id)); }; const updateBlock = (id: string, updates: Partial) => { setBlocks(blocks.map(b => b.id === id ? { ...b, ...updates } : b)); }; const moveBlock = (index: number, direction: 'up' | 'down') => { const newBlocks = [...blocks]; const targetIndex = direction === 'up' ? index - 1 : index + 1; if (targetIndex < 0 || targetIndex >= newBlocks.length) return; [newBlocks[index], newBlocks[targetIndex]] = [newBlocks[targetIndex], newBlocks[index]]; setBlocks(newBlocks); }; const handleSummarize = async () => { if (!lesson) return; setIsGeneratingSummary(true); try { const updated = await cmsApi.summarizeLesson(lesson.id); setSummary(updated.summary || ""); } catch { alert("Failed to generate summary."); } finally { setIsGeneratingSummary(false); } }; const handleGenerateQuiz = async () => { if (!lesson) return; setIsGeneratingQuiz(true); try { const newBlocks = await cmsApi.generateQuiz(lesson.id); setBlocks([...blocks, ...newBlocks]); } catch { alert("Failed to generate quiz."); } finally { setIsGeneratingQuiz(false); } }; if (loading) return
Initializing Activity Builder...
; if (!lesson) return
Activity not found.
; return (
Outline / Activity
{editingId === 'lesson-title' ? (
setEditValue(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSaveLessonTitle()} className="text-4xl font-black bg-transparent border-b-2 border-blue-500 focus:outline-none" />
) : (

{lesson.title}

)}
{editMode ? ( <> ) : ( )}
{editMode && (

⚖️ Grading Configuration

Determine if this activity contributes to the final grade

{isGraded && ( <>
Assessment Category
Manage categories in Grading Policy
After Submission

Enables "Check Answer" buttons for individual blocks

)}
)} { editMode && (

📅 Scheduling & Deadlines

Set deadlines and mark important dates for this activity

) } {/* AI Magic Section */} { editMode && (
🪄

AI Content Assistant

Automate your content creation with Local AI

) } {/* AI Summary Visualization */} { (summary || editMode) && (

AI Lesson Summary

Key insights generated from content

{editMode ? (