254 lines
14 KiB
HTML
254 lines
14 KiB
HTML
{% extends "ticketsystem/base.html" %}
|
||
{% block content %}
|
||
<!-- Messages -->
|
||
{% if messages %}
|
||
<div class="max-w-6xl mx-auto px-4 pt-4">
|
||
{% for message in messages %}
|
||
<div class="mb-4 p-3 rounded {% if message.tags == 'error' %}bg-red-100 text-red-700{% elif message.tags == 'success' %}bg-green-100 text-green-700{% else %}bg-yellow-100 text-yellow-700{% endif %}">
|
||
{{ message }}
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
{% endif %}
|
||
<div class="max-w-6xl mx-auto px-4 py-6">
|
||
<!-- Ticket Bearbeitung -->
|
||
<div class="bg-white rounded-lg shadow p-6 mb-6">
|
||
<h1 class="text-3xl font-bold mb-4">🎫 Ticket #{{ ticket.id }} – {{ ticket.title }}</h1>
|
||
<div class="text-sm text-gray-500 mb-6">
|
||
🕒 Erstellt: {{ ticket.created_at|date:"d.m.Y H:i" }} |
|
||
🔄 Aktualisiert: {{ ticket.updated_at|date:"d.m.Y H:i" }}
|
||
</div>
|
||
<form method="post" class="space-y-4">
|
||
{% csrf_token %}
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">Titel:</label>
|
||
<input type="text"
|
||
name="title"
|
||
value="{{ ticket.title }}"
|
||
{% 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 %}">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">Beschreibung:</label>
|
||
<textarea name="description"
|
||
rows="4"
|
||
{% 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 %}">{{ ticket.description }}</textarea>
|
||
</div>
|
||
<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 %}
|
||
<option value="{{ course.pk }}"
|
||
{% if course.pk == ticket.course.pk %}selected{% endif %}>{{ course }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">Status:</label>
|
||
<select name="status"
|
||
{% 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, label in form.status.field.choices %}
|
||
<option value="{{ value }}"
|
||
{% if value == ticket.status %}selected{% endif %}>{{ label }}</option>
|
||
{% endfor %}
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">Priorität:</label>
|
||
<select name="priority"
|
||
{% 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="low" {% if ticket.priority == 'low' %}selected{% endif %}>Niedrig</option>
|
||
<option value="medium"
|
||
{% if ticket.priority == 'medium' %}selected{% endif %}>Normal</option>
|
||
<option value="high" {% if ticket.priority == 'high' %}selected{% endif %}>Hoch</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<!-- Zugewiesen an (Nur Anzeige) -->
|
||
<div>
|
||
<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-300 rounded shadow-sm">
|
||
<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>
|
||
|
||
<!-- Antwort/Lösung -->
|
||
<div>
|
||
<label class="block text-sm font-medium mb-1">
|
||
Antwort/Lösung:
|
||
{% if form.status.value == 'resolved' or ticket.status == 'resolved' %}
|
||
<span class="text-red-500">*</span>
|
||
{% endif %}
|
||
</label>
|
||
{% if ticket.answer and not view.can_edit %}
|
||
<!-- 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="text-gray-700">{{ ticket.answer|linebreaks }}</div>
|
||
{% if ticket.answered_at %}
|
||
<div class="text-xs text-gray-500 mt-2">
|
||
Beantwortet am: {{ ticket.answered_at|date:"d.m.Y H:i" }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
{% else %}
|
||
<!-- Bearbeitbares Feld -->
|
||
<textarea name="answer"
|
||
rows="4"
|
||
{% if not view.can_edit or ticket.status != 'resolved' %}disabled{% endif %}
|
||
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-300 cursor-not-allowed{% endif %}">{{ ticket.answer|default:'' }}</textarea>
|
||
{% if form.answer.errors %}
|
||
<div class="text-red-600 text-sm mt-1">{{ form.answer.errors }}</div>
|
||
{% endif %}
|
||
<p class="text-xs text-gray-500 mt-1">
|
||
{% if ticket.status != 'resolved' %}
|
||
Eine Antwort ist erforderlich beim Setzen des Status auf "Gelöst"
|
||
{% endif %}
|
||
</p>
|
||
{% endif %}
|
||
</div>
|
||
{% if view.can_edit %}
|
||
<button type="submit"
|
||
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded">💾 Speichern</button>
|
||
{% else %}
|
||
<p class="text-gray-500 italic">Du kannst dieses Ticket nicht bearbeiten.</p>
|
||
{% endif %}
|
||
</form>
|
||
</div>
|
||
<!-- Kommentare -->
|
||
<div class="bg-white rounded-lg shadow p-6 mb-6">
|
||
<h2 class="text-xl font-bold mb-4">💬 Kommentare ({{ ticket.comments.count }})</h2>
|
||
{% if ticket.comments.exists %}
|
||
{% for comment in ticket.comments.all %}
|
||
<div class="border-l-4 border-blue-400 bg-blue-50 p-4 mb-4">
|
||
<div class="text-sm text-gray-600 mb-2">
|
||
<strong>{{ comment.author.username }}</strong> - {{ comment.created_at|date:"d.m.Y H:i" }}
|
||
</div>
|
||
<div>{{ comment.text|linebreaks }}</div>
|
||
</div>
|
||
{% endfor %}
|
||
{% else %}
|
||
<p class="text-gray-500 italic">Keine Kommentare vorhanden.</p>
|
||
{% endif %}
|
||
{% if user.is_authenticated %}
|
||
<div class="bg-gray-50 p-4 rounded mt-4">
|
||
<h3 class="font-medium mb-2">📝 Neuer Kommentar</h3>
|
||
<form method="post">
|
||
{% csrf_token %}
|
||
<textarea name="text"
|
||
rows="3"
|
||
placeholder="Kommentar schreiben..."
|
||
class="w-full p-2 border border-gray-300 rounded shadow-sm mb-2 focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-green-500"></textarea>
|
||
<button type="submit"
|
||
name="comment_submit"
|
||
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded">Absenden</button>
|
||
</form>
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
<!-- Historie -->
|
||
<div class="bg-white rounded-lg shadow p-6">
|
||
<h2 class="text-xl font-bold mb-4">🕓 Änderungen ({{ ticket.history.count }})</h2>
|
||
{% if ticket.history.exists %}
|
||
{% for entry in ticket.history.all %}
|
||
<div class="border-l-4 border-gray-300 bg-gray-50 p-4 mb-4">
|
||
<div class="text-sm">
|
||
<strong>{{ entry.changed_by.username }}</strong> hat
|
||
<code class="bg-gray-200 px-1 rounded">{{ entry.field }}</code> geändert
|
||
</div>
|
||
<div class="text-sm mt-1">
|
||
<span class="text-red-600">{{ entry.old_value }}</span> →
|
||
<span class="text-green-600">{{ entry.new_value }}</span>
|
||
</div>
|
||
<div class="text-xs text-gray-500 mt-1">{{ entry.changed_at|date:"d.m.Y H:i" }}</div>
|
||
</div>
|
||
{% endfor %}
|
||
{% else %}
|
||
<p class="text-gray-500 italic">Keine Änderungen bisher.</p>
|
||
{% endif %}
|
||
</div>
|
||
<!-- Navigation -->
|
||
<div class="mt-6">
|
||
<a href="{% url 'ticket-list' %}"
|
||
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 %}
|
||
});
|
||
|
||
// Answer Feld nur bei Status "resolved" aktivieren
|
||
{% if view.can_edit %}
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const statusSelect = document.querySelector('select[name="status"]');
|
||
const answerField = document.querySelector('textarea[name="answer"]');
|
||
const answerLabel = answerField.previousElementSibling;
|
||
|
||
function toggleAnswerField() {
|
||
if (statusSelect.value === 'resolved') {
|
||
answerField.disabled = false;
|
||
answerField.classList.remove('bg-gray-100', 'cursor-not-allowed');
|
||
answerField.classList.add('bg-white');
|
||
answerField.required = true;
|
||
// Pflichtfeld-Stern anzeigen
|
||
if (!answerLabel.querySelector('.text-red-500')) {
|
||
answerLabel.innerHTML = answerLabel.textContent + ' <span class="text-red-500">*</span>';
|
||
}
|
||
} else {
|
||
answerField.disabled = true;
|
||
answerField.classList.add('bg-gray-100', 'cursor-not-allowed');
|
||
answerField.classList.remove('bg-white');
|
||
answerField.required = false;
|
||
// Pflichtfeld-Stern entfernen
|
||
answerLabel.innerHTML = answerLabel.textContent.replace(' *', '');
|
||
}
|
||
}
|
||
|
||
// Initial prüfen
|
||
toggleAnswerField();
|
||
|
||
// Bei Status-Änderung prüfen
|
||
statusSelect.addEventListener('change', toggleAnswerField);
|
||
});
|
||
{% endif %}
|
||
</script>
|
||
{% endblock %} |