Vibecodingex / src /views /InstructorAdminView.js
Lashtw's picture
Upload 11 files
8f2336a verified
raw
history blame
9.6 kB
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>';
}
}