const restrictClass = (window.TELEARN_INDEX && window.TELEARN_INDEX.restrictClass) || ""; const restrictStudent = (window.TELEARN_INDEX && window.TELEARN_INDEX.restrictStudent) || ""; let projectData = {}; let classIds = {}; let studentIds = {}; let activeClass = ""; let activeStudent = ""; // Resize layout columns dynamically according to restrictions function applyLayoutRestrictions() { const container = document.querySelector('.container'); const panelClasses = document.getElementById('panel-classes'); const panelStudents = document.getElementById('panel-students'); if (restrictClass && restrictStudent) { panelClasses.style.display = 'none'; panelStudents.style.display = 'none'; container.style.gridTemplateColumns = '1fr'; document.querySelector('#panel-projects .panel-header h2').innerText = `${restrictStudent} 的專題列表`; } else if (restrictClass) { panelClasses.style.display = 'none'; container.style.gridTemplateColumns = '1.2fr 1.8fr'; document.querySelector('#panel-students .panel-header h2').innerText = `${restrictClass} 學生名單`; } } // Fetch data from backend async function fetchProjects() { applyLayoutRestrictions(); try { let url = '/api/projects'; const params = []; if (restrictClass) params.push(`class_name=${encodeURIComponent(restrictClass)}`); if (restrictStudent) params.push(`student_name=${encodeURIComponent(restrictStudent)}`); if (params.length > 0) url += '?' + params.join('&'); const response = await fetch(url); const res = await response.json(); projectData = res.projects; classIds = res.class_ids; studentIds = res.student_ids; if (restrictClass && restrictStudent) { activeClass = restrictClass; activeStudent = restrictStudent; renderProjects(); } else if (restrictClass) { activeClass = restrictClass; renderStudents(); } else { renderClasses(); } // Initialize link generator if elements are present on the page (not in restricted view) if (document.getElementById("link-generator")) { initLinkGenerator(); } } catch (error) { console.error("Error fetching projects:", error); const targetId = (restrictClass && restrictStudent) ? 'project-list' : (restrictClass ? 'student-list' : 'class-list'); document.getElementById(targetId).innerHTML = `

載入專案清單失敗。請確認伺服器運作正常。

`; } } // Initialize Share Link Generator function initLinkGenerator() { const classSelect = document.getElementById("gen-class-select"); if (!classSelect) return; classSelect.innerHTML = ''; const classes = Object.keys(projectData); classes.forEach(cls => { const opt = document.createElement("option"); opt.value = cls; opt.textContent = cls; classSelect.appendChild(opt); }); classSelect.onchange = handleGeneratorClassChange; } function handleGeneratorClassChange() { const classSelect = document.getElementById("gen-class-select"); const studentSelect = document.getElementById("gen-student-select"); const urlResult = document.getElementById("gen-url-result"); const selectedClass = classSelect.value; studentSelect.innerHTML = ''; urlResult.value = ""; if (!selectedClass) { studentSelect.disabled = true; return; } studentSelect.disabled = false; const students = Object.keys(projectData[selectedClass] || {}); students.forEach(std => { const opt = document.createElement("option"); opt.value = std; opt.textContent = std; studentSelect.appendChild(opt); }); studentSelect.onchange = generateShareUrl; generateShareUrl(); } function generateShareUrl() { const classSelect = document.getElementById("gen-class-select"); const studentSelect = document.getElementById("gen-student-select"); const urlResult = document.getElementById("gen-url-result"); const selectedClass = classSelect.value; const selectedStudent = studentSelect.value; if (!selectedClass) { urlResult.value = ""; return; } const classId = classIds[selectedClass]; const baseUrl = window.location.origin; if (selectedStudent) { const studentKey = `${selectedClass}/${selectedStudent}`; const studentId = studentIds[studentKey]; urlResult.value = `${baseUrl}/student/${studentId}`; } else { urlResult.value = `${baseUrl}/class/${classId}`; } } function copyGeneratedUrl() { const urlResult = document.getElementById("gen-url-result"); const btnCopy = document.getElementById("btn-copy-url"); if (!urlResult.value) return; urlResult.select(); urlResult.setSelectionRange(0, 99999); navigator.clipboard.writeText(urlResult.value).then(() => { const oldText = btnCopy.textContent; btnCopy.textContent = "已複製!"; btnCopy.style.background = "#10b981"; setTimeout(() => { btnCopy.textContent = oldText; btnCopy.style.background = ""; }, 2000); }).catch(err => { console.error("Failed to copy:", err); }); } // Render Class Cards function renderClasses() { const classList = document.getElementById('class-list'); classList.innerHTML = ""; const classes = Object.keys(projectData); if (classes.length === 0) { classList.innerHTML = `

