Resolve "Fragen Reihenfolge verschieben" #289

Merged
ben8 merged 1 commits from 165-fragen-reihenfolge-verschieben into master 2026-01-17 12:55:54 +00:00
5 changed files with 92 additions and 4 deletions
Showing only changes of commit 0d21d0b94b - Show all commits

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.1.7 on 2026-01-17 11:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('library', '0071_quizcriticism_id_question'),
]
operations = [
migrations.AlterModelOptions(
name='qivipquestion',
options={'ordering': ['order']},
),
migrations.AddField(
model_name='qivipquestion',
name='order',
field=models.PositiveIntegerField(default=0),
),
]

View File

@@ -113,6 +113,10 @@ class QivipQuestion(models.Model):
related_name="questions"
)
credits=models.TextField(blank=True, editable=True)
order = models.PositiveIntegerField(default=0)
class Meta:
ordering = ['order'] # Fragen nach Reihenfolge sortieren
def __str__(self):
return self.data[:50]

View File

@@ -20,6 +20,8 @@ urlpatterns = [
path('detail/copy/<int:pk>/', views.copy_quiz, name='copy_quiz'),
path('favorite_quiz/<int:pk>/', views.favorite_quiz, name='favorite_quiz'),
path('critique/delete/<int:critique_id>/<int:quiz_id>', views.delete_critique, name='delete_critique'),
path('quiz/reorder/', views.reorder_questions, name='reorder_questions'),
path("quiz-names-json/", views.quiz_names_json, name="quiz_names_json"),
path(
"autocomplete/quiz",

View File

@@ -94,6 +94,17 @@ def quiz_names_json(request):
return JsonResponse(names, safe=False)
def reorder_questions(request):
if request.method == "POST":
data = json.loads(request.body)
for item in data['order']:
QivipQuestion.objects.filter(id=item['id']).update(order=item['position'])
return JsonResponse({'status': 'ok'})
return JsonResponse({'status': 'error'}, status=400)
def apply_quiz_filters(queryset, form):
if not form.is_valid():

View File

@@ -1,6 +1,8 @@
{% extends 'base.html' %}
{% block content %}
{% load static %}
<script src="{% static 'js/sortablejs/Sortable.min.js' %}"></script>
<div class="container mx-auto px-4">
<div class="flex flex-wrap space-y-2 items-center justify-between mb-6 dark:text-white">
@@ -129,16 +131,16 @@
{% if questions %}
<div class="divide-y divide-gray-200">
<div class="divide-y divide-gray-200" id="questions-list">
{% for question in questions %}
<div class="w-full rounded-xl p-4 hover:bg-gray-50 dark:hover:bg-[#2a2f3a] dark:text-white">
<p>{{ forloop.counter }}.</p>
<div data-question-id="{{ question.id }}" class="question-item w-full rounded-xl p-4 hover:bg-gray-50 dark:hover:bg-[#2a2f3a] dark:text-white">
{% if quiz.user_id == request.user %} <a class="block flex h-full w-full" href="{% url 'library:edit_question' question.id %}" > {% endif %}
<div class="flex justify-between items-start">
<p class="question-counter pr-4 ">{{ forloop.counter }}.</p>
<div class="flex-grow">
<p class="font-medium">{{ question.data.question }}</p>
<div class="mt-1">
@@ -593,4 +595,51 @@
</details>
{% if quiz.user_id == request.user %}
<script>
Sortable.create(document.getElementById('questions-list'), {
animation: 300,
swapThreshold: 0.65,
delay: 100, // wie lange gedrückt halten in ms
delayOnTouchOnly: true, // nur auf Touch-Geräten anwenden
touchStartThreshold: 5,
onStart: () => {
if (navigator.vibrate) {
navigator.vibrate(75);
}
},
onEnd: () => {
const items = Array.from(document.querySelectorAll('#questions-list .question-item'));
// Counter im Browser updaten
items.forEach((el, i) => {
const counterEl = el.querySelector('.question-counter');
if (counterEl) counterEl.textContent = `${i+1}.`;
});
// Reihenfolge sammeln
const order = Array.from(document.querySelectorAll('#questions-list .question-item'))
.map((el, i) => ({id: el.dataset.questionId, position: i+1}));
// an Django senden
fetch("{% url 'library:reorder_questions' %}", {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': '{{ csrf_token }}'
},
body: JSON.stringify({order})
});
}
});
</script>
{% endif %}
{% endblock %}