Resolve "Fragen Reihenfolge verschieben" #289
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -113,6 +113,10 @@ class QivipQuestion(models.Model):
|
|||||||
related_name="questions"
|
related_name="questions"
|
||||||
)
|
)
|
||||||
credits=models.TextField(blank=True, editable=True)
|
credits=models.TextField(blank=True, editable=True)
|
||||||
|
order = models.PositiveIntegerField(default=0)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ['order'] # Fragen nach Reihenfolge sortieren
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.data[:50]
|
return self.data[:50]
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ urlpatterns = [
|
|||||||
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('critique/delete/<int:critique_id>/<int:quiz_id>', views.delete_critique, name='delete_critique'),
|
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("quiz-names-json/", views.quiz_names_json, name="quiz_names_json"),
|
||||||
path(
|
path(
|
||||||
"autocomplete/quiz",
|
"autocomplete/quiz",
|
||||||
|
|||||||
@@ -94,6 +94,17 @@ def quiz_names_json(request):
|
|||||||
return JsonResponse(names, safe=False)
|
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):
|
def apply_quiz_filters(queryset, form):
|
||||||
|
|
||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
{% load static %}
|
||||||
|
<script src="{% static 'js/sortablejs/Sortable.min.js' %}"></script>
|
||||||
|
|
||||||
<div class="container mx-auto px-4">
|
<div class="container mx-auto px-4">
|
||||||
<div class="flex flex-wrap space-y-2 items-center justify-between mb-6 dark:text-white">
|
<div class="flex flex-wrap space-y-2 items-center justify-between mb-6 dark:text-white">
|
||||||
@@ -129,16 +131,16 @@
|
|||||||
|
|
||||||
{% if questions %}
|
{% if questions %}
|
||||||
|
|
||||||
<div class="divide-y divide-gray-200">
|
<div class="divide-y divide-gray-200" id="questions-list">
|
||||||
{% for question in questions %}
|
{% for question in questions %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="w-full rounded-xl p-4 hover:bg-gray-50 dark:hover:bg-[#2a2f3a] dark:text-white">
|
<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">
|
||||||
<p>{{ forloop.counter }}.</p>
|
|
||||||
{% if quiz.user_id == request.user %} <a class="block flex h-full w-full" href="{% url 'library:edit_question' question.id %}" > {% endif %}
|
{% 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">
|
<div class="flex justify-between items-start">
|
||||||
|
<p class="question-counter pr-4 ">{{ forloop.counter }}.</p>
|
||||||
<div class="flex-grow">
|
<div class="flex-grow">
|
||||||
<p class="font-medium">{{ question.data.question }}</p>
|
<p class="font-medium">{{ question.data.question }}</p>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
@@ -593,4 +595,51 @@
|
|||||||
</details>
|
</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 %}
|
{% endblock %}
|
||||||
|
|||||||
Reference in New Issue
Block a user