Lashtw commited on
Commit
ccb7a43
·
verified ·
1 Parent(s): 06a44ed

Upload 10 files

Browse files
Files changed (1) hide show
  1. src/views/InstructorView.js +148 -7
src/views/InstructorView.js CHANGED
@@ -307,10 +307,10 @@ export async function renderInstructorView() {
307
  </button>
308
  <div id="create-room-container" class="flex items-center space-x-2">
309
  <input type="text" id="rejoin-room-code" placeholder="代碼" class="bg-gray-900 border border-gray-700 text-white px-3 py-2 rounded-lg w-20 text-center focus:outline-none focus:border-cyan-500">
310
- <button id="rejoin-room-btn" class="bg-gray-700 hover:bg-gray-600 text-white px-3 py-2 rounded-lg">重回</button>
311
 
312
- <button id="create-room-btn" class="bg-purple-600 hover:bg-purple-500 text-white font-bold px-4 py-2 rounded-lg shadow-lg">開房</button>
313
- <button id="setup-ai-btn" class="bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-400 hover:to-teal-400 text-white font-bold px-4 py-2 rounded-lg shadow-lg flex items-center space-x-1" title="設定 AI 助教">
314
  <span>🧠 AI 設定</span>
315
  </button>
316
  </div>
@@ -338,7 +338,7 @@ export async function renderInstructorView() {
338
 
339
  <div class="flex justify-end space-x-2">
340
  <button onclick="document.getElementById('ai-settings-modal').classList.add('hidden')" class="px-4 py-2 text-gray-400 hover:text-white">取消</button>
341
- <button id="save-ai-settings" class="px-6 py-2 bg-emerald-600 hover:bg-emerald-500 text-white rounded font-bold">儲存並啟動</button>
342
  </div>
343
  </div>
344
  </div>
@@ -346,10 +346,10 @@ export async function renderInstructorView() {
346
  <!-- Fixed Bottom Right Controls -->
347
 
348
  <div class="fixed bottom-6 right-6 z-40 flex flex-col space-y-4">
349
- <button id="btn-open-gallery" class="w-14 h-14 rounded-full bg-gray-800/80 backdrop-blur-md border border-cyan-500/30 text-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.3)] hover:scale-110 hover:bg-cyan-900/50 hover:border-cyan-400 transition-all duration-300 flex items-center justify-center group" title="怪獸圖鑑">
350
  <span class="text-2xl filter drop-shadow-[0_0_5px_rgba(34,211,238,0.8)] group-hover:animate-bounce">👾</span>
351
  </button>
352
- <button id="logout-btn" class="w-14 h-14 rounded-full bg-gray-800/80 backdrop-blur-md border border-red-500/30 text-red-400 shadow-[0_0_15px_rgba(239,68,68,0.3)] hover:scale-110 hover:bg-red-900/50 hover:border-red-400 transition-all duration-300 flex items-center justify-center" title="登出">
353
  <span class="text-xl filter drop-shadow-[0_0_5px_rgba(248,113,113,0.8)]">🚪</span>
354
  </button>
355
  </div>
@@ -381,7 +381,9 @@ export function setupInstructorEvents() {
381
  // --- Global Nav Functions (Hoisted) ---
382
  window.openAdminPanel = () => {
383
  if (!window.currentInstructor?.permissions?.includes('add_question')) return alert("無權限");
384
- alert("管理功能尚未開放 (Admin Function Placeholder)");
 
 
385
  };
386
 
387
  window.openInstructorPanel = async () => {
@@ -416,6 +418,145 @@ export function setupInstructorEvents() {
416
  }
417
  };
418
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
  // Login Logic
420
  const authErrorMsg = document.getElementById('auth-error');
421
  const authModal = document.getElementById('auth-modal');
 
307
  </button>
308
  <div id="create-room-container" class="flex items-center space-x-2">
309
  <input type="text" id="rejoin-room-code" placeholder="代碼" class="bg-gray-900 border border-gray-700 text-white px-3 py-2 rounded-lg w-20 text-center focus:outline-none focus:border-cyan-500">
310
+ <button id="rejoin-room-btn" onclick="window.rejoinRoom()" class="bg-gray-700 hover:bg-gray-600 text-white px-3 py-2 rounded-lg">重回</button>
311
 
312
+ <button id="create-room-btn" onclick="window.createRoom()" class="bg-purple-600 hover:bg-purple-500 text-white font-bold px-4 py-2 rounded-lg shadow-lg">開房</button>
313
+ <button id="setup-ai-btn" onclick="window.setupAi()" class="bg-gradient-to-r from-emerald-500 to-teal-500 hover:from-emerald-400 hover:to-teal-400 text-white font-bold px-4 py-2 rounded-lg shadow-lg flex items-center space-x-1" title="設定 AI 助教">
314
  <span>🧠 AI 設定</span>
315
  </button>
316
  </div>
 
338
 
339
  <div class="flex justify-end space-x-2">
340
  <button onclick="document.getElementById('ai-settings-modal').classList.add('hidden')" class="px-4 py-2 text-gray-400 hover:text-white">取消</button>
341
+ <button id="save-ai-settings" onclick="window.saveAiSettings()" class="px-6 py-2 bg-emerald-600 hover:bg-emerald-500 text-white rounded font-bold">儲存並啟動</button>
342
  </div>
343
  </div>
344
  </div>
 
346
  <!-- Fixed Bottom Right Controls -->
347
 
348
  <div class="fixed bottom-6 right-6 z-40 flex flex-col space-y-4">
349
+ <button id="btn-open-gallery" onclick="window.openGallery()" class="w-14 h-14 rounded-full bg-gray-800/80 backdrop-blur-md border border-cyan-500/30 text-cyan-400 shadow-[0_0_15px_rgba(6,182,212,0.3)] hover:scale-110 hover:bg-cyan-900/50 hover:border-cyan-400 transition-all duration-300 flex items-center justify-center group" title="怪獸圖鑑">
350
  <span class="text-2xl filter drop-shadow-[0_0_5px_rgba(34,211,238,0.8)] group-hover:animate-bounce">👾</span>
351
  </button>
352
+ <button id="logout-btn" onclick="window.instructorLogout()" class="w-14 h-14 rounded-full bg-gray-800/80 backdrop-blur-md border border-red-500/30 text-red-400 shadow-[0_0_15px_rgba(239,68,68,0.3)] hover:scale-110 hover:bg-red-900/50 hover:border-red-400 transition-all duration-300 flex items-center justify-center" title="登出">
353
  <span class="text-xl filter drop-shadow-[0_0_5px_rgba(248,113,113,0.8)]">🚪</span>
354
  </button>
355
  </div>
 
381
  // --- Global Nav Functions (Hoisted) ---
382
  window.openAdminPanel = () => {
383
  if (!window.currentInstructor?.permissions?.includes('add_question')) return alert("無權限");
384
+ if (confirm("即將離開儀表板前往題目管理頁面,確定嗎?")) {
385
+ window.location.hash = 'admin';
386
+ }
387
  };
388
 
389
  window.openInstructorPanel = async () => {
 
418
  }
419
  };
420
 
421
+ // --- Additional Global Button Functions ---
422
+ window.instructorLogout = async () => {
423
+ if (confirm("確定要登出嗎?")) {
424
+ try {
425
+ const { signOutUser } = await import("../services/auth.js");
426
+ await signOutUser();
427
+ window.location.reload();
428
+ } catch (e) {
429
+ console.error("Logout failed:", e);
430
+ alert("登出失敗");
431
+ }
432
+ }
433
+ };
434
+
435
+ window.openGallery = () => {
436
+ window.open('monster_preview.html', '_blank');
437
+ };
438
+
439
+ window.createRoom = async () => {
440
+ const roomCode = Math.floor(1000 + Math.random() * 9000).toString();
441
+ try {
442
+ const roomInfo = document.getElementById('room-info');
443
+ const displayRoomCode = document.getElementById('display-room-code');
444
+ const createContainer = document.getElementById('create-room-container');
445
+ const dashboardContent = document.getElementById('dashboard-content');
446
+ const { createRoom: createRoomService } = await import("../services/classroom.js");
447
+ const { cleanupOldRooms } = await import("../services/classroom.js");
448
+
449
+ await createRoomService(roomCode, window.currentInstructor ? window.currentInstructor.name : 'Unknown');
450
+ cleanupOldRooms();
451
+
452
+ displayRoomCode.textContent = roomCode;
453
+ localStorage.setItem('vibecoding_room_code', roomCode);
454
+ localStorage.setItem('vibecoding_is_host', 'true');
455
+
456
+ createContainer.classList.add('hidden');
457
+ roomInfo.classList.remove('hidden');
458
+ dashboardContent.classList.remove('hidden');
459
+
460
+ const { subscribeToRoom } = await import("../services/classroom.js");
461
+ subscribeToRoom(roomCode, (data) => {
462
+ if (typeof window.updateDashboard === 'function') {
463
+ window.updateDashboard(data);
464
+ }
465
+ });
466
+ } catch (e) {
467
+ console.error(e);
468
+ alert("無法建立教室: " + e.message);
469
+ }
470
+ };
471
+
472
+ window.rejoinRoom = async () => {
473
+ const inputCode = document.getElementById('rejoin-room-code')?.value.trim().toUpperCase();
474
+ if (!inputCode) return alert("請輸入代碼");
475
+
476
+ try {
477
+ const roomInfo = document.getElementById('room-info');
478
+ const displayRoomCode = document.getElementById('display-room-code');
479
+ const createContainer = document.getElementById('create-room-container');
480
+ const dashboardContent = document.getElementById('dashboard-content');
481
+ const { subscribeToRoom } = await import("../services/classroom.js");
482
+
483
+ displayRoomCode.textContent = inputCode;
484
+ localStorage.setItem('vibecoding_room_code', inputCode);
485
+
486
+ createContainer.classList.add('hidden');
487
+ roomInfo.classList.remove('hidden');
488
+ dashboardContent.classList.remove('hidden');
489
+ const groupPhotoBtn = document.getElementById('group-photo-btn');
490
+ if (groupPhotoBtn) groupPhotoBtn.classList.remove('hidden');
491
+
492
+ subscribeToRoom(inputCode, async (data) => {
493
+ const users = Array.isArray(data) ? data : (data?.users ? Object.values(data.users) : []);
494
+ if (typeof window.renderTransposedHeatmap === 'function') {
495
+ window.renderTransposedHeatmap(users);
496
+ }
497
+ });
498
+ } catch (e) {
499
+ console.error(e);
500
+ alert("加入失敗: " + e.message);
501
+ }
502
+ };
503
+
504
+ window.setupAi = () => {
505
+ const key = localStorage.getItem('vibecoding_gemini_key') || '';
506
+ const studentEnabled = localStorage.getItem('vibecoding_student_ai_enabled') === 'true';
507
+
508
+ const geminiKeyInput = document.getElementById('gemini-api-key');
509
+ const toggleStudentAi = document.getElementById('toggle-student-ai');
510
+ const aiSettingsModal = document.getElementById('ai-settings-modal');
511
+
512
+ if (geminiKeyInput) geminiKeyInput.value = key;
513
+ if (toggleStudentAi) toggleStudentAi.checked = studentEnabled;
514
+ if (aiSettingsModal) aiSettingsModal.classList.remove('hidden');
515
+ };
516
+
517
+ window.saveAiSettings = async () => {
518
+ const geminiKeyInput = document.getElementById('gemini-api-key');
519
+ const toggleStudentAi = document.getElementById('toggle-student-ai');
520
+ const aiSettingsModal = document.getElementById('ai-settings-modal');
521
+
522
+ const key = geminiKeyInput ? geminiKeyInput.value.trim() : '';
523
+ const studentEnabled = toggleStudentAi ? toggleStudentAi.checked : false;
524
+
525
+ if (key) {
526
+ localStorage.setItem('vibecoding_gemini_key', key);
527
+ } else {
528
+ localStorage.removeItem('vibecoding_gemini_key');
529
+ }
530
+
531
+ localStorage.setItem('vibecoding_student_ai_enabled', studentEnabled);
532
+
533
+ const roomCode = localStorage.getItem('vibecoding_room_code');
534
+ if (roomCode) {
535
+ try {
536
+ const { getFirestore, doc, updateDoc } = await import("https://www.gstatic.com/firebasejs/10.7.1/firebase-firestore.js");
537
+ const db = getFirestore();
538
+ await updateDoc(doc(db, "classrooms", roomCode), { ai_active: studentEnabled });
539
+ } catch (e) {
540
+ console.error("Failed to sync AI setting to room:", e);
541
+ }
542
+ }
543
+
544
+ if (key) {
545
+ try {
546
+ const { initGemini } = await import("../services/gemini.js");
547
+ await initGemini(key);
548
+ alert("AI 設定已儲存並啟動!");
549
+ } catch (e) {
550
+ console.error(e);
551
+ alert("AI 啟動失敗: " + e.message);
552
+ }
553
+ } else {
554
+ alert("AI 設定已儲存 (停用)");
555
+ }
556
+
557
+ if (aiSettingsModal) aiSettingsModal.classList.add('hidden');
558
+ };
559
+
560
  // Login Logic
561
  const authErrorMsg = document.getElementById('auth-error');
562
  const authModal = document.getElementById('auth-modal');