feat: auto select tutor as assignee

This commit is contained in:
2025-05-29 22:52:35 +02:00
parent 5688d96e4f
commit c77c966e0e
4 changed files with 143 additions and 34 deletions

View File

@@ -1,5 +1,5 @@
from django import forms
from .models import Comment
from .models import Comment, Ticket
class CommentForm(forms.ModelForm):
@@ -11,3 +11,20 @@ class CommentForm(forms.ModelForm):
attrs={"rows": 3, "placeholder": "Kommentar schreiben..."}
),
}
class TicketForm(forms.ModelForm):
class Meta:
model = Ticket
fields = ["title", "description", "status", "priority", "course"]
def save(self, commit=True):
ticket = super().save(commit=False)
# Automatische Tutor-Zuweisung basierend auf dem ausgewählten Kurs
if ticket.course and ticket.course.tutor:
ticket.assigned_to = ticket.course.tutor
if commit:
ticket.save()
return ticket

View File

@@ -38,6 +38,7 @@
<div>
<label class="block text-sm font-medium mb-1">Kurs:</label>
<select name="course"
id="id_course"
{% 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 course in form.course.field.queryset %}
@@ -72,15 +73,10 @@
</div>
<div>
<label class="block text-sm font-medium mb-1">Zugewiesen an:</label>
<select name="assigned_to"
{% 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 %}">
<option value="">Nicht zugewiesen</option>
{% for user in form.assigned_to.field.queryset %}
<option value="{{ user.pk }}"
{% if ticket.assigned_to == user %}selected{% endif %}>{{ user.username }}</option>
{% endfor %}
</select>
<div id="tutor_display" class="w-full p-2 border border-gray-300 bg-gray-100 rounded shadow-sm text-gray-300">
<span id="tutor_text">{% if ticket.assigned_to %}{{ ticket.assigned_to.username }}{% else %}Niemand zugewiesen{% endif %}</span>
</div>
<p class="text-xs text-gray-500 mt-1">Wird automatisch basierend auf dem ausgewählten Kurs zugewiesen</p>
</div>
{% if view.can_edit %}
<button type="submit"
@@ -148,4 +144,39 @@
class="text-blue-500 hover:text-blue-600">← Zurück zur Übersicht</a>
</div>
</div>
{% endblock %}
<!-- JavaScript für dynamische Tutor-Anzeige -->
<script>
document.addEventListener('DOMContentLoaded', function() {
const courseSelect = document.getElementById('id_course');
const tutorText = document.getElementById('tutor_text');
const tutorDisplay = document.getElementById('tutor_display');
// Course-Tutor Mapping
const courseTutorMap = {
{% for course in form.course.field.queryset %}
{% if course.tutor %}
'{{ course.pk }}': '{{ course.tutor.username }}',
{% endif %}
{% endfor %}
};
// Nur aktivieren wenn User bearbeiten kann
{% if view.can_edit %}
courseSelect.addEventListener('change', function() {
const selectedCourseId = this.value;
if (selectedCourseId && courseTutorMap[selectedCourseId]) {
tutorText.textContent = courseTutorMap[selectedCourseId];
tutorDisplay.classList.remove('bg-gray-100');
tutorDisplay.classList.add('bg-blue-50');
} else if (selectedCourseId) {
tutorText.textContent = 'Kein Tutor zugewiesen';
tutorDisplay.classList.remove('bg-blue-50');
tutorDisplay.classList.add('bg-gray-100');
}
});
{% endif %}
});
</script>
{% endblock %}

View File

