Spaces:
Running
Running
| import { createRoom, joinRoom } from "../services/classroom.js"; | |
| import { generateMonsterSVG, MONSTER_DEFS } from "../utils/monsterUtils.js"; | |
| export function renderLandingView() { | |
| // Select Decor Monsters | |
| // Left: Genesis Dragon (L3_AAA), Right: Gundam (L3_BAA) - or fallbacks | |
| const mLeft = MONSTER_DEFS.find(m => m.id === 'L3_AAA') || MONSTER_DEFS.find(m => m.stage === 3); | |
| const mRight = MONSTER_DEFS.find(m => m.id === 'L3_BAA') || MONSTER_DEFS.find(m => m.stage === 3); | |
| const svgLeft = generateMonsterSVG(mLeft); | |
| const svgRight = generateMonsterSVG(mRight); | |
| return ` | |
| <div class="min-h-screen flex flex-col items-center justify-center p-4 relative overflow-hidden"> | |
| <!-- Decor Monsters (Desktop Only) --> | |
| <div class="absolute bottom-10 left-10 w-48 h-48 hidden lg:block pointer-events-none" | |
| style="animation: float 6s ease-in-out infinite;"> | |
| <div class="w-full h-full drop-shadow-[0_0_15px_rgba(34,211,238,0.5)]"> | |
| ${svgLeft} | |
| </div> | |
| </div> | |
| <div class="absolute bottom-10 right-10 w-48 h-48 hidden lg:block pointer-events-none" | |
| style="animation: float 8s ease-in-out infinite reverse;"> | |
| <div class="w-full h-full drop-shadow-[0_0_15px_rgba(59,130,246,0.5)]"> | |
| ${svgRight} | |
| </div> | |
| </div> | |
| <div class="max-w-md w-full bg-gray-600 bg-opacity-20 backdrop-blur-lg rounded-xl shadow-2xl p-8 border border-gray-700 z-10"> | |
| <h1 class="text-3xl sm:text-4xl font-bold text-transparent bg-clip-text bg-gradient-to-r from-cyan-400 to-purple-500 mb-8 text-center tracking-tighter whitespace-nowrap"> | |
| VIBECODING-怪獸成長營 | |
| </h1> | |
| <!-- Student Join Form --> | |
| <div id="student-form" class="space-y-6"> | |
| <div> | |
| <label class="block text-gray-400 text-sm font-bold mb-2">教室代碼 (Room Code)</label> | |
| <input type="text" id="room-code-input" class="w-full bg-gray-800 text-white border border-gray-600 rounded-lg py-3 px-4 focus:outline-none focus:border-cyan-500 transition-colors" placeholder="1234"> | |
| </div> | |
| <div> | |
| <label class="block text-gray-400 text-sm font-bold mb-2">您的暱稱 (Nickname)</label> | |
| <input type="text" id="nickname-input" class="w-full bg-gray-800 text-white border border-gray-600 rounded-lg py-3 px-4 focus:outline-none focus:border-purple-500 transition-colors" placeholder="小明"> | |
| </div> | |
| <button id="join-btn" class="w-full bg-gradient-to-r from-cyan-600 to-blue-600 hover:from-cyan-500 hover:to-blue-500 text-white font-bold py-3 px-4 rounded-lg transform transition hover:scale-105 active:scale-95 shadow-lg shadow-cyan-500/30"> | |
| 進入教室 | |
| </button> | |
| </div> | |
| <!-- Instructor Toggle --> | |
| <div class="mt-8 pt-6 border-t border-gray-700 text-center"> | |
| <button id="instructor-mode-btn" class="text-gray-500 text-sm hover:text-cyan-400 transition-colors"> | |
| 我是講師 (Instructor Mode) | |
| </button> | |
| </div> | |
| </div> | |
| <style> | |
| @keyframes float { | |
| 0%, 100% { transform: translateY(0); } | |
| 50% { transform: translateY(-20px); } | |
| } | |
| </style> | |
| </div> | |
| `; | |
| } | |
| export function setupLandingEvents(navigateTo) { | |
| const joinBtn = document.getElementById('join-btn'); | |
| const instructorBtn = document.getElementById('instructor-mode-btn'); | |
| joinBtn.addEventListener('click', async () => { | |
| const roomCode = document.getElementById('room-code-input').value.trim(); | |
| const nickname = document.getElementById('nickname-input').value.trim(); | |
| if (!roomCode || !nickname) { | |
| alert('請輸入教室代碼和暱稱'); | |
| return; | |
| } | |
| try { | |
| joinBtn.textContent = '加入中...'; | |
| joinBtn.disabled = true; | |
| const studentId = await joinRoom(roomCode, nickname); | |
| // Save Session | |
| localStorage.setItem('vibecoding_user_id', studentId); | |
| localStorage.setItem('vibecoding_room_code', roomCode); | |
| localStorage.setItem('vibecoding_nickname', nickname); | |
| navigateTo('student'); | |
| } catch (error) { | |
| alert('加入失敗: ' + error.message); | |
| joinBtn.textContent = '進入教室'; | |
| joinBtn.disabled = false; | |
| } | |
| }); | |
| instructorBtn.addEventListener('click', () => { | |
| // Clear any previous admin referer to ensure clean state | |
| localStorage.removeItem('vibecoding_admin_referer'); | |
| navigateTo('instructor'); | |
| }); | |
| } | |