improved_library_structure#140

This commit is contained in:
ben8
2025-06-23 14:54:46 +02:00
parent d2274960cb
commit e65cd408eb
9 changed files with 1669 additions and 522 deletions

View File

@@ -1,5 +1,5 @@
from django import forms from django import forms
from .models import QivipQuiz, QivipQuestion from .models import QivipQuiz, QivipQuestion, QuizCategory
class QuizForm(forms.ModelForm): class QuizForm(forms.ModelForm):
class Meta: class Meta:
@@ -19,13 +19,20 @@ class QuizFilterForm(forms.Form):
})) }))
min_amout_questions = forms.IntegerField(required=False, min_value=1, label="Minimale Anzahl an Fragen", widget=forms.NumberInput(attrs={ min_amount_questions = forms.IntegerField(required=False, min_value=1, label="Minimale Anzahl an Fragen", widget=forms.NumberInput(attrs={
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full' 'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
})) }))
max_amout_questions = forms.IntegerField(required=False, min_value=1, label="Maximale Anzahl an Fragen", widget=forms.NumberInput(attrs={ max_amount_questions = forms.IntegerField(required=False, min_value=1, label="Maximale Anzahl an Fragen", widget=forms.NumberInput(attrs={
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full' 'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
})) }))
user = forms.CharField(required=False, label="von User", widget=forms.TextInput(attrs={ user = forms.CharField(required=False, label="von User", widget=forms.TextInput(attrs={
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full' 'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
})) }))
categories = forms.ModelChoiceField(
queryset=QuizCategory.objects.filter().distinct(),
required=False,
label="Kategorie",
widget=forms.Select(attrs={
'class': 'border-2 border-gray-300 rounded-lg p-2 w-full'
}))

View File

