"Refactor guest blueprint templates and database schema"

This commit is contained in:
2025-05-29 16:16:03 +02:00
parent a1f9e70fd2
commit 2e306cb0df
5 changed files with 118 additions and 101 deletions

View File

@@ -4,6 +4,10 @@ import bcrypt
from datetime import datetime, timedelta from datetime import datetime, timedelta
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, abort, session, flash from flask import Blueprint, render_template, request, jsonify, redirect, url_for, abort, session, flash
from flask_login import current_user, login_required from flask_login import current_user, login_required
from flask_wtf import FlaskForm
from flask_wtf.file import FileField, FileAllowed
from wtforms import StringField, TextAreaField, IntegerField, SelectField
from wtforms.validators import DataRequired, Email, Optional, NumberRange
from functools import wraps from functools import wraps
from sqlalchemy import desc from sqlalchemy import desc
from sqlalchemy.orm import joinedload from sqlalchemy.orm import joinedload
@@ -14,6 +18,19 @@ from utils.logging_config import get_logger
guest_blueprint = Blueprint('guest', __name__) guest_blueprint = Blueprint('guest', __name__)
logger = get_logger("guest") logger = get_logger("guest")
# Flask-WTF Formular für Gastanfragen
class GuestRequestForm(FlaskForm):
name = StringField('Vollständiger Name', validators=[DataRequired()])
email = StringField('E-Mail-Adresse', validators=[DataRequired(), Email()])
printer_id = SelectField('Drucker auswählen', coerce=int, validators=[Optional()])
duration_min = IntegerField('Geschätzte Dauer (Minuten)',
validators=[DataRequired(), NumberRange(min=1, max=1440)],
default=60)
reason = TextAreaField('Projektbeschreibung', validators=[Optional()])
file = FileField('3D-Datei hochladen',
validators=[Optional(), FileAllowed(['stl', 'obj', '3mf', 'amf', 'gcode'],
'3D-Dateien sind erlaubt!')])
# Hilfsfunktionen # Hilfsfunktionen
def can_approve_jobs(user_id): def can_approve_jobs(user_id):
"""Prüft, ob ein Benutzer Anfragen genehmigen darf.""" """Prüft, ob ein Benutzer Anfragen genehmigen darf."""

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@@ -13,10 +13,10 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-3 7h3m-3 4h3m-6-4h.01M9 16h.01"/>
</svg> </svg>
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold text-slate-900 dark:text-white tracking-tight">Druckaufträge</h1> <h1 class="text-3xl font-bold text-slate-900 dark:text-white tracking-tight">Druckaufträge</h1>
<p class="text-slate-500 dark:text-slate-400 mt-1">Verwalten Sie Ihre 3D-Druckjobs mit höchster Präzision</p> <p class="text-slate-500 dark:text-slate-400 mt-1">Verwalten Sie Ihre 3D-Druckjobs mit höchster Präzision</p>
</div> </div>
</div> </div>
<div class="flex flex-wrap gap-3"> <div class="flex flex-wrap gap-3">
<button onclick="refreshJobs()" <button onclick="refreshJobs()"
@@ -40,10 +40,10 @@
Erstellen Sie einen neuen Druckauftrag mit professionellen Einstellungen Erstellen Sie einen neuen Druckauftrag mit professionellen Einstellungen
</p> </p>
</div> </div>
<!-- Queue-Status-Anzeige --> <!-- Queue-Status-Anzeige -->
<div id="queue-status-info" class="mb-6"></div> <div id="queue-status-info" class="mb-6"></div>
<form id="newJobForm" class="space-y-6"> <form id="newJobForm" class="space-y-6">
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Drucker auswählen --> <!-- Drucker auswählen -->
@@ -65,8 +65,8 @@
<div class="flex-shrink-0"> <div class="flex-shrink-0">
<svg class="h-5 w-5 text-orange-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg class="h-5 w-5 text-orange-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg> </svg>
</div> </div>
<div class="ml-3"> <div class="ml-3">
<h3 class="text-sm font-medium text-orange-800 dark:text-orange-400 mb-2"> <h3 class="text-sm font-medium text-orange-800 dark:text-orange-400 mb-2">
Warteschlangen-Modus: Offline-Drucker ausgewählt Warteschlangen-Modus: Offline-Drucker ausgewählt
@@ -75,11 +75,11 @@
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="w-2 h-2 bg-red-500 rounded-full"></div> <div class="w-2 h-2 bg-red-500 rounded-full"></div>
<span>Drucker ist derzeit OFFLINE</span> <span>Drucker ist derzeit OFFLINE</span>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="w-2 h-2 bg-yellow-500 rounded-full"></div> <div class="w-2 h-2 bg-yellow-500 rounded-full"></div>
<span>Job wird in WARTESCHLANGE eingereiht</span> <span>Job wird in WARTESCHLANGE eingereiht</span>
</div> </div>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<div class="w-2 h-2 bg-blue-500 rounded-full"></div> <div class="w-2 h-2 bg-blue-500 rounded-full"></div>
<span>System überwacht alle 2 Minuten</span> <span>System überwacht alle 2 Minuten</span>
@@ -117,33 +117,33 @@
<div class="grid grid-cols-1 gap-6"> <div class="grid grid-cols-1 gap-6">
<!-- Job-Titel --> <!-- Job-Titel -->
<div> <div>
<label for="job_title" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"> <label for="job_title" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
Job-Titel <span class="text-red-500">*</span> Job-Titel <span class="text-red-500">*</span>
</label> </label>
<input type="text" id="job_title" name="job_title" required <input type="text" id="job_title" name="job_title" required
class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500" class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="Geben Sie einen beschreibenden Titel ein"> placeholder="Geben Sie einen beschreibenden Titel ein">
</div> </div>
<!-- STL-Datei hochladen --> <!-- STL-Datei hochladen -->
<div> <div>
<label for="stl_file" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"> <label for="stl_file" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
STL-Datei hochladen (optional) STL-Datei hochladen (optional)
</label> </label>
<div class="flex items-center justify-center w-full"> <div class="flex items-center justify-center w-full">
<label for="stl_file" class="flex flex-col items-center justify-center w-full h-32 border-2 border-gray-300 dark:border-slate-600 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-slate-700/30 hover:bg-gray-100 dark:hover:bg-slate-700/50 transition-colors"> <label for="stl_file" class="flex flex-col items-center justify-center w-full h-32 border-2 border-gray-300 dark:border-slate-600 border-dashed rounded-lg cursor-pointer bg-gray-50 dark:bg-slate-700/30 hover:bg-gray-100 dark:hover:bg-slate-700/50 transition-colors">
<div class="flex flex-col items-center justify-center pt-5 pb-6"> <div class="flex flex-col items-center justify-center pt-5 pb-6">
<svg class="w-8 h-8 mb-3 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-8 h-8 mb-3 text-gray-400 dark:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"/>
</svg> </svg>
<p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">Klicken Sie zum Hochladen</span> oder ziehen Sie die Datei hierher</p> <p class="mb-2 text-sm text-gray-500 dark:text-gray-400"><span class="font-semibold">Klicken Sie zum Hochladen</span> oder ziehen Sie die Datei hierher</p>
<p class="text-xs text-gray-500 dark:text-gray-400">STL-Dateien bis zu 50MB</p> <p class="text-xs text-gray-500 dark:text-gray-400">STL-Dateien bis zu 50MB</p>
</div> </div>
<input id="stl_file" name="stl_file" type="file" accept=".stl" class="hidden" /> <input id="stl_file" name="stl_file" type="file" accept=".stl" class="hidden" />
</label> </label>
</div> </div>
<div id="file-name" class="mt-2 text-sm text-slate-500 dark:text-slate-400 hidden"></div> <div id="file-name" class="mt-2 text-sm text-slate-500 dark:text-slate-400 hidden"></div>
</div> </div>
</div> </div>
@@ -177,10 +177,10 @@
<div class="text-slate-400 dark:text-slate-500 mb-4"> <div class="text-slate-400 dark:text-slate-500 mb-4">
<svg class="w-12 h-12 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor"> <svg class="w-12 h-12 mx-auto" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="1" d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
</svg> </svg>
</div> </div>
<h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-2">Keine aktiven Jobs</h3> <h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-2">Keine aktiven Jobs</h3>
<p class="text-slate-500 dark:text-slate-400">Sie haben derzeit keine aktiven oder geplanten Druckjobs.</p> <p class="text-slate-500 dark:text-slate-400">Sie haben derzeit keine aktiven oder geplanten Druckjobs.</p>
</div> </div>
</div> </div>
</div> </div>
@@ -199,12 +199,12 @@
<button onclick="closeJobModal()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors"> <button onclick="closeJobModal()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors">
<svg class="w-6 h-6 text-slate-500 dark:text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-6 h-6 text-slate-500 dark:text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg> </svg>
</button> </button>
</div> </div>
<div id="jobDetailsContent"> <div id="jobDetailsContent">
<!-- Inhalt wird dynamisch geladen --> <!-- Inhalt wird dynamisch geladen -->
</div> </div>
</div> </div>
</div> </div>
@@ -222,8 +222,8 @@
<button onclick="closeExtendModal()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors"> <button onclick="closeExtendModal()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors">
<svg class="w-6 h-6 text-slate-500 dark:text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-6 h-6 text-slate-500 dark:text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg> </svg>
</button> </button>
</div> </div>
<form id="extendJobForm" class="space-y-6"> <form id="extendJobForm" class="space-y-6">

