// pages/admin/LessonPageBuilder.jsx import { useEffect, useRef, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { ArrowLeft, Save, Pencil, Monitor, Eye, EyeOff, X } from "lucide-react"; import { nanoid } from "nanoid"; import { useCourses } from "@/contexts/AdminCoursesContext"; import { useAuth } from "@/contexts/AuthContext"; import { PageMeta } from "@/contexts/MetadataContext"; import { Button } from "@/components/ui/button"; import { Spinner } from "@/components/ui/spinner"; import { BlockList, DEFAULT_CONTENT } from "@/components/generic/BlockList"; import { AddBlockMenu } from "@/components/generic/AddBlockMenu"; import { cn } from "@/lib/utils"; import { PreviewContent, PreviewChrome } from "../../../components/courses/LessonsPreview"; function makeBlock(type) { return { id: nanoid(), type, content: { ...DEFAULT_CONTENT[type] }, }; } export default function LessonPageBuilder() { const navigate = useNavigate(); const { courseId, unitId, lessonId } = useParams(); const { fetchLesson, saveLessonPage, course, unit, lesson, lessonPage, loading } = useCourses(); const { user } = useAuth(); const [blocks, setBlocks] = useState([]); const [initializing, setInitializing] = useState(true); const [previewVisible, setPreviewVisible] = useState(true); const [previewOpen, setPreviewOpen] = useState(false); const headerRef = useRef(null); const editorPaneRef = useRef(null); const blocksSeeded = useRef(false); useEffect(() => { // The builder stays mounted across lesson navigations (route param change // only), so local editor state must be reset by hand here — otherwise the // previous lesson's blocks leak into the next lesson's editor. (async () => { setInitializing(true); setBlocks([]); blocksSeeded.current = false; await fetchLesson(courseId, unitId, lessonId); setInitializing(false); })(); }, [courseId, unitId, lessonId]); useEffect(() => { if (lessonPage?.blocks?.length && !blocksSeeded.current) { setBlocks(lessonPage.blocks); blocksSeeded.current = true; } }, [lessonPage]); useEffect(() => { if (!headerRef.current) return; const update = () => { document.documentElement.style.setProperty( "--builder-h", `${headerRef.current.offsetHeight}px` ); }; update(); window.addEventListener("resize", update); return () => window.removeEventListener("resize", update); }, []); useEffect(() => { const el = editorPaneRef.current; if (!el) return; const applyHeight = () => { if (window.innerWidth >= 1024 && previewVisible) { el.style.height = `calc(100vh - var(--navbar-h) - var(--builder-h, 0px))`; } else { el.style.height = "auto"; } }; applyHeight(); window.addEventListener("resize", applyHeight); return () => window.removeEventListener("resize", applyHeight); }, [previewVisible]); const addBlock = (type) => setBlocks((prev) => [...prev, makeBlock(type)]); const updateBlock = (id, content) => setBlocks((prev) => prev.map((b) => b.id === id ? { ...b, content } : b)); const deleteBlock = (id) => setBlocks((prev) => prev.filter((b) => b.id !== id)); const moveBlock = (id, direction) => { setBlocks((prev) => { const index = prev.findIndex((b) => b.id === id); const swapIndex = direction === "up" ? index - 1 : index + 1; if (swapIndex < 0 || swapIndex >= prev.length) return prev; const next = [...prev]; [next[index], next[swapIndex]] = [next[swapIndex], next[index]]; return next; }); }; const handleSave = async () => { const result = await saveLessonPage(courseId, unitId, lessonId, { blocks, updatedBy: user?.user_id, }); if (!result) return; navigate(-1); }; const editorStyle = previewVisible ? { top: "calc(var(--navbar-h) + var(--builder-h, 0px))" } : undefined; return (
{lesson?.title}