Spaces:
Running
Running
Upload 9 files
Browse files- src/services/auth.js +2 -1
- src/views/InstructorView.js +39 -6
src/services/auth.js
CHANGED
|
@@ -2,7 +2,8 @@ import { auth, db } from "./firebase.js";
|
|
| 2 |
import {
|
| 3 |
signInWithEmailAndPassword,
|
| 4 |
createUserWithEmailAndPassword,
|
| 5 |
-
signOut
|
|
|
|
| 6 |
} from "https://www.gstatic.com/firebasejs/10.7.1/firebase-auth.js";
|
| 7 |
import {
|
| 8 |
doc,
|
|
|
|
| 2 |
import {
|
| 3 |
signInWithEmailAndPassword,
|
| 4 |
createUserWithEmailAndPassword,
|
| 5 |
+
signOut,
|
| 6 |
+
sendPasswordResetEmail
|
| 7 |
} from "https://www.gstatic.com/firebasejs/10.7.1/firebase-auth.js";
|
| 8 |
import {
|
| 9 |
doc,
|
src/views/InstructorView.js
CHANGED
|
@@ -218,9 +218,7 @@ export async function renderInstructorView() {
|
|
| 218 |
|
| 219 |
<!-- Eraser -->
|
| 220 |
<button class="annotation-tool p-2 rounded-full hover:bg-gray-700 transition-colors ring-2 ring-transparent focus:outline-none flex items-center justify-center" data-tool="eraser" onclick="setPenTool('eraser', null, this)" title="橡皮擦">
|
| 221 |
-
<
|
| 222 |
-
<path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
| 223 |
-
</svg>
|
| 224 |
</button>
|
| 225 |
|
| 226 |
<div class="w-px h-6 bg-gray-600 mx-2"></div>
|
|
@@ -433,6 +431,41 @@ export function setupInstructorEvents() {
|
|
| 433 |
}
|
| 434 |
});
|
| 435 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 436 |
// Register Handler
|
| 437 |
registerBtn.addEventListener('click', async () => {
|
| 438 |
const email = loginEmailInput.value;
|
|
@@ -1198,7 +1231,7 @@ export function setupInstructorEvents() {
|
|
| 1198 |
document.getElementById('broadcast-content').classList.add('hidden');
|
| 1199 |
const stage = document.getElementById('stage-view');
|
| 1200 |
stage.classList.remove('hidden');
|
| 1201 |
-
document.getElementById('stage-prompt').textContent = prompt;
|
| 1202 |
document.getElementById('stage-author').textContent = author;
|
| 1203 |
};
|
| 1204 |
|
|
@@ -1312,7 +1345,7 @@ export function setupInstructorEvents() {
|
|
| 1312 |
onchange="handlePromptSelection(this)">
|
| 1313 |
</div>
|
| 1314 |
<!-- Prompt Content -->
|
| 1315 |
-
<div class="bg-black/30 rounded p-2 flex-1 overflow-y-auto font-mono text-green-300 whitespace-pre-wrap custom-scrollbar text-base leading-snug group-hover:text-green-200 transaction-colors mb-2">${p.prompt}</div>
|
| 1316 |
|
| 1317 |
<!-- Footer: Time + Actions -->
|
| 1318 |
<div class="flex justify-between items-center text-[10px] text-gray-500 mt-auto">
|
|
@@ -1491,7 +1524,7 @@ export function setupInstructorEvents() {
|
|
| 1491 |
</div>
|
| 1492 |
<!-- Prompt Content: Larger Text (text-4xl) -->
|
| 1493 |
<div class="flex-1 overflow-y-auto font-mono text-green-300 text-4xl leading-relaxed whitespace-pre-wrap p-2 hover:bg-white/5 transition-colors rounded custom-scrollbar">
|
| 1494 |
-
${item.prompt}
|
| 1495 |
</div>
|
| 1496 |
`;
|
| 1497 |
grid.appendChild(col);
|
|
|
|
| 218 |
|
| 219 |
<!-- Eraser -->
|
| 220 |
<button class="annotation-tool p-2 rounded-full hover:bg-gray-700 transition-colors ring-2 ring-transparent focus:outline-none flex items-center justify-center" data-tool="eraser" onclick="setPenTool('eraser', null, this)" title="橡皮擦">
|
| 221 |
+
<img src="assets/eraser.svg" class="w-6 h-6 object-contain" alt="Eraser">
|
|
|
|
|
|
|
| 222 |
</button>
|
| 223 |
|
| 224 |
<div class="w-px h-6 bg-gray-600 mx-2"></div>
|
|
|
|
| 431 |
}
|
| 432 |
});
|
| 433 |
|
| 434 |
+
// Forgot Password Handler
|
| 435 |
+
const forgotBtn = document.createElement('button');
|
| 436 |
+
forgotBtn.textContent = "忘記密碼?";
|
| 437 |
+
forgotBtn.className = "text-sm text-gray-400 hover:text-white mt-2 underline block mx-auto"; // Centered link
|
| 438 |
+
|
| 439 |
+
// Insert after auth-error message or append to modal content?
|
| 440 |
+
// Appending to the parent of Login Button seems best, or just below it.
|
| 441 |
+
// The modal structure in index.html is needed to know exact placement.
|
| 442 |
+
// Assuming loginBtn is inside a flex column form.
|
| 443 |
+
loginBtn.parentNode.insertBefore(forgotBtn, loginBtn.nextSibling);
|
| 444 |
+
|
| 445 |
+
forgotBtn.addEventListener('click', async () => {
|
| 446 |
+
const email = loginEmailInput.value;
|
| 447 |
+
if (!email) {
|
| 448 |
+
authErrorMsg.textContent = "請先在上欄輸入 Email 以發送重設信";
|
| 449 |
+
authErrorMsg.classList.remove('hidden');
|
| 450 |
+
return;
|
| 451 |
+
}
|
| 452 |
+
if (!confirm(`確定要發送重設密碼信件至 ${email} 嗎?`)) return;
|
| 453 |
+
|
| 454 |
+
try {
|
| 455 |
+
// Dynamically import to avoid top-level dependency if not needed
|
| 456 |
+
const { resetPassword } = await import("../services/auth.js");
|
| 457 |
+
await resetPassword(email);
|
| 458 |
+
alert(`已發送重設密碼信件至 ${email},請查收信箱並依照指示重設密碼。`);
|
| 459 |
+
authErrorMsg.classList.add('hidden');
|
| 460 |
+
} catch (e) {
|
| 461 |
+
console.error(e);
|
| 462 |
+
let msg = e.message;
|
| 463 |
+
if (e.code === 'auth/user-not-found') msg = "找不到此帳號,請確認 Email 是否正確。";
|
| 464 |
+
authErrorMsg.textContent = "發送失敗: " + msg;
|
| 465 |
+
authErrorMsg.classList.remove('hidden');
|
| 466 |
+
}
|
| 467 |
+
});
|
| 468 |
+
|
| 469 |
// Register Handler
|
| 470 |
registerBtn.addEventListener('click', async () => {
|
| 471 |
const email = loginEmailInput.value;
|
|
|
|
| 1231 |
document.getElementById('broadcast-content').classList.add('hidden');
|
| 1232 |
const stage = document.getElementById('stage-view');
|
| 1233 |
stage.classList.remove('hidden');
|
| 1234 |
+
document.getElementById('stage-prompt').textContent = (prompt || '').trim();
|
| 1235 |
document.getElementById('stage-author').textContent = author;
|
| 1236 |
};
|
| 1237 |
|
|
|
|
| 1345 |
onchange="handlePromptSelection(this)">
|
| 1346 |
</div>
|
| 1347 |
<!-- Prompt Content -->
|
| 1348 |
+
<div class="bg-black/30 rounded p-2 flex-1 overflow-y-auto font-mono text-green-300 whitespace-pre-wrap custom-scrollbar text-base leading-snug group-hover:text-green-200 transaction-colors mb-2">${(p.prompt || '').trim()}</div>
|
| 1349 |
|
| 1350 |
<!-- Footer: Time + Actions -->
|
| 1351 |
<div class="flex justify-between items-center text-[10px] text-gray-500 mt-auto">
|
|
|
|
| 1524 |
</div>
|
| 1525 |
<!-- Prompt Content: Larger Text (text-4xl) -->
|
| 1526 |
<div class="flex-1 overflow-y-auto font-mono text-green-300 text-4xl leading-relaxed whitespace-pre-wrap p-2 hover:bg-white/5 transition-colors rounded custom-scrollbar">
|
| 1527 |
+
${(item.prompt || '').trim()}
|
| 1528 |
</div>
|
| 1529 |
`;
|
| 1530 |
grid.appendChild(col);
|