View File

@@ -16,8 +16,8 @@
<div class="w-12 h-12 flex-shrink-0"> <div class="w-12 h-12 flex-shrink-0">
<svg class="w-full h-full text-slate-900 dark:text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-full h-full text-slate-900 dark:text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>
</svg> </svg>
</div> </div>
<div> <div>
<h1 class="text-3xl font-bold text-slate-900 dark:text-white tracking-tight">3D-Drucker</h1> <h1 class="text-3xl font-bold text-slate-900 dark:text-white tracking-tight">3D-Drucker</h1>
<p class="text-slate-500 dark:text-slate-400 mt-1">Verwaltung und Überwachung Ihrer Produktionseinheiten</p> <p class="text-slate-500 dark:text-slate-400 mt-1">Verwaltung und Überwachung Ihrer Produktionseinheiten</p>
@@ -114,15 +114,15 @@
<select id="filterLocation" class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> <select id="filterLocation" class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<option value="">Alle Standorte</option> <option value="">Alle Standorte</option>
</select> </select>
</div>
</div> </div>
</div> </div>
</div>
<!-- Printers Grid --> <!-- Printers Grid -->
<div id="printers-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div id="printers-grid" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Printers will be loaded here --> <!-- Printers will be loaded here -->
</div> </div>
<!-- Loading State --> <!-- Loading State -->
<div id="loading-state" class="dashboard-card p-8 text-center"> <div id="loading-state" class="dashboard-card p-8 text-center">
<div class="text-slate-400 dark:text-slate-500 mb-4"> <div class="text-slate-400 dark:text-slate-500 mb-4">
@@ -131,14 +131,14 @@
</svg> </svg>
</div> </div>
<p class="text-slate-600 dark:text-slate-400">Lade Drucker...</p> <p class="text-slate-600 dark:text-slate-400">Lade Drucker...</p>
</div> </div>
<!-- Empty State --> <!-- Empty State -->
<div id="empty-state" class="dashboard-card p-8 text-center hidden"> <div id="empty-state" class="dashboard-card p-8 text-center hidden">
<div class="text-slate-400 dark:text-slate-500 mb-4"> <div class="text-slate-400 dark:text-slate-500 mb-4">
<svg class="w-12 h-12 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-12 h-12 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 12h14M5 12a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v4a2 2 0 01-2 2M5 12a2 2 0 00-2 2v4a2 2 0 002 2h14a2 2 0 002-2v-4a2 2 0 00-2-2m-2-4h.01M17 16h.01"/>
</svg> </svg>
</div> </div>
<h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-2">Keine Drucker gefunden</h3> <h3 class="text-lg font-semibold text-slate-900 dark:text-white mb-2">Keine Drucker gefunden</h3>
<p class="text-slate-500 dark:text-slate-400 mb-4"> <p class="text-slate-500 dark:text-slate-400 mb-4">
@@ -146,16 +146,16 @@
</p> </p>
<button onclick="openAddPrinterModal()" class="btn-primary"> <button onclick="openAddPrinterModal()" class="btn-primary">
Ersten Drucker hinzufügen Ersten Drucker hinzufügen
</button> </button>
</div> </div>
</div> </div>
<!-- Add/Edit Printer Modal --> <!-- Add/Edit Printer Modal -->
<div id="printerModal" class="fixed inset-0 bg-black/60 backdrop-blur-sm hidden z-50"> <div id="printerModal" class="fixed inset-0 bg-black/60 backdrop-blur-sm hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4"> <div class="flex items-center justify-center min-h-screen p-4">
<div class="dashboard-card max-w-lg w-full p-8 transform transition-all duration-300 scale-95 opacity-0" id="printerModalContent"> <div class="dashboard-card max-w-lg w-full p-8 transform transition-all duration-300 scale-95 opacity-0" id="printerModalContent">
<div class="flex justify-between items-center mb-6"> <div class="flex justify-between items-center mb-6">
<div> <div>
<h3 id="printerModalTitle" class="text-xl font-bold text-slate-900 dark:text-white mb-2"> <h3 id="printerModalTitle" class="text-xl font-bold text-slate-900 dark:text-white mb-2">
Drucker hinzufügen Drucker hinzufügen
</h3> </h3>
@@ -170,68 +170,68 @@
<form id="printerForm" class="space-y-6"> <form id="printerForm" class="space-y-6">
<input type="hidden" id="printerId" name="printerId"> <input type="hidden" id="printerId" name="printerId">
<div> <div>
<label for="printerName" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"> <label for="printerName" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
Drucker-Name <span class="text-red-500">*</span> Drucker-Name <span class="text-red-500">*</span>
</label> </label>
<input type="text" id="printerName" name="name" required <input type="text" id="printerName" name="name" required
class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500" class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="z.B. Ultimaker S3"> placeholder="z.B. Ultimaker S3">
</div> </div>
<div> <div>
<label for="printerModel" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"> <label for="printerModel" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
Modell Modell
</label> </label>
<input type="text" id="printerModel" name="model" <input type="text" id="printerModel" name="model"
class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500" class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="z.B. Ultimaker S3"> placeholder="z.B. Ultimaker S3">
</div> </div>
<div> <div>
<label for="printerLocation" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"> <label for="printerLocation" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
Standort Standort
</label> </label>
<input type="text" id="printerLocation" name="location" <input type="text" id="printerLocation" name="location"
class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500" class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="z.B. Produktionshalle A"> placeholder="z.B. Produktionshalle A">
</div> </div>
<div> <div>
<label for="printerIP" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2"> <label for="printerIP" class="block text-sm font-medium text-slate-700 dark:text-slate-300 mb-2">
IP-Adresse IP-Adresse
</label> </label>
<input type="text" id="printerIP" name="ip_address" <input type="text" id="printerIP" name="ip_address"
class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500" class="block w-full px-3 py-2 border border-gray-300 dark:border-slate-600 rounded-lg bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="z.B. 192.168.1.100"> placeholder="z.B. 192.168.1.100">
</div> </div>
<div class="flex items-center"> <div class="flex items-center">
<input type="checkbox" id="printerActive" name="active" checked <input type="checkbox" id="printerActive" name="active" checked
class="w-4 h-4 text-blue-600 bg-white dark:bg-slate-800 border-gray-300 dark:border-slate-600 rounded focus:ring-blue-500 focus:ring-2"> class="w-4 h-4 text-blue-600 bg-white dark:bg-slate-800 border-gray-300 dark:border-slate-600 rounded focus:ring-blue-500 focus:ring-2">
<label for="printerActive" class="ml-2 text-sm text-slate-700 dark:text-slate-300"> <label for="printerActive" class="ml-2 text-sm text-slate-700 dark:text-slate-300">
Drucker aktiv Drucker aktiv
</label> </label>
</div> </div>
<div class="flex items-center justify-end gap-3 pt-6 border-t border-gray-200 dark:border-slate-600"> <div class="flex items-center justify-end gap-3 pt-6 border-t border-gray-200 dark:border-slate-600">
<button type="button" onclick="closePrinterModal()" class="btn-secondary"> <button type="button" onclick="closePrinterModal()" class="btn-secondary">
Abbrechen Abbrechen
</button> </button>
<button type="submit" class="btn-primary"> <button type="submit" class="btn-primary">
Speichern Speichern
</button> </button>
<button type="button" id="deletePrinterBtn" onclick="deletePrinter()" style="display: none;" <button type="button" id="deletePrinterBtn" onclick="deletePrinter()" style="display: none;"
class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors"> class="px-4 py-2 bg-red-600 hover:bg-red-700 text-white rounded-lg transition-colors">
Löschen Löschen
</button> </button>
</div> </div>
</form> </form>
</div>
</div> </div>
</div> </div>
</div>
<!-- Printer Details Modal --> <!-- Printer Details Modal -->
<div id="printerDetailsModal" class="fixed inset-0 bg-black/60 backdrop-blur-sm hidden z-50"> <div id="printerDetailsModal" class="fixed inset-0 bg-black/60 backdrop-blur-sm hidden z-50">
<div class="flex items-center justify-center min-h-screen p-4"> <div class="flex items-center justify-center min-h-screen p-4">
@@ -242,20 +242,20 @@
Drucker Details Drucker Details
</h3> </h3>
<p class="text-slate-500 dark:text-slate-400">Detaillierte Informationen über den Drucker</p> <p class="text-slate-500 dark:text-slate-400">Detaillierte Informationen über den Drucker</p>
</div> </div>
<button onclick="closePrinterDetailsModal()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors"> <button onclick="closePrinterDetailsModal()" class="p-2 hover:bg-gray-100 dark:hover:bg-slate-700 rounded-lg transition-colors">
<svg class="w-6 h-6 text-slate-500 dark:text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg class="w-6 h-6 text-slate-500 dark:text-slate-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg> </svg>
</button> </button>
</div> </div>
<div id="printerDetailsContent"> <div id="printerDetailsContent">
<!-- Details will be loaded here --> <!-- Details will be loaded here -->
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script> <script>
let allPrinters = []; let allPrinters = [];
@@ -293,8 +293,8 @@ async function loadPrinters() {
function displayPrinters() { function displayPrinters() {
const grid = document.getElementById('printers-grid'); const grid = document.getElementById('printers-grid');
const emptyState = document.getElementById('empty-state'); const emptyState = document.getElementById('empty-state');
if (filteredPrinters.length === 0) { if (filteredPrinters.length === 0) {
grid.style.display = 'none'; grid.style.display = 'none';
emptyState.classList.remove('hidden'); emptyState.classList.remove('hidden');
return; return;
@@ -310,51 +310,51 @@ function displayPrinters() {
function createPrinterCard(printer) { function createPrinterCard(printer) {
const statusClass = getStatusClass(printer.status); const statusClass = getStatusClass(printer.status);
const statusIcon = getStatusIcon(printer.status); const statusIcon = getStatusIcon(printer.status);
return ` return `
<div class="dashboard-card p-6 border-l-4 ${statusClass.border}"> <div class="dashboard-card p-6 border-l-4 ${statusClass.border}">
<div class="flex justify-between items-start mb-4"> <div class="flex justify-between items-start mb-4">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="w-10 h-10 ${statusClass.bg} rounded-lg flex items-center justify-center"> <div class="w-10 h-10 ${statusClass.bg} rounded-lg flex items-center justify-center">
${statusIcon} ${statusIcon}
</div> </div>
<div> <div>
<h3 class="text-lg font-semibold text-slate-900 dark:text-white">${printer.name}</h3> <h3 class="text-lg font-semibold text-slate-900 dark:text-white">${printer.name}</h3>
<p class="text-sm text-slate-500 dark:text-slate-400">${printer.model || 'Unbekanntes Modell'}</p> <p class="text-sm text-slate-500 dark:text-slate-400">${printer.model || 'Unbekanntes Modell'}</p>
</div> </div>
</div> </div>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${statusClass.badge}"> <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium ${statusClass.badge}">
${printer.status_text || printer.status} ${printer.status_text || printer.status}
</span> </span>
</div> </div>
<div class="space-y-3 mb-4"> <div class="space-y-3 mb-4">
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-500 dark:text-slate-400">Standort:</span> <span class="text-slate-500 dark:text-slate-400">Standort:</span>
<span class="text-slate-900 dark:text-white">${printer.location || 'Nicht angegeben'}</span> <span class="text-slate-900 dark:text-white">${printer.location || 'Nicht angegeben'}</span>
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-500 dark:text-slate-400">IP-Adresse:</span> <span class="text-slate-500 dark:text-slate-400">IP-Adresse:</span>
<span class="text-slate-900 dark:text-white">${printer.ip_address || 'Nicht konfiguriert'}</span> <span class="text-slate-900 dark:text-white">${printer.ip_address || 'Nicht konfiguriert'}</span>
</div> </div>
<div class="flex justify-between text-sm"> <div class="flex justify-between text-sm">
<span class="text-slate-500 dark:text-slate-400">Letzter Job:</span> <span class="text-slate-500 dark:text-slate-400">Letzter Job:</span>
<span class="text-slate-900 dark:text-white">${printer.last_job || 'Kein Job'}</span> <span class="text-slate-900 dark:text-white">${printer.last_job || 'Kein Job'}</span>
</div> </div>
</div> </div>
<div class="flex gap-2"> <div class="flex gap-2">
<button onclick="showPrinterDetails(${printer.id})" <button onclick="showPrinterDetails(${printer.id})"
class="flex-1 px-3 py-2 text-sm bg-gray-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-lg hover:bg-gray-200 dark:hover:bg-slate-600 transition-colors"> class="flex-1 px-3 py-2 text-sm bg-gray-100 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-lg hover:bg-gray-200 dark:hover:bg-slate-600 transition-colors">
Details Details
</button> </button>
<button onclick="editPrinter(${printer.id})" <button onclick="editPrinter(${printer.id})"
class="flex-1 px-3 py-2 text-sm bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 rounded-lg hover:bg-blue-200 dark:hover:bg-blue-900/50 transition-colors"> class="flex-1 px-3 py-2 text-sm bg-blue-100 dark:bg-blue-900/30 text-blue-700 dark:text-blue-400 rounded-lg hover:bg-blue-200 dark:hover:bg-blue-900/50 transition-colors">
Bearbeiten Bearbeiten
</button> </button>
</div> </div>
</div> </div>
`; `;
} }
// Get status styling classes // Get status styling classes
@@ -454,7 +454,7 @@ function openAddPrinterModal() {
document.getElementById('deletePrinterBtn').style.display = 'none'; document.getElementById('deletePrinterBtn').style.display = 'none';
modal.classList.remove('hidden'); modal.classList.remove('hidden');
setTimeout(() => { setTimeout(() => {
modalContent.classList.remove('scale-95', 'opacity-0'); modalContent.classList.remove('scale-95', 'opacity-0');
modalContent.classList.add('scale-100', 'opacity-100'); modalContent.classList.add('scale-100', 'opacity-100');
}, 10); }, 10);
@@ -516,7 +516,7 @@ function refreshPrinters() {
document.getElementById('loading-state').style.display = 'block'; document.getElementById('loading-state').style.display = 'block';
document.getElementById('printers-grid').style.display = 'none'; document.getElementById('printers-grid').style.display = 'none';
document.getElementById('empty-state').classList.add('hidden'); document.getElementById('empty-state').classList.add('hidden');
loadPrinters(); loadPrinters();
} }
function showError(message) { function showError(message) {
@@ -538,22 +538,22 @@ document.getElementById('printerForm').addEventListener('submit', async function
const response = await fetch(url, { const response = await fetch(url, {
method: method, method: method,
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content') 'X-CSRFToken': document.querySelector('meta[name="csrf-token"]').getAttribute('content')
}, },
body: JSON.stringify(data) body: JSON.stringify(data)
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
closePrinterModal(); closePrinterModal();
refreshPrinters(); refreshPrinters();
} else { } else {
showError('Fehler beim Speichern: ' + result.error); showError('Fehler beim Speichern: ' + result.error);
} }
} catch (error) { } catch (error) {
console.error('Error saving printer:', error); console.error('Error saving printer:', error);
showError('Fehler beim Speichern des Druckers'); showError('Fehler beim Speichern des Druckers');
} }