@@ -7,6 +7,9 @@ app_name = 'library'
urlpatterns = [ urlpatterns = [
path('', views.overview_quiz, name='overview_quiz'), path('', views.overview_quiz, name='overview_quiz'),
path('my/', views.overview_quiz_my, name='overview_quiz_my'),
path('favorites/', views.overview_quiz_favorites, name='overview_quiz_favorites'),
path('new/', views.new_quiz, name='new_quiz'), path('new/', views.new_quiz, name='new_quiz'),
path('edit/<int:pk>/', views.edit_quiz, name='edit_quiz'), path('edit/<int:pk>/', views.edit_quiz, name='edit_quiz'),
path('delete/<int:pk>/', views.delete_quiz, name='delete_quiz'), path('delete/<int:pk>/', views.delete_quiz, name='delete_quiz'),

View File

@@ -14,6 +14,7 @@ from .models import QivipQuiz
from .forms import QuizFilterForm from .forms import QuizFilterForm
from django.db.models import Count from django.db.models import Count
from django.contrib.auth.models import User from django.contrib.auth.models import User
from urllib.parse import urlparse, urlunparse
# Übersicht aller Quizze # Übersicht aller Quizze
@@ -23,13 +24,12 @@ def quiz_names_json(request):
search = form.cleaned_data.get('search') search = form.cleaned_data.get('search')
names = QivipQuiz.objects.all().annotate(max_amout_questions=Count('questions')).filter(max_amout_questions__gte=1, status='öffentlich', name__icontains=search) names = QivipQuiz.objects.all().annotate(max_amount_questions=Count('questions')).filter(max_amount_questions__gte=1, status='öffentlich', name__icontains=search)
if request.user.is_authenticated: if request.user.is_authenticated:
names = names | QivipQuiz.objects.filter( names = names | QivipQuiz.objects.filter(
user_id=request.user.id, user_id=request.user.id,
status='privat',
name__icontains=search name__icontains=search
) )
@@ -38,6 +38,167 @@ def quiz_names_json(request):
return JsonResponse(names, safe=False) return JsonResponse(names, safe=False)
def apply_quiz_filters(queryset, form):
if not form.is_valid():
return queryset
queryset = queryset.annotate(question_count=Count('questions'))
search = form.cleaned_data.get('search')
username = form.cleaned_data.get('user')
min_questions = form.cleaned_data.get('min_amount_questions')
max_questions = form.cleaned_data.get('max_amount_questions')
category = form.cleaned_data.get('categories')
filters = {}
if search:
filters['name__icontains'] = search
if min_questions is not None:
filters['question_count__gte'] = min_questions
if max_questions is not None:
filters['question_count__lte'] = max_questions
if category is not None:
filters['category'] = category
if username:
try:
user = User.objects.get(username=username)
filters['user_id'] = user.id
except User.DoesNotExist:
return queryset.none()
return queryset.filter(**filters)
def overview_quiz(request, connect="all"):
form = QuizFilterForm(request.GET)
user = request.user if request.user.is_authenticated else None
# Meine Quizzeze mit mind. 1 Frage
filter_my_quizzes = QivipQuiz.objects.none()
if user:
filter_my_quizzes = QivipQuiz.objects.annotate(max_amount_questions=Count('questions')).filter(
user_id=user.id,
max_amount_questions__gte=0
)
# Öffentliche Alle mit mind. 1 Frage
filter_all_quizzes = QivipQuiz.objects.annotate(max_amount_questions=Count('questions')).filter(
status='öffentlich',
max_amount_questions__gte=1
)
if user:
filter_all_quizzes = filter_all_quizzes.exclude(user_id=user.id)
# Favoriten mit mind. 1 Frage
favorite_quiz_ids = []
favorite_quizzes = QivipQuiz.objects.none()
if user:
favorite_quiz_ids = QivipQuizFavorite.objects.filter(user=user, favorite=True).values_list('quiz', flat=True)
favorite_quizzes = QivipQuiz.objects.annotate(max_amount_questions=Count('questions')).filter(
id__in=favorite_quiz_ids,
max_amount_questions__gte=1
)
# Suche anwenden, falls vorhanden
filter_my_quizzes = apply_quiz_filters(filter_my_quizzes, form)
filter_all_quizzes = apply_quiz_filters(filter_all_quizzes, form)
favorite_quizzes = apply_quiz_filters(favorite_quizzes, form)
if request.user.is_authenticated:
filter_all_quizzes = apply_quiz_filters(filter_all_quizzes, form) | filter_my_quizzes
# Treffer zählen
total_my = filter_my_quizzes.count()
total_all = filter_all_quizzes.count()
total_favorite = favorite_quizzes.count()
total_all_found=total_my + total_all + total_favorite
# Paginierung
paginator_my = Paginator(filter_my_quizzes.order_by('-creation_date'), 8)
paginator_all = Paginator(filter_all_quizzes.order_by('-creation_date'), 8)
paginator_fav = Paginator(favorite_quizzes.order_by('-creation_date'), 8)
my_page = request.GET.get('my_page', 1)
all_page = request.GET.get('all_page', 1)
fav_page = request.GET.get('favorite_page', 1)
try:
my_quizzes = paginator_my.page(my_page)
except:
my_quizzes = paginator_my.page(1)
try:
all_quizzes = paginator_all.page(all_page)
except:
all_quizzes = paginator_all.page(1)
try:
favorite_quizzes = paginator_fav.page(fav_page)
except:
favorite_quizzes = paginator_fav.page(1)
context = {
'form': form,
'my_quizzes': my_quizzes,
'all_quizzes': all_quizzes,
'favorite_quizzes': favorite_quizzes,
'total_my': total_my,
'total_all': total_all,
'total_favorite': total_favorite,
'total_all_found':total_all_found,
'show_search': True,
'favorite_quiz_ids': list(favorite_quiz_ids),
'url_my': '/library/my/',
'url_all': '/library/',
'url_favorite': '/library/favorites/',
}
query_params = request.GET.copy()
querydict_my = query_params.copy()
querydict_all = query_params.copy()
querydict_fav = query_params.copy()
# Entferne die jeweiligen Seiten-Parameter, damit wir sie neu setzen können
querydict_my.pop('my_page', None)
querydict_all.pop('all_page', None)
querydict_fav.pop('favorite_page', None)
# Füge die "bereinigten" Querystrings dem context hinzu
context.update({
'querystring_my': querydict_my.urlencode(),
'querystring_all': querydict_all.urlencode(),
'querystring_favorite': querydict_fav.urlencode(),
})
if connect=="all":
context.update({"link":"library:overview_quiz"})
return render(request, 'library/overview_quiz.html', context)
elif connect=="my":
context.update({"link":"library:overview_quiz_my"})
return render(request, 'library/overview_quiz_my.html', context)
elif connect=="favorite":
context.update({"link":"library:overview_quiz_favorites"})
return render(request, 'library/overview_quiz_favorite.html', context)
def overview_quiz_my(request):
return overview_quiz(request, connect="my")
def overview_quiz_favorites(request):
return overview_quiz(request, connect="favorite")
"""
def overview_quiz(request): def overview_quiz(request):
# Filter # Filter
form = QuizFilterForm(request.GET) form = QuizFilterForm(request.GET)
@@ -53,28 +214,24 @@ def overview_quiz(request):
# Initialize querysets # Initialize querysets
if request.user.is_authenticated: if request.user.is_authenticated:
filter_my_quizzes = QivipQuiz.objects.filter(user_id=request.user).annotate(max_amout_questions=Count('questions')) filter_my_quizzes = QivipQuiz.objects.filter(user_id=request.user).annotate(max_amount_questions=Count('questions'))
favorite_quizzes = favorite_quizzes.filter(id__in=favorite_quiz_ids).annotate(max_amout_questions=Count('questions')) favorite_quizzes = favorite_quizzes.filter(id__in=favorite_quiz_ids).annotate(max_amount_questions=Count('questions'))
filter_other_quizzes = QivipQuiz.objects.exclude(user_id=request.user) filter_all_quizzes = QivipQuiz.objects.exclude(user_id=request.user)
else: else:
favorite_quizzes = QivipQuiz.objects.none() favorite_quizzes = QivipQuiz.objects.none()
filter_my_quizzes = QivipQuiz.objects.none() filter_my_quizzes = QivipQuiz.objects.none()
filter_other_quizzes = QivipQuiz.objects.all() filter_all_quizzes = QivipQuiz.objects.all()
# Apply common filters to other quizzes # Apply common filters to all quizzes
filter_other_quizzes = filter_other_quizzes.annotate(max_amout_questions=Count('questions')) filter_all_quizzes = filter_all_quizzes.annotate(max_amount_questions=Count('questions'))
filter_other_quizzes = filter_other_quizzes.filter(max_amout_questions__gte=1, status='öffentlich') filter_all_quizzes = filter_all_quizzes.filter(max_amount_questions__gte=1, status='öffentlich')
if form.is_valid(): if form.is_valid():
search = form.cleaned_data.get('search') search = form.cleaned_data.get('search')
username= form.cleaned_data.get('user') username= form.cleaned_data.get('user')
max_amout_questions= form.cleaned_data.get('max_amout_questions') max_amount_questions= form.cleaned_data.get('max_amount_questions')
min_amout_questions= form.cleaned_data.get('min_amout_questions') min_amount_questions= form.cleaned_data.get('min_amount_questions')
filters = {} filters = {}
if username: if username:
@@ -84,19 +241,19 @@ def overview_quiz(request):
except User.DoesNotExist: except User.DoesNotExist:
favorite_quizzes = QivipQuiz.objects.none() favorite_quizzes = QivipQuiz.objects.none()
filter_my_quizzes = QivipQuiz.objects.none() filter_my_quizzes = QivipQuiz.objects.none()
filter_other_quizzes=QivipQuiz.objects.none() filter_all_quizzes=QivipQuiz.objects.none()
if search: if search:
filters['name__icontains'] = search filters['name__icontains'] = search
if min_amout_questions: if min_amount_questions:
filters['max_amout_questions__gte'] = min_amout_questions filters['max_amount_questions__gte'] = min_amount_questions
if max_amout_questions: if max_amount_questions:
filters['max_amout_questions__lte'] = max_amout_questions filters['max_amount_questions__lte'] = max_amount_questions
try: try:
filter_other_quizzes = filter_other_quizzes.filter(**filters) filter_all_quizzes = filter_all_quizzes.filter(**filters)
except: except:
filter_other_quizzes=QivipQuiz.objects.none() filter_all_quizzes=QivipQuiz.objects.none()
try: try:
filter_my_quizzes = filter_my_quizzes.filter(**filters) filter_my_quizzes = filter_my_quizzes.filter(**filters)
except: except:
@@ -116,12 +273,12 @@ def overview_quiz(request):
except: except:
my_quizzes = my_quizzes_paginator.page(1) my_quizzes = my_quizzes_paginator.page(1)
# Pagination for other quizzes # Pagination for all quizzes
other_quizzes_paginator = Paginator(filter_other_quizzes.order_by('-creation_date'), 8) # 8 quizzes per page all_quizzes_paginator = Paginator(filter_all_quizzes.order_by('-creation_date'), 8) # 8 quizzes per page
try: try:
other_quizzes = other_quizzes_paginator.page(request.GET.get('other_page', 1)) all_quizzes = all_quizzes_paginator.page(request.GET.get('all_page', 1))
except: except:
other_quizzes = other_quizzes_paginator.page(1) all_quizzes = all_quizzes_paginator.page(1)
@@ -135,9 +292,9 @@ def overview_quiz(request):
# Pagination for favorite_quizzes # Pagination for favorite_quizzes
favorite_quizzes_paginator = Paginator(favorite_quizzes, 8) # 8 quizzes per page favorite_quizzes_paginator = Paginator(favorite_quizzes, 8) # 9 quizzes per page
try: try:
favorite_quizzes = favorite_quizzes_paginator.page(request.GET.get('my_page', 1)) favorite_quizzes = favorite_quizzes_paginator.page(request.GET.get('favorite_page', 1))
except: except:
favorite_quizzes = favorite_quizzes_paginator.page(1) favorite_quizzes = favorite_quizzes_paginator.page(1)
@@ -146,13 +303,13 @@ def overview_quiz(request):
'favorite_quiz_ids': favorite_quiz_ids, 'favorite_quiz_ids': favorite_quiz_ids,
'favorite_quizzes':favorite_quizzes, 'favorite_quizzes':favorite_quizzes,
'my_quizzes': my_quizzes, 'my_quizzes': my_quizzes,
'other_quizzes': other_quizzes, 'all_quizzes': all_quizzes,
'form': form, 'form': form,
} }
return render(request, 'library/overview_quiz.html', context) return render(request, 'library/overview_quiz.html', context)
"""
# Neues Quiz erstellen
@login_required @login_required
def favorite_quiz(request, pk): def favorite_quiz(request, pk):
quiz= get_object_or_404(QivipQuiz, pk=pk) quiz= get_object_or_404(QivipQuiz, pk=pk)
@@ -177,6 +334,7 @@ def favorite_quiz(request, pk):
favorite=QivipQuizFavorite.objects.create(user=request.user, quiz=quiz, favorite=True, favorite_date=timezone.now()) favorite=QivipQuizFavorite.objects.create(user=request.user, quiz=quiz, favorite=True, favorite_date=timezone.now())
statement="Das Quiz '"+ favorite.quiz.name+ "' wurde erfolgreich zu den Favoriten hinzugefügt!" statement="Das Quiz '"+ favorite.quiz.name+ "' wurde erfolgreich zu den Favoriten hinzugefügt!"
messages.success(request,statement ) messages.success(request,statement )
return redirect(request.META.get('HTTP_REFERER', 'library:overview_quiz')) return redirect(request.META.get('HTTP_REFERER', 'library:overview_quiz'))

View File

@@ -0,0 +1,145 @@
{% load static %}
{% block extra_css %}
<style>
.custom-outline {
-webkit-text-stroke: 0.2px rgb(0, 0, 0);
}
</style>
{% endblock %}
{% block head %}
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<button id="filterButton" class="p-2 hover:bg-gray-100 rounded-lg transition-colors duration-200 flex items-center gap-2 text-gray-700">
<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="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
<span>Filter</span>
</button>
<div class="flex gap-2">
<a href="{% url link %}" class="inline-flex items-center px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
Zurücksetzen
</a>
<a href="{% url 'library:new_quiz' %}" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Neues Quiz
</a>
</div>
</div>
<div id="filterPanel" class="container mx-auto px-4 {% if request.GET.filter == '1' %}block{% else %}hidden{% endif %}">
<div class="bg-white rounded-xl shadow-lg p-6 border border-gray-200">
<form action="{% url link %}" method="get" class="grid grid-cols-1 md:grid-cols-3 gap-6">
<input type="hidden" name="filter" id="filter-input">
<div class="space-y-2">
<div class="flex gap-2">
<input type="checkbox" name="use_min_amount_questions" id="use_min_amount_questions" {% if form.use_min_amount_questions.value %}checked{% endif %}>
<label class="block text-sm font-medium text-gray-700">minimale Anzahl an Fragen</label>
</div>
<input
type="range"
name="min_amount_questions"
id="id_min_amount_questions"
min="0"
max="100"
step="1"
value="{{ form.min_amount_questions.value|default_if_none:0 }}"
class="w-full accent-blue-600"
oninput="document.getElementById('min_amount_value').textContent = this.value + ' Frage(n)'"
{% if not form.use_min_amount_questions.value %}disabled{% endif %}
>
<span id="min_amount_value" class="text-sm font-semibold text-gray-800">
{{ form.min_amount_questions.value|default_if_none:0 }} Frage(n)
</span>
<br>
<span class="text-xs text-gray-600"> Quizze anderer User werden erst ab min. 1 Frage angezeigt.</span>
</div>
<div class="space-y-2">
<div class="flex gap-2">
<input type="checkbox" name="use_max_amount_questions" id="use_max_amount_questions" {% if form.use_max_amount_questions.value %}checked{% endif %}>
<label class="block text-sm font-medium text-gray-700">maximale Anzahl an Fragen</label>
</div>
<input
type="range"
name="max_amount_questions"
id="id_max_amount_questions"
min="1"
max="100"
step="1"
value="{{ form.max_amount_questions.value|default_if_none:100 }}"
class="w-full accent-blue-600"
oninput="document.getElementById('max_amount_value').textContent = this.value+' Frage(n)'"
{% if not form.use_max_amount_questions.value %}disabled{% endif %}
>
<span id="max_amount_value" class="text-sm font-semibold text-gray-800">
{{ form.max_amount_questions.value|default_if_none:100 }} Frage(n)
</span>
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">veröffentlicht von User</label>
<input type="text" name="user" class="border-2 border-gray-300 rounded-lg p-2 w-full" id="id_user" value="{{ form.user.value|default_if_none:'' }}">
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">Kategorie</label>
{{ form.categories }}
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">Suche</label>
{{ form.search }}
</div>
<div class="md:col-span-3 flex justify-end">
<button
type="submit" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
</svg>
Filtern
</button>
</div>
</form>
</div>
</div>
</form>
<script>
document.addEventListener('DOMContentLoaded', () => {
const checkbox = document.getElementById('use_min_amount_questions');
const slider = document.getElementById('id_min_amount_questions');
const checkbox_max = document.getElementById('use_max_amount_questions');
const slider_max = document.getElementById('id_max_amount_questions');
checkbox.addEventListener('change', () => {
slider.disabled = !checkbox.checked;
});
checkbox_max.addEventListener('change', () => {
slider_max.disabled = !checkbox_max.checked;
});
});
</script>
{% endblock %}

View File

@@ -0,0 +1,672 @@
{% extends 'base.html' %}
{% load static %}
<!-- Die gesamte Seite wird eigentlich nicht mehr genutzt. WICHTIG: ggf. kann Code aber hiervon noch genutzt werden. -->
{% block extra_css %}
<style>
.custom-outline {
-webkit-text-stroke: 0.2px rgb(0, 0, 0);
}
</style>
{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<button id="filterButton" class="p-2 hover:bg-gray-100 rounded-lg transition-colors duration-200 flex items-center gap-2 text-gray-700">
<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="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
<span>Filter</span>
</button>
<div class="flex gap-2">
<a href="{% url 'library:overview_quiz' %}" class="inline-flex items-center px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
Zurücksetzen
</a>
<a href="{% url 'library:new_quiz' %}" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Neues Quiz
</a>
</div>
</div>
<div id="filterPanel" class="container mx-auto px-4">
<div class="bg-white rounded-xl shadow-lg p-6 border border-gray-200">
<form action="{% url 'library:overview_quiz' %}" method="get" class="grid grid-cols-1 md:grid-cols-3 gap-6">
<input type="hidden" name="filter" value="1">
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">minimale Anzahl an Fragen</label>
<input type="number" name="min_amount_questions" class="border-2 border-gray-300 rounded-lg p-2 w-full" min="1" id="id_min_amount_questions" value="{{ form.min_amount_questions.value|default_if_none:'' }}">
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">maximale Anzahl an Fragen</label>
<input type="number" name="max_amount_questions" class="border-2 border-gray-300 rounded-lg p-2 w-full" min="1" id="id_max_amount_questions" value="{{ form.max_amount_questions.value|default_if_none:'' }}">
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">veröffentlicht von User</label>
<input type="text" name="user" class="border-2 border-gray-300 rounded-lg p-2 w-full" id="id_user" value="{{ form.user.value|default_if_none:'' }}">
</div>
<div class="md:col-span-3 flex justify-end">
<button type="submit" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
</svg>
Filtern
</button>
</div>
</form>
</div>
</div>
{% if user.is_authenticated %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6">
<div class="flex items-center gap-3">
<h2 id="my-quizzes" class="text-2xl font-bold text-gray-900">Meine Quizze</h2>
<div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div>
</div>
</div>
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr">
{% for quiz in my_quizzes %}
<article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col">
<!-- Image Section (Top) -->
<div class="h-48 w-full relative flex-shrink-0">
{% if quiz.image %}
<div class="relative w-full h-full inset-0 bg-cover bg-center">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.image.url }});">
</div>
</div>
{% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-4xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div>
{% endif %}
{% if user.is_authenticated %}
{% if quiz.id not in favorite_quiz_ids %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% else %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% endif %}
{% endif %}
</div>
<!-- Content Section (Bottom) -->
<div class="flex-grow bg-white p-4 flex flex-col gap-3">
<!-- Title -->
<h2 class="text-xl font-bold text-gray-900 break-words mb-2">
<a href="{% url 'library:detail_quiz' quiz.id %}" class="hover:text-blue-600 transition-colors">{{ quiz.name }}</a>
</h2>
<!-- Description -->
<p class="text-sm text-gray-700 break-words mb-3">{{ quiz.description }}</p>
<!-- Quiz Info Grid -->
<div class="grid grid-cols-2 gap-x-4 gap-y-3 text-sm">
<!-- Difficulty -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-gray-800">{{ quiz.difficulty }}</span>
</div>
<!-- Questions Count -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.questions.count }} Fragen</span>
</div>
<!-- Status -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.status }}</span>
</div>
<!-- Rating -->
<div class="flex items-center gap-1">
{% if quiz.rating_count > 0 %}
{% for i in '12345'|make_list %}
{% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %}
<span class="text-yellow-500 text-base"></span>
{% else %}
<span class="text-gray-300 text-base"></span>
{% endif %}
{% endfor %}
<span class="text-gray-600 text-sm">({{ quiz.rating_count }})</span>
{% else %}
<span class="text-gray-600 text-sm">Noch keine Bewertungen</span>
{% endif %}
</div>
</div>
<!-- Authors -->
{% if quiz.authors.all %}
<div class="flex items-center gap-2 text-sm mt-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-gray-600">
{% for author in quiz.authors.all %}
{{ author.username }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</span>
</div>
{% endif %}
<!-- Action Buttons -->
<div class="flex justify-between items-center gap-2 mt-3 border-t pt-3">
{% if quiz.questions.count != 0 %}
<a href="{% url 'play:create_game' quiz.id %}"
class="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors duration-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Spielen
</a>
{% else %}
Fürs Spielen ist min. 1 Frage nötig!
{% endif %}
<div class="flex gap-1">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
{% if quiz.user_id == request.user %}
<a href="{% url 'library:edit_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</a>
<a href="{% url 'library:delete_quiz' quiz.id %}"
class="p-1.5 bg-red-100 hover:bg-red-200 text-red-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</a>
{% endif %}
</div>
</div>
</div>
</article>
{% endfor %}
</div>
<!-- Pagination für Meine Quizze -->
{% if my_quizzes.paginator.num_pages > 1 %}
<div class="flex justify-center space-x-2 mt-6 mb-8">
{% if my_quizzes.has_previous %}
<a href="?my_page={{ my_quizzes.previous_page_number }}{% if request.GET.all_page %}&all_page={{ request.GET.all_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</a>
{% endif %}
<span class="px-3 py-1 text-gray-600">Seite {{ my_quizzes.number }} von {{ my_quizzes.paginator.num_pages }}</span>
{% if my_quizzes.has_next %}
<a href="?my_page={{ my_quizzes.next_page_number }}{% if request.GET.all_page %}&all_page={{ request.GET.all_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
{% endif %}
</div>
{% endif %}
</div>
{% endif %}
{% if user.is_authenticated %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6">
<div class="flex items-center gap-3">
<h2 id="my-quizzes" class="text-2xl font-bold text-gray-900">Meine Favoriten</h2>
<div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div>
</div>
</div>
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr">
{% for quiz in favorite_quizzes %}
<article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col">
<!-- Image Section (Top) -->
<div class="h-48 w-full relative flex-shrink-0">
{% if quiz.image %}
<div class="relative w-full h-full inset-0 bg-cover bg-center">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.image.url }});">
</div>
</div>
{% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-4xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div>
{% endif %}
{% if user.is_authenticated %}
{% if quiz.favorite == False %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% else %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% endif %}
{% endif %}
</div>
<!-- Content Section (Bottom) -->
<div class="flex-grow bg-white p-4 flex flex-col gap-3">
<!-- Title -->
<h2 class="text-xl font-bold text-gray-900 break-words mb-2">
<a href="{% url 'library:detail_quiz' quiz.id %}" class="hover:text-blue-600 transition-colors">{{ quiz.name }}</a>
</h2>
<!-- Description -->
<p class="text-sm text-gray-700 break-words mb-3">{{ quiz.description }}</p>
<!-- Quiz Info Grid -->
<div class="grid grid-cols-2 gap-x-4 gap-y-3 text-sm">
<!-- Difficulty -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-gray-800">{{ quiz.difficulty }}</span>
</div>
<!-- Questions Count -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.questions.count }} Fragen</span>
</div>
<!-- Status -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.status }}</span>
</div>
<!-- Rating -->
<div class="flex items-center gap-1">
{% if quiz.rating_count > 0 %}
{% for i in '12345'|make_list %}
{% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %}
<span class="text-yellow-500 text-base"></span>
{% else %}
<span class="text-gray-300 text-base"></span>
{% endif %}
{% endfor %}
<span class="text-gray-600 text-sm">({{ quiz.rating_count }})</span>
{% else %}
<span class="text-gray-600 text-sm">Noch keine Bewertungen</span>
{% endif %}
</div>
</div>
<!-- Authors -->
{% if quiz.authors.all %}
<div class="flex items-center gap-2 text-sm mt-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-gray-600">
{% for author in quiz.authors.all %}
{{ author.username }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</span>
</div>
{% endif %}
<!-- Action Buttons -->
<div class="flex justify-between items-center gap-2 mt-3 border-t pt-3">
{% if quiz.questions.count != 0 %}
<a href="{% url 'play:create_game' quiz.id %}"
class="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors duration-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Spielen
</a>
{% else %}
Fürs Spielen ist min. 1 Frage nötig!
{% endif %}
<div class="flex gap-1">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
{% if quiz.user_id == request.user %}
<a href="{% url 'library:edit_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</a>
<a href="{% url 'library:delete_quiz' quiz.id %}"
class="p-1.5 bg-red-100 hover:bg-red-200 text-red-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</a>
{% endif %}
</div>
</div>
</div>
</article>
{% endfor %}
</div>
<!-- Pagination für Meine Quizze -->
{% if favorite_quizzes.paginator.num_pages > 1 %}
<div class="flex justify-center space-x-2 mt-6 mb-8">
{% if favorite_quizzes.has_previous %}
<a href="?my_page={{ favorite.previous_page_number }}{% if request.GET.all_page %}&all_page={{ request.GET.all_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</a>
{% endif %}
<span class="px-3 py-1 text-gray-600">Seite {{ favorite_quizzes.number }} von {{ favorite_quizzes.paginator.num_pages }}</span>
{% if favorite_quizzes.has_next %}
<a href="?favorite_page={{ favorite_quizzes.next_page_number }}{% if request.GET.all_page %}&all_page={{ request.GET.all_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
{% endif %}
</div>
{% endif %}
</div>
{% endif %}
<!-- Alle -->
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6">
<div class="flex items-center gap-3">
<h2 id="all-quizzes" class="text-2xl font-bold text-gray-900">Alle</h2>
<div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div>
</div>
</div>
{% if all_quizzes %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr">
{% for quiz in all_quizzes %}
<article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col">
<!-- Image Section (Top) -->
<div class="h-48 w-full relative flex-shrink-0">
{% if quiz.image %}
<div class="relative w-full h-full inset-0 bg-cover bg-center">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.image.url }});">
</div>
</div>
{% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-4xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div>
{% endif %}
{% if user.is_authenticated %}
{% if quiz.id not in favorite_quiz_ids %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% else %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% endif %}
{% endif %}
</div>
<!-- Content Section (Bottom) -->
<div class="flex-grow bg-white p-4 flex flex-col gap-3">
<!-- Title -->
<h2 class="text-xl font-bold text-gray-900 break-words mb-2">
<a href="{% url 'library:detail_quiz' quiz.id %}" class="hover:text-blue-600 transition-colors">{{ quiz.name }}</a>
</h2>
<!-- Description -->
<p class="text-sm text-gray-700 break-words mb-3">{{ quiz.description }}</p>
<!-- Quiz Info Grid -->
<div class="grid grid-cols-2 gap-x-4 gap-y-3 text-sm">
<!-- Difficulty -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-gray-800">{{ quiz.difficulty }}</span>
</div>
<!-- Questions Count -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.questions.count }} Fragen</span>
</div>
<!-- Status -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.status }}</span>
</div>
<!-- Rating -->
<div class="flex items-center gap-1">
{% if quiz.rating_count > 0 %}
{% for i in '12345'|make_list %}
{% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %}
<span class="text-yellow-500 text-base"></span>
{% else %}
<span class="text-gray-300 text-base"></span>
{% endif %}
{% endfor %}
<span class="text-gray-600 text-sm">({{ quiz.rating_count }})</span>
{% else %}
<span class="text-gray-600 text-sm">Noch keine Bewertungen</span>
{% endif %}
</div>
</div>
<!-- Authors -->
{% if quiz.authors.all %}
<div class="flex items-center gap-2 text-sm mt-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-gray-600">
{% for author in quiz.authors.all %}
{{ author.username }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</span>
</div>
{% endif %}
<!-- Action Buttons -->
<div class="flex justify-between items-center gap-2 mt-3 border-t pt-3">
{% if quiz.questions.count != 0 %}
<a href="{% url 'play:create_game' quiz.id %}"
class="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors duration-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Spielen
</a>
{% else %}
Fürs Spielen ist min. 1 Frage nötig!
{% endif %}
<div class="flex gap-1">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
</div>
</div>
</div>
</article>
{% endfor %}
</div>
<!-- Pagination für andere Quiz -->
{% if all_quizzes.paginator.num_pages > 1 %}
<div class="flex justify-center space-x-2 mt-6 mb-8">
{% if all_quizzes.has_previous %}
<a href="?all_page={{ all_quizzes.previous_page_number }}{% if request.GET.my_page %}&my_page={{ request.GET.my_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</a>
{% endif %}
<span class="px-3 py-1 text-gray-600">Seite {{ all_quizzes.number }} von {{ all_quizzes.paginator.num_pages }}</span>
{% if all_quizzes.has_next %}
<a href="?all_page={{ all_quizzes.next_page_number }}{% if request.GET.my_page %}&my_page={{ request.GET.my_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
{% endif %}
</div>
{% endif %}
</div>
{% else %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-4">
<p class="text-gray-600 text-center">Keine öffentlichen Quiz gefunden.</p>
</div>
{% endif %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('filter') === '1') {
document.getElementById('filterPanel').classList.add('show');
}
document.getElementById('filterButton').addEventListener('click', function() {
document.getElementById('filterPanel').classList.toggle('show');
// URL beim Öffnen aktualisieren
const params = new URLSearchParams(window.location.search);
if (document.getElementById('filterPanel').classList.contains('show')) {
params.set('filter', '1');
} else {
params.delete('filter');
}
history.replaceState(null, '', `${location.pathname}?${params}`);
});
});
if (window.location.pathname.includes("/library/")) {
window.addEventListener("beforeunload", function () {
localStorage.setItem("scroll-position", window.scrollY);
});
window.addEventListener("load", function () {
const scrollPos = localStorage.getItem("scroll-position");
if (scrollPos !== null) {
window.scrollTo(0, parseInt(scrollPos));
}
});
}
</script>
{% endblock content %}

