import { useState, useEffect } from 'react' import { useNavigate } from 'react-router-dom' import { apiFetch } from '@core/lib/api' import { Button } from '@core/components/ui/button' import { Input } from '@core/components/ui/input' import { Badge } from '@core/components/ui/badge' import { Skeleton } from '@core/components/ui/skeleton' import { ScrollArea } from '@core/components/ui/scroll-area' import { Separator } from '@core/components/ui/separator' import { GraduationCap, Plus, PlayCircle, Edit, MessageSquare } from 'lucide-react' interface Lesson { id: string; title: string; description?: string; status?: string; data?: Record; created_at: string } interface Message { id: string; content: string; created_at: string; direction: string } interface Thread { id: string } function groupByCourse(lessons: Lesson[]): Map { const map = new Map() for (const l of lessons) { const key = (l.data?.course_title as string) || 'General' if (!map.has(key)) map.set(key, []) map.get(key)!.push(l) } for (const [k, v] of map) map.set(k, [...v].sort((a, b) => ((a.data?.sequence as number) ?? 9999) - ((b.data?.sequence as number) ?? 9999))) return map } function DiscussionPanel({ lesson }: { lesson: Lesson }) { const [messages, setMessages] = useState([]) const [thread, setThread] = useState(null) const [loading, setLoading] = useState(true) const [newMessage, setNewMessage] = useState('') const [submitting, setSubmitting] = useState(false) useEffect(() => { setLoading(true) apiFetch(`/api/admin-data?action=list&entity=threads&target_id=${lesson.id}&limit=5`) .then(r => r.json()).then(j => { const t = (j?.data ?? j)?.[0] ?? null setThread(t) if (t) return apiFetch(`/api/admin-data?action=list&entity=messages&thread_id=${t.id}&limit=50`).then(r => r.json()) return [] }).then(raw => { const msgs = raw?.data ?? raw; setMessages(Array.isArray(msgs) ? msgs : []) }).catch(() => {}).finally(() => setLoading(false)) }, [lesson.id]) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!newMessage.trim() || submitting) return setSubmitting(true) try { // Create thread if it doesn't exist let currentThread = thread if (!currentThread) { const threadResponse = await apiFetch('/api/admin-data?action=create&entity=threads', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ target_type: 'item', target_id: lesson.id, title: `Discussion: ${lesson.title}`, status: 'active' }) }) const threadResult = await threadResponse.json() currentThread = threadResult?.data || threadResult setThread(currentThread) } // Create message const messageResponse = await apiFetch('/api/admin-data?action=create&entity=messages', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ type_slug: 'message', thread_id: currentThread.id, content: newMessage.trim(), direction: 'inbound', sequence: messages.length + 1 }) }) const messageResult = await messageResponse.json() const newMsg = messageResult?.data || messageResult // Add to local state setMessages(prev => [...prev, newMsg]) setNewMessage('') } catch (error) { console.error('Failed to send message:', error) } finally { setSubmitting(false) } } return (

Discussion ({messages.length})

{loading && } {!loading && messages.length === 0 &&

No discussion yet.

} {messages.map(msg => (

{msg.content}

{msg.created_at ? new Date(msg.created_at).toLocaleString() : 'Just now'}

))}
setNewMessage(e.target.value)} placeholder="Add a comment..." className="flex-1 px-3 py-2 text-xs border border-border rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent" disabled={submitting} />
) } export default function CoursesPage() { const navigate = useNavigate() const [lessons, setLessons] = useState([]) const [loading, setLoading] = useState(true) const [search, setSearch] = useState('') const [selectedCourse, setSelectedCourse] = useState(null) const [selected, setSelected] = useState(null) useEffect(() => { apiFetch('/api/admin-data?action=list&entity=items&type_slug=course_lesson&limit=500') .then(r => r.json()).then(j => setLessons(Array.isArray(j?.data) ? j.data : Array.isArray(j) ? j : [])).catch(() => setLessons([])).finally(() => setLoading(false)) }, []) const courseMap = groupByCourse(lessons) const courseNames = Array.from(courseMap.keys()).filter(c => !search || c.toLowerCase().includes(search.toLowerCase())) const chaptersForCourse = selectedCourse ? courseMap.get(selectedCourse) ?? [] : [] return (

Courses

setSearch(e.target.value)} className="h-7 text-xs" />
{loading ?
{Array.from({ length: 3 }).map((_, i) => )}
: courseNames.length === 0 ?
No courses.
: courseNames.map(name => ( )) }

Lessons

{!selectedCourse ?
Select a course
: chaptersForCourse.length === 0 ?
No lessons.
: chaptersForCourse.map(lesson => { const seq = lesson.data?.sequence as number | undefined return ( ) }) }
{!selected ? (

{selectedCourse ? 'Select a lesson' : 'Select a course to get started'}

) : ( <>

{selected.title}

{selected.description &&

{selected.description}

}
{selected.data?.video_url && ( )} {selected.data?.content && <>
{selected.data.content as string}
} {!selected.data?.video_url && !selected.data?.content &&

No content available.

}
)}
) }