publicquiz/templates/player.html
2025-06-28 22:31:22 +02:00

172 lines
5.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- player.html -->
{% extends 'base.html' %}
{% block content %}
<style>
.option.selected {
background-color:rgb(110, 168, 255); /* bootstrap “primary” bg */
color: black;
}
.option.disabled {
background-color:rgb(148, 148, 148); /* bootstrap “secondary” bg */
color: black;
}
.option.correct {
border: 5px solid #198754 !important; /* bootstrap “success” green */
}
</style>
<div class="container" id="player-app">
<!-- Join View -->
<div id="view-join" {% if state == 'join' %} {% else %}style="display:none;"{% endif %}>
<h1 class="mb-4">Dem Quiz Beitreten</h1>
{% if not error %}
<form id="joinForm" action="{{ url_for('do_join') }}" method="post">
<div class="input-group mb-3">
<input name="name" id="nameInput" type="text"
class="form-control" placeholder="Name eingeben" />
<button type="submit" class="btn btn-success">Beitreten</button>
</div>
</form>
{% else %}
<!-- show backend-passed error if present -->
<div id="joinStatus" class="mt-2">
<div class="alert alert-danger" role="alert">{{ error }}</div>
</div>
{% endif %}
</div>
<!-- Wait View -->
<div id="view-wait" {% if state == 'waiting' %} {% else %}style="display:none;"{% endif %}>
<div class="alert alert-info mt-4" role="alert">
Bitte warten bis das Spiel beginnt.
</div>
</div>
<!-- Options View -->
<div id="view-question" {% if state == 'question' %} {% else %}style="display:none;"{% endif %}>
<div id="options" class="d-grid gap-2"></div>
</div>
<!-- End View -->
<div id="view-end" style="display:none;">
<div class="alert alert-success mt-4 text-center">
<h2>Spiel beendet!</h2>
<p>Du hast <strong><span id="playerScore">0</span> Punkte</strong> erzielt und Platz <strong><span id="playerRank"></span></strong> belegt.</p>
</div>
</div>
</div>
<script>
const socket = io();
// Restore state on load
socket.on('connect', () => {
socket.emit('rejoin_game');
});
// Sync initial or reconnect state
socket.on('sync_state', data => {
if (data.state === 'waiting') {
$('#view-join, #view-question').hide();
$('#view-wait').show();
}
else if (data.state === 'question') {
$('#view-join, #view-wait').hide();
$('#view-question').show();
renderOptions(data.options, data.answered);
}
else if (data.state === 'end') {
$('#view-join, #view-wait, #view-question').hide();
$('#view-end').show();
$('#playerScore').text(data.score);
$('#playerRank').text(data.placement);
}
});
// Host clicked “Start Game”
socket.on('game_started', () => {
$('#view-join, #view-wait').hide();
$('#view-question').show();
});
// A new question just dropped
socket.on('new_question', data => {
$('#view-join, #view-wait').hide();
$('#view-question').show();
renderOptions(data.options, false);
});
// when the right answer is revealed
socket.on('answer_revealed', data => {
$('.option').each(function() {
const $opt = $(this);
if ($opt.text() === data.correct) {
// highlight as correct with green outline
$opt
.addClass('correct')
.removeClass('btn-outline-primary disabled')
.prop('disabled', true);
} else {
// any un-clicked options become permanently disabled
if (!$opt.hasClass('selected')) {
$opt
.addClass('disabled')
.removeClass('btn-outline-primary btn-primary')
.prop('disabled', true);
}
}
});
});
// Handle answer clicks
$(document).on('click', '.option', function() {
const $all = $('.option');
const $me = $(this);
const answer = $me.text();
// emit once
socket.emit('submit_answer', { answer });
// style the clicked one as selected
$me
.addClass('selected')
.removeClass('btn-outline-primary')
.prop('disabled', true);
// style all the others as disabled
$all.not($me)
.addClass('disabled')
.removeClass('btn-outline-primary btn-primary')
.prop('disabled', true);
});
// When re-rendering options (e.g. on sync or new_question), clear any classes:
function renderOptions(options, alreadyAnswered) {
const $opts = $('#options').empty();
options.forEach(opt => {
const btn = $(`<button class="btn option btn-outline-primary btn-lg mb-2">${opt}</button>`);
if (alreadyAnswered) {
btn.addClass('disabled')
.removeClass('btn-outline-primary')
.prop('disabled', true);
}
$opts.append(btn);
});
}
// Handle end-of-game for players
socket.on('game_over', data => {
if (data.placement !== undefined) {
// hide everything else
$('#view-join, #view-wait, #view-question').hide();
// show final screen
$('#view-end').show();
// fill in score & rank
$('#playerScore').text(data.score);
$('#playerRank').text(data.placement);
}
})
</script>
{% endblock %}