import React, { useRef, useEffect } from 'react'; import type { Lesson, ContentBlock, Course, CurrentUser, Exam } from '../types'; import Icon from './Icon'; import ChartRenderer from './ChartRenderer'; declare global { interface Window { MathJax?: { typesetPromise: (args?: any[]) => Promise; }; Plotly: any; lightbox: any; } } interface LessonDisplayProps { lesson: Lesson; course?: Course; onUpdateCourse?: (course: Course) => void; currentUser?: CurrentUser | null; onDelete?: () => void; onTakeExam?: (exam: Exam) => void; } function LessonDisplay({ lesson }: LessonDisplayProps): JSX.Element { const lessonRef = useRef(null); useEffect(() => { if (lessonRef.current && window.MathJax) { window.MathJax.typesetPromise(); } // Re-initialize Lightbox for dynamically added content if (window.lightbox) { window.lightbox.init(); } }, [lesson]); const renderProcessedText = (text: string) => { if (!text) return null; // Split by backticks, capturing the content inside. const parts = text.split(/`([^`]+)`/); return parts.map((part, index) => { if (index % 2 === 1) { // This is the content from inside backticks return {'\\('}{part}{'\\)'}; } // This is regular text. React will safely escape it. return {part}; }); }; return (

{lesson.title}

{/* Learning Objectives & Criteria */}

Objetivos de Aprendizaje

    {(lesson.learningObjectives || []).map((obj, i) =>
  • {obj}
  • )}

Criterios de Evaluación

    {(lesson.evaluationCriteria || []).map((crit, i) =>
  • {crit}
  • )}
{lesson.sections.map((section, sectionIndex) => (

{section.title}

{section.content.map((block: ContentBlock, blockIndex: number) => { const key = `${sectionIndex}-${blockIndex}`; switch (block.type) { case 'paragraph': { if (!block.text) return null; // Split text by double newlines to render separate paragraphs. const paragraphs = block.text.split(/\n\s*\n/).filter(p => p.trim()); return ( {paragraphs.map((p, i) => (

{renderProcessedText(p)}

))}
); } case 'image_placeholder': if (lesson.images && typeof block.index === 'number' && lesson.images[block.index]) { const imageUrl = `data:image/jpeg;base64,${lesson.images[block.index]}`; return ( ); } return null; case 'table': if (!block.markdown) return null; const tableRows = block.markdown.trim().split(/\r?\n|\r/).filter(row => row.trim() && row.includes('|')); if (tableRows.length < 2 || !tableRows[1].includes('---')) return null; const parseRow = (rowStr: string) => { // Trim the row, remove optional leading/trailing pipes, then split. const cleanedRow = rowStr.trim().replace(/^\||\|$/g, '').trim(); return cleanedRow.split('|').map(cell => cell.trim()); }; const headers = parseRow(tableRows[0]); const body = tableRows.slice(2).map(row => parseRow(row)); return (
{headers.map((h: string, i: number) => )} {body.map((row: string[], rIndex: number) => ( {row.map((cell, cIndex) => cIndex === 0 ? : )} ))}
{renderProcessedText(h)}
{renderProcessedText(cell)}{renderProcessedText(cell)}
); case 'chart': if (!block.markdown || !block.chartType || !block.title) return null; const rows = block.markdown.trim().split('\n').filter(row => row.includes('|')); if (rows.length < 3 || !rows[1].includes('---')) return null; const chartHeaders = rows[0].split('|').slice(1, -1).map(h => h.trim()); const chartBodyRows = rows.slice(2).map(row => row.split('|').slice(1, -1).map(c => c.trim())); const labels = chartBodyRows.map(r => r[0]); const datasets = chartHeaders.slice(1).map((datasetLabel, index) => ({ label: datasetLabel, data: chartBodyRows.map(r => parseFloat(r[index + 1]) || 0), })); const yHeaders = chartHeaders.slice(1); const yAxisTitle = yHeaders.length === 1 ? yHeaders[0] : 'Valor'; return ; default: return null; } })}
))}
); } export default LessonDisplay;