Spaces:
Running
Running
| import { getInstructors, addInstructor, removeInstructor, checkInstructorPermission } from "../services/auth.js"; | |
| import { auth } from "../services/firebase.js"; | |
| export function renderInstructorAdminView() { | |
| return ` | |
| <div class="min-h-screen p-6 pb-20"> | |
| <header class="flex justify-between items-center mb-10 bg-gray-800 bg-opacity-50 p-4 rounded-xl border border-gray-700 backdrop-blur-sm"> | |
| <div class="flex items-center space-x-4"> | |
| <button id="back-instructor-btn" class="bg-gray-700 hover:bg-gray-600 text-white p-2 rounded-lg transition-all"> | |
| ← 回講師端 | |
| </button> | |
| <h1 class="text-2xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-indigo-400 to-purple-600"> | |
| 講師權限管理 | |
| </h1> | |
| </div> | |
| <button id="add-instructor-btn" class="bg-indigo-600 hover:bg-indigo-500 text-white font-bold py-2 px-6 rounded-lg transition-all shadow-lg"> | |
| + 新增講師 | |
| </button> | |
| </header> | |
| <div class="bg-gray-800 rounded-xl border border-gray-700 overflow-hidden shadow-2xl"> | |
| <table class="w-full text-left border-collapse"> | |
| <thead> | |
| <tr class="bg-gray-900/50"> | |
| <th class="p-4 border-b border-gray-700 font-bold text-gray-400">Email (帳號)</th> | |
| <th class="p-4 border-b border-gray-700 font-bold text-gray-400">姓名</th> | |
| <th class="p-4 border-b border-gray-700 font-bold text-gray-400">權限</th> | |
| <th class="p-4 border-b border-gray-700 font-bold text-gray-400 text-right">操作</th> | |
| </tr> | |
| </thead> | |
| <tbody id="instructors-list"> | |
| <tr><td colspan="4" class="p-8 text-center text-gray-500">載入中...</td></tr> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- Add/Edit Modal --> | |
| <div id="instructor-modal" class="fixed inset-0 bg-black bg-opacity-80 backdrop-blur-sm hidden flex items-center justify-center z-50 p-4"> | |
| <div class="bg-gray-800 rounded-xl w-full max-w-lg border border-gray-700 shadow-2xl"> | |
| <div class="p-6 border-b border-gray-700"> | |
| <h3 class="text-xl font-bold text-white">新增講師</h3> | |
| </div> | |
| <div class="p-6 space-y-4"> | |
| <div> | |
| <label class="block text-gray-400 mb-1">Email (Google 帳號)</label> | |
| <input type="email" id="input-email" class="w-full bg-gray-900 border border-gray-600 rounded p-2 text-white" placeholder="example@gmail.com"> | |
| <p class="text-xs text-yellow-500 mt-1">* 必須是有效的 Google 帳號 Email</p> | |
| </div> | |
| <div> | |
| <label class="block text-gray-400 mb-1">顯示名稱</label> | |
| <input type="text" id="input-name" class="w-full bg-gray-900 border border-gray-600 rounded p-2 text-white" placeholder="王小明"> | |
| </div> | |
| <div> | |
| <label class="block text-gray-400 mb-2">權限設定</label> | |
| <div class="space-y-2"> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" class="perm-check rounded bg-gray-700 border-gray-600 text-indigo-500" value="create_room" checked> | |
| <span class="text-gray-300">建立教室 (Create Room)</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" class="perm-check rounded bg-gray-700 border-gray-600 text-indigo-500" value="manage_instructors"> | |
| <span class="text-gray-300">管理講師 (Manage Instructors)</span> | |
| </label> | |
| <label class="flex items-center space-x-2"> | |
| <input type="checkbox" class="perm-check rounded bg-gray-700 border-gray-600 text-indigo-500" value="add_question"> | |
| <span class="text-gray-300">管理題目 (Manage Questions)</span> | |
| </label> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="p-6 border-t border-gray-700 flex justify-end space-x-3"> | |
| <button onclick="document.getElementById('instructor-modal').classList.add('hidden')" class="px-4 py-2 text-gray-400 hover:text-white">取消</button> | |
| <button id="save-instructor-btn" class="bg-indigo-600 hover:bg-indigo-500 text-white px-6 py-2 rounded font-bold">新增</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| } | |
| export function setupInstructorAdminEvents() { | |
| // Permission Check | |
| const user = auth.currentUser; | |
| if (!user) { | |
| alert("請先登入"); | |
| window.location.hash = ''; // Back to Landing | |
| return; | |
| } | |
| checkInstructorPermission(user).then(inst => { | |
| if (!inst || !inst.permissions?.includes('manage_instructors')) { | |
| alert("您沒有權限管理講師"); | |
| window.location.hash = 'instructor'; | |
| return; | |
| } | |
| loadInstructorList(); | |
| }).catch(e => { | |
| console.error(e); | |
| alert("權限驗證失敗"); | |
| window.location.hash = 'instructor'; | |
| }); | |
| // Navigation | |
| document.getElementById('back-instructor-btn').addEventListener('click', () => { | |
| window.location.hash = 'instructor'; | |
| }); | |
| // Modal Handling | |
| document.getElementById('add-instructor-btn').addEventListener('click', () => { | |
| document.getElementById('input-email').value = ''; | |
| document.getElementById('input-name').value = ''; | |
| document.querySelectorAll('.perm-check').forEach(c => c.checked = true); | |
| document.getElementById('instructor-modal').classList.remove('hidden'); | |
| }); | |
| // Save Logic | |
| document.getElementById('save-instructor-btn').addEventListener('click', async () => { | |
| const email = document.getElementById('input-email').value.trim(); | |
| const name = document.getElementById('input-name').value.trim(); | |
| const permissions = Array.from(document.querySelectorAll('.perm-check:checked')).map(c => c.value); | |
| if (!email || !name) { | |
| alert("請填寫完整資訊"); | |
| return; | |
| } | |
| try { | |
| await addInstructor(email, name, permissions); | |
| alert("新增成功"); | |
| document.getElementById('instructor-modal').classList.add('hidden'); | |
| loadInstructorList(); | |
| } catch (e) { | |
| console.error(e); | |
| alert("新增失敗: " + e.message); | |
| } | |
| }); | |
| // Global Helpers | |
| window.removeInstructorConfirm = async (email) => { | |
| if (confirm(`確定要移除講師權限 (${email}) 嗎?`)) { | |
| try { | |
| await removeInstructor(email); | |
| loadInstructorList(); | |
| } catch (e) { | |
| console.error(e); | |
| alert("移除失敗: " + e.message); | |
| } | |
| } | |
| }; | |
| } | |
| async function loadInstructorList() { | |
| const list = document.getElementById('instructors-list'); | |
| list.innerHTML = '<tr><td colspan="4" class="p-8 text-center text-gray-500">載入中...</td></tr>'; | |
| try { | |
| const instructors = await getInstructors(); | |
| if (instructors.length === 0) { | |
| list.innerHTML = '<tr><td colspan="4" class="p-8 text-center text-gray-500">目前沒有其他講師</td></tr>'; | |
| return; | |
| } | |
| list.innerHTML = instructors.map(inst => ` | |
| <tr class="hover:bg-gray-700/50 transition-colors border-b border-gray-800"> | |
| <td class="p-4 font-mono text-gray-300"> | |
| ${inst.email} | |
| ${inst.role === 'admin' ? '<span class="ml-2 text-xs bg-red-900 text-red-300 px-2 py-0.5 rounded border border-red-700">Admin</span>' : ''} | |
| </td> | |
| <td class="p-4 font-bold text-white">${inst.name}</td> | |
| <td class="p-4"> | |
| <div class="flex flex-wrap gap-1"> | |
| ${(inst.permissions || []).map(p => ` | |
| <span class="text-xs bg-indigo-900/50 text-indigo-300 px-2 py-0.5 rounded border border-indigo-700">${p}</span> | |
| `).join('')} | |
| </div> | |
| </td> | |
| <td class="p-4 text-right"> | |
| ${inst.role !== 'admin' ? ` | |
| <button onclick="window.removeInstructorConfirm('${inst.email}')" class="text-red-400 hover:text-white bg-red-900/20 hover:bg-red-600 px-3 py-1 rounded transition-colors text-sm"> | |
| 移除 | |
| </button> | |
| ` : '<span class="text-gray-600 text-sm">不可變更</span>'} | |
| </td> | |
| </tr> | |
| `).join(''); | |
| } catch (e) { | |
| console.error(e); | |
| list.innerHTML = '<tr><td colspan="4" class="p-8 text-center text-red-500">載入失敗</td></tr>'; | |
| } | |
| } | |