Spiel31 / templates /index.html
Renday's picture
Update templates/index.html
013664c verified
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>31 CARD | Casino Online</title>
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;900&display=swap');
body { font-family: 'Inter', sans-serif; margin: 0; padding: 0; }
.main-wrapper {
display: flex; flex-direction: column; justify-content: center;
align-items: center; min-height: 100vh; width: 100%;
}
.card {
transition: all 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
cursor: pointer; aspect-ratio: 2/3; min-width: 80px; max-width: 110px; user-select: none;
}
.card.selected {
border: 4px solid #d4af37; box-shadow: 0 0 25px rgba(212, 175, 55, 0.5); transform: translateY(-20px) scale(1.05);
}
.poker-bg { background: radial-gradient(circle at center, #1a1c2c 0%, #0a0a0a 100%); }
#game-area.hidden { display: none; }
#cookie-banner {
position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
width: 90%; max-width: 600px; background: rgba(17, 24, 39, 0.95);
backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.1);
padding: 20px; border-radius: 24px; z-index: 1000; display: none;
}
</style>
</head>
<body class="poker-bg text-white overflow-x-hidden">
<div class="main-wrapper p-4">
<div class="w-full max-w-4xl bg-gray-900/80 backdrop-blur-2xl rounded-[2.5rem] shadow-2xl p-6 sm:p-10 border border-white/10">
<div id="login-area" class="text-center py-10">
<h1 class="text-7xl font-black text-yellow-500 mb-4 italic tracking-tighter drop-shadow-lg text-center uppercase">31 Card</h1>
<p class="mb-10 text-gray-400 uppercase tracking-[0.3em] text-[10px] font-bold">Spiel 31 Karten Ultimative PRO</p>
<div class="max-w-xs mx-auto space-y-4">
<input type="email" id="email" placeholder="E-Mail Adresse"
class="bg-gray-800/50 border border-gray-700 text-white px-6 py-4 rounded-2xl w-full outline-none focus:border-yellow-500 text-center text-lg transition-all">
<button onclick="doLogin()" class="bg-red-600 hover:bg-red-500 w-full py-4 rounded-2xl font-black uppercase tracking-widest transition-all shadow-xl active:scale-95">
Spiel beitreten
</button>
<p id="connection-status" class="text-[10px] text-gray-500 uppercase mt-6 tracking-widest font-bold font-sans">Checking Server...</p>
</div>
</div>
<div id="game-area" class="hidden">
<header class="flex justify-between items-start border-b border-white/5 pb-6 mb-8">
<div class="flex gap-6 sm:gap-10">
<div>
<span class="block text-[10px] text-gray-500 uppercase font-black mb-1 tracking-widest">Status</span>
<h2 id="status-text" class="text-sm font-black uppercase text-white italic leading-tight">Warten...</h2>
</div>
<div>
<span class="block text-[10px] text-yellow-500 uppercase font-black mb-1 tracking-widest">Einsatz</span>
<h2 id="display-bet" class="text-sm font-black text-white italic leading-tight">0 $</h2>
</div>
<div>
<span class="block text-[10px] text-green-500 uppercase font-black mb-1 tracking-widest">Pott</span>
<h2 id="display-pott" class="text-sm font-black text-white italic leading-tight">0 $</h2>
</div>
</div>
<div class="flex items-center gap-6">
<div class="text-right">
<p class="text-[10px] text-gray-500 uppercase font-black tracking-widest">Bankroll</p>
<p id="user-coins" class="text-2xl font-black text-green-400">0 $</p>
</div>
<button onclick="doLogout()" class="bg-red-500/10 hover:bg-red-500/20 text-red-500 p-3 rounded-2xl border border-red-500/20 active:scale-90 transition-all">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1" />
</svg>
</button>
</div>
</header>
<div id="admin-panel" class="hidden bg-yellow-500/10 p-5 rounded-3xl border border-yellow-500/20 mb-8 flex flex-wrap items-center justify-between gap-4">
<div class="flex flex-col">
<span class="text-[10px] text-yellow-500 font-black uppercase tracking-widest">Admin Control</span>
<div class="flex items-center gap-2 mt-2">
<input type="number" id="bet-input" value="50" class="bg-gray-800 border border-gray-700 text-yellow-500 text-xs font-black px-2 py-1 rounded w-16 outline-none">
<button onclick="changeBet()" class="text-[10px] bg-gray-700 text-white px-2 py-1 rounded font-bold uppercase hover:bg-gray-600 transition-all">Set Bet</button>
</div>
</div>
<div class="flex gap-2">
<button onclick="startRound()" class="bg-yellow-500 hover:bg-yellow-400 text-black px-4 py-2 rounded-xl font-black uppercase text-xs transition-all">Start</button>
<button onclick="reshuffle()" class="bg-blue-600 hover:bg-blue-500 text-white px-4 py-2 rounded-xl font-black uppercase text-xs transition-all">Mischen</button>
<button onclick="endGame()" class="bg-red-600 hover:bg-red-500 text-white px-4 py-2 rounded-xl font-black uppercase text-xs transition-all">Ende</button>
</div>
</div>
<div id="middle-cards" class="flex justify-center gap-4 sm:gap-8 mb-16 min-h-[150px] items-center"></div>
<div class="bg-white/5 p-8 rounded-[3rem] border border-white/10 relative">
<div class="absolute -top-4 left-1/2 -translate-x-1/2 bg-gray-900 px-6 py-1 rounded-full border border-white/10 text-[10px] text-gray-400 font-black uppercase tracking-widest">
SCORE: <span id="score" class="text-yellow-500 ml-1">0</span>
</div>
<div id="player-hand" class="flex justify-center gap-4 sm:gap-8 mb-10 min-h-[150px] items-center"></div>
<div class="flex flex-wrap justify-center gap-4">
<button onclick="action('swap_all')" class="bg-gray-800 hover:bg-gray-700 px-6 py-4 rounded-2xl font-black text-[10px] uppercase border border-white/5 tracking-widest transition-all">Tauschen (Alle)</button>
<button onclick="action('pass')" class="bg-blue-600 hover:bg-blue-500 px-6 py-4 rounded-2xl font-black text-[10px] uppercase border border-white/5 tracking-widest transition-all">Schieben (Weiter)</button>
<button onclick="action('knock')" class="bg-red-600 hover:bg-red-500 px-10 py-4 rounded-2xl font-black text-[10px] uppercase shadow-2xl border border-red-400/20 tracking-widest transition-all">Klopfen</button>
</div>
</div>
</div>
</div>
</div>
<div id="cookie-banner">
<div class="flex flex-col sm:flex-row items-center justify-between gap-6">
<div class="text-[12px] text-gray-300">
<p class="font-black text-white mb-1 uppercase tracking-wider">🍪 Data Privacy</p>
Diese App nutzt Cookies für die Sitzung.
</div>
<button onclick="acceptCookies()" class="bg-white text-black px-8 py-3 rounded-2xl font-black text-xs uppercase tracking-widest">OK</button>
</div>
</div>
<script>
const socket = io({ transports: ['websocket'] }); // HF nutzt meist relative Pfade für WebSockets
let myEmail = "", selHandIdx = null;
window.onload = function() {
if(!localStorage.getItem('cookiesAccepted')) {
document.getElementById('cookie-banner').style.display = 'block';
}
};
function acceptCookies() {
localStorage.setItem('cookiesAccepted', 'true');
document.getElementById('cookie-banner').style.display = 'none';
}
async function doLogin() {
myEmail = document.getElementById('email').value;
if(!myEmail.includes('@')) return alert("Bitte E-Mail eingeben!");
const res = await fetch(`/login`, {
method: 'POST', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({email: myEmail})
});
const data = await res.json();
document.getElementById('user-coins').innerText = data.stats.coins + " $";
if(data.stats.is_admin) document.getElementById('admin-panel').classList.remove('hidden');
document.getElementById('login-area').classList.add('hidden');
document.getElementById('game-area').classList.remove('hidden');
socket.emit('join_game', {email: myEmail});
}
function doLogout() { location.reload(); }
function startRound() { socket.emit('start_round', {email: myEmail}); }
function reshuffle() { socket.emit('admin/reshuffle', {email: myEmail}); }
function endGame() { socket.emit('admin/end_game', {email: myEmail}); }
function changeBet() { socket.emit('admin/update_bet', { email: myEmail, bet: document.getElementById('bet-input').value }); }
function action(t) { socket.emit('player_action', {email: myEmail, type: t}); }
socket.on('update_table', (data) => {
const me = data.players.find(p => p.email === myEmail);
const isMyTurn = data.players[data.turn_idx]?.email === myEmail;
document.getElementById('status-text').innerText = isMyTurn ? "DEIN ZUG!" : "WARTE...";
document.getElementById('status-text').className = isMyTurn ? "text-sm font-black text-green-400 animate-pulse uppercase tracking-widest" : "text-sm font-black text-gray-500 uppercase tracking-widest";
document.getElementById('display-bet').innerText = data.current_bet + " $";
document.getElementById('display-pott').innerText = (data.pott || 0) + " $";
const getSymbol = (s) => ({ 'Herz': '&hearts;', 'Karo': '&diams;', 'Pik': '&spades;', 'Kreuz': '&clubs;' }[s]);
const getColor = (s) => (s === 'Herz' || s === 'Karo') ? 'text-red-600' : 'text-gray-900';
const cardHTML = (c, onClick, sel = false) => `
<div onclick="${onClick}" class="card bg-white rounded-2xl flex flex-col items-center justify-between p-4 shadow-2xl text-black ${sel ? 'selected' : ''}">
<div class="self-start font-black text-xl leading-none ${getColor(c.suit)}">${c.rank}</div>
<div class="text-5xl ${getColor(c.suit)}">${getSymbol(c.suit)}</div>
<div class="self-end font-black text-xl leading-none rotate-180 ${getColor(c.suit)}">${c.rank}</div>
</div>`;
document.getElementById('middle-cards').innerHTML = data.middle.map((c, i) => cardHTML(c, `swap(${i})`)).join('');
if(me) {
document.getElementById('user-coins').innerText = me.coins + " $";
document.getElementById('score').innerText = me.score;
document.getElementById('player-hand').innerHTML = me.hand.length > 0 ?
me.hand.map((c, i) => cardHTML(c, `selectHand(${i})`, selHandIdx === i)).join('') :
`<p class="text-gray-400 italic text-[10px] mt-10 uppercase font-bold">Warte auf Start...</p>`;
}
});
function selectHand(i) { selHandIdx = (selHandIdx === i) ? null : i; socket.emit('join_game', {email: myEmail}); }
function swap(mIdx) { if(selHandIdx !== null) { socket.emit('player_action', {email: myEmail, type: 'swap_one', h_idx: selHandIdx, m_idx: mIdx}); selHandIdx = null; } }
socket.on('game_over', (d) => {
alert(`RUNDE BEENDET!\n\nGewinner: ${d.winner}\nPunkte: ${d.score}\nPott: ${d.pott} $`);
});
</script>
</body>
</html>