Zwischenstand_Favoriten

This commit is contained in:
ben8
2025-04-10 19:55:11 +02:00
parent b0782868b8
commit 7ce0e6e9dd
9 changed files with 373 additions and 11 deletions

View File

@@ -1,5 +1,5 @@
from django.contrib import admin from django.contrib import admin
from .models import QivipQuiz, QivipQuestion, QuizCategory from .models import QivipQuiz, QivipQuestion, QivipQuizFavorite, QuizCategory
# Für das QivipQuiz Modell # Für das QivipQuiz Modell
class QivipQuizAdmin(admin.ModelAdmin): class QivipQuizAdmin(admin.ModelAdmin):
@@ -18,13 +18,13 @@ class QuizCategoryAdmin(admin.ModelAdmin):
search_fields = ('name',) search_fields = ('name',)
prepopulated_fields = {'slug': ('name',)} # Erstelle automatisch den Slug basierend auf dem Namen prepopulated_fields = {'slug': ('name',)} # Erstelle automatisch den Slug basierend auf dem Namen
# Für das Tag Modell
class TagAdmin(admin.ModelAdmin):
list_display = ('name',) # Zeige nur den Namen des Tags class QivipQuizFavoriteAdmin(admin.ModelAdmin):
search_fields = ('name',) list_display = ('user', 'favorite', 'quiz') # Welche Felder sollen in der Übersicht angezeigt werden
# Registrierung der Modelle im Admin # Registrierung der Modelle im Admin
admin.site.register(QivipQuiz, QivipQuizAdmin) admin.site.register(QivipQuiz, QivipQuizAdmin)
admin.site.register(QivipQuestion, QivipQuestionAdmin) admin.site.register(QivipQuestion, QivipQuestionAdmin)
admin.site.register(QuizCategory, QuizCategoryAdmin) admin.site.register(QuizCategory, QuizCategoryAdmin)
admin.site.register(QivipQuizFavorite, QivipQuizFavoriteAdmin)

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-04-10 14:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0034_alter_qivipquiz_status'),
]
operations = [
migrations.AddField(
model_name='qivipquiz',
name='favorite',
field=models.BooleanField(default=False),
),
]

View File

@@ -0,0 +1,32 @@
# Generated by Django 5.1.7 on 2025-04-10 16:02
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0035_qivipquiz_favorite'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='qivipquiz',
name='favorite',
),
migrations.CreateModel(
name='QuizFavorite',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('favorite', models.BooleanField(default=False)),
('quiz', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='library.qivipquiz')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'unique_together': {('user', 'quiz')},
},
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1.7 on 2025-04-10 16:05
from django.conf import settings
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('library', '0036_remove_qivipquiz_favorite_quizfavorite'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RenameModel(
old_name='QuizFavorite',
new_name='QivipQuizFavorite',
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1.7 on 2025-04-10 17:08
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0037_rename_quizfavorite_qivipquizfavorite'),
]
operations = [
migrations.AlterField(
model_name='qivipquizfavorite',
name='quiz',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='favorites', to='library.qivipquiz'),
),
]

View File

@@ -43,6 +43,13 @@ class QivipQuiz(models.Model):
return self.name return self.name
class QivipQuizFavorite(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE) # Verknüpfung zu User
quiz = models.ForeignKey(QivipQuiz, on_delete=models.CASCADE, related_name='favorites') # Verknüpfung zu Quiz
favorite = models.BooleanField(default=False) # Favoritenstatus
class Meta:
unique_together = ('user', 'quiz') # Sicherstellen, dass es nur ein Favorit pro User und Quiz gibt
# Create your models here. # Create your models here.

View File

@@ -15,5 +15,6 @@ urlpatterns = [
path('question/edit/<int:pk>/', views.edit_question, name='edit_question'), path('question/edit/<int:pk>/', views.edit_question, name='edit_question'),
path('question/delete/<int:pk>/', views.delete_question, name='delete_question'), path('question/delete/<int:pk>/', views.delete_question, name='delete_question'),
path('detail/copy/<int:pk>/', views.copy_quiz, name='copy_quiz'), path('detail/copy/<int:pk>/', views.copy_quiz, name='copy_quiz'),
path('favorite_quiz/<int:pk>/', views.favorite_quiz, name='favorite_quiz'),
] ]

View File