目錄下尚無任何學生專案。

請將學生專案資料夾置於 projects/ 下

`; return; } classes.forEach(cls => { const card = document.createElement('div'); card.className = `item-card ${activeClass === cls ? 'active' : ''}`; const label = document.createElement('span'); label.textContent = cls; card.append(label); card.insertAdjacentHTML('beforeend', ''); card.onclick = () => selectClass(cls); classList.appendChild(card); }); } function selectClass(className) { activeClass = className; activeStudent = ""; renderClasses(); renderStudents(); document.getElementById('project-list').innerHTML = `

請先點選學生以瀏覽其專案

`; } // Render Student Cards function renderStudents() { const studentList = document.getElementById('student-list'); studentList.innerHTML = ""; const students = Object.keys(projectData[activeClass] || {}); if (students.length === 0) { studentList.innerHTML = `

無學生資料

`; return; } students.forEach(std => { const card = document.createElement('div'); card.className = `item-card ${activeStudent === std ? 'active' : ''}`; const label = document.createElement('span'); label.textContent = std; card.append(label); card.insertAdjacentHTML('beforeend', ''); card.onclick = () => selectStudent(std); studentList.appendChild(card); }); } function selectStudent(studentName) { activeStudent = studentName; renderStudents(); renderProjects(); } // Render Project Cards with hover-classes matching category colors function renderProjects() { const projectList = document.getElementById('project-list'); projectList.innerHTML = ""; const projects = (projectData[activeClass] && projectData[activeClass][activeStudent]) || []; if (projects.length === 0) { projectList.innerHTML = `

該名學生尚無可執行的專案項目。

`; return; } projects.forEach(proj => { let tagText = "HTML5 Game"; let tagClass = "tag-html"; let hoverClass = "hover-html"; let btnClass = "btn-launch-html"; if (proj.type === "python") { tagText = "Python (Pygame/CV)"; tagClass = "tag-python"; hoverClass = "hover-python"; btnClass = "btn-launch-python"; } else if (proj.type === "external") { const url = proj.url.toLowerCase(); if (url.includes("scratch")) { tagText = "Scratch Project"; tagClass = "tag-scratch"; hoverClass = "hover-scratch"; btnClass = "btn-launch-scratch"; } else if (url.includes("makecode")) { tagText = "MakeCode Arcade"; tagClass = "tag-makecode"; hoverClass = "hover-makecode"; btnClass = "btn-launch-makecode"; } else { tagText = "External App"; tagClass = "tag-html"; hoverClass = "hover-html"; btnClass = "btn-launch-html"; } } else if (proj.type === "html") { tagText = "HTML5 Offline"; tagClass = "tag-html"; hoverClass = "hover-html"; btnClass = "btn-launch-html"; } else if (proj.type === "apk") { tagText = "Android App (APK)"; tagClass = "tag-apk"; hoverClass = "hover-apk"; btnClass = "btn-launch-apk"; } const card = document.createElement('div'); card.className = `project-card ${hoverClass}`; const meta = document.createElement('div'); meta.className = 'project-meta'; const title = document.createElement('span'); title.className = 'project-title'; title.textContent = proj.display_name || proj.name; const tag = document.createElement('span'); tag.className = `project-tag ${tagClass}`; tag.textContent = tagText; meta.append(title, tag); const launch = document.createElement('button'); launch.type = 'button'; launch.className = `btn-launch ${btnClass}`; launch.insertAdjacentHTML('afterbegin', ''); launch.append(document.createTextNode('啟動專題專區')); launch.addEventListener('click', () => launchProject(proj.name)); card.append(meta, launch); projectList.appendChild(card); }); } function launchProject(projectName) { const studentKey = `${activeClass}/${activeStudent}`; const studentId = studentIds[studentKey] || activeStudent; const url = `/run/${encodeURIComponent(studentId)}/${encodeURIComponent(projectName)}`; window.open(url, '_blank'); } document.getElementById("btn-copy-url")?.addEventListener("click", copyGeneratedUrl); window.addEventListener("load", fetchProjects, { once: true });