315 lines
11 KiB
HTML
315 lines
11 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Mahnungsübersicht{% endblock %}
|
|
|
|
{% block content %}
|
|
<style>
|
|
.mahnungen-shell {
|
|
max-width: 1180px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
.mahnungen-head {
|
|
background: linear-gradient(135deg, #ffffff 0%, #f7f9fc 100%);
|
|
border: 1px solid #dbe4ee;
|
|
border-radius: 16px;
|
|
padding: 18px 20px;
|
|
margin-bottom: 20px;
|
|
box-shadow: 0 10px 26px rgba(15, 23, 42, 0.07);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
gap: 16px;
|
|
}
|
|
|
|
.mahnungen-head h1 {
|
|
margin: 0;
|
|
font-size: 1.55rem;
|
|
color: #1e293b;
|
|
}
|
|
|
|
.head-actions {
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.search-box-wrapper {
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.search-box {
|
|
width: 100%;
|
|
max-width: 360px;
|
|
padding: 10px 14px;
|
|
border: 1px solid #cbd5e1;
|
|
border-radius: 8px;
|
|
font-size: 0.95rem;
|
|
outline: none;
|
|
transition: border-color 0.2s, box-shadow 0.2s;
|
|
}
|
|
|
|
.search-box:focus {
|
|
border-color: #3b82f6;
|
|
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
|
|
}
|
|
|
|
.mahnungen-card {
|
|
background: var(--ui-surface, #ffffff);
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 16px;
|
|
padding: 18px;
|
|
box-shadow: 0 8px 20px rgba(15, 23, 42, 0.06);
|
|
}
|
|
|
|
.mahnungen-table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
.mahnungen-table th,
|
|
.mahnungen-table td {
|
|
padding: 12px 10px;
|
|
border-bottom: 1px solid #edf2f7;
|
|
vertical-align: middle;
|
|
text-align: left;
|
|
}
|
|
|
|
.mahnungen-table th {
|
|
font-size: 0.83rem;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.04em;
|
|
color: #64748b;
|
|
background: var(--ui-surface-soft, #f8fafc);
|
|
}
|
|
|
|
.mahnungen-table tr:hover td {
|
|
background: #fbfdff;
|
|
}
|
|
|
|
.badge-pill {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
padding: 4px 10px;
|
|
border-radius: 999px;
|
|
font-size: 0.78rem;
|
|
font-weight: 700;
|
|
line-height: 1;
|
|
}
|
|
|
|
.badge-open { background: #fee2e2; color: #991b1b; }
|
|
.badge-warning { background: #fef3c7; color: #b45309; }
|
|
.badge-info { background: #e0f2fe; color: #0369a1; }
|
|
.badge-paid { background: #dcfce7; color: #166534; }
|
|
|
|
.mono {
|
|
font-family: monospace;
|
|
font-size: 0.92rem;
|
|
}
|
|
|
|
.text-danger {
|
|
color: #dc2626;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
color: #6b7280;
|
|
padding: 36px 20px;
|
|
}
|
|
|
|
@media (max-width: 900px) {
|
|
.mahnungen-table {
|
|
display: block;
|
|
overflow-x: auto;
|
|
white-space: nowrap;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<div class="container-fluid py-4">
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-primary text-white d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0"><i class="bi bi-exclamation-triangle-fill me-2"></i>Mahnungs- und Ausleihverwaltung</h5>
|
|
<span class="badge bg-light text-dark">Übersicht überfälliger Medien</span>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if overdue_items and overdue_items|length > 0 %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Schüler / Ausweis</th>
|
|
<th>Klasse</th>
|
|
<th>Medium</th>
|
|
<th>Fälligkeitsdatum</th>
|
|
<th>Überfällig seit</th>
|
|
<th>Mahnstufe</th>
|
|
<th class="text-end">Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for item in overdue_items %}
|
|
<tr>
|
|
<td>
|
|
<strong>{{ item.schueler_name }}</strong><br>
|
|
<small class="text-muted">{{ item.ausweis_id }}</small>
|
|
</td>
|
|
<td>{{ item.klasse }}</td>
|
|
<td>
|
|
{{ item.item_name }}<br>
|
|
<small class="text-muted">Code: {{ item.item_code or '—' }}</small>
|
|
</td>
|
|
<td>{{ item.due_date }}</td>
|
|
<td>
|
|
<span class="badge bg-danger">{{ item.days_overdue }} Tage</span>
|
|
</td>
|
|
<td>
|
|
{% if item.mahnstufe == 2 %}
|
|
<span class="badge bg-dark text-danger">Stufe 2 (Gesperrt)</span>
|
|
{% elif item.mahnstufe == 1 %}
|
|
<span class="badge bg-warning text-dark">Stufe 1</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">Stufe 0</span>
|
|
{% endif %}
|
|
</td>
|
|
<td class="text-end">
|
|
<div class="btn-group" role="group">
|
|
{% if mail_service_enabled %}
|
|
<button type="button" class="btn btn-sm btn-outline-primary"
|
|
onclick="openEmailModal('{{ item.id }}', '{{ item.schueler_name }}', '{{ item.email }}')">
|
|
<i class="bi bi-envelope"></i> E-Mail
|
|
</button>
|
|
{% endif %}
|
|
<button type="button" class="btn btn-sm btn-outline-warning text-dark"
|
|
onclick="resetMahnung('{{ item.id }}', '{{ item.schueler_name }}')">
|
|
<i class="bi bi-arrow-counterclockwise"></i> Zurücksetzen
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-5">
|
|
<i class="bi bi-check-circle-fill text-success fs-1"></i>
|
|
<p class="text-muted mt-2">Aktuell gibt es keine überfälligen Mahnungen.</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- E-Mail Modal -->
|
|
<div class="modal fade" id="emailModal" tabindex="-1" aria-labelledby="emailModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="emailModalLabel">Mahnungs-E-Mail senden</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<form id="emailForm">
|
|
<input type="hidden" id="modalLoanId">
|
|
<div class="mb-3">
|
|
<label for="modalStudentName" class="form-label">Empfänger</label>
|
|
<input type="text" class="form-control" id="modalStudentName" readonly>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="modalEmail" class="form-label">E-Mail-Adresse</label>
|
|
<input type="email" class="form-control" id="modalEmail" placeholder="E-Mail-Adresse eingeben...">
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="modalMessage" class="form-label">Nachricht</label>
|
|
<textarea class="form-control" id="modalMessage" rows="4" placeholder="Optionaler Text..."></textarea>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
|
<button type="button" class="btn btn-primary" onclick="submitEmailMahnung()">E-Mail senden</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function openEmailModal(loanId, studentName, studentEmail) {
|
|
document.getElementById('modalLoanId').value = loanId;
|
|
document.getElementById('modalStudentName').value = studentName;
|
|
document.getElementById('modalEmail').value = studentEmail || '';
|
|
document.getElementById('modalMessage').value = '';
|
|
|
|
var myModal = new bootstrap.Modal(document.getElementById('emailModal'));
|
|
myModal.show();
|
|
}
|
|
|
|
function submitEmailMahnung() {
|
|
const loanId = document.getElementById('modalLoanId').value;
|
|
const email = document.getElementById('modalEmail').value;
|
|
const message = document.getElementById('modalMessage').value;
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
|
|
|
if (!email) {
|
|
alert('Bitte geben Sie eine gültige E-Mail-Adresse ein.');
|
|
return;
|
|
}
|
|
|
|
fetch('/mahnungen_send_email', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken },
|
|
body: JSON.stringify({ loan_id: loanId, email: email, message: message })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert('E-Mail erfolgreich gesendet!');
|
|
var modalEl = document.getElementById('emailModal');
|
|
var modal = bootstrap.Modal.getInstance(modalEl);
|
|
modal.hide();
|
|
} else {
|
|
alert('Fehler: ' + data.message);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Fehler beim Senden der Anfrage.');
|
|
console.error(error);
|
|
});
|
|
}
|
|
|
|
function resetMahnung(loanId, studentName) {
|
|
if (!confirm('Möchten Sie die Mahnung für "' + studentName + '" wirklich zurücksetzen?\n\nDadurch wird die Mahnstufe auf 0 gesetzt, die Frist um 14 Tage verlängert und der Schülerausweis entsperrt.')) {
|
|
return;
|
|
}
|
|
|
|
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || '';
|
|
|
|
fetch('/mahnungen_reset', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken },
|
|
body: JSON.stringify({ loan_id: loanId })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
alert(data.message);
|
|
location.reload(); // Seite neu laden, um die aktualisierte Ansicht zu sehen
|
|
} else {
|
|
alert('Fehler: ' + data.message);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Fehler beim Senden der Anfrage.');
|
|
console.error(error);
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %} |