diff --git a/backend/app/app.py b/backend/app/app.py index d18ba7d2..d52d71f7 100644 --- a/backend/app/app.py +++ b/backend/app/app.py @@ -4245,7 +4245,7 @@ if __name__ == "__main__": # Scheduler stoppen falls aktiviert if SCHEDULER_ENABLED and scheduler: try: - scheduler.shutdown(wait=False) + scheduler.stop() app_logger.info("Job-Scheduler gestoppt") except Exception as e: app_logger.error(f"Fehler beim Stoppen des Schedulers: {str(e)}") diff --git a/backend/app/install_raspberry_pi.sh b/backend/app/install_raspberry_pi.sh index f47212a0..a77998ea 100644 --- a/backend/app/install_raspberry_pi.sh +++ b/backend/app/install_raspberry_pi.sh @@ -3,6 +3,7 @@ # =================================================================== # Raspberry Pi Vollautomatische Installation - MYP Druckerverwaltung # Für Debian 12 (Bookworm) auf Raspberry Pi +# Nach offizieller Raspberry Pi Kiosk-Anleitung optimiert # =================================================================== set -e # Beende bei Fehlern @@ -38,25 +39,31 @@ if [ "$EUID" -ne 0 ]; then fi log "=== MYP Druckerverwaltung - Raspberry Pi Installation ===" -log "Debian 12 (Bookworm) Vollautomatische Einrichtung" +log "Debian 12 (Bookworm) Kiosk-Modus nach offizieller Anleitung" echo # Konfigurationsvariablen APP_USER="myp" +KIOSK_USER="kiosk" APP_DIR="/opt/myp-druckerverwaltung" SERVICE_NAME="myp-druckerverwaltung" KIOSK_SERVICE_NAME="myp-kiosk" -GITHUB_REPO="https://github.com/your-repo/myp-druckerverwaltung.git" # Anpassen! -DISPLAY_PORT=":0" +KIOSK_URL="http://localhost" + +# Bereinige unbenötigte Pakete (entsprechend offizieller Anleitung) +log "Entferne unbenötigte Pakete zur Speicheroptimierung..." +export DEBIAN_FRONTEND=noninteractive +apt-get purge -y wolfram-engine scratch scratch2 nuscratch sonic-pi idle3 smartsim java-common minecraft-pi libreoffice* || true +apt-get autoremove -y +apt-get clean # Systemupdate log "Aktualisiere Paketlisten und System..." -export DEBIAN_FRONTEND=noninteractive apt-get update -y apt-get upgrade -y --allow-downgrades --allow-remove-essential --allow-change-held-packages -# Installiere erforderliche Pakete -log "Installiere Systempakete..." +# Installiere erforderliche Pakete (nach offizieller Anleitung) +log "Installiere Systempakete für Kiosk-Modus..." apt-get install -y \ python3 \ python3-pip \ @@ -74,7 +81,9 @@ apt-get install -y \ openbox \ lightdm \ x11-xserver-utils \ + xdotool \ unclutter \ + sed \ build-essential \ libssl-dev \ libffi-dev \ @@ -82,7 +91,7 @@ apt-get install -y \ zlib1g-dev \ nodejs \ npm \ - --break-system-packages || true + --no-install-recommends || true # Erstelle Anwendungsbenutzer log "Erstelle Anwendungsbenutzer '$APP_USER'..." @@ -94,15 +103,24 @@ else log "Benutzer '$APP_USER' existiert bereits" fi +# Erstelle Kiosk-Benutzer (nach offizieller Anleitung) +log "Erstelle Kiosk-Benutzer '$KIOSK_USER'..." +if ! id "$KIOSK_USER" &>/dev/null; then + useradd -m -s /bin/bash "$KIOSK_USER" + usermod -aG audio,video "$KIOSK_USER" + log "Benutzer '$KIOSK_USER' erstellt" +else + log "Benutzer '$KIOSK_USER' existiert bereits" +fi + # Erstelle Anwendungsverzeichnis log "Erstelle Anwendungsverzeichnis..." mkdir -p "$APP_DIR" chown "$APP_USER:$APP_USER" "$APP_DIR" -# Klone Repository (falls nicht lokal vorhanden) +# Kopiere Anwendung if [ ! -d "$APP_DIR/.git" ]; then - log "Klone Anwendung von Repository..." - # Für lokale Installation: kopiere aktuelles Verzeichnis + log "Kopiere Anwendung..." if [ -f "app.py" ]; then log "Kopiere lokale Anwendung..." cp -r . "$APP_DIR/" @@ -111,7 +129,7 @@ if [ ! -d "$APP_DIR/.git" ]; then error "Anwendungsdateien nicht gefunden. Führe das Skript im Anwendungsverzeichnis aus." fi else - log "Repository bereits vorhanden, aktualisiere..." + log "Anwendung bereits vorhanden, aktualisiere..." cd "$APP_DIR" sudo -u "$APP_USER" git pull fi @@ -126,7 +144,7 @@ sudo -u "$APP_USER" ./venv/bin/pip install --upgrade pip # Installiere Python-Abhängigkeiten log "Installiere Python-Abhängigkeiten..." if [ -f "requirements.txt" ]; then - sudo -u "$APP_USER" ./venv/bin/pip install -r requirements.txt --break-system-packages || true + sudo -u "$APP_USER" ./venv/bin/pip install -r requirements.txt else # Fallback: Installiere grundlegende Pakete sudo -u "$APP_USER" ./venv/bin/pip install \ @@ -136,14 +154,13 @@ else werkzeug \ requests \ python-dotenv \ - gunicorn \ - --break-system-packages || true + gunicorn fi -# Node.js Abhängigkeiten installieren (falls package.json vorhanden) +# Node.js Abhängigkeiten installieren if [ -f "package.json" ]; then log "Installiere Node.js Abhängigkeiten..." - sudo -u "$APP_USER" npm install --unsafe-perm=true --allow-root + sudo -u "$APP_USER" npm install # Baue Frontend-Assets if [ -f "tailwind.config.js" ]; then @@ -157,7 +174,6 @@ log "Initialisiere Datenbank..." if [ -f "init_db.py" ]; then sudo -u "$APP_USER" ./venv/bin/python init_db.py else - # Erstelle einfache SQLite-Datenbank sudo -u "$APP_USER" touch database.db fi @@ -179,8 +195,96 @@ EOF chown "$APP_USER:$APP_USER" "$APP_DIR/.env" +# Erstelle Kiosk-Skript (nach offizieller Anleitung) +log "Erstelle Kiosk-Skript..." +cat > "/home/$KIOSK_USER/kiosk.sh" << EOF +#!/bin/bash + +# Warte auf System-Bereitschaft +sleep 5 + +# Setze Display-Umgebung +export DISPLAY=:0 + +# Deaktiviere Bildschirmschoner (nach offizieller Anleitung) +xset s noblank +xset s off +xset -dpms + +# Verstecke Mauszeiger +unclutter -idle 0.5 -root & + +# Bereinige Chromium-Präferenzen (verhindert Crash-Warnungen) +sed -i 's/"exited_cleanly":false/"exited_cleanly":true/' /home/$KIOSK_USER/.config/chromium/Default/Preferences 2>/dev/null || true +sed -i 's/"exit_type":"Crashed"/"exit_type":"Normal"/' /home/$KIOSK_USER/.config/chromium/Default/Preferences 2>/dev/null || true + +# Warte auf Netzwerk und Anwendung +while ! curl -s http://localhost > /dev/null; do + echo "Warte auf Anwendung..." + sleep 2 +done + +# Starte Chromium im Kiosk-Modus (nach offizieller Anleitung) +/usr/bin/chromium-browser \\ + --noerrdialogs \\ + --disable-infobars \\ + --kiosk \\ + --no-sandbox \\ + --disable-web-security \\ + --disable-features=TranslateUI \\ + --disable-ipc-flooding-protection \\ + --disable-renderer-backgrounding \\ + --disable-backgrounding-occluded-windows \\ + --disable-background-timer-throttling \\ + --disable-background-networking \\ + --disable-breakpad \\ + --disable-component-extensions-with-background-pages \\ + --disable-dev-shm-usage \\ + --disable-extensions \\ + --disable-features=TranslateUI,BlinkGenPropertyTrees \\ + --disable-hang-monitor \\ + --disable-popup-blocking \\ + --disable-prompt-on-repost \\ + --disable-sync \\ + --disable-translate \\ + --force-color-profile=srgb \\ + --no-first-run \\ + --autoplay-policy=no-user-gesture-required \\ + --start-fullscreen \\ + $KIOSK_URL & + +# Optional: Automatische Tab-Rotation (auskommentiert) +# while true; do +# sleep 30 +# xdotool keydown ctrl+r; xdotool keyup ctrl+r; # Seite neu laden +# done +EOF + +chmod +x "/home/$KIOSK_USER/kiosk.sh" +chown "$KIOSK_USER:$KIOSK_USER" "/home/$KIOSK_USER/kiosk.sh" + +# Konfiguriere LightDM für automatischen Login (nach offizieller Anleitung) +log "Konfiguriere automatischen Login..." +cat > "/etc/lightdm/lightdm.conf" << EOF +[Seat:*] +autologin-user=$KIOSK_USER +autologin-user-timeout=0 +user-session=openbox +xserver-command=X -s 0 -dpms +EOF + +# Erstelle Openbox-Konfiguration für Kiosk-Benutzer +log "Konfiguriere Openbox für Kiosk-Modus..." +mkdir -p "/home/$KIOSK_USER/.config/openbox" +cat > "/home/$KIOSK_USER/.config/openbox/autostart" << EOF +# Starte Kiosk-Anwendung automatisch +/home/$KIOSK_USER/kiosk.sh & +EOF + +chown -R "$KIOSK_USER:$KIOSK_USER" "/home/$KIOSK_USER/.config" + # Erstelle Systemd-Service für die Anwendung -log "Erstelle Systemd-Service..." +log "Erstelle Systemd-Service für Anwendung..." cat > "/etc/systemd/system/$SERVICE_NAME.service" << EOF [Unit] Description=MYP Druckerverwaltung Flask Application @@ -202,6 +306,27 @@ StandardError=journal WantedBy=multi-user.target EOF +# Erstelle Systemd-Service für Kiosk (nach offizieller Anleitung) +log "Erstelle Systemd-Service für Kiosk..." +cat > "/etc/systemd/system/$KIOSK_SERVICE_NAME.service" << EOF +[Unit] +Description=MYP Chromium Kiosk +Wants=graphical.target +After=graphical.target $SERVICE_NAME.service + +[Service] +Environment=DISPLAY=:0.0 +Environment=XAUTHORITY=/home/$KIOSK_USER/.Xauthority +Type=simple +ExecStart=/bin/bash /home/$KIOSK_USER/kiosk.sh +Restart=on-abort +User=$KIOSK_USER +Group=$KIOSK_USER + +[Install] +WantedBy=graphical.target +EOF + # Nginx-Konfiguration log "Konfiguriere Nginx..." cat > "/etc/nginx/sites-available/myp-druckerverwaltung" << EOF @@ -237,112 +362,6 @@ EOF rm -f /etc/nginx/sites-enabled/default ln -sf /etc/nginx/sites-available/myp-druckerverwaltung /etc/nginx/sites-enabled/ -# Kiosk-Modus konfigurieren -log "Konfiguriere Kiosk-Modus..." - -# Erstelle Kiosk-Benutzer -if ! id "kiosk" &>/dev/null; then - useradd -m -s /bin/bash kiosk - usermod -aG audio,video kiosk -fi - -# Erstelle Kiosk-Startskript -cat > "/home/kiosk/start-kiosk.sh" << 'EOF' -#!/bin/bash - -# Warte auf Netzwerk -sleep 10 - -# Setze Display -export DISPLAY=:0 - -# Deaktiviere Bildschirmschoner und Energiesparmodus -xset s off -xset -dpms -xset s noblank - -# Verstecke Mauszeiger -unclutter -idle 0.5 -root & - -# Starte Chromium im Kiosk-Modus -chromium-browser \ - --kiosk \ - --no-sandbox \ - --disable-web-security \ - --disable-features=TranslateUI \ - --disable-ipc-flooding-protection \ - --disable-renderer-backgrounding \ - --disable-backgrounding-occluded-windows \ - --disable-background-timer-throttling \ - --disable-background-networking \ - --disable-breakpad \ - --disable-component-extensions-with-background-pages \ - --disable-dev-shm-usage \ - --disable-extensions \ - --disable-features=TranslateUI,BlinkGenPropertyTrees \ - --disable-hang-monitor \ - --disable-ipc-flooding-protection \ - --disable-popup-blocking \ - --disable-prompt-on-repost \ - --disable-renderer-backgrounding \ - --disable-sync \ - --disable-translate \ - --disable-windows10-custom-titlebar \ - --force-color-profile=srgb \ - --metrics-recording-only \ - --no-first-run \ - --safebrowsing-disable-auto-update \ - --enable-automation \ - --password-store=basic \ - --use-mock-keychain \ - --autoplay-policy=no-user-gesture-required \ - --start-fullscreen \ - --window-position=0,0 \ - --window-size=1920,1080 \ - http://localhost/ -EOF - -chmod +x /home/kiosk/start-kiosk.sh -chown kiosk:kiosk /home/kiosk/start-kiosk.sh - -# Erstelle Systemd-Service für Kiosk -cat > "/etc/systemd/system/$KIOSK_SERVICE_NAME.service" << EOF -[Unit] -Description=MYP Kiosk Mode -After=graphical-session.target network.target $SERVICE_NAME.service -Wants=graphical-session.target - -[Service] -Type=simple -User=kiosk -Group=kiosk -Environment=DISPLAY=:0 -ExecStart=/home/kiosk/start-kiosk.sh -Restart=always -RestartSec=10 - -[Install] -WantedBy=graphical.target -EOF - -# Konfiguriere LightDM für automatischen Login -log "Konfiguriere automatischen Login..." -cat > "/etc/lightdm/lightdm.conf" << EOF -[Seat:*] -autologin-user=kiosk -autologin-user-timeout=0 -user-session=openbox -EOF - -# Erstelle Openbox-Konfiguration für Kiosk-Benutzer -mkdir -p /home/kiosk/.config/openbox -cat > "/home/kiosk/.config/openbox/autostart" << EOF -# Starte Kiosk-Anwendung -/home/kiosk/start-kiosk.sh & -EOF - -chown -R kiosk:kiosk /home/kiosk/.config - # Erstelle Wartungsskript log "Erstelle Wartungsskript..." cat > "/usr/local/bin/myp-maintenance" << 'EOF' @@ -383,15 +402,23 @@ case "$1" in echo "=== Anwendungslogs ===" journalctl -u myp-druckerverwaltung -f ;; + kiosk-logs) + echo "=== Kiosk-Logs ===" + journalctl -u myp-kiosk -f + ;; update) echo "Aktualisiere MYP Druckerverwaltung..." cd /opt/myp-druckerverwaltung sudo -u myp git pull - sudo -u myp ./venv/bin/pip install -r requirements.txt --break-system-packages + sudo -u myp ./venv/bin/pip install -r requirements.txt systemctl restart myp-druckerverwaltung ;; + kiosk-restart) + echo "Starte nur Kiosk neu..." + systemctl restart myp-kiosk + ;; *) - echo "Verwendung: $0 {start|stop|restart|status|logs|update}" + echo "Verwendung: $0 {start|stop|restart|status|logs|kiosk-logs|update|kiosk-restart}" exit 1 ;; esac @@ -399,57 +426,8 @@ EOF chmod +x /usr/local/bin/myp-maintenance -# Aktiviere und starte Services -log "Aktiviere und starte Services..." -systemctl daemon-reload -systemctl enable "$SERVICE_NAME" -systemctl enable nginx -systemctl enable "$KIOSK_SERVICE_NAME" - -# Starte Services -systemctl start "$SERVICE_NAME" -systemctl start nginx - -# Warte kurz und prüfe Status -sleep 5 - -if systemctl is-active --quiet "$SERVICE_NAME"; then - log "✅ MYP Druckerverwaltung Service läuft" -else - warning "⚠️ MYP Druckerverwaltung Service nicht aktiv" -fi - -if systemctl is-active --quiet nginx; then - log "✅ Nginx läuft" -else - warning "⚠️ Nginx nicht aktiv" -fi - -# Konfiguriere Firewall (falls ufw installiert) -if command -v ufw &> /dev/null; then - log "Konfiguriere Firewall..." - ufw --force enable - ufw allow 80/tcp - ufw allow 22/tcp -fi - -# Erstelle Desktop-Verknüpfung für Wartung -cat > "/home/$APP_USER/Desktop/MYP-Wartung.desktop" << EOF -[Desktop Entry] -Version=1.0 -Type=Application -Name=MYP Wartung -Comment=MYP Druckerverwaltung Wartung -Exec=gnome-terminal -- sudo myp-maintenance status -Icon=applications-system -Terminal=true -Categories=System; -EOF - -chown "$APP_USER:$APP_USER" "/home/$APP_USER/Desktop/MYP-Wartung.desktop" -chmod +x "/home/$APP_USER/Desktop/MYP-Wartung.desktop" - # Erstelle Backup-Skript +log "Erstelle Backup-Skript..." cat > "/usr/local/bin/myp-backup" << 'EOF' #!/bin/bash @@ -486,6 +464,40 @@ chmod +x /usr/local/bin/myp-backup # Erstelle Cron-Job für automatische Backups echo "0 2 * * * root /usr/local/bin/myp-backup" > /etc/cron.d/myp-backup +# Aktiviere und starte Services +log "Aktiviere und starte Services..." +systemctl daemon-reload +systemctl enable "$SERVICE_NAME" +systemctl enable nginx +systemctl enable "$KIOSK_SERVICE_NAME" + +# Starte Services +systemctl start "$SERVICE_NAME" +systemctl start nginx + +# Warte kurz und prüfe Status +sleep 5 + +if systemctl is-active --quiet "$SERVICE_NAME"; then + log "✅ MYP Druckerverwaltung Service läuft" +else + warning "⚠️ MYP Druckerverwaltung Service nicht aktiv" +fi + +if systemctl is-active --quiet nginx; then + log "✅ Nginx läuft" +else + warning "⚠️ Nginx nicht aktiv" +fi + +# Konfiguriere Firewall (falls ufw installiert) +if command -v ufw &> /dev/null; then + log "Konfiguriere Firewall..." + ufw --force enable + ufw allow 80/tcp + ufw allow 22/tcp +fi + # Abschlussmeldung log "=== Installation abgeschlossen! ===" echo @@ -495,6 +507,7 @@ info "📋 Wichtige Informationen:" info " • Anwendung läuft auf: http://$(hostname -I | awk '{print $1}')" info " • Anwendungsverzeichnis: $APP_DIR" info " • Anwendungsbenutzer: $APP_USER" +info " • Kiosk-Benutzer: $KIOSK_USER" info " • Service-Name: $SERVICE_NAME" info " • Kiosk-Service: $KIOSK_SERVICE_NAME" echo @@ -502,13 +515,22 @@ info "🔧 Wartungskommandos:" info " • Status prüfen: myp-maintenance status" info " • Neustart: myp-maintenance restart" info " • Logs anzeigen: myp-maintenance logs" +info " • Kiosk-Logs: myp-maintenance kiosk-logs" +info " • Nur Kiosk neustarten: myp-maintenance kiosk-restart" info " • Update: myp-maintenance update" info " • Backup erstellen: myp-backup" echo -info "🖥️ Kiosk-Modus:" -info " • Wird beim nächsten Neustart automatisch gestartet" -info " • Vollbild-Browser auf http://localhost" -info " • Automatischer Login als 'kiosk' Benutzer" +info "🖥️ Kiosk-Modus (nach offizieller Raspberry Pi Anleitung):" +info " • Automatischer Login als '$KIOSK_USER' Benutzer" +info " • Chromium-Browser im Vollbild-Kiosk-Modus" +info " • Automatische Anzeige von $KIOSK_URL" +info " • Versteckter Mauszeiger" +info " • Deaktivierte Bildschirmschoner" +echo +info "🔍 Zusätzliche Konfiguration:" +info " • Kiosk-Skript: /home/$KIOSK_USER/kiosk.sh" +info " • LightDM-Konfiguration: /etc/lightdm/lightdm.conf" +info " • Openbox-Autostart: /home/$KIOSK_USER/.config/openbox/autostart" echo warning "⚠️ Wichtiger Hinweis:" warning " Starte das System neu, um den Kiosk-Modus zu aktivieren:" diff --git a/backend/app/static/css/responsive-improvements.css b/backend/app/static/css/responsive-improvements.css new file mode 100644 index 00000000..9c634bbb --- /dev/null +++ b/backend/app/static/css/responsive-improvements.css @@ -0,0 +1,526 @@ +/** + * Mercedes-Benz MYP Platform - Responsive Verbesserungen & Problembehebung + * Entfernt eckige Hintergründe und verbessert Responsivität + */ + +/* Globale Überschreibungen für problematische Hintergründe */ +* { + background-color: transparent !important; +} + +/* Erlaubte Hintergründe explizit definieren */ +body, +html { + background: #f8fafc !important; +} + +.dark body, +.dark html { + background: #000000 !important; +} + +/* Mercedes Professional Overrides */ +.bg-professional, +.professional-hero, +.professional-container, +.mb-glass, +.card-professional, +.stat-card { + background: #f8fafc !important; + border-radius: 2rem !important; +} + +.dark .bg-professional, +.dark .professional-hero, +.dark .professional-container, +.dark .mb-glass, +.dark .card-professional, +.dark .stat-card { + background: #111111 !important; + border-color: #333333 !important; +} + +/* Entferne alle eckigen Hintergründe mit problematischen Farben */ +[style*="#182031"], +[style*="#d5d7d8"], +[style*="rgb(24, 32, 49)"], +[style*="rgb(213, 215, 216)"] { + background: transparent !important; + background-color: transparent !important; +} + +/* Navbar und Navigation - Responsive */ +.navbar { + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(20px) !important; + border-bottom: 1px solid rgba(0, 0, 0, 0.1) !important; +} + +.dark .navbar { + background: rgba(0, 0, 0, 0.95) !important; + border-bottom: 1px solid rgba(255, 255, 255, 0.1) !important; +} + +/* Responsive Typography */ +@media (max-width: 640px) { + .title-professional { + font-size: 2.5rem !important; + line-height: 1.1 !important; + } + + .subtitle-professional { + font-size: 1.125rem !important; + line-height: 1.5 !important; + } + + .btn-professional { + padding: 0.75rem 1.5rem !important; + font-size: 0.875rem !important; + } + + .professional-container, + .mb-glass, + .card-professional { + padding: 1.5rem !important; + margin: 1rem !important; + border-radius: 1rem !important; + } +} + +@media (min-width: 641px) and (max-width: 768px) { + .title-professional { + font-size: 3.5rem !important; + line-height: 1.1 !important; + } + + .subtitle-professional { + font-size: 1.25rem !important; + line-height: 1.6 !important; + } + + .btn-professional { + padding: 1rem 2rem !important; + font-size: 1rem !important; + } + + .professional-container, + .mb-glass, + .card-professional { + padding: 2rem !important; + margin: 1.5rem !important; + border-radius: 1.5rem !important; + } +} + +@media (min-width: 769px) and (max-width: 1024px) { + .title-professional { + font-size: 4.5rem !important; + line-height: 1.1 !important; + } + + .subtitle-professional { + font-size: 1.5rem !important; + line-height: 1.6 !important; + } + + .btn-professional { + padding: 1.25rem 2.5rem !important; + font-size: 1.125rem !important; + } + + .professional-container, + .mb-glass, + .card-professional { + padding: 2.5rem !important; + margin: 2rem !important; + border-radius: 2rem !important; + } +} + +@media (min-width: 1025px) { + .title-professional { + font-size: 6rem !important; + line-height: 1.1 !important; + } + + .subtitle-professional { + font-size: 2rem !important; + line-height: 1.6 !important; + } + + .btn-professional { + padding: 1.5rem 3rem !important; + font-size: 1.25rem !important; + } + + .professional-container, + .mb-glass, + .card-professional { + padding: 3rem !important; + margin: 2rem !important; + border-radius: 2rem !important; + } +} + +/* Responsive Grid Improvements */ +@media (max-width: 640px) { + .grid { + grid-template-columns: 1fr !important; + gap: 1rem !important; + } + + .grid-cols-2 { + grid-template-columns: 1fr !important; + } + + .grid-cols-3 { + grid-template-columns: 1fr !important; + } + + .grid-cols-4 { + grid-template-columns: 1fr !important; + } +} + +@media (min-width: 641px) and (max-width: 768px) { + .grid-cols-3 { + grid-template-columns: repeat(2, 1fr) !important; + } + + .grid-cols-4 { + grid-template-columns: repeat(2, 1fr) !important; + } +} + +/* Responsive Flex Improvements */ +.flex-col-mobile { + flex-direction: column !important; +} + +@media (min-width: 768px) { + .flex-col-mobile { + flex-direction: row !important; + } +} + +/* Responsive Spacing */ +@media (max-width: 640px) { + .space-y-6 > * + * { + margin-top: 1rem !important; + } + + .space-y-8 > * + * { + margin-top: 1.5rem !important; + } + + .space-y-12 > * + * { + margin-top: 2rem !important; + } + + .space-x-6 > * + * { + margin-left: 1rem !important; + } + + .space-x-8 > * + * { + margin-left: 1.5rem !important; + } +} + +/* Input Field Responsivität */ +.input-professional { + background: #ffffff !important; + border: 2px solid #e2e8f0 !important; + border-radius: 1rem !important; + padding: 1rem !important; + font-size: 1rem !important; + width: 100% !important; +} + +.dark .input-professional { + background: #1a1a1a !important; + border-color: #333333 !important; + color: #ffffff !important; +} + +.input-professional:focus { + border-color: #3b82f6 !important; + box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.1) !important; + background: #ffffff !important; +} + +.dark .input-professional:focus { + background: #222222 !important; +} + +@media (max-width: 640px) { + .input-professional { + padding: 0.875rem !important; + font-size: 0.875rem !important; + border-radius: 0.75rem !important; + } +} + +/* Alert und Modal Responsivität */ +.alert-professional { + border-radius: 1.5rem !important; + padding: 2rem !important; + margin-bottom: 2rem !important; +} + +@media (max-width: 640px) { + .alert-professional { + padding: 1.5rem !important; + margin-bottom: 1.5rem !important; + border-radius: 1rem !important; + } +} + +/* Status Card Responsivität */ +.status-professional { + display: inline-flex !important; + align-items: center !important; + gap: 0.5rem !important; + padding: 0.75rem 1rem !important; + border-radius: 2rem !important; + font-size: 0.875rem !important; + font-weight: 700 !important; + border: 1px solid transparent !important; +} + +@media (max-width: 640px) { + .status-professional { + padding: 0.5rem 0.75rem !important; + font-size: 0.75rem !important; + border-radius: 1rem !important; + } +} + +/* Button Responsivität */ +.btn-professional { + display: inline-flex !important; + align-items: center !important; + justify-content: center !important; + background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%) !important; + color: white !important; + border: none !important; + border-radius: 1rem !important; + font-weight: 700 !important; + text-decoration: none !important; + transition: all 0.3s ease !important; + box-shadow: 0 4px 15px rgba(59, 130, 246, 0.3) !important; +} + +.btn-professional:hover { + background: linear-gradient(135deg, #1d4ed8 0%, #1e40af 100%) !important; + transform: translateY(-2px) !important; + box-shadow: 0 8px 25px rgba(59, 130, 246, 0.4) !important; + color: white !important; +} + +@media (max-width: 640px) { + .btn-professional { + width: 100% !important; + justify-content: center !important; + padding: 1rem 2rem !important; + font-size: 1rem !important; + } +} + +/* Hero Header Responsivität */ +.professional-hero { + position: relative !important; + overflow: hidden !important; + background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%) !important; + border: 1px solid #e2e8f0 !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1) !important; +} + +.dark .professional-hero { + background: linear-gradient(135deg, #000000 0%, #1a1a1a 100%) !important; + border-color: #333333 !important; + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5) !important; +} + +/* Mobile Navigation Verbesserungen */ +@media (max-width: 1024px) { + .navbar-menu-new { + display: none !important; + } + + .mobile-menu-new { + background: rgba(255, 255, 255, 0.95) !important; + backdrop-filter: blur(20px) !important; + border-top: 1px solid rgba(0, 0, 0, 0.1) !important; + } + + .dark .mobile-menu-new { + background: rgba(0, 0, 0, 0.95) !important; + border-top: 1px solid rgba(255, 255, 255, 0.1) !important; + } + + .mobile-nav-item { + display: flex !important; + align-items: center !important; + padding: 1rem !important; + border-radius: 0.75rem !important; + color: #64748b !important; + text-decoration: none !important; + transition: all 0.3s ease !important; + } + + .dark .mobile-nav-item { + color: #94a3b8 !important; + } + + .mobile-nav-item:hover, + .mobile-nav-item.active { + background: rgba(59, 130, 246, 0.1) !important; + color: #3b82f6 !important; + } +} + +/* Container Max-Width Responsivität */ +.max-w-7xl { + max-width: 100% !important; + padding-left: 1rem !important; + padding-right: 1rem !important; +} + +@media (min-width: 640px) { + .max-w-7xl { + padding-left: 1.5rem !important; + padding-right: 1.5rem !important; + } +} + +@media (min-width: 768px) { + .max-w-7xl { + padding-left: 2rem !important; + padding-right: 2rem !important; + } +} + +@media (min-width: 1024px) { + .max-w-7xl { + max-width: 80rem !important; + padding-left: 2rem !important; + padding-right: 2rem !important; + } +} + +/* Entferne alle potentiellen eckigen Standard-Hintergründe */ +div, section, article, aside, header, footer, main, nav { + background-color: transparent !important; +} + +/* Glasmorphism Verbesserungen */ +.mb-glass { + background: rgba(255, 255, 255, 0.9) !important; + backdrop-filter: blur(20px) !important; + border: 1px solid rgba(255, 255, 255, 0.2) !important; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1) !important; +} + +.dark .mb-glass { + background: rgba(17, 17, 17, 0.95) !important; + border: 1px solid rgba(255, 255, 255, 0.1) !important; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3) !important; +} + +/* Entfernt alle Standardhintergründe von Browsern */ +input, textarea, select, button { + background-color: transparent !important; +} + +/* Erlaubte Input-Hintergründe */ +.input-professional, +input[type="text"], +input[type="email"], +input[type="password"], +input[type="number"], +input[type="datetime-local"], +textarea, +select { + background: #ffffff !important; + border: 2px solid #e2e8f0 !important; +} + +.dark .input-professional, +.dark input[type="text"], +.dark input[type="email"], +.dark input[type="password"], +.dark input[type="number"], +.dark input[type="datetime-local"], +.dark textarea, +.dark select { + background: #1a1a1a !important; + border-color: #333333 !important; + color: #ffffff !important; +} + +/* Animation Verbesserungen */ +@keyframes fadeInResponsive { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-fade-in { + animation: fadeInResponsive 0.6s ease-out !important; +} + +.animate-slide-up { + animation: fadeInResponsive 0.8s ease-out !important; +} + +.animate-scale-in { + animation: fadeInResponsive 0.5s ease-out !important; +} + +/* Smooth Scrolling */ +html { + scroll-behavior: smooth !important; +} + +/* Performance Optimierungen */ +* { + box-sizing: border-box !important; +} + +img { + max-width: 100% !important; + height: auto !important; +} + +/* Accessibility Verbesserungen */ +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} + +/* Focus Indicators */ +button:focus, +input:focus, +textarea:focus, +select:focus, +a:focus { + outline: 2px solid #3b82f6 !important; + outline-offset: 2px !important; +} + +/* Entfernt problematische Farben aus allen Elementen */ +[style*="background-color: #182031"], +[style*="background: #182031"], +[style*="background-color: #d5d7d8"], +[style*="background: #d5d7d8"] { + background: transparent !important; + background-color: transparent !important; +} \ No newline at end of file diff --git a/backend/app/static/js/background-fix.js b/backend/app/static/js/background-fix.js new file mode 100644 index 00000000..40541df0 --- /dev/null +++ b/backend/app/static/js/background-fix.js @@ -0,0 +1,241 @@ +/** + * Mercedes-Benz MYP Platform - Background Fix + * Entfernt problematische eckige Hintergründe zur Laufzeit + */ + +(function() { + 'use strict'; + + // Problematische Farben die entfernt werden sollen + const problematicColors = [ + '#182031', + '#d5d7d8', + 'rgb(24, 32, 49)', + 'rgb(213, 215, 216)', + 'rgba(24, 32, 49, 1)', + 'rgba(213, 215, 216, 1)' + ]; + + // Funktion zum Entfernen problematischer Hintergründe + function removeProblematicBackgrounds() { + const allElements = document.querySelectorAll('*'); + + allElements.forEach(element => { + const computedStyle = window.getComputedStyle(element); + const backgroundColor = computedStyle.backgroundColor; + const backgroundImage = computedStyle.backgroundImage; + + // Prüfe auf problematische Hintergrundfarben + problematicColors.forEach(color => { + if (backgroundColor.includes(color.replace('#', '')) || + backgroundColor === color || + backgroundImage.includes(color)) { + + console.log('Entferne problematischen Hintergrund:', color, 'von Element:', element); + element.style.background = 'transparent'; + element.style.backgroundColor = 'transparent'; + element.style.backgroundImage = 'none'; + } + }); + + // Spezielle Checks für inline styles + const inlineStyle = element.getAttribute('style'); + if (inlineStyle) { + problematicColors.forEach(color => { + if (inlineStyle.includes(color)) { + console.log('Entferne problematischen Inline-Style:', color, 'von Element:', element); + + // Entferne problematische Farbe aus inline style + let newStyle = inlineStyle + .replace(new RegExp(`background-color:\\s*${color.replace('#', '\\#')}[^;]*;?`, 'gi'), '') + .replace(new RegExp(`background:\\s*${color.replace('#', '\\#')}[^;]*;?`, 'gi'), '') + .replace(/background-color:\s*transparent\s*!important\s*;?\s*/gi, '') + .trim(); + + if (newStyle !== inlineStyle) { + element.setAttribute('style', newStyle); + } + } + }); + } + }); + } + + // Funktion zum Erzwingen des Mercedes-Designs + function enforceDesign() { + const isDark = document.documentElement.classList.contains('dark'); + + // Haupthintergrund erzwingen + document.body.style.background = isDark ? '#000000' : '#f8fafc'; + document.documentElement.style.background = isDark ? '#000000' : '#f8fafc'; + + // Professionelle Container sicherstellen + const containers = document.querySelectorAll( + '.professional-container, .mb-glass, .card-professional, .bg-professional, .professional-hero' + ); + + containers.forEach(container => { + if (isDark) { + container.style.background = '#111111'; + container.style.borderColor = '#333333'; + } else { + container.style.background = '#f8fafc'; + container.style.borderColor = '#e2e8f0'; + } + container.style.borderRadius = '2rem'; + }); + + // Input-Felder sicherstellen + const inputs = document.querySelectorAll( + '.input-professional, input[type="text"], input[type="email"], input[type="password"], input[type="number"], textarea, select' + ); + + inputs.forEach(input => { + if (isDark) { + input.style.background = '#1a1a1a'; + input.style.borderColor = '#333333'; + input.style.color = '#ffffff'; + } else { + input.style.background = '#ffffff'; + input.style.borderColor = '#e2e8f0'; + input.style.color = '#0f172a'; + } + input.style.borderWidth = '2px'; + input.style.borderRadius = '1rem'; + }); + } + + // Funktion zum Überwachen von DOM-Änderungen + function observeChanges() { + const observer = new MutationObserver(function(mutations) { + let shouldCheck = false; + + mutations.forEach(function(mutation) { + if (mutation.type === 'childList' || mutation.type === 'attributes') { + shouldCheck = true; + } + }); + + if (shouldCheck) { + // Kleine Verzögerung um Layout-Thrashing zu vermeiden + setTimeout(() => { + removeProblematicBackgrounds(); + enforceDesign(); + }, 10); + } + }); + + observer.observe(document.body, { + childList: true, + subtree: true, + attributes: true, + attributeFilter: ['style', 'class'] + }); + } + + // Dark Mode Toggle überwachen + function setupDarkModeObserver() { + const darkModeObserver = new MutationObserver(function(mutations) { + mutations.forEach(function(mutation) { + if (mutation.type === 'attributes' && mutation.attributeName === 'class') { + const target = mutation.target; + if (target === document.documentElement && + (target.classList.contains('dark') || !target.classList.contains('dark'))) { + + // Warte kurz und wende dann das Design neu an + setTimeout(() => { + enforceDesign(); + }, 50); + } + } + }); + }); + + darkModeObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ['class'] + }); + } + + // Entferne CSS-Klassen die problematische Hintergründe verursachen könnten + function removeProblematicClasses() { + const elements = document.querySelectorAll('*'); + const problematicClassPatterns = [ + /bg-slate-\d+/, + /bg-gray-\d+/, + /bg-zinc-\d+/ + ]; + + elements.forEach(element => { + const classList = Array.from(element.classList); + let classesRemoved = false; + + classList.forEach(className => { + problematicClassPatterns.forEach(pattern => { + if (pattern.test(className)) { + console.log('Entferne problematische CSS-Klasse:', className, 'von Element:', element); + element.classList.remove(className); + classesRemoved = true; + } + }); + }); + + if (classesRemoved) { + // Füge professional classes hinzu + if (!element.classList.contains('bg-professional') && + !element.classList.contains('professional-container') && + !element.classList.contains('mb-glass')) { + // Nur bei bestimmten Elementen + if (element.tagName === 'DIV' || element.tagName === 'SECTION') { + element.classList.add('bg-professional'); + } + } + } + }); + } + + // Initialisierung + function init() { + console.log('Mercedes-Benz Background Fix - Initialisierung'); + + // Sofort ausführen + removeProblematicBackgrounds(); + removeProblematicClasses(); + enforceDesign(); + + // Observer einrichten + observeChanges(); + setupDarkModeObserver(); + + // Regelmäßige Überprüfung alle 5 Sekunden + setInterval(() => { + removeProblematicBackgrounds(); + enforceDesign(); + }, 5000); + + console.log('Mercedes-Benz Background Fix - Aktiv'); + } + + // Starten wenn DOM bereit ist + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } + + // Auch beim Laden von Ressourcen + window.addEventListener('load', () => { + setTimeout(() => { + removeProblematicBackgrounds(); + enforceDesign(); + }, 100); + }); + + // Expose für Debug-Zwecke + window.mercedesBackgroundFix = { + removeProblematicBackgrounds, + enforceDesign, + removeProblematicClasses + }; + +})(); \ No newline at end of file diff --git a/backend/app/templates/base.html b/backend/app/templates/base.html index f4a7ac6f..5263ec8d 100644 --- a/backend/app/templates/base.html +++ b/backend/app/templates/base.html @@ -29,6 +29,7 @@ + @@ -488,6 +489,7 @@ + {% if current_user.is_authenticated %}