feat: added material to model and form

This commit is contained in:
2025-05-31 22:47:43 +02:00
parent f5d2550000
commit a9e32034a7
5 changed files with 98 additions and 60 deletions

View File

@@ -17,7 +17,7 @@ class CommentForm(forms.ModelForm):
class TicketForm(forms.ModelForm): class TicketForm(forms.ModelForm):
class Meta: class Meta:
model = Ticket model = Ticket
fields = ["title", "description", "status", "priority", "course", "answer"] fields = ["title", "description", "status", "priority", "course", "answer", "material"]
widgets = { widgets = {
'answer': forms.Textarea(attrs={ 'answer': forms.Textarea(attrs={
'rows': 4, 'rows': 4,

View File

@@ -46,10 +46,21 @@ class Ticket(models.Model):
("high", "Hoch"), ("high", "Hoch"),
] ]
MATERIAL_CHOICES = [
("learning_sprint", "Learning Sprint"),
("ilse", "Intensive Live Session"),
("video", "Video"),
("exercise", "Übungsaufgabe"),
("solution", "Musterlösung"),
("slides", "Präsentationsfolien"),
("other", "Sonstiges"),
]
title = models.CharField(max_length=200) title = models.CharField(max_length=200)
description = models.TextField() description = models.TextField()
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="new") status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="new")
priority = models.CharField(max_length=10, choices=PRIORITY_CHOICES, default="medium") priority = models.CharField(max_length=10, choices=PRIORITY_CHOICES, default="medium")
material = models.CharField(max_length=20, choices=MATERIAL_CHOICES, default="script")
answer = models.TextField( answer = models.TextField(
blank=True, blank=True,

View File

@@ -35,17 +35,31 @@
{% if not view.can_edit or form.description.field.disabled %}disabled{% endif %} {% if not view.can_edit or form.description.field.disabled %}disabled{% endif %}
class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 {% if not view.can_edit or form.description.field.disabled %}bg-gray-100{% endif %}">{{ ticket.description }}</textarea> class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 {% if not view.can_edit or form.description.field.disabled %}bg-gray-100{% endif %}">{{ ticket.description }}</textarea>
</div> </div>
<div> <div class="grid grid-cols-2 gap-4">
<label class="block text-sm font-medium mb-1">Kurs:</label> <div>
<select name="course" <label class="block text-sm font-medium mb-1">Kurs:</label>
id="id_course" <select name="course"
{% if not view.can_edit %}disabled{% endif %} id="id_course"
class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 {% if not view.can_edit %}bg-gray-100{% endif %}"> {% if not view.can_edit %}disabled{% endif %}
{% for course in form.course.field.queryset %} class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 {% if not view.can_edit %}bg-gray-100{% endif %}">
<option value="{{ course.pk }}" {% for course in form.course.field.queryset %}
{% if course.pk == ticket.course.pk %}selected{% endif %}>{{ course }}</option> <option value="{{ course.pk }}"
{% endfor %} {% if course.pk == ticket.course.pk %}selected{% endif %}>{{ course }}</option>
</select> {% endfor %}
</select>
</div>
<div>
<label class="block text-sm font-medium mb-1">Material:</label>
<select name="material"
id="id_material"
{% if not view.can_edit %}disabled{% endif %}
class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 {% if not view.can_edit %}bg-gray-100{% endif %}">
{% for value, display in form.fields.material.choices %}
<option value="{{ value }}"
{% if form.material.value == value %}selected{% endif %}>{{ display }}</option>
{% endfor %}
</select>
</div>
</div> </div>
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
<div> <div>
@@ -74,28 +88,30 @@
<!-- Zugewiesen an (Nur Anzeige) --> <!-- Zugewiesen an (Nur Anzeige) -->
<div> <div>
<label class="block text-sm font-medium mb-1">Zugewiesen an:</label> <label class="block text-sm font-medium mb-1">Zugewiesen an:</label>
<div id="tutor_display" class="w-full p-2 border border-gray-300 bg-gray-100 rounded shadow-sm"> <div id="tutor_display"
<span id="tutor_text">{% if ticket.assigned_to %}{{ ticket.assigned_to.username }}{% else %}Niemand zugewiesen{% endif %}</span> class="w-full p-2 border border-gray-300 bg-gray-100 rounded shadow-sm">
<span id="tutor_text">
{% if ticket.assigned_to %}
{{ ticket.assigned_to.username }}
{% else %}
Niemand zugewiesen
{% endif %}
</span>
</div> </div>
<p class="text-xs text-gray-500 mt-1">Wird automatisch basierend auf dem ausgewählten Kurs zugewiesen</p> <p class="text-xs text-gray-500 mt-1">Wird automatisch basierend auf dem ausgewählten Kurs zugewiesen</p>
</div> </div>
<!-- Antwort/Lösung --> <!-- Antwort/Lösung -->
<div> <div>
<label class="block text-sm font-medium mb-1"> <label class="block text-sm font-medium mb-1">
Antwort/Lösung: Antwort/Lösung:
{% if form.status.value == 'resolved' or ticket.status == 'resolved' %} {% if form.status.value == 'resolved' or ticket.status == 'resolved' %}<span class="text-red-500">*</span>{% endif %}
<span class="text-red-500">*</span>
{% endif %}
</label> </label>
{% if ticket.answer and not view.can_edit %} {% if ticket.answer and not view.can_edit %}
<!-- Nur Anzeige wenn bereits beantwortet und nicht editierbar --> <!-- Nur Anzeige wenn bereits beantwortet und nicht editierbar -->
<div class="w-full p-3 border border-gray-300 bg-gray-50 rounded shadow-sm"> <div class="w-full p-3 border border-gray-300 bg-gray-50 rounded shadow-sm">
<div class="text-gray-700">{{ ticket.answer|linebreaks }}</div> <div class="text-gray-700">{{ ticket.answer|linebreaks }}</div>
{% if ticket.answered_at %} {% if ticket.answered_at %}
<div class="text-xs text-gray-500 mt-2"> <div class="text-xs text-gray-500 mt-2">Beantwortet am: {{ ticket.answered_at|date:"d.m.Y H:i" }}</div>
Beantwortet am: {{ ticket.answered_at|date:"d.m.Y H:i" }}
</div>
{% endif %} {% endif %}
</div> </div>
{% else %} {% else %}
@@ -105,13 +121,9 @@
{% if not view.can_edit or ticket.status != 'resolved' %}disabled{% endif %} {% if not view.can_edit or ticket.status != 'resolved' %}disabled{% endif %}
placeholder="Beschreibe die Lösung des Problems..." placeholder="Beschreibe die Lösung des Problems..."
class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-black {% if not view.can_edit or ticket.status != 'resolved' %}bg-gray-100 cursor-not-allowed{% endif %}">{{ ticket.answer|default:'' }}</textarea> class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 placeholder:text-black {% if not view.can_edit or ticket.status != 'resolved' %}bg-gray-100 cursor-not-allowed{% endif %}">{{ ticket.answer|default:'' }}</textarea>
{% if form.answer.errors %} {% if form.answer.errors %}<div class="text-red-600 text-sm mt-1">{{ form.answer.errors }}</div>{% endif %}
<div class="text-red-600 text-sm mt-1">{{ form.answer.errors }}</div>
{% endif %}
<p class="text-xs text-gray-500 mt-1"> <p class="text-xs text-gray-500 mt-1">
{% if ticket.status != 'resolved' %} {% if ticket.status != 'resolved' %}Eine Antwort ist erforderlich beim Setzen des Status auf "Gelöst"{% endif %}
Eine Antwort ist erforderlich beim Setzen des Status auf "Gelöst"
{% endif %}
</p> </p>
{% endif %} {% endif %}
</div> </div>
@@ -181,7 +193,6 @@
class="text-blue-500 hover:text-blue-600">← Zurück zur Übersicht</a> class="text-blue-500 hover:text-blue-600">← Zurück zur Übersicht</a>
</div> </div>
</div> </div>
<!-- JavaScript für dynamische Tutor-Anzeige --> <!-- JavaScript für dynamische Tutor-Anzeige -->
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
@@ -192,9 +203,7 @@
// Course-Tutor Mapping // Course-Tutor Mapping
const courseTutorMap = { const courseTutorMap = {
{% for course in form.course.field.queryset %} {% for course in form.course.field.queryset %}
{% if course.tutor %} {% if course.tutor %}'{{ course.pk }}': '{{ course.tutor.username }}',{% endif %}
'{{ course.pk }}': '{{ course.tutor.username }}',
{% endif %}
{% endfor %} {% endfor %}
}; };
@@ -251,4 +260,4 @@
}); });
{% endif %} {% endif %}
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -44,19 +44,38 @@
<div class="text-red-600 text-sm mt-1">{{ form.description.errors }}</div> <div class="text-red-600 text-sm mt-1">{{ form.description.errors }}</div>
{% endif %} {% endif %}
</div> </div>
<div> <!-- Kurs und Material -->
<label class="block text-sm font-medium mb-1">Kurs: *</label> <div class="grid grid-cols-2 gap-4">
<select name="course" <div>
id="id_course" <label class="block text-sm font-medium mb-1">Kurs: *</label>
required <select name="course"
class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> id="id_course"
<option value="">Kurs auswählen...</option> required
{% for course in form.course.field.queryset %} class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="{{ course.pk }}" <option value="">Kurs auswählen...</option>
{% if form.course.value == course.pk %}selected{% endif %}>{{ course }}</option> {% for course in form.course.field.queryset %}
{% endfor %} <option value="{{ course.pk }}"
</select> {% if form.course.value == course.pk %}selected{% endif %}>
{% if form.course.errors %}<div class="text-red-600 text-sm mt-1">{{ form.course.errors }}</div>{% endif %} {{ course }}
</option>
{% endfor %}
</select>
{% if form.course.errors %}<div class="text-red-600 text-sm mt-1">{{ form.course.errors }}</div>{% endif %}
</div>
<div>
<label class="block text-sm font-medium mb-1">Material: *</label>
<select name="material"
id="id_material"
required
class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">Material auswählen...</option>
{% for value, display in form.fields.material.choices %}
<option value="{{ value }}"
{% if form.material.value == value %}selected{% endif %}>{{ display }}</option>
{% endfor %}
</select>
{% if form.course.errors %}<div class="text-red-600 text-sm mt-1">{{ form.course.errors }}</div>{% endif %}
</div>
</div> </div>
<!-- Status und Priorität --> <!-- Status und Priorität -->
<div class="grid grid-cols-2 gap-4"> <div class="grid grid-cols-2 gap-4">
@@ -77,13 +96,10 @@
<label class="block text-sm font-medium mb-1">Priorität:</label> <label class="block text-sm font-medium mb-1">Priorität:</label>
<select name="priority" <select name="priority"
class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> class="w-full p-2 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="low" {% if form.priority.value == 'low' %}selected{% endif %}>Niedrig</option> {% for value, display in form.fields.priority.choices %}
<option value="medium" <option value="{{ value }}"
{% if form.priority.value == 'medium' or not form.priority.value %}selected{% endif %}> {% if form.priority.value == value %}selected{% endif %}>{{ display }}</option>
Normal {% endfor %}
</option>
<option value="high"
{% if form.priority.value == 'high' %}selected{% endif %}>Hoch</option>
</select> </select>
{% if form.priority.errors %}<div class="text-red-600 text-sm mt-1">{{ form.priority.errors }}</div>{% endif %} {% if form.priority.errors %}<div class="text-red-600 text-sm mt-1">{{ form.priority.errors }}</div>{% endif %}
</div> </div>
@@ -91,7 +107,8 @@
<!-- Zugewiesen an --> <!-- Zugewiesen an -->
<div> <div>
<label class="block text-sm font-medium mb-1">Zugewiesen an:</label> <label class="block text-sm font-medium mb-1">Zugewiesen an:</label>
<div id="tutor_display" class="w-full p-2 border border-gray-300 bg-gray-100 rounded shadow-sm"> <div id="tutor_display"
class="w-full p-2 border border-gray-300 bg-gray-100 rounded shadow-sm">
<span id="tutor_text">Bitte Kurs auswählen</span> <span id="tutor_text">Bitte Kurs auswählen</span>
</div> </div>
<p class="text-xs text-gray-500 mt-1">Wird automatisch basierend auf dem ausgewählten Kurs zugewiesen</p> <p class="text-xs text-gray-500 mt-1">Wird automatisch basierend auf dem ausgewählten Kurs zugewiesen</p>
@@ -123,7 +140,6 @@
</div> </div>
</div> </div>
</div> </div>
<script> <script>
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const courseSelect = document.getElementById('id_course'); const courseSelect = document.getElementById('id_course');
@@ -133,9 +149,7 @@
// Course-Tutor Mapping speichern // Course-Tutor Mapping speichern
const courseTutorMap = { const courseTutorMap = {
{% for course in form.course.field.queryset %} {% for course in form.course.field.queryset %}
{% if course.tutor %} {% if course.tutor %}'{{ course.pk }}': '{{ course.tutor.username }}',{% endif %}
'{{ course.pk }}': '{{ course.tutor.username }}',
{% endif %}
{% endfor %} {% endfor %}
}; };
@@ -166,4 +180,4 @@
} }
}); });
</script> </script>
{% endblock %} {% endblock %}

View File

@@ -85,7 +85,11 @@ class TicketDetailUpdateView(UpdateView):
is_assigned_tutor = user == self.ticket.assigned_to is_assigned_tutor = user == self.ticket.assigned_to
is_superuser = user.is_superuser is_superuser = user.is_superuser
self.can_edit = is_assigned_tutor or is_superuser # Nur Autor und Admin kann Tickets bearbeiten
if self.ticket.status == "closed" and not is_superuser:
self.can_edit = False
else:
self.can_edit = is_assigned_tutor or is_superuser
# Zusätzliche Flags für Template # Zusätzliche Flags für Template
self.is_creator = is_creator self.is_creator = is_creator
@@ -122,7 +126,7 @@ class TicketDetailUpdateView(UpdateView):
response = super().form_valid(form) # Speichert das Ticket response = super().form_valid(form) # Speichert das Ticket
# History tracking für geänderte Felder # History tracking für geänderte Felder
tracked_fields = ["title", "description", "status", "priority", "course", "answer"] tracked_fields = ["title", "description", "status", "priority", "course", "answer", "material"]
for field in tracked_fields: for field in tracked_fields:
if field in form.changed_data: if field in form.changed_data:
old_value = getattr(original, field) old_value = getattr(original, field)