@@ -1,10 +1,11 @@
import uuid import uuid
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404 from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.urls import reverse from django.urls import reverse
from django.contrib import messages from django.contrib import messages
from .models import QivipQuiz, QivipQuestion from .models import QivipQuiz, QivipQuestion, QivipQuizFavorite
from .forms import QuizForm, QuestionForm from .forms import QuizForm, QuestionForm
import json import json
from django.core.paginator import Paginator from django.core.paginator import Paginator
@@ -18,6 +19,10 @@ def overview_quiz(request):
# Filter # Filter
form = QuizFilterForm(request.GET) form = QuizFilterForm(request.GET)
favorite_quizzes = QivipQuizFavorite.objects.filter(user=request.user, favorite=True)
favorite_quiz_ids = favorite_quizzes.values_list('quiz', flat=True)
favorite_quizzes = QivipQuiz.objects.filter(id__in=favorite_quiz_ids)
# 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_amout_questions=Count('questions'))
@@ -82,6 +87,8 @@ def overview_quiz(request):
context = { context = {
'show_search': True, 'show_search': True,
'favorite_quiz_ids': favorite_quiz_ids,
'favorite_quizzes':favorite_quizzes,
'my_quizzes': my_quizzes, 'my_quizzes': my_quizzes,
'other_quizzes': other_quizzes, 'other_quizzes': other_quizzes,
'form': form, 'form': form,
@@ -90,7 +97,21 @@ def overview_quiz(request):
return render(request, 'library/overview_quiz.html', context) return render(request, 'library/overview_quiz.html', context)
# Neues Quiz erstellen
@login_required
def favorite_quiz(request, pk):
quiz= get_object_or_404(QivipQuiz, pk=pk)
favorite = QivipQuizFavorite.objects.filter(user=request.user, quiz=quiz).first()
if favorite:
favorite.favorite=not favorite.favorite
print(favorite.favorite)
favorite.save()
else:
QivipQuizFavorite.objects.create(user=request.user, quiz=quiz, favorite=True)
return redirect('library:overview_quiz')
# Neues Quiz erstellen # Neues Quiz erstellen
@login_required @login_required
@@ -102,7 +123,7 @@ def new_quiz(request):
quiz.user_id = request.user quiz.user_id = request.user
#quiz.creator = request.user #quiz.creator = request.user
quiz.save() quiz.save()
form.save_m2m() # Speichert die Many-to-Many Beziehungen (Tags) #form.save_m2m() # Speichert die Many-to-Many Beziehungen (Tags)
return redirect('library:detail_quiz', pk=quiz.pk) return redirect('library:detail_quiz', pk=quiz.pk)
else: else:

View File

@@ -77,6 +77,7 @@
</div> </div>
{% if user.is_authenticated %} {% 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"> <div class="flex items-center gap-3">
@@ -92,13 +93,40 @@
<!-- 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">
{% if quiz.image %} {% if quiz.image %}
<div class="absolute inset-0 bg-cover bg-center" style="background-image: url({{ quiz.image.url }});"></div> <div class="relative w-full h-50">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.url }});">
</div>
</div>
{% else %} {% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600"></div> <div class="h-full bg-gradient-to-r from-blue-500 to-blue-600"></div>
{% endif %} {% endif %}
{% 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> </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 %}
</div>
<!-- Content Section (Bottom) --> <!-- Content Section (Bottom) -->
<div class="flex-grow bg-white p-4 flex flex-col gap-3"> <div class="flex-grow bg-white p-4 flex flex-col gap-3">
<!-- Title --> <!-- Title -->
@@ -228,6 +256,197 @@
</div> </div>
{% endif %} {% 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-50">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.url }});">
</div>
</div>
{% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600"></div>
{% endif %}
{% 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 %}
</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">
{% 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>
</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">
<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>
<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 == 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 --> <!-- Quiz von anderen Nutzern -->
<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"> <div class="flex items-center gap-3">
@@ -244,11 +463,37 @@
<!-- 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">
{% if quiz.image %} {% if quiz.image %}
<div class="absolute inset-0 bg-cover bg-center" style="background-image: url({{ quiz.image.url }});"></div> <div class="relative w-full h-50">
<!-- Hintergrundbild -->
<div class="absolute inset-0 bg-cover bg-center"
style="background-image: url({{ quiz.image.url }});">
</div>
</div>
{% else %} {% else %}
<div class="h-full bg-gradient-to-r from-blue-500 to-blue-600"></div> <div class="h-full bg-gradient-to-r from-blue-500 to-blue-600"></div>
{% endif %} {% endif %}
{% 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 %}
</div> </div>
<!-- Content Section (Bottom) --> <!-- Content Section (Bottom) -->