Compare commits

...

9 Commits

Author SHA1 Message Date
f8050cc32d feat: studybug as new name 2025-06-30 19:40:13 +02:00
e1859f8f29 feat: studybug as new name 2025-06-30 18:48:26 +02:00
f92d34b0a0 fix: open ticket counter 2025-06-29 16:36:57 +02:00
65360a95e5 fix: changed text 2025-06-29 16:26:48 +02:00
71b834393d feat: new welcome message in home.html 2025-06-29 16:22:19 +02:00
05f4102b81 feat: new logo on login page 2025-06-29 15:36:47 +02:00
2fb8435600 feat: added some responsiveness 2025-06-22 14:34:21 +02:00
db9fab89e5 feat: AssignedTicketListView removed, not needed anymore 2025-06-13 13:30:26 +02:00
d282f39ac1 fix: wrong link 2025-06-11 22:45:51 +02:00
12 changed files with 309 additions and 311 deletions

View File

@@ -2,6 +2,10 @@
Die Webanwendung unterstützt das Korrekturmanagement von Materialien aus dem Fernstudium
## Live-Demo
Eine Testumgebung der aktuellen Version wird unter folgender URL bereitgestellt:
### [studybug.de](https://studybug.de)
## Installation
```bash

View File

@@ -126,5 +126,5 @@ STATICFILES_DIRS = [BASE_DIR / "static"]
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
LOGIN_REDIRECT_URL = "/ticketsystem"
LOGIN_REDIRECT_URL = "/"
LOGOUT_REDIRECT_URL = "/accounts/login/"

View File

@@ -19,7 +19,7 @@ from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path("ticketsystem/", include("ticketsystem.urls")),
path("", include("ticketsystem.urls")),
path("admin/", admin.site.urls),
path("accounts/", include("django.contrib.auth.urls")),
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@@ -3,13 +3,18 @@
<!DOCTYPE html>
<head>
<link href="{% static 'css/tailwind.css' %}" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<div class="min-h-screen flex items-center justify-center bg-gray-50 py-12 px-4">
<div class="max-w-md w-full space-y-8">
<div class="text-center">
<div class="text-6xl mb-4">🔐</div>
<h2 class="text-3xl font-bold text-gray-900 mb-2">Anmelden</h2>
<p class="text-gray-600 mb-8">Melde dich in deinem Ticketsystem an</p>
<div class="mb-4 flex justify-center">
<img src="{% static 'IU-logo.png' %}"
alt="Logo"
class="w-[60%] h-auto" />
</div>
<h2 class="text-3xl font-bold text-gray-900 mb-2">Studybug</h2>
<p class="text-gray-600 mb-8">Melde dich im Korrekturmanagementsystem der IU an</p>
</div>
<div class="bg-white rounded-lg shadow-md p-8">
<form method="post" class="space-y-6">
@@ -44,7 +49,7 @@
<div>
<button type="submit"
class="w-full bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-4 rounded-lg transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500">
🚀 Anmelden
🪲 Anmelden
</button>
</div>
</form>

View File

@@ -1,69 +0,0 @@
{% extends "ticketsystem/base.html" %}
{% block content %}
<style>
.ticket-list-container {
max-width: 700px;
margin: 2rem auto;
padding: 2rem;
border: 1px solid #ddd;
border-radius: 8px;
background-color: #fafafa;
font-family: sans-serif;
}
.ticket-list-container h1 {
margin-bottom: 1.5rem;
font-size: 1.8rem;
color: #333;
}
.ticket-item {
padding: 1rem;
border-bottom: 1px solid #ccc;
}
.ticket-item:last-child {
border-bottom: none;
}
.ticket-item a {
text-decoration: none;
color: #007bff;
font-weight: bold;
}
.ticket-item a:hover {
text-decoration: underline;
}
.ticket-meta {
font-size: 0.9rem;
color: #666;
margin-top: 0.3rem;
}
</style>
<div class="ticket-list-container">
<h1>🧾 Meine zugewiesenen Tickets</h1>
<p style="color: #777; font-size: 0.9rem; margin-top: -0.5rem; margin-bottom: 1.5rem;">
Hinweis: Bereits geschlossene Tickets werden hier nicht aufgelistet.
</p>
{% for ticket in tickets %}
<div class="ticket-item">
<a href="{% url 'detail' ticket.pk %}">
#{{ ticket.id }} {{ ticket.title }}
</a>
<div class="ticket-meta">
Status: {{ ticket.get_status_display }} |
Priorität: {{ ticket.get_priority_display }} |
Angelegt am {{ ticket.created_at|date:"d.m.Y H:i" }}
</div>
</div>
{% empty %}
<p>Es sind derzeit keine Tickets vorhanden.</p>
{% endfor %}
</div>
{% endblock %}

View File

@@ -3,35 +3,69 @@
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
{% block title %}TicketSystem{% endblock %}
{% block title %}Studybug🪲{% endblock %}
</title>
<link href="{% static 'css/tailwind.css' %}" rel="stylesheet">
</head>
<body class="bg-gray-100">
<nav class="bg-gray-700 text-white py-4">
<div class="max-w-4xl mx-auto flex justify-between items-center px-4">
<div class="flex space-x-4">
<a href="{% url 'home' %}" class="text-white hover:text-gray-300">🏠 Start</a>
<a href="{% url 'ticket-list' %}" class="text-white hover:text-gray-300">📋 Tickets</a>
<a href="{% url 'assigned-tickets' %}"
class="text-white hover:text-gray-300">🧾 Meine Tickets</a>
<a href="{% url 'faq-list' %}" class="text-white hover:text-gray-300">❓ FAQ</a>
<div class="max-w-6xl mx-auto px-4">
<!-- Mobile Header -->
<div class="flex justify-between items-center md:hidden">
<h1 class="text-lg font-semibold">Studybug🪲</h1>
<button id="mobile-menu-btn" class="text-2xl"></button>
</div>
<div class="flex items-center space-x-4">
<!-- Desktop Menu -->
<div class="hidden md:flex md:justify-between md:items-center">
<div class="flex space-x-6">
<a href="{% url 'home' %}" class="text-white hover:text-gray-300">🏠 Start</a>
<a href="{% url 'ticket-list' %}" class="text-white hover:text-gray-300">📋 Tickets</a>
<a href="{% url 'faq-list' %}" class="text-white hover:text-gray-300">❓ FAQ</a>
</div>
{% if user.is_authenticated %}
<span class="text-white">👤 {{ user.username }}</span>
<form method="post" action="{% url 'logout' %}" class="inline">
{% csrf_token %}
<button type="submit" class="text-white hover:text-gray-300">🚪 Logout</button>
</form>
<div class="flex items-center space-x-4">
<span>👤 {{ user.username }}</span>
<form method="post" action="{% url 'logout' %}" class="inline">
{% csrf_token %}
<button type="submit" class="text-white hover:text-gray-300">🚪 Logout</button>
</form>
</div>
{% endif %}
</div>
<!-- Mobile Menu -->
<div id="mobile-menu" class="hidden mt-4">
<a href="{% url 'home' %}" class="block text-white py-2">🏠 Start</a>
<a href="{% url 'ticket-list' %}" class="block text-white py-2">📋 Tickets</a>
<a href="{% url 'faq-list' %}" class="block text-white py-2">❓ FAQ</a>
{% if user.is_authenticated %}
<div class="border-t border-gray-600 pt-3 mt-3">
<span class="block text-white py-1">👤 {{ user.username }}</span>
<form method="post" action="{% url 'logout' %}">
{% csrf_token %}
<button type="submit" class="text-white py-2">🚪 Logout</button>
</form>
</div>
{% endif %}
</div>
</div>
</nav>
<!-- Global Container -->
<div class="max-w-5xl mx-auto mt-8 px-4">
<div class="max-w-6xl mx-auto mt-8 px-4">
{% block content %}{% endblock %}
</div>
<!-- Mobile Menu Script -->
<script>
const menuBtn = document.getElementById('mobile-menu-btn');
const menu = document.getElementById('mobile-menu');
if (menuBtn && menu) {
menuBtn.onclick = () => menu.classList.toggle('hidden');
}
</script>
</body>
</html>

View File

@@ -1,7 +1,6 @@
{% extends "ticketsystem/base.html" %}
{% load static %}
{% block content %}
<div class="max-w-6xl mx-auto px-4 py-6">
<!-- Header -->
<div class="bg-white rounded-lg shadow p-6 mb-6">
<div class="flex justify-between items-center">
@@ -47,7 +46,6 @@
<p class="text-gray-500">Noch keine FAQs vorhanden.</p>
</div>
{% endfor %}
</div>
<!-- FAQ JavaScript -->
<script src="{% static 'js/faq.js' %}"></script>

View File

@@ -3,10 +3,10 @@
<div class="max-w-6xl mx-auto px-4 py-12">
<!-- Header -->
<div class="text-center mb-12">
<div class="text-6xl mb-4">🎫</div>
<h1 class="text-4xl font-bold text-gray-900 mb-4">Willkommen im Ticketsystem</h1>
<p class="text-lg text-gray-600 mb-2">Verwalte deine Aufgaben und Tickets effizient</p>
<p class="text-gray-500">Was möchten Sie tun?</p>
<div class="text-6xl mb-4">🪲</div>
<h1 class="text-4xl font-bold text-gray-900 mb-4">Willkommen bei Studybug!</h1>
<p class="text-lg text-gray-600 mb-2">- Das Korrekturmanagementsystem der IU Internationale Hochschule - </p>
<p class="text-gray-500">Erstelle ein Ticket, wenn du Probleme oder Fehler erkannt hast</p>
</div>
<!-- Quick Actions -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-12">
@@ -25,7 +25,7 @@
<p class="text-sm text-green-600">Ein neues Ticket erstellen</p>
</a>
<!-- Offene Tickets -->
<a href="{% url 'ticket-list' %}?status=open"
<a href="{% url 'ticket-list' %}?status=new"
class="bg-blue-50 rounded-lg shadow-md p-6 text-center hover:shadow-lg transition-shadow duration-200 border border-blue-200">
<div class="text-3xl mb-3">📂</div>
<h3 class="text-lg font-bold text-blue-800 mb-2">Offene Tickets</h3>
@@ -40,7 +40,7 @@
<div class="text-sm text-gray-600">Tickets insgesamt</div>
</div>
<div class="bg-white rounded-lg p-4 shadow-sm">
<div class="text-2xl font-bold text-red-600 mb-1">{{ open_tickets|default:"0" }}</div>
<div class="text-2xl font-bold text-red-600 mb-1">{{ new_tickets|default:"0" }}</div>
<div class="text-sm text-gray-600">Offene Tickets</div>
</div>
<div class="bg-white rounded-lg p-4 shadow-sm">

View File

@@ -2,167 +2,168 @@
{% block content %}
<!-- Messages -->
{% if messages %}
<div class="max-w-6xl mx-auto px-4 pt-4">
<div class="mb-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 %}">
<div class="mb-2 p-3 rounded text-sm {% 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">
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-6">
<!-- Header -->
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-6">
<div>
<h1 class="text-2xl sm:text-3xl font-bold text-gray-900 mb-2">🎫 Ticket-Übersicht</h1>
<p class="text-gray-600">Verwalte und verfolge Tickets</p>
</div>
<div class="mt-4 sm:mt-0">
<a href="{% url 'create' %}"
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded font-medium block text-center sm:inline-block">
Neues Ticket erstellen
</a>
</div>
</div>
<!-- Filter und Suche -->
<div class="bg-white rounded-lg shadow p-4 mb-6">
<div class="grid grid-cols-1 lg:grid-cols-5 gap-4">
<!-- Status Filter -->
<div>
<h1 class="text-3xl font-bold text-gray-900 mb-2">🎫 Ticket-Übersicht</h1>
<p class="text-gray-600">Verwalte und verfolge alle deine Tickets</p>
</div>
<div class="mt-4 sm:mt-0">
<a href="{% url 'create' %}"
class="bg-green-500 hover:bg-green-600 text-white px-4 py-2 rounded font-medium">
Neues Ticket erstellen
</a>
</div>
</div>
<!-- Filter und Suche -->
<div class="bg-white rounded-lg shadow p-4 mb-6">
<div class="grid grid-cols-1 lg:grid-cols-5 gap-4">
<!-- Status Filter -->
<div>
<form method="get">
<label class="block text-sm font-medium mb-1">Status:</label>
<select name="status"
onchange="this.form.submit()"
class="w-full h-10 px-3 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">Alle Status</option>
{% for value, label in status_choices %}
<option value="{{ value }}"
{% if selected_status == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
<!-- Andere Filter beibehalten -->
{% if request.GET.assigned_to %}
<input type="hidden"
name="assigned_to"
value="{{ request.GET.assigned_to }}">
{% endif %}
{% if request.GET.course %}<input type="hidden" name="course" value="{{ request.GET.course }}">{% endif %}
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
</form>
</div>
<!-- Meine Tickets Filter -->
<div>
<label class="block text-sm font-medium mb-1">Zuweisung:</label>
{% if request.GET.assigned_to == user.id|stringformat:'s' %}
<!-- Wenn aktiv: Button zum Deaktivieren -->
<a href="?{% if selected_status %}status={{ selected_status }}{% endif %}{% if selected_course %}&course={{ selected_course }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}"
class="block w-full h-10 px-3 border border-purple-300 rounded shadow-sm text-center bg-purple-200 hover:bg-purple-300 transition-colors flex items-center justify-center">
👤 Meine Tickets ✓
</a>
{% else %}
<!-- Wenn nicht aktiv: Button zum Aktivieren -->
<a href="?assigned_to={{ user.id }}{% if selected_status %}&status={{ selected_status }}{% endif %}{% if selected_course %}&course={{ selected_course }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}"
class="block w-full h-10 px-3 border border-purple-300 rounded shadow-sm text-center bg-purple-50 hover:bg-purple-100 transition-colors flex items-center justify-center">
👤 Meine Tickets
</a>
<form method="get">
<label class="block text-sm font-medium mb-1">Status:</label>
<select name="status"
onchange="this.form.submit()"
class="w-full h-10 px-3 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">Alle Status</option>
{% for value, label in status_choices %}
<option value="{{ value }}"
{% if selected_status == value %}selected{% endif %}>{{ label }}</option>
{% endfor %}
</select>
<!-- Andere Filter beibehalten -->
{% if request.GET.assigned_to %}
<input type="hidden"
name="assigned_to"
value="{{ request.GET.assigned_to }}">
{% endif %}
</div>
<!-- Kurs Filter -->
<div>
<form method="get">
<label class="block text-sm font-medium mb-1">Kurs:</label>
<select name="course"
onchange="this.form.submit()"
class="w-full h-10 px-3 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">Alle Kurse</option>
{% for course in courses %}
<option value="{{ course.id }}"
{% if selected_course == course.id|stringformat:'s' %}selected{% endif %}>
{{ course.code }} - {{ course.name }}
</option>
{% endfor %}
</select>
<!-- Andere Filter beibehalten -->
{% if request.GET.course %}<input type="hidden" name="course" value="{{ request.GET.course }}">{% endif %}
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
</form>
</div>
<!-- Meine Tickets Filter -->
<div>
<label class="block text-sm font-medium mb-1">Zuweisung:</label>
{% if request.GET.assigned_to == user.id|stringformat:'s' %}
<!-- Wenn aktiv: Button zum Deaktivieren -->
<a href="?{% if selected_status %}status={{ selected_status }}{% endif %}{% if selected_course %}&course={{ selected_course }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}"
class="block w-full h-10 px-3 border border-purple-300 rounded shadow-sm text-center bg-purple-200 hover:bg-purple-300 transition-colors flex items-center justify-center">
👤 Meine Tickets ✓
</a>
{% else %}
<!-- Wenn nicht aktiv: Button zum Aktivieren -->
<a href="?assigned_to={{ user.id }}{% if selected_status %}&status={{ selected_status }}{% endif %}{% if selected_course %}&course={{ selected_course }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}"
class="block w-full h-10 px-3 border border-purple-300 rounded shadow-sm text-center bg-purple-50 hover:bg-purple-100 transition-colors flex items-center justify-center">
👤 Meine Tickets
</a>
{% endif %}
</div>
<!-- Kurs Filter -->
<div>
<form method="get">
<label class="block text-sm font-medium mb-1">Kurs:</label>
<select name="course"
onchange="this.form.submit()"
class="w-full h-10 px-3 border border-gray-300 rounded shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">Alle Kurse</option>
{% for course in courses %}
<option value="{{ course.id }}"
{% if selected_course == course.id|stringformat:'s' %}selected{% endif %}>
{{ course.code }} - {{ course.name }}
</option>
{% endfor %}
</select>
<!-- Andere Filter beibehalten -->
{% if selected_status %}<input type="hidden" name="status" value="{{ selected_status }}">{% endif %}
{% if request.GET.assigned_to %}
<input type="hidden"
name="assigned_to"
value="{{ request.GET.assigned_to }}">
{% endif %}
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
</form>
</div>
<!-- Suche -->
<div class="lg:col-span-2">
<form method="get">
<label class="block text-sm font-medium mb-1">Suche:</label>
<div class="flex">
<input type="text"
name="q"
value="{{ search_query }}"
placeholder="Titel oder Beschreibung durchsuchen..."
class="flex-1 h-10 px-3 border border-gray-300 rounded-l shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
{% if selected_status %}<input type="hidden" name="status" value="{{ selected_status }}">{% endif %}
{% if selected_course %}<input type="hidden" name="course" value="{{ selected_course }}">{% endif %}
{% if request.GET.assigned_to %}
<input type="hidden"
name="assigned_to"
value="{{ request.GET.assigned_to }}">
{% endif %}
{% if search_query %}<input type="hidden" name="q" value="{{ search_query }}">{% endif %}
</form>
</div>
<!-- Suche -->
<div class="lg:col-span-2">
<form method="get">
<label class="block text-sm font-medium mb-1">Suche:</label>
<div class="flex">
<input type="text"
name="q"
value="{{ search_query }}"
placeholder="Titel oder Beschreibung durchsuchen..."
class="flex-1 h-10 px-3 border border-gray-300 rounded-l shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
{% if selected_status %}<input type="hidden" name="status" value="{{ selected_status }}">{% endif %}
{% if selected_course %}<input type="hidden" name="course" value="{{ selected_course }}">{% endif %}
{% if request.GET.assigned_to %}
<input type="hidden"
name="assigned_to"
value="{{ request.GET.assigned_to }}">
{% endif %}
<button type="submit"
class="bg-blue-500 hover:bg-blue-600 text-white h-10 px-4 rounded-r">🔍</button>
</div>
</form>
<button type="submit"
class="bg-blue-500 hover:bg-blue-600 text-white h-10 px-4 rounded-r">🔍</button>
</div>
</form>
</div>
</div>
<!-- Aktive Filter Anzeige -->
{% if selected_status or request.GET.assigned_to or search_query %}
<div class="mt-4 p-3 bg-gray-50 rounded border-l-4 border-blue-400">
<div class="flex flex-wrap items-center gap-2">
<span class="font-medium text-gray-700">Aktive Filter:</span>
{% if selected_status %}
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-sm rounded">
Status:
{% for value, label in status_choices %}
{% if value == selected_status %}{{ label }}{% endif %}
{% endfor %}
<a href="?{% if search_query %}q={{ search_query }}{% endif %}{% if request.GET.assigned_to %}&assigned_to={{ request.GET.assigned_to }}{% endif %}"
class="ml-1 text-blue-600 hover:text-blue-800">×</a>
</span>
{% endif %}
{% if request.GET.assigned_to %}
<span class="px-2 py-1 bg-purple-100 text-purple-800 text-sm rounded">
Meine Tickets
<a href="?{% if selected_status %}status={{ selected_status }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}"
class="ml-1 text-purple-600 hover:text-purple-800">×</a>
</span>
{% endif %}
{% if search_query %}
<span class="px-2 py-1 bg-green-100 text-green-800 text-sm rounded">
Suche: "{{ search_query }}"
<a href="?{% if selected_status %}status={{ selected_status }}{% endif %}{% if request.GET.assigned_to %}&assigned_to={{ request.GET.assigned_to }}{% endif %}"
class="ml-1 text-green-600 hover:text-green-800">×</a>
</span>
{% endif %}
{% if selected_course %}
<span class="px-2 py-1 bg-green-100 text-green-800 text-sm rounded">
Kurs:
{% for course in courses %}
{% if course.id|stringformat:'s' == selected_course %}{{ course.code }}{% endif %}
{% endfor %}
<a href="?{% if selected_status %}status={{ selected_status }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if request.GET.assigned_to %}&assigned_to={{ request.GET.assigned_to }}{% endif %}"
class="ml-1 text-green-600 hover:text-green-800">×</a>
</span>
{% endif %}
<a href="{% url 'ticket-list' %}"
class="text-sm text-gray-600 hover:text-gray-800 font-medium">Alle Filter entfernen</a>
</div>
</div>
<!-- Aktive Filter Anzeige -->
{% if selected_status or request.GET.assigned_to or search_query %}
<div class="mt-4 p-3 bg-gray-50 rounded border-l-4 border-blue-400">
<div class="flex flex-wrap items-center gap-2">
<span class="font-medium text-gray-700">Aktive Filter:</span>
{% if selected_status %}
<span class="px-2 py-1 bg-blue-100 text-blue-800 text-sm rounded">
Status:
{% for value, label in status_choices %}
{% if value == selected_status %}{{ label }}{% endif %}
{% endfor %}
<a href="?{% if search_query %}q={{ search_query }}{% endif %}{% if request.GET.assigned_to %}&assigned_to={{ request.GET.assigned_to }}{% endif %}"
class="ml-1 text-blue-600 hover:text-blue-800">×</a>
</span>
{% endif %}
{% if request.GET.assigned_to %}
<span class="px-2 py-1 bg-purple-100 text-purple-800 text-sm rounded">
Meine Tickets
<a href="?{% if selected_status %}status={{ selected_status }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}"
class="ml-1 text-purple-600 hover:text-purple-800">×</a>
</span>
{% endif %}
{% if search_query %}
<span class="px-2 py-1 bg-green-100 text-green-800 text-sm rounded">
Suche: "{{ search_query }}"
<a href="?{% if selected_status %}status={{ selected_status }}{% endif %}{% if request.GET.assigned_to %}&assigned_to={{ request.GET.assigned_to }}{% endif %}"
class="ml-1 text-green-600 hover:text-green-800">×</a>
</span>
{% endif %}
{% if selected_course %}
<span class="px-2 py-1 bg-green-100 text-green-800 text-sm rounded">
Kurs:
{% for course in courses %}
{% if course.id|stringformat:'s' == selected_course %}{{ course.code }}{% endif %}
{% endfor %}
<a href="?{% if selected_status %}status={{ selected_status }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if request.GET.assigned_to %}&assigned_to={{ request.GET.assigned_to }}{% endif %}"
class="ml-1 text-green-600 hover:text-green-800">×</a>
</span>
{% endif %}
<a href="{% url 'ticket-list' %}"
class="text-sm text-gray-600 hover:text-gray-800 font-medium">Alle Filter entfernen</a>
</div>
</div>
{% endif %}
</div>
<!-- Ticket Tabelle -->
{% if tickets %}
<div class="bg-white rounded-lg shadow overflow-hidden">
{% endif %}
</div>
<!-- Ticket Tabelle -->
{% if tickets %}
<!-- Desktop -->
<div class="hidden lg:block bg-white rounded-lg shadow overflow-hidden">
<div class="overflow-x-auto">
<table class="w-full">
<thead class="bg-blue-600 text-white">
<tr>
@@ -204,62 +205,100 @@
{% elif ticket.status == 'closed' %}
<span class="px-2 py-1 rounded-full text-xs font-bold bg-gray-600 text-white">{{ ticket.get_status_display }}</span>
{% endif %}
<td class="px-4 py-3 text-center">
<span class="px-2 py-1 rounded-full text-xs font-bold whitespace-nowrap {% if ticket.mistake == 'typo' %}bg-blue-500 {% elif ticket.mistake == 'formatting_issue' %}bg-purple-500 {% elif ticket.mistake == 'missing_content' %}bg-red-500 {% elif ticket.mistake == 'outdated_content' %}bg-orange-500 {% elif ticket.mistake == 'audio_problem' %}bg-green-500 {% elif ticket.mistake == 'video_problem' %}bg-yellow-500 {% elif ticket.mistake == 'other' %}bg-gray-500 {% else %}bg-gray-400 {% endif %} text-white">
{{ ticket.get_mistake_display }}
</span>
</td>
<td class="px-4 py-3 text-sm text-gray-600">
{% if ticket.assigned_to %}
{{ ticket.assigned_to.username }}
{% else %}
<span class="text-gray-400 italic">Nicht zugewiesen</span>
{% endif %}
</td>
<td class="px-4 py-3 text-center text-sm text-gray-500">
<div>{{ ticket.created_at|date:"d.m.Y" }}</div>
<div class="text-xs text-gray-400">{{ ticket.created_at|date:"H:i" }}</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if is_paginated %}
<div class="mt-6 flex justify-between items-center">
<div class="text-sm text-gray-600">Seite {{ page_obj.number }} von {{ page_obj.paginator.num_pages }}</div>
<div class="flex gap-2">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}{% if selected_status %}&status={{ selected_status }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if selected_course %}&course={{ selected_course }}{% endif %}"
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-50">Zurück</a>
{% endif %}
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}{% if selected_status %}&status={{ selected_status }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if selected_course %}&course={{ selected_course }}{% endif %}"
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-50">Weiter</a>
</td>
<td class="px-4 py-3 text-center">
<span class="px-2 py-1 rounded-full text-xs font-bold whitespace-nowrap {% if ticket.mistake == 'typo' %}bg-blue-500 {% elif ticket.mistake == 'formatting_issue' %}bg-purple-500 {% elif ticket.mistake == 'missing_content' %}bg-red-500 {% elif ticket.mistake == 'outdated_content' %}bg-orange-500 {% elif ticket.mistake == 'audio_problem' %}bg-green-500 {% elif ticket.mistake == 'video_problem' %}bg-yellow-500 {% elif ticket.mistake == 'other' %}bg-gray-500 {% else %}bg-gray-400 {% endif %} text-white">
{{ ticket.get_mistake_display }}
</span>
</td>
<td class="px-4 py-3 text-sm text-gray-600">
{% if ticket.assigned_to %}
{{ ticket.assigned_to.username }}
{% else %}
<span class="text-gray-400 italic">Nicht zugewiesen</span>
{% endif %}
</td>
<td class="px-4 py-3 text-center text-sm text-gray-500">
<div>{{ ticket.created_at|date:"d.m.Y" }}</div>
<div class="text-xs text-gray-400">{{ ticket.created_at|date:"H:i" }}</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
<!-- Mobile -->
<div class="lg:hidden space-y-4">
{% for ticket in tickets %}
<div class="bg-white rounded-lg shadow p-4">
<div class="flex justify-between items-start mb-2">
<span class="text-sm font-bold text-gray-500">#{{ ticket.id }}</span>
<span class="text-xs text-gray-500">{{ ticket.created_at|date:"d.m.Y" }}</span>
</div>
<a href="{% url 'detail' ticket.pk %}"
class="font-bold text-blue-600 hover:text-blue-800 block mb-2">{{ ticket.title|truncatechars:50 }}</a>
<div class="space-y-2">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">Status:</span>
{% if ticket.status == 'new' %}
<span class="px-2 py-1 rounded-full text-xs font-bold bg-blue-500 text-white">{{ ticket.get_status_display }}</span>
{% elif ticket.status == 'in_progress' %}
<span class="px-2 py-1 rounded-full text-xs font-bold bg-yellow-400 text-gray-900">{{ ticket.get_status_display }}</span>
{% elif ticket.status == 'resolved' %}
<span class="px-2 py-1 rounded-full text-xs font-bold bg-green-400 text-white">{{ ticket.get_status_display }}</span>
{% elif ticket.status == 'closed' %}
<span class="px-2 py-1 rounded-full text-xs font-bold bg-gray-600 text-white">{{ ticket.get_status_display }}</span>
{% endif %}
</div>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">Kurs:</span>
<span class="text-sm font-medium">{{ ticket.course.code }}</span>
</div>
{% if ticket.assigned_to %}
<div class="flex justify-between items-center">
<span class="text-sm text-gray-600">Zugewiesen:</span>
<span class="text-sm">{{ ticket.assigned_to.username }}</span>
</div>
{% endif %}
</div>
{% endif %}
{% else %}
<!-- Keine Tickets -->
<div class="text-center py-12 bg-white rounded-lg shadow">
<div class="text-gray-400 text-6xl mb-4">📋</div>
{% if search_query %}
<h3 class="text-lg font-medium text-gray-900 mb-2">Keine Tickets gefunden</h3>
<p class="text-gray-500 mb-4">
Keine Tickets gefunden für die Suche „<strong>{{ search_query }}</strong>"
</p>
<a href="?" class="text-blue-600 hover:text-blue-800 font-medium">Alle Tickets anzeigen</a>
{% else %}
<h3 class="text-lg font-medium text-gray-900 mb-2">Noch keine Tickets vorhanden</h3>
<p class="text-gray-500 mb-4">Erstelle dein erstes Ticket um loszulegen.</p>
<a href="{% url 'create' %}"
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded font-medium">
Erstes Ticket erstellen
</a>
</div>
{% endfor %}
</div>
<!-- Pagination -->
{% if is_paginated %}
<div class="mt-6 flex justify-between items-center">
<div class="text-sm text-gray-600">Seite {{ page_obj.number }} von {{ page_obj.paginator.num_pages }}</div>
<div class="flex gap-2">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}{% if selected_status %}&status={{ selected_status }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if selected_course %}&course={{ selected_course }}{% endif %}"
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-50">Zurück</a>
{% endif %}
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}{% if selected_status %}&status={{ selected_status }}{% endif %}{% if search_query %}&q={{ search_query }}{% endif %}{% if selected_course %}&course={{ selected_course }}{% endif %}"
class="px-3 py-1 border border-gray-300 rounded hover:bg-gray-50">Weiter</a>
{% endif %}
</div>
</div>
{% endif %}
{% else %}
<!-- Keine Tickets -->
<div class="text-center py-12 bg-white rounded-lg shadow">
<div class="text-gray-400 text-6xl mb-4">📋</div>
{% if search_query %}
<h3 class="text-lg font-medium text-gray-900 mb-2">Keine Tickets gefunden</h3>
<p class="text-gray-500 mb-4">
Keine Tickets gefunden für die Suche „<strong>{{ search_query }}</strong>"
</p>
<a href="?" class="text-blue-600 hover:text-blue-800 font-medium">Alle Tickets anzeigen</a>
{% else %}
<h3 class="text-lg font-medium text-gray-900 mb-2">Noch keine Tickets vorhanden</h3>
<p class="text-gray-500 mb-4">Erstelle dein erstes Ticket um loszulegen.</p>
<a href="{% url 'create' %}"
class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded font-medium">
Erstes Ticket erstellen
</a>
{% endif %}
</div>
{% endblock %}
{% endif %}
{% endblock %}

View File

@@ -1,6 +1,6 @@
from django.urls import path
from .views import (AssignedTicketListView, HomeView, TicketCreateView,
from .views import (HomeView, TicketCreateView,
TicketDetailUpdateView, TicketListView, TicketUpdateView,
faq_list, faq_pdf_download)
@@ -14,7 +14,6 @@ urlpatterns = [
# /ticketsystem/new/
path("new/", TicketCreateView.as_view(), name="create"),
path("<int:pk>/modify/", TicketUpdateView.as_view(), name="modify"),
path("meine-tickets/", AssignedTicketListView.as_view(), name="assigned-tickets"),
path("faq/", faq_list, name="faq-list"),
path("faq/download/", faq_pdf_download, name="faq-pdf-download"),
]

View File

@@ -25,7 +25,7 @@ class HomeView(TemplateView):
context.update(
{
"total_tickets": Ticket.objects.count(),
"open_tickets": Ticket.objects.filter(status="open").count(),
"new_tickets": Ticket.objects.filter(status="new").count(),
"closed_tickets": Ticket.objects.filter(status="closed").count(),
"recent_tickets": Ticket.objects.order_by("-updated_at")[:5],
}
@@ -233,18 +233,6 @@ class TicketDetailUpdateView(UpdateView):
return super().post(request, *args, **kwargs)
class AssignedTicketListView(LoginRequiredMixin, ListView):
model = Ticket
template_name = "ticketsystem/assigned_tickets.html"
context_object_name = "tickets"
ordering = ["-created_at"]
def get_queryset(self):
return Ticket.objects.filter(assigned_to=self.request.user).exclude(
status="closed"
) # oder "geschlossen", je nach Wahl
class TicketCreateView(CreateView):
model = Ticket
form_class = TicketForm