- Implement WebSocket-based lobby system for quiz participants - Add lobby page with real-time participant updates - Create LobbyConsumer for WebSocket communication - Add routing configuration for WebSocket connections - Update requirements with WebSocket dependencies - Add development and production server documentation This change enables real-time quiz lobbies where participants can join and wait for the quiz to start, with instant updates for all connected users.
227 lines
9.6 KiB
HTML
227 lines
9.6 KiB
HTML
{% extends 'base.html' %}
|
|
{% block content %}
|
|
{% load static %}
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<link rel="stylesheet" href="{% static 'swiper/swiper-bundle.min.css' %}">
|
|
<style>
|
|
|
|
|
|
|
|
|
|
.swiper {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.swiper-slide {
|
|
text-align: center;
|
|
font-size: 18px;
|
|
background: #fff;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
}
|
|
|
|
|
|
</style>
|
|
</head>
|
|
|
|
<div class="flex justify-end mr-2">
|
|
<a href="{% url 'library:new_quiz' %}" class="mt-2 p-2 shadow-md border-blue-600 border-2 rounded-md text-black ">Neues Quiz erstellen</a>
|
|
</div>
|
|
<div class="flex justify-end mr-2">
|
|
<a href="{% url 'library:overview_quiz' %}" class="mt-2 p-2 shadow-md border-blue-600 border-2 rounded-md text-black ">Suche und Filter zurücksetzen</a>
|
|
</div>
|
|
|
|
<div class="w-screen flex justify-center items-center">
|
|
<div class="w-full m-4 border-blue-600 border-2 rounded-xl shadow-md p-6 lg:w-1/4">
|
|
<form method="get" class="flex flex-col space-y-4">
|
|
<h1 class="text-xl font-bold">Filter</h1>
|
|
Minimale Anzahl an Fragen {{ form.min_amout_questions }}
|
|
Maximale Anzahl an Fragen {{ form.max_amout_questions }}
|
|
von User {{ form.user }}
|
|
<!-- Rendert die Form-Felder -->
|
|
<button type="submit" class="qp-a-button-small border-2 border-blue-600 text-black">Filtern</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<!-- Swiper Container für eigene Quiz -->
|
|
{% if filter_my_quizzes %}
|
|
<div class="text-center m-8 bg-blue-600 grid mx-8 h-8 place-items-center rounded-xl shadow-md ">
|
|
<h1 id="my-quizzes" class="flex px-4 font-bold text-white">Eigene Quiz</h1>
|
|
</div>
|
|
|
|
<div class="swiper mySwiper">
|
|
<div class="swiper-wrapper">
|
|
{% for quiz in filter_my_quizzes %}
|
|
<div class="swiper-slide">
|
|
<div class="relative h-80 bg-white bg-cover text-white w-full max-w-md rounded-lg p-4 shadow-md border-blue-600 border-2 rounded-xl flex flex-col justify-between"
|
|
{% if quiz.image %} style="background-image: url('http://127.0.0.1:8000/library{{ quiz.image.url }}');" {% endif %}>
|
|
<div class="absolute inset-0 bg-gray-900/60 bg-opacity-50 rounded-lg"></div>
|
|
|
|
<h2 class=" break-words truncate text-white drop-shadow-lg custom-outline"> <a href="{% url 'library:detail_quiz' quiz.id %}">{{ quiz.name }}</a></h2>
|
|
|
|
<p class="text-white font-bold text-white drop-shadow-lg custom-outline">
|
|
<span class="break-words line-clamp-2 ">{{ quiz.description }}</span><br>
|
|
Schwierigkeit: <span class="font-bold">{{ quiz.difficulty }}</span><br>
|
|
Anzahl der Fragen: <span class="font-bold ">{{ quiz.question.count }}</span><br>
|
|
Status: <span class="font-bold ">{{ quiz.status }}</span>
|
|
|
|
|
|
<p class=" font-bold break-words truncate text-white drop-shadow-lg custom-outline">
|
|
{% if quiz.authors.all %}
|
|
Autor(en):
|
|
{% for author in quiz.authors.all %}
|
|
|
|
{{ author.username }}{% if not forloop.last %}, {% endif %}
|
|
{% endfor %}
|
|
</p>
|
|
{% endif %}
|
|
|
|
|
|
</p>
|
|
|
|
<!-- Buttons bleiben am unteren Rand -->
|
|
<div class="flex justify-between items-center gap-2 ">
|
|
<a href="{% url 'play:create_game' quiz.id %}" class="qp-a-button-small border-2 border-blue-600 text-white font-bold text-white drop-shadow-lg custom-outline">Spiel starten</a>
|
|
<div class="flex gap-2">
|
|
<a href="{% url 'library:detail_quiz' quiz.id %}" class="qp-a-button-small border-2 border-blue-600 text-white font-bold drop-shadow-lg custom-outline">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-7">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
|
|
</svg>
|
|
</a>
|
|
<a href="{% url 'library:delete_quiz' quiz.id %}" class="qp-a-button-small border-2 border-blue-600 text-white text-white font-bold text-white drop-shadow-lg custom-outline">
|
|
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-7">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
|
|
</svg>
|
|
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
</div>
|
|
|
|
{% if filter_my_quizzes|length > 1 %}
|
|
<div class="swiper-button-next"></div>
|
|
<div class="swiper-button-prev"></div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<!-- Swiper Container für Quiz von anderen Nutzern -->
|
|
{% if filter_other_quizzes %}
|
|
<div class="text-center m-8 bg-blue-600 grid mx-8 h-8 place-items-center rounded-xl shadow-md">
|
|
<h1 id="other-quizzes" class="flex px-4 font-bold text-white">Quiz von anderen Nutzern</h1>
|
|
</div>
|
|
|
|
<div class="swiper mySwiper">
|
|
<div class="swiper-wrapper">
|
|
|
|
|
|
{% for quiz in filter_other_quizzes %}
|
|
<div class="swiper-slide">
|
|
<div class="relative h-80 bg-white bg-cover text-white w-full max-w-md rounded-lg p-4 shadow-md border-blue-600 border-2 rounded-xl flex flex-col justify-between"
|
|
{% if quiz.image %} style="background-image: url('http://127.0.0.1:8000/library{{ quiz.image.url }}');" {% endif %}>
|
|
<div class="absolute inset-0 bg-gray-900/60 bg-opacity-50 rounded-lg"></div>
|
|
|
|
<h2 class="break-words truncate text-white drop-shadow-lg custom-outline"> <a href="{% url 'library:detail_quiz' quiz.id %}">{{ quiz.name }}</a></h2>
|
|
|
|
<p class="text-white font-bold text-white drop-shadow-lg custom-outline">
|
|
<span class="break-words line-clamp-2 ">{{ quiz.description }}</span><br>
|
|
Schwierigkeit: <span class="font-bold">{{ quiz.difficulty }}</span><br>
|
|
Anzahl der Fragen: <span class="font-bold ">{{ quiz.question.count }}</span><br>
|
|
Erstellt am: <span class="font-bold ">{{ quiz.creation_date }}</span>
|
|
</p>
|
|
{% if quiz.original_creator %}
|
|
<p class=" font-bold break-words truncate text-white drop-shadow-lg custom-outline">Ursprünglich erstellt von: {{ quiz.original_creator }}</p>
|
|
{% endif %}
|
|
|
|
<!-- Buttons bleiben am unteren Rand -->
|
|
<div class="flex justify-between items-center gap-2 ">
|
|
<a href="{% url 'play:create_game' quiz.id %}" class="qp-a-button-small border-2 border-blue-600 text-white font-bold text-white drop-shadow-lg custom-outline">Spiel starten</a>
|
|
<a href="{% url 'library:detail_quiz' quiz.id %}" class="qp-a-button-small border-2 border-blue-600 text-white text-white font-bold text-white drop-shadow-lg custom-outline">
|
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-7">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
|
|
</svg>
|
|
|
|
</a>
|
|
<div class="flex gap-2">
|
|
<a href="{% url 'library:overview_quiz' %}?user={{ quiz.user_id }}" class="qp-a-button-small border-2 border-blue-600 text-white font-bold drop-shadow-lg custom-outline">
|
|
|
|
<button type="submit" class=" text-sm py-1 text-white font-bold drop-shadow-lg custom-outline"> veröffentlicht von {{ quiz.user_id }}</button>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
|
|
</div>
|
|
|
|
|
|
|
|
{% if filter_other_quizzes|length > 1 %}
|
|
<div class="swiper-button-next"></div>
|
|
<div class="swiper-button-prev"></div>
|
|
{% endif %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if not filter_other_quizzes and not filter_my_quizzes %}
|
|
<div class="grid text-center font-bold items-center">Es wurde kein passendes Quiz gefunden!</div>
|
|
{% endif %}
|
|
|
|
|
|
|
|
<!-- Swiper.js -->
|
|
<script src="{% static 'swiper/swiper-bundle.min.js' %}"></script>
|
|
<script>
|
|
|
|
|
|
|
|
var swiper = new Swiper('.swiper', {
|
|
slidesPerView: getSlidesPerView(),
|
|
direction: getDirection(),
|
|
navigation: {
|
|
nextEl: '.swiper-button-next',
|
|
prevEl: '.swiper-button-prev',
|
|
},
|
|
on: {
|
|
resize: function () {
|
|
swiper.changeDirection(getDirection());
|
|
swiper.params.slidesPerView = getSlidesPerView();
|
|
swiper.update();
|
|
},
|
|
},
|
|
});
|
|
|
|
function getDirection() {
|
|
return window.innerWidth <= 0? 'vertical' : 'horizontal';
|
|
}
|
|
|
|
function getSlidesPerView() {
|
|
if (window.innerWidth <= 840) {
|
|
return 1; // Smartphones
|
|
} else if (window.innerWidth <= 1024) {
|
|
return 2; // Tablets
|
|
} else {
|
|
return 3; // Desktops
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.custom-outline {
|
|
-webkit-text-stroke: 0.2px rgb(0, 0, 0); /* Schwarze Umrandung */
|
|
}
|
|
</style>
|
|
{% endblock %}
|