Merge branch '114-suchleiste-vorschlage-anzeigen' into 'master'
Resolve "Suchleiste - Vorschläge anzeigen!" Closes #114 See merge request enrichment-2024/qivip!70
This commit is contained in:
@@ -25,7 +25,7 @@ urlpatterns = [
|
|||||||
path('', include('homepage.urls')),
|
path('', include('homepage.urls')),
|
||||||
path('play/', include('play.urls')),
|
path('play/', include('play.urls')),
|
||||||
path('library/', include('library.urls')),
|
path('library/', include('library.urls')),
|
||||||
path('components/', include('components.urls'))
|
path('components/', include('components.urls')),
|
||||||
]
|
]
|
||||||
|
|
||||||
if settings.DEBUG:
|
if settings.DEBUG:
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from django import forms
|
|||||||
|
|
||||||
class QuizFilterForm(forms.Form):
|
class QuizFilterForm(forms.Form):
|
||||||
search = forms.CharField(required=False, label="Suche", widget=forms.TextInput(attrs={
|
search = forms.CharField(required=False, label="Suche", widget=forms.TextInput(attrs={
|
||||||
'class': 'flex flex-grow rounded-lg p-2' ,'placeholder': 'Suche ...',
|
'class': 'flex flex-grow rounded-lg p-2' ,'placeholder': 'Suche ...', 'list': 'quiz-names',
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -16,5 +16,6 @@ urlpatterns = [
|
|||||||
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'),
|
path('favorite_quiz/<int:pk>/', views.favorite_quiz, name='favorite_quiz'),
|
||||||
|
path("quiz-names-json/", views.quiz_names_json, name="quiz_names_json"),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
import uuid
|
import uuid
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
|
||||||
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
|
||||||
@@ -16,6 +16,18 @@ from django.db.models import Count
|
|||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
# Übersicht aller Quizze
|
# Übersicht aller Quizze
|
||||||
|
|
||||||
|
|
||||||
|
def quiz_names_json(request):
|
||||||
|
form = QuizFilterForm(request.GET)
|
||||||
|
if form.is_valid():
|
||||||
|
search = form.cleaned_data.get('search')
|
||||||
|
names = list(
|
||||||
|
QivipQuiz.objects.all().annotate(max_amout_questions=Count('questions')).filter(max_amout_questions__gte=1, status='öffentlich', name__icontains=search)
|
||||||
|
.values_list('name', flat=True)
|
||||||
|
.distinct()
|
||||||
|
)
|
||||||
|
return JsonResponse(names, safe=False)
|
||||||
|
|
||||||
def overview_quiz(request):
|
def overview_quiz(request):
|
||||||
# Filter
|
# Filter
|
||||||
form = QuizFilterForm(request.GET)
|
form = QuizFilterForm(request.GET)
|
||||||
@@ -85,36 +97,6 @@ def overview_quiz(request):
|
|||||||
except:
|
except:
|
||||||
favorite_quizzes=QivipQuiz.objects.none()
|
favorite_quizzes=QivipQuiz.objects.none()
|
||||||
|
|
||||||
"""
|
|
||||||
if search: # Suche nach Namen
|
|
||||||
filter_other_quizzes = filter_other_quizzes.filter(name__icontains=search)
|
|
||||||
filter_my_quizzes = filter_my_quizzes.filter(name__icontains=search)
|
|
||||||
|
|
||||||
if min_amout_questions:
|
|
||||||
filter_other_quizzes = filter_other_quizzes.filter(max_amout_questions__gte=min_amout_questions)
|
|
||||||
filter_my_quizzes = filter_my_quizzes.filter(max_amout_questions__gte=min_amout_questions) if filter_my_quizzes else QivipQuiz.objects.none()
|
|
||||||
|
|
||||||
if max_amout_questions:
|
|
||||||
filter_other_quizzes = filter_other_quizzes.filter(max_amout_questions__lte=max_amout_questions)
|
|
||||||
try:
|
|
||||||
filter_my_quizzes =filter_my_quizzes.filter(max_amout_questions__lte=max_amout_questions)
|
|
||||||
except:
|
|
||||||
filter_my_quizzes = QivipQuiz.objects.none()
|
|
||||||
|
|
||||||
if username:
|
|
||||||
try:
|
|
||||||
user = User.objects.get(username=username) # Benutzer anhand des Namens holen
|
|
||||||
filter_other_quizzes = filter_other_quizzes.filter(user_id=user.id)
|
|
||||||
filter_my_quizzes = filter_my_quizzes.filter(user_id=user.id) # Nach der user_id filtern
|
|
||||||
except User.DoesNotExist:
|
|
||||||
filter_other_quizzes = QivipQuiz.objects.none() # Falls User nicht existiert, leere Query zurückgeben
|
|
||||||
filter_my_quizzes = QivipQuiz.objects.none()
|
|
||||||
|
|
||||||
try:
|
|
||||||
filter_other_quizzes=filter_other_quizzes.exclude(user_id=request.user)
|
|
||||||
except:
|
|
||||||
filter_other_quizzes=filter_other_quizzes
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
# Pagination for my quizzes
|
# Pagination for my quizzes
|
||||||
|
|||||||
@@ -11,7 +11,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 '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 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">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5 md:size-6">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-5 md:size-6">
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
<!-- 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 '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 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">
|
||||||
<button class="text-blue-600">
|
<button class="text-blue-600">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
|
||||||
@@ -63,6 +63,78 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<datalist id="quiz-names-desktop">
|
||||||
|
{% for quiz in quiz_names %}
|
||||||
|
<option value="{{ quiz.name }}">
|
||||||
|
{% endfor %}
|
||||||
|
</datalist>
|
||||||
|
|
||||||
|
<datalist id="quiz-names-mobile">
|
||||||
|
{% for quiz in quiz_names %}
|
||||||
|
<button value="{{ quiz.name }}" onclick="performSearch(this.value)"><option value="{{ quiz.name }}" ></option></button>
|
||||||
|
{% endfor %}
|
||||||
|
</datalist>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{{ quiz_names|json_script:"quiz-names-data" }}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
async function fetchQuizNames() {
|
||||||
|
try {
|
||||||
|
const response = await fetch("{% url 'library:quiz_names_json' %}");
|
||||||
|
if (!response.ok) throw new Error("Netzwerkfehler");
|
||||||
|
const data = await response.json();
|
||||||
|
if (Array.isArray(data)) return data;
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Fehler beim Laden der Quiz-Namen:", err);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupAutocomplete(inputId, datalistId, quizNames) {
|
||||||
|
const searchInput = document.getElementById(inputId);
|
||||||
|
const datalist = document.getElementById(datalistId);
|
||||||
|
if (!searchInput || !datalist) return;
|
||||||
|
|
||||||
|
// Wenn der Benutzer den Text ändert, Filter anwenden
|
||||||
|
searchInput.addEventListener('input', () => {
|
||||||
|
const value = searchInput.value.toLowerCase();
|
||||||
|
datalist.innerHTML = '';
|
||||||
|
quizNames
|
||||||
|
.filter(name => name.toLowerCase().includes(value))
|
||||||
|
.slice(0, 10)
|
||||||
|
.forEach(name => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = name;
|
||||||
|
datalist.appendChild(option);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Funktion zum Durchführen der Suche
|
||||||
|
function performSearch(query) {
|
||||||
|
datalist.innerHTML = '';
|
||||||
|
// Hier kann deine Logik zur Verarbeitung der Suche eingefügt werden
|
||||||
|
|
||||||
|
// Falls du die Seite zu einer Suchergebnisseite weiterleiten möchtest:
|
||||||
|
window.location.href = `{% url 'library:overview_quiz' %}?search=${query}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
const quizNames = await fetchQuizNames();
|
||||||
|
setupAutocomplete('quiz-search-desktop', 'quiz-names-desktop', quizNames);
|
||||||
|
setupAutocomplete('quiz-search-mobile', 'quiz-names-mobile', quizNames);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<!-- Mobile Navigation Links -->
|
<!-- Mobile Navigation Links -->
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<form action="{% url 'play:join_game' %}" method="post" class="flex flex-col gap-4">
|
<form action="{% url 'play:join_game' %}" method="post" class="flex flex-col gap-4">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="flex gap-3 items-center justify-center">
|
<div class="flex gap-3 items-center justify-center">
|
||||||
<input type="text" name="game_code" placeholder="123456" maxlength="6" class="w-32 p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black focus:outline-none focus:ring-2 focus:ring-blue-400 focus:bg-blue-100 text-center tracking-wider" autofocus>
|
<input type="number" name="game_code" placeholder="123456" maxlength="6" class="w-32 p-3 rounded-full bg-gray-200 focus:scale-105 transition duration-200 font-black focus:outline-none focus:ring-2 focus:ring-blue-400 focus:bg-blue-100 text-center tracking-wider" autofocus>
|
||||||
<button type="submit" class="h-12 px-6 rounded-full bg-blue-600 text-white font-black hover:scale-105 transition duration-200 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-700 active:scale-95 group" disabled>
|
<button type="submit" class="h-12 px-6 rounded-full bg-blue-600 text-white font-black hover:scale-105 transition duration-200 flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed hover:bg-blue-700 active:scale-95 group" disabled>
|
||||||
<span class="hidden sm:block">Beitreten</span>
|
<span class="hidden sm:block">Beitreten</span>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 transition-transform group-hover:translate-x-1">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-5 h-5 transition-transform group-hover:translate-x-1">
|
||||||
|
|||||||
Reference in New Issue
Block a user