View File

@@ -13,70 +13,45 @@
{% block content %} {% block content %}
<div class="container mx-auto px-4 py-4 flex justify-between items-center"> {% block head %}
<button id="filterButton" class="p-2 hover:bg-gray-100 rounded-lg transition-colors duration-200 flex items-center gap-2 text-gray-700"> {% include 'library/head.html' %}
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> {% endblock %}
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
</svg>
<span>Filter</span>
</button>
<div class="flex gap-2">
<a href="{% url 'library:overview_quiz' %}" class="inline-flex items-center px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
</svg>
Zurücksetzen
</a>
<a href="{% url 'library:new_quiz' %}" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Neues Quiz
</a>
</div>
</div>
<div id="filterPanel" class="container mx-auto px-4"> <!-- Alle -->
<div class="bg-white rounded-xl shadow-lg p-6 border border-gray-200">
<form action="{% url 'library:overview_quiz' %}" method="get" class="grid grid-cols-1 md:grid-cols-3 gap-6">
<input type="hidden" name="filter" value="1">
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">minimale Anzahl an Fragen</label>
<input type="number" name="min_amout_questions" class="border-2 border-gray-300 rounded-lg p-2 w-full" min="1" id="id_min_amout_questions" value="{{ form.min_amout_questions.value|default_if_none:'' }}">
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">maximale Anzahl an Fragen</label>
<input type="number" name="max_amout_questions" class="border-2 border-gray-300 rounded-lg p-2 w-full" min="1" id="id_max_amout_questions" value="{{ form.max_amout_questions.value|default_if_none:'' }}">
</div>
<div class="space-y-2">
<label class="block text-sm font-medium text-gray-700">veröffentlicht von User</label>
<input type="text" name="user" class="border-2 border-gray-300 rounded-lg p-2 w-full" id="id_user" value="{{ form.user.value|default_if_none:'' }}">
</div>
<div class="md:col-span-3 flex justify-end">
<button type="submit" class="inline-flex items-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"></path>
</svg>
Filtern
</button>
</div>
</form>
</div>
</div>
{% if user.is_authenticated %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6"> <div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6">
<div class="flex items-center gap-3"> {% if user.is_authenticated %}
<h2 id="my-quizzes" class="text-2xl font-bold text-gray-900">Eigene Quiz</h2>
<div class="flex justify-end text-sm mb-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 text-blue-600">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
</svg>
<div class="gap-2 mx-2">
Anzahl aller Ergebnisse:
<span class="font-bold"> {{ total_all_found }}</span>
</div>
(verschiedene:
<span class="font-bold ml-1"> {{ total_all }}</span>) </div>
<div class="flex flex-wrap justify-end gap-4 font-bold text-blue-600 whitespace-nowrap text-sm mb-2 md:mb-0">
<p class="font-black underline">Alle ({{ total_all }})</p>
<a href="{{ url_my }}?{{ request.GET.urlencode }}">Meine Quizze ({{ total_my }})</a>
<a href="{{ url_favorite }}?{{ request.GET.urlencode }}">Meine Favoriten ({{ total_favorite }})</a>
</div>
{% endif %}
<div class="flex items-center gap-3">
<h2 id="all-quizzes" class="text-2xl font-bold text-gray-900">Alle</h2>
<div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div> <div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div>
</div> </div>
</div> </div>
{% if all_quizzes %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8"> <div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr"> <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr">
{% for quiz in my_quizzes %} {% for quiz in all_quizzes %}
<article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col"> <article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col">
<!-- Image Section (Top) --> <!-- Image Section (Top) -->
<div class="h-48 w-full relative flex-shrink-0"> <div class="h-48 w-full relative flex-shrink-0">
@@ -87,16 +62,17 @@
style="background-image: url({{ quiz.image.image.url }});"> style="background-image: url({{ quiz.image.image.url }});">
</div> </div>
</div> </div>
{% else %} {% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-4xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div> <div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-3xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div>
{% endif %} {% endif %}
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% if quiz.id not in favorite_quiz_ids %} {% if quiz.id not in favorite_quiz_ids %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg"> <div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}"> <a class="favorite-link" href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg> </svg>
@@ -105,398 +81,7 @@
{% else %} {% else %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg"> <div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}"> <a class="favorite-link" href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% endif %}
{% endif %}
</div>
<!-- Content Section (Bottom) -->
<div class="flex-grow bg-white p-4 flex flex-col gap-3">
<!-- Title -->
<h2 class="text-xl font-bold text-gray-900 break-words mb-2">
<a href="{% url 'library:detail_quiz' quiz.id %}" class="hover:text-blue-600 transition-colors">{{ quiz.name }}</a>
</h2>
<!-- Description -->
<p class="text-sm text-gray-700 break-words mb-3">{{ quiz.description }}</p>
<!-- Quiz Info Grid -->
<div class="grid grid-cols-2 gap-x-4 gap-y-3 text-sm">
<!-- Difficulty -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-gray-800">{{ quiz.difficulty }}</span>
</div>
<!-- Questions Count -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.questions.count }} Fragen</span>
</div>
<!-- Status -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.status }}</span>
</div>
<!-- Rating -->
<div class="flex items-center gap-1">
{% if quiz.rating_count > 0 %}
{% for i in '12345'|make_list %}
{% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %}
<span class="text-yellow-500 text-base"></span>
{% else %}
<span class="text-gray-300 text-base"></span>
{% endif %}
{% endfor %}
<span class="text-gray-600 text-sm">({{ quiz.rating_count }})</span>
{% else %}
<span class="text-gray-600 text-sm">Noch keine Bewertungen</span>
{% endif %}
</div>
</div>
<!-- Authors -->
{% if quiz.authors.all %}
<div class="flex items-center gap-2 text-sm mt-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-gray-600">
{% for author in quiz.authors.all %}
{{ author.username }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</span>
</div>
{% endif %}
<!-- Action Buttons -->
<div class="flex justify-between items-center gap-2 mt-3 border-t pt-3">
{% if quiz.questions.count != 0 %}
<a href="{% url 'play:create_game' quiz.id %}"
class="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors duration-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Spielen
</a>
{% else %}
Fürs Spielen ist min. 1 Frage nötig!
{% endif %}
<div class="flex gap-1">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
{% if quiz.user_id == request.user %}
<a href="{% url 'library:edit_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</a>
<a href="{% url 'library:delete_quiz' quiz.id %}"
class="p-1.5 bg-red-100 hover:bg-red-200 text-red-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</a>
{% endif %}
</div>
</div>
</div>
</article>
{% endfor %}
</div>
<!-- Pagination für eigene Quiz -->
{% if my_quizzes.paginator.num_pages > 1 %}
<div class="flex justify-center space-x-2 mt-6 mb-8">
{% if my_quizzes.has_previous %}
<a href="?my_page={{ my_quizzes.previous_page_number }}{% if request.GET.other_page %}&other_page={{ request.GET.other_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</a>
{% endif %}
<span class="px-3 py-1 text-gray-600">Seite {{ my_quizzes.number }} von {{ my_quizzes.paginator.num_pages }}</span>
{% if my_quizzes.has_next %}
<a href="?my_page={{ my_quizzes.next_page_number }}{% if request.GET.other_page %}&other_page={{ request.GET.other_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
{% endif %}
</div>
{% endif %}
</div>
{% endif %}
{% if user.is_authenticated %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6">
<div class="flex items-center gap-3">
<h2 id="my-quizzes" class="text-2xl font-bold text-gray-900">Meine Favoriten</h2>
<div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div>
</div>
</div>
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr">
{% for quiz in favorite_quizzes %}
<article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col">
<!-- Image Section (Top) -->
<div class="h-48 w-full relative flex-shrink-0">
{% if quiz.image %}
<div class="relative w-full h-full inset-0 bg-cover bg-center">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.image.url }});">
</div>
</div>
{% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-4xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div>
{% endif %}
{% if user.is_authenticated %}
{% if quiz.favorite == False %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% else %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% endif %}
{% endif %}
</div>
<!-- Content Section (Bottom) -->
<div class="flex-grow bg-white p-4 flex flex-col gap-3">
<!-- Title -->
<h2 class="text-xl font-bold text-gray-900 break-words mb-2">
<a href="{% url 'library:detail_quiz' quiz.id %}" class="hover:text-blue-600 transition-colors">{{ quiz.name }}</a>
</h2>
<!-- Description -->
<p class="text-sm text-gray-700 break-words mb-3">{{ quiz.description }}</p>
<!-- Quiz Info Grid -->
<div class="grid grid-cols-2 gap-x-4 gap-y-3 text-sm">
<!-- Difficulty -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-gray-800">{{ quiz.difficulty }}</span>
</div>
<!-- Questions Count -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.questions.count }} Fragen</span>
</div>
<!-- Status -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.status }}</span>
</div>
<!-- Rating -->
<div class="flex items-center gap-1">
{% if quiz.rating_count > 0 %}
{% for i in '12345'|make_list %}
{% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %}
<span class="text-yellow-500 text-base"></span>
{% else %}
<span class="text-gray-300 text-base"></span>
{% endif %}
{% endfor %}
<span class="text-gray-600 text-sm">({{ quiz.rating_count }})</span>
{% else %}
<span class="text-gray-600 text-sm">Noch keine Bewertungen</span>
{% endif %}
</div>
</div>
<!-- Authors -->
{% if quiz.authors.all %}
<div class="flex items-center gap-2 text-sm mt-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-gray-600">
{% for author in quiz.authors.all %}
{{ author.username }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</span>
</div>
{% endif %}
<!-- Action Buttons -->
<div class="flex justify-between items-center gap-2 mt-3 border-t pt-3">
{% if quiz.questions.count != 0 %}
<a href="{% url 'play:create_game' quiz.id %}"
class="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors duration-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Spielen
</a>
{% else %}
Fürs Spielen ist min. 1 Frage nötig!
{% endif %}
<div class="flex gap-1">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
{% if quiz.user_id == request.user %}
<a href="{% url 'library:edit_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</a>
<a href="{% url 'library:delete_quiz' quiz.id %}"
class="p-1.5 bg-red-100 hover:bg-red-200 text-red-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</a>
{% endif %}
</div>
</div>
</div>
</article>
{% endfor %}
</div>
<!-- Pagination für eigene Quiz -->
{% if favorite_quizzes.paginator.num_pages > 1 %}
<div class="flex justify-center space-x-2 mt-6 mb-8">
{% if favorite_quizzes.has_previous %}
<a href="?my_page={{ my_quizzes.previous_page_number }}{% if request.GET.other_page %}&other_page={{ request.GET.other_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</a>
{% endif %}
<span class="px-3 py-1 text-gray-600">Seite {{ favorite_quizzes.number }} von {{ favorite_quizzes.paginator.num_pages }}</span>
{% if my_quizzes.has_next %}
<a href="?my_page={{ my_quizzes.next_page_number }}{% if request.GET.other_page %}&other_page={{ request.GET.other_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
{% endif %}
</div>
{% endif %}
</div>
{% endif %}
<!-- Quiz von anderen Nutzern -->
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6">
<div class="flex items-center gap-3">
<h2 id="other-quizzes" class="text-2xl font-bold text-gray-900">Quiz von anderen Nutzern</h2>
<div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div>
</div>
</div>
{% if other_quizzes %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr">
{% for quiz in other_quizzes %}
<article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col">
<!-- Image Section (Top) -->
<div class="h-48 w-full relative flex-shrink-0">
{% if quiz.image %}
<div class="relative w-full h-full inset-0 bg-cover bg-center">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.image.url }});">
</div>
</div>
{% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-4xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div>
{% endif %}
{% if user.is_authenticated %}
{% if quiz.id not in favorite_quiz_ids %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% else %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6"> <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg> </svg>
@@ -602,10 +187,10 @@
</div> </div>
<!-- Pagination für andere Quiz --> <!-- Pagination für andere Quiz -->
{% if other_quizzes.paginator.num_pages > 1 %} {% if all_quizzes.paginator.num_pages > 1 %}
<div class="flex justify-center space-x-2 mt-6 mb-8"> <div class="flex justify-center space-x-2 mt-6 mb-8">
{% if other_quizzes.has_previous %} {% if all_quizzes.has_previous %}
<a href="?other_page={{ other_quizzes.previous_page_number }}{% if request.GET.my_page %}&my_page={{ request.GET.my_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}" <a href="?all_page={{ all_quizzes.previous_page_number }}&{{ querystring_all }}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors"> class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
@@ -613,10 +198,10 @@
</a> </a>
{% endif %} {% endif %}
<span class="px-3 py-1 text-gray-600">Seite {{ other_quizzes.number }} von {{ other_quizzes.paginator.num_pages }}</span> <span class="px-3 py-1 text-gray-600">Seite {{ all_quizzes.number }} von {{ all_quizzes.paginator.num_pages }}</span>
{% if other_quizzes.has_next %} {% if all_quizzes.has_next %}
<a href="?other_page={{ other_quizzes.next_page_number }}{% if request.GET.my_page %}&my_page={{ request.GET.my_page }}{% endif %}{% if request.GET.search %}&search={{ request.GET.search }}{% endif %}" <a href="?all_page={{ all_quizzes.next_page_number }}&{{ querystring_all }}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors"> class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
@@ -630,42 +215,54 @@
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-4"> <div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-4">
<p class="text-gray-600 text-center">Keine öffentlichen Quiz gefunden.</p> <p class="text-gray-600 text-center">Keine öffentlichen Quiz gefunden.</p>
</div> </div>
{% endif %} {% endif %}<script>
<script>
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', function () {
const filterPanel = document.getElementById('filterPanel');
const filterButton = document.getElementById('filterButton');
const urlParams = new URLSearchParams(window.location.search); const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('filter') === '1') { if (urlParams.get('filter') === '1') {
document.getElementById('filterPanel').classList.add('show'); filterPanel.classList.add('show');
} }
document.getElementById('filterButton').addEventListener('click', function() { filterButton.addEventListener('click', function () {
document.getElementById('filterPanel').classList.toggle('show'); filterPanel.classList.toggle('show');
// URL beim Öffnen aktualisieren const params = new URLSearchParams(window.location.search);
const params = new URLSearchParams(window.location.search); if (filterPanel.classList.contains('show')) {
if (document.getElementById('filterPanel').classList.contains('show')) { //params.set('filter', '1');
params.set('filter', '1'); } else {
} else { params.delete('filter');
params.delete('filter'); }
}
history.replaceState(null, '', `${location.pathname}?${params}`); history.replaceState(null, '', `${location.pathname}?${params}`);
}); });
});
document.addEventListener("DOMContentLoaded", function () {
// Scroll-Position beim Laden setzen
const scrollPos = localStorage.getItem("scroll-position");
if (scrollPos !== null) {
window.scrollTo(0, parseInt(scrollPos));
localStorage.removeItem("scroll-position");
}
// Nur Favoriten-Links abfangen
const favoriteLinks = document.querySelectorAll('a.favorite-link');
favoriteLinks.forEach(link => {
link.addEventListener('click', function () {
localStorage.setItem("scroll-position", window.scrollY);
});
});
}); });
if (window.location.pathname === "/library/") {
window.addEventListener("beforeunload", function () {
localStorage.setItem("meine_seite_scroll", window.scrollY);
});
window.addEventListener("load", function () {
const scrollPos = localStorage.getItem("meine_seite_scroll");
if (scrollPos !== null) {
window.scrollTo(0, parseInt(scrollPos));
}
});
}
</script> </script>
{% endblock content %} {% endblock content %}

