feat: auto select tutor as assignee
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
from django import forms
|
from django import forms
|
||||||
from .models import Comment
|
from .models import Comment, Ticket
|
||||||
|
|
||||||
|
|
||||||
class CommentForm(forms.ModelForm):
|
class CommentForm(forms.ModelForm):
|
||||||
@@ -11,3 +11,20 @@ class CommentForm(forms.ModelForm):
|
|||||||
attrs={"rows": 3, "placeholder": "Kommentar schreiben..."}
|
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>
|
<div>
|
||||||
<label class="block text-sm font-medium mb-1">Kurs:</label>
|
<label class="block text-sm font-medium mb-1">Kurs:</label>
|
||||||
<select name="course"
|
<select name="course"
|
||||||
|
id="id_course"
|
||||||
{% if not view.can_edit %}disabled{% endif %}
|
{% 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 %}">
|
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 %}
|
{% for course in form.course.field.queryset %}
|
||||||
@@ -72,15 +73,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<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>
|
||||||
<select name="assigned_to"
|
<div id="tutor_display" class="w-full p-2 border border-gray-300 bg-gray-100 rounded shadow-sm text-gray-300">
|
||||||
{% if not view.can_edit %}disabled{% endif %}
|
<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 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 %}">
|
</div>
|
||||||
<option value="">Nicht zugewiesen</option>
|
<p class="text-xs text-gray-500 mt-1">Wird automatisch basierend auf dem ausgewählten Kurs zugewiesen</p>
|
||||||
{% 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>
|
</div>
|
||||||
{% if view.can_edit %}
|
{% if view.can_edit %}
|
||||||
<button type="submit"
|
<button type="submit"
|
||||||
@@ -148,4 +144,39 @@
|
|||||||
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 -->
|
||||||
|
<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 %}
|
{% endblock %}
|
||||||
@@ -47,6 +47,7 @@
|
|||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium mb-1">Kurs: *</label>
|
<label class="block text-sm font-medium mb-1">Kurs: *</label>
|
||||||
<select name="course"
|
<select name="course"
|
||||||
|
id="id_course"
|
||||||
required
|
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">
|
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>
|
<option value="">Kurs auswählen...</option>
|
||||||
@@ -90,17 +91,10 @@
|
|||||||
<!-- 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>
|
||||||
<select name="assigned_to"
|
<div id="tutor_display" class="w-full p-2 border border-gray-300 bg-gray-300 rounded shadow-sm">
|
||||||
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">
|
<span id="tutor_text">Bitte Kurs auswählen</span>
|
||||||
<option value="">Nicht zugewiesen</option>
|
</div>
|
||||||
{% for user in form.assigned_to.field.queryset %}
|
<p class="text-xs text-gray-500 mt-1">Wird automatisch basierend auf dem ausgewählten Kurs zugewiesen</p>
|
||||||
<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>
|
</div>
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
<div class="flex gap-4 pt-4">
|
<div class="flex gap-4 pt-4">
|
||||||
@@ -122,8 +116,54 @@
|
|||||||
<li>
|
<li>
|
||||||
• Beschreibe das Problem so <strong>detailliert wie möglich</strong>
|
• Beschreibe das Problem so <strong>detailliert wie möglich</strong>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
• Das Ticket wird automatisch dem <strong>Kurs-Tutor zugewiesen</strong>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</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 %}
|
{% endblock %}
|
||||||
@@ -3,7 +3,7 @@ from django.views.generic.edit import CreateView, UpdateView
|
|||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.views.generic.detail import DetailView
|
from django.views.generic.detail import DetailView
|
||||||
from django.views.generic.edit import FormMixin
|
from django.views.generic.edit import FormMixin
|
||||||
from .forms import CommentForm
|
from .forms import CommentForm, TicketForm
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
@@ -60,9 +60,10 @@ class TicketListView(ListView):
|
|||||||
context["status_choices"] = Ticket.STATUS_CHOICES
|
context["status_choices"] = Ticket.STATUS_CHOICES
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class TicketDetailUpdateView(UpdateView):
|
class TicketDetailUpdateView(UpdateView):
|
||||||
model = Ticket
|
model = Ticket
|
||||||
fields = ["title", "description", "status", "priority", "assigned_to", "course"]
|
form_class = TicketForm # Verwende das custom Form anstatt fields
|
||||||
template_name = "ticketsystem/detail.html"
|
template_name = "ticketsystem/detail.html"
|
||||||
comment_form_class = CommentForm
|
comment_form_class = CommentForm
|
||||||
|
|
||||||
@@ -93,25 +94,29 @@ class TicketDetailUpdateView(UpdateView):
|
|||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
ticket = form.instance
|
ticket = form.instance
|
||||||
original = Ticket.objects.get(pk=ticket.pk)
|
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
|
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", "assigned_to", "course"]
|
tracked_fields = ["title", "description", "status", "priority", "course"]
|
||||||
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)
|
||||||
new_value = form.cleaned_data.get(field)
|
new_value = form.cleaned_data.get(field)
|
||||||
|
|
||||||
# Für ForeignKey Felder den Display-Namen verwenden
|
# Für ForeignKey Felder den Display-Namen verwenden
|
||||||
if field == "assigned_to":
|
if field == "status":
|
||||||
old_value = old_value.username if old_value else "Niemand"
|
|
||||||
new_value = new_value.username if new_value else "Niemand"
|
|
||||||
elif field == "status":
|
|
||||||
old_value = original.get_status_display()
|
old_value = original.get_status_display()
|
||||||
new_value = ticket.get_status_display()
|
new_value = ticket.get_status_display()
|
||||||
elif field == "priority":
|
elif field == "priority":
|
||||||
old_value = original.get_priority_display()
|
old_value = original.get_priority_display()
|
||||||
new_value = ticket.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(
|
TicketHistory.objects.create(
|
||||||
ticket=ticket,
|
ticket=ticket,
|
||||||
@@ -121,12 +126,28 @@ class TicketDetailUpdateView(UpdateView):
|
|||||||
new_value=str(new_value),
|
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(
|
messages.success(
|
||||||
self.request,
|
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
|
return response
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
@@ -168,7 +189,7 @@ class AssignedTicketListView(LoginRequiredMixin, ListView):
|
|||||||
|
|
||||||
class TicketCreateView(CreateView):
|
class TicketCreateView(CreateView):
|
||||||
model = Ticket
|
model = Ticket
|
||||||
fields = ["title", "description", "status", "priority", "assigned_to", "course"]
|
form_class = TicketForm
|
||||||
template_name = "ticketsystem/ticket_form.html"
|
template_name = "ticketsystem/ticket_form.html"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
|||||||
Reference in New Issue
Block a user