import { useEffect, useMemo, useState } from 'react' import { listLessons, listResources, updateResourceLinks } from '../api' function formatDateHeader(dateStr) { if (!dateStr) return 'Undated' const d = new Date(`${dateStr}T00:00:00`) if (Number.isNaN(d.getTime())) return dateStr return d.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', year: 'numeric' }) } function groupByDate(lessons) { const groups = {} for (const l of lessons) { const key = l.date || 'Undated' ;(groups[key] ||= []).push(l) } return Object.entries(groups).sort((a, b) => (a[0] < b[0] ? 1 : -1)) } function groupByCategory(lessons) { const groups = {} for (const l of lessons) { const key = l.category || 'General' ;(groups[key] ||= []).push(l) } return Object.entries(groups).sort((a, b) => a[0].localeCompare(b[0])) } function LessonCard({ lesson, onOpen }) { return ( ) } function LessonGroups({ groups, onOpen, openFirst }) { return groups.map(([label, items], i) => (
{label} ({items.length})
{items.map((lesson) => ( ))}
)) } function domainOf(url) { try { const host = new URL(url).hostname return host.startsWith('www.') ? host.slice(4) : host } catch { return url } } function Resources() { const [resources, setResources] = useState(null) const [error, setError] = useState('') const [editingId, setEditingId] = useState(null) const [draftLinks, setDraftLinks] = useState([]) const [saving, setSaving] = useState(false) const [saveError, setSaveError] = useState('') useEffect(() => { listResources() .then((data) => setResources(data.resources)) .catch((e) => setError(e.message)) }, []) if (error) return
Could not load resources: {error}
if (resources === null) return
Loading resources…
if (resources.length === 0) { return (
📚 No resources yet. Save a page that's mostly links or book recommendations (e.g. "Online Resources", "Books to Read") and it'll show up here, beautifully laid out and out of your lecture notes.
) } const startEdit = (page) => { setEditingId(page.id) setDraftLinks(page.links.map((l) => ({ url: l.url, label: l.label }))) setSaveError('') } const cancelEdit = () => { setEditingId(null) setDraftLinks([]) setSaveError('') } const updateDraft = (i, field, value) => { setDraftLinks((prev) => prev.map((l, idx) => (idx === i ? { ...l, [field]: value } : l))) } const removeDraft = (i) => { setDraftLinks((prev) => prev.filter((_, idx) => idx !== i)) } const addDraft = () => { setDraftLinks((prev) => [...prev, { url: '', label: '' }]) } const saveEdit = async (pageId) => { setSaving(true) setSaveError('') try { const { links } = await updateResourceLinks(pageId, draftLinks) setResources((prev) => prev.map((p) => (p.id === pageId ? { ...p, links } : p))) setEditingId(null) setDraftLinks([]) } catch (e) { setSaveError(e.message) } finally { setSaving(false) } } return (
{resources.map((page) => (

📚 {page.title}

{editingId === page.id ? (
) : ( )}
{editingId === page.id ? (
{draftLinks.map((link, i) => (
updateDraft(i, 'label', e.target.value)} /> updateDraft(i, 'url', e.target.value)} />
))} {saveError &&
⚠ Could not save: {saveError}
}
) : ( page.links.length > 0 && (
{page.links.map((link, i) => ( {link.label} {link.domain || domainOf(link.url)} ))}
) )} {page.books.length > 0 && (
{page.books.map((book, i) => { const meta = [book.author, book.note].filter(Boolean).join(' · ') return (
📖 {book.title} {meta && {meta}}
) })}
)}
))}
) } export default function Lessons({ onOpenLesson }) { const [lessons, setLessons] = useState(null) const [error, setError] = useState('') const [query, setQuery] = useState('') const [view, setView] = useState('date') useEffect(() => { listLessons() .then((data) => setLessons(data.lessons)) .catch((e) => setError(e.message)) }, []) const filtered = useMemo(() => { if (!lessons) return [] const q = query.trim().toLowerCase() if (!q) return lessons return lessons.filter((l) => (l.title || '').toLowerCase().includes(q)) }, [lessons, query]) if (error) return
Could not load lessons: {error}
if (lessons === null) return
Loading lessons…
return (
setQuery(e.target.value)} />
{view === 'resources' && } {view !== 'resources' && lessons.length === 0 && (
📓 No lessons saved yet. Open the Notebook tab, paste some French text, and save it.
)} {view !== 'resources' && lessons.length > 0 && filtered.length === 0 && (
No lessons match "{query}".
)} {view === 'date' && filtered.length > 0 && ( [formatDateHeader(d), items])} onOpen={onOpenLesson} openFirst /> )} {view === 'topic' && filtered.length > 0 && ( )}
) }