View File

@@ -0,0 +1,285 @@
{% extends 'base.html' %}
{% load static %}
{% block extra_css %}
<style>
.custom-outline {
-webkit-text-stroke: 0.2px rgb(0, 0, 0);
}
</style>
{% endblock %}
{% block content %}
{% block head %}
{% include 'library/head.html' %}
{% endblock %}
{% if user.is_authenticated %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6">
<div class="flex justify-end text-sm mb-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 text-blue-600">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
</svg>
<div class="gap-2 mx-2">
Anzahl aller Ergebnisse:
<span class="font-bold"> {{ total_all_found }}</span>
</div>
(verschiedene:
<span class="font-bold ml-1"> {{ total_all }}</span>) </div>
<div class="flex flex-wrap justify-end gap-4 font-bold text-blue-600 whitespace-nowrap text-sm mb-2 md:mb-0">
<a href="{{ url_all }}?{{ request.GET.urlencode }}">Alle ({{ total_all }})</a>
<a href="{{ url_my }}?{{ request.GET.urlencode }}">Meine Quizze ({{ total_my }})</a>
<p class="font-black underline">Meine Favoriten ({{ total_favorite }})</p>
</div>
{% endif %}
<div class="flex items-center gap-3">
<h2 id="my-quizzes" class="text-2xl font-bold text-gray-900">Meine Favoriten</h2>
<div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div>
</div>
</div>
{% if request.user.is_authenticated and favorite_quizzes %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr">
{% for quiz in favorite_quizzes %}
<article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col">
<!-- Image Section (Top) -->
<div class="h-48 w-full relative flex-shrink-0">
{% if quiz.image %}
<div class="relative w-full h-full inset-0 bg-cover bg-center">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.image.url }});">
</div>
</div>
{% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-3xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div>
{% endif %}
{% if user.is_authenticated %}
{% if quiz.favorite == False %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a class="favorite-link" href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% else %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a class="favorite-link" href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% endif %}
{% endif %}
</div>
<!-- Content Section (Bottom) -->
<div class="flex-grow bg-white p-4 flex flex-col gap-3">
<!-- Title -->
<h2 class="text-xl font-bold text-gray-900 break-words mb-2">
<a href="{% url 'library:detail_quiz' quiz.id %}" class="hover:text-blue-600 transition-colors">{{ quiz.name }}</a>
</h2>
<!-- Description -->
<p class="text-sm text-gray-700 break-words mb-3">{{ quiz.description }}</p>
<!-- Quiz Info Grid -->
<div class="grid grid-cols-2 gap-x-4 gap-y-3 text-sm">
<!-- Difficulty -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-gray-800">{{ quiz.difficulty }}</span>
</div>
<!-- Questions Count -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.questions.count }} Fragen</span>
</div>
<!-- Status -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.status }}</span>
</div>
<!-- Rating -->
<div class="flex items-center gap-1">
{% if quiz.rating_count > 0 %}
{% for i in '12345'|make_list %}
{% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %}
<span class="text-yellow-500 text-base"></span>
{% else %}
<span class="text-gray-300 text-base"></span>
{% endif %}
{% endfor %}
<span class="text-gray-600 text-sm">({{ quiz.rating_count }})</span>
{% else %}
<span class="text-gray-600 text-sm">Noch keine Bewertungen</span>
{% endif %}
</div>
</div>
<!-- Authors -->
{% if quiz.authors.all %}
<div class="flex items-center gap-2 text-sm mt-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-gray-600">
{% for author in quiz.authors.all %}
{{ author.username }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</span>
</div>
{% endif %}
<!-- Action Buttons -->
<div class="flex justify-between items-center gap-2 mt-3 border-t pt-3">
{% if quiz.questions.count != 0 %}
<a href="{% url 'play:create_game' quiz.id %}"
class="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors duration-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Spielen
</a>
{% else %}
Fürs Spielen ist min. 1 Frage nötig!
{% endif %}
<div class="flex gap-1">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
{% if quiz.user_id == request.user %}
<a href="{% url 'library:edit_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</a>
<a href="{% url 'library:delete_quiz' quiz.id %}"
class="p-1.5 bg-red-100 hover:bg-red-200 text-red-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</a>
{% endif %}
</div>
</div>
</div>
</article>
{% endfor %}
</div>
<!-- Pagination für Meine Quizze -->
{% if favorite_quizzes.paginator.num_pages > 1 %}
<div class="flex justify-center space-x-2 mt-6 mb-8">
{% if favorite_quizzes.has_previous %}
<a href="?favorite_page={{ favorite.previous_page_number }}&{{ querystring_favorite }}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</a>
{% endif %}
<span class="px-3 py-1 text-gray-600">Seite {{ favorite_quizzes.number }} von {{ favorite_quizzes.paginator.num_pages }}</span>
{% if favorite_quizzes.has_next %}
<a href="?favorite_page={{ favorite_quizzes.next_page_number }}&{{ querystring_favorite }}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
{% endif %}
</div>
{% endif %}
</div>
{% else %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-4">
<p class="text-gray-600 text-center">Keine favorisierten Quiz gefunden.</p>
</div>
{% endif %}<script>
document.addEventListener('DOMContentLoaded', function () {
const filterPanel = document.getElementById('filterPanel');
const filterButton = document.getElementById('filterButton');
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('filter') === '1') {
filterPanel.classList.add('show');
}
filterButton.addEventListener('click', function () {
filterPanel.classList.toggle('show');
const params = new URLSearchParams(window.location.search);
if (filterPanel.classList.contains('show')) {
//params.set('filter', '1');
} else {
params.delete('filter');
}
history.replaceState(null, '', `${location.pathname}?${params}`);
});
});
document.addEventListener("DOMContentLoaded", function () {
// Scroll-Position beim Laden setzen
const scrollPos = localStorage.getItem("scroll-position");
if (scrollPos !== null) {
window.scrollTo(0, parseInt(scrollPos));
localStorage.removeItem("scroll-position");
}
// Nur Favoriten-Links abfangen
const favoriteLinks = document.querySelectorAll('a.favorite-link');
favoriteLinks.forEach(link => {
link.addEventListener('click', function () {
localStorage.setItem("scroll-position", window.scrollY);
});
});
});
</script>
{% endblock content %}

View File

@@ -0,0 +1,280 @@
{% extends 'base.html' %}
{% load static %}
{% block extra_css %}
<style>
.custom-outline {
-webkit-text-stroke: 0.2px rgb(0, 0, 0);
}
</style>
{% endblock %}
{% block content %}
{% block head %}
{% include 'library/head.html' %}
{% endblock %}
{% if user.is_authenticated %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-8 mb-6">
<div class="flex justify-end text-sm mb-2">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 text-blue-600">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
</svg>
<div class="gap-2 mx-2">
Anzahl aller Ergebnisse:
<span class="font-bold"> {{ total_all_found }}</span>
</div>
(verschiedene:
<span class="font-bold ml-1"> {{ total_all }}</span>) </div>
<div class="flex flex-wrap justify-end gap-4 font-bold text-blue-600 whitespace-nowrap text-sm mb-2 md:mb-0">
<a href="{{ url_all }}?{{ request.GET.urlencode }}">Alle ({{ total_all }})</a>
<p class="font-black underline">Meine Quizze ({{ total_my }})</p>
<a href="{{ url_favorite }}?{{ request.GET.urlencode }}">Meine Favoriten ({{ total_favorite }})</a>
</div>
<div class="flex items-center gap-3">
<h2 id="my-quizzes" class="text-2xl font-bold text-gray-900">Meine Quizze</h2>
<div class="h-0.5 flex-grow bg-gradient-to-r from-blue-600 to-transparent rounded-full"></div>
</div>
</div>
{% endif %}
{% if request.user.is_authenticated and my_quizzes %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8">
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6 auto-rows-fr">
{% for quiz in my_quizzes %}
<article class="relative min-h-[20rem] rounded-xl shadow-lg overflow-hidden bg-white flex flex-col">
<!-- Image Section (Top) -->
<div class="h-48 w-full relative flex-shrink-0">
{% if quiz.image %}
<div class="relative w-full h-full inset-0 bg-cover bg-center">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.image.url }});">
</div>
</div>
{% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600 flex items-center justify-center font-black text-white text-3xl px-4 overflow-auto break-words hyphens-auto ">{{ quiz.name }}</div>
{% endif %}
{% if user.is_authenticated %}
{% if quiz.id not in favorite_quiz_ids %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a class="favorite-link" href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-black size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% else %}
<div class="absolute top-4 right-4 bg-white bg-opacity-60 text-white px-4 py-2 rounded-lg shadow-lg">
<a class="favorite-link" href="{% url 'library:favorite_quiz' quiz.pk %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="text-yellow-400 size-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 0 1 1.04 0l2.125 5.111a.563.563 0 0 0 .475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 0 0-.182.557l1.285 5.385a.562.562 0 0 1-.84.61l-4.725-2.885a.562.562 0 0 0-.586 0L6.982 20.54a.562.562 0 0 1-.84-.61l1.285-5.386a.562.562 0 0 0-.182-.557l-4.204-3.602a.562.562 0 0 1 .321-.988l5.518-.442a.563.563 0 0 0 .475-.345L11.48 3.5Z" />
</svg>
</a>
</div>
{% endif %}
{% endif %}
</div>
<!-- Content Section (Bottom) -->
<div class="flex-grow bg-white p-4 flex flex-col gap-3">
<!-- Title -->
<h2 class="text-xl font-bold text-gray-900 break-words mb-2">
<a href="{% url 'library:detail_quiz' quiz.id %}" class="hover:text-blue-600 transition-colors">{{ quiz.name }}</a>
</h2>
<!-- Description -->
<p class="text-sm text-gray-700 break-words mb-3">{{ quiz.description }}</p>
<!-- Quiz Info Grid -->
<div class="grid grid-cols-2 gap-x-4 gap-y-3 text-sm">
<!-- Difficulty -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
<span class="text-gray-800">{{ quiz.difficulty }}</span>
</div>
<!-- Questions Count -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.questions.count }} Fragen</span>
</div>
<!-- Status -->
<div class="flex items-center gap-2">
<svg class="w-4 h-4 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="text-gray-800">{{ quiz.status }}</span>
</div>
<!-- Rating -->
<div class="flex items-center gap-1">
{% if quiz.rating_count > 0 %}
{% for i in '12345'|make_list %}
{% if forloop.counter <= quiz.average_rating|floatformat:0|add:0 %}
<span class="text-yellow-500 text-base"></span>
{% else %}
<span class="text-gray-300 text-base"></span>
{% endif %}
{% endfor %}
<span class="text-gray-600 text-sm">({{ quiz.rating_count }})</span>
{% else %}
<span class="text-gray-600 text-sm">Noch keine Bewertungen</span>
{% endif %}
</div>
</div>
<!-- Authors -->
{% if quiz.authors.all %}
<div class="flex items-center gap-2 text-sm mt-2">
<svg class="w-4 h-4 text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-gray-600">
{% for author in quiz.authors.all %}
{{ author.username }}{% if not forloop.last %}, {% endif %}
{% endfor %}
</span>
</div>
{% endif %}
<!-- Action Buttons -->
<div class="flex justify-between items-center gap-2 mt-3 border-t pt-3">
{% if quiz.questions.count != 0 %}
<a href="{% url 'play:create_game' quiz.id %}"
class="px-3 py-1.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg transition-colors duration-200 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
Spielen
</a>
{% else %}
Fürs Spielen ist min. 1 Frage nötig!
{% endif %}
<div class="flex gap-1">
<a href="{% url 'library:detail_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
</svg>
</a>
{% if quiz.user_id == request.user %}
<a href="{% url 'library:edit_quiz' quiz.id %}"
class="p-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</a>
<a href="{% url 'library:delete_quiz' quiz.id %}"
class="p-1.5 bg-red-100 hover:bg-red-200 text-red-700 rounded-lg transition-colors duration-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</a>
{% endif %}
</div>
</div>
</div>
</article>
{% endfor %}
</div>
<!-- Pagination für Meine Quizze -->
{% if my_quizzes.paginator.num_pages > 1 %}
<div class="flex justify-center space-x-2 mt-6 mb-8">
{% if my_quizzes.has_previous %}
<a href="?my_page={{ my_quizzes.previous_page_number }}&{{ querystring_my }}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</a>
{% endif %}
<span class="px-3 py-1 text-gray-600">Seite {{ my_quizzes.number }} von {{ my_quizzes.paginator.num_pages }}</span>
{% if my_quizzes.has_next %}
<a href="?my_page={{ my_quizzes.next_page_number }}&{{ querystring_my }}"
class="px-3 py-1 bg-gray-100 text-gray-700 hover:bg-gray-200 rounded-lg transition-colors">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
{% endif %}
</div>
{% endif %}
</div>
{% else %}
<div class="container mx-auto px-4 sm:px-6 lg:px-8 mt-4">
<p class="text-gray-600 text-center">Keine eigenen Quiz gefunden.</p>
</div>
{% endif %}<script>
document.addEventListener('DOMContentLoaded', function () {
const filterPanel = document.getElementById('filterPanel');
const filterButton = document.getElementById('filterButton');
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('filter') === '1') {
filterPanel.classList.add('show');
}
filterButton.addEventListener('click', function () {
filterPanel.classList.toggle('show');
const params = new URLSearchParams(window.location.search);
if (filterPanel.classList.contains('show')) {
//params.set('filter', '1');
} else {
params.delete('filter');
}
history.replaceState(null, '', `${location.pathname}?${params}`);
});
});
document.addEventListener("DOMContentLoaded", function () {
// Scroll-Position beim Laden setzen
const scrollPos = localStorage.getItem("scroll-position");
if (scrollPos !== null) {
window.scrollTo(0, parseInt(scrollPos));
localStorage.removeItem("scroll-position");
}
// Nur Favoriten-Links abfangen
const favoriteLinks = document.querySelectorAll('a.favorite-link');
favoriteLinks.forEach(link => {
link.addEventListener('click', function () {
localStorage.setItem("scroll-position", window.scrollY);
});
});
});
</script>
{% endblock content %}

