async function fetchJson(url, options) { const res = await fetch(url, options); if (!res.ok) throw new Error(`Fetch failed: ${url}`); return await res.json(); } function option(value, label) { const el = document.createElement('option'); el.value = value; el.textContent = label; return el; } function renderList(targetId, items, formatter) { const target = document.getElementById(targetId); if (!target) return; if (!items.length) { target.textContent = 'Nothing here yet.'; target.classList.add('muted'); return; } target.classList.remove('muted'); target.innerHTML = ''; for (const item of items) { const wrapper = document.createElement('div'); wrapper.className = 'list-item'; wrapper.innerHTML = formatter(item); target.appendChild(wrapper); } } async function populateCourses() { const data = await fetchJson('/api/d2l/courses'); const courseSelect = document.getElementById('class-select'); const courseNameInput = document.getElementById('class-name-input'); const recordingSelect = document.getElementById('recording-class-select'); const recordingNameInput = document.getElementById('recording-class-name-input'); const courses = data.items || []; const byId = new Map(courses.map((course) => [String(course.d2l_course_id || course.id), course])); if (courseSelect) { courseSelect.innerHTML = ''; courseSelect.appendChild(option('', 'Choose a class')); for (const course of courses) { courseSelect.appendChild(option(course.d2l_course_id || course.id, `${course.name}${course.finalSummary ? ' — ' + course.finalSummary : ''}`)); } courseSelect.addEventListener('change', async (event) => { const courseId = event.target.value; const course = byId.get(String(courseId)); if (courseNameInput) courseNameInput.value = course?.name || ''; const assignmentSelect = document.getElementById('assignment-select'); assignmentSelect.innerHTML = ''; if (!courseId) { assignmentSelect.appendChild(option('', 'Pick a class first')); return; } assignmentSelect.appendChild(option('', 'Loading assignments…')); const assignmentData = await fetchJson(`/api/d2l/assignments?course_id=${encodeURIComponent(courseId)}`); assignmentSelect.innerHTML = ''; assignmentSelect.appendChild(option('', 'Choose an assignment')); for (const assignment of assignmentData.items || []) { assignmentSelect.appendChild(option(assignment.title, assignment.title)); } }); } if (recordingSelect) { recordingSelect.innerHTML = ''; recordingSelect.appendChild(option('', 'Choose a class')); for (const course of courses) { recordingSelect.appendChild(option(course.d2l_course_id || course.id, course.name)); } recordingSelect.addEventListener('change', (event) => { const course = byId.get(String(event.target.value)); if (recordingNameInput) recordingNameInput.value = course?.name || ''; }); } } async function loadDashboard() { const data = await fetchJson('/api/dashboard/summary'); const counts = data.counts || {}; const setText = (id, value) => { const el = document.getElementById(id); if (el) el.textContent = value; }; setText('stat-courses', counts.courses ?? '0'); setText('stat-assignments', counts.assignments ?? '0'); setText('stat-submissions', counts.submissions ?? '0'); setText('stat-recordings', counts.recordings ?? '0'); setText('backend-note', `Storage backend: ${data.backend}`); if (data.latest_sync) { setText('sync-status', `Last sync: ${data.latest_sync.completed_at || 'unknown'} · ${data.latest_sync.assignment_count || 0} assignment rows`); } renderList('recent-assignments', data.recent_assignments || [], (item) => `${item.title || 'Untitled'}${item.course_name || ''}Status: ${item.status || 'assigned'} · Grade: ${item.grade || '—'}`); renderList('recent-submissions', data.recent_submissions || [], (item) => `${item.assignment_name || item.original_filename || 'Submission'}${item.class_name || ''}Status: ${item.status || 'unknown'} · File: ${item.original_filename || '—'}`); renderList('recent-recordings', data.recent_recordings || [], (item) => `${item.original_filename || 'Recording'}${item.session_date || ''}Transcription: ${item.transcription_status || '—'} · Summary: ${item.summary_status || '—'}`); } async function wireSyncButton() { const button = document.getElementById('sync-button'); if (!button) return; button.addEventListener('click', async () => { button.disabled = true; button.textContent = 'Refreshing…'; try { const result = await fetchJson('/api/d2l/sync', { method: 'POST' }); const el = document.getElementById('sync-status'); if (el) el.textContent = `Last sync: just now · ${result.assignment_count} assignment rows · backend ${result.backend}`; await loadDashboard(); } catch (error) { const el = document.getElementById('sync-status'); if (el) el.textContent = `Sync failed: ${error}`; } finally { button.disabled = false; button.textContent = 'Refresh from D2L'; } }); } populateCourses().catch((error) => { console.error('Failed to populate D2L selectors', error); }); loadDashboard().catch((error) => { console.error('Failed to load dashboard summary', error); }); wireSyncButton();