Update templates/index.html
Browse files- templates/index.html +87 -64
templates/index.html
CHANGED
|
@@ -7,104 +7,127 @@
|
|
| 7 |
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
| 8 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
<style>
|
| 10 |
-
.card { transition: all 0.2s; cursor: pointer; aspect-ratio: 2/3; width:
|
| 11 |
-
.card.selected { border: 3px solid #
|
| 12 |
-
.poker-bg { background: radial-gradient(circle, #1a1a1a 0%, #
|
| 13 |
-
.suit-red { color: #ef4444; }
|
| 14 |
-
* { user-select: none; -webkit-tap-highlight-color: transparent; }
|
| 15 |
</style>
|
| 16 |
</head>
|
| 17 |
-
<body class="poker-bg text-white min-h-screen flex
|
| 18 |
|
| 19 |
-
<div
|
| 20 |
-
|
| 21 |
-
<
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
<
|
| 28 |
-
<button onclick="startRound()" class="bg-yellow-500 text-black px-4 py-1 rounded-lg text-[10px] font-bold uppercase italic">Spiel Starten</button>
|
| 29 |
</div>
|
| 30 |
|
| 31 |
-
<div id="
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
</div>
|
| 44 |
</div>
|
| 45 |
</div>
|
| 46 |
|
| 47 |
<script>
|
| 48 |
-
const
|
| 49 |
-
|
|
|
|
| 50 |
|
| 51 |
async function doLogin() {
|
| 52 |
-
myEmail = document.getElementById('email').value
|
| 53 |
-
if(!myEmail.includes('@')) return alert("
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
body: JSON.stringify({email: myEmail})
|
| 59 |
});
|
| 60 |
const data = await res.json();
|
| 61 |
-
|
| 62 |
-
if(data.stats
|
| 63 |
document.getElementById('login-area').classList.add('hidden');
|
| 64 |
document.getElementById('game-area').classList.remove('hidden');
|
| 65 |
socket.emit('join_game', {email: myEmail});
|
| 66 |
}
|
| 67 |
|
| 68 |
-
function
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
const icons = {'Herz':'♥','Karo':'♦','Pik':'♠','Kreuz':'♣'};
|
| 72 |
-
return `
|
| 73 |
-
<div onclick="${onClick}" class="card bg-white rounded-xl flex flex-col items-center justify-between p-2 text-black font-bold ${isSelected ? 'selected' : ''}">
|
| 74 |
-
<div class="self-start text-xs ${isRed ? 'suit-red' : ''}">${c.rank}</div>
|
| 75 |
-
<div class="text-3xl ${isRed ? 'suit-red' : ''}">${icons[c.suit]}</div>
|
| 76 |
-
<div class="self-end text-xs rotate-180 ${isRed ? 'suit-red' : ''}">${c.rank}</div>
|
| 77 |
-
</div>`;
|
| 78 |
-
}
|
| 79 |
|
| 80 |
socket.on('update_table', (data) => {
|
| 81 |
const me = data.players.find(p => p.email === myEmail);
|
| 82 |
-
const
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
document.getElementById('status-text').
|
| 86 |
-
document.getElementById('status-text').className = isMyTurn ? "text-green-500 text-center text-[10px] tracking-widest mb-6 uppercase font-bold animate-pulse" : "text-gray-500 text-center text-[10px] tracking-widest mb-6 uppercase font-bold";
|
| 87 |
|
| 88 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
|
|
|
|
| 90 |
if(me) {
|
| 91 |
-
document.getElementById('
|
| 92 |
-
|
| 93 |
-
|
|
|
|
|
|
|
| 94 |
}
|
| 95 |
});
|
| 96 |
|
| 97 |
-
function
|
| 98 |
-
function
|
| 99 |
-
|
| 100 |
-
}
|
| 101 |
function swap(mIdx) {
|
| 102 |
-
if(selHandIdx === null) return
|
| 103 |
socket.emit('player_action', {email: myEmail, type: 'swap_one', h_idx: selHandIdx, m_idx: mIdx});
|
| 104 |
selHandIdx = null;
|
| 105 |
}
|
| 106 |
-
function startRound() { socket.emit('start_round', {email: myEmail}); }
|
| 107 |
function action(t) { socket.emit('player_action', {email: myEmail, type: t}); }
|
|
|
|
| 108 |
</script>
|
| 109 |
</body>
|
| 110 |
</html>
|
|
|
|
| 7 |
<script src="https://cdn.socket.io/4.7.2/socket.io.min.js"></script>
|
| 8 |
<script src="https://cdn.tailwindcss.com"></script>
|
| 9 |
<style>
|
| 10 |
+
.card { transition: all 0.2s; cursor: pointer; aspect-ratio: 2/3; min-width: 70px; max-width: 100px; }
|
| 11 |
+
.card.selected { border: 3px solid #d4af37; box-shadow: 0 0 15px #d4af37; transform: translateY(-10px); }
|
| 12 |
+
.poker-bg { background: radial-gradient(circle, #1a1a1a 0%, #0a0a0a 100%); }
|
|
|
|
|
|
|
| 13 |
</style>
|
| 14 |
</head>
|
| 15 |
+
<body class="poker-bg text-white min-h-screen flex items-center justify-center p-2 sm:p-4 font-sans overflow-x-hidden">
|
| 16 |
|
| 17 |
+
<div class="w-full max-w-4xl bg-gray-900/90 backdrop-blur-xl rounded-3xl shadow-2xl p-4 sm:p-8 border border-white/10">
|
| 18 |
+
|
| 19 |
+
<div id="login-area" class="text-center py-12">
|
| 20 |
+
<h1 class="text-6xl font-black text-yellow-500 mb-2 italic">31 CARD</h1>
|
| 21 |
+
<p class="mb-8 text-gray-500 uppercase tracking-widest text-xs">52 Cards • Responsive</p>
|
| 22 |
+
<div class="max-w-xs mx-auto space-y-4">
|
| 23 |
+
<input type="email" id="email" placeholder="Deine E-Mail" class="bg-gray-800 border border-gray-700 text-white px-4 py-3 rounded-xl w-full outline-none focus:border-yellow-500 text-center">
|
| 24 |
+
<button onclick="doLogin()" class="bg-red-600 hover:bg-red-500 w-full py-4 rounded-xl font-bold uppercase tracking-widest transition-all">Anmelden</button>
|
| 25 |
+
</div>
|
|
|
|
| 26 |
</div>
|
| 27 |
|
| 28 |
+
<div id="game-area" class="hidden">
|
| 29 |
+
<header class="flex justify-between items-center border-b border-gray-800 pb-4 mb-6">
|
| 30 |
+
<div>
|
| 31 |
+
<h2 id="status-text" class="text-xs font-bold uppercase tracking-widest text-gray-500 italic">Warte auf Start...</h2>
|
| 32 |
+
</div>
|
| 33 |
+
<div class="flex items-center gap-4">
|
| 34 |
+
<div class="text-right">
|
| 35 |
+
<p class="text-[10px] text-gray-500 uppercase font-black">Bankroll</p>
|
| 36 |
+
<p id="user-coins" class="text-xl font-black text-green-500">0 $</p>
|
| 37 |
+
</div>
|
| 38 |
+
<button onclick="doLogout()" class="text-red-500 bg-red-500/10 p-2 rounded-lg border border-red-500/20">
|
| 39 |
+
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" 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>
|
| 40 |
+
</button>
|
| 41 |
+
</div>
|
| 42 |
+
</header>
|
| 43 |
|
| 44 |
+
<div id="admin-panel" class="hidden bg-yellow-500/5 p-4 rounded-2xl border border-yellow-500/20 mb-6 flex flex-wrap items-center justify-between gap-4">
|
| 45 |
+
<div>
|
| 46 |
+
<p class="text-[10px] font-black text-yellow-500 uppercase">Admin Dealer</p>
|
| 47 |
+
<p class="text-[10px] text-gray-500">Starteinsatz festlegen</p>
|
| 48 |
+
</div>
|
| 49 |
+
<div class="flex gap-2">
|
| 50 |
+
<input type="number" id="bet-input" value="50" class="bg-gray-800 w-16 px-2 py-1 rounded text-sm outline-none border border-gray-700">
|
| 51 |
+
<button onclick="setBet()" class="bg-yellow-600 text-black px-3 py-1 rounded text-[10px] font-bold uppercase">Save</button>
|
| 52 |
+
<button onclick="startRound()" class="bg-green-600 px-4 py-1 rounded text-[10px] font-black uppercase italic">Start</button>
|
| 53 |
+
</div>
|
| 54 |
+
</div>
|
| 55 |
+
|
| 56 |
+
<div id="middle-cards" class="flex justify-center gap-2 sm:gap-4 mb-10 overflow-x-auto pb-4"></div>
|
| 57 |
+
|
| 58 |
+
<div class="bg-black/40 p-6 rounded-[2rem] border border-white/5 relative">
|
| 59 |
+
<div class="text-center mb-6">
|
| 60 |
+
<span class="text-[10px] text-gray-500 uppercase font-black tracking-widest">Deine Punkte:</span>
|
| 61 |
+
<span id="score" class="text-yellow-500 font-black ml-2 text-xl">0</span>
|
| 62 |
+
</div>
|
| 63 |
+
<div id="player-hand" class="flex justify-center gap-2 sm:gap-4 mb-8"></div>
|
| 64 |
+
<div class="flex justify-center gap-4">
|
| 65 |
+
<button onclick="action('swap_all')" class="bg-gray-800 px-6 py-3 rounded-xl font-bold text-[10px] uppercase">Alle Tauschen</button>
|
| 66 |
+
<button onclick="action('knock')" class="bg-red-600 px-8 py-3 rounded-xl font-black text-[10px] uppercase shadow-lg shadow-red-900/40">Klopfen</button>
|
| 67 |
+
</div>
|
| 68 |
</div>
|
| 69 |
</div>
|
| 70 |
</div>
|
| 71 |
|
| 72 |
<script>
|
| 73 |
+
const SPACE_URL = "https://renday-spiel31.hf.space";
|
| 74 |
+
const socket = io(SPACE_URL, { transports: ['websocket'] });
|
| 75 |
+
let myEmail = "", selHandIdx = null;
|
| 76 |
|
| 77 |
async function doLogin() {
|
| 78 |
+
myEmail = document.getElementById('email').value;
|
| 79 |
+
if(!myEmail.includes('@')) return alert("E-Mail!");
|
| 80 |
+
const res = await fetch(`${SPACE_URL}/login`, {
|
| 81 |
+
method: 'POST',
|
| 82 |
+
headers: {'Content-Type': 'application/json'},
|
| 83 |
+
body: JSON.stringify({email: myEmail})
|
|
|
|
| 84 |
});
|
| 85 |
const data = await res.json();
|
| 86 |
+
document.getElementById('user-coins').innerText = data.stats.coins + " $";
|
| 87 |
+
if(data.stats.is_admin) document.getElementById('admin-panel').classList.remove('hidden');
|
| 88 |
document.getElementById('login-area').classList.add('hidden');
|
| 89 |
document.getElementById('game-area').classList.remove('hidden');
|
| 90 |
socket.emit('join_game', {email: myEmail});
|
| 91 |
}
|
| 92 |
|
| 93 |
+
function doLogout() { if(confirm("Abmelden?")) { socket.emit('leave_game', {email: myEmail}); location.reload(); } }
|
| 94 |
+
function setBet() { socket.emit('admin/set_bet', {bet: document.getElementById('bet-input').value}); }
|
| 95 |
+
function startRound() { socket.emit('start_round', {email: myEmail}); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
socket.on('update_table', (data) => {
|
| 98 |
const me = data.players.find(p => p.email === myEmail);
|
| 99 |
+
const isMyTurn = data.players[data.turn_idx]?.email === myEmail;
|
| 100 |
+
|
| 101 |
+
document.getElementById('status-text').innerText = isMyTurn ? "DU BIST DRAN" : `WARTEN...`;
|
| 102 |
+
document.getElementById('status-text').className = isMyTurn ? "text-green-400 font-black animate-pulse" : "text-gray-500";
|
|
|
|
| 103 |
|
| 104 |
+
const cardHTML = (c, onClick, sel = false) => `
|
| 105 |
+
<div onclick="${onClick}" class="card bg-white rounded-xl flex flex-col items-center justify-between p-2 sm:p-3 shadow-2xl text-black ${sel ? 'selected' : ''}">
|
| 106 |
+
<div class="self-start font-black text-sm sm:text-lg leading-none ${getColor(c.suit)}">${c.rank}</div>
|
| 107 |
+
<div class="text-2xl sm:text-4xl">${getSymbol(c.suit)}</div>
|
| 108 |
+
<div class="self-end font-black text-sm sm:text-lg leading-none rotate-180 ${getColor(c.suit)}">${c.rank}</div>
|
| 109 |
+
</div>`;
|
| 110 |
|
| 111 |
+
document.getElementById('middle-cards').innerHTML = data.middle.map((c, i) => cardHTML(c, `swap(${i})`)).join('');
|
| 112 |
if(me) {
|
| 113 |
+
document.getElementById('user-coins').innerText = me.coins + " $";
|
| 114 |
+
if(me.hand.length > 0) {
|
| 115 |
+
document.getElementById('score').innerText = me.score;
|
| 116 |
+
document.getElementById('player-hand').innerHTML = me.hand.map((c, i) => cardHTML(c, `selectHand(${i})`, selHandIdx === i)).join('');
|
| 117 |
+
}
|
| 118 |
}
|
| 119 |
});
|
| 120 |
|
| 121 |
+
function getSymbol(s) { return { 'Herz': '♥', 'Karo': '♦', 'Pik': '♠', 'Kreuz': '♣' }[s] || s; }
|
| 122 |
+
function getColor(s) { return (s === 'Herz' || s === 'Karo') ? 'text-red-600' : 'text-gray-900'; }
|
| 123 |
+
function selectHand(i) { selHandIdx = (selHandIdx === i) ? null : i; socket.emit('join_game', {email: myEmail}); }
|
|
|
|
| 124 |
function swap(mIdx) {
|
| 125 |
+
if(selHandIdx === null) return;
|
| 126 |
socket.emit('player_action', {email: myEmail, type: 'swap_one', h_idx: selHandIdx, m_idx: mIdx});
|
| 127 |
selHandIdx = null;
|
| 128 |
}
|
|
|
|
| 129 |
function action(t) { socket.emit('player_action', {email: myEmail, type: t}); }
|
| 130 |
+
socket.on('game_over', (d) => alert("Winner: " + d.winner));
|
| 131 |
</script>
|
| 132 |
</body>
|
| 133 |
</html>
|