feat: Hinzufügen neuer API-Endpunkte zur erweiterten Drucker-Status-Verwaltung und Verbesserung der Benutzeroberfläche durch optimierte Lade- und Filtermechanismen. Implementierung von Caching für Online-Drucker und Live-Status sowie Auto-Update-Funktionalität zur Echtzeit-Überwachung. Anpassungen in den Templates zur Anzeige von Status-Übersichten und Filteroptionen für eine verbesserte Benutzererfahrung.
This commit is contained in:
@@ -3226,6 +3226,307 @@ def admin_update_printer_form(printer_id):
|
|||||||
flash("Fehler beim Aktualisieren des Druckers.", "error")
|
flash("Fehler beim Aktualisieren des Druckers.", "error")
|
||||||
return redirect(url_for("admin_printer_settings_page", printer_id=printer_id))
|
return redirect(url_for("admin_printer_settings_page", printer_id=printer_id))
|
||||||
|
|
||||||
|
# Neue API-Endpunkte für erweiterte Drucker-Status-Verwaltung hinzufügen
|
||||||
|
@app.route("/api/printers/online", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def get_online_printers():
|
||||||
|
"""Gibt nur die online/verfügbaren Drucker zurück - optimiert für schnelle Anzeige."""
|
||||||
|
db_session = get_db_session()
|
||||||
|
printers_logger = get_logger("printers")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Session-Cache für Online-Drucker prüfen
|
||||||
|
cache_key = f"online_printers_{current_user.id}"
|
||||||
|
cached_data = session.get(cache_key)
|
||||||
|
cache_timestamp = session.get(f"{cache_key}_timestamp")
|
||||||
|
|
||||||
|
# Cache ist 30 Sekunden gültig
|
||||||
|
if cached_data and cache_timestamp:
|
||||||
|
cache_age = (datetime.now() - datetime.fromisoformat(cache_timestamp)).total_seconds()
|
||||||
|
if cache_age < 30:
|
||||||
|
printers_logger.info(f"Online-Drucker aus Session-Cache geladen (Alter: {cache_age:.1f}s)")
|
||||||
|
return jsonify({
|
||||||
|
"printers": cached_data,
|
||||||
|
"count": len(cached_data),
|
||||||
|
"cached": True,
|
||||||
|
"cache_age": cache_age
|
||||||
|
})
|
||||||
|
|
||||||
|
# Nur verfügbare/online Drucker aus Datenbank laden
|
||||||
|
printers = db_session.query(Printer).filter(
|
||||||
|
Printer.status.in_(["available", "online", "idle"]),
|
||||||
|
Printer.active == True
|
||||||
|
).all()
|
||||||
|
|
||||||
|
current_time = datetime.now()
|
||||||
|
online_printers = []
|
||||||
|
|
||||||
|
for printer in printers:
|
||||||
|
printer_data = {
|
||||||
|
"id": printer.id,
|
||||||
|
"name": printer.name,
|
||||||
|
"model": printer.model or 'Unbekanntes Modell',
|
||||||
|
"location": printer.location or 'Unbekannter Standort',
|
||||||
|
"mac_address": printer.mac_address,
|
||||||
|
"plug_ip": printer.plug_ip,
|
||||||
|
"status": printer.status,
|
||||||
|
"active": printer.active,
|
||||||
|
"ip_address": printer.plug_ip if printer.plug_ip else getattr(printer, 'ip_address', None),
|
||||||
|
"created_at": printer.created_at.isoformat() if printer.created_at else current_time.isoformat(),
|
||||||
|
"last_checked": printer.last_checked.isoformat() if hasattr(printer, 'last_checked') and printer.last_checked else None,
|
||||||
|
"is_online": True # Alle Drucker in dieser Liste sind online
|
||||||
|
}
|
||||||
|
online_printers.append(printer_data)
|
||||||
|
|
||||||
|
# In Session-Cache speichern
|
||||||
|
session[cache_key] = online_printers
|
||||||
|
session[f"{cache_key}_timestamp"] = current_time.isoformat()
|
||||||
|
session.permanent = True
|
||||||
|
|
||||||
|
db_session.close()
|
||||||
|
|
||||||
|
printers_logger.info(f"Online-Drucker geladen: {len(online_printers)} verfügbare Drucker")
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"printers": online_printers,
|
||||||
|
"count": len(online_printers),
|
||||||
|
"cached": False,
|
||||||
|
"message": f"{len(online_printers)} online Drucker gefunden"
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
db_session.rollback()
|
||||||
|
db_session.close()
|
||||||
|
printers_logger.error(f"Fehler beim Abrufen der Online-Drucker: {str(e)}")
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Fehler beim Laden der Online-Drucker: {str(e)}",
|
||||||
|
"printers": []
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
@app.route("/api/printers/status/live", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def get_live_printer_status():
|
||||||
|
"""Gibt Live-Status aller Drucker zurück mit Session-Caching und Echtzeit-Updates."""
|
||||||
|
db_session = get_db_session()
|
||||||
|
printers_logger = get_logger("printers")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Session-Cache für Live-Status prüfen
|
||||||
|
cache_key = f"live_printer_status_{current_user.id}"
|
||||||
|
cached_data = session.get(cache_key)
|
||||||
|
cache_timestamp = session.get(f"{cache_key}_timestamp")
|
||||||
|
|
||||||
|
# Cache ist 15 Sekunden gültig für Live-Status
|
||||||
|
if cached_data and cache_timestamp:
|
||||||
|
cache_age = (datetime.now() - datetime.fromisoformat(cache_timestamp)).total_seconds()
|
||||||
|
if cache_age < 15:
|
||||||
|
printers_logger.info(f"Live-Status aus Session-Cache geladen (Alter: {cache_age:.1f}s)")
|
||||||
|
return jsonify({
|
||||||
|
"printers": cached_data,
|
||||||
|
"cached": True,
|
||||||
|
"cache_age": cache_age,
|
||||||
|
"next_update": 15 - cache_age
|
||||||
|
})
|
||||||
|
|
||||||
|
# Alle Drucker aus der Datenbank laden
|
||||||
|
printers = db_session.query(Printer).all()
|
||||||
|
|
||||||
|
if not printers:
|
||||||
|
return jsonify({
|
||||||
|
"printers": [],
|
||||||
|
"count": 0,
|
||||||
|
"message": "Keine Drucker in der Datenbank gefunden"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Drucker-Daten für Status-Check vorbereiten
|
||||||
|
printer_data = []
|
||||||
|
for printer in printers:
|
||||||
|
printer_data.append({
|
||||||
|
'id': printer.id,
|
||||||
|
'name': printer.name,
|
||||||
|
'ip_address': printer.plug_ip if printer.plug_ip else getattr(printer, 'ip_address', None)
|
||||||
|
})
|
||||||
|
|
||||||
|
# Paralleler Status-Check mit kürzerem Timeout für Live-Updates
|
||||||
|
try:
|
||||||
|
status_results = check_multiple_printers_status(printer_data, timeout=3)
|
||||||
|
except Exception as e:
|
||||||
|
printers_logger.warning(f"Status-Check fehlgeschlagen, verwende letzte bekannte Status: {str(e)}")
|
||||||
|
# Fallback: verwende letzte bekannte Status
|
||||||
|
status_results = {p['id']: (p.get('last_status', 'offline'), False) for p in printer_data}
|
||||||
|
|
||||||
|
# Live-Status-Daten zusammenstellen
|
||||||
|
live_status_data = []
|
||||||
|
current_time = datetime.now()
|
||||||
|
online_count = 0
|
||||||
|
|
||||||
|
for printer in printers:
|
||||||
|
if printer.id in status_results:
|
||||||
|
status, active = status_results[printer.id]
|
||||||
|
frontend_status = "available" if status == "online" else "offline"
|
||||||
|
if frontend_status == "available":
|
||||||
|
online_count += 1
|
||||||
|
else:
|
||||||
|
frontend_status = printer.status or "offline"
|
||||||
|
active = printer.active if hasattr(printer, 'active') else False
|
||||||
|
|
||||||
|
# Status in Datenbank aktualisieren (asynchron)
|
||||||
|
printer.status = frontend_status
|
||||||
|
printer.active = active
|
||||||
|
if hasattr(printer, 'last_checked'):
|
||||||
|
printer.last_checked = current_time
|
||||||
|
|
||||||
|
live_status_data.append({
|
||||||
|
"id": printer.id,
|
||||||
|
"name": printer.name,
|
||||||
|
"model": printer.model or 'Unbekanntes Modell',
|
||||||
|
"location": printer.location or 'Unbekannter Standort',
|
||||||
|
"mac_address": printer.mac_address,
|
||||||
|
"plug_ip": printer.plug_ip,
|
||||||
|
"status": frontend_status,
|
||||||
|
"active": active,
|
||||||
|
"ip_address": printer.plug_ip if printer.plug_ip else getattr(printer, 'ip_address', None),
|
||||||
|
"created_at": printer.created_at.isoformat() if printer.created_at else current_time.isoformat(),
|
||||||
|
"last_checked": current_time.isoformat(),
|
||||||
|
"is_online": frontend_status == "available",
|
||||||
|
"status_changed": True # Für Frontend-Animationen
|
||||||
|
})
|
||||||
|
|
||||||
|
# Änderungen in Datenbank speichern
|
||||||
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
except Exception as e:
|
||||||
|
printers_logger.warning(f"Fehler beim Speichern der Live-Status-Updates: {str(e)}")
|
||||||
|
|
||||||
|
# In Session-Cache speichern
|
||||||
|
session[cache_key] = live_status_data
|
||||||
|
session[f"{cache_key}_timestamp"] = current_time.isoformat()
|
||||||
|
session.permanent = True
|
||||||
|
|
||||||
|
# Online-Drucker-Cache invalidieren
|
||||||
|
online_cache_key = f"online_printers_{current_user.id}"
|
||||||
|
if online_cache_key in session:
|
||||||
|
del session[online_cache_key]
|
||||||
|
del session[f"{online_cache_key}_timestamp"]
|
||||||
|
|
||||||
|
db_session.close()
|
||||||
|
|
||||||
|
printers_logger.info(f"Live-Status aktualisiert: {online_count} von {len(live_status_data)} Drucker online")
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"printers": live_status_data,
|
||||||
|
"count": len(live_status_data),
|
||||||
|
"online_count": online_count,
|
||||||
|
"offline_count": len(live_status_data) - online_count,
|
||||||
|
"cached": False,
|
||||||
|
"timestamp": current_time.isoformat(),
|
||||||
|
"next_update": 15
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
db_session.rollback()
|
||||||
|
db_session.close()
|
||||||
|
printers_logger.error(f"Fehler beim Live-Status-Check: {str(e)}")
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Fehler beim Live-Status-Check: {str(e)}",
|
||||||
|
"printers": []
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
@app.route("/api/printers/status/summary", methods=["GET"])
|
||||||
|
@login_required
|
||||||
|
def get_printer_status_summary():
|
||||||
|
"""Gibt eine Zusammenfassung des Drucker-Status zurück - sehr schnell."""
|
||||||
|
db_session = get_db_session()
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Session-Cache für Status-Zusammenfassung
|
||||||
|
cache_key = f"printer_summary_{current_user.id}"
|
||||||
|
cached_data = session.get(cache_key)
|
||||||
|
cache_timestamp = session.get(f"{cache_key}_timestamp")
|
||||||
|
|
||||||
|
# Cache ist 60 Sekunden gültig
|
||||||
|
if cached_data and cache_timestamp:
|
||||||
|
cache_age = (datetime.now() - datetime.fromisoformat(cache_timestamp)).total_seconds()
|
||||||
|
if cache_age < 60:
|
||||||
|
return jsonify({
|
||||||
|
**cached_data,
|
||||||
|
"cached": True,
|
||||||
|
"cache_age": cache_age
|
||||||
|
})
|
||||||
|
|
||||||
|
# Status-Zusammenfassung aus Datenbank
|
||||||
|
total_printers = db_session.query(Printer).count()
|
||||||
|
online_printers = db_session.query(Printer).filter(
|
||||||
|
Printer.status.in_(["available", "online", "idle"]),
|
||||||
|
Printer.active == True
|
||||||
|
).count()
|
||||||
|
offline_printers = total_printers - online_printers
|
||||||
|
|
||||||
|
# Letzte Aktualisierung ermitteln
|
||||||
|
last_checked = db_session.query(func.max(Printer.last_checked)).scalar()
|
||||||
|
|
||||||
|
summary_data = {
|
||||||
|
"total": total_printers,
|
||||||
|
"online": online_printers,
|
||||||
|
"offline": offline_printers,
|
||||||
|
"percentage_online": round((online_printers / total_printers * 100) if total_printers > 0 else 0, 1),
|
||||||
|
"last_checked": last_checked.isoformat() if last_checked else None,
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
|
||||||
|
# In Session-Cache speichern
|
||||||
|
session[cache_key] = summary_data
|
||||||
|
session[f"{cache_key}_timestamp"] = datetime.now().isoformat()
|
||||||
|
session.permanent = True
|
||||||
|
|
||||||
|
db_session.close()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
**summary_data,
|
||||||
|
"cached": False
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
db_session.close()
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Fehler beim Laden der Status-Zusammenfassung: {str(e)}",
|
||||||
|
"total": 0,
|
||||||
|
"online": 0,
|
||||||
|
"offline": 0
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
# Session-Cache-Management
|
||||||
|
@app.route("/api/printers/cache/clear", methods=["POST"])
|
||||||
|
@login_required
|
||||||
|
def clear_printer_cache():
|
||||||
|
"""Löscht den Drucker-Cache für den aktuellen Benutzer."""
|
||||||
|
try:
|
||||||
|
cache_keys = [
|
||||||
|
f"online_printers_{current_user.id}",
|
||||||
|
f"live_printer_status_{current_user.id}",
|
||||||
|
f"printer_summary_{current_user.id}"
|
||||||
|
]
|
||||||
|
|
||||||
|
cleared_count = 0
|
||||||
|
for key in cache_keys:
|
||||||
|
if key in session:
|
||||||
|
del session[key]
|
||||||
|
cleared_count += 1
|
||||||
|
timestamp_key = f"{key}_timestamp"
|
||||||
|
if timestamp_key in session:
|
||||||
|
del session[timestamp_key]
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"message": f"Cache erfolgreich geleert ({cleared_count} Einträge)",
|
||||||
|
"cleared_keys": cleared_count
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
"error": f"Fehler beim Löschen des Cache: {str(e)}"
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
|
||||||
# ===== STARTUP UND MAIN =====
|
# ===== STARTUP UND MAIN =====
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import sys
|
import sys
|
||||||
@@ -3282,4 +3583,4 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app_logger.error(f"Fehler beim Starten der Anwendung: {str(e)}")
|
app_logger.error(f"Fehler beim Starten der Anwendung: {str(e)}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
177
backend/app/static/css/printers.css
Normal file
177
backend/app/static/css/printers.css
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
/* Erweiterte Drucker-Styles für MYP Platform */
|
||||||
|
|
||||||
|
/* Filter-Button-Styles */
|
||||||
|
.filter-btn {
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-btn.active {
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
|
||||||
|
color: #374151;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .filter-btn.active {
|
||||||
|
background-color: #475569;
|
||||||
|
color: #f1f5f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Online-Drucker-Hervorhebung */
|
||||||
|
.printer-card-online {
|
||||||
|
background: linear-gradient(135deg, #f0fdf4 0%, #ffffff 100%);
|
||||||
|
border-color: #bbf7d0;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(34, 197, 94, 0.1), 0 1px 2px 0 rgba(34, 197, 94, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .printer-card-online {
|
||||||
|
background: linear-gradient(135deg, rgba(34, 197, 94, 0.1) 0%, #1e293b 100%);
|
||||||
|
border-color: #166534;
|
||||||
|
box-shadow: 0 1px 3px 0 rgba(34, 197, 94, 0.2), 0 1px 2px 0 rgba(34, 197, 94, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.printer-card-online:hover {
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(34, 197, 94, 0.2), 0 2px 4px -1px rgba(34, 197, 94, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .printer-card-online:hover {
|
||||||
|
box-shadow: 0 4px 6px -1px rgba(34, 197, 94, 0.3), 0 2px 4px -1px rgba(34, 197, 94, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Online-Indikator-Animation */
|
||||||
|
.online-indicator {
|
||||||
|
animation: pulse-green 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-green {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 1;
|
||||||
|
box-shadow: 0 0 0 0 rgba(34, 197, 94, 0.7);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: .8;
|
||||||
|
box-shadow: 0 0 0 4px rgba(34, 197, 94, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status-Übersicht-Animationen */
|
||||||
|
.status-count-change {
|
||||||
|
animation: count-change 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes count-change {
|
||||||
|
0% { transform: scale(1); }
|
||||||
|
50% { transform: scale(1.1); }
|
||||||
|
100% { transform: scale(1); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auto-Refresh-Button-Animationen */
|
||||||
|
.auto-refresh-active {
|
||||||
|
background: linear-gradient(45deg, #10b981, #059669);
|
||||||
|
animation: gradient-shift 3s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes gradient-shift {
|
||||||
|
0%, 100% { background-position: 0% 50%; }
|
||||||
|
50% { background-position: 100% 50%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Drucker-Karten-Übergangseffekte */
|
||||||
|
.printer-card {
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.printer-card:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loading-Spinner für Live-Updates */
|
||||||
|
.live-update-spinner {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Verbesserungen */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.filter-btn {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-overview {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.printer-card {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark Mode Verbesserungen */
|
||||||
|
.dark .printer-card {
|
||||||
|
background-color: #1e293b;
|
||||||
|
border-color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark .printer-card:hover {
|
||||||
|
background-color: #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accessibility Verbesserungen */
|
||||||
|
.filter-btn:focus {
|
||||||
|
outline: 2px solid #3b82f6;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.printer-card:focus-within {
|
||||||
|
outline: 2px solid #3b82f6;
|
||||||
|
outline-offset: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Print-Styles */
|
||||||
|
@media print {
|
||||||
|
.filter-btn,
|
||||||
|
.auto-refresh-btn,
|
||||||
|
.printer-detail-btn,
|
||||||
|
.delete-printer-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.printer-card {
|
||||||
|
break-inside: avoid;
|
||||||
|
box-shadow: none;
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* High Contrast Mode */
|
||||||
|
@media (prefers-contrast: high) {
|
||||||
|
.printer-card-online {
|
||||||
|
border: 2px solid #059669;
|
||||||
|
}
|
||||||
|
|
||||||
|
.online-indicator {
|
||||||
|
border: 1px solid #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reduced Motion */
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.online-indicator,
|
||||||
|
.auto-refresh-active,
|
||||||
|
.live-update-spinner {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.printer-card {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.printer-card:hover {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
}
|
@@ -170,6 +170,10 @@
|
|||||||
|
|
||||||
{% block extra_js %}
|
{% block extra_js %}
|
||||||
<script>
|
<script>
|
||||||
|
// Globale Variable für Admin-Status
|
||||||
|
window.isAdmin = {% if current_user.is_admin %}true{% else %}false{% endif %};
|
||||||
|
</script>
|
||||||
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// File upload preview
|
// File upload preview
|
||||||
const fileInput = document.getElementById('stl_file');
|
const fileInput = document.getElementById('stl_file');
|
||||||
@@ -251,95 +255,209 @@ function refreshJobs() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Laden der Drucker für das Dropdown
|
// Laden der Drucker für das Dropdown mit verbesserter Online-Erkennung
|
||||||
function loadPrinters() {
|
function loadPrinters() {
|
||||||
// Lade Drucker mit Status-Check für bessere Verfügbarkeitsprüfung
|
const printerSelect = document.getElementById('printer_id');
|
||||||
fetch('/api/printers/status')
|
|
||||||
|
// Loading-State anzeigen
|
||||||
|
printerSelect.innerHTML = '<option value="">Lade Drucker...</option>';
|
||||||
|
printerSelect.disabled = true;
|
||||||
|
|
||||||
|
// Zuerst versuchen, Online-Drucker zu laden (schnell)
|
||||||
|
fetch('/api/printers/online')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const printerSelect = document.getElementById('printer_id');
|
const onlinePrinters = data.printers || [];
|
||||||
printerSelect.innerHTML = '<option value="">Drucker auswählen...</option>';
|
console.log('Online-Drucker geladen:', onlinePrinters);
|
||||||
|
|
||||||
// Prüfe ob data ein Array ist (direkte Antwort) oder ein Objekt mit printers-Property
|
if (onlinePrinters.length > 0) {
|
||||||
const printers = Array.isArray(data) ? data : (data.printers || []);
|
// Online-Drucker verfügbar - diese bevorzugt anzeigen
|
||||||
|
populatePrinterSelect(onlinePrinters, true);
|
||||||
console.log('Geladene Drucker:', printers);
|
showNotification(`${onlinePrinters.length} online Drucker verfügbar`, 'success');
|
||||||
|
|
||||||
// Filtere verfügbare Drucker (status: 'available' oder active: true)
|
|
||||||
const availablePrinters = printers.filter(printer => {
|
|
||||||
return printer.status === 'available' || printer.active === true;
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('Verfügbare Drucker:', availablePrinters);
|
|
||||||
|
|
||||||
if (availablePrinters.length === 0) {
|
|
||||||
// Fallback: Lade alle Drucker ohne Status-Check
|
|
||||||
return fetch('/api/printers')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(fallbackData => {
|
|
||||||
const fallbackPrinters = fallbackData.printers || [];
|
|
||||||
console.log('Fallback Drucker:', fallbackPrinters);
|
|
||||||
|
|
||||||
if (fallbackPrinters.length === 0) {
|
|
||||||
printerSelect.innerHTML = '<option value="">Keine Drucker verfügbar</option>';
|
|
||||||
showNotification('Keine Drucker in der Datenbank gefunden', 'warning');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zeige alle Drucker an, auch wenn Status unbekannt
|
|
||||||
fallbackPrinters.forEach(printer => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = printer.id;
|
|
||||||
option.textContent = `${printer.name} (${printer.model || 'Unbekanntes Modell'}) - Status unbekannt`;
|
|
||||||
printerSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
showNotification(`${fallbackPrinters.length} Drucker geladen (Status unbekannt)`, 'info');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Füge verfügbare Drucker hinzu
|
|
||||||
availablePrinters.forEach(printer => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = printer.id;
|
|
||||||
|
|
||||||
// Status-Indikator hinzufügen
|
// Zusätzlich alle Drucker laden für Vollständigkeit
|
||||||
const statusText = printer.status === 'available' ? '✅ Verfügbar' : '⚠️ Status unbekannt';
|
loadAllPrintersAsSecondary(onlinePrinters);
|
||||||
option.textContent = `${printer.name} (${printer.model || 'Unbekanntes Modell'}) - ${statusText}`;
|
} else {
|
||||||
printerSelect.appendChild(option);
|
// Keine Online-Drucker - lade alle mit Live-Status-Check
|
||||||
});
|
loadPrintersWithLiveStatus();
|
||||||
|
}
|
||||||
showNotification(`${availablePrinters.length} verfügbare Drucker geladen`, 'success');
|
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error('Fehler beim Laden der Drucker:', error);
|
console.error('Fehler beim Laden der Online-Drucker:', error);
|
||||||
|
// Fallback: Lade alle Drucker mit Live-Status
|
||||||
|
loadPrintersWithLiveStatus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hilfsfunktion: Drucker-Select befüllen
|
||||||
|
function populatePrinterSelect(printers, onlineOnly = false) {
|
||||||
|
const printerSelect = document.getElementById('printer_id');
|
||||||
|
printerSelect.innerHTML = '<option value="">Drucker auswählen...</option>';
|
||||||
|
printerSelect.disabled = false;
|
||||||
|
|
||||||
|
if (printers.length === 0) {
|
||||||
|
printerSelect.innerHTML = '<option value="">Keine Drucker verfügbar</option>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sortiere Drucker: Online zuerst, dann nach Name
|
||||||
|
const sortedPrinters = printers.sort((a, b) => {
|
||||||
|
// Online-Status prüfen
|
||||||
|
const aOnline = a.status === 'available' || a.is_online || a.active;
|
||||||
|
const bOnline = b.status === 'available' || b.is_online || b.active;
|
||||||
|
|
||||||
|
if (aOnline && !bOnline) return -1;
|
||||||
|
if (!aOnline && bOnline) return 1;
|
||||||
|
|
||||||
|
// Bei gleichem Online-Status nach Name sortieren
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
sortedPrinters.forEach(printer => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = printer.id;
|
||||||
|
|
||||||
|
// Status-Indikator bestimmen
|
||||||
|
const isOnline = printer.status === 'available' || printer.is_online || printer.active;
|
||||||
|
let statusIcon, statusText;
|
||||||
|
|
||||||
|
if (isOnline) {
|
||||||
|
statusIcon = '🟢';
|
||||||
|
statusText = 'Online';
|
||||||
|
option.style.color = '#059669'; // Grün für online
|
||||||
|
} else {
|
||||||
|
statusIcon = '🔴';
|
||||||
|
statusText = 'Offline';
|
||||||
|
option.style.color = '#dc2626'; // Rot für offline
|
||||||
|
option.disabled = true; // Offline-Drucker deaktivieren
|
||||||
|
}
|
||||||
|
|
||||||
|
// Letzter Check-Zeitstempel
|
||||||
|
let lastChecked = '';
|
||||||
|
if (printer.last_checked) {
|
||||||
|
const checkTime = new Date(printer.last_checked);
|
||||||
|
const now = new Date();
|
||||||
|
const diffMinutes = Math.floor((now - checkTime) / 60000);
|
||||||
|
|
||||||
// Fallback: Versuche normale Drucker-API
|
if (diffMinutes < 1) {
|
||||||
fetch('/api/printers')
|
lastChecked = ' (gerade geprüft)';
|
||||||
.then(response => response.json())
|
} else if (diffMinutes < 60) {
|
||||||
.then(data => {
|
lastChecked = ` (vor ${diffMinutes} Min)`;
|
||||||
const printerSelect = document.getElementById('printer_id');
|
} else {
|
||||||
const printers = data.printers || [];
|
lastChecked = ` (vor ${Math.floor(diffMinutes / 60)} Std)`;
|
||||||
|
}
|
||||||
if (printers.length === 0) {
|
}
|
||||||
printerSelect.innerHTML = '<option value="">Keine Drucker verfügbar</option>';
|
|
||||||
showNotification('Keine Drucker gefunden', 'error');
|
option.textContent = `${statusIcon} ${printer.name} (${printer.model || 'Unbekanntes Modell'}) - ${statusText}${lastChecked}`;
|
||||||
return;
|
|
||||||
}
|
// Tooltip für zusätzliche Informationen
|
||||||
|
option.title = `Standort: ${printer.location || 'Unbekannt'}\nIP: ${printer.plug_ip || printer.ip_address || 'Unbekannt'}\nStatus: ${statusText}${lastChecked}`;
|
||||||
printers.forEach(printer => {
|
|
||||||
const option = document.createElement('option');
|
printerSelect.appendChild(option);
|
||||||
option.value = printer.id;
|
});
|
||||||
option.textContent = `${printer.name} (${printer.model || 'Unbekanntes Modell'}) - Status unbekannt`;
|
|
||||||
printerSelect.appendChild(option);
|
// Hinweis für Offline-Drucker
|
||||||
});
|
const offlineCount = printers.filter(p => !(p.status === 'available' || p.is_online || p.active)).length;
|
||||||
|
if (offlineCount > 0) {
|
||||||
showNotification(`${printers.length} Drucker geladen (ohne Status-Check)`, 'warning');
|
const infoOption = document.createElement('option');
|
||||||
})
|
infoOption.disabled = true;
|
||||||
.catch(fallbackError => {
|
infoOption.textContent = `--- ${offlineCount} Drucker offline (nicht verfügbar) ---`;
|
||||||
console.error('Auch Fallback-API fehlgeschlagen:', fallbackError);
|
infoOption.style.fontStyle = 'italic';
|
||||||
showNotification('Fehler beim Laden der Drucker', 'error');
|
infoOption.style.color = '#6b7280';
|
||||||
});
|
printerSelect.appendChild(infoOption);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle Drucker als sekundäre Option laden
|
||||||
|
function loadAllPrintersAsSecondary(onlinePrinters) {
|
||||||
|
fetch('/api/printers/status/live')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const allPrinters = data.printers || [];
|
||||||
|
|
||||||
|
// Prüfe, ob es zusätzliche Drucker gibt, die nicht in der Online-Liste sind
|
||||||
|
const onlineIds = new Set(onlinePrinters.map(p => p.id));
|
||||||
|
const additionalPrinters = allPrinters.filter(p => !onlineIds.has(p.id));
|
||||||
|
|
||||||
|
if (additionalPrinters.length > 0) {
|
||||||
|
// Kombiniere Online- und zusätzliche Drucker
|
||||||
|
const combinedPrinters = [...onlinePrinters, ...additionalPrinters];
|
||||||
|
populatePrinterSelect(combinedPrinters, false);
|
||||||
|
|
||||||
|
const totalOnline = combinedPrinters.filter(p => p.status === 'available' || p.is_online || p.active).length;
|
||||||
|
showNotification(`${totalOnline} von ${combinedPrinters.length} Drucker online`, totalOnline > 0 ? 'success' : 'warning');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fehler beim Laden aller Drucker:', error);
|
||||||
|
// Nicht kritisch, Online-Drucker sind bereits geladen
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drucker mit Live-Status-Check laden (Fallback)
|
||||||
|
function loadPrintersWithLiveStatus() {
|
||||||
|
showNotification('Überprüfe Drucker-Status...', 'info');
|
||||||
|
|
||||||
|
fetch('/api/printers/status/live')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const printers = data.printers || [];
|
||||||
|
console.log('Live-Status-Drucker geladen:', printers);
|
||||||
|
|
||||||
|
if (printers.length === 0) {
|
||||||
|
// Letzter Fallback: Normale Drucker-API
|
||||||
|
return loadPrintersBasic();
|
||||||
|
}
|
||||||
|
|
||||||
|
populatePrinterSelect(printers, false);
|
||||||
|
|
||||||
|
const onlineCount = printers.filter(p => p.status === 'available' || p.is_online || p.active).length;
|
||||||
|
if (onlineCount > 0) {
|
||||||
|
showNotification(`${onlineCount} von ${printers.length} Drucker online (Live-Check)`, 'success');
|
||||||
|
} else {
|
||||||
|
showNotification(`${printers.length} Drucker gefunden, aber alle offline`, 'warning');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Fehler beim Live-Status-Check:', error);
|
||||||
|
showNotification('Live-Status-Check fehlgeschlagen, lade Basis-Daten...', 'warning');
|
||||||
|
loadPrintersBasic();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Basis-Drucker-Laden (letzter Fallback)
|
||||||
|
function loadPrintersBasic() {
|
||||||
|
fetch('/api/printers')
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
const printers = data.printers || [];
|
||||||
|
console.log('Basis-Drucker geladen:', printers);
|
||||||
|
|
||||||
|
if (printers.length === 0) {
|
||||||
|
const printerSelect = document.getElementById('printer_id');
|
||||||
|
printerSelect.innerHTML = '<option value="">Keine Drucker in der Datenbank</option>';
|
||||||
|
printerSelect.disabled = true;
|
||||||
|
showNotification('Keine Drucker in der Datenbank gefunden', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alle Drucker als "Status unbekannt" anzeigen
|
||||||
|
const printersWithUnknownStatus = printers.map(printer => ({
|
||||||
|
...printer,
|
||||||
|
status: 'unknown',
|
||||||
|
is_online: false,
|
||||||
|
active: true // Erlaube Auswahl trotz unbekanntem Status
|
||||||
|
}));
|
||||||
|
|
||||||
|
populatePrinterSelect(printersWithUnknownStatus, false);
|
||||||
|
showNotification(`${printers.length} Drucker geladen (Status unbekannt)`, 'warning');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('Auch Basis-API fehlgeschlagen:', error);
|
||||||
|
const printerSelect = document.getElementById('printer_id');
|
||||||
|
printerSelect.innerHTML = '<option value="">Fehler beim Laden der Drucker</option>';
|
||||||
|
printerSelect.disabled = true;
|
||||||
|
showNotification('Fehler beim Laden der Drucker', 'error');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -723,7 +841,7 @@ function renderJobCard(job) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Admin: Beenden-Button für laufende Jobs
|
// Admin: Beenden-Button für laufende Jobs
|
||||||
if (job.status === 'running' && isAdmin) {
|
if (job.status === 'running' && window.isAdmin) {
|
||||||
actionButtons += `
|
actionButtons += `
|
||||||
<button type="button" onclick="finishJob(${job.id})"
|
<button type="button" onclick="finishJob(${job.id})"
|
||||||
class="text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 text-sm font-medium transition-colors duration-200">
|
class="text-red-600 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300 text-sm font-medium transition-colors duration-200">
|
||||||
@@ -978,7 +1096,6 @@ function formatDateTime(isoString) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Globale Variable für Admin-Status
|
// Globale Variable für Admin-Status wird über window.isAdmin gesetzt
|
||||||
const isAdmin = {% if current_user.is_admin %}true{% else %}false{% endif %};
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
@@ -2,22 +2,63 @@
|
|||||||
|
|
||||||
{% block title %}Drucker - MYP Platform{% endblock %}
|
{% block title %}Drucker - MYP Platform{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_css %}
|
||||||
|
<link href="{{ url_for('static', filename='css/printers.css') }}" rel="stylesheet">
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="max-w-7xl mx-auto px-3 sm:px-6 lg:px-8 py-4 sm:py-8">
|
<div class="max-w-7xl mx-auto px-3 sm:px-6 lg:px-8 py-4 sm:py-8">
|
||||||
<!-- Header -->
|
<!-- Header mit Status-Übersicht -->
|
||||||
<div class="mb-4 sm:mb-8">
|
<div class="mb-4 sm:mb-8">
|
||||||
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
<div class="flex flex-col sm:flex-row sm:items-center sm:justify-between">
|
||||||
<div class="mb-4 sm:mb-0">
|
<div class="mb-4 sm:mb-0">
|
||||||
<h1 class="text-2xl sm:text-3xl font-bold text-slate-900 dark:text-white">Drucker</h1>
|
<h1 class="text-2xl sm:text-3xl font-bold text-slate-900 dark:text-white">Drucker</h1>
|
||||||
<p class="mt-1 sm:mt-2 text-sm sm:text-base text-slate-600 dark:text-slate-400">Verwalten Sie Ihre 3D-Drucker</p>
|
<p class="mt-1 sm:mt-2 text-sm sm:text-base text-slate-600 dark:text-slate-400">Verwalten Sie Ihre 3D-Drucker</p>
|
||||||
|
|
||||||
|
<!-- Live-Status-Übersicht -->
|
||||||
|
<div id="status-overview" class="mt-3 flex flex-wrap gap-2 text-xs sm:text-sm">
|
||||||
|
<div class="flex items-center space-x-1">
|
||||||
|
<div class="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">Online: <span id="online-count" class="font-semibold text-green-600 dark:text-green-400">-</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-1">
|
||||||
|
<div class="w-2 h-2 bg-red-500 rounded-full"></div>
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">Offline: <span id="offline-count" class="font-semibold text-red-600 dark:text-red-400">-</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-1">
|
||||||
|
<div class="w-2 h-2 bg-blue-500 rounded-full"></div>
|
||||||
|
<span class="text-slate-600 dark:text-slate-400">Gesamt: <span id="total-count" class="font-semibold text-blue-600 dark:text-blue-400">-</span></span>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-1 ml-2">
|
||||||
|
<svg id="auto-refresh-icon" class="w-3 h-3 text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
|
</svg>
|
||||||
|
<span class="text-xs text-slate-500 dark:text-slate-400">Auto-Update: <span id="next-update-time">-</span>s</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-wrap gap-2 sm:space-x-4">
|
<div class="flex flex-wrap gap-2 sm:space-x-4">
|
||||||
|
<!-- Filter-Buttons -->
|
||||||
|
<div class="flex bg-slate-100 dark:bg-slate-700 rounded-lg p-1">
|
||||||
|
<button id="filter-all" class="filter-btn active px-3 py-1 text-xs rounded-md transition-all duration-200">Alle</button>
|
||||||
|
<button id="filter-online" class="filter-btn px-3 py-1 text-xs rounded-md transition-all duration-200">Online</button>
|
||||||
|
<button id="filter-offline" class="filter-btn px-3 py-1 text-xs rounded-md transition-all duration-200">Offline</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick="toggleAutoRefresh()" id="auto-refresh-btn" class="flex-1 sm:flex-none bg-blue-600 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700 text-white px-3 sm:px-4 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-center justify-center">
|
||||||
|
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
Auto-Update
|
||||||
|
</button>
|
||||||
|
|
||||||
<button onclick="refreshPrinters()" class="flex-1 sm:flex-none bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-600 dark:hover:bg-indigo-700 text-white px-3 sm:px-4 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-center justify-center">
|
<button onclick="refreshPrinters()" class="flex-1 sm:flex-none bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-600 dark:hover:bg-indigo-700 text-white px-3 sm:px-4 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-center justify-center">
|
||||||
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
</svg>
|
</svg>
|
||||||
Aktualisieren
|
Jetzt aktualisieren
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
{% if current_user.is_admin %}
|
{% if current_user.is_admin %}
|
||||||
<button id="addPrinterBtn" class="flex-1 sm:flex-none bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700 text-white px-3 sm:px-4 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-center justify-center">
|
<button id="addPrinterBtn" class="flex-1 sm:flex-none bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-700 text-white px-3 sm:px-4 py-2 rounded-lg transition-all duration-200 text-sm sm:text-base flex items-center justify-center">
|
||||||
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
@@ -143,6 +184,11 @@
|
|||||||
<script>
|
<script>
|
||||||
// Globale Variablen für die Drucker-Verwaltung
|
// Globale Variablen für die Drucker-Verwaltung
|
||||||
let printers = [];
|
let printers = [];
|
||||||
|
let currentFilter = 'all';
|
||||||
|
let autoRefreshEnabled = false;
|
||||||
|
let autoRefreshInterval = null;
|
||||||
|
let nextUpdateCountdown = null;
|
||||||
|
let nextUpdateTime = 30; // Sekunden bis zum nächsten Auto-Update
|
||||||
|
|
||||||
// Refresh printers mit Status-Check - Make it globally available
|
// Refresh printers mit Status-Check - Make it globally available
|
||||||
function refreshPrinters() {
|
function refreshPrinters() {
|
||||||
@@ -171,8 +217,8 @@
|
|||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Drucker laden mit Status-Check
|
// Drucker laden mit Live-Status-Check
|
||||||
loadPrintersWithStatusCheck().finally(() => {
|
loadPrintersWithLiveStatus().finally(() => {
|
||||||
// Button wieder aktivieren
|
// Button wieder aktivieren
|
||||||
if (refreshBtn) {
|
if (refreshBtn) {
|
||||||
refreshBtn.disabled = false;
|
refreshBtn.disabled = false;
|
||||||
@@ -180,7 +226,7 @@
|
|||||||
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
</svg>
|
</svg>
|
||||||
Aktualisieren
|
Jetzt aktualisieren
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -338,22 +384,46 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render printers grid
|
// Render printers grid mit Filter-Unterstützung
|
||||||
function renderPrinters() {
|
function renderPrinters() {
|
||||||
const grid = document.getElementById('printers-grid');
|
const grid = document.getElementById('printers-grid');
|
||||||
|
|
||||||
if (printers.length === 0) {
|
// Filter anwenden
|
||||||
|
let filteredPrinters = printers;
|
||||||
|
if (currentFilter === 'online') {
|
||||||
|
filteredPrinters = printers.filter(p => p.status === 'available' || p.is_online);
|
||||||
|
} else if (currentFilter === 'offline') {
|
||||||
|
filteredPrinters = printers.filter(p => p.status === 'offline' || !p.is_online);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status-Übersicht aktualisieren falls nicht bereits gesetzt
|
||||||
|
if (!document.getElementById('online-count').textContent || document.getElementById('online-count').textContent === '-') {
|
||||||
|
const onlineCount = printers.filter(p => p.status === 'available' || p.is_online).length;
|
||||||
|
const offlineCount = printers.length - onlineCount;
|
||||||
|
updateStatusOverview(onlineCount, offlineCount, printers.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filteredPrinters.length === 0) {
|
||||||
|
let emptyMessage = 'Keine Drucker vorhanden';
|
||||||
|
if (currentFilter === 'online') {
|
||||||
|
emptyMessage = 'Keine Online-Drucker gefunden';
|
||||||
|
} else if (currentFilter === 'offline') {
|
||||||
|
emptyMessage = 'Keine Offline-Drucker gefunden';
|
||||||
|
}
|
||||||
|
|
||||||
grid.innerHTML = `
|
grid.innerHTML = `
|
||||||
<div class="col-span-full text-center py-6 sm:py-12">
|
<div class="col-span-full text-center py-6 sm:py-12">
|
||||||
<svg class="h-12 w-12 sm:h-16 sm:w-16 text-slate-400 dark:text-slate-500 mx-auto mb-3 sm:mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="h-12 w-12 sm:h-16 sm:w-16 text-slate-400 dark:text-slate-500 mx-auto mb-3 sm:mb-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z" />
|
||||||
</svg>
|
</svg>
|
||||||
<p class="text-slate-700 dark:text-slate-300 text-base sm:text-lg">Keine Drucker vorhanden</p>
|
<p class="text-slate-700 dark:text-slate-300 text-base sm:text-lg">${emptyMessage}</p>
|
||||||
{% if current_user.is_admin %}
|
${currentFilter === 'all' && printers.length === 0 ? `
|
||||||
<button id="addFirstPrinterBtn" class="mt-3 sm:mt-4 bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-500 text-white px-4 sm:px-6 py-1.5 sm:py-2 rounded-lg transition-all duration-200 text-sm sm:text-base">
|
{% if current_user.is_admin %}
|
||||||
Ersten Drucker hinzufügen
|
<button id="addFirstPrinterBtn" class="mt-3 sm:mt-4 bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-500 text-white px-4 sm:px-6 py-1.5 sm:py-2 rounded-lg transition-all duration-200 text-sm sm:text-base">
|
||||||
</button>
|
Ersten Drucker hinzufügen
|
||||||
{% endif %}
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
` : ''}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@@ -366,43 +436,55 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
grid.innerHTML = printers.map(printer => {
|
grid.innerHTML = filteredPrinters.map(printer => {
|
||||||
const statusColor = getPrinterStatusColor(printer.status);
|
const statusColor = getPrinterStatusColor(printer.status);
|
||||||
const statusText = getPrinterStatusText(printer.status);
|
const statusText = getPrinterStatusText(printer.status);
|
||||||
|
const isOnline = printer.status === 'available' || printer.is_online;
|
||||||
|
|
||||||
|
// Spezielle Styling für Online-Drucker
|
||||||
|
const cardClasses = isOnline
|
||||||
|
? 'bg-gradient-to-br from-green-50 to-white dark:from-green-900/20 dark:to-slate-800 border-green-200 dark:border-green-700 shadow-green-100 dark:shadow-green-900/20'
|
||||||
|
: 'bg-white dark:bg-slate-800 border-slate-200 dark:border-slate-700';
|
||||||
|
|
||||||
|
const onlineIndicator = isOnline
|
||||||
|
? '<div class="absolute top-2 right-2 w-3 h-3 bg-green-500 rounded-full animate-pulse shadow-lg"></div>'
|
||||||
|
: '';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="bg-white dark:bg-slate-800 rounded-xl p-4 sm:p-6 shadow-sm hover:shadow-lg transition-shadow duration-200 border border-slate-200 dark:border-slate-700">
|
<div class="relative ${cardClasses} rounded-xl p-4 sm:p-6 shadow-sm hover:shadow-lg transition-all duration-200 border ${isOnline ? 'hover:shadow-green-200 dark:hover:shadow-green-900/30' : ''}">
|
||||||
|
${onlineIndicator}
|
||||||
|
|
||||||
<div class="flex items-start justify-between mb-3 sm:mb-4">
|
<div class="flex items-start justify-between mb-3 sm:mb-4">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<h3 class="text-base sm:text-lg font-bold text-slate-900 dark:text-white">${printer.name}</h3>
|
<h3 class="text-base sm:text-lg font-bold ${isOnline ? 'text-green-900 dark:text-green-100' : 'text-slate-900 dark:text-white'}">${printer.name}</h3>
|
||||||
<p class="text-xs sm:text-sm text-slate-600 dark:text-slate-400">${printer.model}</p>
|
<p class="text-xs sm:text-sm ${isOnline ? 'text-green-700 dark:text-green-300' : 'text-slate-600 dark:text-slate-400'}">${printer.model}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col items-end">
|
<div class="flex flex-col items-end">
|
||||||
<span class="inline-flex items-center px-2 py-0.5 sm:px-2.5 sm:py-0.5 rounded-full text-xs font-medium ${statusColor}">
|
<span class="inline-flex items-center px-2 py-0.5 sm:px-2.5 sm:py-0.5 rounded-full text-xs font-medium ${statusColor}">
|
||||||
${statusText}
|
${isOnline ? '🟢 ' : '🔴 '}${statusText}
|
||||||
</span>
|
</span>
|
||||||
${printer.last_checked ? `<span class="text-xs text-slate-500 dark:text-slate-400 mt-1">Geprüft: ${formatTime(printer.last_checked)}</span>` : ''}
|
${printer.last_checked ? `<span class="text-xs ${isOnline ? 'text-green-600 dark:text-green-400' : 'text-slate-500 dark:text-slate-400'} mt-1">Geprüft: ${formatTime(printer.last_checked)}</span>` : ''}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="space-y-1.5 sm:space-y-2 mb-3 sm:mb-4">
|
<div class="space-y-1.5 sm:space-y-2 mb-3 sm:mb-4">
|
||||||
<div class="flex items-center text-xs sm:text-sm text-slate-600 dark:text-slate-400">
|
<div class="flex items-center text-xs sm:text-sm ${isOnline ? 'text-green-700 dark:text-green-300' : 'text-slate-600 dark:text-slate-400'}">
|
||||||
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 text-slate-500 dark:text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 ${isOnline ? 'text-green-600 dark:text-green-400' : 'text-slate-500 dark:text-slate-400'}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z" />
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||||
</svg>
|
</svg>
|
||||||
${printer.location}
|
${printer.location}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center text-xs sm:text-sm text-slate-600 dark:text-slate-400">
|
<div class="flex items-center text-xs sm:text-sm ${isOnline ? 'text-green-700 dark:text-green-300' : 'text-slate-600 dark:text-slate-400'}">
|
||||||
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 text-slate-500 dark:text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 ${isOnline ? 'text-green-600 dark:text-green-400' : 'text-slate-500 dark:text-slate-400'}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||||
</svg>
|
</svg>
|
||||||
${printer.mac_address}
|
${printer.mac_address}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center text-xs sm:text-sm text-slate-600 dark:text-slate-400">
|
<div class="flex items-center text-xs sm:text-sm ${isOnline ? 'text-green-700 dark:text-green-300' : 'text-slate-600 dark:text-slate-400'}">
|
||||||
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 text-slate-500 dark:text-slate-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
<svg class="h-3.5 w-3.5 sm:h-4 sm:w-4 mr-1.5 sm:mr-2 ${isOnline ? 'text-green-600 dark:text-green-400' : 'text-slate-500 dark:text-slate-400'}" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9v-9m0-9v9" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9v-9m0-9v9" />
|
||||||
</svg>
|
</svg>
|
||||||
${printer.plug_ip}
|
${printer.plug_ip}
|
||||||
@@ -410,7 +492,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex space-x-2">
|
<div class="flex space-x-2">
|
||||||
<button class="printer-detail-btn flex-1 bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-600 dark:hover:bg-indigo-500 text-white py-1.5 sm:py-2 px-2 sm:px-3 rounded-lg text-xs sm:text-sm transition-all duration-200" data-printer-id="${printer.id}">
|
<button class="printer-detail-btn flex-1 ${isOnline ? 'bg-green-600 hover:bg-green-700 dark:bg-green-600 dark:hover:bg-green-500' : 'bg-indigo-600 hover:bg-indigo-700 dark:bg-indigo-600 dark:hover:bg-indigo-500'} text-white py-1.5 sm:py-2 px-2 sm:px-3 rounded-lg text-xs sm:text-sm transition-all duration-200" data-printer-id="${printer.id}">
|
||||||
Details
|
Details
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@@ -720,14 +802,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Erweiterte Funktion zum Laden der Drucker mit Status-Check
|
// Erweiterte Funktion zum Laden der Drucker mit Live-Status
|
||||||
async function loadPrintersWithStatusCheck() {
|
async function loadPrintersWithLiveStatus() {
|
||||||
try {
|
try {
|
||||||
// Erstelle einen AbortController für Timeout
|
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30 Sekunden Timeout für Status-Check
|
const timeoutId = setTimeout(() => controller.abort(), 20000); // 20 Sekunden Timeout für Live-Status
|
||||||
|
|
||||||
const response = await fetch('/api/printers/status', {
|
const response = await fetch('/api/printers/status/live', {
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
@@ -739,54 +820,205 @@
|
|||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
if (response.status === 408) {
|
if (response.status === 408) {
|
||||||
throw new Error('Timeout beim Status-Check der Drucker. Versuchen Sie es später erneut.');
|
throw new Error('Timeout beim Live-Status-Check der Drucker.');
|
||||||
}
|
}
|
||||||
throw new Error(`Fehler beim Laden der Drucker-Status: ${response.status} ${response.statusText}`);
|
throw new Error(`Fehler beim Laden des Live-Status: ${response.status} ${response.statusText}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusData = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
// Prüfe ob statusData ein Array ist
|
if (data.error) {
|
||||||
if (!Array.isArray(statusData)) {
|
throw new Error(data.error);
|
||||||
console.error('Invalid response from /api/printers/status:', statusData);
|
|
||||||
throw new Error('Ungültige Antwort vom Server (erwartet Array, erhalten: ' + typeof statusData + ')');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drucker-Daten mit Status-Informationen anreichern
|
// Drucker-Daten aktualisieren
|
||||||
printers = statusData.map(printer => ({
|
printers = data.printers || [];
|
||||||
...printer,
|
|
||||||
// Status ist bereits korrekt gemappt vom Backend
|
|
||||||
status: printer.status || 'offline',
|
|
||||||
last_checked: printer.last_checked || new Date().toISOString()
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
// Status-Übersicht aktualisieren
|
||||||
|
updateStatusOverview(data.online_count, data.offline_count, data.count);
|
||||||
|
|
||||||
|
// Drucker rendern
|
||||||
renderPrinters();
|
renderPrinters();
|
||||||
|
|
||||||
// Erfolgs-Nachricht anzeigen
|
// Auto-Update-Timer aktualisieren
|
||||||
const onlineCount = printers.filter(p => p.status === 'available').length;
|
if (data.next_update) {
|
||||||
const totalCount = printers.length;
|
nextUpdateTime = data.next_update;
|
||||||
|
updateNextUpdateDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
if (totalCount > 0) {
|
// Erfolgs-Nachricht nur bei manueller Aktualisierung
|
||||||
|
if (!autoRefreshEnabled) {
|
||||||
showStatusMessage(
|
showStatusMessage(
|
||||||
`Status-Check abgeschlossen: ${onlineCount} von ${totalCount} Drucker verfügbar`,
|
`Live-Status aktualisiert: ${data.online_count} von ${data.count} Drucker online`,
|
||||||
onlineCount > 0 ? 'success' : 'warning'
|
data.online_count > 0 ? 'success' : 'warning'
|
||||||
);
|
);
|
||||||
} else {
|
|
||||||
showStatusMessage('Keine Drucker gefunden', 'info');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading printer status:', error);
|
console.error('Error loading live printer status:', error);
|
||||||
showStatusMessage('Fehler beim Überprüfen der Drucker-Status: ' + error.message, 'error');
|
if (!autoRefreshEnabled) {
|
||||||
|
showStatusMessage('Fehler beim Live-Status-Check: ' + error.message, 'error');
|
||||||
|
}
|
||||||
// Fallback: Lade normale Drucker-Liste
|
// Fallback: Lade normale Drucker-Liste
|
||||||
loadPrinters();
|
loadPrinters();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nur Online-Drucker laden (schnell)
|
||||||
|
async function loadOnlinePrinters() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/printers/online');
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Fehler beim Laden der Online-Drucker: ${response.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
throw new Error(data.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.printers || [];
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading online printers:', error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Status-Übersicht aktualisieren
|
||||||
|
function updateStatusOverview(onlineCount, offlineCount, totalCount) {
|
||||||
|
document.getElementById('online-count').textContent = onlineCount || 0;
|
||||||
|
document.getElementById('offline-count').textContent = offlineCount || 0;
|
||||||
|
document.getElementById('total-count').textContent = totalCount || 0;
|
||||||
|
|
||||||
|
// Animiere die Online-Anzeige bei Änderungen
|
||||||
|
const onlineElement = document.getElementById('online-count');
|
||||||
|
if (onlineElement.dataset.lastValue !== String(onlineCount)) {
|
||||||
|
onlineElement.classList.add('animate-pulse');
|
||||||
|
setTimeout(() => onlineElement.classList.remove('animate-pulse'), 1000);
|
||||||
|
onlineElement.dataset.lastValue = String(onlineCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-Refresh-Funktionalität
|
||||||
|
function toggleAutoRefresh() {
|
||||||
|
autoRefreshEnabled = !autoRefreshEnabled;
|
||||||
|
const btn = document.getElementById('auto-refresh-btn');
|
||||||
|
const icon = document.getElementById('auto-refresh-icon');
|
||||||
|
|
||||||
|
if (autoRefreshEnabled) {
|
||||||
|
btn.classList.remove('bg-blue-600', 'hover:bg-blue-700');
|
||||||
|
btn.classList.add('bg-green-600', 'hover:bg-green-700');
|
||||||
|
btn.innerHTML = `
|
||||||
|
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2 animate-spin" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||||
|
</svg>
|
||||||
|
Auto-Update AN
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Starte Auto-Refresh
|
||||||
|
startAutoRefresh();
|
||||||
|
showStatusMessage('Auto-Update aktiviert - Drucker werden alle 30 Sekunden aktualisiert', 'info');
|
||||||
|
} else {
|
||||||
|
btn.classList.remove('bg-green-600', 'hover:bg-green-700');
|
||||||
|
btn.classList.add('bg-blue-600', 'hover:bg-blue-700');
|
||||||
|
btn.innerHTML = `
|
||||||
|
<svg class="h-4 w-4 sm:h-5 sm:w-5 mr-1 sm:mr-2" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
Auto-Update
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Stoppe Auto-Refresh
|
||||||
|
stopAutoRefresh();
|
||||||
|
showStatusMessage('Auto-Update deaktiviert', 'info');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startAutoRefresh() {
|
||||||
|
stopAutoRefresh(); // Stoppe vorherige Intervalle
|
||||||
|
|
||||||
|
nextUpdateTime = 30;
|
||||||
|
updateNextUpdateDisplay();
|
||||||
|
|
||||||
|
// Countdown-Timer
|
||||||
|
nextUpdateCountdown = setInterval(() => {
|
||||||
|
nextUpdateTime--;
|
||||||
|
updateNextUpdateDisplay();
|
||||||
|
|
||||||
|
if (nextUpdateTime <= 0) {
|
||||||
|
loadPrintersWithLiveStatus();
|
||||||
|
nextUpdateTime = 30;
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
// Auto-Refresh-Interval
|
||||||
|
autoRefreshInterval = setInterval(() => {
|
||||||
|
loadPrintersWithLiveStatus();
|
||||||
|
}, 30000); // Alle 30 Sekunden
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopAutoRefresh() {
|
||||||
|
if (autoRefreshInterval) {
|
||||||
|
clearInterval(autoRefreshInterval);
|
||||||
|
autoRefreshInterval = null;
|
||||||
|
}
|
||||||
|
if (nextUpdateCountdown) {
|
||||||
|
clearInterval(nextUpdateCountdown);
|
||||||
|
nextUpdateCountdown = null;
|
||||||
|
}
|
||||||
|
document.getElementById('next-update-time').textContent = '-';
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNextUpdateDisplay() {
|
||||||
|
const element = document.getElementById('next-update-time');
|
||||||
|
if (autoRefreshEnabled && nextUpdateTime > 0) {
|
||||||
|
element.textContent = nextUpdateTime;
|
||||||
|
element.parentElement.style.opacity = '1';
|
||||||
|
} else {
|
||||||
|
element.textContent = '-';
|
||||||
|
element.parentElement.style.opacity = '0.5';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter-Funktionalität
|
||||||
|
function setupFilters() {
|
||||||
|
const filterButtons = document.querySelectorAll('.filter-btn');
|
||||||
|
|
||||||
|
filterButtons.forEach(btn => {
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
// Entferne active-Klasse von allen Buttons
|
||||||
|
filterButtons.forEach(b => {
|
||||||
|
b.classList.remove('active', 'bg-white', 'dark:bg-slate-600', 'shadow-sm');
|
||||||
|
b.classList.add('text-slate-600', 'dark:text-slate-400');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Füge active-Klasse zum geklickten Button hinzu
|
||||||
|
this.classList.add('active', 'bg-white', 'dark:bg-slate-600', 'shadow-sm');
|
||||||
|
this.classList.remove('text-slate-600', 'dark:text-slate-400');
|
||||||
|
|
||||||
|
// Setze aktuellen Filter
|
||||||
|
currentFilter = this.id.replace('filter-', '');
|
||||||
|
|
||||||
|
// Rendere Drucker mit Filter
|
||||||
|
renderPrinters();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erweiterte Funktion zum Laden der Drucker mit Status-Check (Legacy-Kompatibilität)
|
||||||
|
async function loadPrintersWithStatusCheck() {
|
||||||
|
return loadPrintersWithLiveStatus();
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Lade Drucker beim Start
|
// Setup Filter-Buttons
|
||||||
loadPrinters();
|
setupFilters();
|
||||||
|
|
||||||
|
// Lade Drucker beim Start mit Live-Status
|
||||||
|
loadPrintersWithLiveStatus();
|
||||||
|
|
||||||
// Event-Listener für den "Drucker hinzufügen" Button
|
// Event-Listener für den "Drucker hinzufügen" Button
|
||||||
const addPrinterBtn = document.getElementById('addPrinterBtn');
|
const addPrinterBtn = document.getElementById('addPrinterBtn');
|
||||||
@@ -837,6 +1069,19 @@
|
|||||||
hidePrinterDetailModal();
|
hidePrinterDetailModal();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Auto-Refresh bei Sichtbarkeitsänderung der Seite
|
||||||
|
document.addEventListener('visibilitychange', function() {
|
||||||
|
if (!document.hidden && autoRefreshEnabled) {
|
||||||
|
// Seite ist wieder sichtbar und Auto-Refresh ist aktiv
|
||||||
|
loadPrintersWithLiveStatus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Cleanup bei Seitenverlassen
|
||||||
|
window.addEventListener('beforeunload', function() {
|
||||||
|
stopAutoRefresh();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Make all functions globally available for onclick handlers
|
// Make all functions globally available for onclick handlers
|
||||||
@@ -845,6 +1090,9 @@
|
|||||||
window.deletePrinter = deletePrinter;
|
window.deletePrinter = deletePrinter;
|
||||||
window.loadPrinters = loadPrinters;
|
window.loadPrinters = loadPrinters;
|
||||||
window.handleAddPrinter = handleAddPrinter;
|
window.handleAddPrinter = handleAddPrinter;
|
||||||
|
window.toggleAutoRefresh = toggleAutoRefresh;
|
||||||
|
window.loadPrintersWithLiveStatus = loadPrintersWithLiveStatus;
|
||||||
|
window.loadOnlinePrinters = loadOnlinePrinters;
|
||||||
|
|
||||||
// Debug: Log that functions are available
|
// Debug: Log that functions are available
|
||||||
console.log('All printer functions loaded and available globally:', {
|
console.log('All printer functions loaded and available globally:', {
|
||||||
|
Reference in New Issue
Block a user