Vibecodingex / src /views /LandingView.js
Lashtw's picture
Upload 9 files
1662a78 verified
raw
history blame
4.99 kB
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');
});
}