feat: auto select tutor as assignee
This commit is contained in:
@@ -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
|
||||
@@ -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>
|
||||
|
||||
<!-- 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 %}
|
||||
@@ -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>
|
||||
|
||||
<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 %}
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user