@@ -47,6 +47,7 @@
<div>
<label class="block text-sm font-medium mb-1">Kurs: *</label>
<select name="course"
id="id_course"
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="">Kurs auswählen...</option>
@@ -90,17 +91,10 @@
<!-- Zugewiesen an -->
<div>
<label class="block text-sm font-medium mb-1">Zugewiesen an:</label>
<select name="assigned_to"
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="">Nicht zugewiesen</option>
{% for user in form.assigned_to.field.queryset %}
<option value="{{ user.pk }}"
{% if form.assigned_to.value == user.pk %}selected{% endif %}>
{{ user.username }}
</option>
{% endfor %}
</select>
{% if form.assigned_to.errors %}<div class="text-red-600 text-sm mt-1">{{ form.assigned_to.errors }}</div>{% endif %}
<div id="tutor_display" class="w-full p-2 border border-gray-300 bg-gray-300 rounded shadow-sm">
<span id="tutor_text">Bitte Kurs auswählen</span>
</div>
<p class="text-xs text-gray-500 mt-1">Wird automatisch basierend auf dem ausgewählten Kurs zugewiesen</p>
</div>
<!-- Buttons -->
<div class="flex gap-4 pt-4">
@@ -122,8 +116,54 @@
<li>
• Beschreibe das Problem so <strong>detailliert wie möglich</strong>
</li>
<li>
• Das Ticket wird automatisch dem <strong>Kurs-Tutor zugewiesen</strong>
</li>
</ul>
</div>
</div>
</div>
{% endblock %}
<script>
document.addEventListener('DOMContentLoaded', function() {
const courseSelect = document.getElementById('id_course');
const tutorText = document.getElementById('tutor_text');
const tutorDisplay = document.getElementById('tutor_display');
// Course-Tutor Mapping speichern
const courseTutorMap = {
{% for course in form.course.field.queryset %}
{% if course.tutor %}
'{{ course.pk }}': '{{ course.tutor.username }}',
{% endif %}
{% endfor %}
};
courseSelect.addEventListener('change', function() {
const selectedCourseId = this.value;
if (selectedCourseId && courseTutorMap[selectedCourseId]) {
// Tutor gefunden
tutorText.textContent = courseTutorMap[selectedCourseId];
tutorDisplay.classList.remove('bg-gray-100');
tutorDisplay.classList.add('bg-blue-50');
} else if (selectedCourseId) {
// Kurs hat keinen Tutor
tutorText.textContent = 'Kein Tutor zugewiesen';
tutorDisplay.classList.remove('bg-blue-50');
tutorDisplay.classList.add('bg-gray-100');
} else {
// Kein Kurs ausgewählt
tutorText.textContent = 'Bitte erst Kurs auswählen';
tutorDisplay.classList.remove('bg-blue-50');
tutorDisplay.classList.add('bg-gray-100');
}
});
// Initial load: Check if course is already selected
if (courseSelect.value) {
courseSelect.dispatchEvent(new Event('change'));
}
});
</script>
{% endblock %}

View File

@@ -3,7 +3,7 @@ from django.views.generic.edit import CreateView, UpdateView
from django.urls import reverse_lazy
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormMixin
from .forms import CommentForm
from .forms import CommentForm, TicketForm
from django.urls import reverse
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib import messages
@@ -60,9 +60,10 @@ class TicketListView(ListView):
context["status_choices"] = Ticket.STATUS_CHOICES
return context
class TicketDetailUpdateView(UpdateView):
model = Ticket
fields = ["title", "description", "status", "priority", "assigned_to", "course"]
form_class = TicketForm # Verwende das custom Form anstatt fields
template_name = "ticketsystem/detail.html"
comment_form_class = CommentForm
@@ -93,25 +94,29 @@ class TicketDetailUpdateView(UpdateView):
def form_valid(self, form):
ticket = form.instance
original = Ticket.objects.get(pk=ticket.pk)
# Alten assigned_to Wert merken für History
old_assigned_to = original.assigned_to
response = super().form_valid(form) # Speichert das Ticket
# History tracking für geänderte Felder
tracked_fields = ["title", "description", "status", "priority", "assigned_to", "course"]
tracked_fields = ["title", "description", "status", "priority", "course"]
for field in tracked_fields:
if field in form.changed_data:
old_value = getattr(original, field)
new_value = form.cleaned_data.get(field)
# Für ForeignKey Felder den Display-Namen verwenden
if field == "assigned_to":
old_value = old_value.username if old_value else "Niemand"
new_value = new_value.username if new_value else "Niemand"
elif field == "status":
if field == "status":
old_value = original.get_status_display()
new_value = ticket.get_status_display()
elif field == "priority":
old_value = original.get_priority_display()
new_value = ticket.get_priority_display()
elif field == "course":
old_value = str(old_value)
new_value = str(new_value)
TicketHistory.objects.create(
ticket=ticket,
@@ -121,12 +126,28 @@ class TicketDetailUpdateView(UpdateView):
new_value=str(new_value),
)
if form.changed_data:
if "course" in form.changed_data and old_assigned_to != ticket.assigned_to:
old_name = old_assigned_to.username if old_assigned_to else "Niemand"
new_name = ticket.assigned_to.username if ticket.assigned_to else "Niemand"
TicketHistory.objects.create(
ticket=ticket,
changed_by=self.request.user,
field="assigned_to",
old_value=old_name,
new_value=new_name,
)
# Erweitere die changed_data Liste für die Nachricht
changed_fields = list(form.changed_data)
if "assigned_to" not in changed_fields:
changed_fields.append("assigned_to (automatisch)")
else:
changed_fields = form.changed_data
if changed_fields:
messages.success(
self.request,
f"Ticket erfolgreich aktualisiert. Geänderte Felder: {', '.join(form.changed_data)}",
f"Ticket erfolgreich aktualisiert. Geänderte Felder: {', '.join(changed_fields)}",
)
return response
def post(self, request, *args, **kwargs):
@@ -168,7 +189,7 @@ class AssignedTicketListView(LoginRequiredMixin, ListView):
class TicketCreateView(CreateView):
model = Ticket
fields = ["title", "description", "status", "priority", "assigned_to", "course"]
form_class = TicketForm
template_name = "ticketsystem/ticket_form.html"
def form_valid(self, form):