Spaces:
Running
Running
Upload 10 files
Browse files- src/views/InstructorView.js +46 -43
src/views/InstructorView.js
CHANGED
|
@@ -775,7 +775,7 @@ const checkPermissions = (instructor) => {
|
|
| 775 |
// User is known to Firebase, check if they are an instructor
|
| 776 |
const instructorData = await checkInstructorPermission(user);
|
| 777 |
if (instructorData) {
|
| 778 |
-
authModal.classList.add('hidden');
|
| 779 |
checkPermissions(instructorData);
|
| 780 |
localStorage.setItem('vibecoding_instructor_name', instructorData.name);
|
| 781 |
|
|
@@ -1163,70 +1163,73 @@ if (navAdminBtn) {
|
|
| 1163 |
}
|
| 1164 |
|
| 1165 |
// Handle Instructor Management
|
| 1166 |
-
|
| 1167 |
-
|
| 1168 |
-
|
|
|
|
| 1169 |
|
| 1170 |
-
|
| 1171 |
-
|
| 1172 |
-
|
| 1173 |
<tr class="border-b border-gray-700 hover:bg-gray-800">
|
| 1174 |
<td class="p-3">${inst.name}</td>
|
| 1175 |
<td class="p-3 font-mono text-sm text-cyan-400">${inst.email}</td>
|
| 1176 |
<td class="p-3 text-xs">
|
| 1177 |
${inst.permissions?.map(p => {
|
| 1178 |
-
|
| 1179 |
-
|
| 1180 |
-
|
| 1181 |
</td>
|
| 1182 |
<td class="p-3">
|
| 1183 |
${inst.role === 'admin' ? '<span class="text-gray-500 italic">不可移除</span>' :
|
| 1184 |
-
|
| 1185 |
</td>
|
| 1186 |
</tr>
|
| 1187 |
`).join('');
|
| 1188 |
|
| 1189 |
-
|
| 1190 |
-
});
|
| 1191 |
|
| 1192 |
-
// Add New Instructor
|
| 1193 |
-
const addInstBtn = document.getElementById('btn-add-inst');
|
| 1194 |
-
if (addInstBtn) {
|
| 1195 |
-
|
| 1196 |
-
|
| 1197 |
-
|
| 1198 |
|
| 1199 |
-
|
| 1200 |
|
| 1201 |
-
|
| 1202 |
-
|
| 1203 |
-
|
| 1204 |
-
|
| 1205 |
|
| 1206 |
-
|
| 1207 |
-
|
| 1208 |
-
|
| 1209 |
-
|
| 1210 |
-
|
| 1211 |
-
|
| 1212 |
-
|
| 1213 |
-
|
| 1214 |
-
|
| 1215 |
-
|
| 1216 |
-
}
|
| 1217 |
|
| 1218 |
|
| 1219 |
-
// Global helper for remove (hacky but works for simple onclick)
|
| 1220 |
-
window.removeInst = async (email) => {
|
| 1221 |
-
|
| 1222 |
-
|
| 1223 |
-
|
| 1224 |
-
|
| 1225 |
-
|
| 1226 |
-
|
|
|
|
| 1227 |
}
|
| 1228 |
}
|
| 1229 |
};
|
|
|
|
| 1230 |
|
| 1231 |
// Auto Check Auth (Persistence)
|
| 1232 |
// We rely on Firebase Auth state observer instead of session storage for security?
|
|
|
|
| 775 |
// User is known to Firebase, check if they are an instructor
|
| 776 |
const instructorData = await checkInstructorPermission(user);
|
| 777 |
if (instructorData) {
|
| 778 |
+
if (authModal) authModal.classList.add('hidden');
|
| 779 |
checkPermissions(instructorData);
|
| 780 |
localStorage.setItem('vibecoding_instructor_name', instructorData.name);
|
| 781 |
|
|
|
|
| 1163 |
}
|
| 1164 |
|
| 1165 |
// Handle Instructor Management
|
| 1166 |
+
if (navInstBtn) {
|
| 1167 |
+
navInstBtn.addEventListener('click', async () => {
|
| 1168 |
+
const modal = document.getElementById('instructor-modal');
|
| 1169 |
+
const listBody = document.getElementById('instructor-list-body');
|
| 1170 |
|
| 1171 |
+
// Load list
|
| 1172 |
+
const instructors = await getInstructors();
|
| 1173 |
+
listBody.innerHTML = instructors.map(inst => `
|
| 1174 |
<tr class="border-b border-gray-700 hover:bg-gray-800">
|
| 1175 |
<td class="p-3">${inst.name}</td>
|
| 1176 |
<td class="p-3 font-mono text-sm text-cyan-400">${inst.email}</td>
|
| 1177 |
<td class="p-3 text-xs">
|
| 1178 |
${inst.permissions?.map(p => {
|
| 1179 |
+
const map = { create_room: '開房', add_question: '題目', manage_instructors: '管理' };
|
| 1180 |
+
return `<span class="bg-gray-700 px-2 py-1 rounded mr-1">${map[p] || p}</span>`;
|
| 1181 |
+
}).join('')}
|
| 1182 |
</td>
|
| 1183 |
<td class="p-3">
|
| 1184 |
${inst.role === 'admin' ? '<span class="text-gray-500 italic">不可移除</span>' :
|
| 1185 |
+
`<button class="text-red-400 hover:text-red-300" onclick="window.removeInst('${inst.email}')">移除</button>`}
|
| 1186 |
</td>
|
| 1187 |
</tr>
|
| 1188 |
`).join('');
|
| 1189 |
|
| 1190 |
+
modal.classList.remove('hidden');
|
| 1191 |
+
});
|
| 1192 |
|
| 1193 |
+
// Add New Instructor
|
| 1194 |
+
const addInstBtn = document.getElementById('btn-add-inst');
|
| 1195 |
+
if (addInstBtn) {
|
| 1196 |
+
addInstBtn.addEventListener('click', async () => {
|
| 1197 |
+
const email = document.getElementById('new-inst-email').value.trim();
|
| 1198 |
+
const name = document.getElementById('new-inst-name').value.trim();
|
| 1199 |
|
| 1200 |
+
if (!email || !name) return alert("請輸入完整資料");
|
| 1201 |
|
| 1202 |
+
const perms = [];
|
| 1203 |
+
if (document.getElementById('perm-room').checked) perms.push('create_room');
|
| 1204 |
+
if (document.getElementById('perm-q').checked) perms.push('add_question');
|
| 1205 |
+
if (document.getElementById('perm-inst').checked) perms.push('manage_instructors');
|
| 1206 |
|
| 1207 |
+
try {
|
| 1208 |
+
await addInstructor(email, name, perms);
|
| 1209 |
+
alert("新增成功");
|
| 1210 |
+
navInstBtn.click(); // Reload list
|
| 1211 |
+
document.getElementById('new-inst-email').value = '';
|
| 1212 |
+
document.getElementById('new-inst-name').value = '';
|
| 1213 |
+
} catch (e) {
|
| 1214 |
+
alert("新增失敗: " + e.message);
|
| 1215 |
+
}
|
| 1216 |
+
});
|
| 1217 |
+
}
|
| 1218 |
|
| 1219 |
|
| 1220 |
+
// Global helper for remove (hacky but works for simple onclick)
|
| 1221 |
+
window.removeInst = async (email) => {
|
| 1222 |
+
if (confirm(`確定移除 ${email}?`)) {
|
| 1223 |
+
try {
|
| 1224 |
+
await removeInstructor(email);
|
| 1225 |
+
navInstBtn.click(); // Reload
|
| 1226 |
+
} catch (e) {
|
| 1227 |
+
alert(e.message);
|
| 1228 |
+
}
|
| 1229 |
}
|
| 1230 |
}
|
| 1231 |
};
|
| 1232 |
+
}
|
| 1233 |
|
| 1234 |
// Auto Check Auth (Persistence)
|
| 1235 |
// We rely on Firebase Auth state observer instead of session storage for security?
|