View File

@@ -10,7 +10,7 @@
<!-- Desktop Search (centered on screen) - Hidden on mobile --> <!-- Desktop Search (centered on screen) - Hidden on mobile -->
<div class="hidden md:flex justify-center items-center absolute left-1/2 transform -translate-x-1/2 min-w-40"> <div class="hidden md:flex justify-center items-center absolute left-1/2 transform -translate-x-1/2 min-w-40">
<form method="get" action="{% url 'library:overview_quiz' %}" class="mr-2 ml-2 flex items-center justify-center w-screen max-w-sm bg-white rounded-full px-4 py-1 shadow-md"> <form method="get" action="{% url link|default:'library:overview_quiz' %}" class="mr-2 ml-2 flex items-center justify-center w-screen max-w-sm bg-white rounded-full px-4 py-1 shadow-md">
<input id="quiz-search-desktop" list="quiz-names-desktop" type="text" name="search" placeholder="Suche ..." value="{{ request.GET.search }}" <input id="quiz-search-desktop" list="quiz-names-desktop" type="text" name="search" placeholder="Suche ..." value="{{ request.GET.search }}"
class="text-sm w-full px-3 py-1 text-black bg-transparent focus:outline-none lg:text-base"> class="text-sm w-full px-3 py-1 text-black bg-transparent focus:outline-none lg:text-base">
<button class="py-1 text-blue-600"> <button class="py-1 text-blue-600">
@@ -51,7 +51,7 @@
<div id="mobile-menu" class="hidden absolute w-full z-50 md:hidden opacity-0 transform -translate-y-2 transition-all duration-300"> <div id="mobile-menu" class="hidden absolute w-full z-50 md:hidden opacity-0 transform -translate-y-2 transition-all duration-300">
<div class="bg-blue-500 pt-3 pb-4 rounded-b-lg shadow-inner"> <div class="bg-blue-500 pt-3 pb-4 rounded-b-lg shadow-inner">
<!-- Search in mobile menu --> <!-- Search in mobile menu -->
<form method="get" action="{% url 'library:overview_quiz' %}" class="px-4 mb-4"> <form method="get" action="{% url link|default:'library:overview_quiz' %}" class="px-4 mb-4">
<div class="flex items-center justify-center bg-white rounded-full px-4 py-2 shadow-md"> <div class="flex items-center justify-center bg-white rounded-full px-4 py-2 shadow-md">
<input id="quiz-search-mobile" list="quiz-names-mobile" type="text" name="search" placeholder="Suche ..." value="{{ request.GET.search }}" <input id="quiz-search-mobile" list="quiz-names-mobile" type="text" name="search" placeholder="Suche ..." value="{{ request.GET.search }}"
class="w-full text-sm px-2 text-black bg-transparent focus:outline-none"> class="w-full text-sm px-2 text-black bg-transparent focus:outline-none">