🎉 Added new files for data collection and documentation 📚, updated error resilience script, and refactored admin schedule template. #123

This commit is contained in:
2025-06-02 15:10:39 +02:00
parent 6ff407a895
commit c29ef2c075
4 changed files with 6002 additions and 101 deletions

View File

@ -12,38 +12,147 @@
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.calendar-container {
background: white;
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
padding: 20px;
margin-bottom: 20px;
/* ===== DARK MODE CALENDAR OPTIMIERUNG ===== */
.fc {
background: transparent;
}
.fc-theme-standard .fc-view-harness {
background: transparent;
}
.fc-theme-standard .fc-scrollgrid {
border-color: rgb(148 163 184 / 0.3);
}
.dark .fc-theme-standard .fc-scrollgrid {
border-color: rgb(71 85 105 / 0.4);
}
.fc-theme-standard td,
.fc-theme-standard th {
border-color: rgb(148 163 184 / 0.2);
}
.dark .fc-theme-standard td,
.dark .fc-theme-standard th {
border-color: rgb(71 85 105 / 0.3);
}
.fc-col-header-cell {
background: rgb(248 250 252);
color: rgb(51 65 85);
}
.dark .fc-col-header-cell {
background: rgb(15 23 42);
color: rgb(203 213 225);
}
.fc-daygrid-day {
background: transparent;
}
.fc-day-today {
background: rgb(59 130 246 / 0.1) !important;
}
.dark .fc-day-today {
background: rgb(59 130 246 / 0.2) !important;
}
.fc-button-primary {
background: rgb(59 130 246);
border-color: rgb(59 130 246);
color: white;
}
.fc-button-primary:hover {
background: rgb(37 99 235);
border-color: rgb(37 99 235);
}
.fc-button-primary:disabled {
background: rgb(148 163 184);
border-color: rgb(148 163 184);
}
.dark .fc-button-primary:disabled {
background: rgb(71 85 105);
border-color: rgb(71 85 105);
}
.fc-event {
font-size: 11px;
border-radius: 4px;
padding: 2px 4px;
}
.fc-event-title {
font-weight: 500;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.dark .fc-event {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.fc-h-event {
border: none !important;
}
/* ===== KALENDER CONTAINER ===== */
.calendar-container {
background: rgb(255 255 255);
border: 1px solid rgb(226 232 240);
border-radius: 12px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
padding: 20px;
margin-bottom: 20px;
backdrop-filter: blur(12px);
}
.dark .calendar-container {
background: rgb(15 23 42);
border-color: rgb(51 65 85);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2), 0 2px 4px -1px rgba(0, 0, 0, 0.15);
}
/* ===== STATISTIK KARTEN ===== */
.stats-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
background: linear-gradient(135deg, rgb(59 130 246) 0%, rgb(99 102 241) 100%);
border-radius: 12px;
padding: 20px;
color: white;
margin-bottom: 20px;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
transition: all 0.3s ease;
}
.stats-card:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px -8px rgba(59, 130, 246, 0.3);
}
.dark .stats-card {
background: linear-gradient(135deg, rgb(37 99 235) 0%, rgb(79 70 229) 100%);
border-color: rgba(255, 255, 255, 0.05);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3), 0 2px 4px -1px rgba(0, 0, 0, 0.25);
}
/* ===== KONTROLLPANEL ===== */
.control-panel {
background: white;
background: rgb(255 255 255 / 0.8);
border: 1px solid rgb(226 232 240);
border-radius: 12px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 20px;
margin-bottom: 20px;
backdrop-filter: blur(12px);
}
.dark .control-panel {
background: rgb(15 23 42 / 0.8);
border-color: rgb(51 65 85);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.filter-group {
@ -53,24 +162,45 @@
flex-wrap: wrap;
}
/* ===== BUTTONS ===== */
.btn-calendar {
background: #3b82f6;
background: rgb(59 130 246);
color: white;
border: none;
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.2s;
transition: all 0.2s ease;
font-weight: 500;
border: 1px solid transparent;
}
.btn-calendar:hover {
background: #2563eb;
background: rgb(37 99 235);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
.btn-calendar.active {
background: #1d4ed8;
background: rgb(29 78 216);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
.dark .btn-calendar {
background: rgb(37 99 235);
border-color: rgb(59 130 246);
}
.dark .btn-calendar:hover {
background: rgb(29 78 216);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.6);
}
.dark .btn-calendar.active {
background: rgb(30 64 175);
}
/* ===== LEGENDE ===== */
.legend {
display: flex;
gap: 20px;
@ -83,54 +213,133 @@
align-items: center;
gap: 8px;
font-size: 14px;
color: rgb(71 85 105);
transition: color 0.3s ease;
}
.dark .legend-item {
color: rgb(203 213 225);
}
.legend-color {
width: 16px;
height: 16px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}
.dark .legend-color {
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
}
/* ===== FORM ELEMENTS ===== */
select, input {
background: rgb(255 255 255);
border: 1px solid rgb(203 213 225);
color: rgb(51 65 85);
transition: all 0.2s ease;
}
select:focus, input:focus {
border-color: rgb(59 130 246);
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
outline: none;
}
.dark select, .dark input {
background: rgb(30 41 59);
border-color: rgb(71 85 105);
color: rgb(203 213 225);
}
.dark select:focus, .dark input:focus {
border-color: rgb(99 102 241);
box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
}
/* ===== MODAL STYLING ===== */
.modal-overlay {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
.dark .modal-overlay {
background: rgba(0, 0, 0, 0.7);
}
.modal-content {
background: rgb(255 255 255);
border: 1px solid rgb(226 232 240);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
.dark .modal-content {
background: rgb(15 23 42);
border-color: rgb(51 65 85);
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.4), 0 10px 10px -5px rgba(0, 0, 0, 0.2);
}
/* ===== RESPONSIVE IMPROVEMENTS ===== */
@media (max-width: 768px) {
.calendar-container {
padding: 15px;
}
.control-panel {
padding: 15px;
}
.filter-group {
flex-direction: column;
align-items: stretch;
gap: 10px;
}
.legend {
gap: 15px;
}
}
</style>
{% endblock %}
{% block content %}
<div class="min-h-screen">
<div class="min-h-screen bg-gradient-to-br from-slate-50 to-blue-50 dark:from-slate-900 dark:to-slate-800 transition-colors duration-300">
<!-- Header mit Breadcrumb -->
<div>
<div class="border-b border-slate-200 dark:border-slate-700 bg-white/80 dark:bg-slate-900/80 backdrop-blur-md">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="py-6">
<nav class="text-sm font-medium text-gray-600 mb-4">
<nav class="text-sm font-medium text-slate-600 dark:text-slate-400 mb-4">
<ol class="list-none p-0 inline-flex">
<li class="flex items-center">
<a href="{{ url_for('admin_page') }}" class="hover:text-blue-600">Admin-Dashboard</a>
<svg class="fill-current w-3 h-3 mx-3" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<a href="{{ url_for('admin_page') }}" class="hover:text-blue-600 dark:hover:text-blue-400 transition-colors">Admin-Dashboard</a>
<svg class="fill-current w-3 h-3 mx-3 text-slate-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path d="m285.476 272.971c4.686 4.686 4.686 12.284 0 16.97l-133.952 133.954c-4.686 4.686-12.284 4.686-16.97 0l-133.952-133.954c-4.686-4.686-4.686-12.284 0-16.97 4.686-4.686 12.284-4.686 16.97 0l125.462 125.463 125.462-125.463c4.686-4.686 12.284-4.686 16.97 0z"/>
</svg>
</li>
<li class="text-gray-900 font-semibold">
<li class="text-slate-900 dark:text-white font-semibold">
Steckdosenschaltzeiten
</li>
</ol>
</nav>
<div class="flex justify-between items-center">
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center gap-4">
<div>
<h1 class="text-3xl font-bold text-gray-900">
<i class="fas fa-plug text-blue-600 mr-3"></i>
<h1 class="text-3xl font-bold text-slate-900 dark:text-white">
<i class="fas fa-plug text-blue-600 dark:text-blue-400 mr-3"></i>
Steckdosenschaltzeiten
</h1>
<p class="mt-2 text-gray-600">
<p class="mt-2 text-slate-600 dark:text-slate-400">
Kalenderübersicht aller Drucker-Steckdosenschaltungen mit detaillierter Analyse
</p>
</div>
<div class="flex gap-3">
<button id="refreshData" class="btn-calendar">
<button id="refreshData" class="btn-calendar flex items-center">
<i class="fas fa-sync-alt mr-2"></i>
Aktualisieren
</button>
<button id="cleanupLogs" class="bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700">
<button id="cleanupLogs" class="bg-red-600 dark:bg-red-700 text-white px-4 py-2 rounded-lg hover:bg-red-700 dark:hover:bg-red-600 transition-all duration-200 font-medium flex items-center">
<i class="fas fa-trash mr-2"></i>
Alte Logs löschen
</button>
@ -141,33 +350,39 @@
</div>
<!-- Haupt-Container -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pb-8">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<!-- Statistik-Karten -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-6">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-6">
<div class="stats-card">
<div class="flex items-center justify-between">
<div>
<p class="text-sm opacity-80">Schaltungen (24h)</p>
<p class="text-sm opacity-80 font-medium">Schaltungen (24h)</p>
<p class="text-2xl font-bold" id="totalLogs">{{ stats.total_logs or 0 }}</p>
<p class="text-xs opacity-70 mt-1">Gesamte Aktivität</p>
</div>
<div class="text-2xl opacity-60">
<i class="fas fa-chart-line"></i>
</div>
<i class="fas fa-chart-line text-2xl opacity-60"></i>
</div>
</div>
<div class="stats-card">
<div class="flex items-center justify-between">
<div>
<p class="text-sm opacity-80">Erfolgsrate</p>
<p class="text-sm opacity-80 font-medium">Erfolgsrate</p>
<p class="text-2xl font-bold" id="successRate">{{ "%.1f"|format(100 - stats.error_rate) }}%</p>
<p class="text-xs opacity-70 mt-1">Ohne Fehler</p>
</div>
<div class="text-2xl opacity-60">
<i class="fas fa-check-circle"></i>
</div>
<i class="fas fa-check-circle text-2xl opacity-60"></i>
</div>
</div>
<div class="stats-card">
<div class="flex items-center justify-between">
<div>
<p class="text-sm opacity-80">Ø Antwortzeit</p>
<p class="text-sm opacity-80 font-medium">Ø Antwortzeit</p>
<p class="text-2xl font-bold" id="avgResponseTime">
{% if stats.average_response_time_ms %}
{{ "%.0f"|format(stats.average_response_time_ms) }}ms
@ -175,18 +390,24 @@
N/A
{% endif %}
</p>
<p class="text-xs opacity-70 mt-1">Durchschnitt</p>
</div>
<div class="text-2xl opacity-60">
<i class="fas fa-stopwatch"></i>
</div>
<i class="fas fa-stopwatch text-2xl opacity-60"></i>
</div>
</div>
<div class="stats-card">
<div class="flex items-center justify-between">
<div>
<p class="text-sm opacity-80">Fehlerzahl</p>
<p class="text-sm opacity-80 font-medium">Fehlerzahl</p>
<p class="text-2xl font-bold" id="errorCount">{{ stats.error_count or 0 }}</p>
<p class="text-xs opacity-70 mt-1">Letzte 24h</p>
</div>
<div class="text-2xl opacity-60">
<i class="fas fa-exclamation-triangle"></i>
</div>
<i class="fas fa-exclamation-triangle text-2xl opacity-60"></i>
</div>
</div>
</div>
@ -194,19 +415,23 @@
<!-- Filter und Steuerung -->
<div class="control-panel">
<div class="filter-group">
<label for="printerFilter" class="font-medium text-gray-700">Drucker filtern:</label>
<select id="printerFilter" class="border border-gray-300 rounded-lg px-3 py-2 bg-white">
<option value="">Alle Drucker</option>
{% for printer in printers %}
<option value="{{ printer.id }}">{{ printer.name }}</option>
{% endfor %}
</select>
<div class="flex items-center gap-3">
<label for="printerFilter" class="font-medium text-slate-700 dark:text-slate-300 whitespace-nowrap">Drucker filtern:</label>
<select id="printerFilter" class="border border-slate-300 dark:border-slate-600 rounded-lg px-3 py-2 bg-white dark:bg-slate-800 text-slate-900 dark:text-white min-w-40">
<option value="">Alle Drucker</option>
{% for printer in printers %}
<option value="{{ printer.id }}">{{ printer.name }}</option>
{% endfor %}
</select>
</div>
<div class="border-l border-gray-300 pl-4 ml-4">
<label class="font-medium text-gray-700 mr-3">Ansicht:</label>
<button id="monthView" class="btn-calendar active">Monat</button>
<button id="weekView" class="btn-calendar">Woche</button>
<button id="dayView" class="btn-calendar">Tag</button>
<div class="border-l border-slate-300 dark:border-slate-600 pl-4 ml-4">
<label class="font-medium text-slate-700 dark:text-slate-300 mr-3">Ansicht:</label>
<div class="inline-flex gap-1">
<button id="monthView" class="btn-calendar active">Monat</button>
<button id="weekView" class="btn-calendar">Woche</button>
<button id="dayView" class="btn-calendar">Tag</button>
</div>
</div>
</div>
@ -237,22 +462,22 @@
</div>
<!-- Detailansicht Modal -->
<div id="eventDetailModal" class="fixed inset-0 bg-black bg-opacity-50 hidden z-50">
<div id="eventDetailModal" class="fixed inset-0 modal-overlay hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4">
<div class="bg-white rounded-lg max-w-lg w-full p-6">
<div class="modal-content rounded-lg max-w-lg w-full p-6">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold">Schaltung Details</h3>
<button id="closeModal" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times"></i>
<h3 class="text-lg font-semibold text-slate-900 dark:text-white">Schaltung Details</h3>
<button id="closeModal" class="text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors">
<i class="fas fa-times text-lg"></i>
</button>
</div>
<div id="modalContent">
<div id="modalContent" class="text-slate-700 dark:text-slate-300">
<!-- Wird dynamisch gefüllt -->
</div>
<div class="mt-6 flex justify-end">
<button id="closeModalBtn" class="px-4 py-2 bg-gray-300 text-gray-700 rounded-lg hover:bg-gray-400">
<button id="closeModalBtn" class="px-4 py-2 bg-slate-300 dark:bg-slate-600 text-slate-700 dark:text-slate-200 rounded-lg hover:bg-slate-400 dark:hover:bg-slate-500 transition-colors font-medium">
Schließen
</button>
</div>
@ -329,75 +554,75 @@ document.addEventListener('DOMContentLoaded', function() {
const startTime = new Date(event.start).toLocaleString('de-DE');
let detailsHtml = `
<div class="space-y-3">
<div class="flex items-center">
<span class="w-4 h-4 rounded mr-3" style="background-color: ${event.backgroundColor}"></span>
<span class="font-medium">${event.title}</span>
<div class="space-y-4">
<div class="flex items-center p-3 bg-slate-50 dark:bg-slate-800 rounded-lg">
<span class="w-4 h-4 rounded mr-3 flex-shrink-0" style="background-color: ${event.backgroundColor}"></span>
<span class="font-medium text-slate-900 dark:text-white">${event.title}</span>
</div>
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<span class="font-medium text-gray-600">Zeitpunkt:</span>
<p>${startTime}</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 text-sm">
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Zeitpunkt:</span>
<p class="text-slate-900 dark:text-white">${startTime}</p>
</div>
<div>
<span class="font-medium text-gray-600">Drucker:</span>
<p>${props.printer_name}</p>
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Drucker:</span>
<p class="text-slate-900 dark:text-white">${props.printer_name}</p>
</div>
<div>
<span class="font-medium text-gray-600">Status:</span>
<p class="capitalize">${props.status}</p>
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Status:</span>
<p class="text-slate-900 dark:text-white capitalize font-medium">${props.status}</p>
</div>
<div>
<span class="font-medium text-gray-600">Quelle:</span>
<p class="capitalize">${props.source}</p>
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Quelle:</span>
<p class="text-slate-900 dark:text-white capitalize">${props.source}</p>
</div>
`;
if (props.user_name) {
detailsHtml += `
<div>
<span class="font-medium text-gray-600">Benutzer:</span>
<p>${props.user_name}</p>
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Benutzer:</span>
<p class="text-slate-900 dark:text-white">${props.user_name}</p>
</div>
`;
}
if (props.response_time_ms) {
detailsHtml += `
<div>
<span class="font-medium text-gray-600">Antwortzeit:</span>
<p>${props.response_time_ms}ms</p>
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Antwortzeit:</span>
<p class="text-slate-900 dark:text-white">${props.response_time_ms}ms</p>
</div>
`;
}
if (props.power_consumption) {
detailsHtml += `
<div>
<span class="font-medium text-gray-600">Verbrauch:</span>
<p>${props.power_consumption}W</p>
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Verbrauch:</span>
<p class="text-slate-900 dark:text-white">${props.power_consumption}W</p>
</div>
`;
}
if (props.voltage) {
detailsHtml += `
<div>
<span class="font-medium text-gray-600">Spannung:</span>
<p>${props.voltage}V</p>
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Spannung:</span>
<p class="text-slate-900 dark:text-white">${props.voltage}V</p>
</div>
`;
}
if (props.current) {
detailsHtml += `
<div>
<span class="font-medium text-gray-600">Strom:</span>
<p>${props.current}A</p>
<div class="bg-slate-50 dark:bg-slate-800 p-3 rounded-lg">
<span class="font-medium text-slate-600 dark:text-slate-400 block mb-1">Strom:</span>
<p class="text-slate-900 dark:text-white">${props.current}A</p>
</div>
`;
}
@ -406,18 +631,18 @@ document.addEventListener('DOMContentLoaded', function() {
if (props.notes) {
detailsHtml += `
<div class="mt-4">
<span class="font-medium text-gray-600">Notizen:</span>
<p class="text-sm bg-gray-50 p-2 rounded mt-1">${props.notes}</p>
<div class="mt-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg p-3">
<span class="font-medium text-blue-800 dark:text-blue-200 block mb-2">Notizen:</span>
<p class="text-sm text-blue-700 dark:text-blue-300">${props.notes}</p>
</div>
`;
}
if (props.error_message) {
detailsHtml += `
<div class="mt-4">
<span class="font-medium text-red-600">Fehlermeldung:</span>
<p class="text-sm bg-red-50 text-red-700 p-2 rounded mt-1">${props.error_message}</p>
<div class="mt-4 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded-lg p-3">
<span class="font-medium text-red-800 dark:text-red-200 block mb-2">Fehlermeldung:</span>
<p class="text-sm text-red-700 dark:text-red-300">${props.error_message}</p>
</div>
`;
}
@ -435,6 +660,20 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('closeModal').addEventListener('click', closeModal);
document.getElementById('closeModalBtn').addEventListener('click', closeModal);
// Escape-Taste zum Schließen des Modals
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeModal();
}
});
// Modal schließen bei Klick außerhalb
document.getElementById('eventDetailModal').addEventListener('click', function(e) {
if (e.target === this) {
closeModal();
}
});
// Drucker-Filter
document.getElementById('printerFilter').addEventListener('change', function() {
currentPrinterFilter = this.value;
@ -466,13 +705,28 @@ document.addEventListener('DOMContentLoaded', function() {
// Aktualisieren-Button
document.getElementById('refreshData').addEventListener('click', function() {
calendar.refetchEvents();
loadStatistics();
const btn = this;
const icon = btn.querySelector('i');
// Button-State während des Ladens
btn.disabled = true;
icon.classList.add('fa-spin');
Promise.all([
calendar.refetchEvents(),
loadStatistics()
]).finally(() => {
btn.disabled = false;
icon.classList.remove('fa-spin');
});
});
// Cleanup-Button
document.getElementById('cleanupLogs').addEventListener('click', function() {
if (confirm('Möchten Sie wirklich alte Logs löschen? (älter als 30 Tage)')) {
if (confirm('Möchten Sie wirklich alte Logs löschen? (älter als 30 Tage)\n\nDieser Vorgang kann nicht rückgängig gemacht werden.')) {
const btn = this;
btn.disabled = true;
fetch('/api/admin/plug-schedules/cleanup', {
method: 'POST',
headers: {
@ -484,23 +738,44 @@ document.addEventListener('DOMContentLoaded', function() {
.then(response => response.json())
.then(data => {
if (data.success) {
alert(`Erfolgreich ${data.deleted_count} alte Einträge gelöscht`);
if (typeof showToast === 'function') {
showToast(`Erfolgreich ${data.deleted_count} alte Einträge gelöscht`, 'success', 5000, {
title: 'Bereinigung erfolgreich'
});
} else {
alert(`Erfolgreich ${data.deleted_count} alte Einträge gelöscht`);
}
calendar.refetchEvents();
loadStatistics();
} else {
alert('Fehler beim Löschen: ' + data.error);
if (typeof showToast === 'function') {
showToast('Fehler beim Löschen: ' + data.error, 'error', 5000, {
title: 'Bereinigung fehlgeschlagen'
});
} else {
alert('Fehler beim Löschen: ' + data.error);
}
}
})
.catch(error => {
console.error('Fehler:', error);
alert('Fehler beim Löschen der Logs');
if (typeof showToast === 'function') {
showToast('Fehler beim Löschen der Logs', 'error', 5000, {
title: 'Netzwerkfehler'
});
} else {
alert('Fehler beim Löschen der Logs');
}
})
.finally(() => {
btn.disabled = false;
});
}
});
// Statistiken laden
function loadStatistics() {
fetch('/api/admin/plug-schedules/statistics')
return fetch('/api/admin/plug-schedules/statistics')
.then(response => response.json())
.then(data => {
if (data.success) {
@ -519,6 +794,9 @@ document.addEventListener('DOMContentLoaded', function() {
// Kalender initialisieren
initCalendar();
// Initial Statistics Load
loadStatistics();
});
</script>
{% endblock %}