/** * LessonNotes Component * Note-taking interface for lessons with auto-save */ import React, { useState, useEffect, useCallback, useRef } from 'react'; import { StickyNote, Save, Clock, Trash2, Download, ChevronDown, ChevronUp, BookmarkPlus, CheckCircle, Loader2, } from 'lucide-react'; interface Note { id: string; lessonId: string; content: string; timestamp?: number; // Video timestamp in seconds createdAt: string; updatedAt: string; } interface LessonNotesProps { lessonId: string; courseId: string; notes: Note[]; currentVideoTime?: number; onSaveNote: (content: string, timestamp?: number) => Promise; onUpdateNote: (noteId: string, content: string) => Promise; onDeleteNote: (noteId: string) => Promise; onSeekToTimestamp?: (timestamp: number) => void; onExportNotes?: () => void; collapsed?: boolean; onToggleCollapse?: () => void; } const LessonNotes: React.FC = ({ lessonId, courseId, notes, currentVideoTime, onSaveNote, onUpdateNote, onDeleteNote, onSeekToTimestamp, onExportNotes, collapsed = false, onToggleCollapse, }) => { const [newNote, setNewNote] = useState(''); const [editingNoteId, setEditingNoteId] = useState(null); const [editContent, setEditContent] = useState(''); const [saving, setSaving] = useState(false); const [lastSaved, setLastSaved] = useState(null); const [includeTimestamp, setIncludeTimestamp] = useState(true); const textareaRef = useRef(null); // Auto-resize textarea const adjustTextareaHeight = useCallback((textarea: HTMLTextAreaElement) => { textarea.style.height = 'auto'; textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`; }, []); useEffect(() => { if (textareaRef.current) { adjustTextareaHeight(textareaRef.current); } }, [newNote, adjustTextareaHeight]); const formatTimestamp = (seconds: number): string => { const mins = Math.floor(seconds / 60); const secs = Math.floor(seconds % 60); return `${mins}:${secs.toString().padStart(2, '0')}`; }; const handleSaveNote = async () => { if (!newNote.trim()) return; setSaving(true); try { const timestamp = includeTimestamp && currentVideoTime ? Math.floor(currentVideoTime) : undefined; await onSaveNote(newNote.trim(), timestamp); setNewNote(''); setLastSaved(new Date()); } catch (error) { console.error('Error saving note:', error); } finally { setSaving(false); } }; const handleUpdateNote = async (noteId: string) => { if (!editContent.trim()) return; setSaving(true); try { await onUpdateNote(noteId, editContent.trim()); setEditingNoteId(null); setEditContent(''); setLastSaved(new Date()); } catch (error) { console.error('Error updating note:', error); } finally { setSaving(false); } }; const handleDeleteNote = async (noteId: string) => { if (!confirm('Estas seguro de eliminar esta nota?')) return; try { await onDeleteNote(noteId); } catch (error) { console.error('Error deleting note:', error); } }; const startEditing = (note: Note) => { setEditingNoteId(note.id); setEditContent(note.content); }; const cancelEditing = () => { setEditingNoteId(null); setEditContent(''); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) { e.preventDefault(); handleSaveNote(); } }; // Sort notes by timestamp or creation date const sortedNotes = [...notes].sort((a, b) => { if (a.timestamp !== undefined && b.timestamp !== undefined) { return a.timestamp - b.timestamp; } return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(); }); return (
{/* Header */} {!collapsed && (
{/* New Note Input */}