import csv
import os
import io
import json
from datetime import datetime, timedelta, date, time
import traceback
import re
import pandas as pd
from functools import wraps
from urllib.parse import quote, unquote
from typing import List, Dict, Tuple, Optional
from docx import Document
from docx.shared import Pt
from werkzeug.utils import secure_filename
from collections import Counter
import shutil
from flask import Flask, flash, jsonify, request, render_template_string, session, redirect, url_for, abort, send_file, \
    get_flashed_messages, send_from_directory
import zipfile
import threading
import uuid
from urllib.parse import unquote

# Globaler Lock, damit niemals zwei Threads gleichzeitig schreiben
file_write_lock = threading.Lock()

# 1. Flask App Instanz erstellen
app = Flask(__name__, static_folder='db/static')
app.secret_key = os.environ.get('FLASK_SECRET_KEY', 'dein-sehr-geheimer-schluessel-fuer-flask-sessions')

# Globale Variablen
EMPLOYEES_FILE_PATH = 'db/MBET_employees.csv'
QUALIFICATIONS_FILE_PATH = 'db/MBET_quali_display.csv'
USERS_FILE_PATH = 'db/MBET_user_display.csv'
ASSIGNED_EMPLOYEES_CSV = 'db/Pruefst4_assigned_employees.csv'
ASSIGNMENT_RULES_FILE_PATH = 'db/Pruefst4_assignment_rules.csv'
COMMENTS_FILE_PATH = 'db/MBET_comments.csv'
CURRENT_PROJECT_FILE_PATH = 'db/Pruefst4_timeline_projects.csv'
TIMELINE_PROJECTS_DISPLAY_CSV = 'db/timeline_projects_display.csv'
PROCESS_TEMPLATES_FILE_PATH = 'db/process_templates.json'
DOCUMENTS_FOLDER_PATH = 'db/documents'
CERTIFICATES_FOLDER_PATH = 'db/certificates'
CERTIFICATES_UPLOADS_FOLDER_PATH = os.path.join(CERTIFICATES_FOLDER_PATH, 'uploads')
LOGGED_IN_USER_DISPLAY_NAME_FOR_PYTHON = "System"
ALL_USERS = []
ALL_QUALIFICATIONS = []
ALL_EMPLOYEES = []
MERKMALE_IN_ORDER = []  # Wird von load_qualifications gefüllt
USER_CSV_FIELDNAMES = ['NACHNAME', 'VORNAME', 'ID', 'LEVEL', 'PASSWORD', 'ADMIN', 'ROLES', 'UNITS']
DEFAULT_PAST_WEEKS_PRESENCE = 0
DEFAULT_FUTURE_WEEKS_PRESENCE = 1
ALL_PROJECTS = []
ALL_COMMENTS = []
ASSIGNED_EMPLOYEES_FIELDNAMES = ['project_code', 'engine_serial', 'event_title', 'date_str', 'shift', 'employee_id',
                                 'engine_type']
ASSIGNMENT_RULES_FIELDNAMES = ['rule_id', 'event_titles', 'engine_types', 'required_skills']
VALID_ASSIGNABLE_STATUSES = {'X', 'TS', 'TT'}
LOCATIONS_FILE_PATH = 'db/MBET_Locations.csv'
LOCATIONS_EVENTS_CSV_PATH = 'db/MBET_locations_events.csv'  # NEUE Globale Variable
ALL_LOCATIONS = []
DB_ROOT_PATH = 'db'
ORDER_DATA_DIR = "db/ordersystem"
ORDER_ORDERS_FILE = os.path.join(ORDER_DATA_DIR, "order_order.json")
ORDER_TEMPLATES_FILE = os.path.join(ORDER_DATA_DIR, "order_templates.json")

PROJECT_STATUS_DIR = "db/projectstatus"
TEST_EQUIPMENT_DATA_FILE = 'db/hardware/test_equipment_data.json'
HARDWARE_UPLOADS_DIR = 'db/hardware/uploads'

TESTCELL_HISTORY_FILE = os.path.join(DB_ROOT_PATH, 'testcell_history.json')

# --- MAINTENANCE  ---
MAINTENANCE_DATA_DIR = os.path.join(DB_ROOT_PATH, 'maintenance')
MAINTENANCE_ORDERS_FILE = os.path.join(MAINTENANCE_DATA_DIR, 'maintenance_orders.json')
MAINTENANCE_DEFINITIONS_FILE = os.path.join(MAINTENANCE_DATA_DIR, 'maintenance_definitions.json')
MAINTENANCE_COMPANIES_FILE = os.path.join(MAINTENANCE_DATA_DIR, 'maintenance_companies.json')
MAINTENANCE_UPLOADS_DIR = os.path.join(MAINTENANCE_DATA_DIR, 'uploads')
MAINTENANCE_LOGBOOK_FILE = os.path.join(MAINTENANCE_DATA_DIR, 'maintenance_logbook.json')

# --- ASSIGNMENTs  ---
GLOBAL_ASSIGNMENTS_FILE = os.path.join(DB_ROOT_PATH, 'global_assignments.json')
ASSIGNMENT_HISTORY_FILE = os.path.join(DB_ROOT_PATH, 'assignment_history.json')

# --- TRAINING KONSTANTEN ---
TRAINING_ROOT_DIR = os.path.join(DB_ROOT_PATH, 'training')
TRAINING_DATA_FILE = os.path.join(TRAINING_ROOT_DIR, 'trainings.json')
USER_RELATIONS_FILE = os.path.join(TRAINING_ROOT_DIR, 'user_relations.json')
TRAINING_UPLOADS_DIR = os.path.join(TRAINING_ROOT_DIR, 'uploads')

# 1a. Zentrale Definition der Rollen als Konstanten
class Roles:
    ADMIN = "ADMIN"
    OBSERVER = "OBSERVER"
    PLANNER = "PLANNER"
    TESTCELLINSPECTOR = "TESTCELLINSPECTOR"
    TESTMECHANIC = "TESTMECHANIC"
    TESTENGINEER = "TESTENGINEER"
    MAINTENANCEMANAGER = "MAINTENANCEMANAGER"
    HARDWAREMANAGER = "HARDWAREMANAGER"
    CONSUMABLEMANAGER = "CONSUMABLEMANAGER"
    TOPIC_ELEKTRIK = "TOPIC_ELEKTRIK"
    TOPIC_MECHANIK = "TOPIC_MECHANIK"
    TOPIC_IT = "TOPIC_IT"
    TOPIC_FACILITY = "TOPIC_FACILITY"
    TOPIC_TESTENG = "TOPIC_TESTENG"

# 1b.  Beschreibungen für Verwaltungsseite
AVAILABLE_ROLES = {
    Roles.ADMIN: "Vollzugriff.",
    Roles.OBSERVER: "Reiner Lesezugriff.",
    Roles.PLANNER: "Darf Timelines planen und Projekte bearbeiten.",
    Roles.TESTCELLINSPECTOR: "Darf als 'Vier-Augen-Prinzip' abgeschlossene Aufgaben prüfen und freigeben.",
    Roles.TESTMECHANIC: "Darf TFS und den Status von Wartungsaufgaben und Checklisten ändern.",
    Roles.TESTENGINEER: "Darf Testzellen, Rüstwagen und dazugehörige Prozesse verwalten.",
    Roles.MAINTENANCEMANAGER: "Darf Wartungspläne definieren und bearbeiten.",
    Roles.HARDWAREMANAGER: "Darf das Hardware-Inventar verwalten.",
    Roles.CONSUMABLEMANAGER: "Verwaltet das Bestellsystem für Verbrauchsmaterialien.",
    Roles.TOPIC_ELEKTRIK: "Darf Wartungen im Bereich Elektrik/Messtechnik freigeben.",
    Roles.TOPIC_MECHANIK: "Darf Wartungen im Bereich Anlagen/Mechanik freigeben.",
    Roles.TOPIC_IT: "Darf Wartungen im Bereich IT/Software freigeben.",
    Roles.TOPIC_FACILITY: "Darf Wartungen im Bereich Facility Management freigeben.",
    Roles.TOPIC_TESTENG: "Darf Wartungen im Bereich Test Engineering freigeben."
}

# --- 3D ANLAGENÜBERSICHT ---
FACILITY_DATA_DIR = os.path.join(DB_ROOT_PATH, 'facility')
FACILITY_STRUCTURE_FILE = os.path.join(FACILITY_DATA_DIR, 'structure.json')
FACILITY_MODELS_DIR = FACILITY_DATA_DIR
FACILITY_BACKUP_DIR = os.path.join(FACILITY_DATA_DIR, 'backups')
FACILITY_MODEL_FILENAME = 'anlage_gesamt_test.glb'
FACILITY_MODEL_PATH = os.path.join(FACILITY_MODELS_DIR, FACILITY_MODEL_FILENAME)

# Standard-Daten für neue Struktur
FACILITY_EMPTY_DATA = {
    "betriebseinheiten": [],
    "gebaeude": [],
    "fluesse": []
}

# Simulierte Live-Daten für die Status-Anzeige
ROOM_STATUS_DATA = {
}

# --- SFM TEXTS CONFIG ---
SFM_TEXTS_FILE = os.path.join(DB_ROOT_PATH, 'SFM_texts.json')
SFM_CALENDAR_FILE = os.path.join(DB_ROOT_PATH, 'sfm_calendar.json')

# Standard-Werte, falls Datei nicht existiert
DEFAULT_SFM_TEXTS = {
    "motd": {
        "text": "Willkommen im Shop Floor Management System.",
        "type": "info",    # info, warning, danger, success
        "visible": True
    }
}

def load_app_config():
    """Lädt die zentrale Konfigurationsdatei config.json."""
    config_path = os.path.join(DB_ROOT_PATH, 'config.json')
    try:
        with open(config_path, 'r', encoding='utf-8') as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError) as e:
        print(f"!!! KRITISCHER FEHLER: Konfigurationsdatei '{config_path}' konnte nicht geladen werden: {e}")
        # Im Fehlerfall ein leeres Fallback zurückgeben, um einen Totalabsturz zu verhindern
        return {"timelines": [], "sap_filters": {}}


def load_sfm_texts():
    """Lädt die konfigurierbaren Texte."""
    if not os.path.exists(SFM_TEXTS_FILE):
        return DEFAULT_SFM_TEXTS.copy()

    try:
        with open(SFM_TEXTS_FILE, 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception as e:
        print(f"Fehler beim Laden von SFM_texts.json: {e}")
        return DEFAULT_SFM_TEXTS.copy()


def save_sfm_texts_data(data):
    """Speichert die Texte atomar."""
    return save_json_file(SFM_TEXTS_FILE, data)


# 2. HTML-Konstanten und andere globale Konfigurationen
LOGIN_PAGE_HTML = """
<!DOCTYPE html>
<html lang="de"><head>
<meta charset="utf-8"/>
<link crossorigin="" href="https://fonts.gstatic.com/" rel="preconnect"/>
<link as="style" href="https://fonts.googleapis.com/css2?display=swap&family=Noto+Sans%3Awght%40400%3B500%3B700%3B900&family=Space+Grotesk%3Awght%40400%3B500%3B700" onload="this.rel='stylesheet'" rel="stylesheet"/>
<title>Engine Test Facility - Login</title>
<link href="data:image/x-icon;base64," rel="icon" type="image/x-icon"/>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<style type="text/tailwindcss">
    @layer base {
      :root {
        --brand-primary-color: #0c7ff2;
        --brand-secondary-color: #1f2937;
        --text-primary-color: #ffffff;
        --text-secondary-color: #9ca3af;
        --surface-primary-color: #111418;
        --surface-secondary-color: #1f2937;
        --border-primary-color: #283039;
      }
    }
    body {
        background-color: var(--surface-primary-color);
        color: var(--text-primary-color);
        font-family: "Space Grotesk", "Noto Sans", sans-serif;
    }
    .form-input::placeholder {
      color: var(--text-secondary-color);
    }
    .login-button {
        background-color: var(--brand-primary-color);
    }
    .login-button:hover {
        background-color: #0a6cce;
    }
  </style>
</head>
<body>
<div class="relative flex size-full min-h-screen flex-col items-center justify-center overflow-x-hidden p-4">
<div class="w-full max-w-md">
<header class="mb-8 text-center">
<div class="inline-flex items-center gap-3 mb-4">
<div class="size-8 text-[var(--brand-primary-color)]">
<svg fill="none" viewBox="0 0 48 48" xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd" d="M24 4H6V17.3333V30.6667H24V44H42V30.6667V17.3333H24V4Z" fill="currentColor" fill-rule="evenodd"></path>
</svg>
</div>
<h1 class="text-3xl font-bold tracking-tight">Engine Test Facility</h1>
</div>
<p class="text-[var(--text-secondary-color)] text-lg">Please enter your credentials to continue.</p>
</header>
<main class="bg-[var(--surface-secondary-color)] shadow-2xl rounded-xl p-8">
<h2 class="text-2xl font-semibold text-center mb-6">Login</h2>
{% if error %}
    <p class="text-red-500 text-center mb-4 text-sm">{{ error }}</p>
{% endif %}
<form class="space-y-6" method="POST" action="{{ url_for('login_page') }}">
<div>
    <label class="sr-only" for="username">Benutzer-ID</label>
    <div class="relative">
    <input class="form-input block w-full rounded-lg border-0 bg-[#283039] py-3.5 pr-4 pl-10 text-[var(--text-primary-color)] shadow-sm ring-1 ring-inset ring-[var(--border-primary-color)] focus:ring-2 focus:ring-inset focus:ring-[var(--brand-primary-color)] sm:text-sm sm:leading-6 h-14 placeholder:text-[var(--text-secondary-color)]"
           id="username" name="username" placeholder="Benutzer-ID (z.B. YJ12345)" type="text" value="{{ request.form.username if request and request.form else '' }}"/>
    <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="h-5 w-5 text-[var(--text-secondary-color)]">
            <path d="M10 8a3 3 0 100-6 3 3 0 000 6ZM3.465 14.493a1.23 1.23 0 00.41 1.412A9.957 9.957 0 0010 18c2.31 0 4.438-.784 6.131-2.1.43-.333.604-.903.408-1.41a7.002 7.002 0 00-13.074.003Z" />
        </svg>
    </div>
    </div>
</div>
<div>
<label class="sr-only" for="password">Password</label>
<div class="relative">
<input class="form-input block w-full rounded-lg border-0 bg-[#283039] py-3.5 pr-4 pl-10 text-[var(--text-primary-color)] shadow-sm ring-1 ring-inset ring-[var(--border-primary-color)] focus:ring-2 focus:ring-inset focus:ring-[var(--brand-primary-color)] sm:text-sm sm:leading-6 h-14 placeholder:text-[var(--text-secondary-color)]"
       id="password" name="password" placeholder="Passwort" type="password"/>
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
<svg aria-hidden="true" class="h-5 w-5 text-[var(--text-secondary-color)]" fill="currentColor" viewBox="0 0 20 20">
<path clip-rule="evenodd" d="M10 1a4.5 4.5 0 00-4.5 4.5V9H5a2 2 0 00-2 2v7a2 2 0 002 2h10a2 2 0 002 2v-7a2 2 0 00-2-2h-.5V5.5A4.5 4.5 0 0010 1zm3 8V5.5a3 3 0 10-6 0V9h6z" fill-rule="evenodd"></path>
</svg>
</div>
</div>
</div>
<div>
<button class="login-button flex w-full justify-center rounded-lg px-3 py-3 text-sm font-semibold leading-6 text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--brand-primary-color)] transition-colors duration-150" type="submit">
              Anmelden
            </button>
</div>
</form>
</main>
<footer class="mt-10 text-center text-sm text-[var(--text-secondary-color)]">
<p>© 2025 MTU Maintenance Test Facility. All rights reserved.</p>
</footer>
</div>
</div>
</body></html>
"""

DASHBOARD_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <title>MBET - Dashboard</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <style>
        body { font-family: 'Inter', sans-serif; background-color: #FFFFFF; }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
        .cover-area { height: 20vh; background-color: #f1f5f9; }
        .event-card { min-width: 280px; }
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label, #sidebar.is-collapsed .sidebar-title, #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link, #sidebar.is-collapsed .sidebar-header, #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined, #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
    </style>
</head>
<body>
<div class="flex h-screen bg-white">
    {% include 'sidebar.html' %}
    <main class="flex-1 overflow-y-auto">
        <div class="cover-area"></div>
        <div class="px-8 md:px-12 lg:px-16">
            <div class="relative h-12">
                <div class="absolute -top-10 left-0">
                    <div class="h-24 w-24 bg-white rounded-full flex items-center justify-center text-3xl font-bold text-slate-700 shadow-lg ring-8 ring-white">
                         {{ current_user_initials }}
                    </div>
                </div>
            </div>
            <h1 class="text-4xl font-bold text-slate-800 mt-8 mb-12">Maintenance & Test Facility</h1>
<!-- Persönliche Begrüßung & Aufgabe -->
            <section class="mb-10 grid grid-cols-1 lg:grid-cols-3 gap-6">
                <!-- Mein Plan (Clean) -->
                <div class="lg:col-span-2 bg-white rounded-lg border border-slate-200 shadow-sm p-6 flex flex-col justify-center relative overflow-hidden">
                    <div class="absolute left-0 top-0 bottom-0 w-1 {% if my_missions %}bg-blue-500{% else %}bg-slate-300{% endif %}"></div>
                    
                    <div class="flex items-center gap-4 mb-3">
                        <div class="h-10 w-10 rounded-full flex items-center justify-center flex-shrink-0 {% if my_missions %}bg-blue-50 text-blue-600{% else %}bg-slate-100 text-slate-400{% endif %}">
                            <span class="material-icons-outlined text-xl">
                                {% if my_missions %}engineering{% else %}free_breakfast{% endif %}
                            </span>
                        </div>
                        <h2 class="text-lg font-bold text-slate-800">Hallo, {{ current_user_display_name }}!</h2>
                    </div>

                    <div class="pl-[3.5rem]">
                        {% if my_missions %}
                            <div class="space-y-3"> <!-- Etwas engerer Abstand -->
                            {% for mission in my_missions %}
                                <div class="group">
                                    <div class="flex items-center gap-2 text-slate-800">
                                        <span class="text-base font-bold">{{ mission.task }}</span>
                                        <span class="text-slate-400 text-sm font-light">an</span>
                                        <span class="text-sm font-medium">{{ mission.engine }}</span>
                                    </div>
                                    <div class="flex items-center gap-3 text-xs text-slate-500 mt-0.5">
                                        <span class="bg-slate-100 px-1.5 py-0.5 rounded text-slate-600 font-mono">{{ mission.project }}</span>
                                        {% if mission.team %}
                                            <span class="flex items-center gap-1">
                                                <span class="text-slate-300">+</span> {{ mission.team|join(', ') }}
                                            </span>
                                        {% endif %}
                                    </div>
                                </div>
                            {% endfor %}
                            </div>
                        {% else %}
                            <p class="text-slate-500 text-sm">Für heute liegen keine Zuweisungen vor.</p>
                        {% endif %}
                                                <!-- NEU: ENTSCHEIDUNGEN -->
                        {% if my_decisions %}
                            <div class="mt-6 pt-4 border-t border-slate-100">
                                <h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-3">Ausstehende Entscheidungen</h3>
                                <div class="space-y-2">
                                    {% for dec in my_decisions %}
                                        <a href="{{ url_for('serve_maintenance_page', openId=dec.id) }}" class="flex items-center gap-3 p-2 rounded-md hover:bg-slate-50 transition-colors group">
                                            {% if dec.type == 'triage' %}
                                                <div class="w-2 h-2 rounded-full bg-orange-500"></div>
                                                <span class="text-sm font-medium text-slate-700 group-hover:text-blue-600">Bewertung: {{ dec.title }}</span>
                                            {% elif dec.type == 'planning' %}
                                            <div class="w-2 h-2 rounded-full bg-blue-500"></div>
                                            <span class="text-sm font-medium text-slate-700 group-hover:text-blue-600">Maßnahme definieren: {{ dec.title }}</span>
                                            {% else %}
                                                <div class="w-2 h-2 rounded-full bg-purple-500"></div>
                                                <span class="text-sm font-medium text-slate-700 group-hover:text-blue-600">Freigabe: {{ dec.title }}</span>
                                            {% endif %}
                                            <span class="material-icons-outlined text-slate-300 text-sm ml-auto opacity-0 group-hover:opacity-100">arrow_forward</span>
                                        </a>
                                    {% endfor %}
                                </div>
                            </div>
                        {% endif %}
                    </div>
                </div>               


                <!-- Rechte Spalte: MOTD & Tools (1/3) -->
                <div class="flex flex-col gap-4 h-full">
                    
                    <!-- 1. MOTD -->
                    {% set motd = sfm_texts.get('motd', {'visible': False}) %}
                    
                    {# Definition der Farb-Klassen basierend auf dem Typ #}
                    {% set colors = {
                        'info':    {'bg': 'bg-blue-50', 'border': 'border-blue-500', 'icon': 'text-blue-600', 'title': 'text-blue-800', 'text': 'text-blue-700'},
                        'warning': {'bg': 'bg-amber-50', 'border': 'border-amber-500', 'icon': 'text-amber-600', 'title': 'text-amber-800', 'text': 'text-amber-800'},
                        'danger':  {'bg': 'bg-red-50', 'border': 'border-red-500', 'icon': 'text-red-600', 'title': 'text-red-800', 'text': 'text-red-800'},
                        'success': {'bg': 'bg-green-50', 'border': 'border-green-500', 'icon': 'text-green-600', 'title': 'text-green-800', 'text': 'text-green-800'}
                    } %}
                    {% set c = colors[motd.type] if motd.type in colors else colors['info'] %}

                    {% if motd.visible %}
                        <!-- Sichtbare Box für alle -->
                        <div class="{{ c.bg }} border-l-4 {{ c.border }} p-5 rounded-r-lg relative transition-all duration-300 flex-grow">
                             <div class="flex items-start gap-3">
                                <span class="material-icons-outlined {{ c.icon }} mt-0.5">campaign</span>
                                <div>
                                    <h3 class="font-semibold {{ c.title }}">Mitteilung des Tages</h3>
                                    <p class="text-sm {{ c.text }} mt-1 whitespace-pre-line">{{ motd.text }}</p>
                                </div>
                            </div>
                            {% if 'ADMIN' in session.get('roles', []) %}
                            <button onclick="openMotdModal()" class="absolute top-2 right-2 {{ c.icon }} hover:opacity-70 p-1 rounded-full" title="Mitteilung bearbeiten">
                                <span class="material-icons-outlined" style="font-size: 20px;">settings</span>
                            </button>
                            {% endif %}
                        </div>
                    {% elif 'ADMIN' in session.get('roles', []) %}
                        <!-- Admin-Only Platzhalter wenn ausgeblendet -->
                        <div class="border-2 border-dashed border-slate-300 p-4 rounded-lg relative text-center group hover:border-blue-400 transition-colors cursor-pointer flex-grow flex items-center justify-center" onclick="openMotdModal()">
                             <div class="flex items-center justify-center gap-2 text-slate-400 group-hover:text-blue-500">
                                <span class="material-icons-outlined">visibility_off</span>
                                <span class="text-sm font-medium">Mitteilung des Tages ist ausgeblendet</span>
                            </div>
                        </div>
                    {% endif %}

                    <!-- 2. Global SAP Update (Admin Only) -->
                    {% if session.get('is_admin') %}
                    <div class="bg-indigo-50 border-l-4 border-indigo-500 p-5 rounded-r-lg relative">
                        <div class="flex items-center justify-between">
                            <div class="flex items-center gap-3">
                                <span class="material-icons-outlined text-indigo-600">cloud_sync</span>
                                <div>
                                    <h3 class="font-semibold text-indigo-800">Globales SAP Update</h3>
                                    <p class="text-xs text-indigo-600">Aktualisiert alle Planungs-Boards zentral.</p>
                                </div>
                            </div>
                            <button id="globalSapBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white text-xs font-bold py-2 px-3 rounded shadow-sm flex items-center gap-1">
                                <span class="material-icons-outlined text-sm">upload</span>
                                <span>Update Starten</span>
                            </button>
                            <input type="file" id="globalSapInput" class="hidden" accept=".csv">
                        </div>
                        <div id="globalSapStatus" class="hidden mt-3 pt-3 border-t border-indigo-200 text-xs text-indigo-800"></div>
                    </div>
                    {% endif %}
                </div>
            </section>
            
            <section class="mb-36 grid grid-cols-1 lg:grid-cols-3 gap-6">
                <!-- Mitarbeiter-Kachel -->
                {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) %}
                <div onclick="document.getElementById('employeeStatusModal').classList.remove('hidden')" class="cursor-pointer bg-white rounded-lg border border-slate-200 shadow-sm p-5 transition-shadow duration-200 hover:shadow-md">
                    <div class="flex items-start justify-between">
                        <div>
                            <h3 class="font-bold text-slate-500">Mitarbeiter Heute</h3>
                            <div class="mt-2 flex items-baseline gap-2">
                                <p class="text-3xl font-bold text-slate-800">{{ free_employees_count }}</p>
                                <p class="text-lg text-slate-500">/ {{ present_employees_count }}</p>
                            </div>
                            <p class="text-sm text-slate-500">frei / anwesend</p>
                        </div>
                        <div class="h-10 w-10 bg-neutral-100 flex items-center justify-center rounded-lg">
                            <span class="material-icons-outlined text-sky-900">groups</span>
                        </div>
                    </div>
                </div>
                {% endif %}
                <!-- Testzellen-Kachel -->
                <a href="{{ url_for('serve_test_cell_management_page') }}" class="block bg-white rounded-lg border border-slate-200 shadow-sm p-5 transition-shadow duration-200 hover:shadow-md">
                    <div class="flex items-start justify-between mb-3">
                        <h3 class="font-bold text-slate-500">Testzellen</h3>
                        <div class="h-10 w-10 bg-neutral-100 flex items-center justify-center rounded-lg">
                            <span class="material-icons-outlined text-sky-900">other_houses</span>
                        </div>
                    </div>
                    <div class="flex flex-wrap gap-2">
                        {% for cell in test_cell_statuses %}
                            <span class="inline-flex items-center rounded-full bg-slate-100 px-3 py-1 text-sm font-medium text-slate-700">
                                {{ cell.name }}
                            </span>
                        {% else %}
                            <p class="text-xs text-slate-500">Keine Testzellen konfiguriert.</p>
                        {% endfor %}
                    </div>
                    
                </a>
            </section>
            
            <!-- Planungs-Boards Sektion -->
            <section class="mb-16">
                <div class="flex justify-between items-center mb-4">
                    <h2 class="text-xl font-bold text-slate-700">Planungs-Boards</h2>
                    {% if session.get('is_admin') %}
                    <button id="addTimelineBtn" class="flex-shrink-0 h-12 w-12 flex items-center justify-center rounded-full bg-sky-900 text-white shadow-sm ring-0 ring-inset ring-slate-200 hover:bg-slate-200 transition-colors" title="Neue Timeline erstellen">
                    <span class="material-icons-outlined">add</span>
                    </button>
                    {% endif %}
                </div>
                <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-6 gap-5">
                    {% for timeline in timelines | selectattr('type', 'equalto', 'editable') %}
                    <div class="bg-white rounded-lg border border-slate-200 shadow-sm overflow-hidden flex flex-col transition-shadow duration-200 hover:shadow-lg">
                        <div class="h-32 bg-gradient-to-br from-blue-100 to-indigo-200 flex items-center justify-center">
                            <span class="material-icons-outlined text-blue-500" style="font-size: 48px;">edit_calendar</span>
                        </div>
                        <div class="p-5 flex flex-col flex-grow">
                            <div class="flex justify-between items-start mb-1">
                                <h3 class="text-lg font-bold text-slate-800">{{ timeline.name }}</h3>
                                {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) %}
                                <button class="edit-timeline-btn text-slate-400 hover:text-blue-600 -mr-2 -mt-2 p-2 rounded-full" data-timeline-id="{{ timeline.id }}" title="Einstellungen bearbeiten">
                                    <span class="material-icons-outlined" style="font-size: 20px;">settings</span>
                                </button>
                                {% endif %}
                            </div>
                            <p class="text-xs text-slate-400 mb-3">Typ: {{ timeline.type }}</p>
                            <div class="flex-grow"></div>
                            <div class="mt-4 pt-4 border-t border-slate-100 flex items-center gap-3">
                                <a href="{{ url_for('serve_timeline_page', timeline_id=timeline.id) }}" class="flex-1 inline-flex items-center justify-center gap-x-2 rounded-md bg-white px-3 py-2 text-sm font-semibold text-slate-700 shadow-sm ring-1 ring-inset ring-slate-200 hover:bg-slate-100">
                                    <span class="material-icons-outlined" style="font-size: 18px;">visibility</span>
                                    View
                                </a>
                                {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) %}
                                <a href="{{ url_for('serve_editable_timeline_page', timeline_id=timeline.id) }}" class="flex-1 inline-flex items-center justify-center gap-x-2 rounded-md bg-sky-900 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500">
                                    <span class="material-icons-outlined" style="font-size: 18px;">edit_calendar</span>
                                    Plan
                                </a>
                                {% endif %}
                            </div>
                        </div>
                    </div>
                    {% endfor %}
                </div>
            </section>

            <!-- Übersichten Sektion -->
            {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) %}
            <section class="mb-16">
                <h2 class="text-xl font-bold text-slate-700 mb-36"></h2>
                <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-6 gap-5">
                     {% for timeline in timelines | selectattr('type', 'equalto', 'static') %}
                     <div class="bg-white rounded-lg border border-slate-200 shadow-sm overflow-hidden flex flex-col transition-shadow duration-200 hover:shadow-lg">
                        <div class="h-32 bg-gradient-to-br from-slate-100 to-slate-200 flex items-center justify-center">
                            <span class="material-icons-outlined text-slate-500" style="font-size: 48px;">visibility</span>
                        </div>
                        <div class="p-5 flex flex-col flex-grow">
                            <div class="flex justify-between items-start mb-1">
                                <h3 class="text-lg font-bold text-slate-800">{{ timeline.name }}</h3>
                                {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) %}
                                <button class="edit-timeline-btn text-slate-400 hover:text-blue-600 -mr-2 -mt-2 p-2 rounded-full" data-timeline-id="{{ timeline.id }}" title="Einstellungen bearbeiten">
                                    <span class="material-icons-outlined" style="font-size: 20px;">settings</span>
                                </button>
                                {% endif %}
                            </div>
                            <p class="text-xs text-slate-400 mb-3">Typ: {{ timeline.type }}</p>
                            <div class="flex-grow"></div>
                            <div class="mt-4 pt-4 border-t border-slate-100 flex items-center">
                                <a href="{{ url_for('serve_timeline_page', timeline_id=timeline.id) }}" class="w-full inline-flex items-center justify-center gap-x-2 rounded-md bg-white px-3 py-2 text-sm font-semibold text-slate-700 shadow-sm ring-1 ring-inset ring-slate-200 hover:bg-slate-100">
                                    <span class="material-icons-outlined" style="font-size: 18px;">visibility</span>
                                    Öffnen
                                </a>
                            </div>
                        </div>
                    </div>
                    {% endfor %}
                </div>
            </section>
            {% endif %}

            <!-- Weitere Module (Grid angepasst auf 4 Spalten für Large Screens) -->
            <section class="mb-32">
                <h2 class="text-xl font-bold text-slate-700 mb-4">Weitere Module</h2>
                <div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-5">

                    <!-- SWAT -->
                    {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTMECHANIC' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) or 'TESTENGINEER' in session.get('roles', []) or 'MAINTENANCEMANAGER' in session.get('roles', []) or 'HARDWAREMANAGER' in session.get('roles', []) %}
                    <a href="{{ url_for('serve_maintenance_page') }}" class="block bg-white rounded-lg border border-slate-200 shadow-sm p-5 transition-all duration-200 hover:shadow-lg hover:-translate-y-1">
                        <div class="flex items-center gap-4">
                            <div class="h-12 w-12 bg-gradient-to-br from-amber-100 to-orange-200 flex items-center justify-center rounded-lg">
                                <span class="material-icons-outlined text-amber-600">engineering</span>
                            </div>
                            <div>
                                <h3 class="text-lg font-bold text-slate-800">Maintenance</h3>
                                <p class="text-sm text-slate-500">Maintenance Center</p>
                            </div>
                        </div>
                    </a>
                    {% endif %}

                    <!-- Hardware -->
                    {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTMECHANIC' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) or 'TESTENGINEER' in session.get('roles', []) or 'MAINTENANCEMANAGER' in session.get('roles', []) or 'HARDWAREMANAGER' in session.get('roles', []) %}
                    <a href="{{ url_for('serve_test_equipment_page') }}" class="block bg-white rounded-lg border border-slate-200 shadow-sm p-5 transition-all duration-200 hover:shadow-lg hover:-translate-y-1">
                        <div class="flex items-center gap-4">
                            <div class="h-12 w-12 bg-gradient-to-br from-teal-100 to-cyan-200 flex items-center justify-center rounded-lg">
                                <span class="material-icons-outlined text-teal-600">memory</span>
                            </div>
                            <div>
                                <h3 class="text-lg font-bold text-slate-800">Hardware</h3>
                                <p class="text-sm text-slate-500">Übersicht & Verwaltung</p>
                            </div>
                        </div>
                    </a>
                    {% endif %}

                    <!-- NEU: Anlagenübersicht (Platzhalter) -->
                    <a href="{{ url_for('serve_facility_viewer') }}" class="block bg-white rounded-lg border border-slate-200 shadow-sm p-5 transition-all duration-200 hover:shadow-lg hover:-translate-y-1">
                        <div class="flex items-center gap-4">
                            <div class="h-12 w-12 bg-gradient-to-br from-indigo-100 to-blue-200 flex items-center justify-center rounded-lg">
                                <span class="material-icons-outlined text-indigo-600">warehouse</span>
                            </div>
                            <div>
                                <h3 class="text-lg font-bold text-slate-800">Anlagenübersicht</h3>
                                <p class="text-sm text-slate-500">Live Status & Monitoring</p>
                            </div>
                        </div>
                    </a>
                    <!-- Bestellsystem -->
                    {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTMECHANIC' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) or 'TESTENGINEER' in session.get('roles', []) or 'MAINTENANCEMANAGER' in session.get('roles', []) or 'HARDWAREMANAGER' in session.get('roles', []) %}
                    <a href="{{ url_for('serve_consumable_order_page') }}" class="block bg-white rounded-lg border border-slate-200 shadow-sm p-5 transition-all duration-200 hover:shadow-lg hover:-translate-y-1">
                        <div class="flex items-center gap-4">
                            <div class="h-12 w-12 bg-gradient-to-br from-violet-100 to-purple-200 flex items-center justify-center rounded-lg">
                                <span class="material-icons-outlined text-violet-600">shopping_cart</span>
                            </div>
                            <div>
                                <h3 class="text-lg font-bold text-slate-800">Bestellsystem</h3>
                                <p class="text-sm text-slate-500">Verbrauchsmaterialien ordern</p>
                            </div>
                        </div>
                    </a>
                    {% endif %}
                </div>
            </section>
<!-- Trainings Vorschau -->
            <section class="mb-24">
                <div class="flex justify-between items-center mb-4">
                    <h2 class="text-xl font-bold text-slate-700">Bevorstehende Trainings & Events</h2>
                    <a href="{{ url_for('serve_training_page') }}" class="text-sm font-medium text-blue-600 hover:text-blue-800">Alle anzeigen</a>
                </div>
                
                <div class="flex space-x-6 overflow-x-auto pb-4 -mx-8 px-8 snap-x">
                    {% if upcoming_trainings %}
                        {% for t in upcoming_trainings %}
                        
                        {# Farb-Rotation als Fallback #}
                        {% set bg_class = loop.cycle('bg-slate-200', 'bg-slate-400', 'bg-slate-300') %}
                        
                        <!-- Event-Karte -->
                        <div class="event-card flex-shrink-0 bg-white rounded-lg border border-slate-200 shadow-sm overflow-hidden w-72 snap-center hover:shadow-md transition-shadow">
                            <!-- Header (Bild oder Farbe) -->
                            <!-- 'bg-cover bg-center' sorgt für gute Skalierung des Bildes -->
                            <div class="h-32 {{ bg_class }} flex items-end p-4 relative bg-cover bg-center group" 
                                 style="{% if t.image %}background-image: url('{{ t.image }}');{% endif %}">
                               
                               <!-- Dunkles Overlay für Lesbarkeit, nur wenn Bild da ist -->
                               {% if t.image %}
                               <div class="absolute inset-0 bg-gradient-to-t from-black/60 to-transparent"></div>
                               {% else %}
                               <!-- Deko-Icon wenn kein Bild -->
                               <span class="material-icons-outlined absolute right-2 top-2 text-white opacity-20 text-6xl transform group-hover:scale-110 transition-transform">event</span>
                               {% endif %}
                               
                               <!-- Datum Box -->
                               <div class="relative z-10 text-white font-bold drop-shadow-md">
                                    <span class="block text-sm opacity-90 uppercase tracking-wider">{{ t.date_month }}</span>
                                    <span class="text-3xl leading-none">{{ t.date_day }}</span>
                               </div>
                            </div>
                            
                            <div class="p-4">
                                <p class="text-xs font-bold text-blue-600 mb-1 uppercase tracking-wide">{{ t.category }}</p>
                                <h4 class="font-bold text-slate-800 text-sm mb-2 line-clamp-2 min-h-[2.5rem]">{{ t.title }}</h4>
                                
                                <div class="flex items-center gap-3 text-xs text-slate-500">
                                    <span class="flex items-center gap-1"><span class="material-icons-outlined text-[14px]">schedule</span> {{ t.time }}</span>
                                    {% if t.location %}
                                    <span class="flex items-center gap-1 truncate max-w-[80px]" title="{{ t.location }}"><span class="material-icons-outlined text-[14px]">place</span> {{ t.location }}</span>
                                    {% endif %}
                                </div>
                                
                                <div class="mt-4 pt-3 border-t border-slate-100 flex justify-end">
                                    <a href="{{ url_for('serve_training_page') }}" class="text-slate-400 hover:text-blue-600 transition-colors p-1">
                                        <span class="material-icons-outlined text-xl">arrow_forward</span>
                                    </a>
                                </div>
                            </div>
                        </div>
                        {% endfor %}
                        
                        <!-- "Mehr" Karte -->
                        <a href="{{ url_for('serve_training_page') }}" class="event-card flex-shrink-0 bg-slate-50 rounded-lg border border-dashed border-slate-300 w-40 flex flex-col items-center justify-center text-slate-400 hover:text-blue-600 hover:border-blue-400 hover:bg-blue-50 transition-all snap-center cursor-pointer">
                            <span class="material-icons-outlined text-3xl mb-2">calendar_month</span>
                            <span class="text-sm font-medium">Zum Kalender</span>
                        </a>

                    {% else %}
                        <!-- Empty State -->
                        <div class="w-full text-center py-10 bg-slate-50 rounded-lg border border-dashed border-slate-300 text-slate-400 italic">
                            Aktuell keine Trainings geplant. <a href="{{ url_for('serve_training_page') }}" class="text-blue-600 hover:underline not-italic ml-1">Jetzt planen</a>
                        </div>
                    {% endif %}
                </div>
            </section>
        </div>
    </main>
</div>

<!-- Modals für Timeline Erstellung (bleibt unverändert) -->
<div id="addTimelineModal" class="fixed inset-0 bg-gray-600 bg-opacity-75 overflow-y-auto h-full w-full flex items-center justify-center hidden z-50 p-4">
    <div class="relative mx-auto p-5 border w-full max-w-2xl shadow-lg rounded-md bg-white">
        <div class="flex justify-between items-center pb-3 border-b">
            <h3 class="text-xl leading-6 font-medium text-gray-900">Neue Timeline erstellen</h3>
            <button id="closeTimelineModalBtn" class="text-gray-400 hover:text-gray-600">
                <span class="material-icons-outlined">close</span>
            </button>
        </div>
        <form id="addTimelineForm" class="mt-4 px-2 sm:px-7 py-3 space-y-4">
            <div>
                <label for="timelineName" class="block text-sm font-medium text-gray-700">Name der Timeline *</label>
                <input type="text" id="timelineName" name="timelineName" class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md text-sm shadow-sm" required placeholder="z.B. Planung SWAT Team">
            </div>
             <div>
                <label for="timelineId" class="block text-sm font-medium text-gray-700">Eindeutige ID *</label>
                <input type="text" id="timelineId" name="timelineId" class="mt-1 block w-full px-3 py-2 bg-white border border-gray-300 rounded-md text-sm shadow-sm" required placeholder="z.B. swat_team">
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700">Typ *</label>
                <select id="timelineType" name="timelineType" class="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-blue-500 focus:border-blue-500 sm:text-sm rounded-md">
                    <option value="editable">Editable (Planung & Ansicht)</option>
                    <option value="static">Static (Nur Ansicht)</option>
                </select>
            </div>
            <div id="editableOptions" class="space-y-4 pt-4 border-t">
                 <div>
                    <label class="block text-sm font-medium text-gray-700">Sichtbare Testzellen</label>
                    <div id="modalTestCells" class="mt-2 grid grid-cols-2 sm:grid-cols-3 gap-2 max-h-32 overflow-y-auto border p-2 rounded-md"></div>
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700">Zuweisungsgruppen</label>
                    <div id="modalAssignmentGroups" class="mt-2 grid grid-cols-2 sm:grid-cols-3 gap-2 max-h-32 overflow-y-auto border p-2 rounded-md"></div>
                </div>
                <div>
                    <label class="block text-sm font-medium text-gray-700">SAP-Filter</label>
                    <div id="sapFilterContainer" class="mt-2 space-y-3 p-3 border rounded-md bg-slate-50"></div>
                    <button id="addSapFilterRuleBtn" type="button" class="mt-2 text-sm font-medium text-blue-600 hover:text-blue-800">+ Filterregel hinzufügen</button>
                </div>
            </div>
            <div id="staticOptions" class="space-y-4 pt-4 border-t hidden">
                 <div>
                    <label class="block text-sm font-medium text-gray-700">Datenquellen</label>
                    <div id="modalDataSources" class="mt-2 grid grid-cols-1 sm:grid-cols-2 gap-2 max-h-40 overflow-y-auto border p-2 rounded-md"></div>
                </div>
            </div>
        </form>
        <div class="items-center px-4 py-3 bg-gray-50 rounded-b-md border-t">
            <div id="modalError" class="text-red-600 text-sm mb-2 text-center hidden"></div>
            <div class="flex justify-between items-center">
                <button id="deleteTimelineBtn" type="button" class="hidden items-center gap-x-1.5 rounded-md bg-red-50 px-3 py-2 text-sm font-semibold text-red-600 shadow-sm ring-1 ring-inset ring-red-200 hover:bg-red-100">
                    <span class="material-icons-outlined text-base -ml-1">delete_outline</span> Löschen
                </button>
                <div class="flex justify-end space-x-3 flex-grow">
                    <button id="cancelTimelineModalBtn" type="button" class="px-4 py-2 bg-gray-200 text-gray-700 text-base font-medium rounded-md shadow-sm hover:bg-gray-300">Abbrechen</button>
                    <button id="saveTimelineBtn" type="submit" form="addTimelineForm" class="px-4 py-2 bg-blue-600 text-white text-base font-medium rounded-md shadow-sm hover:bg-blue-700">Timeline erstellen</button>
                </div>
            </div>
        </div>
    </div>
</div>
<!-- MOTD Bearbeiten Modal -->
<div id="motdModal" class="fixed inset-0 bg-gray-600 bg-opacity-75 overflow-y-auto h-full w-full flex items-center justify-center hidden z-50 p-4">
    <div class="relative mx-auto p-5 border w-full max-w-md shadow-lg rounded-md bg-white">
        <div class="flex justify-between items-center pb-3 border-b">
            <h3 class="text-lg font-medium text-gray-900">Mitteilung bearbeiten</h3>
            <button onclick="document.getElementById('motdModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600">
                <span class="material-icons-outlined">close</span>
            </button>
        </div>
        <div class="mt-4 space-y-4">
            <div>
                <label class="block text-sm font-medium text-gray-700">Typ / Dringlichkeit</label>
                <div class="mt-2 grid grid-cols-4 gap-2">
                    <button type="button" onclick="selectMotdType('info')" class="motd-type-btn p-2 rounded border border-blue-200 bg-blue-50 text-blue-700 hover:ring-2 hover:ring-blue-400 text-center text-xs font-bold" data-type="info">Info</button>
                    <button type="button" onclick="selectMotdType('warning')" class="motd-type-btn p-2 rounded border-amber-200 bg-amber-50 text-amber-700 hover:ring-2 hover:ring-amber-400 text-center text-xs font-bold" data-type="warning">Warnung</button>
                    <button type="button" onclick="selectMotdType('danger')" class="motd-type-btn p-2 rounded border-red-200 bg-red-50 text-red-700 hover:ring-2 hover:ring-red-400 text-center text-xs font-bold" data-type="danger">Alarm</button>
                    <button type="button" onclick="selectMotdType('success')" class="motd-type-btn p-2 rounded border-green-200 bg-green-50 text-green-700 hover:ring-2 hover:ring-green-400 text-center text-xs font-bold" data-type="success">Erfolg</button>
                </div>
                <input type="hidden" id="motdTypeInput">
            </div>
            <div>
                <label class="block text-sm font-medium text-gray-700">Nachricht</label>
                <textarea id="motdTextInput" rows="3" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"></textarea>
            </div>
            <div class="flex items-center">
                <input id="motdVisibleInput" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
                <label for="motdVisibleInput" class="ml-2 block text-sm text-gray-900">Mitteilung auf dem Dashboard anzeigen</label>
            </div>
        </div>
        <div class="mt-5 flex justify-end gap-2">
            <button onclick="document.getElementById('motdModal').classList.add('hidden')" class="px-4 py-2 bg-gray-200 text-gray-700 text-sm font-medium rounded-md hover:bg-gray-300">Abbrechen</button>
            <button onclick="saveMotd()" class="px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700">Speichern</button>
        </div>
    </div>
</div>
<!-- NEUES MODAL: Mitarbeiter-Status Heute -->
<div id="employeeStatusModal" class="fixed inset-0 bg-gray-600 bg-opacity-75 overflow-y-auto h-full w-full flex items-center justify-center hidden z-50 p-4">
    <div class="relative mx-auto p-6 border w-full max-w-md shadow-lg rounded-md bg-white">
        <div class="flex justify-between items-center pb-3 border-b">
            <h3 class="text-xl leading-6 font-medium text-gray-900">Mitarbeiter-Status Heute</h3>
            <button onclick="document.getElementById('employeeStatusModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600">
                <span class="material-icons-outlined">close</span>
            </button>
        </div>
        <div class="mt-4 max-h-[70vh] overflow-y-auto pr-2 space-y-6">
            <!-- Verfügbare Mitarbeiter -->
            <div>
                <h4 class="text-sm font-bold text-green-700 uppercase tracking-wider mb-2">Verfügbar ({{ free_employees_count }})</h4>
                <ul class="space-y-1 text-sm text-slate-600">
                    {% for emp in free_employees %}
                    <li>{{ emp.name }}</li>
                    {% else %}
                    <li class="italic text-slate-400">Keine freien Mitarbeiter.</li>
                    {% endfor %}
                </ul>
            </div>
            <!-- Verplante Mitarbeiter -->
            <div>
                <h4 class="text-sm font-bold text-slate-500 uppercase tracking-wider mb-2">Verplant ({{ busy_employees|length }})</h4>
                <ul class="space-y-2 text-sm text-slate-600">
                    {% for emp in busy_employees %}
                    <li>
                        {{ emp.name }} 
                        <span class="text-xs ml-2 bg-slate-100 text-slate-500 px-2 py-0.5 rounded-full font-mono">{{ emp.assignment }}</span>
                    </li>
                    {% else %}
                    <li class="italic text-slate-400">Keine verplanten Mitarbeiter.</li>
                    {% endfor %}
                </ul>
            </div>
        </div>
    </div>
</div>
<script>
    const currentMotd = {{ sfm_texts.get('motd', {}) | tojson }};

    function openMotdModal() {
        document.getElementById('motdTextInput').value = currentMotd.text || '';
        document.getElementById('motdVisibleInput').checked = currentMotd.visible;
        selectMotdType(currentMotd.type || 'info');
        document.getElementById('motdModal').classList.remove('hidden');
    }

    function selectMotdType(type) {
        document.getElementById('motdTypeInput').value = type;
        document.querySelectorAll('.motd-type-btn').forEach(btn => {
            if (btn.dataset.type === type) {
                btn.classList.add('ring-2', 'ring-offset-1', 'ring-slate-400');
            } else {
                btn.classList.remove('ring-2', 'ring-offset-1', 'ring-slate-400');
            }
        });
    }

    async function saveMotd() {
        const payload = {
            key: 'motd',
            text: document.getElementById('motdTextInput').value,
            type: document.getElementById('motdTypeInput').value,
            visible: document.getElementById('motdVisibleInput').checked
        };

        try {
            const response = await fetch("{{ url_for('api_save_sfm_text') }}", {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload)
            });

            if (response.ok) {
                location.reload(); // Einfachster Weg, um das Dashboard zu aktualisieren
            } else {
                alert("Fehler beim Speichern.");
            }
        } catch (error) {
            console.error(error);
            alert("Netzwerkfehler.");
        }
    }
document.addEventListener('DOMContentLoaded', () => {
    // Sidebar Logik
    const sidebar = document.getElementById('sidebar');
    const sidebarToggleBtn = document.getElementById('sidebar-toggle');
    if (sidebar && sidebarToggleBtn) {
        const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
        const applyState = (isCollapsed) => {
            if (isCollapsed) { sidebar.classList.add('is-collapsed'); toggleBtnIcon.textContent = 'menu'; } 
            else { sidebar.classList.remove('is-collapsed'); toggleBtnIcon.textContent = 'chevron_left'; }
        };
        sidebarToggleBtn.addEventListener('click', () => {
            const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
            localStorage.setItem('sidebarCollapsed', isNowCollapsed);
            applyState(isNowCollapsed);
        });
        applyState(localStorage.getItem('sidebarCollapsed') === 'true');
    }

    // --- Global SAP Update Logic ---
    const globalSapBtn = document.getElementById('globalSapBtn');
    const globalSapInput = document.getElementById('globalSapInput');
    const globalSapStatus = document.getElementById('globalSapStatus');

    if (globalSapBtn && globalSapInput) {
        globalSapBtn.addEventListener('click', () => globalSapInput.click());
        globalSapInput.addEventListener('change', async (e) => {
            if (e.target.files.length === 0) return;
            const file = e.target.files[0];

            globalSapBtn.disabled = true;
            globalSapBtn.innerHTML = '<span class="material-icons-outlined text-sm animate-spin">sync</span> <span>Verarbeite...</span>';
            globalSapStatus.classList.remove('hidden');
            globalSapStatus.innerHTML = 'Daten werden analysiert und verteilt... Bitte warten.';
            globalSapStatus.className = 'mt-3 pt-3 border-t border-indigo-200 text-xs text-indigo-800 block';

            const formData = new FormData();
            formData.append('sap_file', file);

            try {
                const response = await fetch("{{ url_for('api_global_sap_update') }}", {
                    method: 'POST',
                    body: formData
                });
                const result = await response.json();

                if (response.ok) {
                    globalSapStatus.innerHTML = `<strong>Erfolg:</strong><br>${result.details}`;
                    globalSapStatus.className = 'mt-3 pt-3 border-t border-indigo-200 text-xs text-green-700 block';
                } else {
                    throw new Error(result.error || 'Unbekannter Fehler');
                }
            } catch (error) {
                globalSapStatus.textContent = `Fehler: ${error.message}`;
                globalSapStatus.className = 'mt-3 pt-3 border-t border-indigo-200 text-xs text-red-600 block';
            } finally {
                globalSapBtn.disabled = false;
                globalSapBtn.innerHTML = '<span class="material-icons-outlined text-sm">upload</span> <span>Update Starten</span>';
                globalSapInput.value = ''; 
            }
        });
    }

    // --- Bestehende Timeline Modal Logik ---
    const addTimelineBtn = document.getElementById('addTimelineBtn');
    if (!addTimelineBtn) return;

    const modal = document.getElementById('addTimelineModal');
    const closeBtn = document.getElementById('closeTimelineModalBtn');
    const cancelBtn = document.getElementById('cancelTimelineModalBtn');
    const form = document.getElementById('addTimelineForm');
    const modalTitle = modal.querySelector('h3');
    const saveBtn = document.getElementById('saveTimelineBtn');
    const deleteBtn = document.getElementById('deleteTimelineBtn');
    const timelineNameInput = document.getElementById('timelineName');
    const timelineIdInput = document.getElementById('timelineId');
    const timelineTypeSelect = document.getElementById('timelineType');
    const editableOptionsDiv = document.getElementById('editableOptions');
    const staticOptionsDiv = document.getElementById('staticOptions');
    const modalError = document.getElementById('modalError');
    const sapFilterContainer = document.getElementById('sapFilterContainer');
    const addSapFilterRuleBtn = document.getElementById('addSapFilterRuleBtn');

    let isEditMode = false;
    let editingTimelineId = null;

    function openModal() { form.reset(); modalError.classList.add('hidden'); modal.classList.remove('hidden'); }
    function closeModal() { modal.classList.add('hidden'); isEditMode = false; editingTimelineId = null; }

    function addSapFilterRule(column = '', values = '') {
        const ruleDiv = document.createElement('div');
        ruleDiv.className = 'flex items-center gap-2 sap-filter-rule';
        ruleDiv.innerHTML = `<input type="text" name="sap_filter_column" class="flex-1 block w-full px-2 py-1.5 bg-white border border-gray-300 rounded-md text-sm" placeholder="Spaltenname" value="${column}"><input type="text" name="sap_filter_values" class="flex-1 block w-full px-2 py-1.5 bg-white border border-gray-300 rounded-md text-sm" placeholder="Werte (kommagetrennt)" value="${values}"><button type="button" class="remove-rule-btn text-red-500 hover:text-red-700 p-1"><span class="material-icons-outlined text-xl">delete_outline</span></button>`;
        ruleDiv.querySelector('.remove-rule-btn').addEventListener('click', () => { ruleDiv.remove(); if (sapFilterContainer.querySelectorAll('.sap-filter-rule').length === 0) sapFilterContainer.innerHTML = '<p class="text-xs text-gray-500 text-center">Keine Filterregeln definiert.</p>'; });
        if (!sapFilterContainer.querySelector('.sap-filter-rule')) sapFilterContainer.innerHTML = '';
        sapFilterContainer.appendChild(ruleDiv);
    }

    async function populateModalWithOptions() {
        try {
            const [cellsRes, presenceRes, configRes] = await Promise.all([fetch("{{ url_for('api_get_test_cells') }}"), fetch("{{ url_for('api_get_employee_presence_data') }}"), fetch("{{ url_for('get_app_config') }}")]);
            if (!cellsRes.ok || !presenceRes.ok || !configRes.ok) throw new Error('Datenfehler');
            const allCells = await cellsRes.json(); const presenceData = await presenceRes.json(); const configData = await configRes.json();
            document.getElementById('modalTestCells').innerHTML = allCells.map(cell => `<label class="flex items-center space-x-2"><input type="checkbox" name="visible_test_cells" value="${cell.ID}" class="h-4 w-4 rounded border-gray-300"><span class="text-sm text-gray-700">${cell.TESTCELL} (${cell.ID})</span></label>`).join('') || '<p class="text-xs text-gray-500">Keine Testzellen.</p>';
            const allGroups = [...new Set(presenceData.employee_groups.map(g => g.group_name))].sort();
            document.getElementById('modalAssignmentGroups').innerHTML = allGroups.map(group => `<label class="flex items-center space-x-2"><input type="checkbox" name="assignment_groups" value="${group}" class="h-4 w-4 rounded border-gray-300"><span class="text-sm text-gray-700">${group}</span></label>`).join('');
            const sourceableTimelines = (configData.timelines || []).filter(tl => tl.data_file);
            document.getElementById('modalDataSources').innerHTML = sourceableTimelines.map(tl => `<label class="flex items-center space-x-2"><input type="checkbox" name="data_files_source" value="${tl.data_file}" class="h-4 w-4 rounded border-gray-300"><span class="text-sm text-gray-700">${tl.name}</span></label>`).join('') || '<p class="text-xs text-gray-500">Keine Quellen.</p>';
        } catch(error) { modalError.textContent = 'Fehler: ' + error.message; modalError.classList.remove('hidden'); }
    }

    addTimelineBtn.addEventListener('click', async () => { isEditMode = false; editingTimelineId = null; openModal(); modalTitle.textContent = 'Neue Timeline erstellen'; saveBtn.textContent = 'Erstellen'; timelineIdInput.readOnly = false; timelineIdInput.classList.remove('bg-gray-100'); deleteBtn.classList.add('hidden'); sapFilterContainer.innerHTML = '<p class="text-xs text-gray-500 text-center">Keine Filterregeln definiert.</p>'; editableOptionsDiv.style.display = 'block'; staticOptionsDiv.style.display = 'none'; await populateModalWithOptions(); });

    document.querySelector('main').addEventListener('click', async (e) => {
        const editButton = e.target.closest('.edit-timeline-btn');
        if (!editButton) return;
        isEditMode = true; editingTimelineId = editButton.dataset.timelineId; openModal(); modalTitle.textContent = 'Timeline bearbeiten'; saveBtn.textContent = 'Speichern'; timelineIdInput.readOnly = true; timelineIdInput.classList.add('bg-gray-100'); deleteBtn.classList.remove('hidden'); await populateModalWithOptions();
        try {
            const response = await fetch(`/api/timeline_config/${editingTimelineId}`); if (!response.ok) throw new Error('Fehler beim Laden');
            const data = await response.json();
            timelineNameInput.value = data.name; timelineIdInput.value = data.id; timelineTypeSelect.value = data.type;
            if (data.type === 'editable') {
                editableOptionsDiv.style.display = 'block'; staticOptionsDiv.style.display = 'none';
                (data.visible_test_cells || []).forEach(id => { const cb = form.querySelector(`input[name="visible_test_cells"][value="${id}"]`); if (cb) cb.checked = true; });
                (data.assignment_groups || []).forEach(group => { const cb = form.querySelector(`input[name="assignment_groups"][value="${group}"]`); if (cb) cb.checked = true; });
                sapFilterContainer.innerHTML = ''; const rules = Object.entries(data.sap_filter || {});
                if (rules.length > 0) rules.forEach(([c, v]) => addSapFilterRule(c, v.join(', '))); else sapFilterContainer.innerHTML = '<p class="text-xs text-gray-500 text-center">Keine Filterregeln.</p>';
            } else {
                editableOptionsDiv.style.display = 'none'; staticOptionsDiv.style.display = 'block';
                (data.data_files || []).forEach(f => { const cb = form.querySelector(`input[name="data_files_source"][value="${f}"]`); if (cb) cb.checked = true; });
            }
        } catch (error) { modalError.textContent = error.message; modalError.classList.remove('hidden'); }
    });

    closeBtn.addEventListener('click', closeModal); cancelBtn.addEventListener('click', closeModal);
    addSapFilterRuleBtn.addEventListener('click', () => addSapFilterRule());
    timelineNameInput.addEventListener('input', () => { if (!isEditMode) timelineIdInput.value = timelineNameInput.value.toLowerCase().replace(/\s+/g, '_').replace(/[^a-z0-9_]/g, ''); });
    timelineTypeSelect.addEventListener('change', () => { const isEditable = timelineTypeSelect.value === 'editable'; editableOptionsDiv.style.display = isEditable ? 'block' : 'none'; staticOptionsDiv.style.display = isEditable ? 'none' : 'block'; });

    deleteBtn.addEventListener('click', async () => {
        if (!isEditMode || !confirm(`Timeline wirklich löschen?`)) return;
        try { await fetch(`/api/admin/delete_timeline/${editingTimelineId}`, { method: 'DELETE' }); window.location.reload(); } catch (error) { modalError.textContent = error.message; modalError.classList.remove('hidden'); }
    });

    form.addEventListener('submit', async (e) => {
        e.preventDefault(); const formData = new FormData(form); const type = formData.get('timelineType');
        let data = { name: formData.get('timelineName'), id: formData.get('timelineId'), type: type };
        if (type === 'editable') {
            const sapFilter = {};
            sapFilterContainer.querySelectorAll('.sap-filter-rule').forEach(ruleDiv => {
                const c = ruleDiv.querySelector('input[name="sap_filter_column"]').value.trim();
                const v = ruleDiv.querySelector('input[name="sap_filter_values"]').value.trim();
                if (c && v) sapFilter[c] = v.split(',').map(s => s.trim()).filter(Boolean);
            });
            data.visible_test_cells = formData.getAll('visible_test_cells'); data.assignment_groups = formData.getAll('assignment_groups'); data.sap_filter = sapFilter;
        } else { data.data_files = formData.getAll('data_files_source'); }

        try {
            await fetch(isEditMode ? `/api/admin/update_timeline/${editingTimelineId}` : "{{ url_for('api_create_timeline') }}", { method: isEditMode ? 'PUT' : 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
            window.location.reload();
        } catch (error) { modalError.textContent = error.message; modalError.classList.remove('hidden'); }
    });
});
</script>
</body>
</html>
"""

PROCESS_TEMPLATES_MANAGEMENT_HTML = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <title>MBET - Prozess-Templates verwalten</title>
    <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?display=swap&family=Inter:wght@400;500;600;700">
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.0/Sortable.min.js"></script>
    <style>
        body { font-family: 'Inter', sans-serif; }
        .type-list-item.selected { background-color: #eef2ff; color: #4338ca; font-weight: 600; }

        /* Styles for TFS Step List */
        .step-list { list-style: none; padding: 0; margin: 0; space-y: 0.75rem; }
        .step-list-item-wrapper.main-step > .step-list-item { background-color: #f9fafb; border-top-width: 2px; border-bottom-width: 2px; border-color: #e5e7eb; }
        .step-list-item-wrapper.main-step .step-name-input { font-weight: 600; font-size: 0.95rem; }
        .step-list-item-wrapper.sub-step { margin-left: 2.5rem; }
        .step-list-item { background-color: white; border: 1px solid #e5e7eb; border-radius: 0.5rem; padding: 1rem; display: grid; grid-template-columns: auto 1fr 1fr auto auto; gap: 1rem; align-items: center; }
        .step-list-item .drag-handle { cursor: grab; color: #9ca3af; }

        /* Styles for Timeline Event List (NEU) */
        .timeline-event-item .drag-handle { cursor: grab; color: #9ca3af; }

        .sortable-ghost { background-color: #dbeafe; opacity: 0.7; }
        .form-input, .form-select { @apply block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500 sm:text-sm; }
        .action-button { @apply text-slate-400 hover:text-slate-700 p-2 rounded-full hover:bg-slate-100; }

        /* Styles for Tabs (NEU) */
        .tab-btn.text-blue-600 { color: #2563eb; }
        .tab-btn.border-blue-600 { border-color: #2563eb; }

        /* Styles für die einklappbare Sidebar */
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }

        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator {
            display: none;
        }
        #sidebar.is-collapsed .sidebar-separator {
            display: block;
            height: 1px;
            background-color: #e2e8f0;
            margin: 0.5rem 0.75rem;
        }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center sticky top-0 z-50 justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Engine Types</h1>
        </header>
        <div class="p-4 sm:p-6 lg:p-8">
            <div class="max-w-7xl mx-auto">
                <div class="flex justify-between items-center mb-6">
                    <div>
                        <h2 class="text-2xl font-bold tracking-tight text-slate-900">TFS & Timeline</h2>
                        <p class="text-sm text-slate-500 mt-1">Definieren Sie hier die Prozessabfolgen für jeden Triebwerkstyp.</p>
                    </div>
                    <button id="saveAllChangesBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 disabled:opacity-50">
                        <span class="material-icons-outlined text-base -ml-0.5">save</span>
                        Änderungen speichern
                    </button>
                </div>

            <div class="grid grid-cols-1 lg:grid-cols-4 gap-8">
                <!-- Linke Spalte -->
                <div class="lg:col-span-1 bg-white p-4 rounded-lg shadow-md border border-slate-200">
                    <h2 class="text-lg font-semibold text-slate-800 mb-3">Engine Types</h2>
                    <ul id="engineTypesList" class="space-y-1"></ul>
                    <div class="mt-4 pt-4 border-t border-slate-200">
                        <button id="addNewTypeBtn" class="w-full text-sm font-medium text-blue-600 hover:text-blue-700 p-2 rounded-md hover:bg-blue-50 text-center">
                            + Neuen Typ hinzufügen
                        </button>
                    </div>
                </div>

                <!-- Rechte Spalte (MODIFIZIERT) -->
                <div class="lg:col-span-3 bg-white p-6 rounded-lg shadow-md border border-slate-200">
                    <div id="templateDetailView" class="hidden">
                        <div class="flex justify-between items-start mb-4">
                            <div>
                                <h2 class="text-2xl font-bold text-slate-800">Template für "<span id="selectedTypeName"></span>"</h2>
                                <div class="mt-2 border-b border-slate-200">
                                    <nav class="-mb-px flex space-x-6" aria-label="Tabs">
                                        <button id="tab-tfs" onclick="switchTemplateView('tfs')" class="tab-btn whitespace-nowrap py-3 px-1 border-b-2 font-medium text-sm">TFS Schritte</button>
                                        <button id="tab-timeline" onclick="switchTemplateView('timeline')" class="tab-btn whitespace-nowrap py-3 px-1 border-b-2 font-medium text-sm">Timeline Events</button>
                                    </nav>
                                </div>
                            </div>
                            <button id="deleteTypeBtn" class="text-sm font-medium text-red-600 hover:text-red-800 flex items-center gap-1">
                                <span class="material-icons-outlined text-base">delete_outline</span>
                                Diesen Typ löschen
                            </button>
                        </div>

                        <!-- Container für TFS-Schritte (bestehende Logik) -->
                        <div id="tfsStepsView">
                            <p class="text-xs text-slate-500 mb-4">Schritte per Drag & Drop sortieren. Haupt- und Unterschritte können nicht gemischt werden.</p>
                            <ul id="stepsList" class="step-list"></ul>
                            <div class="mt-6 pt-6 border-t border-slate-200">
                                <button id="addNewMainStepBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-green-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-500">
                                    <span class="material-icons-outlined text-base -ml-0.5">add</span>
                                    Neuen Hauptschritt hinzufügen
                                </button>
                            </div>
                        </div>

                        <!-- NEUER Container für Timeline-Events -->
                        <div id="timelineEventsView" class="hidden">
                            <p class="text-xs text-slate-500 mb-4">Definieren Sie die Standard-Event-Abfolge für die Planungs-Timeline. Events per Drag & Drop sortieren.</p>
                            <ul id="timelineEventsList" class="space-y-3">
                                <!-- Inhalt wird per JS generiert -->
                            </ul>
                            <div class="mt-6 pt-6 border-t border-slate-200">
                                <button id="addNewTimelineEventBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-sky-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-sky-500">
                                    <span class="material-icons-outlined text-base -ml-0.5">add</span>
                                    Neues Timeline-Event hinzufügen
                                </button>
                            </div>
                        </div>
                    </div>

                    <div id="placeholderView" class="text-center py-16">
                        <span class="material-icons-outlined text-5xl text-slate-300">rule_folder</span>
                        <p class="mt-2 text-slate-500">Bitte wählen Sie links einen Triebwerkstyp aus.</p>
                    </div>
                </div>
            </div>
        </div>
    </main>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
    // --- Logik für die einklappbare Sidebar ---
    const sidebar = document.getElementById('sidebar');
    const sidebarToggleBtn = document.getElementById('sidebar-toggle');
    if (sidebar && sidebarToggleBtn) {
        const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
        const applyState = (isCollapsed) => {
            if (isCollapsed) {
                sidebar.classList.add('is-collapsed');
                toggleBtnIcon.textContent = 'menu';
            } else {
                sidebar.classList.remove('is-collapsed');
                toggleBtnIcon.textContent = 'chevron_left';
            }
        };
        sidebarToggleBtn.addEventListener('click', () => {
            const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
            localStorage.setItem('sidebarCollapsed', isNowCollapsed);
            applyState(isNowCollapsed);
        });
        const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
        applyState(savedState);
    }

    let templatesData = {};
    let availableDocs = [];
    let selectedType = null;
    let hasChanges = false;
    let currentView = 'tfs'; // NEU: 'tfs' oder 'timeline'

    const dom = {
        engineTypesList: document.getElementById('engineTypesList'),
        stepsList: document.getElementById('stepsList'),
        selectedTypeName: document.getElementById('selectedTypeName'),
        addNewTypeBtn: document.getElementById('addNewTypeBtn'),
        addNewMainStepBtn: document.getElementById('addNewMainStepBtn'),
        deleteTypeBtn: document.getElementById('deleteTypeBtn'),
        saveAllChangesBtn: document.getElementById('saveAllChangesBtn'),
        placeholderView: document.getElementById('placeholderView'),
        templateDetailView: document.getElementById('templateDetailView'),
        tabTfs: document.getElementById('tab-tfs'),
        tabTimeline: document.getElementById('tab-timeline'),
        tfsStepsView: document.getElementById('tfsStepsView'),
        timelineEventsView: document.getElementById('timelineEventsView'),
        timelineEventsList: document.getElementById('timelineEventsList'),
        addNewTimelineEventBtn: document.getElementById('addNewTimelineEventBtn'),
    };

    function switchTemplateView(view) {
        currentView = view;
        dom.tfsStepsView.style.display = view === 'tfs' ? 'block' : 'none';
        dom.timelineEventsView.style.display = view === 'timeline' ? 'block' : 'none';

        dom.tabTfs.classList.toggle('border-blue-600', view === 'tfs');
        dom.tabTfs.classList.toggle('text-blue-600', view === 'tfs');
        dom.tabTfs.classList.toggle('border-transparent', view !== 'tfs');
        dom.tabTfs.classList.toggle('text-slate-500', view !== 'tfs');

        dom.tabTimeline.classList.toggle('border-blue-600', view === 'timeline');
        dom.tabTimeline.classList.toggle('text-blue-600', view === 'timeline');
        dom.tabTimeline.classList.toggle('border-transparent', view !== 'timeline');
        dom.tabTimeline.classList.toggle('text-slate-500', view !== 'timeline');
    }
    window.switchTemplateView = switchTemplateView;

    dom.stepsList.addEventListener('click', (e) => {
        // A) Unterschritt hinzufügen
        const addBtn = e.target.closest('.add-substep-btn');
        if (addBtn) {
            const mainStepContainer = addBtn.closest('.main-step-container');
            const parentId = mainStepContainer.querySelector('.main-step').dataset.id;

            let sublist = mainStepContainer.querySelector('.sub-step-list');
            if (!sublist) {
                sublist = document.createElement('ul');
                sublist.className = 'step-list sub-step-list';
                sublist.dataset.parentId = parentId;
                mainStepContainer.appendChild(sublist);
                // WICHTIG: Sortable für die neue Liste aktivieren
                initializeSortable(sublist, 'sub-steps');
            }

            const newStep = { id: `new_${Date.now()}`, parent_id: parentId, name: 'Neuer Unterschritt', document_template: null };
            sublist.appendChild(createStepElement(newStep));
            markAsChanged();
            return;
        }

        // B) Schritt löschen
        const deleteBtn = e.target.closest('.delete-step-btn');
        if (deleteBtn) {
            const wrapper = deleteBtn.closest('.step-list-item-wrapper');
            const isMain = wrapper.classList.contains('main-step');
            const message = isMain 
                ? "Möchten Sie diesen Hauptschritt und alle seine Unterschritte wirklich löschen?"
                : "Möchten Sie diesen Unterschritt wirklich löschen?";
            
            if (confirm(message)) {
                if (isMain) {
                    wrapper.closest('.main-step-container').remove();
                } else {
                    wrapper.remove();
                }
                markAsChanged();
            }
            return;
        }
    });

    // 2. Input-Handler für Textfelder (Änderungen erkennen)
    dom.stepsList.addEventListener('input', (e) => {
        if (e.target.classList.contains('step-name-input') || e.target.classList.contains('document-template-select')) {
            markAsChanged();
        }
    });
    
    async function fetchData() {
        try {
            const [templatesRes, docsRes] = await Promise.all([
                fetch("{{ url_for('api_get_process_templates') }}"),
                fetch("{{ url_for('api_get_available_documents') }}")
            ]);
            if (!templatesRes.ok || !docsRes.ok) throw new Error('Fehler beim Laden der Konfigurationsdaten.');

            templatesData = await templatesRes.json();
            availableDocs = await docsRes.json();
            migrateDataToHierarchical();
            renderTypeList();
            renderViews(); // Ersetzt den Aufruf an renderStepList
            updateSaveButtonState();
        } catch (error) { console.error(error); alert('Fehler: ' + error.message); }
    }

    function migrateDataToHierarchical() {
        Object.keys(templatesData).forEach(type => {
            const steps = templatesData[type].steps;
            if (steps.length > 0 && typeof steps[0].id === 'undefined') {
                console.log(`Migriere Daten für Typ: ${type}`);
                templatesData[type].steps = steps.map((step, index) => ({
                    id: String(index + 1), parent_id: null, name: step.name,
                    document_template: step.document_template
                }));
            }
        });
    }

    function renderTypeList() {
        dom.engineTypesList.innerHTML = '';
        Object.keys(templatesData).sort().forEach(type => {
            const li = document.createElement('li');
            li.className = 'type-list-item cursor-pointer p-2 rounded-md text-sm';
            if (type === selectedType) li.classList.add('selected');
            li.textContent = type;
            li.dataset.type = type;
            li.addEventListener('click', () => {
                if (hasChanges && !confirm("Sie haben ungespeicherte Änderungen. Wirklich den Typ wechseln? Die Änderungen gehen verloren.")) {
                    return;
                }
                selectedType = type; hasChanges = false;
                // Wichtig: Beim Wechsel immer auf die erste Ansicht zurücksetzen
                currentView = 'tfs';
                fetchData();
            });
            dom.engineTypesList.appendChild(li);
        });
    }

    function renderViews() {
        if (!selectedType || !templatesData[selectedType]) {
            dom.placeholderView.style.display = 'block';
            dom.templateDetailView.style.display = 'none';
            return;
        }

        dom.placeholderView.style.display = 'none';
        dom.templateDetailView.style.display = 'block';
        dom.selectedTypeName.textContent = selectedType;
        dom.deleteTypeBtn.style.display = selectedType === 'default' ? 'none' : 'block';

        renderStepList();
        renderTimelineEventsList();

        switchTemplateView(currentView);
    }

    function renderStepList() {
        dom.stepsList.innerHTML = '';
        const steps = templatesData[selectedType].steps;
        const stepMap = new Map(steps.map(s => [s.id, { ...s, children: [] }]));
        const rootSteps = [];
        steps.forEach(s => {
            if (s.parent_id && stepMap.has(s.parent_id)) {
                stepMap.get(s.parent_id).children.push(stepMap.get(s.id));
            } else if (!s.parent_id) {
                rootSteps.push(stepMap.get(s.id));
            }
        });
        rootSteps.forEach(step => {
            const mainStepWrapper = document.createElement('li');
            mainStepWrapper.className = 'main-step-container';
            mainStepWrapper.appendChild(createStepElement(step));
            if (step.children.length > 0) {
                const sublist = document.createElement('ul');
                sublist.className = 'step-list sub-step-list';
                sublist.dataset.parentId = step.id;
                step.children.forEach(child => sublist.appendChild(createStepElement(child)));
                mainStepWrapper.appendChild(sublist);
            }
            dom.stepsList.appendChild(mainStepWrapper);
        });
        initializeSortable(dom.stepsList, 'main-steps');
        document.querySelectorAll('.sub-step-list').forEach(sublist => {
            initializeSortable(sublist, 'sub-steps');
        });
    }

    function renderTimelineEventsList() {
        dom.timelineEventsList.innerHTML = '';
        if (!selectedType || !templatesData[selectedType]) return;

        const timelineEvents = templatesData[selectedType].timeline_events || [];

        if (timelineEvents.length === 0) {
            dom.timelineEventsList.innerHTML = '<p class="text-sm text-center text-slate-400 py-4">Noch keine Timeline-Events definiert.</p>';
        } else {
            timelineEvents.forEach(event => {
                dom.timelineEventsList.appendChild(createTimelineEventElement(event));
            });
        }

        Sortable.create(dom.timelineEventsList, {
            animation: 150,
            handle: '.drag-handle',
            ghostClass: 'sortable-ghost',
            onEnd: () => markAsChanged()
        });
        attachTimelineEventListeners();
    }

    function createTimelineEventElement(event) {
        const li = document.createElement('li');
        li.className = 'timeline-event-item grid grid-cols-[auto_1fr_1fr_1fr_auto] items-center gap-4 p-3 bg-white border rounded-lg';
        li.innerHTML = `
            <span class="material-icons-outlined drag-handle">drag_indicator</span>
            <div>
                <label class="block text-xs font-medium text-slate-500">Event Name</label>
                <input type="text" class="form-input mt-1 event-name-input" value="${event.name || ''}" />
            </div>
            <div>
                <label class="block text-xs font-medium text-slate-500">Dauer (Schichten)</label>
                <input type="number" min="1" class="form-input mt-1 event-duration-input" value="${event.duration_shifts || 1}" />
            </div>
            <div>
                <label class="block text-xs font-medium text-slate-500">Farbe</label>
                <input type="text" class="form-input mt-1 event-color-input" value="${event.color || 'grey'}" />
            </div>
            <button class="action-button delete-event-btn" title="Dieses Event löschen">
                <span class="material-icons-outlined">delete_outline</span>
            </button>
        `;
        return li;
    }

    function attachTimelineEventListeners() {
        dom.timelineEventsList.querySelectorAll('.event-name-input, .event-duration-input, .event-color-input').forEach(el => {
            el.addEventListener('input', () => markAsChanged());
        });
        dom.timelineEventsList.querySelectorAll('.delete-event-btn').forEach(btn => {
            btn.addEventListener('click', (e) => {
                if (confirm("Möchten Sie dieses Timeline-Event wirklich löschen?")) {
                    e.currentTarget.closest('.timeline-event-item').remove();
                    markAsChanged();
                }
            });
        });
    }

    function createStepElement(step) {
        const isMainStep = !step.parent_id;
        const docOptions = ['<option value="">-- Kein Dokument --</option>', ...availableDocs.map(doc => `<option value="${doc}" ${step.document_template === doc ? 'selected' : ''}>${doc}</option>`)].join('');
        const li = document.createElement('li');
        li.className = `step-list-item-wrapper ${isMainStep ? 'main-step' : 'sub-step'}`;
        li.dataset.id = step.id;
        const addSubstepButtonHtml = isMainStep ? `
            <button class="action-button add-substep-btn" title="Unterschritt hinzufügen">
                <span class="material-icons-outlined">add_circle_outline</span>
            </button>
        ` : `<div class="w-10"></div>`;
        li.innerHTML = `
            <div class="step-list-item">
                <span class="material-icons-outlined drag-handle">drag_indicator</span>
                <div>
                    <label class="block text-xs font-medium text-slate-500">Schrittname</label>
                    <input type="text" class="form-input mt-1 step-name-input" value="${step.name}" />
                </div>
                <div>
                    <label class="block text-xs font-medium text-slate-500">Dokument-Template</label>
                    <select class="form-select mt-1 document-template-select">${docOptions}</select>
                </div>
                ${addSubstepButtonHtml}
                <button class="action-button delete-step-btn" title="Diesen Schritt löschen">
                    <span class="material-icons-outlined">delete_outline</span>
                </button>
            </div>
        `;
        return li;
    }

    function initializeSortable(element, groupName) {
        Sortable.create(element, {
            group: groupName,
            animation: 150,
            handle: '.drag-handle',
            ghostClass: 'sortable-ghost',
            onEnd: () => markAsChanged()
        });
    }


    function markAsChanged() {
        hasChanges = true;
        updateSaveButtonState();
    }

    function updateSaveButtonState() {
        dom.saveAllChangesBtn.disabled = !hasChanges;
    }

    function readDataFromDOM() {
        const steps = [];
        let mainStepCounter = 1;
        document.querySelectorAll('.main-step-container').forEach(container => {
            const mainStepWrapper = container.querySelector('.main-step');
            const mainStepItem = mainStepWrapper.querySelector('.step-list-item');
            const mainStepId = String(mainStepCounter++);
            steps.push({
                id: mainStepId,
                parent_id: null,
                name: mainStepItem.querySelector('.step-name-input').value.trim(),
                document_template: mainStepItem.querySelector('.document-template-select').value || null
            });
            const sublist = container.querySelector('.sub-step-list');
            if (sublist) {
                Array.from(sublist.children).forEach((subLi, index) => {
                    const subStepItem = subLi.querySelector('.step-list-item');
                    const subStepId = `${mainStepId}.${index + 1}`;
                    steps.push({
                        id: subStepId,
                        parent_id: mainStepId,
                        name: subStepItem.querySelector('.step-name-input').value.trim(),
                        document_template: subStepItem.querySelector('.document-template-select').value || null
                    });
                });
            }
        });

        const timelineEvents = [];
        dom.timelineEventsList.querySelectorAll('.timeline-event-item').forEach(li => {
            const name = li.querySelector('.event-name-input').value.trim();
            const duration = parseInt(li.querySelector('.event-duration-input').value, 10) || 1;
            const color = li.querySelector('.event-color-input').value.trim();
            if (name) {
                timelineEvents.push({
                    name: name,
                    duration_shifts: duration,
                    color: color
                });
            }
        });

        return {
            [selectedType]: {
                steps: steps,
                timeline_events: timelineEvents
            }
        };
    }

    dom.addNewTypeBtn.addEventListener('click', () => {
        const newTypeName = prompt("Name für den neuen Triebwerkstyp (z.B. PW1900G):");
        if (newTypeName && newTypeName.trim()) {
            const trimmedName = newTypeName.trim();
            if (templatesData[trimmedName]) {
                alert("Ein Template mit diesem Namen existiert bereits."); return;
            }
            templatesData[trimmedName] = { steps: [], timeline_events: [] }; // Initialisiere beide Felder
            selectedType = trimmedName; 
            markAsChanged(); 
            renderTypeList(); 
            renderViews();
        }
    });

    dom.deleteTypeBtn.addEventListener('click', async () => {
            if (selectedType && selectedType !== 'default' && confirm(`Möchten Sie das Template für "${selectedType}" wirklich und endgültig löschen?`)) {
                // 1. Aus lokalem Objekt löschen
                delete templatesData[selectedType]; 
                
                // 2. Sofort an den Server senden
                try {
                    const response = await fetch("{{ url_for('api_save_process_templates') }}", {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify(templatesData) // Sende das gesamte, bereinigte Objekt
                    });
    
                    if (!response.ok) {
                        const error = await response.json(); 
                        throw new Error(error.error || 'Fehler beim Löschen auf dem Server.');
                    }
    
                    // 3. Erfolg: Zustand zurücksetzen
                    hasChanges = false;
                    selectedType = null;
                    updateSaveButtonState();
                    
                    alert("Template erfolgreich gelöscht.");
                    
                    // 4. UI neu laden
                    renderTypeList();
                    renderViews();
    
                } catch (error) {
                    console.error(error); 
                    alert("Fehler: " + error.message);
                    // Bei Fehler: Daten neu laden, um inkonsistenten Zustand zu vermeiden
                    fetchData();
                }
            }
        });

    dom.addNewMainStepBtn.addEventListener('click', () => {
        if (selectedType) {
            const newStep = { id: `new_${Date.now()}`, parent_id: null, name: 'Neuer Hauptschritt', document_template: null, children: [] };
            const mainStepContainer = document.createElement('li');
            mainStepContainer.className = 'main-step-container';
            mainStepContainer.appendChild(createStepElement(newStep));
            dom.stepsList.appendChild(mainStepContainer);         
            markAsChanged();
        }
    });

    dom.addNewTimelineEventBtn.addEventListener('click', () => {
        const newEvent = { name: 'Neues Event', duration_shifts: 1, color: 'grey' };
        dom.timelineEventsList.appendChild(createTimelineEventElement(newEvent));
        attachTimelineEventListeners();
        markAsChanged();
    });

    dom.saveAllChangesBtn.addEventListener('click', async () => {
        const dataToSave = readDataFromDOM();

        // Da die readDataFromDOM jetzt das gesamte Objekt für den Typ liefert,
        // muss der Server nicht geändert werden. Er überschreibt den Schlüssel.
        const fullDataToSend = { ...templatesData, ...dataToSave };

        try {
            const response = await fetch("{{ url_for('api_save_process_templates') }}", {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                // Sende das vollständige, aktualisierte Objekt
                body: JSON.stringify(fullDataToSend)
            });
            if (!response.ok) {
                const error = await response.json(); throw new Error(error.error || 'Fehler beim Speichern.');
            }
            hasChanges = false;
            updateSaveButtonState();
            alert("Änderungen erfolgreich gespeichert!");
            fetchData();
        } catch (error) {
            console.error(error); alert("Fehler: " + error.message);
        }
    });

    fetchData();
});
</script>
</body>
</html>
"""

FILE_MANAGER_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <title>MBET - Dateiverwaltung</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <!-- NEU: Material Symbols für die Icons aus dem Design-Beispiel -->
    <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <style>
        body { font-family: 'Inter', sans-serif; }
        .material-symbols-outlined { font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24; }
        .action-btn { @apply inline-flex items-center justify-center w-9 h-9 rounded-full text-slate-500 hover:bg-slate-200 hover:text-slate-700 transition-colors; }
        .action-btn.delete:hover { @apply bg-red-100 text-red-600; }

        /* Styles für die einklappbare Sidebar */
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }

        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator {
            display: none;
        }
        #sidebar.is-collapsed .sidebar-separator {
            display: block;
            height: 1px;
            background-color: #e2e8f0;
            margin: 0.5rem 0.75rem;
        }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <!-- Hauptinhalt -->
    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Dateiverwaltung</h1>
        </header>

        <div class="p-4 sm:p-6 lg:p-8">
            <div class="max-w-7xl mx-auto">
                <header class="flex flex-wrap items-center justify-between gap-4 mb-6">
                    <div class="flex flex-col gap-1">
                        <h2 class="text-2xl font-bold text-slate-800">Stammverzeichnis (db)</h2>
                        <p class="text-sm font-normal text-slate-500">Laden, verwalten und ersetzen Sie Ihre Konfigurationsdateien.</p>
                    </div>
                    <div class="flex items-center gap-2"> <!-- NEUER WRAPPER -->
                        <!-- Button zum Downloaden des gesamten Ordners -->
                        <a href="{{ url_for('download_db_folder', folderpath='ROOT') }}" class="flex min-w-[84px] cursor-pointer items-center justify-center gap-2 overflow-hidden rounded-lg h-11 px-5 bg-slate-100 text-slate-700 text-sm font-medium leading-normal tracking-[0.015em] ring-1 ring-slate-200 hover:bg-slate-200">
                            <span class="material-symbols-outlined">archive</span>
                            <span class="truncate">Alles zippen</span>
                        </a>
                        <!-- Bestehender Upload-Button -->
                        <form method="POST" action="{{ url_for('upload_new_db_file', dirpath='.') }}" enctype="multipart/form-data">
                            <label class="flex min-w-[84px] cursor-pointer items-center justify-center gap-2 overflow-hidden rounded-lg h-11 px-5 bg-blue-600 text-white text-sm font-bold leading-normal tracking-[0.015em] hover:bg-blue-700">
                                <span class="material-symbols-outlined">upload_file</span>
                                <span class="truncate">Datei hochladen</span>
                                <input type="file" name="new_file" class="hidden" onchange="this.form.submit()">
                            </label>
                        </form>
                    </div>
                </header>

                {% with messages = get_flashed_messages(with_categories=true) %}
                  {% if messages %}
                    <div class="mb-4 space-y-2">
                    {% for category, message in messages %}
                      <div class="p-4 rounded-md text-sm {% if category == 'success' %}bg-green-100 text-green-800{% elif category == 'error' %}bg-red-100 text-red-800{% else %}bg-blue-100 text-blue-800{% endif %}" role="alert">
                        <p class="font-medium">{{ message }}</p>
                      </div>
                    {% endfor %}
                    </div>
                  {% endif %}
                {% endwith %}

                <div class="overflow-hidden bg-white rounded-xl border border-slate-200 shadow-sm">
                    <div class="overflow-x-auto">
                        <table class="w-full text-sm text-left">
                            <thead class="bg-slate-50 text-xs text-slate-500 uppercase tracking-wider">
                                <tr>
                                    <th class="px-6 py-3" scope="col">Dateiname</th>
                                    <th class="px-6 py-3 text-right" scope="col">Aktionen</th>
                                </tr>
                            </thead>
                            <tbody class="divide-y divide-slate-200">
                                {% for item in file_tree %}
                                    <tr class="hover:bg-slate-50">
                                        <td class="px-6 py-4 font-medium text-slate-800 whitespace-nowrap">
                                            <div class="flex items-center gap-3" style="padding-left: {{ item.depth * 24 }}px;">
                                                {% if item.type == 'dir' %}
                                                    <span class="material-symbols-outlined text-amber-500">folder</span>
                                                {% else %}
                                                    <span class="material-symbols-outlined text-slate-400">description</span>
                                                {% endif %}
                                                <span>{{ item.name }}</span>
                                            </div>
                                        </td>
                                        <td class="px-6 py-4 text-right">
                                            <div class="flex items-center justify-end space-x-1">
                                                {% if item.type == 'dir' %}
                                                    <!-- NEU: Download-Button für Ordner -->
                                                    <a href="{{ url_for('download_db_folder', folderpath=item.path) }}" class="action-btn" title="Diesen Ordner als ZIP herunterladen">
                                                        <span class="material-symbols-outlined text-lg">folder_zip</span>
                                                    </a>
                                                    <form method="POST" action="{{ url_for('upload_new_db_file', dirpath=item.path) }}" enctype="multipart/form-data">
                                                        <label class="action-btn cursor-pointer" title="Neue Datei in diesen Ordner hochladen">
                                                            <span class="material-symbols-outlined text-lg">create_new_folder</span>
                                                            <input type="file" name="new_file" class="hidden" onchange="this.form.submit()">
                                                        </label>
                                                    </form>
                                                {% else %}
                                                    <a href="{{ url_for('download_db_file', filepath=item.path) }}" class="action-btn" title="Datei herunterladen">
                                                        <span class="material-symbols-outlined text-lg">download</span>
                                                    </a>
                                                    <form method="POST" action="{{ url_for('upload_db_file', filepath=item.path) }}" enctype="multipart/form-data">
                                                        <label class="action-btn cursor-pointer" title="Datei ersetzen">
                                                            <span class="material-symbols-outlined text-lg">upload</span>
                                                            <input type="file" name="file" class="hidden" onchange="this.form.submit()">
                                                        </label>
                                                    </form>
                                                    <form method="POST" action="{{ url_for('delete_db_file', filepath=item.path) }}">
                                                        <button type="button" class="action-btn delete" title="Datei löschen" onclick="confirmDelete(this, '{{ item.name }}')">
                                                            <span class="material-symbols-outlined text-lg">delete</span>
                                                        </button>
                                                    </form>
                                                {% endif %}
                                            </div>
                                        </td>
                                    </tr>
                                {% else %}
                                    <tr>
                                        <td colspan="2" class="p-8 text-center text-slate-500">Der Ordner 'db' ist leer oder konnte nicht gelesen werden.</li>
                                    </tr>
                                {% endfor %}
                            </tbody>
                        </table>
                    </div>
                </div>
                <div class="mt-4 text-xs text-gray-500">
                    <p><span class="font-semibold">Hinweis:</span> Änderungen an Konfigurationsdateien (.csv) erfordern in der Regel einen Neustart der Anwendung, damit sie wirksam werden.</p>
                </div>
            </div>
        </div>
    </main>
</div>
<script>
    // --- Logik für die einklappbare Sidebar ---
    document.addEventListener('DOMContentLoaded', () => {
        const sidebar = document.getElementById('sidebar');
        const sidebarToggleBtn = document.getElementById('sidebar-toggle');
        if (sidebar && sidebarToggleBtn) {
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
            const applyState = (isCollapsed) => {
                if (isCollapsed) {
                    sidebar.classList.add('is-collapsed');
                    toggleBtnIcon.textContent = 'menu';
                } else {
                    sidebar.classList.remove('is-collapsed');
                    toggleBtnIcon.textContent = 'chevron_left';
                }
            };
            sidebarToggleBtn.addEventListener('click', () => {
                const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
                localStorage.setItem('sidebarCollapsed', isNowCollapsed);
                applyState(isNowCollapsed);
            });
            const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
            applyState(savedState);
        }

        // --- Bestehende Logik für die Dateiverwaltung ---
        function confirmDelete(buttonElement, fileName) {
            const message = `Möchten Sie die Datei '${fileName}' wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.`;
            if (confirm(message)) {
                const form = buttonElement.closest('form');
                if (form) {
                  form.submit();
                }
            }
        }

        // Mache die Funktion global verfügbar, damit onclick im HTML sie finden kann
        window.confirmDelete = confirmDelete;
    });
</script>

</body>
</html>
"""

EMPLOYEE_DASHBOARD_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>MBET - Mitarbeiter Dashboard</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
    <script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
    <style>
        body { font-family: 'Inter', sans-serif; }
        .kpi-card .text-gray-600 { color: #4b5563 !important; }
        .kpi-card .text-gray-900 { color: #111827 !important; }
        .text-slate-800 { color: #1e293b !important; }
        .text-gray-700 { color: #374151 !important; }
        .text-sm.text-gray-600 { color: #4b5563 !important; }
        .text-sm.text-gray-500 { color: #6b7280 !important; }
        .form-select, .form-input, .form-checkbox { @apply border-gray-300 focus:border-blue-500 focus:ring-blue-500 rounded-md shadow-sm bg-white text-gray-700; }
        .apexcharts-tooltip { @apply bg-white text-gray-800 border border-gray-200 shadow-lg; }
        .custom-scrollbar::-webkit-scrollbar { width: 6px; }
        .custom-scrollbar::-webkit-scrollbar-track { background: #e5e7eb; border-radius: 10px; }
        .custom-scrollbar::-webkit-scrollbar-thumb { background: #9ca3af; border-radius: 10px; }
        .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: #6b7280; }
        .apexcharts-tooltip-custom { background: #fff; color: #333; border: 1px solid #e0e0e0; padding: 8px; border-radius: 5px; box-shadow: 0 2px 5px rgba(0,0,0,0.15); }

        /* Styles für die einklappbare Sidebar */
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator {
            display: none;
        }
        #sidebar.is-collapsed .sidebar-separator {
            display: block;
            height: 1px;
            background-color: #e2e8f0;
            margin: 0.5rem 0.75rem;
        }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
        .sfm-tooltip {
            display: none;
            position: absolute;
            top: 100%;
            left: 0;
            width: 320px;
            background: white;
            border: 1px solid #e2e8f0;
            box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
            z-index: 100;
            border-radius: 0.75rem;
            padding: 1rem;
            margin-top: 0.5rem;
        }
        .group:hover .sfm-tooltip {
            display: block;
        }
        .tooltip-section-title {
            font-size: 10px;
            font-weight: 800;
            text-transform: uppercase;
            letter-spacing: 0.05em;
            color: #94a3b8;
            margin-bottom: 0.5rem;
            border-bottom: 1px solid #f1f5f9;
            padding-bottom: 0.25rem;
        }
        .task-checkbox:checked + span { text-decoration: line-through; color: #94a3b8; }
        .task-delete-btn { display: none; }
        .task-item:hover .task-delete-btn { display: block; }
        /* Drag & Drop Styles */
        .sortable-ghost { opacity: 0.4; background-color: #f1f5f9; border: 1px dashed #cbd5e1; }
        .sortable-drag { cursor: grabbing; }
        .task-item { cursor: default; }
        /* Griff-Bereich für Drag (optional, hier machen wir das ganze Element ziehbar außer Input) */
        .drag-handle { cursor: grab; color: #cbd5e1; margin-right: 4px; }
        .drag-handle:active { cursor: grabbing; }
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

<main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Shop Floor Management</h1>
        </header>

        <div class="p-4 sm:p-6 lg:p-8"> <!-- WICHTIG: Padding-Wrapper öffnen -->
            
            <!-- NAV TABS -->
            <div class="mb-6 border-b border-slate-200">
                <nav class="-mb-px flex space-x-8">
                    <button onclick="switchEmpTab('sfm', this)" class="emp-tab-btn active border-b-2 border-blue-500 text-blue-600 px-1 pb-4 text-sm font-medium">SFM Board</button>
                    <button onclick="switchEmpTab('overview', this)" class="emp-tab-btn border-b-2 border-transparent text-slate-500 hover:text-slate-700 hover:border-slate-300 px-1 pb-4 text-sm font-medium">Qualifikationenübersicht</button>
                    <button onclick="switchEmpTab('matrix', this)" class="emp-tab-btn border-b-2 border-transparent text-slate-500 hover:text-slate-700 hover:border-slate-300 px-1 pb-4 text-sm font-medium">Kompetenz-Matrix</button>
                    <button onclick="switchEmpTab('projektplan', this)" class="emp-tab-btn border-b-2 border-transparent text-slate-500 hover:text-slate-700 hover:border-slate-300 px-1 pb-4 text-sm font-medium">Projektplan</button>
                </nav>
            </div>
            <!-- TAB 0: SFM BOARD -->
            <div id="view-sfm" class="emp-tab-content">
                
                <!-- Reihe 1: KPIs (Dashboard Design) -->
                <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
                    
            <!-- SAFETY / CRITICAL KACHEL -->
            <div class="group bg-white rounded-lg border border-slate-200 shadow-sm p-6 relative flex flex-col justify-center cursor-help">
                <div class="absolute left-0 top-0 bottom-0 w-1 {{ 'bg-red-500' if sfm_stats.critical_total > 0 else 'bg-green-500' }}"></div>
                
                <div class="flex items-center gap-4 mb-3">
                    <div class="h-10 w-10 rounded-full flex items-center justify-center flex-shrink-0 {{ 'bg-red-50 text-red-600' if sfm_stats.critical_total > 0 else 'bg-green-50 text-green-600' }}">
                        <span class="material-icons-outlined text-xl">health_and_safety</span>
                    </div>
                    <h2 class="text-lg font-bold text-slate-800">Safety & Critical</h2>
                </div>
            
                <div class="pl-[3.5rem]">
                    <div class="flex items-baseline gap-2">
                        <span class="text-3xl font-bold text-slate-800">{{ sfm_stats.critical_total }}</span>
                        <span class="text-sm font-medium text-slate-500 uppercase tracking-wider">Kritische Themen</span>
                    </div>
                </div>
            
                <!-- TOOLTIP SAFETY -->
                <div class="sfm-tooltip">
                    {% if sfm_stats.details.critical_prio %}
                        <div class="tooltip-section-title text-red-500">Prio: Critical</div>
                        <ul class="text-xs text-slate-600 space-y-1 mb-4">
                            {% for item in sfm_stats.details.critical_prio %}<li>• {{ item }}</li>{% endfor %}
                        </ul>
                    {% endif %}
                    
                    {% if sfm_stats.details.blocked_units %}
                        <div class="tooltip-section-title text-red-600">Anlagen-Sperren</div>
                        <ul class="text-xs text-slate-600 space-y-1 mb-4">
                            {% for item in sfm_stats.details.blocked_units %}<li>• {{ item }}</li>{% endfor %}
                        </ul>
                    {% endif %}
            
                    {% if sfm_stats.details.blocked_hw %}
                        <div class="tooltip-section-title text-orange-600">Hardware-Sperren</div>
                        <ul class="text-xs text-slate-600 space-y-1">
                            {% for item in sfm_stats.details.blocked_hw %}<li>• {{ item }}</li>{% endfor %}
                        </ul>
                    {% endif %}
            
                    {% if not sfm_stats.details.critical_prio and not sfm_stats.details.blocked_units and not sfm_stats.details.blocked_hw %}
                        <p class="text-xs text-green-600 italic">Keine kritischen Blockaden vorhanden.</p>
                    {% endif %}
                </div>
            </div>
            
            <!-- QUALITY / OPEN KACHEL -->
            <div class="group bg-white rounded-lg border border-slate-200 shadow-sm p-6 relative flex flex-col justify-center cursor-help">
                <div class="absolute left-0 top-0 bottom-0 w-1 bg-amber-500"></div>
                
                <div class="flex items-center gap-4 mb-3">
                    <div class="h-10 w-10 rounded-full flex items-center justify-center flex-shrink-0 bg-amber-50 text-amber-600">
                        <span class="material-icons-outlined text-xl">assignment_late</span>
                    </div>
                    <h2 class="text-lg font-bold text-slate-800">Quality & Open</h2>
                </div>
            
                <div class="pl-[3.5rem]">
                    <div class="flex items-baseline gap-2">
                        <span class="text-3xl font-bold text-slate-800">{{ sfm_stats.open_issues }}</span>
                        <span class="text-sm font-medium text-slate-500 uppercase tracking-wider">Offene Issues</span>
                    </div>
                </div>
            
                <!-- TOOLTIP QUALITY -->
                <div class="sfm-tooltip">
                    <div class="tooltip-section-title">Alle offenen Issues</div>
                    <div class="max-h-60 overflow-y-auto pr-2 custom-scrollbar">
                        <ul class="text-xs text-slate-600 space-y-2">
                            {% for item in sfm_stats.details.critical_prio + sfm_stats.details.blocked_units + sfm_stats.details.blocked_hw + sfm_stats.details.other_open %}
                                <li class="border-b border-slate-50 pb-1">• {{ item }}</li>
                            {% else %}
                                <li class="italic text-slate-400">Keine offenen Issues.</li>
                            {% endfor %}
                        </ul>
                    </div>
                </div>
            </div>
                
                <!-- PEOPLE (Heute) -->
                    <div class="bg-white rounded-lg border border-slate-200 shadow-sm p-6 relative overflow-hidden flex flex-col justify-center">
                        <div class="absolute left-0 top-0 bottom-0 w-1 bg-blue-500"></div>
                        
                        <div class="flex items-center gap-4 mb-3">
                            <div class="h-10 w-10 rounded-full flex items-center justify-center flex-shrink-0 bg-blue-50 text-blue-600">
                                <span class="material-icons-outlined text-xl">groups</span>
                            </div>
                            <h2 class="text-lg font-bold text-slate-800">People (Heute)</h2>
                        </div>
                
                        <div class="pl-[3.5rem]">
                            <div class="flex items-baseline gap-3">
                                <span class="text-3xl font-bold text-slate-800" id="sfm-people-count">-</span>
                                
                                <!-- NEU: Differenz Anzeige -->
                                <div id="sfm-people-diff" class="flex items-center text-xs font-bold px-2 py-0.5 rounded-full hidden">
                                    <!-- Icon und Wert werden per JS gesetzt -->
                                </div>
                            </div>
                            <p class="text-sm font-medium text-slate-500 uppercase tracking-wider mt-1">Mitarbeiter anwesend</p>
                        </div>
                    </div>
                </div>
                <div class="mb-6 flex items-center gap-2 text-slate-400 border-b border-slate-100 pb-2 ml-1">
                    <span class="material-icons-outlined text-sm">calendar_today</span>
                    <span id="sfm-current-date-title" class="text-xs font-bold uppercase tracking-widest"></span>
                </div>
            <!-- ZEILE 2: KALENDER & INFOS -->
                <div class="grid grid-cols-1 lg:grid-cols-4 gap-6 h-[500px] mb-6">
                    
                    <!-- LINKER BEREICH: KALENDER (2 Spalten breit) -->
                    <div class="lg:col-span-3 bg-white rounded-xl shadow-sm border border-slate-200 flex flex-col overflow-hidden">
                        <div class="px-6 py-4 border-b border-slate-100 flex justify-between items-center bg-slate-50/50 flex-shrink-0">
                            <h3 class="font-bold text-slate-800 flex items-center gap-2">
                                <span class="material-icons-outlined text-slate-400">calendar_view_week</span> 2-Wochen Vorschau
                            </h3>
                            <span id="autoSaveIndicator" class="text-xs font-medium text-green-600 opacity-0 transition-opacity duration-500 flex items-center gap-1">
                                <span class="material-icons-outlined text-sm">cloud_done</span> Gespeichert
                            </span>
                        </div>
                        <div class="overflow-x-auto flex-grow custom-scrollbar">
                            <table class="w-full text-sm border-collapse h-full" id="sfmCalendarTable">
                                <thead class="bg-slate-50 text-slate-500 uppercase text-[10px] font-bold tracking-wider sticky top-0 z-20">
                                    <tr id="sfmCalendarHeader"></tr>
                                </thead>
                                <tbody id="sfmCalendarBody" class="divide-y divide-slate-200"></tbody>
                            </table>
                        </div>
                    </div>
            
                    <!-- RECHTER BEREICH: AKTUELLE INFOS (1 Spalte breit) -->
                    <div class="lg:col-span-1 bg-white rounded-xl shadow-sm border border-slate-200 flex flex-col overflow-hidden">
                        <div class="px-4 py-4 border-b border-slate-100 bg-blue-50/30 flex-shrink-0">
                            <h3 class="font-bold text-blue-900 flex items-center gap-2 text-sm">
                                <span class="material-icons-outlined text-blue-500">info</span> Aktuelle Infos / Schicht
                            </h3>
                        </div>
                        <div class="flex-grow overflow-y-auto custom-scrollbar p-2 bg-white" id="taskList-info">
                            <!-- JS gefüllt -->
                        </div>
                        <div class="p-3 border-t border-slate-100 bg-slate-50">
                            <input type="text" class="w-full text-sm border border-slate-200 rounded-lg p-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent placeholder-slate-400" 
                                placeholder="+ Info..." onkeydown="if(event.key === 'Enter') { addTask('info', this.value); this.value = ''; }">
                        </div>
                    </div>
                </div>
            
                <!-- ZEILE 3: THEMEN GRIDS -->
                <div class="grid grid-cols-1 md:grid-cols-3 gap-6 h-96">
                    
                    <!-- 1. QUALITY & SAFETY -->
                    <div class="bg-white rounded-xl shadow-sm border border-slate-200 flex flex-col overflow-hidden">
                        <div class="px-4 py-3 border-b border-slate-100 bg-slate-50/50 flex-shrink-0">
                            <h3 class="font-bold text-slate-800 flex items-center gap-2 text-sm">
                                <span class="material-icons-outlined text-amber-500">health_and_safety</span> Quality & Arbeitssicherheit
                            </h3>
                        </div>
                        <div class="flex-grow overflow-y-auto custom-scrollbar p-2" id="taskList-quality_safety"></div>
                        <div class="p-3 border-t border-slate-100 bg-white">
                            <input type="text" class="w-full text-sm border border-slate-200 rounded-lg p-2 focus:ring-2 focus:ring-amber-500 focus:border-transparent placeholder-slate-400" 
                                placeholder="+ Thema..." onkeydown="if(event.key === 'Enter') { addTask('quality_safety', this.value); this.value = ''; }">
                        </div>
                    </div>
            
                    <!-- 2. ERFOLGE & ESKALATIONEN (Spezial) -->
                    <div class="bg-white rounded-xl shadow-sm border border-slate-200 flex flex-col overflow-hidden">
                        <div class="px-4 py-3 border-b border-slate-100 bg-slate-50/50 flex-shrink-0">
                            <h3 class="font-bold text-slate-800 flex items-center gap-2 text-sm">
                                <span class="material-icons-outlined text-purple-500">campaign</span> Erfolge & Eskalationen
                            </h3>
                        </div>
                        <div class="flex-grow overflow-y-auto custom-scrollbar p-2" id="taskList-success_escalation"></div>
                        
                        <!-- Spezial Input Bereich -->
                        <div class="p-3 border-t border-slate-100 bg-white">
                            <div class="flex gap-2">
                                <input type="text" id="input-success_escalation" class="flex-grow text-sm border border-slate-200 rounded-lg p-2 focus:ring-2 focus:ring-purple-500 focus:border-transparent placeholder-slate-400" 
                                    placeholder="Eintrag verfassen...">
                            </div>
                            <div class="flex gap-2 mt-2">
                                <button onclick="addSpecialTask('success')" class="flex-1 bg-green-50 text-green-700 border border-green-200 hover:bg-green-100 text-xs font-bold py-1.5 px-2 rounded flex items-center justify-center gap-1 transition-colors">
                                    <span class="material-icons-outlined text-sm">emoji_events</span> Erfolg
                                </button>
                                <button onclick="addSpecialTask('escalation')" class="flex-1 bg-red-50 text-red-700 border border-red-200 hover:bg-red-100 text-xs font-bold py-1.5 px-2 rounded flex items-center justify-center gap-1 transition-colors">
                                    <span class="material-icons-outlined text-sm">warning</span> Eskalation
                                </button>
                            </div>
                        </div>
                    </div>
            
                    <!-- 3. BACKLOG / ALLGEMEIN -->
                    <div class="bg-white rounded-xl shadow-sm border border-slate-200 flex flex-col overflow-hidden">
                        <div class="px-4 py-3 border-b border-slate-100 bg-slate-50/50 flex-shrink-0">
                            <h3 class="font-bold text-slate-800 flex items-center gap-2 text-sm">
                                <span class="material-icons-outlined text-slate-400">format_list_bulleted</span> Backlog / Allgemein
                            </h3>
                        </div>
                        <div class="flex-grow overflow-y-auto custom-scrollbar p-2" id="taskList-general"></div>
                        <div class="p-3 border-t border-slate-100 bg-white">
                            <input type="text" class="w-full text-sm border border-slate-200 rounded-lg p-2 focus:ring-2 focus:ring-blue-500 focus:border-transparent placeholder-slate-400" 
                                placeholder="+ To-Do..." onkeydown="if(event.key === 'Enter') { addTask('general', this.value); this.value = ''; }">
                        </div>
                    </div>
                </div>
                <!-- ZEILE 4: EFFIZIENZ DIAGRAMM (MOCKUP) -->
                <div class="mt-6 bg-white rounded-xl shadow-sm border border-slate-200 p-6">
                    <div class="flex justify-between items-center mb-4">
                        <div>
                            <h3 class="font-bold text-slate-800 flex items-center gap-2">
                                <span class="material-icons-outlined text-blue-500">show_chart</span> Gesamteffizienz (OEE)
                            </h3>
                            <p class="text-xs text-slate-500">Täglicher Durchschnitt über den aktuellen Monat (Mockup-Daten)</p>
                        </div>
                        <div class="flex items-center gap-2">
                            <span class="text-xs font-bold bg-green-100 text-green-700 px-2 py-1 rounded">Ziel: > 85%</span>
                        </div>
                    </div>
                    
                    <!-- Chart Container -->
                    <div id="efficiencyChart" class="w-full h-64"></div>
                </div>
            </div>
            <!-- TAB 1: ÜBERSICHT -->
            <div id="view-overview" class="emp-tab-content hidden">
                <!-- Haupt-Grid -->
                <div class="grid grid-cols-1 lg:grid-cols-3 xl:grid-cols-4 gap-6">
                    
                    <!-- Spalte 1: Filter -->
                    <div class="lg:col-span-1 xl:col-span-1 space-y-6">
                        <div class="bg-white rounded-xl p-6 border border-gray-200 shadow-s text-slate-700">
                            <h3 class="text-lg font-semibold text-slate-800 mb-4">Filter</h3>
                            <div class="space-y-4">
                                <div>
                                    <label for="employeeFilter" class="block text-sm font-medium text-gray-700">Mitarbeiter</label>
                                    <select id="employeeFilter" class="form-select rounded-md mt-1 w-full">
                                        <option value="">Alle Mitarbeiter</option>
                                        {% for emp in data.employees_list %}
                                        <option value="{{ emp.id }}">{{ emp.name }}</option>
                                        {% endfor %}
                                    </select>
                                </div>
                                <div>
                                    <label class="block text-sm font-medium text-gray-700">Gruppen</label>
                                    <div id="groupCheckboxes" class="mt-1 space-y-1 max-h-32 overflow-y-auto p-2 border rounded-md custom-scrollbar">
                                        {% for group in data.groups_list %}
                                        <label class="flex items-center p-1 hover:bg-gray-50 rounded">
                                            <input type="checkbox" class="h-4 w-4 form-checkbox group-checkbox" value="{{ group }}">
                                            <span class="ml-2 text-sm text-gray-600">{{ group }}</span>
                                        </label>
                                        {% endfor %}
                                    </div>
                                </div>
                                <div>
                                    <label class="block text-sm font-medium text-gray-700">Spezifische Skills</label>
                                    <div id="skillCheckboxes" class="mt-1 space-y-1 max-h-32 overflow-y-auto p-2 border rounded-md custom-scrollbar">
                                        {% for skill in data.skills_list %}
                                        <label class="flex items-center p-1 hover:bg-gray-50 rounded">
                                            <input type="checkbox" class="h-4 w-4 form-checkbox skill-checkbox" value="{{ skill }}">
                                            <span class="ml-2 text-sm text-gray-600">{{ skill }}</span>
                                        </label>
                                        {% endfor %}
                                    </div>
                                </div>
                                <div id="activeFiltersContainer" class="pt-3 flex flex-wrap gap-2 border-t border-gray-200"></div>
                                <button id="clearFiltersBtn" class="mt-2 w-full text-sm text-blue-600 hover:underline">Alle Filter zurücksetzen</button>
                            </div>
                        </div>
                        <div id="filterSummaryCard" class="space-y-6"></div>
                        <div id="employeeDetailCard" class="bg-white rounded-xl p-6 border border-gray-200 shadow-xl text-slate-700 hidden">
                            <div id="employeeDetailContent"></div>
                        </div>
                    </div>

                    <!-- Spalte 2: Dashboard -->
                    <div class="lg:col-span-2 xl:col-span-3 space-y-6">
                        <div class="grid grid-cols-1 xl:grid-cols-3 gap-6">
                            <div class="xl:col-span-1 bg-white rounded-xl p-6 border border-gray-200 shadow-xl">
                                <h3 class="font-semibold text-slate-800 text-center mb-2">Qualifikationen</h3>
                                <div id="trainingStatusChart"></div>
                            </div>
                            <div class="xl:col-span-2 bg-white rounded-xl p-6 border border-gray-200 shadow-xl">
                                <h3 class="font-semibold text-slate-800 mb-2">Triebwerkstypen</h3>
                                <div id="skillDistributionChart"></div>
                            </div>
                        </div>
                        <div class="bg-white rounded-xl p-6 border border-gray-200 shadow-xl">
                            <div class="flex justify-between items-center mb-2">
                                <h3 class="font-semibold text-slate-800">Verfügbarkeitstrend pro Skill</h3>
                                <select id="availabilityTimeFilter" class="form-select text-xs py-1">
                                    <option value="14">Nächste 14 Tage</option>
                                    <option value="7">Nächste 7 Tage</option>
                                    <option value="30">Nächste 30 Tage</option>
                                </select>
                            </div>
                            <div id="availabilityChart"></div>
                        </div>
                    </div>
                </div> <!-- Grid Ende -->
            </div> <!-- Tab 1 Ende -->

            <!-- TAB 2: MATRIX -->
            <div id="view-matrix" class="emp-tab-content hidden">
                <div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
                    <div class="overflow-x-auto custom-scrollbar">
                        <table class="w-full text-sm text-left border-collapse" id="skillMatrixTable">
                            <thead class="bg-slate-50 text-xs text-slate-500 uppercase">
                                <tr>
                                    <th class="px-6 py-4 font-bold border-b border-r sticky left-0 bg-slate-50 z-10">Mitarbeiter</th>
                                </tr>
                            </thead>
                            <tbody class="divide-y divide-slate-100 text-slate-700"></tbody>
                        </table>
                    </div>
                    <div class="p-4 border-t border-slate-200 bg-slate-50 flex gap-4 text-xs">
                        <div class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-green-100 border border-green-300"></span> < 3 Monate</div>
                        <div class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-yellow-100 border border-yellow-300"></span> < 6 Monate</div>
                        <div class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-red-100 border border-red-300"></span> > 6 Monate</div>
                    </div>
                </div>
            </div> <!-- Tab 2 Ende -->

            <!-- TAB 3: PROJEKTPLAN -->
            <div id="view-projektplan" class="emp-tab-content hidden">
                <div id="projektplan-container">
                    <div class="text-slate-400 italic text-sm p-4 flex items-center gap-2">
                        <span class="material-icons-outlined text-sm animate-spin">refresh</span> Lade Projektplan...
                    </div>
                </div>
            </div>

        </div> <!-- Padding Wrapper Ende -->
    </main>
</div>
<!-- PROJEKTPLAN COMMENTS MODAL (read-only) -->
<div id="ppCommentsOverlay" class="fixed inset-0 bg-gray-800 bg-opacity-60 hidden z-[200] flex items-center justify-center" onclick="if(event.target===this)closePpComments()">
    <div class="bg-white rounded-xl shadow-2xl w-full max-w-lg max-h-[80vh] flex flex-col" onclick="event.stopPropagation()">
        <div class="flex items-center justify-between px-5 py-4 border-b border-slate-200">
            <div>
                <h3 class="text-base font-bold text-slate-800" id="ppCommentsTitle">Kommentare</h3>
                <p class="text-xs text-slate-400 mt-0.5" id="ppCommentsSubtitle"></p>
            </div>
            <button onclick="closePpComments()" class="text-slate-400 hover:text-slate-600 transition-colors">
                <span class="material-icons-outlined">close</span>
            </button>
        </div>
        <div id="ppCommentsList" class="overflow-y-auto flex-1 px-5 py-4 space-y-4">
            <p class="text-slate-400 italic text-sm">Lade Kommentare…</p>
        </div>
    </div>
</div>

<!-- SKILL DETAIL MODAL -->
<div id="skillDetailModal" class="fixed inset-0 bg-gray-900 bg-opacity-50 hidden z-50 flex items-center justify-center" onclick="this.classList.add('hidden')">
    <div class="bg-white rounded-lg shadow-xl p-6 max-w-sm w-full" onclick="event.stopPropagation()">
        <h3 class="text-lg font-bold text-slate-800 mb-1" id="skillDetailTitle">Details</h3>
        <p class="text-sm text-slate-500 mb-4" id="skillDetailSubtitle">Mitarbeiter @ Engine</p>
        
        <div class="space-y-2" id="skillDetailList">
            <!-- Liste der Tasks -->
        </div>
        
        <button class="mt-6 w-full py-2 bg-slate-100 text-slate-600 rounded hover:bg-slate-200 text-sm font-medium" onclick="document.getElementById('skillDetailModal').classList.add('hidden')">Schließen</button>
    </div>
</div>
<script>
    // --- Logik für die einklappbare Sidebar ---
    document.addEventListener('DOMContentLoaded', function () {
        const sidebar = document.getElementById('sidebar');
        const sidebarToggleBtn = document.getElementById('sidebar-toggle');
        if (sidebar && sidebarToggleBtn) {
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
            const applyState = (isCollapsed) => {
                if (isCollapsed) {
                    sidebar.classList.add('is-collapsed');
                    toggleBtnIcon.textContent = 'menu';
                } else {
                    sidebar.classList.remove('is-collapsed');
                    toggleBtnIcon.textContent = 'chevron_left';
                }
            };
            sidebarToggleBtn.addEventListener('click', () => {
                const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
                localStorage.setItem('sidebarCollapsed', isNowCollapsed);
                applyState(isNowCollapsed);
            });
            const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
            applyState(savedState);
        }

        const allEmployees = {{ data.full_employee_data | tojson }};
        const allQualifications = {{ data.full_qualification_data | tojson }};

        const globalChartOptions = {
            chart: { fontFamily: 'Inter, sans-serif', toolbar: { show: false }, animations: { speed: 400 } },
            dataLabels: { enabled: false },
            legend: { position: 'bottom', fontSize: '12px', markers: { width: 10, height: 10, radius: 10 }, itemMargin: { horizontal: 10 } }
        };
        const statusLabelsInOrder = ['Abgeschlossen', 'Gestartet', 'Überfällig', 'Geplant'];
        const statusColorsInOrder = ['#10B981', '#F59E0B', '#EF4444', '#6B7280'];

        const trainingStatusChart = new ApexCharts(document.querySelector("#trainingStatusChart"), {
            ...globalChartOptions, 
            series: [], // Startet immer noch mit leeren Daten
            labels: statusLabelsInOrder, // NEU: Labels von Anfang an setzen
            colors: statusColorsInOrder, // NEU: Farben von Anfang an setzen
            chart: { ...globalChartOptions.chart, type: 'donut' },
            plotOptions: { pie: { donut: { labels: { show: true, total: { show: true, label: 'Merkmale' } } } } },
            noData: { text: 'Keine Daten für die aktuelle Filterauswahl.' }
        });
        trainingStatusChart.render();

        const skillDistributionChart = new ApexCharts(document.querySelector("#skillDistributionChart"), {
            ...globalChartOptions, series: [], chart: { ...globalChartOptions.chart, type: 'bar', height: 250, stacked: true },
            plotOptions: { bar: { horizontal: true, borderRadius: 4 } },
            xaxis: { categories: [], labels: { show: false }, axisBorder: { show: false }, axisTicks: { show: false } },
            yaxis: { labels: { show: true, style: { fontSize: '11px' } } },
            grid: { show: false },
            tooltip: { y: { formatter: (val) => val + " Mitarbeiter" } },
            legend: { position: 'top', horizontalAlign: 'left' },
            noData: { text: 'Keine Daten zum Anzeigen.' }
        });
        skillDistributionChart.render();

        const availabilityChart = new ApexCharts(document.querySelector("#availabilityChart"), {
            ...globalChartOptions,
            series: [],
            chart: { ...globalChartOptions.chart, type: 'area', height: 300, zoom: { enabled: false } },
            stroke: { width: 3, curve: 'smooth' },
            xaxis: { type: 'datetime', labels: { format: 'dd MMM' } },
            yaxis: { title: { text: 'Anzahl verfügbarer Mitarbeiter', style: { fontWeight: 500, color: '#6b7280' } } },
            tooltip: { x: { format: 'dd.MM.yyyy' } },
            noData: { text: 'Bitte einen Skill-Typ filtern, um den Trend anzuzeigen.' }
        });
        availabilityChart.render();

        const employeeFilter = document.getElementById('employeeFilter');
        const groupCheckboxes = document.querySelectorAll('.group-checkbox');
        const skillCheckboxes = document.querySelectorAll('.skill-checkbox');
        const clearFiltersBtn = document.getElementById('clearFiltersBtn');
        const availabilityTimeFilter = document.getElementById('availabilityTimeFilter');
        const employeeDetailCard = document.getElementById('employeeDetailCard');
        const employeeDetailContent = document.getElementById('employeeDetailContent');

        function applyFilters() {
            const selectedEmployeeId = employeeFilter.value;
            const selectedGroups = Array.from(groupCheckboxes).filter(cb => cb.checked).map(cb => cb.value);
            const selectedSkills = Array.from(skillCheckboxes).filter(cb => cb.checked).map(cb => cb.value);

            let filteredQuals = [];

            if (selectedEmployeeId) {
                filteredQuals = allQualifications.filter(q => q.id.toLowerCase() === selectedEmployeeId.toLowerCase());
                groupCheckboxes.forEach(cb => { cb.checked = false; cb.disabled = true; });
                skillCheckboxes.forEach(cb => { cb.checked = false; cb.disabled = true; });
                showEmployeeDetails(selectedEmployeeId);
            } else {
                groupCheckboxes.forEach(cb => cb.disabled = false);
                skillCheckboxes.forEach(cb => cb.disabled = false);
                employeeDetailCard.classList.add('hidden');

                let tempQuals = allQualifications;
                if (selectedGroups.length > 0) {
                    tempQuals = tempQuals.filter(q => selectedGroups.includes(q.quali));
                }
                if (selectedSkills.length > 0) {
                    tempQuals = tempQuals.filter(q => 
                        q.merkmale.some(merkmal => {
                            if (!merkmal.merkmal.includes('/')) return false;
                            const skillName = merkmal.merkmal.split('/')[1];
                            return selectedSkills.includes(skillName) && merkmal.wert;
                        })
                    );
                }
                filteredQuals = tempQuals;
            }

            const relevantMerkmale = new Set();
            filteredQuals.forEach(q => {
                q.merkmale.forEach(m => { if (m.wert) { relevantMerkmale.add(m.merkmal); } });
            });


            const filterContext = {
                isSingleEmployee: !!selectedEmployeeId,
                isGroupOnly: selectedGroups.length > 0 && selectedSkills.length === 0
            };

            updateTrainingStatusChart(filteredQuals);
            updateSkillDistributionChart(filteredQuals, selectedSkills, filterContext);
            updateAvailabilityChart(filteredQuals, selectedSkills);
            updateFilterSummary(filteredQuals);
            updateActiveFilterTags(selectedGroups, selectedSkills);
        }

        function showEmployeeDetails(employeeId) {
            const qualData = allQualifications.find(q => q.id.toLowerCase() === employeeId.toLowerCase());
            if (!qualData) return;

            const abgeschlossen = qualData.merkmale.filter(m => m.wert === 'X').map(m => m.merkmal).sort();
            const inArbeit = qualData.merkmale.filter(m => ['TS', 'TT', 'TP'].includes(m.wert)).map(m => m.merkmal).sort();

            let html = `<h3 class="text-lg font-semibold text-slate-800 mb-4">${qualData.name}</h3>`;
            html += '<h4 class="text-sm font-semibold text-green-600">Abgeschlossen ('+abgeschlossen.length+')</h4>';
            if (abgeschlossen.length > 0) {
                html += '<ul class="list-disc list-inside text-sm text-gray-600 mt-1 space-y-0.5">' + abgeschlossen.map(s => `<li>${s}</li>`).join('') + '</ul>';
            } else { html += '<p class="text-sm text-gray-500 italic mt-1">Keine</p>'; }

            html += '<h4 class="text-sm font-semibold text-amber-600 mt-4">In Arbeit ('+inArbeit.length+')</h4>';
            if (inArbeit.length > 0) {
                html += '<ul class="list-disc list-inside text-sm text-gray-600 mt-1 space-y-0.5">' + inArbeit.map(s => `<li>${s}</li>`).join('') + '</ul>';
            } else { html += '<p class="text-sm text-gray-500 italic mt-1">Keine</p>'; }

            employeeDetailContent.innerHTML = html;
            employeeDetailCard.classList.remove('hidden');
        }

        function updateFilterSummary(filteredQuals) {
            const summaryContainer = document.getElementById('filterSummaryCard');
            if (employeeFilter.value !== "") {
                summaryContainer.innerHTML = '';
                return;
            }

            const totalEmployees = filteredQuals.length;
            let totalSkillOccurrences = 0;
            let certifiedSkillOccurrences = 0;

            filteredQuals.forEach(q => {
                q.merkmale.forEach(m => {
                    if (m.merkmal.includes('/') && m.wert) {
                        totalSkillOccurrences++;
                        if (m.wert === 'X') {
                            certifiedSkillOccurrences++;
                        }
                    }
                });
            });

            const certificationRate = totalSkillOccurrences > 0 ? Math.round((certifiedSkillOccurrences / totalSkillOccurrences) * 100) : 0;

            summaryContainer.innerHTML = `
                <div class="flex items-center space-x-4 py-2">
                    <div class="p-3 bg-blue-100 rounded-full"><span class="material-icons-outlined text-blue-600">person_search</span></div>
                    <div>
                        <p class="text-sm text-gray-500">Gefundene Mitarbeiter</p>
                        <p class="text-2xl font-bold text-gray-900">${totalEmployees}</p>
                    </div>
                </div>
                <div class="flex items-center space-x-4 py-2">
                    <div class="p-3 bg-yellow-100 rounded-full"><span class="material-icons-outlined text-yellow-600">military_tech</span></div>
                    <div>
                        <p class="text-sm text-gray-500">Zertifizierungsrate</p>
                        <p class="text-2xl font-bold text-gray-900">${certificationRate}%</p>
                    </div>
                </div>
            `;
        }

        function updateActiveFilterTags(selectedGroups, selectedSkills) {
            const tagsContainer = document.getElementById('activeFiltersContainer');
            tagsContainer.innerHTML = ''; 

            selectedGroups.forEach(group => {
                const tag = document.createElement('span');
                tag.className = "inline-flex items-center rounded-full bg-blue-100 px-2.5 py-0.5 text-xs font-medium text-blue-800";
                tag.textContent = group;
                tagsContainer.appendChild(tag);
            });

            selectedSkills.forEach(skill => {
                const tag = document.createElement('span');
                tag.className = "inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-800";
                tag.textContent = skill;
                tagsContainer.appendChild(tag);
            });
        }

        function updateTrainingStatusChart(quals) {
            if (quals.length === 0) {
                trainingStatusChart.updateSeries([]);
                return;
            }

            const statusCounts = { 'X': 0, 'TS': 0, 'TT': 0, 'TP': 0 };
            quals.forEach(q => {
                q.merkmale.forEach(m => {
                    if (m.merkmal.includes('/') && statusCounts.hasOwnProperty(m.wert)) {
                        statusCounts[m.wert]++;
                    }
                });
            });

            // Die Reihenfolge der Keys muss zur Reihenfolge der zentralen Labels/Farben passen
            const keysInOrder = ['X', 'TS', 'TT', 'TP'];
            const seriesData = keysInOrder.map(key => statusCounts[key]);

            // Der updateOptions-Aufruf ist jetzt viel schlanker, da Labels und Farben schon bekannt sind.
            trainingStatusChart.updateSeries(seriesData);
        }

        function updateSkillDistributionChart(quals, selectedSkills, filterContext = {}) {
            // Die Logik zum Sammeln der Daten wird erweitert
            const engineData = {}; // Format: { "CF34-10": { "X": { count: 1, names: ["Name A"] }, ... }, ... }

            quals.forEach(q => {
                q.merkmale.forEach(m => {
                    if (m.merkmal.includes('/') && m.wert) {
                        const [engine, skill] = m.merkmal.split('/');
                        if (selectedSkills.length > 0 && !selectedSkills.includes(skill)) {
                            return; 
                        }

                        if (!engineData[engine]) engineData[engine] = {};
                        if (!engineData[engine][m.wert]) engineData[engine][m.wert] = { count: 0, names: new Set() };

                        // Füge den Namen zum Set hinzu, um Duplikate pro Skill/Status/Engine zu vermeiden
                        engineData[engine][m.wert].names.add(q.name);
                    }
                });
            });

            // Aktualisiere die `count`-Eigenschaft basierend auf der Größe des Sets
            Object.values(engineData).forEach(statuses => {
                Object.values(statuses).forEach(data => {
                    data.count = data.names.size;
                });
            });

            const categories = Object.keys(engineData).sort();

            if (categories.length === 0) {
                let noDataMessage = 'Keine passenden Skills in dieser Auswahl gefunden.';
                if (filterContext.isSingleEmployee || filterContext.isGroupOnly) noDataMessage = '';
                skillDistributionChart.updateSeries([], false);
                skillDistributionChart.updateOptions({ xaxis: { categories: [] }, noData: { text: noDataMessage } });
                return;
            }

            const series = [];
            const statuses = ['X', 'TS', 'TT', 'TP'];
            const statusLabels = {'X': 'Abgeschlossen', 'TS': 'Gestartet', 'TT': 'Überfällig', 'TP': 'Geplant'};
            const statusColors = {'X': '#10B981', 'TS': '#F59E0B', 'TT': '#F05252', 'TP': '#6B7280'};

            statuses.forEach(status => {
                series.push({
                    name: statusLabels[status],
                    // Die `data` für die Serie enthält jetzt nur die Anzahl
                    data: categories.map(engine => (engineData[engine]?.[status]?.count || 0))
                });
            });

            skillDistributionChart.updateOptions({
                series: series,
                xaxis: { categories: categories },
                colors: statuses.map(s => statusColors[s]),
                noData: { text: 'Keine Daten zum Anzeigen.' },
                // HIER IST DER NEUE BENUTZERDEFINIERTE TOOLTIP
                tooltip: {
                    custom: function({ series, seriesIndex, dataPointIndex, w }) {
                        const engineName = w.globals.labels[dataPointIndex];
                        const statusLabel = w.globals.seriesNames[seriesIndex];
                        const statusKey = statuses[seriesIndex];

                        const data = engineData[engineName]?.[statusKey];
                        const count = data?.count || 0;

                        if (count === 0) return ''; // Zeige keinen Tooltip für leere Segmente

                        const namesArray = data.names ? Array.from(data.names) : [];
                        const namesHtml = namesArray.slice(0, 10).map(name => `<li>${name}</li>`).join('');
                        const moreCount = namesArray.length > 10 ? `<li>... und ${namesArray.length - 10} weitere</li>` : '';

                        return `<div class="p-2 apexcharts-tooltip-custom">
                                    <div class="font-bold border-b pb-1 mb-1">${engineName} - ${statusLabel}: ${count}</div>
                                    <ul class="list-disc pl-4 text-xs">
                                        ${namesHtml}
                                        ${moreCount}
                                    </ul>
                                </div>`;
                    }
                }
            });
        }

        function updateAvailabilityChart(filteredQuals, selectedSkills) {
            // Schritt 1: Erstelle einmalig eine einfache Verfügbarkeits-Map für alle Mitarbeiter
            const availabilityMap = new Map();
            allEmployees.forEach(emp => {
                const empAvail = new Map();
                (emp.weeks || []).forEach(week => {
                    const weekShift = week.Schicht === 'F' ? 'AM' : (week.Schicht === 'S' ? 'PM' : null);
                    if (weekShift) {
                        (week.days || []).forEach(day => {
                            // Die Daten sind jetzt Strings, der Vergleich funktioniert
                            if (day.date && !day.ereignis.trim()) {
                                const key = `${day.date}_${weekShift}`;
                                empAvail.set(key, true);
                            }
                        });
                    }
                });
                availabilityMap.set(emp.id.toLowerCase(), empAvail);
            });

            // Schritt 2: Definiere den Zeitraum und die Zeitschlitze
            const numDays = parseInt(availabilityTimeFilter.value, 10);
            const today = new Date();
            today.setUTCHours(0, 0, 0, 0);
            const todayTimestamp = today.getTime();

            const timeSlots = [];
            for (let i = 0; i < numDays; i++) {
                const dayTimestamp = todayTimestamp + i * 24 * 60 * 60 * 1000;
                timeSlots.push({ timestamp: dayTimestamp, shift: 'AM' });
                timeSlots.push({ timestamp: dayTimestamp + 12 * 60 * 60 * 1000, shift: 'PM' });
            }

            // Schritt 3: Wähle die zu plottenden Skills aus
            let skillsToPlot = new Set();
            if (selectedSkills.length > 0) {
                filteredQuals.forEach(q => {
                    q.merkmale.forEach(m => {
                        if (m.merkmal.includes('/') && selectedSkills.includes(m.merkmal.split('/')[1])) {
                            skillsToPlot.add(m.merkmal);
                        }
                    });
                });
            } else {
                filteredQuals.forEach(q => {
                    q.merkmale.forEach(m => {
                        if (m.merkmal.includes('/')) skillsToPlot.add(m.merkmal);
                    });
                });
            }

            if (skillsToPlot.size === 0) {
                availabilityChart.updateSeries([]);
                availabilityChart.updateOptions({ noData: { text: 'Bitte einen Skill auswählen, um den Trend zu sehen.' } });
                return;
            }

            // Schritt 4: Berechne die Daten für jede Linie
            const series = [];
            skillsToPlot.forEach(merkmal => {
                const countsPerSlot = [];
                const employeeIdsWithSkill = new Set(
                    filteredQuals
                        .filter(q => q.merkmale.some(m => m.merkmal === merkmal && ['X', 'TS', 'TT'].includes(m.wert)))
                        .map(q => q.id.toLowerCase())
                );

                timeSlots.forEach(slot => {
                    let availableCount = 0;
                    const slotDate = new Date(slot.timestamp);
                    const isoDate = `${slotDate.getUTCFullYear()}-${String(slotDate.getUTCMonth() + 1).padStart(2, '0')}-${String(slotDate.getUTCDate()).padStart(2, '0')}`;
                    const availabilityKey = `${isoDate}_${slot.shift}`;

                    employeeIdsWithSkill.forEach(empId => {
                        const empAvail = availabilityMap.get(empId);
                        if (empAvail && empAvail.has(availabilityKey)) {
                            availableCount++;
                        }
                    });

                    countsPerSlot.push({ x: slot.timestamp, y: availableCount });
                });

                if (countsPerSlot.some(p => p.y > 0)) {
                    series.push({ name: merkmal, data: countsPerSlot });
                }
            });

            // Schritt 5: Aktualisiere das Diagramm
            availabilityChart.updateSeries(series);
            availabilityChart.updateOptions({
                xaxis: {
                    type: 'datetime',
                    labels: {
                        formatter: function(value, timestamp) {
                            if (!timestamp) return '';
                            const date = new Date(timestamp);
                            const day = date.toLocaleDateString('de-DE', { weekday: 'short' });
                            // Wichtig: getUTCHours() verwenden, um Zeitzonenfehler zu vermeiden
                            const shift = date.getUTCHours() < 12 ? 'AM' : 'PM';
                            return `${day} ${shift}`;
                        },
                        rotate: -45,
                        hideOverlappingLabels: false,
                        trim: true,
                    },
                    tooltip: {
                        enabled: true,
                        formatter: function (val) {
                             const date = new Date(val);
                             return date.toLocaleDateString('de-DE', {dateStyle: 'medium'}) + (date.getUTCHours() < 12 ? ' Früh' : ' Spät');
                        }
                    }
                },
                tooltip: { x: { show: false } },
                noData: { text: 'Keine Verfügbarkeiten für die aktuelle Auswahl.' }
            });
        }

        function clearAllFilters() {
            employeeFilter.value = "";
            groupCheckboxes.forEach(cb => cb.checked = false);
            skillCheckboxes.forEach(cb => cb.checked = false);
            applyFilters();
        }

        employeeFilter.addEventListener('change', applyFilters);
        groupCheckboxes.forEach(cb => cb.addEventListener('change', applyFilters));
        skillCheckboxes.forEach(cb => cb.addEventListener('change', applyFilters));
        availabilityTimeFilter.addEventListener('change', applyFilters);
        clearFiltersBtn.addEventListener('click', clearAllFilters);

        applyFilters(); // Initialer Aufruf
        
    // --- SFM BOARD LOGIC ---
    // 1. People Count berechnen (aus den vorhandenen Daten)
    const todayDate = new Date();
    const todayIso = todayDate.toISOString().split('T')[0];
    
    // Berechne "Gestern" (bzw. Freitag, wenn heute Montag ist)
    const yesterdayDate = new Date(todayDate);
    let daysBack = 1;
    if (todayDate.getDay() === 1) daysBack = 3; // Wenn Montag, gehe zu Freitag
    if (todayDate.getDay() === 0) daysBack = 2; // Wenn Sonntag, gehe zu Freitag
    yesterdayDate.setDate(todayDate.getDate() - daysBack);
    const yesterdayIso = yesterdayDate.toISOString().split('T')[0];

    let presentCount = 0;
    let yesterdayCount = 0;
    
    if (allEmployees) {
        allEmployees.forEach(emp => {
            let isPresentToday = false;
            let isPresentYesterday = false;
            
            (emp.weeks || []).forEach(week => {
                (week.days || []).forEach(day => {
                    let dStr = day.date;
                    if (typeof dStr !== 'string') {
                         try { dStr = new Date(day.date).toISOString().split('T')[0]; } catch(e){}
                    }
                    
                    if (!day.ereignis) { // Wenn kein Ereignis (Urlaub/Krank), dann anwesend
                        if (dStr === todayIso) isPresentToday = true;
                        if (dStr === yesterdayIso) isPresentYesterday = true;
                    }
                });
            });
            
            if (isPresentToday) presentCount++;
            if (isPresentYesterday) yesterdayCount++;
        });
    }
    
    // Werte setzen
    const countEl = document.getElementById('sfm-people-count');
    if(countEl) countEl.textContent = presentCount;

    // Diff anzeigen
    const diffEl = document.getElementById('sfm-people-diff');
    if(diffEl) {
        const diff = presentCount - yesterdayCount;
        diffEl.classList.remove('hidden');
        
        if (diff > 0) {
            diffEl.className = "flex items-center text-xs font-bold px-2 py-0.5 rounded-full bg-green-100 text-green-700";
            diffEl.innerHTML = `<span class="material-icons-outlined text-[14px] mr-0.5">trending_up</span> +${diff}`;
        } else if (diff < 0) {
            diffEl.className = "flex items-center text-xs font-bold px-2 py-0.5 rounded-full bg-red-100 text-red-700";
            diffEl.innerHTML = `<span class="material-icons-outlined text-[14px] mr-0.5">trending_down</span> ${diff}`;
        } else {
            diffEl.className = "flex items-center text-xs font-bold px-2 py-0.5 rounded-full bg-slate-100 text-slate-500";
            diffEl.innerHTML = `<span class="material-icons-outlined text-[14px] mr-0.5">remove</span> +/- 0`;
        }
    }
    // --- EFFICIENCY CHART MOCKUP ---
    const chartEl = document.querySelector("#efficiencyChart");
    if (chartEl && typeof ApexCharts !== 'undefined') {
        
        // Mockup Daten generieren (Letzte 30 Tage)
        const dates = [];
        const values = [];
        for (let i = 0; i < 30; i++) {
            const d = new Date();
            d.setDate(d.getDate() - (29 - i));
            dates.push(d.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }));
            
            // Random Wert zwischen 75 und 98
            let val = Math.floor(Math.random() * (98 - 75 + 1) + 75);
            // Wochenend-Dip simulieren (optional)
            if (d.getDay() === 0 || d.getDay() === 6) val = null; 
            
            values.push(val);
        }

        const options = {
            series: [{
                name: "Effizienz",
                data: values
            }],
            chart: {
                type: 'area',
                height: 250,
                fontFamily: 'Inter, sans-serif',
                toolbar: { show: false },
                zoom: { enabled: false }
            },
            dataLabels: { enabled: false },
            stroke: { curve: 'smooth', width: 2 },
            colors: ['#3b82f6'],
            fill: {
                type: 'gradient',
                gradient: {
                    shadeIntensity: 1,
                    opacityFrom: 0.4,
                    opacityTo: 0.05,
                    stops: [0, 100]
                }
            },
            xaxis: {
                categories: dates,
                labels: { style: { fontSize: '10px', colors: '#64748b' } },
                tooltip: { enabled: false }
            },
            yaxis: {
                min: 50,
                max: 100,
                labels: { formatter: (val) => val.toFixed(0) + "%" }
            },
            grid: {
                borderColor: '#f1f5f9',
                strokeDashArray: 4,
            },
            markers: { size: 0, hover: { size: 5 } },
            annotations: {
                yaxis: [{
                    y: 85,
                    borderColor: '#10b981',
                    label: {
                        borderColor: '#10b981',
                        style: { color: '#fff', background: '#10b981', fontSize: '10px' },
                        text: 'Ziel (85%)'
                    }
                }]
            }
        };

        const chart = new ApexCharts(chartEl, options);
        chart.render();
    }    
    window.saveSfmNotes = async function() {
        const text = document.getElementById('sfm-whiteboard').value;
        const payload = {
            key: 'sfm_board_notes',
            text: text,
            type: 'info', // Default
            visible: true
        };

        try {
            const response = await fetch("{{ url_for('api_save_sfm_text') }}", {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(payload)
            });
            if (response.ok) {
                alert("Notizen gespeichert.");
            } else {
                alert("Fehler beim Speichern.");
            }
        } catch (e) {
            console.error(e);
            alert("Netzwerkfehler.");
        }
    };
    });

    // Globale Variable für die Daten
    let sfmCalendarData = {{ sfm_calendar_data | tojson }};
    
    // Debounce Helper (wie besprochen)
    function debounce(func, timeout = 1000) {
        let timer;
        return (...args) => {
            clearTimeout(timer);
            timer = setTimeout(() => { func.apply(this, args); }, timeout);
        };
    }
    
    // Globaler Auto-Saver
    const triggerAutoSave = debounce(() => saveSfmCalendar(true), 1000);
    
    function initSfmCalendar() {
        const header = document.getElementById('sfmCalendarHeader');
        const body = document.getElementById('sfmCalendarBody');
                const keys = ['general', 'info', 'quality_safety', 'success_escalation'];
        keys.forEach(key => {
            if(!Array.isArray(sfmCalendarData[key])) sfmCalendarData[key] = [];
            renderTasksForDate(key);
        });
        const dateTitleEl = document.getElementById('sfm-current-date-title');
        if (dateTitleEl) {
            const options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' };
            dateTitleEl.textContent = new Date().toLocaleDateString('de-DE', options);
        }
        
        // Initialisiere auch das Backlog
        if(!Array.isArray(sfmCalendarData['general'])) {
            sfmCalendarData['general'] = [];
        }
        renderTasksForDate('general');
    
        if(!header || !body) return;
    
        // --- Datumsberechnung ---
        const today = new Date();
        const currentDay = today.getDay() || 7; // 1-7 (Mo-So)
        const mondayThisWeek = new Date(today);
        mondayThisWeek.setDate(today.getDate() - currentDay + 1);
        
        const startDate = new Date(mondayThisWeek);
    
        // Hilfsvariable für den "Heute"-Vergleich (String YYYY-MM-DD)
        const todayIsoStr = today.toISOString().split('T')[0];
    
        header.innerHTML = '<th class="p-3 border-r w-24 bg-slate-100 sticky left-0 z-10 border-b border-slate-200">KW</th>';
        const dayLabels = ['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'];
        dayLabels.forEach(d => header.innerHTML += `<th class="p-3 text-center border-r min-w-[140px] bg-slate-50 border-b border-slate-200">${d}</th>`);
    
        body.innerHTML = '';
        
        for (let w = 0; w < 2; w++) {
            const tr = document.createElement('tr');
            const weekStart = new Date(startDate);
            weekStart.setDate(startDate.getDate() + (w * 7));
            
            // KW berechnen
            const tempDate = new Date(weekStart);
            tempDate.setDate(tempDate.getDate() + 4 - (tempDate.getDay() || 7));
            const yearStart = new Date(tempDate.getFullYear(), 0, 1);
            const kw = Math.ceil((((tempDate - yearStart) / 86400000) + 1) / 7);
            
            let label = `KW ${kw}`;
            let subLabel = w === 0 ? "Aktuell" : "Nächste";
    
            let rowHtml = `
                <td class="p-3 font-bold text-slate-500 bg-slate-50 border-r text-center sticky left-0 z-10 shadow-[2px_0_5px_-2px_rgba(0,0,0,0.05)] align-middle">
                    <div class="text-lg">${kw}</div>
                    <div class="text-[10px] uppercase tracking-wide text-slate-400 font-normal">${subLabel}</div>
                </td>`;
            
            for (let d = 0; d < 7; d++) {
                const date = new Date(weekStart);
                date.setDate(weekStart.getDate() + d);
                const dateIso = date.toISOString().split('T')[0];
                
                // --- NEUE FARB-LOGIK ---
                const isToday = dateIso === todayIsoStr;
                const isPast = dateIso < todayIsoStr;
                const isWeekend = date.getDay() === 0 || date.getDay() === 6; // 0=So, 6=Sa
    
                let bgClass = 'bg-white'; // Standard Zukunft Werktag
                
                if (isToday) {
                    bgClass = 'bg-amber-50'; // Heute (Prio 1)
                } else if (isPast) {
                    bgClass = 'bg-slate-100'; // Vergangenheit (Prio 2) - etwas dunkleres Grau
                } else if (isWeekend) {
                    bgClass = 'bg-slate-50'; // Zukunft Wochenende (Prio 3) - ganz leichtes Grau
                }
                // -----------------------
                
                if (!Array.isArray(sfmCalendarData[dateIso])) {
                    sfmCalendarData[dateIso] = [];
                }
    
                rowHtml += `
                    <td class="p-0 border-r ${bgClass} align-top h-40">
                        <div class="flex flex-col h-full relative group/cell">
                            <!-- Datum Header -->
                            <div class="text-[10px] p-1.5 text-slate-400 font-bold flex justify-between items-center select-none border-b border-dashed border-slate-200/50">
                                <span>${date.getDate()}.${date.getMonth()+1}.</span>
                                ${isToday ? '<span class="text-amber-600 bg-amber-100 px-1.5 rounded text-[9px]">HEUTE</span>' : ''}
                            </div>
                            
                            <!-- Task Liste -->
                            <div class="flex-grow overflow-y-auto custom-scrollbar p-1 space-y-1" id="taskList-${dateIso}">
                                <!-- Wird per JS gefüllt -->
                            </div>
                            
                            <!-- Input Feld -->
                            <div class="p-1 border-t border-slate-100 mt-auto opacity-0 group-hover/cell:opacity-100 focus-within:opacity-100 transition-opacity bg-white/80 backdrop-blur-sm">
                                <input type="text" 
                                    class="w-full text-xs border-0 bg-transparent p-1 focus:ring-0 placeholder-slate-400 text-slate-700" 
                                    placeholder="+ Eintrag..." 
                                    onkeydown="if(event.key === 'Enter') { addTask('${dateIso}', this.value); this.value = ''; }">
                            </div>
                        </div>
                    </td>`;
                
                setTimeout(() => renderTasksForDate(dateIso), 0);
            }
            tr.innerHTML = rowHtml;
            body.appendChild(tr);
        }
    }
    
    function renderTasksForDate(listKey) {
        const container = document.getElementById(`taskList-${listKey}`);
        if (!container) return;
        
        const tasks = sfmCalendarData[listKey] || [];
        container.innerHTML = '';
    
        tasks.forEach((task, index) => {
            // Basis-Klassen
            let containerClass = "task-item flex items-start p-1.5 rounded hover:bg-black/5 group text-xs transition-colors mb-1 bg-white border border-transparent";
            
            // Spezial-Styling
            if (task.type === 'success') {
                containerClass += " !bg-green-50 !border-green-200 border-l-4 !border-l-green-500 pl-2";
            } else if (task.type === 'escalation') {
                containerClass += " !bg-red-50 !border-red-200 border-l-4 !border-l-red-500 pl-2";
            }
    
            const div = document.createElement('div');
            div.className = containerClass;
            // WICHTIG für SortableJS: Index speichern
            div.dataset.index = index; 
            
            // 1. Drag Handle (Neu)
            const handle = document.createElement('span');
            handle.className = "material-icons-outlined text-[14px] drag-handle mt-0.5 opacity-0 group-hover:opacity-100 transition-opacity";
            handle.textContent = "drag_indicator";
    
            // 2. Checkbox
            const checkbox = document.createElement('input');
            checkbox.type = "checkbox";
            checkbox.className = "task-checkbox mt-0.5 w-3.5 h-3.5 rounded border-slate-300 text-blue-600 focus:ring-blue-500 cursor-pointer flex-shrink-0 mr-2";
            checkbox.checked = task.done;
            checkbox.onchange = () => toggleTask(listKey, index);
            
            // 3. Content Wrapper
            const contentWrapper = document.createElement('div');
            contentWrapper.className = "flex-grow min-w-0 flex flex-col";
    
            // Badges (wie gehabt)
            if (task.type === 'success') contentWrapper.innerHTML += `<span class="block text-[9px] font-bold text-green-700 uppercase tracking-wider mb-0.5">Erfolg</span>`;
            else if (task.type === 'escalation') contentWrapper.innerHTML += `<span class="block text-[9px] font-bold text-red-700 uppercase tracking-wider mb-0.5">Eskalation</span>`;
    
            // Text (Editierbar)
            const span = document.createElement('span');
            span.className = `block break-words outline-none ${task.done ? 'text-slate-400 line-through' : 'text-slate-700'}`;
            span.contentEditable = true;
            span.textContent = task.text;
            
            // Update Logik (Text + Datum)
            span.oninput = () => {
                tasks[index].text = span.textContent;
                // Datum aktualisieren
                tasks[index].updatedAt = new Date().toLocaleDateString('de-DE');
                triggerAutoSave();
            };
            // Blur beim Enter, um Fokus zu verlieren (Datum wird beim input schon gespeichert)
            span.onkeydown = (e) => {
                if (e.key === 'Enter') { e.preventDefault(); span.blur(); }
            };
    
            // Datums-Anzeige (Neu)
            const dateInfo = document.createElement('div');
            dateInfo.className = "text-[9px] text-slate-400 mt-1 flex justify-end gap-2 opacity-60 group-hover:opacity-100";
            // Zeige "Bearbeitet" Datum wenn vorhanden und ungleich Erstellt, sonst Erstellt
            let dateText = task.createdAt || "";
            if (task.updatedAt && task.updatedAt !== task.createdAt) {
                dateText = `Ed. ${task.updatedAt}`;
            }
            dateInfo.textContent = dateText;
    
            contentWrapper.appendChild(span);
            contentWrapper.appendChild(dateInfo);
    
            // 4. Delete Button
            const delBtn = document.createElement('button');
            delBtn.className = "task-delete-btn text-slate-300 hover:text-red-500 transition-colors ml-1 opacity-0 group-hover:opacity-100";
            delBtn.innerHTML = '<span class="material-icons-outlined text-[14px]">close</span>';
            delBtn.onclick = () => deleteTask(listKey, index);
    
            div.appendChild(handle);
            div.appendChild(checkbox);
            div.appendChild(contentWrapper);
            div.appendChild(delBtn);
            container.appendChild(div);
        });
    
        // --- SORTABLE INITIALISIEREN ---
        // Wir erstellen jedes Mal eine neue Instanz, da das DOM neu gebaut wird.
        // SortableJS ist sehr leichtgewichtig.
        if (typeof Sortable !== 'undefined') {
            new Sortable(container, {
                handle: '.drag-handle', // Nur am Griff ziehbar (wichtig damit Text markierbar bleibt)
                animation: 150,
                ghostClass: 'sortable-ghost',
                onEnd: function (evt) {
                    // Array umstrukturieren
                    const item = tasks.splice(evt.oldIndex, 1)[0];
                    tasks.splice(evt.newIndex, 0, item);
                    
                    // Wir müssen nicht neu rendern, da das DOM schon stimmt, 
                    // aber wir müssen speichern.
                    triggerAutoSave();
                    
                    // Optional: Re-Render aufrufen, um Indizes in den Event-Listenern (Delete/Toggle) zu korrigieren.
                    // Da unsere Closures (index) beim Erstellen gebunden werden, stimmen sie nach dem Drop nicht mehr!
                    // DESHALB: Re-Render zwingend notwendig.
                    renderTasksForDate(listKey);
                }
            });
        }
    }
    
    // --- INTERAKTIONS-LOGIK ---
    
    function addTask(listKey, text, type = null) {
        if (!text.trim()) return;
        
        if (!sfmCalendarData[listKey]) sfmCalendarData[listKey] = [];
        
        const todayStr = new Date().toLocaleDateString('de-DE'); // Nur Datum, keine Zeit
    
        const newTask = {
            text: text.trim(),
            done: false,
            createdAt: todayStr, // NEU
            updatedAt: todayStr  // NEU
        };
        
        if (type) newTask.type = type;
        
        sfmCalendarData[listKey].push(newTask);
        
        renderTasksForDate(listKey);
        triggerAutoSave();
    }

    function addSpecialTask(type) {
        const input = document.getElementById('input-success_escalation');
        const text = input.value;
        if(text) {
            addTask('success_escalation', text, type);
            input.value = '';
        }
    }
    
    function toggleTask(dateIso, index) {
        if (sfmCalendarData[dateIso] && sfmCalendarData[dateIso][index]) {
            sfmCalendarData[dateIso][index].done = !sfmCalendarData[dateIso][index].done;
            renderTasksForDate(dateIso); // Re-Render für Durchstreichen-Effekt
            triggerAutoSave();
        }
    }
    
    function deleteTask(dateIso, index) {
        if (sfmCalendarData[dateIso]) {
            sfmCalendarData[dateIso].splice(index, 1);
            renderTasksForDate(dateIso);
            triggerAutoSave();
        }
    }
    
    // --- SPEICHERN ---
    async function saveSfmCalendar(isAutoSave = false) {
        try {
            const res = await fetch('/api/save_sfm_calendar', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify(sfmCalendarData)
            });
            
            if(!res.ok) {
                console.error("Auto-Save failed");
            } else {
                // Success Feedback (Indikator aufleuchten lassen)
                const indicator = document.getElementById('autoSaveIndicator');
                if(indicator) {
                    indicator.classList.remove('opacity-0');
                    setTimeout(() => indicator.classList.add('opacity-0'), 2000);
                }
            }
        } catch(e) { 
            console.error("Network error during save:", e); 
        }
    }


    initSfmCalendar();
    function switchEmpTab(name, btn) {
        document.querySelectorAll('.emp-tab-content').forEach(c => c.classList.add('hidden'));
        const target = document.getElementById('view-' + name);
        if(target) target.classList.remove('hidden');
        
        document.querySelectorAll('.emp-tab-btn').forEach(b => {
            b.classList.remove('border-blue-500', 'text-blue-600');
            b.classList.add('border-transparent', 'text-slate-500');
        });
        btn.classList.remove('border-transparent', 'text-slate-500');
        btn.classList.add('border-blue-500', 'text-blue-600');
        
        if(name === 'matrix') loadSkillMatrix();
        if(name === 'sfm') initSfmCalendar();
        if(name === 'projektplan') loadProjektplan();
    }
    
    // ── PROJEKTPLAN ─────────────────────────────────────────────────────────
    const PLAN_COLORS = {
        'RIG':      { bg:'#FDF2F8', border:'#EC4899', text:'#9D174D' },
        'DE-RIG':   { bg:'#FDF2F8', border:'#EC4899', text:'#9D174D' },
        'TEST':     { bg:'#FFF7ED', border:'#F97316', text:'#9A3412' },
        'ATW':      { bg:'#F8FAFC', border:'#64748B', text:'#334155' },
        'BSI':      { bg:'#EFF6FF', border:'#3B82F6', text:'#1E40AF' },
        'INACTIVE': { bg:'#F9FAFB', border:'#9CA3AF', text:'#6B7280' },
    };
    const PLAN_DEFAULT_COLOR = { bg:'#F5F3FF', border:'#8B5CF6', text:'#5B21B6' };

    function planColor(title) {
        return PLAN_COLORS[title.toUpperCase()] || PLAN_DEFAULT_COLOR;
    }

    function fmtDate(iso) {
        if (!iso) return '';
        const [, m, d] = iso.split('-');
        return `${d}.${m}`;
    }

    async function loadProjektplan() {
        const container = document.getElementById('projektplan-container');
        container.innerHTML = '<div class="text-slate-400 italic text-sm p-4 flex items-center gap-2"><span class="material-icons-outlined text-sm animate-spin">refresh</span> Lade Projektplan...</div>';
        try {
            const res = await fetch('/api/projektplan');
            if (!res.ok) throw new Error('Server error');
            const timelines = await res.json();
            renderProjektplan(timelines, container);
        } catch(e) {
            container.innerHTML = '<p class="text-red-500 text-sm p-4">Fehler beim Laden des Projektplans.</p>';
        }
    }

    function renderProjektplan(timelines, container) {
        container.innerHTML = '';
        const today = new Date().toISOString().slice(0, 10);
        const todayFmt = new Date().toLocaleDateString('de-DE', {day:'2-digit', month:'2-digit'});

        if (!timelines.length) {
            container.innerHTML = '<p class="text-slate-400 italic text-sm p-4">Keine Timelines konfiguriert.</p>';
            return;
        }

        // Outer scroll wrapper
        const wrapper = document.createElement('div');
        wrapper.className = 'overflow-x-auto rounded-xl border border-slate-200 shadow-sm';

        // Main grid – 5 columns: Engine | Past | Today | Future | Notes
        const grid = document.createElement('div');
        grid.style.cssText = 'display:grid; grid-template-columns: 210px 1fr 180px 1fr 200px; min-width: 880px;';

        // ── Sticky header row ──────────────────────────────────────────────────
        const makeHeaderCell = (text, extra, style) => {
            const c = document.createElement('div');
            c.className = `px-3 py-2 text-xs font-semibold uppercase tracking-wide border-b ${extra}`;
            if (style) c.style.cssText = style;
            c.textContent = text;
            return c;
        };
        grid.appendChild(makeHeaderCell('Anlage / Motor', 'text-slate-500 border-r border-slate-200 bg-white'));
        grid.appendChild(makeHeaderCell('Vergangen', 'text-slate-400 border-r border-slate-200 bg-white text-right'));
        const todayHeader = makeHeaderCell(`HEUTE  ${todayFmt}`, 'text-white text-center border-b border-blue-600', 'background:#2563EB');
        grid.appendChild(todayHeader);
        grid.appendChild(makeHeaderCell('Geplant', 'text-slate-500 border-r border-slate-200 bg-white'));
        grid.appendChild(makeHeaderCell('Notizen', 'text-slate-500 bg-white'));

        // ── Per-timeline sections ───────────────────────────────────────────────
        timelines.forEach(tl => {
            // Section header spanning all 4 columns
            const secRow = document.createElement('div');
            secRow.style.gridColumn = '1 / -1';
            secRow.className = 'flex items-center gap-2 px-4 py-2 bg-slate-100 border-b border-t border-slate-200';
            const icon = document.createElement('span');
            icon.className = 'material-icons-outlined text-slate-500';
            icon.style.fontSize = '16px';
            icon.textContent = 'account_tree';
            secRow.appendChild(icon);
            const label = document.createElement('span');
            label.className = 'text-sm font-bold text-slate-700 uppercase tracking-wide';
            label.textContent = tl.name;
            secRow.appendChild(label);
            grid.appendChild(secRow);

            if (!tl.projects.length) {
                const empty = document.createElement('div');
                empty.style.cssText = 'grid-column: 1 / -1';
                empty.className = 'px-4 py-3 text-slate-400 italic text-sm border-b border-slate-100 bg-white';
                empty.textContent = 'Keine aktiven Projekte.';
                grid.appendChild(empty);
                return;
            }

            tl.projects.forEach((proj, idx) => {
                const rowBg = idx === 0 ? '#EFF6FF' : '#FFFFFF';
                const borderB = 'border-b border-slate-100';

                // Classify milestones
                const pastMs    = (proj.milestones || []).filter(m => m.iso < today);
                const todayMs   = (proj.milestones || []).filter(m => m.iso === today);
                const futureMs  = (proj.milestones || []).filter(m => m.iso > today);

                // Classify events using real_end (exclusive-end convention corrected)
                const pastEvts   = proj.events.filter(e => e.real_end < today);
                const activeEvts = proj.events.filter(e => e.is_current);
                // Future: strictly future events + multi-day active events that extend beyond today
                const futureEvts = proj.events.filter(e => e.real_end > today && e.start > today)
                    .concat(proj.events.filter(e => e.is_current && e.real_end > today));

                // ── Col 1: Engine info ──
                const col1 = document.createElement('div');
                col1.className = `${borderB} border-r border-slate-200 px-3 py-3 flex flex-col gap-0.5 min-w-0`;
                col1.style.background = rowBg;
                const et = document.createElement('p');
                et.className = 'font-bold text-slate-800 text-sm truncate';
                et.textContent = proj.engine_type;
                col1.appendChild(et);
                const sn = document.createElement('p');
                sn.className = 'text-xs text-slate-500 truncate';
                sn.textContent = 'SN ' + proj.engine_serial;
                col1.appendChild(sn);
                const pc = document.createElement('p');
                pc.className = 'text-xs text-slate-400 truncate';
                pc.textContent = proj.project_code;
                col1.appendChild(pc);
                if (proj.progress !== null && proj.progress !== undefined) {
                    const bar = document.createElement('div');
                    bar.className = 'flex items-center gap-1.5 mt-1';
                    bar.innerHTML = `<div class="flex-1 h-1.5 bg-slate-200 rounded-full overflow-hidden" style="max-width:90px"><div class="h-full bg-blue-500 rounded-full" style="width:${proj.progress}%"></div></div><span class="text-xs font-bold text-slate-400">${proj.progress}%</span>`;
                    col1.appendChild(bar);
                }

                // ── Col 2: Past ──
                const col2 = document.createElement('div');
                col2.className = `${borderB} border-r border-slate-200 px-3 py-3 flex flex-wrap gap-1.5 items-start content-start justify-end`;
                col2.style.background = rowBg;
                pastMs.forEach(m => {
                    const chip = document.createElement('span');
                    chip.className = 'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-400 border border-gray-200';
                    chip.textContent = `◆ ${m.label} ${m.value}`;
                    col2.appendChild(chip);
                });
                pastEvts.forEach(evt => {
                    const chip = document.createElement('span');
                    chip.className = 'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-400 border border-gray-200';
                    const sf = fmtDate(evt.start), ef = fmtDate(evt.real_end);
                    chip.textContent = `${evt.title} ${sf === ef ? sf : sf + '–' + ef}`;
                    col2.appendChild(chip);
                });
                if (!pastMs.length && !pastEvts.length) {
                    const dash = document.createElement('span');
                    dash.className = 'text-xs text-slate-200 self-center';
                    dash.textContent = '–';
                    col2.appendChild(dash);
                }

                // ── Col 3: TODAY ──
                const col3 = document.createElement('div');
                col3.className = `border-b border-blue-200 px-3 py-3 flex flex-col gap-1.5 items-center justify-center`;
                col3.style.background = '#EFF6FF';
                const todayItems = [
                    ...todayMs.map(m => ({type:'milestone', ...m})),
                    ...activeEvts.map(e => ({type:'event', ...e}))
                ];
                if (todayItems.length === 0) {
                    const sb = document.createElement('span');
                    sb.className = 'text-xs font-medium uppercase tracking-wider';
                    sb.style.color = '#93C5FD';
                    sb.textContent = 'STANDBY';
                    col3.appendChild(sb);
                } else {
                    todayItems.forEach(item => {
                        const block = document.createElement('div');
                        block.className = 'flex items-center gap-1 px-2 py-1 rounded text-xs font-bold w-full justify-center border';
                        if (item.type === 'milestone') {
                            block.className = 'flex items-center gap-1 px-2 py-1 rounded text-xs font-bold w-full justify-center border';
                            block.style.cssText = 'background:#2563EB;color:#fff;border-color:#1D4ED8';
                            block.textContent = `◆ ${item.label} ${item.value}`;
                        } else {
                            block.className = 'flex items-center gap-1 px-2 py-1 rounded-full text-xs font-bold w-full justify-center border';
                            const c = planColor(item.title);
                            block.style.cssText = `background:${c.bg};border-color:${c.border};color:${c.text}`;
                            const dot = document.createElement('span');
                            dot.style.cssText = `width:6px;height:6px;border-radius:50%;background:${c.border};flex-shrink:0;display:inline-block`;
                            block.appendChild(dot);
                            block.appendChild(document.createTextNode(item.title));
                        }
                        col3.appendChild(block);
                    });
                }

                // ── Col 4: Future ──
                const col4 = document.createElement('div');
                col4.className = `${borderB} border-r border-slate-200 px-3 py-3 flex flex-wrap gap-1.5 items-start content-start`;
                col4.style.background = rowBg;
                futureMs.forEach(m => {
                    const chip = document.createElement('span');
                    chip.className = 'inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-50 text-blue-700 border border-blue-200';
                    chip.textContent = `◆ ${m.label} ${m.value}`;
                    col4.appendChild(chip);
                });
                futureEvts.forEach(evt => {
                    const c = planColor(evt.title);
                    const chip = document.createElement('span');
                    chip.className = 'inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium border';
                    chip.style.cssText = `background:${c.bg};border-color:${c.border};color:${c.text}`;
                    // For multi-day active events continuing into future, show remaining range
                    const dispStart = evt.is_current ? today : evt.start;
                    const sf = fmtDate(dispStart), ef = fmtDate(evt.real_end);
                    const suffix = evt.is_current ? '→ ' : '';
                    chip.textContent = `${suffix}${evt.title} ${sf === ef ? sf : sf + '–' + ef}`;
                    col4.appendChild(chip);
                });
                if (!futureMs.length && !futureEvts.length) {
                    const dash = document.createElement('span');
                    dash.className = 'text-xs text-slate-200 self-center';
                    dash.textContent = '–';
                    col4.appendChild(dash);
                }

                // ── Col 5: Comments ──
                const projectId = `${proj.project_code}_${proj.engine_serial}`;
                const projectLabel = `${proj.engine_type} · SN ${proj.engine_serial}`;
                const col5 = document.createElement('div');
                col5.className = `${borderB} px-3 py-3 flex flex-col gap-2 cursor-pointer group`;
                col5.style.background = rowBg;
                col5.addEventListener('click', () => openCommentsModal(projectId, projectLabel));
                const comments = proj.comments || [];
                if (comments.length === 0) {
                    // blank — no placeholder
                } else {
                    comments.forEach((c, ci) => {
                        const item = document.createElement('div');
                        item.className = 'flex flex-col gap-0.5';
                        // Author initials + date line
                        const authorParts = c.author.split(' ');
                        const initials = authorParts.map(w => w[0] || '').join('').toUpperCase().slice(0, 2);
                        const dateFmt = c.timestamp ? c.timestamp.slice(5).replace('-', '.') : '';
                        const meta = document.createElement('div');
                        meta.className = 'flex items-center gap-1.5';
                        meta.innerHTML = `<span class="inline-flex items-center justify-center w-5 h-5 rounded-full bg-slate-200 text-slate-600 text-xs font-bold flex-shrink-0">${initials}</span><span class="text-xs text-slate-400">${dateFmt}</span>`;
                        // Comment text — tooltip shows full text on hover
                        const txt = document.createElement('p');
                        txt.className = 'text-xs text-slate-600 leading-snug pl-6';
                        txt.style.cssText = 'display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden';
                        txt.textContent = c.text;
                        item.appendChild(meta);
                        item.appendChild(txt);
                        col5.appendChild(item);
                        if (ci < comments.length - 1) {
                            const divider = document.createElement('div');
                            divider.className = 'border-t border-slate-100';
                            col5.appendChild(divider);
                        }
                    });
                }
                // Subtle hover highlight to signal clickability
                col5.addEventListener('mouseenter', () => col5.style.background = idx === 0 ? '#DBEAFE' : '#F8FAFC');
                col5.addEventListener('mouseleave', () => col5.style.background = rowBg);

                grid.appendChild(col1);
                grid.appendChild(col2);
                grid.appendChild(col3);
                grid.appendChild(col4);
                grid.appendChild(col5);
            });
        });

        // ── Footer legend ──────────────────────────────────────────────────────
        const legend = document.createElement('div');
        legend.style.gridColumn = '1 / -1';
        legend.className = 'bg-slate-50 px-4 py-3 flex flex-wrap gap-5 text-xs text-slate-500 border-t border-slate-200';
        legend.innerHTML = `
            <div class="flex items-center gap-1.5"><span class="inline-block w-3 h-3 rounded bg-gray-100 border border-gray-300"></span>Abgeschlossen</div>
            <div class="flex items-center gap-1.5"><span class="inline-block w-3 h-3 rounded bg-blue-600 border border-blue-700"></span>Heute aktiv</div>
            <div class="flex items-center gap-1.5"><span class="inline-block w-3 h-3 rounded bg-blue-50 border border-blue-300"></span>Geplant (Meilenstein)</div>
            <div class="flex items-center gap-1.5"><span class="inline-block w-3 h-3 rounded" style="background:#FFF7ED;border:1px solid #F97316"></span>TEST</div>
            <div class="flex items-center gap-1.5"><span class="inline-block w-3 h-3 rounded" style="background:#FDF2F8;border:1px solid #EC4899"></span>RIG / DE-RIG</div>
        `;
        grid.appendChild(legend);

        wrapper.appendChild(grid);
        container.appendChild(wrapper);
    }
    // ── PROJEKTPLAN COMMENTS MODAL ───────────────────────────────────────────
    function openCommentsModal(projectId, projectName) {
        document.getElementById('ppCommentsTitle').textContent = projectName;
        document.getElementById('ppCommentsSubtitle').textContent = 'Projekt-Kommentare';
        const list = document.getElementById('ppCommentsList');
        list.innerHTML = '<p class="text-slate-400 italic text-sm">Lade Kommentare\u2026</p>';
        document.getElementById('ppCommentsOverlay').classList.remove('hidden');
        fetch(`/api/comments/${encodeURIComponent(projectId)}`)
            .then(r => r.ok ? r.json() : Promise.reject())
            .then(comments => {
                if (!comments.length) {
                    list.innerHTML = '<p class="text-slate-400 italic text-sm">Keine Kommentare vorhanden.</p>';
                    return;
                }
                list.innerHTML = '';
                comments.forEach(c => {
                    const authorParts = (c.author || '').split(' ');
                    const initials = authorParts.map(w => w[0] || '').join('').toUpperCase().slice(0, 2);
                    const ts = c.timestamp ? new Date(c.timestamp).toLocaleString('de-DE', {day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit'}) : '';
                    const item = document.createElement('div');
                    item.className = 'flex gap-3';
                    const avatar = document.createElement('div');
                    avatar.className = 'flex-shrink-0 w-8 h-8 rounded-full bg-slate-200 flex items-center justify-center text-xs font-bold text-slate-600';
                    avatar.textContent = initials;
                    const body = document.createElement('div');
                    body.className = 'flex-1 min-w-0';
                    const meta = document.createElement('div');
                    meta.className = 'flex items-baseline gap-2 mb-1';
                    meta.innerHTML = `<span class="text-sm font-semibold text-slate-700">${c.author || ''}</span><span class="text-xs text-slate-400">${ts}</span>`;
                    const txt = document.createElement('p');
                    txt.className = 'text-sm text-slate-600 bg-slate-50 border border-slate-100 rounded-lg px-3 py-2 whitespace-pre-wrap';
                    txt.textContent = c.comment || c.text || '';
                    body.appendChild(meta);
                    body.appendChild(txt);
                    item.appendChild(avatar);
                    item.appendChild(body);
                    list.appendChild(item);
                });
            })
            .catch(() => {
                list.innerHTML = '<p class="text-red-400 italic text-sm">Fehler beim Laden der Kommentare.</p>';
            });
    }
    function closePpComments() {
        document.getElementById('ppCommentsOverlay').classList.add('hidden');
    }
    // ── END PROJEKTPLAN ──────────────────────────────────────────────────────

    async function loadSkillMatrix() {
        const table = document.getElementById('skillMatrixTable');
        table.querySelector('tbody').innerHTML = '<tr><td class="p-4">Lade Daten...</td></tr>';
        
        try {
            const res = await fetch('/api/analytics/skill_matrix');
            const data = await res.json();
            renderMatrix(data);
        } catch(e) { console.error(e); }
    }
    
    function renderMatrix(data) {
        const theadRow = document.querySelector('#skillMatrixTable thead tr');
        const tbody = document.querySelector('#skillMatrixTable tbody');
        
        // Header bauen
        theadRow.innerHTML = '<th class="px-6 py-4 font-bold border-b border-r sticky left-0 bg-slate-50 z-10 w-48">Mitarbeiter</th>';
        data.engines.forEach(eng => {
            theadRow.innerHTML += `<th class="px-4 py-3 font-semibold border-b text-center min-w-[100px]">${eng}</th>`;
        });
        
        // Body bauen
        tbody.innerHTML = '';
        const today = new Date();
        
        data.matrix.forEach(user => {
            let rowHtml = `<td class="px-6 py-3 font-medium text-slate-800 border-r bg-white sticky left-0">${user.name}</td>`;
            
            data.engines.forEach(eng => {
                const skillData = user.skills[eng]; // Ist jetzt ein Objekt oder undefined
                
                let cellClass = "bg-slate-50"; 
                let text = "-";
                let clickAction = "";
                let cursorClass = "cursor-default";
    
                if (skillData) {
                    const lastDateIso = skillData.last_total;
                    const date = new Date(lastDateIso);
                    const diffDays = (today - date) / (1000 * 60 * 60 * 24);
                    
                    text = date.toLocaleDateString('de-DE', {month:'2-digit', year:'2-digit'});
                    
                    if (diffDays < 90) cellClass = "bg-green-100 text-green-800";
                    else if (diffDays < 180) cellClass = "bg-yellow-50 text-yellow-800";
                    else cellClass = "bg-red-100 text-red-800 font-bold";
                    
                    // Interaktion aktivieren
                    // Wir speichern die Task-Daten direkt im DOM-Element als JSON-String
                    const taskDataSafe = JSON.stringify(skillData.tasks).replace(/"/g, '&quot;');
                    clickAction = `openSkillDetails('${user.name}', '${eng}', ${taskDataSafe})`;
                    cursorClass = "cursor-pointer hover:brightness-90";
                }
                
                rowHtml += `<td class="px-4 py-3 text-center border-l border-slate-100 ${cellClass} ${cursorClass}" onclick="${clickAction}">${text}</td>`;
            });
            
            tbody.innerHTML += `<tr class="hover:brightness-95 transition">${rowHtml}</tr>`;
        });
    }
    function openSkillDetails(userName, engine, tasks) {
        document.getElementById('skillDetailTitle').textContent = engine;
        document.getElementById('skillDetailSubtitle').textContent = userName;
        
        const list = document.getElementById('skillDetailList');
        list.innerHTML = '';
        
        const today = new Date();
        
        // Sortieren: Neueste zuerst
        const sortedTasks = Object.entries(tasks).sort((a,b) => new Date(b[1]) - new Date(a[1]));
        
        sortedTasks.forEach(([taskName, dateIso]) => {
            const d = new Date(dateIso);
            const diff = Math.round((today - d) / (1000 * 60 * 60 * 24));
            const dateStr = d.toLocaleDateString('de-DE');
            
            let color = "bg-green-100 text-green-800";
            if(diff > 180) color = "bg-red-100 text-red-800";
            else if(diff > 90) color = "bg-yellow-100 text-yellow-800";
            
            list.innerHTML += `
                <div class="flex justify-between items-center p-2 rounded border border-slate-100">
                    <span class="font-medium text-sm text-slate-700">${taskName}</span>
                    <span class="text-xs px-2 py-0.5 rounded ${color}">${dateStr} (${diff}d)</span>
                </div>`;
        });
        
        document.getElementById('skillDetailModal').classList.remove('hidden');
    }

</script>
</div>
</body>
</html>
"""

EDITABLE_TIMELINE_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <title>MBET - Zeitleiste Bearbeiten</title>
    <link crossorigin="" href="https://fonts.gstatic.com/" rel="preconnect"/>
    <link as="style" href="https://fonts.googleapis.com/css2?display=swap&family=Inter%3Awght%40400%3B500%3B600%3B700&family=Noto+Sans%3Awght%40400%3B500%3B700%3B900" onload="this.rel='stylesheet'" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <style>
        body {
            font-family: Inter, 'Noto Sans', sans-serif;
            background-color: #F5F6F8;
            -webkit-user-select: none; /* Safari */
            -moz-user-select: none;    /* Firefox */
            -ms-user-select: none;     /* IE10+/Edge */
            user-select: none;         /* Standard */
        }
        body.is-resizing, body.is-dragging, body.is-row-dragging {
            cursor: grabbing !important;
        }
        body.is-resizing {
            cursor: col-resize !important;
        }
        body.is-row-dragging { cursor: move !important; }

        .table-container {
            overflow: auto;
            max-height: 75vh;
            border: 1px solid #e5e7eb;
            border-radius: 0.5rem;
        }

        .unified-table {
            border-collapse: separate;
            border-spacing: 0;
        }
        .unified-table th, .unified-table td {
            padding: 0;
            vertical-align: middle;
            white-space: nowrap;
        }
        .unified-table tr { height: 75px; }
        .unified-table tbody td {
            border-bottom: 1px solid #e5e7eb;
            border-right: 1px solid #e5e7eb;
            min-width: 45px; width: 45px; max-width: 45px;
            position: relative;
        }
        .unified-table tbody tr:last-child td { border-bottom: 0; }
        .unified-table tfoot td { border-right: 1px solid #e5e7eb; }
        .unified-table td:last-child, .unified-table th:last-child { border-right: 0; }

        .unified-table thead th {
            position: sticky;
            background-color: #f9fafb;
        }
        .header-row-dates th { top: 0; z-index: 21; }
        .header-row-shifts th { top: 35px; z-index: 20; }

        .unified-table tfoot td {
            position: sticky;
            bottom: 0;
            z-index: 20;
            background-color: #ffffff;
            border-top: 2px solid #d1d5db;
            transition: box-shadow 0.2s ease-in-out;
        }
        .table-container.is-scrollable-down .unified-table tfoot td {
            box-shadow: inset 0 10px 8px -8px rgba(0, 0, 0, 0.12);
        }

        .unified-table .sticky-col {
            position: sticky;
            left: 0;
            z-index: 10;
            background-color: #ffffff;
            border-right: 1px solid #d1d5db;
            width: 280px; min-width: 280px; max-width: 280px;
            padding-left: 1rem;
            padding-right: 1rem;
        }
        .unified-table tbody tr:hover .sticky-col {
            background-color: #f3f4f6;
        }
        .unified-table thead .sticky-col { background-color: #f9fafb; }
        .unified-table thead th.sticky-col, .unified-table tfoot td.sticky-col { z-index: 30; }

        .project-name-cell { font-size: 0.875rem; color: #334155; text-align: left; cursor: grab; }
        .header-cell-project { text-align: left; font-size: 0.875rem; font-weight: 600; color: #0f172a; }
        .header-cell-date { padding: 0 0.25rem; font-size: 0.75rem; font-weight: 500; color: #475569; text-align: center; height: 35px; }
        .header-cell-date > div { padding-top: 0.375rem; padding-bottom: 0.375rem; border-bottom: 1px solid #e5e7eb; }
        .header-cell-shift { padding: 0.25rem; font-size: 0.75rem; font-weight: 500; color: #64748b; text-align: center; }

        .weekend-bg { background-color: #f8fafc; }
        .weekend-header { background-color: #f1f5f9 !important; }
        .current-timeslot-highlight { background-color: rgba(239, 68, 68, 0.1) !important; }

        .task-segment-container { width: 100%; height: 100%; display: flex; align-items: center; font-size: 0.75rem; color: white; overflow: hidden; cursor: pointer; }
        .task-segment-container.inactive { cursor: not-allowed; }
        .task-segment-content {
            flex-grow: 1;
            display: flex;
            flex-direction: column; /* WICHTIG: Elemente untereinander stapeln */
            justify-content: center; /* Zentriert den Inhalt vertikal */
            align-items: center;
            padding: 2px 1px;
            overflow: hidden;
            white-space: nowrap;
            height: 100%;
            text-align: center;
            gap: 4px; /* Kleiner Abstand zwischen Titel, MA und Zelle */
        }
        .task-name-display { font-weight: 600; font-size: 0.7rem; line-height: 1.1; margin-bottom: 3px; width: 100%; overflow: hidden; text-overflow: ellipsis; }
        .task-bar { width: 100%; height: 100%; position: relative; }
        .task-bar.is-dragging-segment { opacity: 0.5; }

        .resize-handle { position: absolute; top: 0; bottom: 0; width: 8px; cursor: col-resize; z-index: 10; pointer-events: auto; }
        .resize-handle.inactive { cursor: not-allowed; }
        .resize-handle-left { left: -4px; }
        .resize-handle-right { right: -4px; background-color: rgba(59, 130, 246, 0.5); border-top-right-radius: 3px; border-bottom-right-radius: 3px; transition: background-color 0.2s ease-in-out; }
        .resize-handle-right:hover { background-color: rgba(37, 99, 235, 0.8); }

        .drop-target-highlight { background-color: #bae6fd !important; }
        .row-drop-target-highlight { background-color: #dbeafe !important; }

        .connector-line-container { width: 100%; height: 100%; display: flex; align-items: center; cursor: pointer; }
        .connector-line { width: 100%; height: 2px; background-color: #d1d5db; transition: background-color 0.2s ease-in-out; }
        .connector-line-container:hover .connector-line { background-color: #9ca3af; }

        .toggle-active-btn, .reset-project-btn { 
            margin-left: 0.25rem; padding: 0.25rem 0.625rem; font-size: 0.75rem; line-height: 1rem;
            font-weight: 600; border-radius: 0.375rem; color: white;
            box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
            transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
            transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
            transition-duration: 150ms;
        }
        /* ### NEU: Button-Stile ### */
        .add-new-block-btn {
            margin-left: 0.25rem; padding: 0.375rem; border-radius: 0.375rem; color: #3b82f6; background-color: #eff6ff;
            box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
        }
        .add-new-block-btn:hover { background-color: #dbeafe; }
        .add-new-block-btn .material-icons-outlined { font-size: 14px; vertical-align: middle; }

        .open-comments-btn {
            position: relative;
            margin-left: 0.25rem; padding: 0.375rem; font-size: 0.75rem; line-height: 1rem;
            border-radius: 0.375rem; color: #64748b; background-color: #f1f5f9;
            box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
            transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;
            transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
            transition-duration: 150ms;
        }
        .open-comments-btn:hover { background-color: #e2e8f0; }
        .open-comments-btn .material-icons-outlined { font-size: 14px; vertical-align: middle; }
        .delete-project-btn {
            margin-left: 0.25rem; padding: 0.25rem; font-size: 0.75rem; line-height: 1rem;
            border-radius: 0.375rem;
            box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
            transition-property: background-color;
            transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
            transition-duration: 150ms;
        }
        .comment-count-badge {
            position: absolute;
            top: -5px;
            right: -5px;
            background-color: #3b82f6; /* blue-600 */
            color: white;
            border-radius: 9999px;
            width: 16px;
            height: 16px;
            font-size: 10px;
            font-weight: 600;
            display: flex;
            align-items: center;
            justify-content: center;
            line-height: 1;
            border: 1px solid white;
        }

        .milestone-marker { position: absolute; left: 0; bottom: -9px; width: 18px; height: 18px; background-color: #FBBF24; border: 1px solid #B45309; transform: translateX(-50%) rotate(45deg); display: flex; align-items: center; justify-content: center; z-index: 15; pointer-events: all; cursor: help; }
        .milestone-marker span { transform: rotate(-45deg); font-size: 8px; font-weight: 700; color: #422006; }

        .animate-spin { animation: spin 1s linear infinite; }
        @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }

        .employee-avatars-container {
            display: flex;
            justify-content: center;
            align-items: center;
            gap: 2px;
            /* margin-top entfernt, da wir jetzt 'gap' im Eltern-Container nutzen */
            height: 16px; 
            width: 100%; /* Volle Breite nutzen */
        }
        .employee-avatar { 
            position: relative; width: 16px; height: 16px; border-radius: 50%;
            display: flex; justify-content: center; align-items: center; 
            font-size: 0.55rem; font-weight: 700; line-height: 1; 
            color: white; border: 1px solid rgba(0,0,0,0.2);
        }
        .test-cell-badge {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            height: 16px;
            padding: 0 6px;
            border-radius: 6px; /* Eckig mit leicht runden Ecken (Unterschied zu MA) */
            font-size: 0.6rem;
            font-weight: 700;
            line-height: 1;
            color: white;
            box-shadow: 0 1px 2px rgba(0,0,0,0.1);
            max-width: 80%; /* Damit es nicht über den Block ragt */
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .manual-assignment-dot { position: absolute; top: -1px; right: -1px; width: 5px; height: 5px; background-color: #ef4444; border-radius: 50%; border: 1px solid white; }

        .avatars-in-cell-container {
            display: flex;
            flex-wrap: wrap;
            justify-content: flex-start;
            align-items: flex-start;
            gap: 2px;
            padding: 2px;
            min-height: 36px;
        }
        .available-employee-avatar {
            width: 16px;
            height: 16px;
            border-radius: 9999px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 0.55rem;
            font-weight: 700;
            line-height: 1;
            position: relative;
        }
        .footer-cell-clickable { cursor: pointer; }
        .remaining-count {
            font-size: 0.6rem;
            font-weight: 600;
            color: #64748b;
            text-align: center;
            margin-top: 2px;
            line-height: 1;
        }

        .modal-backdrop { position: fixed; inset: 0; background-color: rgba(0, 0, 0, 0.5); z-index: 40; display: none; }
        .modal { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 50; display: none; }
        .modal-content { background-color: white; border-radius: 0.5rem; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05); width: 90vw; max-width: 800px; }
        .assigned-employee-tag { @apply inline-flex items-center gap-x-1.5 rounded-md px-2 py-1 text-xs font-medium bg-blue-100 text-blue-800; }
        .remove-employee-btn { @apply ml-1.5 inline-flex h-4 w-4 items-center justify-center rounded-full text-blue-600 hover:bg-blue-200 hover:text-blue-700; }

        .comments-scrollbar::-webkit-scrollbar { width: 8px; }
        .comments-scrollbar::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 10px; }
        .comments-scrollbar::-webkit-scrollbar-thumb { background: #c4c4c4; border-radius: 10px; }
        .comments-scrollbar::-webkit-scrollbar-thumb:hover { background: #a1a1a1; }

        .comment-container { position: relative; }
        .comment-actions {
            position: absolute;
            top: 0.5rem;
            right: 0.5rem;
            display: none;
        }
        .comment-container:hover .comment-actions {
            display: block;
        }
        .comment-actions-menu {
            position: absolute;
            right: 0;
            top: 1.5rem;
            background-color: white;
            border: 1px solid #e5e7eb;
            border-radius: 0.375rem;
            box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
            z-index: 10;
        }
        .comment-actions-menu button {
            display: block;
            width: 100%;
            text-align: left;
            padding: 0.5rem 1rem;
            font-size: 0.875rem;
            line-height: 1.25rem;
        }
        .comment-actions-menu button:hover {
            background-color: #f3f4f6;
        }
                #sidebar {
            transition: width 0.3s ease-in-out;
        }
        #sidebar.is-collapsed {
            width: 5rem; /* 80px */
        }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info {
            display: none;
        }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile {
            justify-content: center;
        }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined {
            margin-right: 0;
        }
        .sidebar-label {
            transition: opacity 0.2s ease-in-out;
        }
        .sidebar-separator {
            display: none;
        }
        #sidebar.is-collapsed .sidebar-separator {
            display: block;
            height: 1px;
            background-color: #e5e7eb;
            margin: 0.5rem 0.75rem;
        }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
        .task-bar.selected .task-segment-container {
            outline: 2px solid #ef4444;
            box-shadow: 0 0 0 2px #ef4444; /* Blauer Rahmen außen */
            z-index: 20;
        }

        /* Der Auswahlrahmen (Rubberband) */
        #selection-marquee {
            position: fixed;
            border: 1px solid #3b82f6;
            background-color: rgba(59, 130, 246, 0.2);
            z-index: 9999;
            pointer-events: none; /* Klicks gehen durch */
            display: none;
        }
    </style>
</head>
<body class="bg-white"> <!-- Ändere Hintergrund, falls nötig -->
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}
    <main class="flex-grow bg-neutral-50 px-4 sm:px-6 lg:px-8 py-8">
        <div class="bg-white p-4 sm:p-6 rounded-lg shadow-lg">
            <div class="flex flex-col md:flex-row justify-between items-center mb-6 gap-4">
                <div><h1 class="text-2xl font-bold tracking-tight text-slate-900">{{ timeline_name }}</h1><p class="text-sm text-slate-500 mt-1">Projekte und Ressourcen planen. Änderungen werden erst nach Klick auf "Speichern" übernommen.</p></div>
                <div class="flex items-center space-x-2">
                    <div class="relative">
                        <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" style="font-size: 20px;">search</span>
                        <input id="projectSearchInput" class="pl-10 pr-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-48 text-sm" placeholder="Projekte suchen..." type="text"/>
                    </div>
                    <button id="prevWeekBtnEt" class="inline-flex items-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 disabled:opacity-50">
                        <span class="material-icons-outlined text-base -ml-0.5">arrow_back_ios</span>Vorherige
                    </button>
                    <button id="currentWeekBtnEt" class="inline-flex items-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 disabled:opacity-50">
                        <span class="material-icons-outlined text-base -ml-0.5">event_repeat</span>Aktuell
                    </button>
                    <button id="nextWeekBtnEt" class="inline-flex items-center gap-x-1.5 rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 disabled:opacity-50">
                        Nächste<span class="material-icons-outlined text-base -mr-0.5">arrow_forward_ios</span>
                    </button>
                    <button id="createProjectBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-green-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600">
                        <span class="material-icons-outlined text-base -ml-0.5">add_circle_outline</span>New
                    </button>
                    <button id="sortProjectsBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">
                        <span class="material-icons-outlined text-base -ml-0.5">sort</span>Sort
                    </button>
                    <button id="openSapUploadModalBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-purple-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-purple-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-purple-600 disabled:opacity-50">
                        <span class="material-icons-outlined text-base -ml-0.5">cloud_upload</span>SAP
                    </button>
                    <button id="sapUpdateBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-teal-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600 disabled:opacity-50">
                        <span class="material-icons-outlined text-base -ml-0.5">sync</span>SAP
                    </button>
                    <button id="saveTimelineChangesBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:opacity-50">
                        <span class="material-icons-outlined text-base -ml-0.5">save</span>Save
                    </button>
                    <button id="tempGroupsBtn" class="w-9 h-9 bg-slate-100 rounded-full flex items-center justify-center cursor-pointer hover:bg-slate-200 ml-2" title="Team & Aushilfen verwalten">
                        <span class="material-icons-outlined text-slate-600">group_add</span>
                    </button>
                    <button id="settingsBtn" class="w-9 h-9 bg-slate-100 rounded-full flex items-center justify-center cursor-pointer hover:bg-slate-200" title="Einstellungen">
                        <span class="material-icons-outlined text-slate-600">settings</span>
                    </button>
                </div>
            </div>

            <div class="table-container" id="unifiedTableContainer">
                <table class="min-w-full unified-table" id="unifiedTable">
                    <thead id="unifiedHeader"></thead>
                    <tbody id="unifiedBody" class="divide-y divide-slate-200 bg-white"></tbody>
                    <tfoot id="unifiedFooter"></tfoot>
                </table>
            </div>
        </div>
    </main>
</div>
<div id="selection-marquee"></div>
<div id="createProjectModal" class="modal">
    <div class="modal-content max-w-2xl">
        <div class="p-6">
            <div class="flex items-start justify-between">
                <div>
                    <h3 class="text-lg font-semibold leading-6 text-gray-900">Neues Projekt manuell erstellen</h3>
                    <p class="mt-1 text-sm text-gray-500">Geben Sie die Projektdaten ein. Events werden automatisch ab dem Startdatum generiert.</p>
                </div>
                <button id="closeCreateProjectModal" type="button" class="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
                    <span class="material-icons-outlined">close</span>
                </button>
            </div>
            <form id="createProjectForm" class="mt-6 space-y-4">
                <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
                    <div>
                        <label for="newProjectName" class="block text-sm font-medium text-gray-700">Projektname*</label>
                        <input type="text" id="newProjectName" class="form-input-modal w-full mt-1" required>
                    </div>
                    <div>
                        <label for="newEngineType" class="block text-sm font-medium text-gray-700">Triebwerkstyp*</label>
                        <input type="text" id="newEngineType" class="form-input-modal w-full mt-1" required>
                    </div>
                </div>
                <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
                    <div>
                        <label for="newProjectCode" class="block text-sm font-medium text-gray-700">Projekt-Code*</label>
                        <input type="text" id="newProjectCode" class="form-input-modal w-full mt-1" required>
                    </div>
                    <div>
                        <label for="newEngineSerial" class="block text-sm font-medium text-gray-700">Engine Serial*</label>
                        <input type="text" id="newEngineSerial" class="form-input-modal w-full mt-1" required>
                    </div>
                </div>
                 <div>
                    <label for="newCustomer" class="block text-sm font-medium text-gray-700">Kunde</label>
                    <input type="text" id="newCustomer" class="form-input-modal w-full mt-1">
                </div>
                <div>
                    <label for="newEventsStartDate" class="block text-sm font-medium text-gray-700">Startdatum der Events*</label>
                    <input type="date" id="newEventsStartDate" class="form-input-modal w-full mt-1" required>
                </div>
            </form>
        </div>
        <div class="bg-gray-50 px-6 py-4 flex justify-end">
            <button id="submitNewProjectBtn" type="button" class="inline-flex justify-center rounded-md bg-green-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-700">
                Projekt erstellen & Speichern
            </button>
        </div>
    </div>
</div>

<div id="assignmentModalBackdrop" class="modal-backdrop"></div>
<div id="assignmentModal" class="modal">
    <div class="modal-content">
        <div class="p-6">
            <div class="flex items-start justify-between">
                <div>
                    <h3 class="text-lg font-semibold leading-6 text-gray-900" id="modalTitle">Mitarbeiter zuweisen</h3>
                    <p class="mt-1 text-sm text-gray-500" id="modalSubtitle">Slot-Details</p>
                </div>
                <div class="flex items-center space-x-2">
                    <button id="deleteEventBtn" type="button" class="rounded-md bg-red-50 text-red-500 hover:bg-red-100 hover:text-red-600 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 p-1.5" title="Event löschen">
                        <span class="material-icons-outlined">delete_outline</span>
                    </button>
                    <button id="closeAssignmentModal" type="button" class="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
                        <span class="material-icons-outlined">close</span>
                    </button>
                </div>
            </div>
            <div class="mt-5">
                <h4 class="text-sm font-medium text-gray-700 mb-2">Zugewiesene Mitarbeiter</h4>
                <div id="assignedEmployeesContainer" class="space-x-2 space-y-2">
                    <p class="text-xs text-gray-400 italic">Keine Mitarbeiter zugewiesen.</p>
                </div>
            </div>
            <div class="mt-5">
                <label for="availableEmployeesSelect" class="block text-sm font-medium leading-6 text-gray-900">Verfügbare Mitarbeiter hinzufügen</label>
                <div class="mt-2 flex rounded-md shadow-sm">
                    <select id="availableEmployeesSelect" class="block w-full flex-1 rounded-none rounded-l-md border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"></select>
                    <button id="addEmployeeBtn" type="button" class="relative -ml-px inline-flex items-center gap-x-1.5 rounded-r-md px-3 py-2 text-sm font-semibold text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50">
                        <span class="material-icons-outlined text-base -ml-0.5">add</span> Hinzufügen
                    </button>
                </div>
            </div>
        </div>
        <div class="bg-gray-50 px-4 py-3 sm:flex sm:flex-row-reverse sm:px-6">
            <button id="saveAssignmentsBtn" type="button" class="mt-3 sm:mt-0 sm:ml-3 inline-flex w-full justify-center rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 sm:w-auto">Speichern</button>
            <button id="deleteAssignmentsBtn" type="button" class="mt-3 sm:mt-0 sm:ml-3 hidden inline-flex w-full justify-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 sm:w-auto">Manuelle Zuweisung löschen</button>
            <button id="cancelAssignmentsBtn" type="button" class="mt-3 sm:mt-0 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:w-auto">Abbrechen</button>
        </div>
    </div>
</div>

<div id="blockerModal" class="modal">
    <div class="modal-content p-6">
        <div class="flex justify-between items-center mb-4">
            <h3 class="text-lg font-semibold leading-6 text-gray-900" id="blockerModalTitle">Manuelle Blockaden</h3>
            <!-- Schließen-Kreuz oben rechts -->
            <button onclick="document.getElementById('blockerModal').style.display='none'; document.getElementById('assignmentModalBackdrop').style.display='none';" class="text-gray-400 hover:text-gray-500">
                <span class="material-icons-outlined">close</span>
            </button>
        </div>

        <!-- Liste mit Checkboxen -->
        <div id="blockerModalContent" class="mt-2 space-y-2 text-sm max-h-96 overflow-y-auto"></div>

        <!-- Footer mit Buttons -->
        <div class="mt-6 pt-4 border-t border-gray-100 flex justify-between items-center">
            <label class="flex items-center gap-2 text-sm text-gray-600 cursor-pointer">
                <input type="checkbox" id="selectAllBlockers" class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500">
                Alle auswählen
            </label>
            <div class="flex gap-3">
                <button id="closeBlockerModal" class="rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Schließen</button>
                <button id="deleteSelectedBlockersBtn" class="rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 disabled:opacity-50 disabled:cursor-not-allowed">
                    Ausgewählte löschen
                </button>
            </div>
        </div>
    </div>
</div>

<div id="settingsModal" class="modal">
    <div class="modal-content max-w-4xl w-full flex flex-col max-h-[90vh] bg-white rounded-lg shadow-xl overflow-hidden">
        <!-- Fester Modal-Header -->
        <div class="p-6 border-b border-gray-200 flex-shrink-0">
            <div class="flex items-start justify-between">
                <h3 class="text-lg font-semibold leading-6 text-gray-900">Einstellungen</h3>
                <button id="closeSettingsModalBtn" type="button" class="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
                    <span class="material-icons-outlined">close</span>
                </button>
            </div>
        </div>

        <!-- Scrollbarer Modal-Body -->
        <div class="mt-5 p-6 flex-grow overflow-y-auto">
            <!-- Der Inhalt (Mitarbeitergruppen, Testzellen und Regeln) wird hier per JavaScript eingefügt und ist nun scrollbar -->
        </div>

        <!-- Fester Modal-Footer -->
        <div class="p-6 bg-gray-50 border-t border-gray-200 text-right flex-shrink-0">
            <button id="saveSettingsBtn" type="button" class="inline-flex justify-center rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500">Anwenden</button>
        </div>
    </div>
</div>

<div class="fixed inset-0 bg-gray-800 bg-opacity-75 flex items-center justify-center z-50 hidden" id="commentsOverlay">
    <div class="bg-white rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] flex flex-col" id="commentsModal">
        <div class="flex items-center justify-between p-4 border-b border-gray-200">
            <div class="flex items-center">
                <span class="material-icons-outlined text-teal-600 mr-2">forum</span>
                <h2 class="text-xl font-semibold text-gray-800" id="commentsModalTitle">Kommentare für Projekt</h2>
            </div>
            <button class="text-gray-500 hover:text-gray-700" id="closeCommentsButton">
                <span class="material-icons-outlined">close</span>
            </button>
        </div>
        <div class="p-6 space-y-6 overflow-y-auto comments-scrollbar flex-grow" id="commentsListContainer">
            <p class="text-center text-gray-500">Lade Kommentare...</p>
        </div>
        <div class="p-4 border-t border-gray-200 bg-gray-50">
            <div class="flex items-start space-x-3">
                <div class="flex-grow">
                    <textarea class="w-full p-2 border border-gray-300 rounded-md focus:ring-teal-500 focus:border-teal-500 resize-none" id="newCommentTextarea" placeholder="Neuen Kommentar hinzufügen..." rows="3"></textarea>
                    <div class="mt-2 flex justify-end">
                        <button id="postCommentBtn" class="bg-teal-600 hover:bg-teal-700 text-white font-semibold py-1.5 px-4 rounded-md text-sm flex items-center disabled:opacity-50 disabled:cursor-not-allowed">
                            <span class="material-icons-outlined text-base mr-1">send</span> Posten
                        </button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- ### NEU: ANFANG - Modal für manuellen SAP-Upload ### -->
<div id="manualSapUploadModal" class="modal">
    <div class="modal-content max-w-lg">
        <div class="p-6">
            <div class="flex items-start justify-between">
                <div>
                    <h3 class="text-lg font-semibold leading-6 text-gray-900">Manueller SAP-Daten-Upload</h3>
                    <p class="mt-1 text-sm text-gray-500">Ziehen Sie die `ZLSGS_Dataexport.csv` hierher oder wählen Sie sie aus.</p>
                </div>
                <button id="closeManualSapUploadModal" type="button" class="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2">
                    <span class="material-icons-outlined">close</span>
                </button>
            </div>
            <div id="manual-sap-drop-zone" class="mt-6 flex justify-center items-center w-full px-6 py-10 border-2 border-dashed border-gray-300 rounded-lg cursor-pointer hover:border-blue-500 hover:bg-blue-50 transition-colors duration-200">
                <div class="text-center">
                    <span class="material-icons-outlined text-4xl text-gray-400">cloud_upload</span>
                    <p class="mt-2 text-sm text-gray-600">
                        <span class="font-semibold">SAP-CSV hierher ziehen</span> oder klicken zum Auswählen
                    </p>
                    <p class="text-xs text-gray-500 mt-1">Nur `ZLSGS_Dataexport.csv` mit aktuellem Format.</p>
                </div>
                <input type="file" id="manual-sap-file-input" class="hidden" accept=".csv">
            </div>
            <div id="manual-sap-upload-status" class="mt-4 text-sm text-center"></div>
        </div>
        <div class="bg-gray-50 px-6 py-4 flex justify-end">
            <button id="cancelManualSapUploadBtn" type="button" class="inline-flex justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50">Abbrechen</button>
        </div>
    </div>
</div>
<!-- Modal für temporäre Gruppen -->
<div id="tempGroupsModal" class="modal">
    <div class="modal-content max-w-sm">
        <div class="p-6 border-b border-gray-200">
            <div class="flex items-start justify-between">
                <h3 class="text-lg font-semibold leading-6 text-gray-900">Team-Zusammenstellung</h3>
                <button id="closeTempGroupsModalBtn" type="button" class="text-gray-400 hover:text-gray-500">
                    <span class="material-icons-outlined">close</span>
                </button>
            </div>
            <p class="text-xs text-gray-500 mt-1">Wähle Gruppen aus, die temporär für diese Planung verfügbar sein sollen.</p>
        </div>
        <div class="p-6 max-h-64 overflow-y-auto">
            <div id="tempGroupsList" class="space-y-3">
                <!-- Checkboxen werden hier generiert -->
            </div>
        </div>
        <div class="p-6 bg-gray-50 border-t border-gray-200 text-right">
            <button id="applyTempGroupsBtn" type="button" class="inline-flex justify-center rounded-md bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500">Anwenden</button>
        </div>
    </div>
</div>

<script>
    const timelineId = {{ timeline_id|tojson }};

document.addEventListener('DOMContentLoaded', () => {

    // --- NEU: LOGIK FÜR DIE EINKLAPPBARE SIDEBAR ---
    const sidebar = document.getElementById('sidebar');
    const sidebarToggleBtn = document.getElementById('sidebar-toggle');

    if (sidebar && sidebarToggleBtn) {
        const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');

        const applyState = (isCollapsed) => {
            if (isCollapsed) {
                sidebar.classList.add('is-collapsed');
                toggleBtnIcon.textContent = 'menu';
            } else {
                sidebar.classList.remove('is-collapsed');
                toggleBtnIcon.textContent = 'chevron_left';
            }
        };

        sidebarToggleBtn.addEventListener('click', () => {
            const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
            localStorage.setItem('sidebarCollapsed', isNowCollapsed);
            applyState(isNowCollapsed);
        });

        // Initialen Zustand aus dem Local Storage laden
        const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
        applyState(savedState);
    }
    // --- ENDE DES NEUEN BLOCKS ---


    // --- BESTEHENDE LOGIK FÜR DIE EDITABLE TIMELINE ---
    const currentUsername = "{{ session.get('username_display') }}";
    const isAdmin = {{ 'true' if session.get('is_admin') else 'false' }};

    // DOM-Elemente
    const unifiedBody = document.getElementById('unifiedBody');
    const unifiedFooter = document.getElementById('unifiedFooter');
    const prevWeekBtn = document.getElementById('prevWeekBtnEt');
    const currentWeekBtn = document.getElementById('currentWeekBtnEt');
    const nextWeekBtn = document.getElementById('nextWeekBtnEt');
    const saveBtn = document.getElementById('saveTimelineChangesBtn');
    const sapUpdateBtn = document.getElementById('sapUpdateBtn');
    const searchInput = document.getElementById('projectSearchInput');
    const settingsBtn = document.getElementById('settingsBtn');
    const sortBtn = document.getElementById('sortProjectsBtn');
    const assignmentModal = document.getElementById('assignmentModal');
    const assignmentModalBackdrop = document.getElementById('assignmentModalBackdrop');
    const modalTitle = document.getElementById('modalTitle');
    const modalSubtitle = document.getElementById('modalSubtitle');
    const assignedEmployeesContainer = document.getElementById('assignedEmployeesContainer');
    const availableEmployeesSelect = document.getElementById('availableEmployeesSelect');
    const addEmployeeBtn = document.getElementById('addEmployeeBtn');
    const saveAssignmentsBtn = document.getElementById('saveAssignmentsBtn');
    const cancelAssignmentsBtn = document.getElementById('cancelAssignmentsBtn');
    const closeAssignmentModalBtn = document.getElementById('closeAssignmentModal');
    const deleteAssignmentsBtn = document.getElementById('deleteAssignmentsBtn');
    const deleteEventBtn = document.getElementById('deleteEventBtn');
    const blockerModal = document.getElementById('blockerModal');
    const blockerModalTitle = document.getElementById('blockerModalTitle');
    const blockerModalContent = document.getElementById('blockerModalContent');
    const closeBlockerModalBtn = document.getElementById('closeBlockerModal');
    const settingsModal = document.getElementById('settingsModal');
    const closeSettingsModalBtn = document.getElementById('closeSettingsModalBtn');
    const employeeGroupFilterSelect = document.getElementById('employeeGroupFilterSelect');
    const saveSettingsBtn = document.getElementById('saveSettingsBtn');
    const commentsOverlay = document.getElementById('commentsOverlay');
    const commentsModal = document.getElementById('commentsModal');
    const closeCommentsButton = document.getElementById('closeCommentsButton');
    const commentsModalTitle = document.getElementById('commentsModalTitle');
    const commentsListContainer = document.getElementById('commentsListContainer');
    const newCommentTextarea = document.getElementById('newCommentTextarea');
    const postCommentBtn = document.getElementById('postCommentBtn');
    let activeProjectForComments = null;

    const configModal = document.getElementById('settingsModal');
    const openConfigBtn = document.getElementById('settingsBtn');
    const closeConfigBtn = document.getElementById('closeSettingsModalBtn');
    const saveConfigBtn = document.getElementById('saveSettingsBtn');
    const configModalBody = configModal.querySelector('.overflow-y-auto');

    let assignmentRules = [];
    let ruleOptions = {};
    let allAvailableSkills = [];
    
    let selectedTaskIds = new Set(); // Speichert IDs der markierten Tasks
    let isSelecting = false;
    let selectionStart = { x: 0, y: 0 };
    const selectionMarquee = document.getElementById('selection-marquee');

    let activeDragData = null;
    let isResizing = false;
    let resizingTask = null;
    let currentModalData = null;
    let isDraggingFlag = false;
    let currentEmployeeGroupFilter = []; 
    let currentTestCellFilter = [];
    let engineEventMapping = {};
    
    let masterApiDataCache = null;
    let masterTaskData = {};
    let masterProjectDetails = {};
    let masterProjectOrder = [];
    let filteredProjectOrder = [];
    let masterTimelineStartDate = null;
    let masterTotalColumns = 0;
    let masterFooterData = {}; 
    let masterQualStatuses = {};

    let allEmployees = [];
    let allQualifications = [];
    let allLocations = [];
    let manualAssignments = new Map();
    let employeeQualificationsMap = new Map();
    let employeeAvailabilityMap = new Map();
    let employeeColorMap = new Map();

    let currentWeekOffset = 3;
    const WEEKS_IN_PAST = 3;
    const TOTAL_WEEKS_LOADED = 10;
    const VISIBLE_WEEKS = 2;

    const EVENT_COLOR_MAP = { 'RIG': '#EC4899', 'TEST': '#F97316', 'DE-RIG': '#EC4899', 'ATW': '#64748B', 'BSI': '#3B82F6', 'INACTIVE': '#9CA3AF', 'DEFAULT': '#8B5CF6' };
    const TAILWIND_AVATAR_COLORS = [ 'bg-blue-100 text-blue-700', 'bg-pink-100 text-pink-700', 'bg-indigo-100 text-indigo-700', 'bg-green-100 text-green-700', 'bg-purple-100 text-purple-700', 'bg-teal-100 text-teal-700', 'bg-orange-100 text-orange-700', 'bg-yellow-100 text-yellow-700', 'bg-red-100 text-red-700', 'bg-gray-200 text-gray-800', 'bg-cyan-100 text-cyan-700', 'bg-lime-100 text-lime-700' ];
    const QUALI_STATUS_COLORS = {
        'X': '#10B981',
        'TS': '#F59E0B',
        'TT': '#EF4444',
        'TP': '#6B7280',
        'MANUAL_NO_QUALI': '#FFFFFF',
        'DEFAULT': 'rgba(0, 0, 0, 0.2)'
    };

    const openSapUploadModalBtn = document.getElementById('openSapUploadModalBtn');
    const manualSapUploadModal = document.getElementById('manualSapUploadModal');
    const closeManualSapUploadModalBtn = document.getElementById('closeManualSapUploadModal');
    const cancelManualSapUploadBtn = document.getElementById('cancelManualSapUploadBtn');
    const manualSapDropZone = document.getElementById('manual-sap-drop-zone');
    const manualSapFileInput = document.getElementById('manual-sap-file-input');
    const manualSapUploadStatusDiv = document.getElementById('manual-sap-upload-status');

    let allAvailableGroups = []; 

    const tempGroupsBtn = document.getElementById('tempGroupsBtn');
    const tempGroupsModal = document.getElementById('tempGroupsModal');
    const closeTempGroupsModalBtn = document.getElementById('closeTempGroupsModalBtn');
    const tempGroupsList = document.getElementById('tempGroupsList');
    const applyTempGroupsBtn = document.getElementById('applyTempGroupsBtn');

    tempGroupsBtn.addEventListener('click', () => {
        // Liste bauen
        tempGroupsList.innerHTML = '';
        
        // Alle Gruppen aus den geladenen Mitarbeiterdaten extrahieren, falls noch nicht geschehen
        if (allAvailableGroups.length === 0 && allEmployees.length > 0) {
            allAvailableGroups = [...new Set(allEmployees.map(e => e.quali).filter(Boolean))].sort();
        }

        allAvailableGroups.forEach(group => {
            const isChecked = currentEmployeeGroupFilter.includes(group);
            // Wir markieren die "Original"-Gruppen (aus der Config) vielleicht speziell?
            // Für jetzt: Einfach alle Checkboxen.
            
            const div = document.createElement('div');
            div.className = "flex items-center";
            div.innerHTML = `
                <input id="tg-${group}" type="checkbox" value="${group}" ${isChecked ? 'checked' : ''} class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500">
                <label for="tg-${group}" class="ml-3 text-sm font-medium text-gray-700 cursor-pointer select-none">${group}</label>
            `;
            tempGroupsList.appendChild(div);
        });

        assignmentModalBackdrop.style.display = 'block';
        tempGroupsModal.style.display = 'block';
    });

    function closeTempGroupsModal() {
        assignmentModalBackdrop.style.display = 'none';
        tempGroupsModal.style.display = 'none';
    }
    
    closeTempGroupsModalBtn.addEventListener('click', closeTempGroupsModal);

    applyTempGroupsBtn.addEventListener('click', () => {
        // Neue Auswahl sammeln
        const selected = Array.from(tempGroupsList.querySelectorAll('input:checked')).map(cb => cb.value);
        
        if (selected.length === 0) {
            alert("Bitte mindestens eine Gruppe auswählen.");
            return;
        }

        // Globalen Filter aktualisieren
        currentEmployeeGroupFilter = selected;
        
        // Neu berechnen & rendern
        closeTempGroupsModal();
        recalculateAndRerenderAssignments();
    });
    function openManualSapUploadModal() {
        manualSapUploadStatusDiv.textContent = '';
        manualSapUploadModal.style.display = 'block';
        assignmentModalBackdrop.style.display = 'block';
    }

    function closeManualSapUploadModal() {
        manualSapUploadModal.style.display = 'none';
        assignmentModalBackdrop.style.display = 'none';
        manualSapDropZone.classList.remove('border-blue-500', 'bg-blue-50');
        manualSapFileInput.value = '';
    }

    async function handleManualSapFileUpload(file) {
        if (!file.name.toLowerCase().endsWith('.csv')) {
            manualSapUploadStatusDiv.textContent = 'Fehler: Es werden nur .csv-Dateien akzeptiert.';
            manualSapUploadStatusDiv.className = 'mt-2 text-sm text-center text-red-600';
            return;
        }

        manualSapUploadStatusDiv.textContent = 'SAP-Datei wird hochgeladen und verarbeitet...';
        manualSapUploadStatusDiv.className = 'mt-2 text-sm text-center text-blue-600';
        sapUpdateBtn.disabled = true;
        openSapUploadModalBtn.disabled = true;

        const formData = new FormData();
        formData.append('sap_file', file);

        try {
            const response = await fetch("{{ url_for('api_manual_sap_update') }}", {
                method: 'POST',
                body: formData
            });

            const result = await response.json();

            if (response.ok) {
                manualSapUploadStatusDiv.textContent = result.message;
                manualSapUploadStatusDiv.className = 'mt-2 text-sm text-center text-green-600';
                setTimeout(async () => {
                    closeManualSapUploadModal();
                    await initializeTimeline();
                    sapUpdateBtn.disabled = false;
                    openSapUploadModalBtn.disabled = false;
                    markChanges();
                }, 2000);
            } else {
                throw new Error(result.error || 'Unbekannter Fehler beim manuellen SAP-Upload.');
            }
        } catch (error) {
            manualSapUploadStatusDiv.textContent = `Fehler: ${error.message}`;
            manualSapUploadStatusDiv.className = 'mt-2 text-sm text-center text-red-600';
            sapUpdateBtn.disabled = false;
            openSapUploadModalBtn.disabled = false;
        }
    }

    openSapUploadModalBtn.addEventListener('click', openManualSapUploadModal);
    closeManualSapUploadModalBtn.addEventListener('click', closeManualSapUploadModal);
    cancelManualSapUploadBtn.addEventListener('click', closeManualSapUploadModal);
    manualSapDropZone.addEventListener('click', () => manualSapFileInput.click());
    manualSapFileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) { handleManualSapFileUpload(e.target.files[0]); } });
    manualSapDropZone.addEventListener('dragover', (e) => { e.preventDefault(); manualSapDropZone.classList.add('border-blue-500', 'bg-blue-50'); });
    manualSapDropZone.addEventListener('dragleave', (e) => { e.preventDefault(); manualSapDropZone.classList.remove('border-blue-500', 'bg-blue-50'); });
    manualSapDropZone.addEventListener('drop', (e) => { e.preventDefault(); manualSapDropZone.classList.remove('border-blue-500', 'bg-blue-50'); if (e.dataTransfer.files.length > 0) { handleManualSapFileUpload(e.dataTransfer.files[0]); } });

    const createProjectModal = document.getElementById('createProjectModal');
    const createProjectBtn = document.getElementById('createProjectBtn');
    const closeCreateProjectModalBtn = document.getElementById('closeCreateProjectModal');
    const submitNewProjectBtn = document.getElementById('submitNewProjectBtn');
    const createProjectForm = document.getElementById('createProjectForm');

    function openCreateProjectModal() { createProjectForm.reset(); assignmentModalBackdrop.style.display = 'block'; createProjectModal.style.display = 'block'; }
    function closeCreateProjectModal() { assignmentModalBackdrop.style.display = 'none'; createProjectModal.style.display = 'none'; }

    createProjectBtn.addEventListener('click', openCreateProjectModal);
    closeCreateProjectModalBtn.addEventListener('click', closeCreateProjectModal);

    submitNewProjectBtn.addEventListener('click', async () => {
        const projectData = {
            name: document.getElementById('newProjectName').value,
            engine_type: document.getElementById('newEngineType').value,
            project_code: document.getElementById('newProjectCode').value,
            engine_serial: document.getElementById('newEngineSerial').value,
            customer: document.getElementById('newCustomer').value,
            events_start_date: document.getElementById('newEventsStartDate').value
        };
        if (!projectData.name || !projectData.engine_type || !projectData.project_code || !projectData.engine_serial || !projectData.events_start_date) { alert("Bitte füllen Sie alle Pflichtfelder aus."); return; }
        try {
            const response = await fetch(`/api/create_manual_project/${timelineId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(projectData) });
            const result = await response.json();
            if (!response.ok) { throw new Error(result.error || 'Unbekannter Fehler beim Erstellen des Projekts.'); }
            alert(result.message);
            closeCreateProjectModal();
            await initializeTimeline();
        } catch (error) { alert(`Fehler: ${error.message}`); }
    });

    function setupTagInput(inputField, getSuggestionsFn, initialTags = [], color = 'slate', onChangeCallback = null) {
        const container = inputField.parentElement;
        let suggestions = [];
    
        const createTag = (value) => {
            const chip = document.createElement('span');
            chip.className = `inline-flex items-center rounded-md bg-${color}-100 px-2 py-1 text-xs font-medium text-${color}-700 ring-1 ring-inset ring-${color}-200 tag-chip`;
            chip.dataset.value = value;
            chip.innerHTML = `${value}<button type="button" class="ml-1.5 text-${color}-500 hover:text-${color}-700"><span class="material-icons-outlined text-sm">close</span></button>`;
            chip.querySelector('button').addEventListener('click', () => {
                chip.remove();
                if (onChangeCallback) onChangeCallback();
            });
            container.insertBefore(chip, inputField);
        };
        
        initialTags.forEach(createTag);
    
        const dropdown = document.createElement('div');
        dropdown.className = 'absolute z-10 mt-1 w-full bg-white shadow-lg rounded-md border border-slate-200 max-h-40 overflow-y-auto hidden';
        container.parentElement.style.position = 'relative'; // Wichtig für die Positionierung des Dropdowns
        container.parentElement.appendChild(dropdown);
    
        const showSuggestions = () => {
            const currentValue = inputField.value.toLowerCase();
            const currentTags = Array.from(container.querySelectorAll('.tag-chip')).map(t => t.dataset.value);
            suggestions = getSuggestionsFn().filter(s => !currentTags.includes(s) && s.toLowerCase().includes(currentValue));
            
            dropdown.innerHTML = '';
            if (suggestions.length > 0) {
                suggestions.forEach(s => {
                    const item = document.createElement('div');
                    item.className = 'px-3 py-2 cursor-pointer hover:bg-slate-100 text-sm';
                    item.textContent = s;
                    item.addEventListener('click', () => {
                        createTag(s);
                        inputField.value = '';
                        dropdown.classList.add('hidden');
                        inputField.focus();
                        if (onChangeCallback) onChangeCallback();
                    });
                    dropdown.appendChild(item);
                });
                dropdown.classList.remove('hidden');
            } else {
                dropdown.classList.add('hidden');
            }
        };
    
        inputField.addEventListener('input', showSuggestions);
        inputField.addEventListener('focus', showSuggestions);
        inputField.addEventListener('keydown', (e) => {
            if (e.key === 'Enter' && suggestions.length > 0) {
                e.preventDefault();
                createTag(suggestions[0]);
                inputField.value = '';
                showSuggestions();
                if (onChangeCallback) onChangeCallback();
            }
        });
    
        // Custom Event Listener, um Vorschläge von außen zu aktualisieren
        inputField.addEventListener('update-suggestions', (e) => {
            // Diese Funktion wird vom Engine-Type Input getriggert
            getSuggestionsFn = () => e.detail;
        });
    
        document.addEventListener('click', (e) => {
            if (!container.parentElement.contains(e.target)) {
                dropdown.classList.add('hidden');
            }
        });
    }
    
    function addRuleRow(engineTypes = [], eventTitles = [], requiredSkills = []) {
        const ruleDiv = document.createElement('div');
        ruleDiv.className = 'grid grid-cols-[2fr_2fr_3fr_auto] gap-x-4 items-start p-3 border-b border-slate-100 last:border-b-0 rule-row';
    
        const createTagInputContainer = (label, type) => {
            const wrapper = document.createElement('div');
            wrapper.className = `${type}-container`;
            wrapper.innerHTML = `<label class="block text-xs font-medium text-slate-500 mb-1">${label}</label>`;
            const inputContainer = document.createElement('div');
            inputContainer.className = 'flex flex-wrap items-center gap-2 p-1.5 border border-slate-300 rounded-md bg-white focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-blue-500 min-h-[38px]';
            inputContainer.innerHTML = `<input type="text" placeholder="+ Add..." class="flex-grow p-1 border-0 focus:ring-0 text-sm tag-input-field">`;
            wrapper.appendChild(inputContainer);
            return wrapper;
        };
    
        const engineContainer = createTagInputContainer('Engine Type(s)', 'engine-types');
        const eventContainer = createTagInputContainer('Event(s)', 'event-titles');
        const skillsContainer = createTagInputContainer('Benötigte Skills', 'required-skills');
    
        const deleteButton = document.createElement('div');
        deleteButton.className = 'flex justify-end items-start pt-6';
        deleteButton.innerHTML = `<button type="button" class="remove-rule-btn text-red-500 hover:text-red-700 p-2 rounded-full hover:bg-red-50"><span class="material-icons-outlined text-xl">delete_outline</span></button>`;
        deleteButton.querySelector('.remove-rule-btn').addEventListener('click', () => {
            ruleDiv.remove();
            if (document.getElementById('configTableBody').querySelectorAll('.rule-row').length === 0) {
                document.getElementById('configTableBody').innerHTML = '<tr><td colspan="4" class="text-center text-slate-400 py-4">Keine Regeln definiert.</td></tr>';
            }
        });
        
        ruleDiv.appendChild(engineContainer);
        ruleDiv.appendChild(eventContainer);
        ruleDiv.appendChild(skillsContainer);
        ruleDiv.appendChild(deleteButton);
    
        const engineInput = engineContainer.querySelector('input');
        const eventInput = eventContainer.querySelector('input');
        const skillInput = skillsContainer.querySelector('input');
    
        // Engine Types
        setupTagInput(engineInput, () => Object.keys(engineEventMapping).sort(), engineTypes, 'sky', () => {
            const selectedEngines = Array.from(engineContainer.querySelectorAll('.tag-chip')).map(t => t.dataset.value);
            const eventSuggestions = (selectedEngines.length === 0)
                ? [...new Set(Object.values(engineEventMapping).flat())].sort()
                : [...new Set(selectedEngines.flatMap(eng => engineEventMapping[eng] || []))].sort();
            eventInput.dispatchEvent(new CustomEvent('update-suggestions', { detail: eventSuggestions }));
        });
        
        // Events
        setupTagInput(eventInput, () => {
            const selectedEngines = Array.from(engineContainer.querySelectorAll('.tag-chip')).map(t => t.dataset.value);
            if (selectedEngines.length === 0) return [...new Set(Object.values(engineEventMapping).flat())].sort();
            return [...new Set(selectedEngines.flatMap(eng => engineEventMapping[eng] || []))].sort();
        }, eventTitles, 'indigo');
    
        // Benötigte Skills
        setupTagInput(skillInput, () => allAvailableSkills, requiredSkills, 'emerald');
    
        const tableBody = document.getElementById('configTableBody');
        if (!tableBody.querySelector('.rule-row')) tableBody.innerHTML = '';
        tableBody.appendChild(ruleDiv);
    }
    
    function renderSettingsModalContent() {
        configModalBody.innerHTML = `
            <div>
                <label class="block text-sm font-medium leading-6 text-gray-900">Mitarbeitergruppen für Zuweisung</label>
                <p class="text-xs text-gray-500 mb-2">Wähle die Gruppen aus, die für die automatische Zuweisung und im Footer berücksichtigt werden.</p>
                <div id="employeeGroupCheckboxes" class="max-h-32 overflow-y-auto space-y-2 rounded-md border border-gray-200 p-3"></div>
            </div>
            <div class="mt-6 pt-6 border-t border-gray-200">
                <label class="block text-sm font-medium leading-6 text-gray-900">Testzellen für Zuweisung</label>
                <p class="text-xs text-gray-500 mb-2">Wähle die Testzellen aus, die für "Test"-Events automatisch verplant werden sollen.</p>
                <div id="testCellCheckboxes" class="max-h-32 overflow-y-auto space-y-2 rounded-md border border-gray-200 p-3"></div>
            </div>
            <div class="mt-6 pt-6 border-t border-gray-200">
                <div class="flex justify-between items-center mb-2">
                    <div>
                        <label class="block text-sm font-medium leading-6 text-gray-900">Qualifikationsregeln für Zuweisung</label>
                        <p class="text-xs text-gray-500">Definiere, welcher Skill für welchen Task und Triebwerkstyp benötigt wird.</p>
                    </div>
                    <button id="addRuleBtn" class="text-sm font-medium text-blue-600 hover:text-blue-800">+ Neue Regel hinzufügen</button>
                </div>
                <div id="configTableBody" class="divide-y divide-gray-200 rounded-lg border border-gray-200 bg-white">
                    <!-- Regel-Zeilen werden hier dynamisch eingefügt -->
                </div>
            </div>`;

        // Mitarbeitergruppen und Testzellen befüllen (unverändert)
        const groupCheckboxesContainer = document.getElementById('employeeGroupCheckboxes');
        const allGroups = [...new Set(allEmployees.map(e => e.quali).filter(Boolean))].sort();
        allGroups.forEach(group => { const isChecked = currentEmployeeGroupFilter.includes(group); const checkboxId = `group-checkbox-${group}`; const label = document.createElement('label'); label.className = 'flex items-center space-x-3 p-1 rounded-md hover:bg-gray-50 cursor-pointer'; label.setAttribute('for', checkboxId); label.innerHTML = `<input id="${checkboxId}" name="group_filter" type="checkbox" value="${group}" ${isChecked ? 'checked' : ''} class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"><span class="text-sm text-gray-700">${group}</span>`; groupCheckboxesContainer.appendChild(label); });

        const cellCheckboxesContainer = document.getElementById('testCellCheckboxes');
        allLocations.forEach(loc => { const isChecked = currentTestCellFilter.includes(loc.ID); const checkboxId = `cell-checkbox-${loc.ID}`; const title = `Kompatibel mit: ${loc.TYPES.join(', ') || 'Alle'}`; const label = document.createElement('label'); label.className = `flex items-center space-x-3 p-1 rounded-md hover:bg-gray-50 cursor-pointer`; label.setAttribute('for', checkboxId); label.title = title; label.innerHTML = `<input id="${checkboxId}" name="cell_filter" type="checkbox" value="${loc.ID}" ${isChecked ? 'checked' : ''} class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"><span class="text-sm text-gray-700">${loc.TESTCELL} (${loc.ID})</span>`; cellCheckboxesContainer.appendChild(label); });

        // Regeln rendern
        const tableBody = document.getElementById('configTableBody');
        if (assignmentRules.length > 0) {
            assignmentRules.forEach(rule => {
                addRuleRow(
                    rule.engine_types || [],      // Muss ein Array sein
                    rule.event_titles || [],      // Muss ein Array sein
                    rule.required_skills || []      // Muss ein Array sein
                );
            });
        } else {
            tableBody.innerHTML = '<tr><td colspan="4" class="text-center text-slate-400 py-4">Keine Regeln definiert.</td></tr>';
        }
        
        // Listener für "Neue Regel" Button
        document.getElementById('addRuleBtn').addEventListener('click', () => addRuleRow());
    }

    function createTagHtml(tag, type, index, color) { return `<span class="inline-flex items-center rounded-md bg-${color}-100 px-2 py-1 text-xs font-medium text-${color}-700 ring-1 ring-inset ring-${color}-200">${tag}<button onclick="removeTag(${index}, '${type}', '${tag}')" class="ml-1.5 text-${color}-500 hover:text-${color}-700"><span class="material-icons-outlined text-sm">close</span></button></span>`; }
    function createAddButtonHtml(type, index) { return `<button onclick="addTag(${index}, '${type}')" aria-label="Add tag" class="flex items-center justify-center rounded-full h-6 w-6 bg-slate-200 text-slate-600 hover:bg-slate-300 hover:text-slate-700 transition-colors"><span class="material-icons-outlined text-base">add</span></button>`; }

    function showSelectModal(title, options, callback) {
        const existingModal = document.getElementById('selectTagModal'); if (existingModal) existingModal.remove();
        const modalOverlay = document.createElement('div'); modalOverlay.id = 'selectTagModal'; modalOverlay.className = 'fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4';
        let optionsHtml = options.map(opt => `<option value="${opt}">${opt}</option>`).join('');
        modalOverlay.innerHTML = `<div class="w-full max-w-md rounded-lg bg-white shadow-xl p-6"><h3 class="text-lg font-medium text-gray-900 mb-4">${title}</h3><select id="tagSelect" class="w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500">${optionsHtml}</select><div class="mt-6 flex justify-end space-x-3"><button id="cancelSelectBtn" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300">Abbrechen</button><button id="confirmSelectBtn" class="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700">Hinzufügen</button></div></div>`;
        document.body.appendChild(modalOverlay);
        const selectEl = document.getElementById('tagSelect'), confirmBtn = document.getElementById('confirmSelectBtn'), cancelBtn = document.getElementById('cancelSelectBtn');
        const closeModal = () => modalOverlay.remove();
        confirmBtn.onclick = () => { callback(selectEl.value); closeModal(); };
        cancelBtn.onclick = closeModal;
        modalOverlay.onclick = (e) => { if (e.target === modalOverlay) closeModal(); };
    }

    window.addTag = (index, type) => {
        if (type === 'required_skills') {
            const currentSkillsInRule = new Set(assignmentRules[index][type]);
            const availableOptions = ruleOptions.skills.filter(opt => !currentSkillsInRule.has(opt));
            if (availableOptions.length === 0) { alert("Alle verfügbaren Skills wurden dieser Regel bereits hinzugefügt."); return; }
            showSelectModal('Benötigten Skill auswählen', availableOptions, (selectedValue) => { if (selectedValue) { assignmentRules[index][type].push(selectedValue); renderSettingsModalContent(); } });
        } else {
            const options = ruleOptions[type];
            const value = prompt(`Neuen Wert für "${type}" hinzufügen.\nVerfügbare Optionen: ${options.join(', ')}`);
            if (value && value.trim()) { const trimmedValue = value.trim().toUpperCase(); if (!assignmentRules[index][type].includes(trimmedValue)) { assignmentRules[index][type].push(trimmedValue); renderSettingsModalContent(); } else { alert(`Der Wert "${trimmedValue}" ist bereits in dieser Regel vorhanden.`); } }
        }
    };

    window.removeTag = (index, type, tag) => { assignmentRules[index][type] = assignmentRules[index][type].filter(t => t !== tag); renderSettingsModalContent(); };
    window.addRule = () => { assignmentRules.push({ rule_id: '', event_titles: [], engine_types: [], required_skills: [] }); renderSettingsModalContent(); };
    window.removeRule = (index) => { if (confirm('Möchten Sie diese Regel wirklich löschen?')) { assignmentRules.splice(index, 1); renderSettingsModalContent(); } };

    openConfigBtn.addEventListener('click', async () => {
        try {
            // +++ ANFANG DER ÄNDERUNG +++
            // Rufe BEIDE APIs gleichzeitig auf
            const [rulesResponse, optionsResponse] = await Promise.all([
                fetch(`/api/assignment_rules/${timelineId}`),
                fetch(`/api/process_template_options`) // Die neue API
            ]);

            if (!rulesResponse.ok || !optionsResponse.ok) {
                throw new Error('Konfiguration konnte nicht geladen werden.');
            }
            
            const rulesData = await rulesResponse.json();
            engineEventMapping = await optionsResponse.json(); // Speichere die Optionen global
            
            allAvailableSkills = rulesData.options.skills || [];
            assignmentRules = rulesData.rules;
            // +++ ENDE DER ÄNDERUNG +++

            renderSettingsModalContent(); // Diese Funktion passen wir als nächstes an
            configModal.style.display = 'block'; 
            assignmentModalBackdrop.style.display = 'block';
        } catch (error) { 
            console.error("Fehler beim Laden der Einstellungen:", error); 
            alert("Fehler beim Laden der Konfiguration."); 
        }
    });

    function closeConfigModal() { configModal.style.display = 'none'; assignmentModalBackdrop.style.display = 'none'; }
    closeConfigBtn.addEventListener('click', closeConfigModal);

    saveConfigBtn.addEventListener('click', async () => {
        const selectedGroups = Array.from(document.querySelectorAll('#employeeGroupCheckboxes input:checked')).map(cb => cb.value);
        if (selectedGroups.length === 0) { alert("Bitte mindestens eine Mitarbeitergruppe auswählen."); return; }
        currentEmployeeGroupFilter = selectedGroups;
        const selectedCells = Array.from(document.querySelectorAll('#testCellCheckboxes input:checked')).map(cb => cb.value);
        currentTestCellFilter = selectedCells;
    
        const newRules = [];
        document.querySelectorAll('#configTableBody .rule-row').forEach(row => {
            const engineTypes = Array.from(row.querySelectorAll('.engine-types-container .tag-chip')).map(t => t.dataset.value);
            const eventTitles = Array.from(row.querySelectorAll('.event-titles-container .tag-chip')).map(t => t.dataset.value);
            const requiredSkills = Array.from(row.querySelectorAll('.required-skills-container .tag-chip')).map(t => t.dataset.value);
    
            // Statt Komma-getrenntem Text lesen wir jetzt auch die Skills als Pillen aus
            // Falls das Textfeld noch benutzt wird, kann man es als Fallback nehmen:
            // const skillsFromInput = row.querySelector('input[name="required_skills"]').value.split(',').map(s => s.trim()).filter(Boolean);
            // const allSkills = [...new Set([...requiredSkillsFromPills, ...skillsFromInput])];
    
            if (requiredSkills.length > 0) {
                newRules.push({
                    rule_id: '',
                    event_titles: eventTitles,
                    engine_types: engineTypes,
                    required_skills: requiredSkills
                });
            }
        });
        assignmentRules = newRules;
        
        try {
            const response = await fetch(`/api/assignment_rules/${timelineId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rules: assignmentRules }) });
            if (!response.ok) throw new Error('Fehler beim Speichern der Regeln.');
            alert("Einstellungen erfolgreich gespeichert!"); closeConfigModal();
            await recalculateAndRerenderAssignments();
        } catch (error) { alert(error.message); }
    });

    function getStartOfWeek(d) { const date = new Date(d); const day = date.getUTCDay() || 7; if (day !== 1) { date.setUTCHours(-24 * (day - 1)); } date.setUTCHours(0, 0, 0, 0); return date; }
    function getProjectPriority(projectId) { const tasks = masterTaskData[projectId] || []; if (tasks.length === 0) return 12; const priorities = []; const today = new Date(); const startOfCurrentWeek = getStartOfWeek(today); const startOfNextWeek = new Date(startOfCurrentWeek.getTime() + 604800000); const startOfLastWeek = new Date(startOfCurrentWeek.getTime() - 604800000); for (const task of tasks) { const taskStartDate = new Date(masterTimelineStartDate.getTime() + task.start * 43200000); if (taskStartDate >= startOfCurrentWeek && taskStartDate < startOfNextWeek) { priorities.push(1); continue; } const startOfNextNextWeek = new Date(startOfNextWeek.getTime() + 604800000); if (taskStartDate >= startOfNextWeek && taskStartDate < startOfNextNextWeek) { priorities.push(2); continue; } if (taskStartDate >= startOfLastWeek && taskStartDate < startOfCurrentWeek) { priorities.push(3); continue; } let isFuture = false; for (let i = 2; i <= 6; i++) { const startFutureWeek = new Date(startOfCurrentWeek.getTime() + i * 604800000); const endFutureWeek = new Date(startFutureWeek.getTime() + 604800000); if (taskStartDate >= startFutureWeek && taskStartDate < endFutureWeek) { priorities.push(4 + i); isFuture = true; break; } } if (isFuture) continue; } return priorities.length > 0 ? Math.min(...priorities) : 12; }
    function handleSortProjects() { if (masterProjectOrder.length === 0) return; masterProjectOrder.sort((a, b) => { const prioA = getProjectPriority(a); const prioB = getProjectPriority(b); return prioA - prioB; }); filterAndRender(); markChanges(); alert('Projekte wurden nach Priorität sortiert. Bitte speichern, um die neue Reihenfolge zu übernehmen.'); }

    function isTestCellBlocked(cellId, isoDate, shift) {
        if (!cellId || !allLocations) return null;
        const cell = allLocations.find(loc => loc.ID === cellId);
        if (!cell || !cell.events) return null;
        
        // Suche nach Blockern (Prio 1) oder Maintenance (Prio 2)
        const activeEvent = cell.events.find(e => {
            if (e.type !== 'Maintenance' && e.type !== 'Blocked') return false;
            const startOk = (e.start_date < isoDate) || (e.start_date === isoDate && e.start_shift <= shift);
            const endOk = (e.end_date > isoDate) || (e.end_date === isoDate && e.end_shift >= shift);
            return startOk && endOk;
        });
        
        return activeEvent; // Gibt das Event-Objekt oder undefined zurück
    }

    function convertApiDataToMasterFormat(apiData) {
        masterTimelineStartDate = new Date(apiData.timeline_config.start_date + 'T00:00:00.000Z');
        masterTotalColumns = apiData.timeline_config.num_days * apiData.timeline_config.columns_per_day;
        masterApiDataCache = apiData.projects;
        allEmployees = apiData.employees;
        allQualifications = apiData.qualifications;
        allLocations = apiData.locations || [];
        manualAssignments.clear();
        prepareEmployeeData();
        masterTaskData = {}; masterProjectDetails = {}; masterProjectOrder = [];
        const serverAssignments = apiData.calculated_data.assignments;
        apiData.projects.forEach(proj => {
            const projectId = `${proj.project_code}_${proj.engine_serial}`;
            masterProjectOrder.push(projectId);
            masterProjectDetails[projectId] = { ...proj };
            masterTaskData[projectId] = [];
            (proj.events || []).forEach(event => {
                if (!event.start_datetime || !event.end_datetime) return;
                const eventStart = new Date(event.start_datetime); const eventEnd = new Date(event.end_datetime);
                const startDiffMs = eventStart.getTime() - masterTimelineStartDate.getTime();
                const startColumn = Math.round(startDiffMs / 43200000);
                const durationMs = eventEnd.getTime() - eventStart.getTime();
                const span = Math.max(1, Math.round(durationMs / 43200000));
                const assigned_employees_per_slot = [], is_manual_per_slot = [], test_cell_id_per_slot = [];
                for (let i = 0; i < span; i++) {
                    const eventSlotKey = `${event.id}_${i}`;
                    const assignmentInfo = serverAssignments[eventSlotKey];
                    if (assignmentInfo) {
                        assigned_employees_per_slot.push(assignmentInfo.employees);
                        is_manual_per_slot.push(assignmentInfo.is_manual);
                        test_cell_id_per_slot.push(assignmentInfo.test_cell_id || null);
                    } else { assigned_employees_per_slot.push([]); is_manual_per_slot.push(false); test_cell_id_per_slot.push(null); }
                }
                masterTaskData[projectId].push({ id: event.id, name: event.title, start: startColumn, span: span, originalEvent: { ...event }, assigned_employees: assigned_employees_per_slot, is_manual: is_manual_per_slot, test_cell_id: test_cell_id_per_slot });
            });
        });
        filteredProjectOrder = [...masterProjectOrder];
    }

    function convertMasterDataToApiFormat() { const projectDetailsMap = new Map(masterApiDataCache.map(p => [`${p.project_code}_${p.engine_serial}`, p])); const orderedProjectsToSave = masterProjectOrder.map(projectId => { const originalProject = projectDetailsMap.get(projectId); if (!originalProject) return null; const updatedProject = JSON.parse(JSON.stringify(originalProject)); const localTasks = masterTaskData[projectId] || []; updatedProject.events = []; localTasks.forEach(localTask => { const startMs = masterTimelineStartDate.getTime() + localTask.start * 43200000; const endMs = startMs + localTask.span * 43200000; const startDt = new Date(startMs); const endDt = new Date(endMs); const apiEvent = { ...localTask.originalEvent, start_datetime: startDt.toISOString(), end_datetime: endDt.toISOString(), start: startDt.toISOString().split('T')[0], start_shift: startDt.getUTCHours() < 12 ? 'AM' : 'PM', end: endDt.toISOString().split('T')[0], end_shift: endDt.getUTCHours() < 12 ? 'AM' : 'PM', }; updatedProject.events.push(apiEvent); }); return updatedProject; }).filter(Boolean); return orderedProjectsToSave; }
    function generateEmployeeColor(initials) { if (employeeColorMap.has(initials)) return employeeColorMap.get(initials); let hash = 0; for (let i = 0; i < initials.length; i++) { hash = initials.charCodeAt(i) + ((hash << 5) - hash); } const colorIndex = Math.abs(hash % TAILWIND_AVATAR_COLORS.length); const colorClass = TAILWIND_AVATAR_COLORS[colorIndex]; employeeColorMap.set(initials, colorClass); return colorClass; }
    function prepareEmployeeData() { employeeQualificationsMap.clear(); allQualifications.forEach(q => { if (q.id) employeeQualificationsMap.set(q.id, q); }); employeeAvailabilityMap.clear(); allEmployees.forEach(emp => { if (!emp.id) return; const empId = emp.id.toLowerCase(); generateEmployeeColor(emp.initials); const availability = {}; (emp.weeks || []).forEach(week => { const shiftFromCsv = (week.Schicht || '').toUpperCase(); let webShift = null; if (shiftFromCsv === 'F') webShift = 'AM'; else if (shiftFromCsv === 'S') webShift = 'PM'; if (!webShift) return; (week.days || []).forEach(day => { if (day.date) { const dateObj = new Date(day.date); if (isNaN(dateObj.getTime())) return; const year = dateObj.getUTCFullYear(); const month = String(dateObj.getUTCMonth() + 1).padStart(2, '0'); const dayOfMonth = String(dateObj.getUTCDate()).padStart(2, '0'); const isoKey = `${year}-${month}-${dayOfMonth}`; const isAvailable = !(day.ereignis || '').trim(); availability[isoKey] = { shift: webShift, available: isAvailable }; } }); }); employeeAvailabilityMap.set(emp.id, availability); }); }
    function findTask(projectId, taskId) { return masterTaskData[projectId]?.find(t => t.id === taskId); }

    function createAvatarsHTML(employeeIds, isManual, project, task, testCellId, slotDate, slotShift) {
        // 1. Mitarbeiter-Avatare generieren (Runde Kreise)
        let employeeAvatarsHtml = '';
        if (employeeIds && employeeIds.length > 0) {
            const avatars = employeeIds.map(empId => {
                const employee = allEmployees.find(e => e.id.toLowerCase() === empId.toLowerCase());
                if (!employee) { 
                    return `<div class="employee-avatar" title="Unbekannt: ${empId}" style="background-color: red;">?</div>`; 
                }

                const initial = employee.initials; 
                let bgColor = QUALI_STATUS_COLORS.DEFAULT; 
                let textColor = 'white'; 
                let borderColor = 'rgba(0,0,0,0.1)';

                const status = masterQualStatuses[task.id]?.[empId.toLowerCase()] || null;

                if (status && QUALI_STATUS_COLORS[status]) { 
                    bgColor = QUALI_STATUS_COLORS[status]; 
                    if (bgColor === '#FFFFFF') { textColor = '#ef4444'; borderColor = '#ef4444'; } 
                } else if (isManual) { 
                    bgColor = QUALI_STATUS_COLORS.MANUAL_NO_QUALI; 
                    textColor = '#ef4444'; 
                    borderColor = '#ef4444'; 
                }

                const manualDotHtml = isManual ? '<div class="manual-assignment-dot"></div>' : '';
                return `<div class="employee-avatar" title="${initial}" style="background-color: ${bgColor}; color: ${textColor}; border-color: ${borderColor};">${initial}${manualDotHtml}</div>`;
            }).join('');

            employeeAvatarsHtml = `<div class="employee-avatars-container">${avatars}</div>`;
        } else {
            // Platzhalter, damit das Layout nicht springt, wenn keine MA da sind (optional)
            employeeAvatarsHtml = `<div class="employee-avatars-container" style="height: 16px;"></div>`;
        }

    // 2. Testzellen-Badge generieren (Rechteckig)
        let testCellHtml = '';
        if (testCellId) {
            const location = allLocations.find(loc => loc.ID === testCellId);
            const cellName = location ? location.TESTCELL : testCellId;

            // HIER DIE ÄNDERUNG: Wir nutzen jetzt strikt die ID für die Anzeige
            const displayName = testCellId; 

            const blockingEvent = isTestCellBlocked(testCellId, slotDate, slotShift);
            const isBlocked = !!blockingEvent;
            const bgColorStr = isBlocked ? '#dc2626' : '#475569';
            
            let titleText = cellName;
            if (isBlocked) {
                // Nutze den Titel aus dem Event ("⛔ Sperre: ...")
                titleText = `${cellName} (KONFLIKT)\n${blockingEvent.title}`;
                if (blockingEvent.description) titleText += `\n${blockingEvent.description}`;
            }

            testCellHtml = `<div class="test-cell-badge" style="background-color: ${bgColorStr};" title="${titleText}">${displayName}</div>`;
        }

        return employeeAvatarsHtml + testCellHtml;
    }

    function renderHeader(offsetWeeks) { const header = document.getElementById('unifiedHeader'); header.innerHTML = ''; if (!masterTimelineStartDate) return; const headerDatesRow = document.createElement('tr'); headerDatesRow.className = 'header-row-dates'; const headerShiftsRow = document.createElement('tr'); headerShiftsRow.className = 'header-row-shifts'; const projectTh = document.createElement('th'); projectTh.className = 'sticky-col header-cell-project'; projectTh.rowSpan = 2; projectTh.innerHTML = 'Projekt'; headerDatesRow.appendChild(projectTh); const days = ['So', 'Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa']; const numDays = VISIBLE_WEEKS * 7; const displayStartDate = new Date(masterTimelineStartDate.getTime() + offsetWeeks * 604800000); for (let i = 0; i < numDays; i++) { const currentDate = new Date(displayStartDate.getTime() + i * 86400000); const isWeekend = currentDate.getUTCDay() === 0 || currentDate.getUTCDay() === 6; const dateCell = document.createElement('th'); dateCell.colSpan = 2; dateCell.className = "header-cell-date"; if (isWeekend) dateCell.classList.add('weekend-header'); dateCell.innerHTML = `<div>${days[currentDate.getUTCDay()]}, ${currentDate.toLocaleDateString('de-DE', {day: '2-digit', month: '2-digit'})}</div>`; headerDatesRow.appendChild(dateCell); ['AM', 'PM'].forEach(shift => { const shiftCell = document.createElement('th'); shiftCell.className = "header-cell-shift"; if (isWeekend) shiftCell.classList.add('weekend-header'); shiftCell.innerHTML = shift; headerShiftsRow.appendChild(shiftCell); }); } header.appendChild(headerDatesRow); header.appendChild(headerShiftsRow); }

    function renderBody(offsetWeeks) {
        const body = document.getElementById('unifiedBody'); body.innerHTML = '';
        const visibleStartCol = offsetWeeks * 14;
        const visibleEndCol = visibleStartCol + (VISIBLE_WEEKS * 14);
        filteredProjectOrder.forEach(projectId => {
            const row = document.createElement('tr'); row.dataset.projectRowId = projectId; const proj = masterProjectDetails[projectId]; if (proj && !proj.active) row.classList.add('opacity-60');
            const projectNameCell = document.createElement('td'); projectNameCell.className = 'sticky-col project-name-cell'; projectNameCell.draggable = true; projectNameCell.dataset.dragType = 'project-row'; projectNameCell.dataset.projectId = projectId;
            const activeClass = proj.active ? '' : 'opacity-60'; const activeBtnClass = proj.active ? 'bg-green-500 hover:bg-green-600' : 'bg-red-500 hover:bg-red-600'; const activeBtnText = proj.active ? 'Aktiv' : 'Inaktiv';
            const commentCount = proj.comment_count || 0; const countBadgeHtml = commentCount > 0 ? `<span class="comment-count-badge">${commentCount}</span>` : '';

// --- Buttons definieren ---
            const addBlockBtnHtml = `<button class="add-new-block-btn" data-project-id="${projectId}" title="Neuen Block hinzufügen"><span class="material-icons-outlined">add_circle_outline</span></button>`;
            const commentBtnHtml = `<button class="open-comments-btn" data-project-id="${projectId}" data-project-name="${proj.name}" title="Kommentare"><span class="material-icons-outlined">chat_bubble_outline</span>${countBadgeHtml}</button>`;

            const isManualProject = (!proj.BE || proj.BE.trim() === '');
            const manualProjectIcon = isManualProject ? '<span class="material-icons-outlined text-base text-green-600 ml-2" title="Manuell erstelltes Projekt">add_circle</span>' : '';

            // Aktiv/Inaktiv Button bekommt jetzt volle Breite im Flex-Container unten
            const toggleBtnHtml = `<button class="toggle-active-btn ${activeBtnClass} w-full mt-1" data-project-id="${projectId}" title="${proj.active ? 'Projekt deaktivieren' : 'Projekt aktivieren'}">${activeBtnText}</button>`;

            let extraButtonsHtml = '';
            if (!proj.active) { 
                extraButtonsHtml += `<button class="reset-project-btn bg-gray-400 hover:bg-gray-500 ml-1" data-project-id="${projectId}" title="Events zurücksetzen"><span class="material-icons-outlined" style="font-size: 14px; vertical-align: middle;">restart_alt</span></button>`; 
                if (isManualProject) { 
                    extraButtonsHtml += `<button class="delete-project-btn bg-red-600 hover:bg-red-700 text-white ml-1" data-project-id="${projectId}" title="Projekt löschen"><span class="material-icons-outlined" style="font-size: 14px; vertical-align: middle;">delete_forever</span></button>`; 
                } 
            }

            // --- NEUES LAYOUT FÜR DIE PROJEKT-ZELLE ---
            // Wir nutzen ein Grid oder Flex-Column für die rechte Seite, um zu stapeln
            projectNameCell.innerHTML = `
            <div class="flex justify-between items-center h-full ${activeClass} w-full pr-2" draggable="false">
                <div class="flex items-center flex-grow min-w-0 mr-2">
                    <div class="truncate">
                        <div class="font-medium text-slate-900 truncate" title="${proj.name}">${proj.name}</div>
                        <div class="text-xs text-slate-500 truncate" title="${proj.engine_type} / ${proj.project_code} / ${proj.engine_serial} - ${proj.customer}">${proj.customer} / ${proj.engine_serial} </div>
                    </div>
                    ${manualProjectIcon}
                </div>
                <div class="flex flex-col items-end flex-shrink-0">
                    <div class="flex items-center mb-1">
                        ${addBlockBtnHtml}
                        ${commentBtnHtml}
                        ${extraButtonsHtml}
                    </div>
                    ${toggleBtnHtml}
                </div>
            </div>`;

            row.appendChild(projectNameCell);
            const cells = Array.from({ length: VISIBLE_WEEKS * 14 }, (_, i) => { const cell = document.createElement('td'); cell.dataset.columnIndex = visibleStartCol + i; const cellDate = new Date(masterTimelineStartDate.getTime() + (visibleStartCol + i) * 43200000); if (cellDate.getUTCDay() === 0 || cellDate.getUTCDay() === 6) { cell.classList.add('weekend-bg'); } return cell; });
            const projectTasks = masterTaskData[projectId] || []; projectTasks.sort((a, b) => a.start - b.start);
            projectTasks.forEach((task, taskIndex) => {
                const isFirstSegment = taskIndex === 0 || projectTasks[taskIndex - 1].name !== task.name; const isLastSegment = taskIndex === projectTasks.length - 1 || projectTasks[taskIndex + 1].name !== task.name;
                for (let i = 0; i < task.span; i++) {
                    const globalColIndex = task.start + i;
                    if (globalColIndex >= visibleStartCol && globalColIndex < visibleEndCol) {
                        const slotDateTime = new Date(masterTimelineStartDate.getTime() + globalColIndex * 43200000);
                        const slotDateIso = slotDateTime.toISOString().split('T')[0];
                        const slotShift = slotDateTime.getUTCHours() < 12 ? 'AM' : 'PM';
                        const localColIndex = globalColIndex - visibleStartCol; const cell = cells[localColIndex]; cell.innerHTML = '';
                        let roundingClass = ''; if (isFirstSegment && i === 0) roundingClass += ' rounded-l-md'; if (isLastSegment && i === task.span - 1) roundingClass += ' rounded-r-md';

                        // --- ANFANG DER KORREKTUR ---
                        const employeesForSegment = (task.assigned_employees && task.assigned_employees[i]) ? task.assigned_employees[i] : [];
                        const isManualForSegment = (task.is_manual && task.is_manual[i]) ? task.is_manual[i] : false;
                        // NEU: Testzelle für diesen spezifischen Slot auslesen
                        const testCellForSegment = (task.test_cell_id && task.test_cell_id[i]) ? task.test_cell_id[i] : null;

                        // NEU: Parameter für Testzelle an createAvatarsHTML übergeben
                        const avatarsHTML = createAvatarsHTML(employeesForSegment, isManualForSegment, proj, task, testCellForSegment, slotDateIso, slotShift);
                        // --- ENDE DER KORREKTUR ---

                        const taskBar = document.createElement('div'); taskBar.className = 'task-bar'; taskBar.draggable = proj.active; taskBar.dataset.taskId = task.id; taskBar.dataset.projectId = projectId;
                        const inactiveClass = proj.active ? '' : 'inactive'; const eventTitleUpper = task.name.toUpperCase(); const colorHex = proj.active ? (EVENT_COLOR_MAP[eventTitleUpper] || EVENT_COLOR_MAP['DEFAULT']) : EVENT_COLOR_MAP['INACTIVE'];
                        taskBar.innerHTML = `<div class="task-segment-container ${inactiveClass} ${roundingClass}" style="background-color: ${colorHex};"><div class="task-segment-content"><span class="task-name-display">${task.name}</span>${avatarsHTML}</div></div>${i === task.span - 1 ? `<div class="resize-handle resize-handle-right ${inactiveClass}"></div>` : ''}`;
                        cell.appendChild(taskBar);
                    }
                }
            });
            for (let i = 0; i < projectTasks.length - 1; i++) { const currentTask = projectTasks[i]; const nextTask = projectTasks[i + 1]; const endOfCurrent = currentTask.start + currentTask.span; const startOfNext = nextTask.start; for (let j = endOfCurrent; j < startOfNext; j++) { if (j >= visibleStartCol && j < visibleEndCol) { const localColIndex = j - visibleStartCol; const cell = cells[localColIndex]; if (cell && !cell.hasChildNodes()) { cell.innerHTML = `<div class="connector-line-container"><div class="connector-line"></div></div>`; } } } }
            const milestoneLabels = { 'BE': 'B3', 'TS': 'TS', 'TE': 'TE', 'SA': 'SA' };
            Object.keys(milestoneLabels).forEach(key => { try { const dateStr = proj[key]; if (!dateStr || typeof dateStr !== 'string' || dateStr.trim() === '') return; const parts = dateStr.trim().split(/\s+/).filter(Boolean); if (parts.length === 0) return; const dateParts = parts[0].split('.'); if (dateParts.length < 3) return; const day = parseInt(dateParts[0], 10), month = parseInt(dateParts[1], 10) - 1, year = parseInt(dateParts[2], 10); if (isNaN(day) || isNaN(month) || isNaN(year)) return; let hour = 0; if (parts.length > 1 && parts[1]) { const timeParts = parts[1].split(':'); if (timeParts.length > 1) { const parsedHour = parseInt(timeParts[0], 10); if (!isNaN(parsedHour)) hour = parsedHour; } } const milestoneDate = new Date(Date.UTC(year, month, day, hour)); if (isNaN(milestoneDate.getTime())) return; const diffMs = milestoneDate.getTime() - masterTimelineStartDate.getTime(); const globalColFloat = diffMs / 43200000; if (globalColFloat >= visibleStartCol && globalColFloat < visibleEndCol) { const localColFloat = globalColFloat - visibleStartCol; const targetCellIndex = Math.floor(localColFloat); const targetCell = cells[targetCellIndex]; const fractionInCell = localColFloat - targetCellIndex; const leftPercentage = fractionInCell * 100; if (targetCell) { const marker = document.createElement('div'); marker.className = 'milestone-marker'; marker.title = `${milestoneLabels[key]}: ${dateStr}`; marker.style.left = `${leftPercentage}%`; marker.innerHTML = `<span>${milestoneLabels[key]}</span>`; targetCell.appendChild(marker); } } } catch (error) { console.error(`Fehler bei Meilenstein '${key}' für Projekt '${proj.name}':`, error); } });
            cells.forEach(cell => row.appendChild(cell)); body.appendChild(row);
        });
    }

    function renderFooter(offsetWeeks, availabilityData) { const footer = document.getElementById('unifiedFooter'); footer.innerHTML = ''; const footerRow = document.createElement('tr'); const availableLabelCell = document.createElement('td'); availableLabelCell.className = 'sticky-col project-name-cell font-semibold'; availableLabelCell.innerHTML = 'Verfügbar'; footerRow.appendChild(availableLabelCell); const visibleStartCol = offsetWeeks * 14; const MAX_AVATARS_TO_SHOW = 6; for (let i = 0; i < VISIBLE_WEEKS * 14; i++) { const globalColIndex = visibleStartCol + i; const slotData = availabilityData[globalColIndex] || { available: [], manual: [] }; const cell = document.createElement('td'); cell.dataset.columnIndex = globalColIndex; const cellDate = new Date(masterTimelineStartDate.getTime() + globalColIndex * 43200000); if (cellDate.getUTCDay() === 0 || cellDate.getUTCDay() === 6) { cell.classList.add('weekend-bg'); } const allPeopleInCell = [...slotData.available, ...slotData.manual]; const peopleToShow = allPeopleInCell.slice(0, MAX_AVATARS_TO_SHOW); const remainingCount = allPeopleInCell.length - peopleToShow.length; const avatarsHTML = peopleToShow.map(initials => { const colorClass = employeeColorMap.get(initials) || 'bg-gray-200 text-gray-800'; const isManual = slotData.manual.includes(initials); const manualDot = isManual ? '<div class="manual-assignment-dot"></div>' : ''; return `<div class="available-employee-avatar ${colorClass}" title="${initials}">${initials}${manualDot}</div>`; }).join(''); let remainingCountHTML = remainingCount > 0 ? `<div class="remaining-count" title="${remainingCount} weitere verfügbar">+${remainingCount}</div>` : ''; cell.innerHTML = `<div><div class="avatars-in-cell-container">${avatarsHTML}</div>${remainingCountHTML}</div>`; if (slotData.manual.length > 0) { cell.classList.add('footer-cell-clickable'); } footerRow.appendChild(cell); } footer.appendChild(footerRow); }
    function filterAndRender() { const searchTerm = searchInput.value.toLowerCase(); if (!searchTerm) { filteredProjectOrder = [...masterProjectOrder]; } else { filteredProjectOrder = masterProjectOrder.filter(projectId => { const proj = masterProjectDetails[projectId]; return proj.name.toLowerCase().includes(searchTerm) || proj.project_code.toLowerCase().includes(searchTerm) || proj.engine_serial.toLowerCase().includes(searchTerm); }); } renderUnifiedTable(); }
    function renderUnifiedTable() { if (!masterApiDataCache) return; renderHeader(currentWeekOffset); renderBody(currentWeekOffset); renderFooter(currentWeekOffset, masterFooterData); updateNavButtons(); updateScrollShadow(); highlightCurrentTimeslotAndScroll(); }

    // ### ANFANG DER ÄNDERUNG ###
    function handleListClick(e) {
        const toggleBtn = e.target.closest('.toggle-active-btn');
        const resetBtn = e.target.closest('.reset-project-btn');
        const commentBtn = e.target.closest('.open-comments-btn');
        const deleteBtn = e.target.closest('.delete-project-btn');
        const addBtn = e.target.closest('.add-new-block-btn'); // Neuer Button

        if (toggleBtn) handleToggleActive(toggleBtn);
        else if (resetBtn) handleResetProject(resetBtn);
        else if (deleteBtn) handleDeleteProject(deleteBtn);
        else if (addBtn) handleAddNewBlock(addBtn); // Neue Handler-Funktion aufrufen
        else if (commentBtn) {
            const projectId = commentBtn.dataset.projectId;
            const projectName = commentBtn.dataset.projectName;
            openCommentsModal(projectId, projectName);
        }
    }


    async function handleDeleteProject(button) { const projectId = button.dataset.projectId; const proj = masterProjectDetails[projectId]; if (!proj) return; if (confirm(`Möchten Sie das Projekt "${proj.name}" wirklich löschen?`)) { delete masterProjectDetails[projectId]; delete masterTaskData[projectId]; masterProjectOrder = masterProjectOrder.filter(id => id !== projectId); masterApiDataCache = masterApiDataCache.filter(p => `${p.project_code}_${p.engine_serial}` !== projectId); markChanges(); const saved = await saveChanges(true); if (saved) { alert("Projekt wurde gelöscht."); initializeTimeline(); } else { alert("Projekt konnte nicht gelöscht werden."); } } }
    function handleToggleActive(button) { const projectId = button.dataset.projectId; if (masterProjectDetails[projectId]) { masterProjectDetails[projectId].active = !masterProjectDetails[projectId].active; const apiProject = masterApiDataCache.find(p => `${p.project_code}_${p.engine_serial}` === projectId); if (apiProject) { apiProject.active = masterProjectDetails[projectId].active; } markChanges(); recalculateAndRerenderAssignments(); } }
    async function handleResetProject(button) { // <-- Funktion wird async
        const projectId = button.dataset.projectId;
        const proj = masterProjectDetails[projectId];
    
        if (!proj || proj.active || !confirm(`Events für "${proj.name}" zurücksetzen?`)) {
            return;
        }
    
        const tsDateStr = proj.TS;
        if (!tsDateStr) {
            alert("Kein 'TS' Datum für dieses Projekt gefunden. Zurücksetzen nicht möglich.");
            return;
        }
        
        // --- ANFANG DER NEUEN LOGIK ---
        
        // TS-Datum parsen, um es an die API zu senden
        const parts = tsDateStr.trim().split(/\s+/).filter(Boolean);
        const dateParts = parts[0].split('.');
        if (dateParts.length < 3) {
            alert("Ungültiges TS-Datum.");
            return;
        }
        const day = parseInt(dateParts[0], 10), month = parseInt(dateParts[1], 10) - 1, year = parseInt(dateParts[2], 10);
        let hour = 8; // Standard 8 Uhr
        if (parts.length > 1 && parts[1]) {
            const timeParts = parts[1].split(':');
            if (timeParts.length > 1) hour = parseInt(timeParts[0], 10);
        }
        // WICHTIG: Sende das Datum als UTC ISO String, damit die Zeitzone im Backend stimmt
        const tsDateUTC = new Date(Date.UTC(year, month, day, hour));
        if (isNaN(tsDateUTC.getTime())) {
            alert("TS-Datum konnte nicht verarbeitet werden.");
            return;
        }
    
        try {
            const encodedDate = encodeURIComponent(tsDateUTC.toISOString());
            const response = await fetch(`/api/generate_events_from_template/${encodeURIComponent(proj.engine_type)}/${encodedDate}`);
            if (!response.ok) {
                const error = await response.json();
                throw new Error(error.error || 'Serverfehler beim Generieren der Events.');
            }
            const eventTemplates = await response.json();
    
            // Die neuen Events in das richtige Format für die Timeline umwandeln
            const newEvents = [];
            eventTemplates.forEach(eventTpl => {
                const startDt = new Date(`${eventTpl.start}T${eventTpl.start_shift === 'AM' ? '00' : '12'}:00:00.000Z`);
                const endDt = new Date(`${eventTpl.end}T${eventTpl.end_shift === 'AM' ? '12' : '24'}:00:00.000Z`);
    
                const startDiffMs = startDt.getTime() - masterTimelineStartDate.getTime();
                const startColumn = Math.round(startDiffMs / 43200000);
                const span = Math.round((endDt - startDt) / 43200000);
                
                const newId = `${projectId}_${eventTpl.title}_${startDt.toISOString()}`;
                
                const assigned_employees_per_slot = Array.from({ length: span }, () => []);
                const is_manual_per_slot = Array.from({ length: span }, () => false);
                const test_cell_id_per_slot = Array.from({ length: span }, () => null);
    
                newEvents.push({
                    id: newId,
                    name: eventTpl.title,
                    start: startColumn,
                    span: span,
                    assigned_employees: assigned_employees_per_slot,
                    is_manual: is_manual_per_slot,
                    test_cell_id: test_cell_id_per_slot,
                    originalEvent: {
                        id: newId,
                        title: eventTpl.title,
                        start: eventTpl.start,
                        start_shift: eventTpl.start_shift,
                        end: eventTpl.end,
                        end_shift: eventTpl.end_shift,
                        start_datetime: startDt.toISOString(),
                        end_datetime: endDt.toISOString(),
                        color: eventTpl.color
                    }
                });
            });
            
            masterTaskData[projectId] = newEvents;
            markChanges();
            recalculateAndRerenderAssignments();
            alert("Events wurden basierend auf dem Template zurückgesetzt. Bitte speichern.");
    
        } catch (error) {
            alert("Fehler beim Zurücksetzen der Events: " + error.message);
        }
        // --- ENDE DER NEUEN LOGIK ---
    }    
    function handleDrop(e) {
        e.preventDefault();
        e.stopPropagation();

        // Visuelle Highlights entfernen
        document.querySelectorAll('.drop-target-highlight, .row-drop-target-highlight').forEach(el => { 
            el.classList.remove('drop-target-highlight', 'row-drop-target-highlight'); 
        });

        const targetCell = e.target.closest('td');
        if (!targetCell || !activeDragData) return;

        const targetRow = targetCell.closest('tr');
        if (!targetRow) return;
        const targetProjectId = targetRow.dataset.projectRowId;

        // --- Logik für Projekt-Sortierung ---
        if (activeDragData.type === 'project-row') {
             if (!targetProjectId) return;
             const sourceProjectId = activeDragData.projectId; 
             if (targetProjectId !== sourceProjectId) {
                 const sourceIndex = masterProjectOrder.indexOf(sourceProjectId); 
                 const targetIndex = masterProjectOrder.indexOf(targetProjectId);
                 if (sourceIndex === -1 || targetIndex === -1) return;

                 const [movedProject] = masterProjectOrder.splice(sourceIndex, 1);
                 const newTargetIndex = masterProjectOrder.indexOf(targetProjectId);

                 if (sourceIndex < targetIndex) { 
                     masterProjectOrder.splice(newTargetIndex + 1, 0, movedProject); 
                 } else { 
                     masterProjectOrder.splice(newTargetIndex, 0, movedProject); 
                 } 
                 markChanges(); 
                 filterAndRender();
             }
             return;
        }

        // --- Logik für Task-Verschiebung ---
        if (activeDragData.type === 'task') {
            const targetTimelineCell = e.target.closest('td:not(.sticky-col)');
            if (!targetTimelineCell) return;

            const dropColIndex = parseInt(targetTimelineCell.dataset.columnIndex, 10);
            const delta = dropColIndex - activeDragData.leaderOriginalStart;

            if (delta === 0) return; 

            let canMoveAll = true;

            for (const item of activeDragData.group) {
                const currentTask = item.task;
                const newStart = currentTask.start + delta;
                const newEnd = newStart + currentTask.span;

                if (newStart < 0 || newEnd > masterTotalColumns) {
                    canMoveAll = false; 
                    break;
                }

                const projectTasks = masterTaskData[item.projectId];
                for (const t of projectTasks) {
                    if (selectedTaskIds.has(t.id)) continue;
                    if (newStart < (t.start + t.span) && newEnd > t.start) {
                        canMoveAll = false; 
                        break;
                    }
                }
                if (!canMoveAll) break;
            }

            if (canMoveAll) {
                activeDragData.group.forEach(item => {
                    item.task.start += delta;
                });
                markChanges();
                recalculateAndRerenderAssignments();

                // ### FIX: HIERFÜGEN WIR DAS HINZU ###
                clearSelection(); 
                // #####################################
            } else {
                console.log("Verschieben blockiert: Kollision oder außerhalb des Bereichs");
            }
        }
    }
    async function recalculateAndRerenderAssignments() { const currentProjectsState = convertMasterDataToApiFormat(); try { const payload = { projects: currentProjectsState, assignment_group: currentEmployeeGroupFilter, selected_test_cells: currentTestCellFilter }; const response = await fetch(`/api/v2/recalculate_assignments/${timelineId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { throw new Error("Neuberechnung fehlgeschlagen."); } const responseData = await response.json(); const newAssignments = responseData.calculated_data.assignments; for (const projectId in masterTaskData) { masterTaskData[projectId].forEach(task => { for (let i = 0; i < task.span; i++) { const eventSlotKey = `${task.id}_${i}`; const assignmentInfo = newAssignments[eventSlotKey]; if (assignmentInfo) { task.assigned_employees[i] = assignmentInfo.employees; task.is_manual[i] = assignmentInfo.is_manual; if (task.test_cell_id) { task.test_cell_id[i] = assignmentInfo.test_cell_id || null; } } else { task.assigned_employees[i] = []; task.is_manual[i] = false; if (task.test_cell_id) { task.test_cell_id[i] = null; } } } }); } masterBlockedData = responseData.calculated_data.blocked_employees || {}; masterFooterData = responseData.calculated_data.footer_availability; manualAssignments.clear(); if (responseData.manual_assignments) { responseData.manual_assignments.forEach(ma => { const key = `${ma.project_code}_${ma.engine_serial}_${ma.event_title}_${ma.date_str}_${ma.shift}_${ma.employee_id.toLowerCase()}`; manualAssignments.set(key, ma); }); } renderBody(currentWeekOffset); renderFooter(currentWeekOffset, masterFooterData); highlightCurrentTimeslotAndScroll(); updateScrollShadow(); } catch (error) { console.error("Fehler bei Echtzeit-Aktualisierung:", error); alert("Fehler bei der Mitarbeiterzuweisung."); } }
    function handleDragOver(e) {
        e.preventDefault();
        if (!activeDragData) return;

        // Alte Highlights entfernen
        document.querySelectorAll('.drop-target-highlight, .row-drop-target-highlight').forEach(el => { 
            el.classList.remove('drop-target-highlight', 'row-drop-target-highlight'); 
        });

        // --- Vorschau für Projekt-Sortierung ---
        if (activeDragData.type === 'project-row') {
             const targetRow = e.target.closest('tr');
             if (targetRow) {
                 document.body.classList.add('is-row-dragging');
                 e.dataTransfer.dropEffect = 'move';
                 targetRow.classList.add('row-drop-target-highlight');
             }
             return;
        }

        // --- Vorschau für Task-Verschiebung ---
        if (activeDragData.type === 'task') {
            document.body.classList.remove('is-row-dragging');

            const targetTimelineCell = e.target.closest('td:not(.sticky-col)');
            if (!targetTimelineCell) { 
                e.dataTransfer.dropEffect = 'none'; 
                return; 
            }

            // Delta berechnen (wie weit hat sich die Maus vom Startpunkt des Leaders bewegt)
            const hoverColIndex = parseInt(targetTimelineCell.dataset.columnIndex, 10);
            const delta = hoverColIndex - activeDragData.leaderOriginalStart;

            // Grobe Validierung für die Vorschau: Fliegt irgendwas aus dem Bild?
            // (Kollisionsprüfung ist hier meist zu teuer für "jedes Pixel Mausbewegung", 
            // daher prüfen wir nur die Grenzen)
            let canDrop = true;
             for (const item of activeDragData.group) {
                const newStart = item.task.start + delta;
                const newEnd = newStart + item.task.span;
                if (newStart < 0 || newEnd > masterTotalColumns) { 
                    canDrop = false; 
                    break; 
                }
            }

            if (canDrop) {
                e.dataTransfer.dropEffect = 'move';

                // Berechne den sichtbaren Bereich, um Performance zu sparen
                // (Wir markieren nur Zellen, die man auch sieht)
                const visibleStartCol = currentWeekOffset * 14;
                const visibleEndCol = visibleStartCol + (VISIBLE_WEEKS * 14);

                // Iteriere über ALLE bewegten Tasks und markiere ihre Zielposition
                activeDragData.group.forEach(item => {
                     const newStart = item.task.start + delta;

                     // Finde die HTML-Zeile (TR) für das Projekt dieses Tasks
                     const row = document.querySelector(`tr[data-project-row-id="${item.projectId}"]`);

                     if (row) {
                         // Markiere die Zellen für die Länge (Span) des Tasks
                         for(let i=0; i < item.task.span; i++) {
                             const colIdx = newStart + i;
                             // Nur markieren, wenn im sichtbaren Bereich
                             if (colIdx >= visibleStartCol && colIdx < visibleEndCol) {
                                 const cell = row.querySelector(`td[data-column-index="${colIdx}"]`);
                                 if(cell) cell.classList.add('drop-target-highlight');
                             }
                         }
                     }
                });
            } else {
                e.dataTransfer.dropEffect = 'none';
            }
        }
    }
    function handleDragEnd(e) { document.body.classList.remove('is-dragging', 'is-row-dragging'); document.querySelectorAll('.is-dragging-segment').forEach(el => el.classList.remove('is-dragging-segment')); activeDragData = null; isDraggingFlag = false; }
    function handleDragStart(e) {
        if (isResizing) { 
            e.preventDefault(); 
            return; 
        }

        const dragTarget = e.target;
        const taskBar = dragTarget.closest('.task-bar');

        // Fall 1: Ein Task wird gezogen
        if (taskBar) {
            const projectId = taskBar.dataset.projectId;
            // Prüfen ob Projekt aktiv ist
            if (!masterProjectDetails[projectId] || !masterProjectDetails[projectId].active) { 
                e.preventDefault(); 
                return; 
            }

            e.stopPropagation();
            const taskId = taskBar.dataset.taskId;

            // Wenn der gezogene Task NICHT Teil der aktuellen Auswahl ist,
            // löschen wir die alte Auswahl und wählen nur diesen neuen Task aus.
            // Das ist Standard-Verhalten in Betriebssystemen.
            if (!selectedTaskIds.has(taskId)) {
                clearSelection();
                toggleTaskSelection(taskId, true);
            }

            const leaderTask = findTask(projectId, taskId);

            // Wir erstellen eine Liste aller Tasks, die bewegt werden sollen.
            // Dazu iterieren wir über alle IDs in selectedTaskIds und suchen die Objekte.
            const movingTasks = [];

            // Performance-Hinweis: Wir gehen durch alle Projekte, um die Tasks zur ID zu finden.
            // Da die ID eindeutig ist, ist das sicher.
            selectedTaskIds.forEach(selId => {
                for (const pId in masterTaskData) {
                    const foundTask = masterTaskData[pId].find(t => t.id === selId);
                    if (foundTask) {
                        movingTasks.push({
                            task: foundTask,
                            projectId: pId,
                            // WICHTIG: Wir speichern den Abstand (Offset) relativ zum gezogenen Element ("Leader")
                            offsetCols: foundTask.start - leaderTask.start 
                        });
                        break; // Task gefunden, weiter zum nächsten
                    }
                }
            });

            // Das Drag-Daten-Objekt
            activeDragData = { 
                type: 'task', 
                leaderTaskId: taskId,        // Die ID des Elements unter der Maus
                leaderProjectId: projectId,  // Das Projekt des Elements unter der Maus
                leaderOriginalStart: leaderTask.start, // Startposition vor dem Drag
                leaderSpan: leaderTask.span,
                group: movingTasks           // Die gesamte Gruppe inkl. Leader
            };

            e.dataTransfer.setData('text/plain', JSON.stringify({ taskId, projectId }));
            e.dataTransfer.effectAllowed = 'move';

            // Visuelles Feedback: Wir setzen die Klasse 'is-dragging-segment' auf alle
            // bewegten Elemente, damit sie halbtransparent werden.
            setTimeout(() => {
                selectedTaskIds.forEach(id => {
                    document.querySelectorAll(`[data-task-id='${id}']`).forEach(el => el.classList.add('is-dragging-segment'));
                });
                document.body.classList.add('is-dragging');
            }, 0);

            return;
        }

        // Fall 2: Eine ganze Projekt-Zeile wird sortiert (Sortier-Handle links)
        const projectCell = dragTarget.closest('.project-name-cell[data-drag-type="project-row"]'); 
        if (projectCell) {
            e.stopPropagation();
            const projectId = projectCell.dataset.projectId;
            activeDragData = { type: 'project-row', projectId };
            e.dataTransfer.setData('text/plain', JSON.stringify({ projectId }));
            e.dataTransfer.effectAllowed = 'move';
            setTimeout(() => {
                document.body.classList.add('is-dragging');
            }, 0);
        }
    }
    function handleConnectorClick(e) { const connectorContainer = e.target.closest('.connector-line-container'); if (!connectorContainer) return; e.preventDefault(); e.stopPropagation(); const cell = connectorContainer.closest('td'); const row = cell.closest('tr'); const projectId = row.dataset.projectRowId; const clickedColumnIndex = parseInt(cell.dataset.columnIndex, 10); if (!projectId || isNaN(clickedColumnIndex) || !masterProjectDetails[projectId].active) return; const taskName = prompt("Name für neuen Block (z.B. TEST):"); if (!taskName || taskName.trim() === '') return; const projectTasks = [...masterTaskData[projectId]].sort((a, b) => a.start - b.start); const taskBefore = projectTasks.filter(t => (t.start + t.span) <= clickedColumnIndex).pop(); const taskAfter = projectTasks.find(t => t.start > clickedColumnIndex); if (!taskBefore || !taskAfter) { return; } const newStartColumn = taskBefore.start + taskBefore.span; const gapSize = taskAfter.start - newStartColumn; if (gapSize <= 0) { alert("Kein Platz zum Einfügen."); return; } const startDt = new Date(masterTimelineStartDate.getTime() + newStartColumn * 43200000); const endDt = new Date(startDt.getTime() + 43200000); const newId = `${projectId}_task_${Date.now()}`; const newTask = { id: newId, name: taskName.trim(), start: newStartColumn, span: 1, assigned_employees: [[]], is_manual: [false], test_cell_id: [null], originalEvent: { id: newId, title: taskName.trim(), start: startDt.toISOString().split('T')[0], start_shift: startDt.getUTCHours() < 12 ? 'AM' : 'PM', end: endDt.toISOString().split('T')[0], end_shift: endDt.getUTCHours() < 12 ? 'AM' : 'PM', start_datetime: startDt.toISOString(), end_datetime: endDt.toISOString(), color: EVENT_COLOR_MAP[taskName.trim().toUpperCase()] || EVENT_COLOR_MAP['DEFAULT'] } }; masterTaskData[projectId].push(newTask); markChanges(); recalculateAndRerenderAssignments(); }    

    function handleAddNewBlock(button) {
        const projectId = button.dataset.projectId;
        if (!masterProjectDetails[projectId] || !masterProjectDetails[projectId].active) {
            alert("Blöcke können nur zu aktiven Projekten hinzugefügt werden.");
            return;
        }

        const taskName = prompt("Name für neuen Block (z.B. TEST):");
        if (!taskName || taskName.trim() === '') return;

        const now = new Date();
        const nowUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getMinutes()));
        const diffMs = nowUTC.getTime() - masterTimelineStartDate.getTime();

        let newStartColumn = 0; // Fallback, falls aktueller Tag vor Timeline-Start liegt
        if (diffMs >= 0) {
            newStartColumn = Math.floor(diffMs / 43200000); // 43200000ms = 12 Stunden
        }

        const projectTasks = masterTaskData[projectId] || [];

        // NEU: Prüfe auf Kollisionen und finde den nächsten freien Platz ab dem heutigen Tag
        while (projectTasks.some(task => newStartColumn >= task.start && newStartColumn < task.start + task.span)) {
            newStartColumn++; // Gehe zur nächsten Spalte, bis eine freie gefunden wird
        }

        if (newStartColumn >= masterTotalColumns) {
            alert("Kein Platz mehr in der Timeline ab dem heutigen Datum für einen neuen Block.");
            return;
        }

        const startDt = new Date(masterTimelineStartDate.getTime() + newStartColumn * 43200000);
        const endDt = new Date(startDt.getTime() + 43200000); // Standarddauer: 1 Slot (12h)
        const newId = `${projectId}_task_${Date.now()}`;

        const newTask = {
            id: newId,
            name: taskName.trim(),
            start: newStartColumn,
            span: 1,
            assigned_employees: [[]],
            is_manual: [false],
            test_cell_id: [null],
            originalEvent: {
                id: newId,
                title: taskName.trim(),
                start: startDt.toISOString().split('T')[0],
                start_shift: startDt.getUTCHours() < 12 ? 'AM' : 'PM',
                end: endDt.toISOString().split('T')[0],
                end_shift: endDt.getUTCHours() < 12 ? 'AM' : 'PM',
                start_datetime: startDt.toISOString(),
                end_datetime: endDt.toISOString(),
                color: EVENT_COLOR_MAP[taskName.trim().toUpperCase()] || EVENT_COLOR_MAP['DEFAULT']
            }
        };

        masterTaskData[projectId].push(newTask);
        markChanges();
        recalculateAndRerenderAssignments();
    }

function handleMouseDown(e) {
        // 1. Resize hat Vorrang (Größe ändern)
        const handle = e.target.closest('.resize-handle');
        if (handle) {
            const projectId = handle.closest('.task-bar').dataset.projectId;
            if (!masterProjectDetails[projectId] || !masterProjectDetails[projectId].active) return;
            e.preventDefault();
            e.stopPropagation();
            isResizing = true;
            const taskBar = handle.closest('.task-bar');
            const taskId = taskBar.dataset.taskId;
            const task = findTask(projectId, taskId);
            resizingTask = { taskId, projectId, handle, startX: e.clientX, startSpan: task.span, lastDeltaCols: 0 };
            document.body.classList.add('is-resizing');
            document.addEventListener('mousemove', handleMouseMove);
            document.addEventListener('mouseup', handleMouseUp);
            return;
        }

        // 2. Task Click / Drag Vorbereitung
        if (e.target.closest('.task-bar')) {
            return; 
        }

        // ### FIX: Keine Auswahlbox starten, wenn man in die linke Projektspalte klickt ###
        // Die linke Spalte hat die Klasse 'sticky-col'. Wenn der Klick dort passiert, brechen wir ab.
        if (e.target.closest('.sticky-col')) {
            return;
        }
        // ################################################################################

        // 3. Start Selection Box (Nur wenn auf leere Zelle im Zeitbereich geklickt wird)
        if (e.target.closest('#unifiedTableContainer')) {
            isSelecting = true;
            selectionStart = { x: e.clientX, y: e.clientY };

            // Wenn keine Modifier-Taste (Strg/Shift) gedrückt, Auswahl zurücksetzen
            if (!e.ctrlKey && !e.shiftKey) {
                clearSelection();
            }

            selectionMarquee.style.left = e.clientX + 'px';
            selectionMarquee.style.top = e.clientY + 'px';
            selectionMarquee.style.width = '0px';
            selectionMarquee.style.height = '0px';
            selectionMarquee.style.display = 'block';

            document.body.style.userSelect = 'none'; // Textauswahl verhindern
            document.addEventListener('mousemove', handleSelectionMove);
            document.addEventListener('mouseup', handleSelectionUp);
        }
    }
    // Hilfsfunktion: Auswahl leeren
    function clearSelection() {
        selectedTaskIds.clear();
        document.querySelectorAll('.task-bar.selected').forEach(el => el.classList.remove('selected'));
    }

    // Hilfsfunktion: Task zur Auswahl hinzufügen
    function toggleTaskSelection(taskId, forceSelect = null) {
        const els = document.querySelectorAll(`.task-bar[data-task-id="${taskId}"]`);
        if (forceSelect === true || (forceSelect === null && !selectedTaskIds.has(taskId))) {
            selectedTaskIds.add(taskId);
            els.forEach(el => el.classList.add('selected'));
        } else {
            selectedTaskIds.delete(taskId);
            els.forEach(el => el.classList.remove('selected'));
        }
    }

    function handleSelectionMove(e) {
        if (!isSelecting) return;

        const currentX = e.clientX;
        const currentY = e.clientY;

        const width = Math.abs(currentX - selectionStart.x);
        const height = Math.abs(currentY - selectionStart.y);
        const left = Math.min(currentX, selectionStart.x);
        const top = Math.min(currentY, selectionStart.y);

        selectionMarquee.style.width = width + 'px';
        selectionMarquee.style.height = height + 'px';
        selectionMarquee.style.left = left + 'px';
        selectionMarquee.style.top = top + 'px';

        // Kollisionsprüfung live während des Ziehens
        const marqueeRect = selectionMarquee.getBoundingClientRect();

        // Wir iterieren über alle sichtbaren Task-Bars
        document.querySelectorAll('.task-bar').forEach(taskBar => {
            const taskRect = taskBar.getBoundingClientRect();
            const taskId = taskBar.dataset.taskId;

            // Einfache AABB Kollisionsprüfung (Axis-Aligned Bounding Box)
            const intersects = !(
                marqueeRect.right < taskRect.left || 
                marqueeRect.left > taskRect.right || 
                marqueeRect.bottom < taskRect.top || 
                marqueeRect.top > taskRect.bottom
            );

            if (intersects) {
                 // Wenn berührt, temporär markieren (wir nutzen forceSelect=true)
                 if (!selectedTaskIds.has(taskId)) {
                     toggleTaskSelection(taskId, true);
                 }
            }
        });
    }

    function handleSelectionUp(e) {
        isSelecting = false;
        selectionMarquee.style.display = 'none';
        document.body.style.userSelect = '';
        document.removeEventListener('mousemove', handleSelectionMove);
        document.removeEventListener('mouseup', handleSelectionUp);
    }
    function handleMouseMove(e) { if (!isResizing || !resizingTask) return; const task = findTask(resizingTask.projectId, resizingTask.taskId); if (!task) return; const cellWidth = 45; const deltaX = e.clientX - resizingTask.startX; const currentDeltaCols = Math.round(deltaX / cellWidth); if (currentDeltaCols !== resizingTask.lastDeltaCols) { if (resizingTask.handle.classList.contains('resize-handle-right')) { let newSpan = resizingTask.startSpan + currentDeltaCols; if (newSpan < 1) newSpan = 1; const endPos = task.start + newSpan; if (endPos > masterTotalColumns) newSpan = masterTotalColumns - task.start; const nextTask = masterTaskData[resizingTask.projectId].filter(t => t.id !== task.id && t.start > task.start).sort((a,b) => a.start - b.start)[0]; if (nextTask && endPos > nextTask.start) newSpan = nextTask.start - task.start; task.span = newSpan; markChanges(); recalculateAndRerenderAssignments(); resizingTask.lastDeltaCols = currentDeltaCols; } } }
    function handleMouseUp() { if (isResizing) { isResizing = false; resizingTask = null; document.body.classList.remove('is-resizing'); document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); } }
    function markChanges() { saveBtn.dataset.hasChanges = "true"; saveBtn.classList.replace('bg-blue-600', 'bg-orange-500'); saveBtn.classList.replace('hover:bg-blue-700', 'hover:bg-orange-600'); }
    
    let masterBlockedData = {}; 
    async function initializeTimeline() {
        document.getElementById('unifiedBody').innerHTML = '<tr><td class="p-4 text-center" colspan="29">Lade Daten...</td></tr>';
        try {

            const response = await fetch(`/api/v2/timeline_data/${timelineId}`);
            if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
            const apiData = await response.json();

            masterFooterData = apiData.calculated_data.footer_availability;
            masterQualStatuses = apiData.qualification_statuses_for_tasks || {};
            masterBlockedData = apiData.calculated_data.blocked_employees || {};
            
            manualAssignments.clear(); 
            if (apiData.manual_assignments) {
                apiData.manual_assignments.forEach(ma => {
                    const key = `${ma.project_code}_${ma.engine_serial}_${ma.event_title}_${ma.date_str}_${ma.shift}_${ma.employee_id.toLowerCase()}`;
                    manualAssignments.set(key, ma);
                });
            }
            allEmployees = apiData.employees;
            // NEU: Gruppen extrahieren
            allAvailableGroups = [...new Set(allEmployees.map(e => e.quali).filter(Boolean))].sort();
            convertApiDataToMasterFormat(apiData);

            if (allLocations.length > 0) {
                // Das Backend prüft pro Slot, ob eine Zelle blockiert ist.
                // Daher ist es sicher und korrekt, hier initial alle Zellen zu übergeben.
                currentTestCellFilter = allLocations.map(loc => loc.ID);
            }


            filterAndRender();
            if (apiData.timeline_config && apiData.timeline_config.assignment_groups) {
                 currentEmployeeGroupFilter = apiData.timeline_config.assignment_groups;
            } else {
                 // Fallback, falls die API noch nicht angepasst ist
                 currentEmployeeGroupFilter = ['FF'];
                 console.warn("Keine 'assignment_groups' in der API-Antwort gefunden. Fallback auf ['FF'].");
            }
        } catch (error) { 
            console.error("Fehler beim Initialisieren der Timeline:", error); 
            document.getElementById('unifiedBody').innerHTML = '<tr><td class="p-4 text-red-500 text-center" colspan="29">Fehler beim Laden.</td></tr>';
        }
    }

    async function saveChanges(forceSave = false) { if (!forceSave && saveBtn.dataset.hasChanges !== "true") { return true; } const payload = { projects: convertMasterDataToApiFormat() }; try { const response = await fetch(`/api/save_editable_timeline_data/${timelineId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const result = await response.json(); if (response.ok) { if (!forceSave) alert(result.message || "Gespeichert!"); saveBtn.dataset.hasChanges = "false"; saveBtn.classList.replace('bg-orange-500', 'bg-blue-600'); saveBtn.classList.replace('hover:bg-orange-600', 'hover:bg-blue-700'); return true; } else { alert('Fehler: ' + (result.error || 'Unbekannt.')); return false; } } catch (error) { console.error("Netzwerkfehler beim Speichern:", error); alert("Netzwerkfehler."); return false; } }
    async function handleSapUpdate() { if (saveBtn.dataset.hasChanges === "true") { if (confirm("Ungespeicherte Änderungen. Vor dem SAP-Update speichern?")) { await saveChanges(); if (saveBtn.dataset.hasChanges === "true") { alert("Speichern fehlgeschlagen. Update abgebrochen."); return; } } else { alert("Update abgebrochen."); return; } } sapUpdateBtn.disabled = true; sapUpdateBtn.innerHTML = `<span class="material-icons-outlined text-base -ml-0.5 animate-spin">sync</span>Aktualisiere...`; try { const response = await fetch(`/api/update_from_sap/${timelineId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' } }); const result = await response.json(); if (response.ok) { alert(result.message || "SAP-Update abgeschlossen!"); initializeTimeline(); } else { alert('Fehler: ' + (result.error || 'Serverfehler.')); } } catch (error) { console.error("Netzwerkfehler:", error); alert("Netzwerkfehler."); } finally { sapUpdateBtn.disabled = false; sapUpdateBtn.innerHTML = `<span class="material-icons-outlined text-base -ml-0.5">sync</span>SAP-Update`; } }
    function updateNavButtons() { prevWeekBtn.disabled = currentWeekOffset <= 0; nextWeekBtn.disabled = currentWeekOffset >= (TOTAL_WEEKS_LOADED - VISIBLE_WEEKS); }
    function updateScrollShadow() { const container = document.getElementById('unifiedTableContainer'); if (!container) return; const { scrollTop, scrollHeight, clientHeight } = container; const canScrollDown = scrollHeight > clientHeight && scrollTop + clientHeight < scrollHeight - 1; if (canScrollDown) container.classList.add('is-scrollable-down'); else container.classList.remove('is-scrollable-down'); }
    function highlightCurrentTimeslotAndScroll() { document.querySelectorAll('.current-timeslot-highlight').forEach(el => el.classList.remove('current-timeslot-highlight')); if (!masterTimelineStartDate) return; const now = new Date(); const nowUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getMinutes())); const diffMs = nowUTC.getTime() - masterTimelineStartDate.getTime(); if (diffMs < 0) return; const currentGlobalCol = Math.floor(diffMs / 43200000); const visibleStartCol = currentWeekOffset * 14; const visibleEndCol = visibleStartCol + (VISIBLE_WEEKS * 14); if (currentGlobalCol < visibleStartCol || currentGlobalCol >= visibleEndCol) return; document.querySelectorAll(`#unifiedBody tr, #unifiedFooter tr`).forEach(row => { const cellToHighlight = row.querySelector(`td[data-column-index='${currentGlobalCol}']`); if (cellToHighlight) { cellToHighlight.classList.add('current-timeslot-highlight'); } }); const cellWidth = 45; const container = document.getElementById('unifiedTableContainer'); const scrollTarget = currentGlobalCol * cellWidth - (container.offsetWidth / 3); container.scrollLeft = Math.max(0, scrollTarget); }
    function openAssignmentModal(data) { 
        currentModalData = data; 
        modalTitle.textContent = `Mitarbeiter für "${data.event.name}" zuweisen`; 
        modalSubtitle.textContent = `Projekt: ${data.project.name} | Slot: ${data.slotDate.toLocaleDateString('de-DE')} ${data.slotShift}`; 
        deleteAssignmentsBtn.style.display = data.isManual ? 'inline-flex' : 'none'; 
        availableEmployeesSelect.innerHTML = '<option value="">-- Mitarbeiter auswählen --</option>'; 
        
        const currentAssignedIds = new Set(data.assignedEmployeeIds.map(id => id.toLowerCase())); 
        
        // Slot Key bauen
        const slotKey = `${data.slotDate.toISOString().split('T')[0]}_${data.slotShift}`;
        
        // Globale Blockaden holen
        const globalBlockedIds = new Set(masterBlockedData[slotKey] || []);

        const manuallyBlockedElsewhereIds = new Set(); 
        manualAssignments.forEach(ma => { 
            const isSameSlot = ma.date_str === data.slotDate.toISOString().split('T')[0] && ma.shift === data.slotShift; 
            const isThisTask = ma.project_code === data.project.project_code && ma.engine_serial === data.project.engine_serial && ma.event_title === data.event.name; 
            if (isSameSlot && !isThisTask) { 
                const blockedEmp = allEmployees.find(e => e.id.toLowerCase() === ma.employee_id.toLowerCase()); 
                if(blockedEmp) manuallyBlockedElsewhereIds.add(blockedEmp.id.toLowerCase()); 
            } 
        }); 
        
        const statusMap = { 'X': { icon: '🟢', title: 'Experte' }, 'TS': { icon: '🟡', title: 'Training gestartet' }, 'TT': { icon: '🔴', title: 'Training überfällig' }, 'TP': { icon: '⚪️', title: 'Training geplant' }, 'DEFAULT': { icon: '➖', title: 'Nicht qualifiziert' } }; 
        const groupsToFilter = Array.isArray(currentEmployeeGroupFilter) ? currentEmployeeGroupFilter : [currentEmployeeGroupFilter]; 
        
        allEmployees.forEach(emp => { 
            const empId = emp.id.toLowerCase(); 
            
            // CHECK: Ist er global blockiert?
            const isGlobalBlocked = globalBlockedIds.has(empId);

            if (!currentAssignedIds.has(empId) && !manuallyBlockedElsewhereIds.has(empId) && !isGlobalBlocked && groupsToFilter.includes(emp.quali)) { 
                const empAvailability = employeeAvailabilityMap.get(empId) || {}; 
                const dayInfo = empAvailability[data.slotDate.toISOString().split('T')[0]]; 
                if (dayInfo && dayInfo.available && dayInfo.shift === data.slotShift) { 
                    const option = document.createElement('option'); 
                    option.value = empId; 
                    const status = masterQualStatuses[data.event.id]?.[empId] || null; 
                    const indicator = statusMap[status] || statusMap['DEFAULT']; 
                    option.textContent = `${indicator.icon} ${emp.name} (${emp.initials})`; 
                    option.title = indicator.title; 
                    availableEmployeesSelect.appendChild(option); 
                } 
            } 
        }); 
        
        renderAssignedEmployeesInModal(data.assignedEmployeeIds, data.isManual); 
        assignmentModal.style.display = 'block'; 
        assignmentModalBackdrop.style.display = 'block'; 
    }    
    function renderAssignedEmployeesInModal(assignedIds, isManualAssignment) { assignedEmployeesContainer.innerHTML = ''; if (assignedIds.length === 0) { assignedEmployeesContainer.innerHTML = '<p class="text-xs text-gray-400 italic">Keine Mitarbeiter zugewiesen.</p>'; return; } assignedIds.forEach(empId => { const employee = allEmployees.find(e => e.id.toLowerCase() === empId.toLowerCase()); if (employee) { const tag = document.createElement('span'); tag.className = 'assigned-employee-tag'; const manualIcon = isManualAssignment ? `<span class="material-icons-outlined text-sm text-blue-500" title="Manuell">push_pin</span>` : `<span class="material-icons-outlined text-sm text-gray-400" title="Automatisch">smart_toy</span>`; tag.innerHTML = `${manualIcon} <span class="mx-1">${employee.initials}</span> <button data-id="${empId.toLowerCase()}" class="remove-employee-btn"><span class="material-icons-outlined text-sm">close</span></button>`; assignedEmployeesContainer.appendChild(tag); } }); }
    addEmployeeBtn.addEventListener('click', () => { const selectedId = availableEmployeesSelect.value; if (selectedId && currentModalData && !currentModalData.assignedEmployeeIds.includes(selectedId)) { currentModalData.assignedEmployeeIds.push(selectedId); currentModalData.isManual = true; renderAssignedEmployeesInModal(currentModalData.assignedEmployeeIds, currentModalData.isManual); Array.from(availableEmployeesSelect.options).forEach(opt => { if (opt.value === selectedId) opt.remove(); }); } });
    assignedEmployeesContainer.addEventListener('click', e => { const removeBtn = e.target.closest('.remove-employee-btn'); if (removeBtn && currentModalData) { const idToRemove = removeBtn.dataset.id; currentModalData.assignedEmployeeIds = currentModalData.assignedEmployeeIds.filter(id => id !== idToRemove); currentModalData.isManual = true; renderAssignedEmployeesInModal(currentModalData.assignedEmployeeIds, currentModalData.isManual); const emp = allEmployees.find(e => e.id.toLowerCase() === idToRemove); if (emp) { if (!availableEmployeesSelect.querySelector(`option[value="${emp.id.toLowerCase()}"]`)) { const option = document.createElement('option'); option.value = emp.id.toLowerCase(); option.textContent = `${emp.name} (${emp.initials})`; availableEmployeesSelect.appendChild(option); } } } });
    function closeAssignmentModal() { assignmentModal.style.display = 'none'; assignmentModalBackdrop.style.display = 'none'; currentModalData = null; }
    function handleDeleteEvent() { if (!currentModalData || !currentModalData.project || !currentModalData.event) return; const { project, event } = currentModalData; if (confirm(`Event "${event.name}" löschen?`)) { const projectId = `${project.project_code}_${project.engine_serial}`; const tasks = masterTaskData[projectId]; if (tasks) { const taskIndex = tasks.findIndex(t => t.id === event.id); if (taskIndex > -1) { tasks.splice(taskIndex, 1); markChanges(); closeAssignmentModal(); recalculateAndRerenderAssignments(); } } } }
    async function saveManualAssignments(assignmentsToSaveIds, slotInfoOverride = null) { const timelineSaved = await saveChanges(true); if (!timelineSaved) { alert("Timeline konnte nicht gespeichert werden. Zuweisung abgebrochen."); return; } const slotInfo = slotInfoOverride || (currentModalData ? { project_code: currentModalData.project.project_code, engine_serial: currentModalData.project.engine_serial, event_title: currentModalData.event.name, date_str: currentModalData.slotDate.toISOString().split('T')[0], shift: currentModalData.slotShift, engine_type: currentModalData.project.engine_type } : null); if (!slotInfo) return; const payload = { slot_info: slotInfo, assignments: assignmentsToSaveIds.map(id => ({ ...slotInfo, employee_id: id })), test_cell_id: assignmentsToSaveIds.length > 0 ? (currentModalData ? currentModalData.testCellId : null) : null }; try { const response = await fetch(`/api/manual_assignment/${timelineId}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const result = await response.json(); if (response.ok) { closeAssignmentModal(); closeBlockerModal(); await recalculateAndRerenderAssignments(); } else { alert('Fehler: ' + (result.error || 'Zuweisung konnte nicht gespeichert werden.')); } } catch (error) { console.error("Fehler bei manueller Zuweisung:", error); alert("Netzwerkfehler."); } }
    function updateCommentCountBadge(projectId, newCount) { const commentBtn = document.querySelector(`.open-comments-btn[data-project-id="${projectId}"]`); if (!commentBtn) return; let badge = commentBtn.querySelector('.comment-count-badge'); if (newCount > 0) { if (!badge) { badge = document.createElement('span'); badge.className = 'comment-count-badge'; commentBtn.appendChild(badge); } badge.textContent = newCount; } else { if (badge) { badge.remove(); } } }
    async function openCommentsModal(projectId, projectName) { activeProjectForComments = projectId; commentsModalTitle.textContent = `Kommentare für: ${projectName}`; commentsListContainer.innerHTML = '<p>Lade Kommentare...</p>'; commentsOverlay.classList.remove('hidden'); try { const response = await fetch(`/api/comments/${projectId}`); if (!response.ok) throw new Error('Kommentare konnten nicht geladen werden.'); const comments = await response.json(); renderComments(comments); } catch (error) { commentsListContainer.innerHTML = `<p class="text-red-500">${error.message}</p>`; } }
    function closeCommentsModal() { commentsOverlay.classList.add('hidden'); activeProjectForComments = null; newCommentTextarea.value = ''; }
    function renderComments(comments) { commentsListContainer.innerHTML = ''; if (comments.length === 0) { commentsListContainer.innerHTML = '<p>Keine Kommentare.</p>'; return; } comments.forEach(comment => { const commentDiv = document.createElement('div'); commentDiv.className = 'comment-container relative flex items-start space-x-3'; commentDiv.dataset.commentId = comment.comment_id; const timestamp = new Date(comment.timestamp).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short'}); let actionsHtml = ''; if (comment.author === currentUsername) { actionsHtml = `<div class="comment-actions"><button class="text-gray-400 hover:text-gray-600 p-1" onclick="toggleCommentMenu(event, '${comment.comment_id}')"><span class="material-icons-outlined text-base">more_vert</span></button><div id="menu-${comment.comment_id}" class="comment-actions-menu hidden"><button onclick="editComment('${comment.comment_id}')">Bearbeiten</button><button onclick="deleteComment('${comment.comment_id}')" class="text-red-600">Löschen</button></div></div>`; } commentDiv.innerHTML = `<div class="flex-shrink-0 h-10 w-10 rounded-full bg-slate-200 flex items-center justify-center font-bold text-slate-600 text-sm">${comment.author.substring(0,2).toUpperCase()}</div><div class="flex-grow"><div class="flex items-center space-x-2"><span class="font-semibold text-gray-700">${comment.author}</span><span class="text-xs text-gray-500">${timestamp}</span></div><div class="comment-text-container mt-1"><p class="text-gray-600 bg-gray-50 p-3 rounded-md border border-gray-200 whitespace-pre-wrap">${comment.comment}</p></div></div>${actionsHtml}`; commentsListContainer.appendChild(commentDiv); }); }
    async function postNewComment() { const commentText = newCommentTextarea.value.trim(); if (!commentText || !activeProjectForComments) return; postCommentBtn.disabled = true; try { const response = await fetch(`/api/comments/${activeProjectForComments}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ comment: commentText }) }); if (!response.ok) throw new Error('Kommentar konnte nicht gesendet werden.'); newCommentTextarea.value = ''; const comments = await (await fetch(`/api/comments/${activeProjectForComments}`)).json(); renderComments(comments); updateCommentCountBadge(activeProjectForComments, comments.length); } catch (error) { alert(error.message); } finally { postCommentBtn.disabled = false; } }
    window.toggleCommentMenu = (event, commentId) => { event.stopPropagation(); document.querySelectorAll('.comment-actions-menu').forEach(menu => { if (menu.id !== `menu-${commentId}`) { menu.classList.add('hidden'); } }); const menu = document.getElementById(`menu-${commentId}`); if (menu) menu.classList.toggle('hidden'); }
    window.editComment = (commentId) => { const commentDiv = document.querySelector(`[data-comment-id="${commentId}"]`); const textContainer = commentDiv.querySelector('.comment-text-container'); const currentText = textContainer.querySelector('p').textContent; textContainer.innerHTML = `<textarea class="w-full p-2 border border-blue-400 rounded-md resize-none text-gray-600" rows="3">${currentText}</textarea><div class="mt-2 flex justify-end space-x-2"><button onclick="cancelEdit('${commentId}', \`${currentText}\`)" class="bg-gray-200 hover:bg-gray-300 text-sm font-medium px-3 py-1 rounded-md">Abbrechen</button><button onclick="saveEdit('${commentId}')" class="bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium px-3 py-1 rounded-md">Speichern</button></div>`; }
    window.cancelEdit = (commentId, originalText) => { const commentDiv = document.querySelector(`[data-comment-id="${commentId}"]`); const textContainer = commentDiv.querySelector('.comment-text-container'); textContainer.innerHTML = `<p class="text-gray-600 bg-gray-50 p-3 rounded-md border border-gray-200 whitespace-pre-wrap">${originalText}</p>`; }
    window.saveEdit = async (commentId) => { const commentDiv = document.querySelector(`[data-comment-id="${commentId}"]`); const textarea = commentDiv.querySelector('textarea'); const newText = textarea.value.trim(); if (!newText) { alert("Kommentar darf nicht leer sein."); return; } try { const response = await fetch(`/api/comments/entry/${commentId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ comment: newText }) }); const result = await response.json(); if (!response.ok) throw new Error(result.error || 'Fehler beim Speichern.'); const textContainer = commentDiv.querySelector('.comment-text-container'); textContainer.innerHTML = `<p class="text-gray-600 bg-gray-50 p-3 rounded-md border border-gray-200 whitespace-pre-wrap">${result.comment}</p>`; const timestampEl = commentDiv.querySelector('.text-xs.text-gray-500'); if (timestampEl) { timestampEl.textContent = new Date(result.timestamp).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short'}) + ' (bearbeitet)'; } } catch (error) { alert(`Fehler: ${error.message}`); } }
    window.deleteComment = async (commentId) => { if (!confirm("Kommentar löschen?")) return; try { const response = await fetch(`/api/comments/entry/${commentId}`, { method: 'DELETE' }); if (!response.ok) { const result = await response.json(); throw new Error(result.error || 'Fehler beim Löschen.'); } document.querySelector(`[data-comment-id="${commentId}"]`).remove(); const comments = await (await fetch(`/api/comments/${activeProjectForComments}`)).json(); renderComments(comments); updateCommentCountBadge(activeProjectForComments, comments.length); } catch(error) { alert(`Fehler: ${error.message}`); } }
    let mouseDownPos = {x: 0, y: 0};
    unifiedBody.addEventListener('mousedown', e => { handleMouseDown(e); isDraggingFlag = false; mouseDownPos = {x: e.clientX, y: e.clientY}; const onMouseMove = (moveEvent) => { const dx = Math.abs(moveEvent.clientX - mouseDownPos.x); const dy = Math.abs(moveEvent.clientY - mouseDownPos.y); if (dx > 5 || dy > 5) { isDraggingFlag = true; document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); } }; const onMouseUp = (upEvent) => { if (!isDraggingFlag) { handleTaskClick(upEvent); } document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); }; document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); });
    unifiedBody.addEventListener('dragstart', handleDragStart); unifiedBody.addEventListener('dragover', handleDragOver); unifiedBody.addEventListener('drop', handleDrop); unifiedBody.addEventListener('dragend', handleDragEnd); unifiedBody.addEventListener('click', e => { handleConnectorClick(e); handleListClick(e); });
    document.addEventListener('click', (e) => { if (!e.target.closest('.comment-actions')) { document.querySelectorAll('.comment-actions-menu').forEach(menu => menu.classList.add('hidden')); } });
    sortBtn.addEventListener('click', handleSortProjects);
    prevWeekBtn.addEventListener('click', () => { currentWeekOffset--; filterAndRender(); });
    currentWeekBtn.addEventListener('click', () => { currentWeekOffset = WEEKS_IN_PAST; filterAndRender(); });
    nextWeekBtn.addEventListener('click', () => { currentWeekOffset++; filterAndRender(); });
    saveBtn.addEventListener('click', () => saveChanges(false));
    sapUpdateBtn.addEventListener('click', handleSapUpdate);
    searchInput.addEventListener('input', filterAndRender);
    document.getElementById('unifiedTableContainer').addEventListener('scroll', updateScrollShadow);
    window.addEventListener('resize', updateScrollShadow);
    saveAssignmentsBtn.addEventListener('click', () => saveManualAssignments(currentModalData.assignedEmployeeIds));
    deleteAssignmentsBtn.addEventListener('click', () => saveManualAssignments([]));
    cancelAssignmentsBtn.addEventListener('click', closeAssignmentModal);
    closeAssignmentModalBtn.addEventListener('click', closeAssignmentModal);
    assignmentModalBackdrop.addEventListener('click', closeAssignmentModal);
    deleteEventBtn.addEventListener('click', handleDeleteEvent);
    closeCommentsButton.addEventListener('click', closeCommentsModal);
    commentsOverlay.addEventListener('click', (e) => { if (e.target === commentsOverlay) closeCommentsModal(); });
    postCommentBtn.addEventListener('click', postNewComment);

function handleTaskClick(e) {
        const taskContainer = e.target.closest('.task-segment-container'); 
        if (!taskContainer || e.target.closest('.resize-handle')) return;

        const taskBar = taskContainer.closest('.task-bar'); 
        const projectId = taskBar.dataset.projectId; 
        const taskId = taskBar.dataset.taskId;

        // ### MULTI-SELECT LOGIK ###
        // Wenn STRG oder CMD (Meta) gedrückt ist, nur die Auswahl umschalten und KEIN Modal öffnen
        if (e.ctrlKey || e.metaKey) {
            toggleTaskSelection(taskId);
            return; 
        }
        // ##########################

        const project = masterProjectDetails[projectId]; 
        const task = findTask(projectId, taskId);

        if (!project || !task || !project.active) return;

        // Wenn es ein einfacher Klick ist (kein Drag, kein Ctrl), und der Task noch nicht ausgewählt war,
        // setzen wir die Auswahl zurück und wählen nur diesen einen Task aus.
        if (!selectedTaskIds.has(taskId)) {
             clearSelection();
             toggleTaskSelection(taskId, true);
        }

        // --- Ab hier Standard-Logik zum Öffnen des Modals ---
        const cell = taskBar.closest('td'); 
        const colIndex = parseInt(cell.dataset.columnIndex, 10);

        const slotDateTime = new Date(masterTimelineStartDate.getTime() + colIndex * 43200000); 
        const slotDate = new Date(Date.UTC(slotDateTime.getUTCFullYear(), slotDateTime.getUTCMonth(), slotDateTime.getUTCDate())); 
        const slotShift = slotDateTime.getUTCHours() < 12 ? 'AM' : 'PM';

        const segmentIndex = colIndex - task.start; 
        const assignedEmployeeIds = (task.assigned_employees && task.assigned_employees[segmentIndex]) ? task.assigned_employees[segmentIndex] : []; 
        const isManual = (task.is_manual && task.is_manual[segmentIndex]) ? task.is_manual[segmentIndex] : false;
        const testCellIdForSlot = (task.test_cell_id && task.test_cell_id[segmentIndex]) ? task.test_cell_id[segmentIndex] : null;

        openAssignmentModal({ 
            project: project, 
            event: task, 
            slotDate: slotDate, 
            slotShift: slotShift, 
            assignedEmployeeIds: [...assignedEmployeeIds], 
            isManual: isManual, 
            testCellId: testCellIdForSlot 
        });
    }

// 1. Klick auf Footer-Zelle -> Modal öffnen & Checkboxen rendern
    unifiedFooter.addEventListener('click', e => {
        const cell = e.target.closest('td.footer-cell-clickable'); 
        if (!cell) return;

        const colIndex = parseInt(cell.dataset.columnIndex, 10); 
        const slotDateTime = new Date(masterTimelineStartDate.getTime() + colIndex * 43200000); 
        const slotDateIso = slotDateTime.toISOString().split('T')[0]; 
        const slotShift = slotDateTime.getUTCHours() < 12 ? 'AM' : 'PM';

        blockerModalTitle.textContent = `Blockaden am ${new Date(slotDateIso).toLocaleDateString('de-DE')} (${slotShift})`; 
        blockerModalContent.innerHTML = '';

        let foundBlockers = false;

        manualAssignments.forEach(ma => {
            if (ma.date_str === slotDateIso && ma.shift === slotShift) {
                foundBlockers = true; 
                const employee = allEmployees.find(emp => emp.id.toLowerCase() === ma.employee_id.toLowerCase()); 
                const employeeInitials = employee ? employee.initials : ma.employee_id;
                const employeeName = employee ? employee.name : 'Unbekannt';

                // Wir speichern alle nötigen Daten direkt im Dataset der Checkbox
                const div = document.createElement('div');
                div.className = 'flex items-center p-3 hover:bg-gray-50 rounded border border-gray-100 transition-colors';
                div.innerHTML = `
                    <label class="flex items-center w-full cursor-pointer gap-3">
                        <input type="checkbox" class="blocker-checkbox h-5 w-5 rounded border-gray-300 text-blue-600 focus:ring-blue-500"
                            data-project-code="${ma.project_code}"
                            data-engine-serial="${ma.engine_serial}"
                            data-event-title="${ma.event_title}"
                            data-date-str="${ma.date_str}"
                            data-shift="${ma.shift}"
                            data-employee-id="${ma.employee_id}">
                        <div class="flex-1">
                            <div class="flex items-center gap-2">
                                <span class="inline-flex items-center justify-center h-6 w-6 rounded-full bg-blue-100 text-blue-800 text-xs font-bold">${employeeInitials}</span>
                                <span class="font-medium text-gray-900 text-sm">${employeeName}</span>
                            </div>
                            <div class="text-xs text-gray-500 mt-0.5 pl-8">
                                Geblockt durch: <span class="font-semibold">${ma.event_title}</span> (${ma.project_code})
                            </div>
                        </div>
                    </label>
                `;
                blockerModalContent.appendChild(div);
            }
        });

        if (!foundBlockers) { 
            blockerModalContent.innerHTML = '<p class="text-gray-500 text-center py-4">Keine manuellen Blockaden gefunden.</p>'; 
        }

        // Reset "Select All"
        const selectAllCb = document.getElementById('selectAllBlockers');
        if(selectAllCb) {
            selectAllCb.checked = false;
            selectAllCb.disabled = !foundBlockers;
        }

        assignmentModalBackdrop.style.display = 'block'; 
        blockerModal.style.display = 'flex'; // Flex für Zentrierung
    });

    // 2. "Alle auswählen" Logik
    document.getElementById('selectAllBlockers').addEventListener('change', (e) => {
        document.querySelectorAll('.blocker-checkbox').forEach(cb => cb.checked = e.target.checked);
    });

    // 3. "Ausgewählte löschen" Logik (Gruppen-Verarbeitung)
    document.getElementById('deleteSelectedBlockersBtn').addEventListener('click', async () => {
        const checkedBoxes = document.querySelectorAll('.blocker-checkbox:checked');
        if (checkedBoxes.length === 0) {
            alert("Bitte wählen Sie mindestens einen Eintrag aus.");
            return;
        }

        if (!confirm(`${checkedBoxes.length} Zuweisung(en) wirklich löschen?`)) return;

        // Wir müssen die Löschungen gruppieren, da die API pro "Task-Slot" speichert.
        // Ein Task-Slot ist definiert durch: Project + Engine + Event + Date + Shift.
        // Wenn wir MA aus verschiedenen Events gleichzeitig löschen, müssen wir die API mehrfach aufrufen.

        const tasksToUpdate = {}; // Key: Unique Task String, Value: { slotInfo, idsToDelete: [] }

        checkedBoxes.forEach(cb => {
            const d = cb.dataset;
            // Erstelle einen eindeutigen Schlüssel für das Event
            const taskKey = `${d.projectCode}|${d.engineSerial}|${d.eventTitle}|${d.dateStr}|${d.shift}`;

            if (!tasksToUpdate[taskKey]) {
                tasksToUpdate[taskKey] = {
                    slotInfo: {
                        project_code: d.projectCode,
                        engine_serial: d.engineSerial,
                        event_title: d.eventTitle,
                        date_str: d.dateStr,
                        shift: d.shift
                    },
                    idsToDelete: []
                };
            }
            tasksToUpdate[taskKey].idsToDelete.push(d.employeeId.toLowerCase());
        });

        // Jetzt arbeiten wir die Gruppen nacheinander ab
        let successCount = 0;
        const entries = Object.values(tasksToUpdate);

        for (const taskData of entries) {
            const { slotInfo, idsToDelete } = taskData;

            // Finde alle MA, die aktuell diesem Slot zugewiesen sind (aus dem globalen Speicher)
            // Wir filtern die globalen 'manualAssignments', um alle zu finden, die zu diesem Event gehören
            const currentAssignmentsForEvent = Array.from(manualAssignments.values()).filter(ma => 
                ma.project_code === slotInfo.project_code &&
                ma.engine_serial === slotInfo.engine_serial &&
                ma.event_title === slotInfo.event_title &&
                ma.date_str === slotInfo.date_str &&
                ma.shift === slotInfo.shift
            );

            // Berechne, wer übrig bleibt (Alle aktuellen - Die, die wir löschen wollen)
            const remainingIds = currentAssignmentsForEvent
                .map(ma => ma.employee_id.toLowerCase())
                .filter(id => !idsToDelete.includes(id));

            // API Aufruf für dieses Event
            // Wir nutzen die existierende saveManualAssignments Funktion, die wir vorher definiert haben
            await saveManualAssignments(remainingIds, slotInfo);
            successCount++;
        }

        if (successCount === entries.length) {
            closeBlockerModal();
        }
    });

    function closeBlockerModal() { 
        assignmentModalBackdrop.style.display = 'none'; 
        blockerModal.style.display = 'none'; 
    }

    closeBlockerModalBtn.addEventListener('click', closeBlockerModal);

    settingsBtn.addEventListener('click', () => { assignmentModalBackdrop.style.display = 'block'; settingsModal.style.display = 'block'; });
    function closeSettingsModal() { assignmentModalBackdrop.style.display = 'none'; settingsModal.style.display = 'none'; }
    closeSettingsModalBtn.addEventListener('click', closeSettingsModal);

    initializeTimeline();
});
</script>
</body>
</html>
"""

TIME_MAPPING = {'AM': 0, 'PM': 12}  # 0 = Beginn des Tages, 12 = 12 Uhr mittags

COLOR_MAP = {'default_task_color': 'blue', 'swat_task_color': 'yellow'}

USER_MANAGEMENT_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <link href="https://fonts.googleapis.com/css2?family=Inter%3Awght%40400%3B500%3B600%3B700&family=Noto+Sans%3Awght%40400%3B500%3B700%3B900" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <title>User Management</title>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <style>
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label, #sidebar.is-collapsed .sidebar-title, #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link, #sidebar.is-collapsed .sidebar-header, #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined, #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }

        .role-pill {
            display: inline-flex;
            align-items: center;
            padding: 0.125rem 0.625rem;
            margin: 0.125rem;
            font-size: 0.75rem;
            font-weight: 500;
            border-radius: 9999px;
            border: 1px solid;
        }
        .role-ADMIN { background-color: #fee2e2; color: #991b1b; border-color: #fecaca; }
        .role-PLANNER { background-color: #e0e7ff; color: #312e81; border-color: #c7d2fe; }
        .role-DEFAULT { background-color: #f3f4f6; color: #374151; border-color: #e5e7eb; }

    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">
    {% include 'sidebar.html' %}
    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center sticky top-0 z-50 justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Benutzerverwaltung</h1>
        </header>

        <div class="p-4 sm:p-6 lg:p-8">
            <div class="max-w-7xl mx-auto bg-white shadow-lg rounded-lg p-6">
                <div class="mb-8 flex justify-between items-center">
                    <div>
                        <h2 class="text-2xl font-bold text-slate-800">User Management</h2>
                        <p class="text-slate-500 mt-1 text-sm">Benutzer anlegen, bearbeiten und Rollen zuweisen.</p>
                    </div>
                    <button class="px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700 flex items-center gap-2" id="addUserBtn">
                        <span class="material-icons-outlined text-xl">person_add</span> Neuen Benutzer hinzufügen
                    </button>
                </div>

                <!-- Formulare (werden bei Bedarf eingeblendet) -->
                <div id="formContainer" class="hidden mb-8"></div>
         
                <div class="bg-white rounded-lg overflow-x-auto border border-slate-200">
                    <table class="min-w-full divide-y divide-slate-200">
                        <thead class="bg-slate-50">
                            <tr>
                                <th class="px-6 py-4 text-left text-xs font-semibold text-slate-600 uppercase">Name</th>
                                <th class="px-6 py-4 text-left text-xs font-semibold text-slate-600 uppercase">ID</th>
                                <th class="px-6 py-4 text-left text-xs font-semibold text-slate-600 uppercase">Level</th>
                                <th class="px-6 py-4 text-left text-xs font-semibold text-slate-600 uppercase">Bereiche</th>
                                <th class="px-6 py-4 text-left text-xs font-semibold text-slate-600 uppercase">Rollen</th>
                                <th class="px-6 py-4 text-center text-xs font-semibold text-slate-600 uppercase">Aktionen</th>
                            </tr>
                        </thead>
                        <tbody class="bg-white divide-y divide-slate-200" id="userTableBody">
                            {% for user in users %}
                                <tr data-userid="{{ user.id }}"
                                    data-nachname="{{ user.nachname }}"
                                    data-vorname="{{ user.vorname }}"
                                    data-level="{{ user.level or '' }}"
                                    data-roles="{{ user.roles|join(',') }}"
                                    data-units="{{ user.units|join(', ') }}"> <!-- NEU: Units als String -->
                                    <td class="px-6 py-4 whitespace-nowrap"><div class="text-sm font-medium text-slate-900">{{ user.nachname }}, {{ user.vorname }}</div></td>
                                    <td class="px-6 py-4 whitespace-nowrap text-sm text-slate-600">{{ user.id.upper() }}</td>
                                    <td class="px-6 py-4 whitespace-nowrap text-sm text-slate-600">{{ user.level or 'N/A' }}</td>
                                    <td class="px-6 py-4">
                                        <div class="flex flex-wrap gap-1">
                                            {% for unit in user.units %}
                                                <span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-slate-100 text-slate-800 border border-slate-200">
                                                    {{ unit }}
                                                </span>
                                            {% else %}
                                                <span class="text-xs text-slate-400 italic">Alle</span>
                                            {% endfor %}
                                        </div>
                                    </td>
                                    <td class="px-6 py-4">
                                        <div class="flex flex-wrap">
                                            {% for role in user.roles %}
                                                <span class="role-pill role-{{ role.split('_')[0] if '_' in role else ('ADMIN' if role == 'ADMIN' else 'DEFAULT') }}">{{ role }}</span>
                                            {% endfor %}
                                        </div>
                                    </td>
                                    <td class="px-6 py-4 whitespace-nowrap text-center">
                                        <button class="text-blue-500 hover:text-blue-700 edit-user-btn mr-2" title="Benutzer bearbeiten"><span class="material-icons-outlined text-lg">edit</span></button>
                                        <button class="text-red-500 hover:text-red-700 delete-user-btn" title="Benutzer löschen"><span class="material-icons-outlined text-lg">delete_outline</span></button>
                                    </td>
                                </tr>
                            {% else %}
                                <tr><td colspan="5" class="text-center py-4 text-slate-500">Keine Benutzer gefunden.</td></tr>
                            {% endfor %}
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </main>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
    // --- Sidebar Logik (unverändert) ---
    const sidebar = document.getElementById('sidebar');
    const sidebarToggleBtn = document.getElementById('sidebar-toggle');
    if (sidebar && sidebarToggleBtn) {
        const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
        const applyState = (isCollapsed) => {
            if (isCollapsed) { sidebar.classList.add('is-collapsed'); toggleBtnIcon.textContent = 'menu'; } 
            else { sidebar.classList.remove('is-collapsed'); toggleBtnIcon.textContent = 'chevron_left'; }
        };
        sidebarToggleBtn.addEventListener('click', () => {
            const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
            localStorage.setItem('sidebarCollapsed', isNowCollapsed);
            applyState(isNowCollapsed);
        });
        const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
        applyState(savedState);
    }

    // --- NEUE Benutzerverwaltungslogik ---
    const addUserBtn = document.getElementById('addUserBtn');
    const formContainer = document.getElementById('formContainer');
    const userTableBody = document.getElementById('userTableBody');
    const availableRoles = {{ available_roles | tojson }};

    function generateFormHTML(mode, userData = {}) {
        const isEdit = mode === 'edit';
        const title = isEdit ? 'Benutzer bearbeiten' : 'Neuen Benutzer hinzufügen';
        const submitText = isEdit ? 'Änderungen speichern' : 'Benutzer hinzufügen';
        const formId = isEdit ? 'editUserForm' : 'addUserForm';
        
        // Dynamisch die Checkboxen für die Rollen generieren
        let rolesCheckboxesHTML = '';
        for (const roleName in availableRoles) {
            const description = availableRoles[roleName];
            const isChecked = isEdit && userData.roles && userData.roles.includes(roleName);
            rolesCheckboxesHTML += `
                <label class="flex items-start p-2 rounded-md hover:bg-slate-50 cursor-pointer">
                    <input type="checkbox" name="roles" value="${roleName}" class="mt-1 h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" ${isChecked ? 'checked' : ''}>
                    <div class="ml-3 text-sm">
                        <span class="font-medium text-gray-900">${roleName}</span>
                        <p class="text-gray-500 text-xs">${description}</p>
                    </div>
                </label>
            `;
        }

        return `
            <div class="bg-white rounded-lg p-6 border border-slate-200">
                <h2 class="text-xl font-semibold text-slate-800 mb-4">${title}</h2>
                <form class="space-y-4" id="${formId}">
                    ${isEdit ? `<input type="hidden" id="editUserIdOriginal" name="editUserIdOriginal" value="${userData.id}">` : ''}
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                        <div><label class="block text-sm font-medium text-slate-700">Nachname</label><input class="mt-1 block w-full rounded-md border-gray-300 shadow-sm" name="nachname" value="${userData.nachname || ''}" required></div>
                        <div><label class="block text-sm font-medium text-slate-700">Vorname</label><input class="mt-1 block w-full rounded-md border-gray-300 shadow-sm" name="vorname" value="${userData.vorname || ''}" required></div>
                    </div>
                    <div><label class="block text-sm font-medium text-slate-700">Benutzer-ID</label><input class="mt-1 block w-full rounded-md border-gray-300 shadow-sm ${isEdit ? 'bg-slate-100' : ''}" name="id" value="${userData.id || ''}" ${isEdit ? 'readonly' : ''} required></div>
                    <div><label class="block text-sm font-medium text-slate-700">Level</label><input class="mt-1 block w-full rounded-md border-gray-300 shadow-sm" name="level" value="${userData.level || ''}" placeholder="z.B. Teamleiter, Mechaniker" required></div>
                    <div>
                        <label class="block text-sm font-medium text-slate-700">Zuständige Bereiche (Indizes)</label>
                        <input class="mt-1 block w-full rounded-md border-gray-300 shadow-sm" name="units" value="${userData.units ? userData.units.join(', ') : ''}" placeholder="z.B. 1.1, 5 (Leer = Alle)">
                        <p class="text-xs text-gray-500 mt-1">Kommagetrennt. Schließt Unterbereiche (z.B. 1.1.2) automatisch ein.</p>
                    </div>
                    <div>
                        <label class="block text-sm font-medium text-slate-700 mb-2">Rollen</label>
                        <div class="grid grid-cols-1 sm:grid-cols-2 gap-x-4 gap-y-2 border rounded-lg p-3 bg-slate-50 max-h-48 overflow-y-auto">
                            ${rolesCheckboxesHTML}
                        </div>
                    </div>
                    <div class="flex justify-end space-x-3 pt-2">
                        <button class="px-4 py-2 bg-slate-200 text-slate-700 text-sm font-medium rounded-md hover:bg-slate-300" type="button" onclick="hideForms()">Abbrechen</button>
                        <button class="px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-md hover:bg-blue-700" type="submit">${submitText}</button>
                    </div>
                </form>
            </div>
        `;
    }

    function showForm(mode, userData = {}) {
        formContainer.innerHTML = generateFormHTML(mode, userData);
        formContainer.classList.remove('hidden');
        addUserBtn.classList.add('hidden');
        
        const form = formContainer.querySelector('form');
        form.addEventListener('submit', mode === 'edit' ? handleEditUser : handleAddUser);
    }

    window.hideForms = function() {
        formContainer.classList.add('hidden');
        formContainer.innerHTML = '';
        addUserBtn.classList.remove('hidden');
    }

    addUserBtn.addEventListener('click', () => showForm('add'));

    userTableBody.addEventListener('click', (event) => {
        const editButton = event.target.closest('.edit-user-btn');
        const deleteButton = event.target.closest('.delete-user-btn');

        if (editButton) {
            const row = editButton.closest('tr');
            const unitsStr = row.dataset.units || '';
            const units = unitsStr ? unitsStr.split(',').map(s => s.trim()) : [];

            const userData = {
                id: row.dataset.userid,
                nachname: row.dataset.nachname,
                vorname: row.dataset.vorname,
                level: row.dataset.level,
                roles: row.dataset.roles ? row.dataset.roles.split(',') : [],
                units: units // <--- NEU
            };
            showForm('edit', userData);
        } else if (deleteButton) {
            const userId = deleteButton.closest('tr').dataset.userid;
            if (confirm(`Benutzer '${userId.toUpperCase()}' wirklich löschen?`)) {
                handleDeleteUser(userId);
            }
        }
    });

    async function handleAddUser(event) {
        event.preventDefault();
        const formData = new FormData(event.target);
        const roles = formData.getAll('roles');
        const unitsStr = formData.get('units');
        console.log("Units Input:", unitsStr); 
        const units = unitsStr ? unitsStr.split(',').map(s => s.trim()).filter(Boolean) : [];
        
        const userData = {
            nachname: formData.get('nachname'),
            vorname: formData.get('vorname'),
            id: formData.get('id'),
            level: formData.get('level'),
            roles: roles,
            units: units
        };

        try {
            const response = await fetch("{{ url_for('api_add_user') }}", {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(userData)
            });
            const result = await response.json();
            if (response.ok) {
                alert(result.message || 'Benutzer hinzugefügt!');
                location.reload();
            } else { throw new Error(result.error); }
        } catch (err) { alert('Fehler: ' + err.message); }
    }

    async function handleEditUser(event) {
        event.preventDefault();
        const formData = new FormData(event.target);
        const userId = formData.get('editUserIdOriginal');
        const roles = formData.getAll('roles');
        const unitsStr = formData.get('units');
        const units = unitsStr ? unitsStr.split(',').map(s => s.trim()).filter(Boolean) : [];

        const updatedUserData = { 
            nachname: formData.get('nachname'),
            vorname: formData.get('vorname'),
            level: formData.get('level'),
            roles: roles,
            units: units
        };
        
        try {
            const response = await fetch(`{{ url_for('api_edit_user', user_id='USER_ID_PLACEHOLDER') }}`.replace('USER_ID_PLACEHOLDER', userId), {
                method: 'PUT',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(updatedUserData)
            });
            const result = await response.json();
            if (response.ok) {
                alert(result.message || 'Benutzer aktualisiert!');
                location.reload();
            } else { throw new Error(result.error); }
        } catch (err) { alert('Fehler: ' + err.message); }
    }
    
    async function handleDeleteUser(userId) {
        try {
            const response = await fetch(`{{ url_for('api_delete_user', user_id='USER_ID_PLACEHOLDER') }}`.replace('USER_ID_PLACEHOLDER', userId), { method: 'DELETE' });
            const result = await response.json();
            if (response.ok) {
                alert(result.message || 'Benutzer gelöscht!');
                location.reload();
            } else { throw new Error(result.error); }
        } catch (err) { alert('Fehler: ' + err.message); }
    }
});
</script>
</body></html>
"""

TIMELINE_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <title>MBET - Timeline</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter%3Awght%40400%3B500%3B600%3B700&family=Noto+Sans%3Awght%40400%3B500%3B700%3B900" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet"/>
    <style>
        .timeline-table tbody tr { height: 65px; } .timeline-table thead th { border-bottom: 1px solid #e5e7eb; }
        .timeline-table tbody td { border-bottom: 1px solid #e5e7eb; border-right: 1px solid #e5e7eb; min-width: 45px; width: 45px; max-width: 45px; padding: 0; vertical-align: middle; position: relative; overflow: hidden; }
        .timeline-table tbody td:last-child { border-right: none; } .timeline-table thead th > div:first-child { line-height: 1.5rem; padding-top: 0.25rem; padding-bottom: 0.25rem; }
        .timeline-table thead th div { padding-top: 0.25rem; padding-bottom: 0.25rem; }
        .task-segment-container { width: 100%; height: 100%; display: flex; align-items: center; font-size: 0.75rem; color: white; overflow: hidden; }
        .task-segment-content { flex-grow: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; padding: 2px 4px; overflow: hidden; white-space: nowrap; height: 100%; text-align: center; }
        .task-name-display { font-weight: 600; font-size: 0.65rem;letter-spacing: -0.04em; line-height: 1.1; margin-bottom: 3px; width: 100%; overflow: hidden; text-overflow: ellipsis; }
        .employee-initials-container { display: flex; justify-content: center; align-items: center; gap: 2px; margin-top: 2px; }
        .employee-initial-circle { width: 16px; height: 16px; border-radius: 50%; background-color: rgba(255, 255, 255, 0.3); color: white; display: flex; justify-content: center; align-items: center; font-size: 0.6rem; font-weight: 500; }
        .task-bar { width: 100%; height: 100%; cursor: default; position: relative; } .task-bar:hover .tooltip-text { visibility: visible; opacity: 1; }
        .tooltip-text { visibility: hidden; width: max-content; background-color: black; color: #fff; text-align: center; border-radius: 6px; padding: 5px 8px; position: absolute; z-index: 30; bottom: 125%; left: 50%; transform: translateX(-50%); opacity: 0; transition: opacity 0.3s; font-size: 0.75rem; white-space: pre-line; }
        .weekend-bg { background-color: #f3f4f6; } .current-timeslot-highlight { background-color: rgba(239, 68, 68, 0.15) !important; }
        .material-icons-outlined, .material-symbols-outlined, .material-icons { font-weight: normal; font-style: normal; font-size: 24px; line-height: 1; letter-spacing: normal; text-transform: none; display: inline-block; white-space: nowrap; word-wrap: normal; direction: ltr; -webkit-font-smoothing: antialiased; }
        .swat-spacer-row { height: 22px !important; } .swat-spacer-row-cell { height: 22px !important; background-color: #e2e8f0 !important; padding: 0 !important; line-height: 0 !important; font-size: 0 !important; border-top: 1px solid #cbd5e1 !important; border-bottom: 1px solid #cbd5e1 !important; border-left: none !important; border-right: none !important; vertical-align: top !important; }
        .modal-backdrop { position: fixed; inset: 0; background-color: rgba(0, 0, 0, 0.75); z-index: 40; display: none; }
        .modal { position: fixed; inset: 0; z-index: 50; display: none; align-items: center; justify-content: center; }
        .modal-container { padding: 1rem; box-sizing: border-box; }
        .modal-content { background-color: white; border-radius: 0.75rem; box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04); width: 100%; max-width: 60rem; padding: 1.5rem 2rem; position: relative; max-height: calc(100vh - 2rem); overflow-y: auto; box-sizing: border-box; }
        .modal-close-button { position: absolute; top: 1rem; right: 1rem; color: #6b7280; cursor: pointer; } .modal-close-button:hover { color: #374151; }
        .project-step-grid { display: grid; grid-template-columns: auto 1fr; gap: 0 1rem; align-items: start; cursor: pointer; padding: 0.25rem 0.5rem; border-radius: 0.375rem; margin-bottom: 0.125rem; }
        .project-step-grid:hover { background-color: #f0f4f8; }
        .project-step-grid.selected-step { background-color: #e9eff5; }
        .project-step-icon-col { display: flex; flex-direction: column; align-items: center; height: 100%; position: relative; } .project-step-icon-col.first-step { padding-top: 0.25rem; }
        .project-step-icon-connector { width: 2px; flex-grow: 1; margin: 0.25rem 0; }
        .project-step-icon-circle { display: flex; align-items: center; justify-content: center; width: 2rem; height: 2rem; border-radius: 9999px; position:relative; }
        .project-step-icon-circle .step-number { font-size: 0.875rem; font-weight: 600; }
        .project-step-details { padding-bottom: 1rem; }
        #deviationInputArea, #troubleshootingInputArea, #solutionInputArea { border: 1px dashed #cbd5e1; padding: 1rem; margin-top: 1rem; border-radius: 0.375rem; background-color: #f8fafc; }
        .form-select, .form-input { display: block; width: 100%; border-radius: 0.375rem; border-width: 1px; border-color: #cbd5e1; background-color: #fff; padding: 0.75rem; font-size: 0.875rem; line-height: 1.25rem;}
        #stepHistoryContainer { margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid #e5e7eb; }
        .history-entry { display: flex; align-items: flex-start; gap: 0.75rem; padding: 0.75rem; background-color: #f9fafb; border-radius: 0.375rem; margin-bottom: 0.75rem; }
        .history-entry .material-symbols-outlined { font-size: 1.125rem; color: #6b7280; margin-top: 0.125rem; }

        /* Styles für die einklappbare Sidebar */
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator {
            display: none;
        }
        #sidebar.is-collapsed .sidebar-separator {
            display: block;
            height: 1px;
            background-color: #e2e8f0;
            margin: 0.5rem 0.75rem;
        }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
        
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <!-- Hauptinhalt -->
    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <div class="p-4 sm:p-6 lg:p-8">
            
            <!-- HEADER BOX (Immer sichtbar) -->
            <div class="bg-white shadow-lg rounded-lg p-4 sm:p-6 mb-6">
                <div class="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
                    <div>
                        <h1 class="text-3xl font-bold tracking-tight text-slate-900">{{ timeline_name }}</h1>
                    </div>
                    <div class="flex items-center gap-2">
                        <div class="relative">
                            <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" style="font-size: 20px;">search</span>
                            <input id="projectSearchInput" class="pl-10 pr-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-48 md:w-64 text-sm" placeholder="Projekte/Tasks suchen..." type="text"/>
                        </div>
                        <button id="toggleViewBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-slate-200 px-3 py-2 text-sm font-semibold text-slate-700 shadow-sm hover:bg-slate-300" type="button"></button>
                        <button id="toggleArchiveBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-slate-200 px-3 py-2 text-sm font-semibold text-slate-700 shadow-sm hover:bg-slate-300 transition-colors" onclick="toggleArchiveView()">
                            <span class="material-icons-outlined text-[20px] leading-none">inventory_2</span> Archiv
                        </button>
                        <button id="refreshBtn" class="inline-flex items-center gap-x-1.5 rounded-md bg-blue-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-500 transition-colors" type="button">
                            <span class="material-icons-outlined text-[20px] leading-none">refresh</span>
                            Aktualisieren
                        </button>

                    </div>
                </div>
            </div>

            <!-- === LIVE VIEW WRAPPER (Zeitleiste & Heute) === -->
            <div id="liveTimelineView">
                <!-- Timeline Box -->
                <div class="bg-white shadow-lg rounded-lg p-4 sm:p-6 mb-8">
                    <div class="overflow-x-auto @container">
                        <div class="align-middle inline-block min-w-full">
                            <div class="flex">
                                <div class="sticky left-0 z-10 bg-white">
                                    <table class="min-w-[280px] divide-y divide-slate-200 border-r border-slate-200">
                                        <thead class="bg-slate-50"><tr><th class="px-4 py-3.5 text-left text-sm font-semibold text-slate-900 h-[65px] align-middle" scope="col">Projekt</th></tr></thead>
                                        <tbody class="divide-y divide-slate-200 bg-white" id="projectListBody"></tbody>
                                    </table>
                                </div>
                                <div class="flex-grow overflow-x-auto relative" id="timelineScrollContainer">
                                    <table class="min-w-full divide-y divide-slate-200 timeline-table">
                                        <thead class="bg-slate-50 sticky top-0 z-10">
                                            <tr id="timelineHeaderDates"></tr>
                                            <tr id="timelineHeaderShifts"></tr>
                                        </thead>
                                        <tbody class="divide-y divide-slate-200 bg-white" id="timelineBody"></tbody>
                                    </table>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                
                <!-- Heute Box -->
                <div class="bg-white p-6 rounded-lg shadow-lg">
                    <div class="flex justify-between items-center mb-6">
                        <h2 class="text-2xl font-bold text-gray-800">Heutige Zusammenfassung <span id="todayDateSummary" class="text-lg font-normal text-gray-500"></span></h2>
                    </div>
                    <div id="todaysSummaryContent"></div>
                </div>      
            </div>

            <!-- === ARCHIVE VIEW WRAPPER (Tabelle) === -->
            <div id="archiveView" class="hidden">
                <div class="bg-white shadow-lg rounded-lg border border-slate-200">
                    <div class="px-6 py-4 border-b border-slate-100 bg-slate-50 flex justify-between items-center rounded-t-lg">
                        <h2 class="text-xl font-bold text-slate-800 flex items-center gap-2">
                            <span class="material-icons-outlined text-indigo-500">inventory_2</span> Abgeschlossene Projekte
                        </h2>
                        <span class="text-xs text-slate-500">Zieht Daten direkt aus dem System-Protokoll</span>
                    </div>
                    <div class="overflow-x-auto custom-scrollbar min-h-[500px]">
                        <table class="w-full text-sm text-left">
                            <thead class="text-xs text-gray-500 uppercase bg-white border-b border-slate-200 sticky top-0 z-10">
                                <tr>
                                    <th class="px-6 py-4 font-bold">Projekt & Typ</th>
                                    <th class="px-6 py-4 font-bold">S/N</th>
                                    <th class="px-6 py-4 font-bold">Kunde</th>
                                    <th class="px-6 py-4 font-bold text-center">Status</th>
                                    <th class="px-6 py-4 font-bold text-right">Letztes Update</th>
                                </tr>
                            </thead>
                            <tbody id="archiveTableBody" class="divide-y divide-slate-100">
                                <tr><td colspan="5" class="text-center py-8 text-slate-500">Lade Archiv...</td></tr>
                            </tbody>
                        </table>
                    </div>
                </div>
            </div>

        </div>
    </main>
</div>
<div id="modalBackdrop" class="modal-backdrop"></div>
<div id="projectStatusModal" class="modal"> <div class="modal-container"> <div class="modal-content"> <button id="closeModalButton" class="modal-close-button" aria-label="Schließen"> <span class="material-symbols-outlined">close</span> </button> <div id="projectStatusModalContentContainer"> <p class="text-center text-slate-500">Lade Projektdetails...</p> </div> </div> </div> </div>
<script>
    // --- 1. Sidebar Logik ---
    const sidebar = document.getElementById('sidebar');
    const sidebarToggleBtn = document.getElementById('sidebar-toggle');
    if (sidebar && sidebarToggleBtn) {
        const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
        const applyState = (isCollapsed) => {
            if (isCollapsed) {
                sidebar.classList.add('is-collapsed');
                toggleBtnIcon.textContent = 'menu';
            } else {
                sidebar.classList.remove('is-collapsed');
                toggleBtnIcon.textContent = 'chevron_left';
            }
        };
        sidebarToggleBtn.addEventListener('click', () => {
            const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
            localStorage.setItem('sidebarCollapsed', isNowCollapsed);
            applyState(isNowCollapsed);
        });
        const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
        applyState(savedState);
    }

    // --- 2. Globale Variablen & Konfiguration ---
    const timelineId = {{ timeline_id|tojson }};
    const currentUserId = "{{ session.get('user_id', '').lower() }}";
    const userRoles = {{ session.get('roles', []) | tojson }};
    const isObserverOnly = userRoles.length === 1 && userRoles[0] === 'OBSERVER';
    const projectListBody = document.getElementById('projectListBody');
    const timelineHeaderDates = document.getElementById('timelineHeaderDates');
    const timelineHeaderShifts = document.getElementById('timelineHeaderShifts');
    const timelineBody = document.getElementById('timelineBody');
    const timelineScrollContainer = document.getElementById('timelineScrollContainer');
    const todaysSummaryContent = document.getElementById('todaysSummaryContent');
    const todayDateSummarySpan = document.getElementById('todayDateSummary');
    const projectSearchInput = document.getElementById('projectSearchInput');
    const userProfileIcon = document.getElementById('userProfileCircle');

    const monthNames = ["Jan","Feb","Mär","Apr","Mai","Jun","Jul","Aug","Sep","Okt","Nov","Dez"];
    const dayNames = ["So","Mo","Di","Mi","Do","Fr","Sa"];
    const dayNamesLong = ["Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag", "Samstag"];

    let colorMapping = {'default':'blue','test':'green','rig':'purple','de-rig':'indigo','atw':'red','rigging':'sky','de-rigging':'rose','paperwork':'amber'};
    let tailwindColors = ["slate","gray","zinc","neutral","stone","red","orange","amber","yellow","lime","green","emerald","teal","cyan","sky","blue","indigo","violet","purple","fuchsia","pink","rose"];

    let currentDisplayWeeks = 2;
    let toggleViewBtn, refreshBtn;
    let allFetchedProjects = [];
    let allFetchedTodaysSummary = [];
    let currentGlobalTimelineStartDate;

    // ### FIX TEIL 1: Neue globale Variablen für die Suche ###
    let savedAssignments = {}; 
    let savedEmployeeMap = new Map();
    let savedViewStartDate = new Date(); 
    // ########################################################

    // Modal Variablen
    const modalBackdrop = document.getElementById('modalBackdrop');
    const projectStatusModal = document.getElementById('projectStatusModal');
    const closeModalButton = document.getElementById('closeModalButton');
    const projectStatusModalContentContainer = document.getElementById('projectStatusModalContentContainer');

    let currentSelectedStepIdStr = null;
    let currentProjectDetailsForModal = null;
    const LOGGED_IN_USER_DISPLAY_NAME = "{{ current_user_display_name }}";
    let isHistoryVisible = false;
    let tempSelectedStatusForStep = null;
    let isDetailViewInEditMode = false;
    // --- NEUE ARCHIV VARIABLEN ---
    let isArchiveView = false;
    let allArchivedProjects =[];

    // --- ARCHIV FUNKTIONEN ---
    function toggleArchiveView() {
        isArchiveView = !isArchiveView;
        const liveView = document.getElementById('liveTimelineView');
        const archiveView = document.getElementById('archiveView');
        const toggleBtn = document.getElementById('toggleArchiveBtn');
        const refreshBtn = document.getElementById('refreshBtn');
        const viewToggleBtn = document.getElementById('toggleViewBtn');

        if (isArchiveView) {
            // Zeige Archiv
            liveView.classList.add('hidden');
            archiveView.classList.remove('hidden');
            toggleBtn.innerHTML = '<span class="material-icons-outlined text-base">arrow_back</span> Zurück zur Planung';
            refreshBtn.style.display = 'none';
            viewToggleBtn.style.display = 'none';
            loadArchiveData();
        } else {
            // Zeige Live
            liveView.classList.remove('hidden');
            archiveView.classList.add('hidden');
            toggleBtn.innerHTML = '<span class="material-icons-outlined text-base">inventory_2</span> Archiv';
            refreshBtn.style.display = 'inline-flex';
            viewToggleBtn.style.display = 'inline-flex';
            // Optional: Live-Ansicht Filter wiederherstellen
            const searchTerm = document.getElementById('projectSearchInput').value;
            fetchAndRenderTimeline(searchTerm);
        }
    }

    async function loadArchiveData() {
        const tbody = document.getElementById('archiveTableBody');
        tbody.innerHTML = '<tr><td colspan="5" class="text-center py-12 text-slate-400 flex flex-col items-center"><span class="material-icons-outlined text-4xl mb-2 animate-spin">sync</span> Lade Archivdaten aus dem System...</td></tr>';
        try {
            const res = await fetch(`/api/archive/projects/${timelineId}`);
            if (!res.ok) throw new Error('Netzwerkfehler');
            allArchivedProjects = await res.json();
            renderArchiveTable();
        } catch (e) {
            tbody.innerHTML = `<tr><td colspan="5" class="text-center py-8 text-red-500">Fehler beim Laden: ${e.message}</td></tr>`;
        }
    }

    function renderArchiveTable() {
        const tbody = document.getElementById('archiveTableBody');
        tbody.innerHTML = '';
        const searchTerm = document.getElementById('projectSearchInput').value.toLowerCase();

        const filtered = allArchivedProjects.filter(p =>
            p.name.toLowerCase().includes(searchTerm) ||
            p.engine_serial.toLowerCase().includes(searchTerm) ||
            p.customer.toLowerCase().includes(searchTerm) ||
            p.engine_type.toLowerCase().includes(searchTerm)
        );

        if (filtered.length === 0) {
            tbody.innerHTML = '<tr><td colspan="5" class="text-center py-12 text-slate-400 italic">Keine Projekte gefunden.</td></tr>';
            return;
        }

        filtered.forEach(p => {
            const tr = document.createElement('tr');
            tr.className = 'hover:bg-indigo-50/50 cursor-pointer transition-colors group';
            // Wir encodieren den Namen, da unsere Modal-Funktion das so erwartet
            tr.onclick = () => openProjectStatusModal(encodeURIComponent(p.name));

            let statusBadge = p.progress_percent === 100 
                ? '<span class="px-2 py-0.5 bg-green-100 text-green-700 border border-green-200 rounded-full text-xs font-bold uppercase tracking-wider">Erledigt (100%)</span>' 
                : `<span class="px-2 py-0.5 bg-slate-100 text-slate-600 border border-slate-200 rounded-full text-xs font-bold uppercase tracking-wider">${p.progress_percent}%</span>`;

            tr.innerHTML = `
                <td class="px-6 py-4">
                    <div class="font-bold text-indigo-700 group-hover:text-indigo-900">${p.name}</div>
                    <div class="text-[10px] text-slate-400 uppercase tracking-widest mt-0.5">${p.engine_type}</div>
                </td>
                <td class="px-6 py-4 text-slate-700 font-mono font-medium">${p.engine_serial}</td>
                <td class="px-6 py-4 text-slate-600">${p.customer}</td>
                <td class="px-6 py-4 text-center">${statusBadge}</td>
                <td class="px-6 py-4 text-right text-slate-500 text-xs">
                    <div class="flex items-center justify-end gap-1">
                        <span class="material-icons-outlined text-[14px] text-slate-300">history</span>
                        ${p.last_updated}
                    </div>
                </td>
            `;
            tbody.appendChild(tr);
        });
    }
    // --- 3. Hilfsfunktionen für Modal & Steps ---
    function findStepById(steps, idStr) {
        return steps.find(s => s.id_str === idStr);
    }
    function getStepChildren(steps, parentIdStr) {
        return steps.filter(s => s.parent_id_str === parentIdStr).sort((a, b) => {
            const aParts = a.id_str.split('.').map(Number);
            const bParts = b.id_str.split('.').map(Number);
            for (let i = 0; i < Math.min(aParts.length, bParts.length); i++) {
                if (aParts[i] !== bParts[i]) return aParts[i] - bParts[i];
            }
            return aParts.length - bParts.length;
        });
    }
    function generateNewStepId(steps, parentIdStr) {
        const children = getStepChildren(steps, parentIdStr);
        let maxSubIndex = 0;
        children.forEach(child => {
            const parts = child.id_str.split('.');
            const subIndex = parseInt(parts[parts.length - 1], 10);
            if (subIndex > maxSubIndex) {
                maxSubIndex = subIndex;
            }
        });
        return parentIdStr ? `${parentIdStr}.${maxSubIndex + 1}` : `${maxSubIndex + 1}`;
    }

    function closeProjectStatusModal() {
        modalBackdrop.style.display = 'none'; projectStatusModal.style.display = 'none';
        projectStatusModalContentContainer.innerHTML = ''; currentSelectedStepIdStr = null;
        currentProjectDetailsForModal = null; isHistoryVisible = false; tempSelectedStatusForStep = null;
    }

    async function saveProjectStatusToBackend(projectDetails) {
        if (!projectDetails || !projectDetails.project_code || !projectDetails.engine_serial) {
            console.error("Speichern fehlgeschlagen: project_code oder engine_serial fehlen.", projectDetails);
            return;
        }
        projectDetails.origin_timeline = timelineId;

        const project_id = `${projectDetails.project_code}_${projectDetails.engine_serial}`;
        try {
            const response = await fetch(`/api/project_status/${encodeURIComponent(project_id)}`, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(projectDetails)
            });
            if (!response.ok) {
                const errorData = await response.json();
                throw new Error(errorData.error || 'Fehler beim Speichern auf dem Server.');
            }
        } catch (error) {
            console.error("Fehler beim Speichern des Projektstatus:", error);
            alert("Fehler: Änderungen konnten nicht gespeichert werden. Bitte versuchen Sie es erneut.");
        }
    }

    function deleteStep(stepIdToDelete) {
        if (!confirm("Möchten Sie diesen Schritt und alle untergeordneten Schritte wirklich und endgültig löschen?")) {
            return;
        }
        if (!currentProjectDetailsForModal) return;
        const stepToDelete = findStepById(currentProjectDetailsForModal.steps, stepIdToDelete);
        if (!stepToDelete || !stepToDelete.parent_id_str) {
            alert("Fehler: Hauptschritte können nicht gelöscht werden.");
            return;
        }
        const parentId = stepToDelete.parent_id_str;
        const idsToDelete = [stepIdToDelete];
        const idsToScanForChildren = [stepIdToDelete];

        while (idsToScanForChildren.length > 0) {
            const currentId = idsToScanForChildren.shift();
            const children = getStepChildren(currentProjectDetailsForModal.steps, currentId);
            children.forEach(child => {
                idsToDelete.push(child.id_str);
                idsToScanForChildren.push(child.id_str);
            });
        }
        currentProjectDetailsForModal.steps = currentProjectDetailsForModal.steps.filter(
            step => !idsToDelete.includes(step.id_str)
        );
        currentSelectedStepIdStr = parentId;
        updateProjectProgress(currentProjectDetailsForModal);
        saveProjectStatusToBackend(currentProjectDetailsForModal).then(() => {
            renderModalContent(currentProjectDetailsForModal); 
        });
    }        

    function updateToggleViewButtonText(){ if(toggleViewBtn) toggleViewBtn.textContent = (currentDisplayWeeks === 2) ? "1 Woche anzeigen" : "2 Wochen anzeigen"; }    

    // --- 4. Hauptfunktion zum Laden und Rendern ---
    async function fetchAndRenderTimeline(searchTerm = '') {
        projectListBody.innerHTML = '<tr><td colspan="1">Lade Projekte...</td></tr>';
        timelineHeaderDates.innerHTML = `<th>Lade Tage...</th>`;
        timelineHeaderShifts.innerHTML = `<th>Lade Schichten...</th>`;
        timelineBody.innerHTML = `<tr><td>Lade Timeline...</td></tr>`;
        todaysSummaryContent.innerHTML = '<p class="text-gray-500 col-span-full text-center">Lade Zusammenfassung...</p>';

        const today = new Date();
        todayDateSummarySpan.textContent = `(${dayNamesLong[today.getDay()]}, ${today.getDate()}. ${monthNames[today.getMonth()]} ${today.getFullYear()})`;

        try {
            const response = await fetch(`/api/v2/timeline_data/${timelineId}?view_mode=static`);
            if (!response.ok) {
                throw new Error(`HTTP ${response.status} ${response.statusText}`);
            }
            const data = await response.json();
            if (data.error) {
                throw new Error(data.error);
            }

            allFetchedProjects = data.projects || [];
            const timelineConfig = data.timeline_config;
            currentGlobalTimelineStartDate = new Date(timelineConfig.start_date + 'T00:00:00.000Z');

            // Zeitraum berechnen
            const staticViewStartDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() - today.getDay() + 1);
            const numDaysToDisplay = currentDisplayWeeks * 7;
            const numColumnsToDisplay = numDaysToDisplay * 2;

            const employeeMapById = new Map((data.employees || []).map(e => [e.id.toLowerCase(), e]));

            // ### FIX TEIL 2: Daten für die Suche zwischenspeichern ###
            savedAssignments = data.calculated_data.assignments || {};
            savedEmployeeMap = employeeMapById;
            savedViewStartDate = staticViewStartDate; // WICHTIG: Das berechnete Datum speichern
            // ########################################################

            renderTimeline(
                filterProjects(allFetchedProjects, searchTerm),
                staticViewStartDate,
                numDaysToDisplay,
                numColumnsToDisplay,
                savedAssignments, 
                savedEmployeeMap 
            );

            const todaysSummaryData = generateTodaysSummary(allFetchedProjects, savedAssignments, savedEmployeeMap);
            allFetchedTodaysSummary = todaysSummaryData; // Auch wichtig für Suche
            renderTodaysSummary(todaysSummaryData, searchTerm);

        } catch (error) {
            projectListBody.innerHTML = `<tr><td colspan="1" class="text-red-500 p-2">Fehler: ${error.message}</td></tr>`;
            timelineBody.innerHTML = `<tr class="h-[65px]"><td class="text-red-500 p-2">Fehler: ${error.message}</td></tr>`;
            todaysSummaryContent.innerHTML = `<p class="text-red-500">Fehler: ${error.message}</p>`;
        }
    }

    // --- 5. Render-Hilfsfunktionen ---
    function filterProjects(projects,term){ 
        if(!term) return projects; 
        const l=term.toLowerCase(); 
        return projects.filter(p=>(p.name.toLowerCase().includes(l))||(p.engine_type&&p.engine_type.toLowerCase().includes(l))||(p.events.some(t=>t.title.toLowerCase().includes(l))));
    }

    function getTaskColor(taskName,projectType="ENGINE"){ 
        if(projectType==="SWAT") return 'yellow'; 
        const l=taskName.toLowerCase().replace(/\s+/g,'-'); 
        if(colorMapping[l]) return colorMapping[l]; 
        let h=0; 
        for(let i=0;i<l.length;i++) h=l.charCodeAt(i)+((h<<5)-h); 
        return tailwindColors[Math.abs(h)%tailwindColors.length];
    }

    function renderTimeline(projectsData, timelineStartDate, numDaysToDisplay, numColumnsToDisplay, assignments, employeeMap) {
        if (!timelineStartDate || !numDaysToDisplay || !numColumnsToDisplay) {
            projectListBody.innerHTML = '<tr><td class="text-red-500 p-2">Konfig.fehler</td></tr>';
            timelineBody.innerHTML = `<tr class="h-[65px]"><td colspan="${numColumnsToDisplay}" class="text-red-500 p-2">Konfig.fehler</td></tr>`;
            return;
        }

        const S = new Date(timelineStartDate);

        // Linke Spalte (Projektliste)
        projectListBody.innerHTML = '';
        projectsData.forEach(p => {
            if (!p.active) return;
            const r = document.createElement('tr');
            r.className = 'h-[65px]';
            const encodedProjectName = encodeURIComponent(p.name);

            // --- HIER IST DIE ÄNDERUNG ---
            if (isObserverOnly) {
                // Version für OBSERVER (nicht klickbar)
                r.innerHTML = `<td class="px-4 py-3 align-middle">
                                   <div>
                                       <p class="font-medium text-slate-800 truncate" title="${p.name}">${p.name}</p>
                                       <p class="text-xs text-slate-500 truncate" title="${p.engine_type} / ${p.project_code} / ${p.engine_serial} - ${p.customer}">${p.customer} / ${p.engine_serial}</p>
                                   </div>
                               </td>`;
            } else {
                // Version für alle anderen Rollen (klickbar)
                r.innerHTML = `<td class="px-4 py-3 align-middle">
                                   <div onclick="openProjectStatusModal('${encodedProjectName}')" class="cursor-pointer group">
                                       <p class="font-medium text-slate-800 group-hover:text-blue-600 group-hover:underline truncate" title="${p.name}">${p.name}</p>
                                       <p class="text-xs text-slate-500 truncate" title="${p.engine_type} / ${p.project_code} / ${p.engine_serial} - ${p.customer}">${p.customer} / ${p.engine_serial}</p>
                                   </div>
                               </td>`;
            }
            // --- ENDE DER ÄNDERUNG ---

            projectListBody.appendChild(r);
        });

        // Header
        timelineHeaderDates.innerHTML = '';
        timelineHeaderShifts.innerHTML = '';
        for (let i = 0; i < numDaysToDisplay; i++) {
            const d = new Date(S);
            d.setDate(S.getDate() + i);
            const w = d.getDay() === 0 || d.getDay() === 6;
            const c = w ? ' weekend-bg' : '';
            const thD = document.createElement('th');
            thD.className = `px-1 py-0 text-center text-xs font-medium text-slate-600 w-[90px] whitespace-nowrap border-r border-slate-200 last:border-r-0${c}`;
            thD.colSpan = 2;
            thD.innerHTML = `<div class="py-1.5 border-b border-slate-200">${dayNames[d.getDay()]}, ${d.getDate()}. ${monthNames[d.getMonth()]}</div>`;
            timelineHeaderDates.appendChild(thD);
            ['AM', 'PM'].forEach(shift => {
                const thS = document.createElement('th');
                thS.className = `px-1 py-0 text-center text-xs font-medium text-slate-500 w-[45px] whitespace-nowrap border-r border-slate-200 last:border-r-0${c}`;
                thS.innerHTML = `<div class="py-1.5">${shift}</div>`;
                timelineHeaderShifts.appendChild(thS);
            });
        }

        // Body
        timelineBody.innerHTML = '';
        projectsData.forEach(p => {
            if (!p.active) return;
            const row = document.createElement('tr');
            const cells = Array.from({ length: numColumnsToDisplay }, (_, i) => {
                const cell = document.createElement('td');
                const cellDate = new Date(S.getTime() + Math.floor(i / 2) * 86400000);
                if (cellDate.getDay() === 0 || cellDate.getDay() === 6) {
                    cell.classList.add('weekend-bg');
                }
                return cell;
            });

            (p.events || []).forEach(task => {
                const startDt = new Date(task.start_datetime);
                const endDt = new Date(task.end_datetime);
                let currentDt = new Date(startDt);
                let slotIndex = 0;

                while(currentDt < endDt) {
                    const diffDays = Math.floor((currentDt.getTime() - S.getTime()) / 86400000);
                    const colIndex = (diffDays * 2) + (currentDt.getHours() < 12 ? 0 : 1);

                    if (colIndex >= 0 && colIndex < numColumnsToDisplay) {
                        const eventSlotKey = `${task.id || (p.name + '_' + task.title)}_${slotIndex}`;

                        // Sicherer Zugriff, falls assignments undefined ist
                        const assignmentInfo = (assignments && assignments[eventSlotKey]) ? assignments[eventSlotKey] : { employees:[] };
                            const employeeIds = assignmentInfo.employees ||[];
                        
                            // FIX: Wir müssen die Liste für den Tooltip behalten!
                            const employeeInitials = employeeIds.map(id => (employeeMap && employeeMap.get(id.toLowerCase()) || {}).initials || '??');
                        
                            // ID-basierte Logik für die Anzeige im Balken
                            const ec = employeeIds.map(id => {
                                const lowerId = id.toLowerCase();
                                const emp = employeeMap ? employeeMap.get(lowerId) : null;
                                const initial = emp ? emp.initials : '??';
                                
                                if (lowerId === currentUserId) {
                                    return `<div class="employee-initial-circle" style="background-color: #2563eb; opacity: 1; font-weight: bold; font-size: 9px;" title="Ich (${initial})">${initial}</div>`;
                                } else {
                                    return `<div class="employee-initial-circle" title="${initial}"></div>`;
                                }
                            }).join('');
                        let sch = `<div class="task-segment-content"><span class="task-name-display">${task.title}</span><div class="employee-initials-container">${ec || ''}</div></div>`;
                        const tip = `${task.title}\nMitarbeiter: ${employeeInitials.join(', ')||'-'}`;
                        const tdc = getTaskColor(task.title);
                        const sc = `task-segment-container bg-${tdc}-500 rounded-md`;

                        cells[colIndex].innerHTML = `<div class="task-bar"><div class="${sc}">${sch}</div><span class="tooltip-text">${tip}</span></div>`;
                    }
                    currentDt.setHours(currentDt.getHours() + 12);
                    slotIndex++;
                }
            });
            cells.forEach(cell => row.appendChild(cell));
            timelineBody.appendChild(row);
        });

        highlightCurrentTimeslot(S, numDaysToDisplay);
    }

    function generateTodaysSummary(projects, assignments, employeeMap) {
        const today = new Date();
        today.setHours(0, 0, 0, 0); 
        const todayYear = today.getFullYear();
        const todayMonth = today.getMonth();
        const todayDate = today.getDate();
        const todaysTasks = [];

        projects.forEach(p => {
            if (!p.active) return;
            (p.events || []).forEach(task => {
                const startDt = new Date(task.start_datetime);
                const endDt = new Date(task.end_datetime);
                let currentDt = new Date(startDt);
                let slotIndex = 0;

                while (currentDt < endDt) {
                    if (currentDt.getFullYear() === todayYear &&
                        currentDt.getMonth() === todayMonth &&
                        currentDt.getDate() === todayDate) {

                        const eventSlotKey = `${task.id || (p.name + '_' + task.title)}_${slotIndex}`;
                        const assignmentInfo = (assignments && assignments[eventSlotKey]) ? assignments[eventSlotKey] : { employees: [] };
                        const assignedPeople = assignmentInfo.employees.map(id => {
                            const lowerId = id.toLowerCase();
                            const emp = employeeMap ? employeeMap.get(lowerId) : null;
                            return { id: lowerId, initial: emp ? emp.initials : '??' };
                        });
                    
                        todaysTasks.push({
                            task_name: task.title,
                            assigned_people: assignedPeople, // <-- Name der Eigenschaft geändert!
                            original_shift_name: currentDt.getHours() < 12 ? 'AM' : 'PM',
                            project_name: p.name,
                            project_typ: "ENGINE"
                        });
                    }
                    currentDt.setHours(currentDt.getHours() + 12);
                    slotIndex++;
                }
            });
        });

        todaysTasks.sort((a, b) => (a.original_shift_name === 'PM') - (b.original_shift_name === 'PM') || a.project_name.localeCompare(b.project_name));
        return todaysTasks;
    }

    function highlightCurrentTimeslot(startObj,numDays){
        if(!timelineBody)return;
        timelineBody.querySelectorAll('td:not(.swat-spacer-row-cell)').forEach(c=>c.classList.remove('current-timeslot-highlight')); 
        const T=new Date();
        const TDO=new Date(T.getFullYear(),T.getMonth(),T.getDate(),0,0,0,0); 
        const NSO=new Date(startObj.getFullYear(),startObj.getMonth(),startObj.getDate(),0,0,0,0); 
        const DD=Math.floor((TDO.getTime()-NSO.getTime())/(1000*60*60*24));
        if(DD>=0&&DD<numDays){
            let tci=(T.getHours()>=12)?(DD*2)+1:DD*2; 
            const rows=timelineBody.getElementsByTagName('tr'); 
            for(let i=0;i<rows.length;i++){
                if(rows[i].classList.contains('swat-spacer-row'))continue; 
                const cells=rows[i].getElementsByTagName('td'); 
                if(cells[tci]&&!cells[tci].classList.contains('swat-spacer-row-cell')){
                    cells[tci].classList.add('current-timeslot-highlight');
                }
            } 
            if(timelineScrollContainer){
                const cw=45;
                timelineScrollContainer.scrollLeft=Math.max(0,tci*cw-(timelineScrollContainer.offsetWidth/3));
            }
        }
    }

    function renderTodaysSummary(summaryData,searchTerm=''){
        todaysSummaryContent.innerHTML=''; 
        const lST=searchTerm.toLowerCase(); 
        const fS=searchTerm?summaryData.filter(t=>(t.project_name.toLowerCase().includes(lST))||(t.project_typ&&t.project_typ.toLowerCase().includes(lST))||(t.task_name.toLowerCase().includes(lST))):summaryData;
        if(!fS||fS.length===0){
            todaysSummaryContent.className='grid grid-cols-1';
            const m=searchTerm?'Keine passenden Einträge für heute.':'Keine Projekte für heute geplant'; 
            const sm=searchTerm?'Anderen Suchbegriff versuchen.':'Ein ruhiger Tag!';
            todaysSummaryContent.innerHTML=`<div class="bg-gray-50 p-4 rounded-lg shadow-md border-l-4 border-gray-400 flex flex-col items-center justify-center text-center col-span-full"><span class="material-icons-outlined text-gray-400 text-5xl mb-3">event_busy</span><h3 class="text-lg font-semibold text-gray-700 mb-1">${m}</h3><p class="text-sm text-gray-500">${sm}</p></div>`;return;
        }
        todaysSummaryContent.className='grid grid-cols-1 md:grid-cols-2 gap-x-8 gap-y-6'; 
        const amC=document.createElement('div');amC.className='space-y-4';
        const amH=document.createElement('h4');amH.className='text-xl font-semibold text-gray-700 mb-4 border-b border-gray-300 pb-2';amH.textContent='Frühschicht';amC.appendChild(amH); 
        const pmC=document.createElement('div');pmC.className='space-y-4';
        const pmH=document.createElement('h4');pmH.className='text-xl font-semibold text-gray-700 mb-4 border-b border-gray-300 pb-2';pmH.textContent='Spätschicht';pmC.appendChild(pmH); 
        let amE=false,pmE=false;
        fS.forEach(t=>{
            let tcN,tbC,thC,tcbC,otbC,ottC; 
            if(t.project_typ==="SWAT"){tcN='slate';tbC=`border-${tcN}-400`;thC=`text-${tcN}-600`;tcbC=`bg-${tcN}-100`;otbC=`bg-${tcN}-300`;ottC=`text-${tcN}-700`;}
            else{tcN=getTaskColor(t.task_name,t.project_typ);tbC=`border-${tcN}-500`;thC=`text-${tcN}-700`;tcbC=`bg-${tcN}-50`;otbC=`bg-${tcN}-200`;ottC=`text-${tcN}-600`;}
            const assigned = t.assigned_people ||[];
            
            const eiH = assigned.map(p => {
                if (p.id === currentUserId) {
                    return `<span class="inline-flex h-6 w-6 rounded-full ring-2 ring-white bg-blue-600 text-white items-center justify-center text-[10px] font-bold" title="Ich (${p.initial})">${p.initial}</span>`;
                } else {
                    return `<span class="inline-block h-6 w-6 rounded-full ring-2 ring-white bg-gray-300" title="${p.initial}"></span>`;
                }
            }).join('');
            
            const ptd = t.project_typ || 'N/A';
            
            // FIX: Hier 'assigned.length' statt 't.people_initials.length' verwenden!
            const card = `<div class="${tcbC} p-4 rounded-lg shadow-md border-l-4 ${tbC} hover:shadow-lg transition-shadow duration-200"><div class="flex justify-between items-start mb-2"><h3 class="text-lg font-semibold ${thC}">${t.project_name}</h3><span class="text-xs font-medium ${ottC} ${otbC} px-2.5 py-1 rounded-full whitespace-nowrap">${ptd}</span></div><p class="text-sm text-gray-800 mb-3 font-medium">${t.task_name}</p>${assigned.length > 0 ? `<div class="mt-auto"><span class="text-xs font-semibold text-gray-500 uppercase tracking-wider">Team:</span><div class="flex -space-x-2 mt-1.5 overflow-hidden">${eiH}</div></div>` : '<div class="mt-auto"><p class="text-xs text-gray-400 italic">Keine MA zugewiesen.</p></div>'}</div>`;
            if(t.original_shift_name==='AM'){amC.insertAdjacentHTML('beforeend',card);amE=true;}
            else if(t.original_shift_name==='PM'){pmC.insertAdjacentHTML('beforeend',card);pmE=true;}
        });
        if(!amE)amC.insertAdjacentHTML('beforeend','<p class="text-sm text-gray-500 italic p-1 bg-gray-50 rounded tc">Keine AM-Tasks.</p>'); 
        if(!pmE)pmC.insertAdjacentHTML('beforeend','<p class="text-sm text-gray-500 italic p-1 bg-gray-50 rounded tc">Keine PM-Tasks.</p>');
        todaysSummaryContent.appendChild(amC);todaysSummaryContent.appendChild(pmC);
    }

    async function openProjectStatusModal(encodedProjectName) {
        modalBackdrop.style.display = 'block'; projectStatusModal.style.display = 'flex';
        projectStatusModalContentContainer.innerHTML = '<p class="text-center text-slate-500 py-10">Lade Projektdetails...</p>';
        currentSelectedStepIdStr = null; currentProjectDetailsForModal = null; isHistoryVisible = false;
        tempSelectedStatusForStep = null;
        try {
            const response = await fetch(`/api/project_details/${encodedProjectName}`);
            if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.error || `HTTP Error ${response.status}`); }
            currentProjectDetailsForModal = await response.json();
            currentProjectDetailsForModal.steps.forEach((step, index) => {
                if (!step.id_str) step.id_str = String(index + 1);
                if (step.parent_id_str === undefined) step.parent_id_str = null;
                if (!step.type) step.type = 'main';
                if (step.is_expanded === undefined) step.is_expanded = true; 
                if (!step.history) step.history = []; 
            });
            if (currentProjectDetailsForModal.current_step_for_notes) {
                 const initialStep = findStepById(currentProjectDetailsForModal.steps, currentProjectDetailsForModal.current_step_for_notes.id_str) ||
                                   currentProjectDetailsForModal.steps.find(s => s.name === currentProjectDetailsForModal.current_step_for_notes.name); 
                 if (initialStep) currentSelectedStepIdStr = initialStep.id_str;
            } else if (currentProjectDetailsForModal.steps && currentProjectDetailsForModal.steps.length > 0) {
                const firstMainStep = currentProjectDetailsForModal.steps.find(s => s.type === 'main' && s.parent_id_str === null);
                currentSelectedStepIdStr = firstMainStep ? firstMainStep.id_str : currentProjectDetailsForModal.steps[0].id_str;
            }
            renderModalContent(currentProjectDetailsForModal);
        } catch (error) { projectStatusModalContentContainer.innerHTML = `<p class="text-center text-red-500 py-10">Fehler: ${error.message}</p>`; }
    }

    function renderStepDetailSection(project, selectedStepIdStr) {
        const stepData = findStepById(project.steps, selectedStepIdStr);
        if (!stepData) return '<p class="text-slate-500">Bitte einen Schritt auswählen, um Details anzuzeigen.</p>';

        if (tempSelectedStatusForStep === null || currentSelectedStepIdStr !== selectedStepIdStr) {
            tempSelectedStatusForStep = stepData.status;
        }
        const notes = stepData.notes || '';
        const detailViewTitle = stepData.name.replace(/\s*\(\s*\d+(\.\d+)*\s*\)$/, '').trim();
        let lastUpdatedHtml = '';
        if (stepData.last_updated_by && stepData.last_updated_at) {
            lastUpdatedHtml = `<p class="text-slate-500 text-xs mt-1">Zuletzt aktualisiert von ${stepData.last_updated_by} am ${stepData.last_updated_at}</p>`;
        } else if (stepData.status === 'Upcoming') {
            lastUpdatedHtml = `<p class="text-slate-500 text-xs mt-1">Noch nicht zugewiesen oder gestartet.</p>`;
        }

        let hubIncidentDetailHtml = '';
        if (stepData.type === 'deviation' && stepData.deviation_details && stepData.deviation_details.hub_incident_created) {
            const ticketNr = stepData.deviation_details.hub_incident_number || '';
            if (ticketNr) {
                hubIncidentDetailHtml = `
                <div class="mt-4 flex flex-col gap-2 bg-blue-50 border border-blue-200 text-blue-800 text-sm font-medium px-4 py-3 rounded-lg">
                    <div class="flex items-center justify-between">
                        <div class="flex items-center gap-2">
                            <span class="material-symbols-outlined" style="font-size: 20px;">confirmation_number</span>
                            <span>MTU.Hub Ticket: <strong>${ticketNr}</strong></span>
                        </div>
                        <button onclick="editIncidentNumber('${project.name}', '${stepData.id_str}', '${ticketNr}')" class="text-xs text-blue-600 hover:text-blue-900 underline">Ändern</button>
                    </div>
                </div>`;
            } else {
                hubIncidentDetailHtml = `
                <div class="mt-4 bg-blue-50 border border-blue-200 text-blue-800 text-sm px-4 py-3 rounded-lg">
                    <div class="flex items-center gap-2 mb-2">
                        <span class="material-symbols-outlined">open_in_new</span>
                        <span>Incident wurde extern eröffnet.</span>
                    </div>
                    <div class="flex items-center gap-2">
                        <input type="text" id="incNumberInput_${stepData.id_str}" placeholder="Ticket-Nr. (z.B. INC...)" class="form-input h-8 text-xs w-48">
                        <button onclick="saveIncidentNumber('${project.name}', '${stepData.id_str}')" class="bg-blue-600 text-white px-3 py-1 rounded text-xs hover:bg-blue-700">Speichern</button>
                    </div>
                </div>`;
            }
        }

        function getEditViewHtml() {
            const bearbeitetDurchHtml = `<label class="flex flex-col"><span class="text-sm font-medium text-slate-700 mb-1">Bearbeitet durch</span><input type="text" class="form-input rounded-md bg-slate-100 h-10" value="${LOGGED_IN_USER_DISPLAY_NAME}" readonly /></label>`;
            let contextSpecificFieldHtml = '';
            if (stepData.type === 'deviation') {
                const reason = (stepData.deviation_details && stepData.deviation_details.reason) ? stepData.deviation_details.reason : '';
                contextSpecificFieldHtml = `<label class="flex flex-col w-full"><span class="text-sm font-medium text-slate-700 mb-1">Grund der Abweichung</span><input type="text" id="stepDetailReason" class="form-input h-10" value="${reason}" placeholder="z.B. Falsches Teil geliefert"></label>`;
            } else if (stepData.type === 'troubleshooting') {
                const attempt = (stepData.troubleshooting_details && stepData.troubleshooting_details.attempt) ? stepData.troubleshooting_details.attempt : '';
                contextSpecificFieldHtml = `<label class="flex flex-col w-full"><span class="text-sm font-medium text-slate-700 mb-1">Versuch / Maßnahme</span><input type="text" id="stepDetailAttempt" class="form-input h-10" value="${attempt}" placeholder="z.B. Sensor ausgetauscht"></label>`;
            }
            return `<div class="grid grid-cols-1 md:grid-cols-2 gap-6 items-end"><label class="flex flex-col"><span class="text-sm font-medium text-slate-700 mb-1">Status aktualisieren</span><select id="stepStatusSelect" class="form-select rounded-md h-10 p-3 py-0" onchange="tempSelectedStatusForStep = this.value;"><option value="Upcoming" ${tempSelectedStatusForStep === 'Upcoming' ? 'selected' : ''}>Anstehend</option><option value="In Progress" ${tempSelectedStatusForStep === 'In Progress' ? 'selected' : ''}>In Bearbeitung</option><option value="Completed" ${tempSelectedStatusForStep === 'Completed' ? 'selected' : ''}>Abgeschlossen</option><option value="On Hold" ${tempSelectedStatusForStep === 'On Hold' ? 'selected' : ''}>Angehalten</option><option value="N/R" ${tempSelectedStatusForStep === 'N/R' ? 'selected' : ''}>N/R (Nicht benötigt)</option></select></label>${bearbeitetDurchHtml}</div>${contextSpecificFieldHtml}<label class="flex flex-col w-full"><span class="text-sm font-medium text-slate-700 mb-1">Notizen & Beobachtungen</span><textarea id="stepNotesTextarea" class="form-input rounded-md min-h-32" placeholder="Notizen hinzufügen...">${notes}</textarea></label><div class="flex justify-end items-center gap-3"><button type="button" class="px-4 py-2 text-sm font-medium rounded-md text-slate-700 hover:bg-slate-200" onclick="toggleDetailViewEditMode(false)">Abbrechen</button><button class="flex items-center justify-center gap-2 min-w-[140px] h-10 px-4 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-lg shadow-sm" onclick="saveStepUpdate('${project.name}', '${stepData.id_str}')"><span class="material-symbols-outlined text-base">save</span> Update speichern</button></div>`;
        }

        function getReadOnlyViewHtml() {
            const statusMap = { "Upcoming": { text: "Anstehend", color: "bg-slate-200 text-slate-700" }, "In Progress": { text: "In Bearbeitung", color: "bg-blue-100 text-blue-700" }, "Completed": { text: "Abgeschlossen", color: "bg-green-100 text-green-700" }, "On Hold": { text: "Angehalten", color: "bg-yellow-100 text-yellow-700" }, "N/R": { text: "Nicht benötigt", color: "bg-gray-200 text-gray-600" } };
            const statusInfo = statusMap[stepData.status] || statusMap["Upcoming"];
            let contextSpecificFieldHtml = '';
            if (stepData.type === 'deviation' && stepData.deviation_details && stepData.deviation_details.reason) {
                contextSpecificFieldHtml = `<div class="mt-4"><p class="text-sm font-medium text-slate-700 mb-1">Grund der Abweichung</p><p class="p-3 bg-white border rounded-md text-slate-800">${stepData.deviation_details.reason}</p></div>`;
            } else if (stepData.type === 'troubleshooting' && stepData.troubleshooting_details && stepData.troubleshooting_details.attempt) {
                contextSpecificFieldHtml = `<div class="mt-4"><p class="text-sm font-medium text-slate-700 mb-1">Versuch / Maßnahme</p><p class="p-3 bg-white border rounded-md text-slate-800">${stepData.troubleshooting_details.attempt}</p></div>`;
            }
            return `<div class="flex justify-between items-center"><p class="text-sm font-medium text-slate-700">Status</p><span class="text-sm font-semibold px-3 py-1 rounded-full ${statusInfo.color}">${statusInfo.text}</span></div>${contextSpecificFieldHtml}<div class="mt-4"><p class="text-sm font-medium text-slate-700 mb-1">Notizen & Beobachtungen</p><div class="p-3 bg-white border rounded-md text-slate-800 min-h-[8rem] whitespace-pre-wrap">${notes || '<span class="text-slate-400 italic">Keine Notizen vorhanden.</span>'}</div></div>`;
        }

        let actionButtonsHtml = '';
        if (stepData.type === 'main') { actionButtonsHtml += `<button class="flex items-center justify-center gap-2 h-10 px-4 bg-yellow-100 hover:bg-yellow-200 text-yellow-800 text-sm font-semibold rounded-lg border border-yellow-300" onclick="toggleDeviationInput(true, '${stepData.id_str}')"><span class="material-symbols-outlined text-base">warning</span> Deviation hinzufügen</button>`; } 
        else if (stepData.type === 'deviation') { actionButtonsHtml += `<button class="flex items-center justify-center gap-2 h-10 px-4 bg-orange-100 hover:bg-orange-200 text-orange-800 text-sm font-semibold rounded-lg border border-orange-300" onclick="toggleTroubleshootingInput(true, '${stepData.id_str}')"><span class="material-symbols-outlined text-base">build_circle</span> Troubleshooting</button>`; actionButtonsHtml += `<button class="flex items-center justify-center gap-2 h-10 px-4 bg-green-100 hover:bg-green-200 text-green-800 text-sm font-semibold rounded-lg border border-green-300" onclick="toggleSolutionInput(true, '${stepData.id_str}')"><span class="material-symbols-outlined text-base">task_alt</span> Solution hinzufügen</button>`; } 
        else if (stepData.type === 'troubleshooting') { actionButtonsHtml += `<button class="flex items-center justify-center gap-2 h-10 px-4 bg-green-100 hover:bg-green-200 text-green-800 text-sm font-semibold rounded-lg border border-green-300" onclick="toggleSolutionInput(true, '${stepData.id_str}')"><span class="material-symbols-outlined text-base">task_alt</span> Solution hinzufügen</button>`; }

        return `<div>
            <div class="flex justify-between items-start">
                <div><h2 class="text-slate-900 text-2xl font-bold leading-tight tracking-tight mb-1">${detailViewTitle} (${stepData.id_str})</h2>${lastUpdatedHtml}</div>
                ${!isDetailViewInEditMode ? `<button class="flex items-center justify-center gap-2 h-10 px-4 text-blue-600 hover:bg-blue-100 text-sm font-semibold rounded-lg" onclick="toggleDetailViewEditMode(true)"><span class="material-symbols-outlined text-base">edit</span> Bearbeiten</button>` : ''}
            </div>
            <div class="mt-6 flex flex-col gap-6">
                <div class="bg-slate-50 p-6 rounded-lg border border-slate-200 space-y-6">${isDetailViewInEditMode ? getEditViewHtml() : getReadOnlyViewHtml()}</div>
                ${hubIncidentDetailHtml}
                <div class="mt-2 pt-6 border-t border-slate-200">
                    <h3 class="text-lg font-semibold text-slate-800 mb-4">Nächste Schritte & Problemlösung</h3>
                    <div class="flex justify-start flex-wrap gap-3">${actionButtonsHtml}</div>
                    
                    <!-- DEVIATION INPUT AREA -->
                    <div id="deviationInputArea" class="hidden mt-4">
                        <div class="p-4 rounded-lg bg-yellow-50 border border-yellow-200 space-y-3">
                            <h4 class="text-md font-semibold text-yellow-900">Neue Deviation hinzufügen</h4>
                            <input type="hidden" id="deviationParentStepId" value="" />
                            <label class="flex flex-col"><span class="text-sm font-medium text-slate-700 mb-1">Grund der Abweichung</span><input type="text" id="deviationReasonInput" class="form-input h-10" placeholder="z.B. Falsches Teil geliefert, Sensor defekt"></label>
                            <label class="flex flex-col"><span class="text-sm font-medium text-slate-700 mb-1">Weitere Informationen</span><textarea id="deviationInfoTextarea" class="form-input min-h-20" placeholder="Details zur Abweichung..."></textarea></label>
                            
                            <!-- CHECKBOXEN -->
                            <div class="flex flex-col gap-2 mt-3">
                                <div class="flex items-center">
                                    <input id="createHubIncidentCheckbox" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500">
                                    <label for="createHubIncidentCheckbox" class="ml-2 block text-sm font-medium text-gray-800">MTU.Hub Incident eröffnen</label>
                                </div>
                                <!-- NEU: PPE Checkbox -->
                                <div class="flex items-center">
                                    <input id="checkPartsCheckbox" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="togglePpeText(this)">
                                    <label for="checkPartsCheckbox" class="ml-2 block text-sm font-medium text-gray-800">PPE involvieren & Teilecheck</label>
                                </div>
                                <!-- Shop Support Checkbox -->
                                <div class="flex items-center">
                                    <input id="shopSupportCheckbox" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" onchange="toggleShopSupportText(this)">
                                    <label for="shopSupportCheckbox" class="ml-2 block text-sm font-medium text-gray-800">Shop Support involvieren</label>
                                </div>
                            </div>

                            <div class="flex justify-start gap-2 pt-2">
                                <button class="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-md text-sm" onclick="submitDeviation()">Deviation Speichern</button>
                                <button type="button" class="bg-slate-200 hover:bg-slate-300 text-slate-700 px-4 py-2 rounded-md text-sm" onclick="toggleDeviationInput(false)">Abbrechen</button>
                            </div>
                        </div>
                    </div>
                    
                    <div id="troubleshootingInputArea" class="hidden mt-4"><div class="p-4 rounded-lg bg-orange-50 border border-orange-200 space-y-3"><h4 class="text-md font-semibold text-orange-900">Neues Troubleshooting hinzufügen</h4><input type="hidden" id="troubleshootingParentStepId" value="" /><label class="flex flex-col"><span class="text-sm font-medium text-slate-700 mb-1">Versuch / Maßnahme</span><input type="text" id="troubleshootingAttemptInput" class="form-input h-10" placeholder="z.B. Sensor ausgetauscht"></label><label class="flex flex-col"><span class="text-sm font-medium text-slate-700 mb-1">Ergebnis / Beobachtung</span><textarea id="troubleshootingInfoTextarea" class="form-input min-h-20" placeholder="Details zum Versuch..."></textarea></label><div class="flex justify-start gap-2 pt-2"><button class="bg-orange-600 hover:bg-orange-700 text-white px-4 py-2 rounded-md text-sm" onclick="submitTroubleshooting()">Troubleshooting Speichern</button><button type="button" class="bg-slate-200 hover:bg-slate-300 text-slate-700 px-4 py-2 rounded-md text-sm" onclick="toggleTroubleshootingInput(false)">Abbrechen</button></div></div></div>
                    <div id="solutionInputArea" class="hidden mt-4"><div class="p-4 rounded-lg bg-green-50 border border-green-200 space-y-3"><h4 class="text-md font-semibold text-green-900">Neue Solution hinzufügen</h4><input type="hidden" id="solutionParentStepId" value="" /><label class="flex flex-col"><span class="text-sm font-medium text-slate-700 mb-1">Beschreibung der Lösung</span><textarea id="solutionDescriptionTextarea" class="form-input min-h-20" placeholder="Was war die endgültige Lösung des Problems?"></textarea></label><div class="flex justify-start gap-2 pt-2"><button class="bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded-md text-sm" onclick="submitSolution()">Solution Speichern</button><button type="button" class="bg-slate-200 hover:bg-slate-300 text-slate-700 px-4 py-2 rounded-md text-sm" onclick="toggleSolutionInput(false)">Abbrechen</button></div></div></div>
                </div>
            </div>
        </div>`;
    }

    function saveIncidentNumber(projectName, stepIdStr) {
        const input = document.getElementById(`incNumberInput_${stepIdStr}`);
        const newNumber = input ? input.value.trim() : '';
        if(!newNumber) return; // Nichts tun bei leerer Eingabe
        
        updateIncidentNumberInBackend(projectName, stepIdStr, newNumber);
    }

    function editIncidentNumber(projectName, stepIdStr, currentNumber) {
        // Wir nutzen prompt für den Edit-Fall, um das UI einfach zu halten, 
        // oder wir setzen den State zurück. Einfacher Hack:
        const newNumber = prompt("Ticket-Nummer korrigieren:", currentNumber);
        if (newNumber !== null) {
            updateIncidentNumberInBackend(projectName, stepIdStr, newNumber.trim());
        }
    }

    async function updateIncidentNumberInBackend(projectName, stepIdStr, number) {
        if (!currentProjectDetailsForModal) return;
        const step = findStepById(currentProjectDetailsForModal.steps, stepIdStr);
        
        if (step && step.deviation_details) {
            step.deviation_details.hub_incident_number = number;
            
            // Speichern
            await saveProjectStatusToBackend(currentProjectDetailsForModal);
            
            // Neu rendern
            renderModalContent(currentProjectDetailsForModal);
        }
    }
    function toggleDetailViewEditMode(enableEditing) {
        isDetailViewInEditMode = enableEditing;
        renderModalContent(currentProjectDetailsForModal);
    }
    function selectStepInModal(stepIdStr) {
        currentSelectedStepIdStr = stepIdStr;
        isDetailViewInEditMode = false; 
        tempSelectedStatusForStep = null; 
        if (currentProjectDetailsForModal) {
            renderModalContent(currentProjectDetailsForModal);
            if (isHistoryVisible) {
                const selectedStep = findStepById(currentProjectDetailsForModal.steps, stepIdStr);
                if (selectedStep) renderStepHistory(currentProjectDetailsForModal, selectedStep);
            }
        }
    }

    function saveStepUpdate(projectName, stepIdStr) {
        const newStatus = tempSelectedStatusForStep;
        const notes = document.getElementById('stepNotesTextarea') ? document.getElementById('stepNotesTextarea').value : '';
        if (currentProjectDetailsForModal) {
            const step = findStepById(currentProjectDetailsForModal.steps, stepIdStr);
            if (step) {
                const oldStatus = step.status;
                const oldNotes = step.notes || '';
                let detailsChanged = false;
                if (step.type === 'deviation') {
                    const reasonInput = document.getElementById('stepDetailReason');
                    if (reasonInput) {
                        const newReason = reasonInput.value;
                        if (!step.deviation_details) step.deviation_details = {};
                        if (step.deviation_details.reason !== newReason) {
                            step.deviation_details.reason = newReason;
                            detailsChanged = true;
                        }
                    }
                } else if (step.type === 'troubleshooting') {
                    const attemptInput = document.getElementById('stepDetailAttempt');
                    if (attemptInput) {
                        const newAttempt = attemptInput.value;
                        if (!step.troubleshooting_details) step.troubleshooting_details = {};
                        if (step.troubleshooting_details.attempt !== newAttempt) {
                            step.troubleshooting_details.attempt = newAttempt;
                            detailsChanged = true;
                        }
                    }
                }
                step.notes = notes;
                if (newStatus !== oldStatus || notes !== oldNotes || detailsChanged) { 
                    step.status = newStatus;
                    step.last_updated_by = LOGGED_IN_USER_DISPLAY_NAME || "Benutzer";
                    const now = new Date();
                    step.last_updated_at = now.toLocaleString('de-DE', { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit'});
                    step.history = step.history || [];
                    let historyAction = `Status: ${newStatus}`;
                    if (notes !== oldNotes) historyAction += ', Notiz aktualisiert';
                    if (detailsChanged) historyAction += ', Details aktualisiert';
                    step.history.push({ action: historyAction, user: LOGGED_IN_USER_DISPLAY_NAME, timestamp: now.toISOString() });
                }
                tempSelectedStatusForStep = newStatus;
                updateProjectProgress(currentProjectDetailsForModal);

                saveProjectStatusToBackend(currentProjectDetailsForModal).then(() => {
                    isDetailViewInEditMode = false; 
                    renderModalContent(currentProjectDetailsForModal); 
                    if (isHistoryVisible) renderStepHistory(currentProjectDetailsForModal, step);
                });
            }
        }
    }
    function updateProjectProgress(project) {
        const mainSteps = project.steps.filter(s => s.type === 'main' && s.parent_id_str === null);
        const relevantSteps = mainSteps.filter(step => step.status !== 'N/R');
        const totalRelevantSteps = relevantSteps.length;
        if (totalRelevantSteps === 0) {
            project.progress_percent = 100;
            return;
        }
        const stepWeight = 100 / totalRelevantSteps;
        let calculatedProgress = 0;
        relevantSteps.forEach(step => {
            if (step.status === 'Completed') {
                calculatedProgress += stepWeight;
            } else if (step.status === 'In Progress') {
                calculatedProgress += stepWeight * 0.5;
            }
        });
        let finalProgress = Math.round(calculatedProgress);
        if (finalProgress >= 100) {
            const allRelevantStepsCompleted = relevantSteps.every(step => step.status === 'Completed');
            if (!allRelevantStepsCompleted) {
                finalProgress = 99; 
            } else {
                finalProgress = 100; 
            }
        }
        project.progress_percent = finalProgress;
    }
    function toggleInputArea(areaId, parentStepIdInputId, show, parentStepIdStr = null) {
        const area = document.getElementById(areaId);
        const parentStepIdInput = document.getElementById(parentStepIdInputId);
        if (area) {
            area.style.display = show ? 'block' : 'none';
            if (show && parentStepIdInput && parentStepIdStr) parentStepIdInput.value = parentStepIdStr;
            if (!show) { area.querySelectorAll('input[type="text"], textarea').forEach(input => input.value = ''); if (parentStepIdInput) parentStepIdInput.value = '';}
        }
        ['deviationInputArea', 'troubleshootingInputArea', 'solutionInputArea'].forEach(id => { if (id !== areaId) { const otherArea = document.getElementById(id); if (otherArea) otherArea.style.display = 'none';}});
    }
    function toggleDeviationInput(show, parentStepIdStr = null) { toggleInputArea('deviationInputArea', 'deviationParentStepId', show, parentStepIdStr); }
    function toggleShopSupportText(checkbox) {
        const textarea = document.getElementById('deviationInfoTextarea');
        if (!textarea) return;
        const shopText = "INFO: Shop Support ist notwendig\\n\\n";
        if (checkbox.checked) {
            if (!textarea.value.startsWith(shopText)) {
                textarea.value = shopText + textarea.value;
            }
        } else {
            if (textarea.value.startsWith(shopText)) {
                textarea.value = textarea.value.replace(shopText, "");
            }
        }
    }
    function togglePpeText(checkbox) {
        const textarea = document.getElementById('deviationInfoTextarea');
        if (!textarea) return;
    
        const ppeText = "INFO: Bitte PPE involvieren und bestellte Teilnummern auf Richtigkeit prüfen lassen.\\n\\n";
    
        if (checkbox.checked) {
            // Text vorne einfügen, falls noch nicht da
            if (!textarea.value.startsWith(ppeText)) {
                textarea.value = ppeText + textarea.value;
            }
        } else {
            // Text entfernen, falls er da ist
            if (textarea.value.startsWith(ppeText)) {
                textarea.value = textarea.value.replace(ppeText, "");
            }
        }
    }
    function toggleTroubleshootingInput(show, parentStepIdStr = null) { toggleInputArea('troubleshootingInputArea', 'troubleshootingParentStepId', show, parentStepIdStr); }
    function toggleSolutionInput(show, parentStepIdStr = null) { toggleInputArea('solutionInputArea', 'solutionParentStepId', show, parentStepIdStr); }

    function addStep(parentStepIdStr, type, namePrefix, details) {
        if (!currentProjectDetailsForModal) return;
        const parentStep = findStepById(currentProjectDetailsForModal.steps, parentStepIdStr);
        if (!parentStep) return;
        const newIdStr = generateNewStepId(currentProjectDetailsForModal.steps, parentStepIdStr);
        const now = new Date(), timestamp = now.toISOString(), displayTimestamp = now.toLocaleString('de-DE', { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit'});

        const initialNotes = (details && (details.info || details.description)) || '';
        const newStep = { id_str: newIdStr, parent_id_str: parentStepIdStr, name: `${namePrefix} (${newIdStr})`, status: 'In Progress', type: type, notes: initialNotes, last_updated_by: LOGGED_IN_USER_DISPLAY_NAME, last_updated_at: displayTimestamp, is_expanded: true, history: [{ action: `Erstellt als ${type}`, user: LOGGED_IN_USER_DISPLAY_NAME, timestamp: timestamp }]};

        if (type === 'deviation') {
            newStep.deviation_details = details; 
            newStep.name = `${parentStep.name.split(' (')[0]} - Deviation`;
            parentStep.history = parentStep.history || []; 
            parentStep.history.push({ action: `Deviation ${newIdStr} hinzugefügt`, user: LOGGED_IN_USER_DISPLAY_NAME, timestamp: timestamp });
        } else if (type === 'troubleshooting') {
            newStep.troubleshooting_details = details; newStep.name = `Troubleshooting für ${parentStep.id_str}`;
        } else if (type === 'solution') {
            newStep.solution_details = details; newStep.name = `Solution für ${parentStep.id_str}`; newStep.status = 'Completed';
            const deviationStep = parentStep.type === 'deviation' ? parentStep : findStepById(currentProjectDetailsForModal.steps, parentStep.parent_id_str);
            if (deviationStep && deviationStep.type === 'deviation') {
                deviationStep.status = 'Completed'; deviationStep.history = deviationStep.history || []; deviationStep.history.push({ action: `Geschlossen durch Solution ${newIdStr}`, user: LOGGED_IN_USER_DISPLAY_NAME, timestamp: timestamp });
                getStepChildren(currentProjectDetailsForModal.steps, deviationStep.id_str).forEach(child => { if (child.type === 'troubleshooting') { child.status = 'Completed'; child.history = child.history || []; child.history.push({ action: `Geschlossen durch Solution ${newIdStr}`, user: LOGGED_IN_USER_DISPLAY_NAME, timestamp: timestamp });}});
                const mainParentStep = findStepById(currentProjectDetailsForModal.steps, deviationStep.parent_id_str);
                if (mainParentStep) { if (getStepChildren(currentProjectDetailsForModal.steps, mainParentStep.id_str).filter(d => d.type === 'deviation' && d.status !== 'Completed').length === 0) { mainParentStep.status = 'In Progress'; mainParentStep.history = mainParentStep.history || []; mainParentStep.history.push({ action: `Alle Deviations geschlossen`, user: LOGGED_IN_USER_DISPLAY_NAME, timestamp: timestamp });}}}}
        let insertAtIndex = -1, parentIndex = currentProjectDetailsForModal.steps.indexOf(parentStep); if (parentIndex === -1) return;
        let lastDescendantIndex = parentIndex; function findLastDescendantRecursive(currentParentId) { const children = getStepChildren(currentProjectDetailsForModal.steps, currentParentId); if (children.length === 0) return; const lastChild = children[children.length - 1], lastChildIndex = currentProjectDetailsForModal.steps.indexOf(lastChild); if (lastChildIndex > lastDescendantIndex) lastDescendantIndex = lastChildIndex; findLastDescendantRecursive(lastChild.id_str); } findLastDescendantRecursive(parentStepIdStr); insertAtIndex = lastDescendantIndex + 1;
        currentProjectDetailsForModal.steps.splice(insertAtIndex, 0, newStep); parentStep.is_expanded = true; currentSelectedStepIdStr = newStep.id_str; updateProjectProgress(currentProjectDetailsForModal); 

        saveProjectStatusToBackend(currentProjectDetailsForModal).then(() => {
            renderModalContent(currentProjectDetailsForModal);
        });
    }

    function submitDeviation() {
        const parentStepId = document.getElementById('deviationParentStepId').value;
        const reason = document.getElementById('deviationReasonInput').value;
        const info = document.getElementById('deviationInfoTextarea').value;
        if (!parentStepId || !reason) {
            alert("Bitte geben Sie einen Grund für die Deviation an.");
            return;
        }
        const createIncidentCheckbox = document.getElementById('createHubIncidentCheckbox');
        let incidentCreated = false; 
        if (createIncidentCheckbox && createIncidentCheckbox.checked) {
            if (currentProjectDetailsForModal && currentProjectDetailsForModal.project_code) {
                incidentCreated = true; 
                const baseUrl = 'https://mtuhubtest.service-now.com/esc?id=sc_cat_item&sys_id=6774556a1bc4f910b734ddb89b4bcb33';
                const workOrder = currentProjectDetailsForModal.project_code;
                const shortDescription = encodeURIComponent(reason.trim());
                const description = encodeURIComponent(info.trim());
                const hubUrl = `${baseUrl}&wbs=${workOrder}&desc=${description}&sdesc=${shortDescription}`;
                window.open(hubUrl, '_blank');
            } else {
                alert("Fehler: Projekt-Code konnte nicht gefunden werden, um den Hub-Incident zu erstellen.");
            }
        }
        addStep(parentStepId, 'deviation', 'Deviation', { reason, info, hub_incident_created: incidentCreated });
        toggleDeviationInput(false);
    }

    function submitTroubleshooting() { const parentStepId = document.getElementById('troubleshootingParentStepId').value, attempt = document.getElementById('troubleshootingAttemptInput').value, info = document.getElementById('troubleshootingInfoTextarea').value; if (!parentStepId || !attempt) { alert("Bitte geben Sie eine Maßnahme für das Troubleshooting an."); return; } addStep(parentStepId, 'troubleshooting', 'Troubleshooting', { attempt, info }); toggleTroubleshootingInput(false); }
    function submitSolution() { const parentStepId = document.getElementById('solutionParentStepId').value, description = document.getElementById('solutionDescriptionTextarea').value; if (!parentStepId || !description) { alert("Bitte geben Sie eine Beschreibung für die Solution an."); return; } addStep(parentStepId, 'solution', 'Solution', { description }); toggleSolutionInput(false); }

    function renderStepHistory(project, selectedStep) {
        const historyContainer = document.getElementById('stepHistoryContent'); if (!historyContainer || !selectedStep) return;
        const history = selectedStep.history || []; let historyHtml = '';
        if (history.length > 0) { [...history].reverse().forEach(entry => { const actionText = entry.action || `Status: ${entry.status || 'Unbekannt'}`, user = entry.user || 'System', timestamp = entry.timestamp ? new Date(entry.timestamp).toLocaleString('de-DE', { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit'}) : 'Unbekannt'; historyHtml += `<div class="history-entry"><span class="material-symbols-outlined">history</span><div><p class="text-slate-700 text-sm font-medium">${actionText}</p><p class="text-slate-500 text-xs">Von ${user} am ${timestamp}</p></div></div>`; });
        } else if (selectedStep.last_updated_by && selectedStep.last_updated_at) { historyHtml = ` <div class="history-entry"> <span class="material-symbols-outlined">history</span> <div> <p class="text-slate-700 text-sm font-medium">Status geändert zu <span class="font-semibold text-blue-600">${selectedStep.status.replace("In Progress", "In Bearbeitung")}</span></p> <p class="text-slate-500 text-xs">Von ${selectedStep.last_updated_by} am ${selectedStep.last_updated_at}</p> </div> </div> `;
        } else { historyHtml = '<p class="text-slate-500 text-sm">Keine Historie für diesen Schritt verfügbar.</p>'; }
        historyContainer.innerHTML = historyHtml;
    }
    function toggleStepHistory() {
        isHistoryVisible = !isHistoryVisible; const historyContainer = document.getElementById('stepHistoryContainer'), historyButtonText = document.getElementById('viewHistoryButtonText'), historyButtonIcon = document.getElementById('viewHistoryButtonIcon');
        if (historyContainer && historyButtonText && historyButtonIcon) { if (isHistoryVisible) { historyContainer.style.display = 'block'; historyButtonText.textContent = 'Hide History'; historyButtonIcon.textContent = 'visibility_off'; if (currentProjectDetailsForModal && currentSelectedStepIdStr) { const selectedStep = findStepById(currentProjectDetailsForModal.steps, currentSelectedStepIdStr); if (selectedStep) renderStepHistory(currentProjectDetailsForModal, selectedStep); } } else { historyContainer.style.display = 'none'; historyButtonText.textContent = 'View History'; historyButtonIcon.textContent = 'history'; } }
    }
    function toggleStepExpansion(stepIdStr) {
        if (!currentProjectDetailsForModal) return; const step = findStepById(currentProjectDetailsForModal.steps, stepIdStr);
        if (step && (step.type === 'deviation' || getStepChildren(currentProjectDetailsForModal.steps, step.id_str).length > 0)) { step.is_expanded = !step.is_expanded; renderModalContent(currentProjectDetailsForModal); if (currentSelectedStepIdStr === stepIdStr) { const detailSectionContainer = document.querySelector('.lg\\:w-3\\/5'); if (detailSectionContainer) detailSectionContainer.innerHTML = renderStepDetailSection(currentProjectDetailsForModal, currentSelectedStepIdStr);}}
    }
    function findFirstOpenDescendantDeviation(startStepId, allSteps) {
        const children = getStepChildren(allSteps, startStepId);
        for (const child of children) {
            if (child.type === 'deviation' && child.status !== 'Completed' && child.status !== 'N/R') {
                return child; 
            }
            const foundInChildren = findFirstOpenDescendantDeviation(child.id_str, allSteps);
            if (foundInChildren) {
                return foundInChildren; 
            }
        }
        return null; 
    }

    function hasOpenDescendantDeviations(startStepId, allSteps) {
        return findFirstOpenDescendantDeviation(startStepId, allSteps) !== null;
    }
    function renderModalContent(project) {
        let stepsHtml = ''; const mainSteps = project.steps.filter(s => s.parent_id_str === null).sort((a,b) => parseFloat(a.id_str) - parseFloat(b.id_str));
        function renderStepRecursive(step, level, projectSteps, isLastInLevel) {
            let html = '', statusText = '', statusColorClass = '', stepNameColorClass = 'text-slate-500', connectorAboveColorClass = 'bg-slate-300', connectorBelowColorClass = 'bg-slate-300';
            let isSelectedClass = (step.id_str === currentSelectedStepIdStr) ? 'selected-step' : '';
            const indentMargin = level * 25;

            const children = getStepChildren(projectSteps, step.id_str), hasChildren = children.length > 0;
            let expansionIcon = '';
            if (hasChildren || step.type === 'deviation') {
                expansionIcon = step.is_expanded ? 'remove_circle_outline' : 'add_circle_outline';
            }

            const iconHtmlContent = `<span class="step-number">${step.id_str}</span>`;

            if (step.status === 'Completed') { statusText = 'Abgeschlossen'; statusColorClass = 'bg-green-500 text-white'; stepNameColorClass = 'text-slate-900'; connectorBelowColorClass = 'bg-green-500';}
            else if (step.status === 'In Progress') { statusText = 'In Bearbeitung'; statusColorClass = 'bg-blue-600 text-white ring-4 ring-blue-200'; stepNameColorClass = 'text-blue-700';}
            else if (step.status === 'On Hold') { statusText = 'Angehalten'; statusColorClass = 'bg-yellow-500 text-black'; stepNameColorClass = 'text-yellow-700';}
            else if (step.status === 'N/R') { statusText = 'Nicht benötigt'; statusColorClass = 'bg-slate-400 text-white'; stepNameColorClass = 'text-slate-500 line-through';}
            else { statusText = 'Anstehend'; statusColorClass = 'border-2 border-slate-400 text-slate-400';}

            if (level > 0) { const parentStep = findStepById(projectSteps, step.parent_id_str); if (parentStep && parentStep.status === 'Completed') connectorAboveColorClass = 'bg-green-500'; } else { const stepIndex = mainSteps.indexOf(step); if (stepIndex > 0 && mainSteps[stepIndex-1].status === 'Completed') connectorAboveColorClass = 'bg-green-500';}

            let stepLastUpdatedHtml = '';
            if (step.last_updated_by && step.last_updated_at) stepLastUpdatedHtml = `<p class="text-slate-500 text-xs mt-1">Von ${step.last_updated_by} am ${step.last_updated_at}</p>`;
            else if (step.status === 'Upcoming') stepLastUpdatedHtml = `<p class="text-slate-500 text-xs mt-1">Noch nicht gestartet</p>`;

            let stepDisplayName = step.name.replace(/\s*\(\s*\d+(\.\d+)*\s*\)$/, '').trim();
            let detailTextHtml = '';
            if (step.type === 'deviation' && step.deviation_details && step.deviation_details.reason) {
                stepDisplayName = `${step.name.split(' (')[0]}`;
                detailTextHtml = `<p class="text-sm italic text-slate-600 mt-0.5">${step.deviation_details.reason}</p>`;
            } else if (step.type === 'troubleshooting' && step.troubleshooting_details && step.troubleshooting_details.attempt) {
                stepDisplayName = `${step.name.split(' (')[0]}`;
                detailTextHtml = `<p class="text-sm italic text-slate-600 mt-0.5">${step.troubleshooting_details.attempt}</p>`;
            } else if (step.type === 'solution') {
                stepDisplayName = `${step.name.split(' (')[0]}`;
                detailTextHtml = `<p class="text-sm italic text-slate-600 mt-0.5">Lösung implementiert</p>`;
            }

            let hubIncidentHtml = '';
            if (step.type === 'deviation' && step.deviation_details && step.deviation_details.hub_incident_created) {
                hubIncidentHtml = `<p class="text-xs text-blue-700 font-semibold mt-1.5 flex items-center gap-1 bg-blue-100 px-2 py-1 rounded"><span class="material-symbols-outlined" style="font-size: 14px;">open_in_new</span>Incident in MTU.Hub</p>`;
            }

            let downloadIconHtml = '', deleteIconHtml = '', detailPaddingClass = 'pr-2';
            if (step.has_document) {
                detailPaddingClass = 'pr-8';
                const projectIdForUrl = `${project.project_code}_${project.engine_serial}`;
                const downloadLink = `{{ url_for('download_document_route', project_id='PROJECT_ID_PLACEHOLDER', step_name='STEP_NAME_PLACEHOLDER') }}`.replace('PROJECT_ID_PLACEHOLDER', encodeURIComponent(projectIdForUrl)).replace('STEP_NAME_PLACEHOLDER', encodeURIComponent(step.name));
                downloadIconHtml = `<a href="${downloadLink}" class="absolute bottom-2 right-2 text-slate-400 hover:text-blue-600 p-1 rounded-full transition-colors" title="Dokument herunterladen" onclick="event.stopPropagation();"><span class="material-symbols-outlined" style="font-size: 20px;">download</span></a>`;
            }
            if (step.type !== 'main') {
                detailPaddingClass = 'pr-8';
                deleteIconHtml = `<button class="absolute top-2 right-2 text-slate-400 hover:text-red-600 p-1 rounded-full transition-colors" title="Diesen Schritt löschen" onclick="event.stopPropagation(); deleteStep('${step.id_str}');"><span class="material-symbols-outlined" style="font-size: 18px;">delete_outline</span></button>`;
            }
            let blockedByDeviationHtml = '';
            if (step.type === 'main') {
                const firstOpenDeviation = findFirstOpenDescendantDeviation(step.id_str, projectSteps);
                if (firstOpenDeviation) {
                    blockedByDeviationHtml = `<p class="text-xs text-red-600 font-semibold mt-1 flex items-center gap-1"><span class="material-symbols-outlined" style="font-size: 14px;">error</span>Offene Deviation in <a href="#" class="underline" onclick="event.preventDefault(); selectStepInModal('${firstOpenDeviation.id_str}');">${firstOpenDeviation.id_str}</a></p>`;
                }
            }
            const projectStepDetailsHtml = `<div class="project-step-details relative ${detailPaddingClass}">${deleteIconHtml} <p class="text-base font-semibold leading-normal ${stepNameColorClass}">${stepDisplayName}</p>${detailTextHtml}${hubIncidentHtml}<p class="text-sm font-medium leading-normal mt-1 ${step.status === 'Completed' ? 'text-green-600' : (step.status === 'In Progress' ? 'text-blue-600' : (step.status === 'On Hold' ? 'text-yellow-600' : 'text-slate-500'))}">${statusText}</p>${blockedByDeviationHtml} ${stepLastUpdatedHtml}${downloadIconHtml}</div>`;
            const extraGridClasses = (step.type === 'deviation' && step.status !== 'Completed' && step.status !== 'N/R') ? 'bg-red-50 border border-red-200 rounded-md !pl-4' : '';
            html += `<div class="project-step-grid ${isSelectedClass} ${extraGridClasses}" style="margin-left: ${indentMargin}px;" onclick="selectStepInModal('${step.id_str}')"><div class="project-step-icon-col ${level === 0 && projectSteps.indexOf(step) === 0 && mainSteps.indexOf(step) === 0 ? 'first-step' : ''}">${ (level > 0 || mainSteps.indexOf(step) > 0 || (projectSteps.indexOf(step) > 0 && findStepById(projectSteps, step.parent_id_str))) ? `<div class="project-step-icon-connector ${connectorAboveColorClass}" style="height: 0.25rem;"></div>` : ''}<div class="project-step-icon-circle ${statusColorClass}" ${ (hasChildren || step.type === 'deviation') ? `onclick="event.stopPropagation(); toggleStepExpansion('${step.id_str}')"` : ''} style="cursor: ${ (hasChildren || step.type === 'deviation') ? 'pointer' : 'default'};">${expansionIcon ? `<span class="material-symbols-outlined" style="font-size: 16px; position: absolute; top: -6px; right: -6px; background: white; border-radius: 50%; color: #555; line-height:1; padding:1px;">${expansionIcon}</span>` : ''}${iconHtmlContent}</div>${ (!isLastInLevel || (hasChildren && step.is_expanded)) ? `<div class="project-step-icon-connector ${connectorBelowColorClass}"></div>` : ''}</div>${projectStepDetailsHtml}</div>`;
            if (step.is_expanded && hasChildren) {
                children.forEach((child, index) => {
                    html += renderStepRecursive(child, level + 1, projectSteps, index === children.length - 1);
                });
            }
            return html;
        }
        mainSteps.forEach((mainStep, index) => { stepsHtml += renderStepRecursive(mainStep, 0, project.steps, index === mainSteps.length -1); });
        const stepDetailHtml = currentSelectedStepIdStr ? renderStepDetailSection(project, currentSelectedStepIdStr) : '<div class="lg:w-3/5 border-t lg:border-t-0 lg:border-l border-slate-200 lg:pl-8 pt-6 lg:pt-0"><p class="text-slate-500">Bitte einen Schritt auswählen, um Details anzuzeigen.</p></div>';
        const modalHtml = ` <div class="pb-6 border-b border-slate-200"> <div class="flex flex-wrap justify-between items-start gap-x-4 gap-y-1 mb-1"> <h1 class="text-slate-900 tracking-tight text-2xl md:text-3xl font-bold leading-tight"> ${project.name} </h1> <div class="flex items-center gap-2 text-sm text-slate-600"> <span class="material-symbols-outlined text-slate-500 text-lg">calendar_today</span> <span>Fällig bis: ${project.due_date_display || 'N/A'}</span> </div> </div> <div class="text-slate-500 text-base"> ${project.engine_serial && project.engine_serial !== "N/A" ? `<span>SN: ${project.engine_serial}</span>` : ''} ${project.engine_serial && project.engine_serial !== "N/A" && project.engine_type ? `<span class="mx-1">|</span>` : ''} <span>Typ: ${project.engine_type || 'N/A'}</span> </div> </div> <div class="py-6"> <div class="flex gap-4 justify-between items-center mb-2"> <p class="text-slate-800 text-base font-medium leading-normal">Fortschritt</p> <p class="text-blue-600 text-sm font-semibold leading-normal">${project.progress_percent}%</p> </div> <div class="rounded-full bg-slate-200 h-3 overflow-hidden"> <div class="h-full rounded-full bg-blue-600 transition-all duration-500 ease-out" style="width: ${project.progress_percent}%;"></div> </div> </div> <div class="flex flex-col lg:flex-row gap-8 pt-6"> <div class="lg:w-2/5"> <div class="flex justify-between items-center pb-4"> <h2 class="text-slate-900 text-xl md:text-2xl font-bold leading-tight tracking-[-0.015em]">Projektschritte</h2> <button id="viewHistoryButton" onclick="toggleStepHistory()" class="flex items-center gap-1 text-sm text-blue-600 hover:text-blue-700 font-medium"> <span id="viewHistoryButtonIcon" class="material-symbols-outlined text-base">history</span> <span id="viewHistoryButtonText">View History</span> </button> </div> <div class="space-y-0">${stepsHtml}</div> <div id="stepHistoryContainer" class="hidden"> <h3 class="text-slate-800 text-lg font-semibold mb-3 mt-2">Schritt Historie</h3> <div id="stepHistoryContent" class="space-y-3 max-h-60 overflow-y-auto pr-2"></div> </div> </div> <div class="lg:w-3/5 border-t lg:border-t-0 lg:border-l border-slate-200 lg:pl-8 pt-6 lg:pt-0"> ${stepDetailHtml} </div> </div> `;
        projectStatusModalContentContainer.innerHTML = modalHtml; const historyContainer = document.getElementById('stepHistoryContainer'); if (historyContainer) historyContainer.style.display = isHistoryVisible ? 'block' : 'none';
        const selectedStepForDetails = findStepById(project.steps, currentSelectedStepIdStr);
        if (selectedStepForDetails) { const devArea = document.getElementById('deviationInputArea'), troubArea = document.getElementById('troubleshootingInputArea'), solArea = document.getElementById('solutionInputArea'); if (devArea && devArea.style.display === 'block' && selectedStepForDetails.type !== 'main') toggleDeviationInput(false); if (troubArea && troubArea.style.display === 'block' && selectedStepForDetails.type !== 'deviation') toggleTroubleshootingInput(false); if (solArea && solArea.style.display === 'block' && (selectedStepForDetails.type !== 'deviation' && selectedStepForDetails.type !== 'troubleshooting')) toggleSolutionInput(false);
        } else { toggleDeviationInput(false); toggleTroubleshootingInput(false); toggleSolutionInput(false); }
    }

    window.onload = () => {
        toggleViewBtn = document.getElementById('toggleViewBtn'); refreshBtn = document.getElementById('refreshBtn');
        if (userProfileIcon) userProfileIcon.addEventListener('click', () => { window.location.href = "{{ url_for('serve_my_qualifications_page') }}"; });
        if (toggleViewBtn) {
            toggleViewBtn.addEventListener('click', () => {
                currentDisplayWeeks = (currentDisplayWeeks === 2) ? 1 : 2;
                updateToggleViewButtonText();
                fetchAndRenderTimeline(projectSearchInput.value); 
            });
        }
        if (refreshBtn) refreshBtn.addEventListener('click', () => fetchAndRenderTimeline(projectSearchInput.value));

        // ### FIX TEIL 3: Event-Listener für die Suche korrigieren ###
        if (projectSearchInput) projectSearchInput.addEventListener('input', (event) => { 
                const searchTerm = event.target.value; 
                
                // --- NEU: Prüfen, in welcher Ansicht wir sind ---
                if (isArchiveView) {
                    renderArchiveTable(); // Filtere das Archiv
                } else {
                    // Das ist der alte Code für die Live-Timeline
                    renderTimeline(
                        filterProjects(allFetchedProjects, searchTerm), 
                        savedViewStartDate,
                        currentDisplayWeeks * 7, 
                        currentDisplayWeeks * 14,
                        savedAssignments,
                        savedEmployeeMap
                    ); 
                    renderTodaysSummary(allFetchedTodaysSummary, searchTerm); 
                }
        });

        if (closeModalButton) closeModalButton.addEventListener('click', closeProjectStatusModal);
        if (modalBackdrop) modalBackdrop.addEventListener('click', (event) => { if (event.target === modalBackdrop) closeProjectStatusModal(); });
        document.addEventListener('keydown', (event) => { if (event.key === 'Escape' && projectStatusModal.style.display !== 'none') closeProjectStatusModal(); });
        updateToggleViewButtonText(); 
        fetchAndRenderTimeline();
    };
</script>
</body></html>
"""

PERSONAL_QUALIFICATIONS_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>MBET - Meine Qualifikationen</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <style>
        body { font-family: 'Inter', sans-serif; }
        .material-icons-outlined, .material-symbols-outlined { font-weight: normal; font-style: normal; line-height: 1; letter-spacing: normal; text-transform: none; display: inline-block; white-space: nowrap; word-wrap: normal; direction: ltr; -webkit-font-smoothing: antialiased; }

        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center justify-between whitespace-nowrap px-6 py-4 bg-neutral-100">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800"></h1>
        </header>

        <div class="p-4 sm:p-6 lg:p-8">
            <div class="mx-auto max-w-7xl">
                <div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-8">
                    <h1 class="text-slate-900 text-3xl md:text-4xl font-bold leading-tight tracking-tight">Willkommen, {{ user_full_name }}!</h1>
                    <div class="flex items-center gap-2 px-4 py-2 rounded-lg bg-blue-50 border border-blue-200">
                        <span class="material-symbols-outlined text-blue-600">workspace_premium</span>
                        <p class="text-blue-700 text-sm font-medium">Level: {{ user_level }}</p>
                    </div>
                </div>

                <!-- NEUES GRID-LAYOUT FÜR QUALIFIKATIONEN UND ANWESENHEIT -->
                <div class="grid grid-cols-1 lg:grid-cols-3 gap-8">

                    <!-- Spalte für Qualifikationen und Trainings (nimmt 2/3 des Platzes) -->
                    <div class="lg:col-span-2 space-y-10">
                        <section>
                            <h2 class="text-slate-800 text-2xl font-semibold leading-snug tracking-tight mb-4 pb-2 border-b border-slate-300">Aktuelle Qualifikationen</h2>
                            <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                                {% if current_qualifications %}
                                    {% for qual in current_qualifications %}
                                    <div class="bg-white p-6 rounded-lg shadow-md border border-slate-200 hover:shadow-lg transition-shadow">
                                        <div class="flex items-center gap-3 mb-3"><span class="material-symbols-outlined text-2xl text-green-500">verified_user</span><h3 class="text-slate-900 text-lg font-semibold">{{ qual.name }}</h3></div>
                                        <p class="text-slate-600 text-sm mb-1">Abgeschlossen am: {{ qual.end_date or 'N/A' }}</p>
                                        {% if qual.cert_path %}
                                            <a href="{{ url_for('download_certificate_route', employee_id=user_id, filename=qual.cert_path) }}" target="_blank" class="mt-4 inline-flex items-center gap-1 text-sm font-medium text-blue-600 hover:text-blue-700">
                                                Zertifikat ansehen <span class="material-symbols-outlined text-base">arrow_forward</span>
                                            </a>
                                        {% else %}
                                            <p class="mt-4 text-sm text-gray-400 italic">Kein Zertifikat hinterlegt.</p>
                                        {% endif %}
                                    </div>
                                    {% endfor %}
                                {% else %}
                                    <p class="text-slate-600 text-sm col-span-full">Keine aktuellen Qualifikationen für dich hinterlegt.</p>
                                {% endif %}
                            </div>
                        </section>
                        <section>
                            <h2 class="text-slate-800 text-2xl font-semibold leading-snug tracking-tight mb-4 pb-2 border-b border-slate-300">Laufende Trainings</h2>
                            <div class="space-y-4">
                                {% if ongoing_trainings %}
                                    {% for training in ongoing_trainings %}
                                    <div class="bg-white p-6 rounded-lg shadow-md border border-slate-200 hover:shadow-lg transition-shadow">
                                        <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-3">
                                            <div><div class="flex items-center gap-3 mb-2"><span class="material-symbols-outlined text-2xl {% if training.status == 'TT' %}text-red-500{% else %}text-blue-500{% endif %}">model_training</span><h3 class="text-slate-900 text-lg font-semibold">{{ training.name }}</h3></div><p class="text-slate-600 text-sm">Gestartet am: {{ training.start_date or 'N/A' }}</p></div>
                                            <div class="w-full sm:w-auto"><p class="text-slate-800 text-sm font-medium mb-1 text-right">Fortschritt: {{ training.progress }}%</p><div class="rounded-full bg-slate-200 h-2.5 w-full sm:w-40 overflow-hidden"><div class="h-full rounded-full {% if training.status == 'TT' %}bg-red-500{% else %}bg-blue-500{% endif %}" style="width: {{ training.progress }}%;"></div></div></div>
                                        </div>
                                    </div>
                                    {% endfor %}
                                {% else %}
                                    <p class="text-slate-600 text-sm">Keine laufenden Trainings für dich hinterlegt.</p>
                                {% endif %}
                            </div>
                        </section>
                    </div>

                    <!-- Anwesenheits-Spalte -->
                    <div class="lg:col-span-1">
                        <section class="bg-white p-6 rounded-lg shadow-md border border-slate-200 sticky top-8">
                            <div class="flex items-center gap-3 mb-4 pb-3 border-b border-slate-200">
                                <span class="material-icons-outlined text-blue-600">event_available</span>
                                <h2 class="text-xl font-semibold text-slate-800">Meine Anwesenheit</h2>
                            </div>
                            <div class="space-y-4">
                                <!-- Mock-up für diese Woche -->
                                <div>
                                    <h3 class="text-sm font-bold text-slate-600 mb-2">Diese Woche (KW 43)</h3>
                                    <ul class="space-y-1.5 text-sm text-slate-700 list-disc list-inside">
                                        <li><span class="font-semibold">Mo, 20.10:</span> Frühschicht</li>
                                        <li><span class="font-semibold">Di, 21.10:</span> Frühschicht</li>
                                        <li><span class="font-semibold">Mi, 22.10:</span> <span class="text-blue-600 font-medium">Urlaub</span></li>
                                        <li><span class="font-semibold">Do, 23.10:</span> Frühschicht</li>
                                        <li><span class="font-semibold">Fr, 24.10:</span> Frühschicht</li>
                                    </ul>
                                </div>
                                <!-- Mock-up für nächste Woche -->
                                <div class="pt-4 border-t border-slate-100">
                                    <h3 class="text-sm font-bold text-slate-600 mb-2">Nächste Woche (KW 44)</h3>
                                    <ul class="space-y-1.5 text-sm text-slate-700 list-disc list-inside">
                                        <li><span class="font-semibold">Mo, 27.10:</span> Spätschicht</li>
                                        <li><span class="font-semibold">Di, 28.10:</span> Spätschicht</li>
                                        <li><span class="font-semibold">Mi, 29.10:</span> Spätschicht</li>
                                        <li><span class="font-semibold">Do, 30.10:</span> <span class="text-orange-600 font-medium">Training</span></li>
                                        <li><span class="font-semibold">Fr, 31.10:</span> Spätschicht</li>
                                    </ul>
                                </div>
                                <!-- Mock-up für zukünftige Urlaube -->
                                <div class="pt-4 border-t border-slate-100">
                                    <h3 class="text-sm font-bold text-slate-600 mb-2">Zukünftige Abwesenheiten</h3>
                                    <ul class="space-y-1.5 text-sm text-slate-700 list-disc list-inside">
                                        <li><span class="font-semibold text-blue-600">22.12. - 26.12.</span> (Weihnachten)</li>
                                        <li><span class="font-semibold text-blue-600">31.12.</span> (Silvester)</li>
                                    </ul>
                                </div>
                                <div class="pt-4 text-center">
                                    <a href="{{ url_for('serve_employee_presence_page') }}" class="text-sm font-medium text-blue-600 hover:text-blue-800">
                                        Zur vollständigen Übersicht &rarr;
                                    </a>
                                </div>
                            </div>
                        </section>
                    </div>
                </div>
            </div>
        </div>
    </main>
</div>
<script>
    // --- Logik für die einklappbare Sidebar ---
    document.addEventListener('DOMContentLoaded', () => {
        const sidebar = document.getElementById('sidebar');
        const sidebarToggleBtn = document.getElementById('sidebar-toggle');
        if (sidebar && sidebarToggleBtn) {
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
            const applyState = (isCollapsed) => {
                if (isCollapsed) {
                    sidebar.classList.add('is-collapsed');
                    toggleBtnIcon.textContent = 'menu';
                } else {
                    sidebar.classList.remove('is-collapsed');
                    toggleBtnIcon.textContent = 'chevron_left';
                }
            };
            sidebarToggleBtn.addEventListener('click', () => {
                const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
                localStorage.setItem('sidebarCollapsed', isNowCollapsed);
                applyState(isNowCollapsed);
            });
            const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
            applyState(savedState);
        }
    });
</script>
</body>
</html>
"""

TEST_CELL_MANAGEMENT_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <title>MBET - Testzellen Verwaltung</title>
    <link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?display=swap&family=Inter:wght@400;500;600;700&family=Noto+Sans:wght@400;500;700;900">
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
<style>
    body { font-family: 'Inter', "Noto Sans", sans-serif; }
    .timeline-table tbody tr { height: 65px; }
    .timeline-table thead th { border-bottom: 1px solid #e5e7eb; }
    .timeline-table tbody td { border-bottom: 1px solid #e5e7eb; border-right: 1px solid #e5e7eb; min-width: 45px; width: 45px; max-width: 45px; padding: 2px; vertical-align: middle; position: relative; }
    .timeline-table tbody td:last-child { border-right: none; }
    .weekend-bg { background-color: #f3f4f6; }
    .current-timeslot-highlight { background-color: rgba(239, 68, 68, 0.1) !important; }
    .material-icons-outlined { font-weight: normal; font-style: normal; font-size: 24px; line-height: 1; letter-spacing: normal; text-transform: none; display: inline-block; white-space: nowrap; word-wrap: normal; direction: ltr; -webkit-font-smoothing: antialiased; }
    .modal { display: none; }
    .modal.is-open { display: flex; }
    .event-type-btn.bg-blue-600 { background-color: #2563eb !important; color: white !important; }
    .event-type-btn.bg-blue-600:hover { background-color: #1d4ed8 !important; }
    .cell-card { transition: transform 0.2s ease, box-shadow 0.2s ease; }
    .cell-card:hover { transform: translateY(-4px); box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1); }
    @keyframes breathe { 0%, 100% { transform: scale(1); box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1); } 50% { transform: scale(1.02); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1); } }
    .cell-card.operational-pulse { animation: breathe 3s ease-in-out infinite; }
    .cell-card.operational-pulse:hover { animation: none; }
    .analyse-toggle-btn.active {
        background-color: #e0f2fe; /* war #eef2ff (indigo-100) -> jetzt sky-100 */
        color: #0369a1;            /* war #4338ca (indigo-700) -> jetzt sky-700 */
        font-weight: 600;
    }
    #sidebar {
        transition: width 0.3s ease-in-out;
    }
    #sidebar.is-collapsed {
        width: 5rem; /* 80px */
    }
    #sidebar.is-collapsed .sidebar-label,
    #sidebar.is-collapsed .sidebar-title,
    #sidebar.is-collapsed .user-info {
        display: none;
    }
    #sidebar.is-collapsed .sidebar-link,
    #sidebar.is-collapsed .sidebar-header,
    #sidebar.is-collapsed .user-profile {
        justify-content: center;
    }
    #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
    #sidebar.is-collapsed .sidebar-header .material-icons-outlined {
        margin-right: 0;
    }
    .sidebar-label {
        transition: opacity 0.2s ease-in-out;
    }
    .sidebar-link.active {
        background-color: #e5e7eb; /* war #eef2ff (indigo-100) -> jetzt sky-100 */
        color: #0c4a6e;            /* war #4338ca (indigo-700) -> jetzt sky-700 */
        font-weight: 600;
    }
    .sidebar-link.active .material-icons-outlined {
        color: #0c4a6e; /* war #4f46e5 (indigo-600) -> jetzt sky-600 */
    }
    .sidebar-separator {
        display: none;
    }
    #sidebar.is-collapsed .sidebar-separator {
        display: block;
        height: 1px;
        background-color: #e2e8f0;
        margin: 0.5rem 0.75rem;
    }
    .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
    .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
</style>
</head>
<body class="bg-white">

<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

<!-- Hauptinhalt -->
    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center sticky top-0 z-50 justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Testzellen-Cockpit</h1>
        </header>

        <div class="p-4 sm:p-6 lg:p-8">

            <!-- Oberer Teil: Button und Karten -->
            <div class="mb-6 flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
                <div>
                    <!-- Titel wurde entfernt, wie gewünscht -->
                </div>
                <!-- NEUE BOX / WRAPPER FÜR DEN BUTTON -->
                <div>
                    {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) %}
                    <button id="newTestCellBtn" class="flex-shrink-0 h-12 w-12 flex items-center justify-center rounded-full bg-sky-900 text-white shadow-sm ring-0 ring-inset ring-slate-200 hover:bg-slate-200 transition-colors" title="Neue Testzelle erstellen">
                        <span class="material-icons-outlined">add</span>
                    </button>
                    {% endif %}
                </div>
            </div>
            <div id="cellCardsContainer" class="mb-24 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
                <!-- Inhalt wird per JS geladen -->
            </div>

            <!-- Steuerungsleiste -->
            <div class="mb-6 flex items-center justify-between">
                <nav id="viewTabs" class="-mb-px flex space-x-8 border-b border-slate-200 w-full">
                    <button data-view="timeline" class="tab-btn whitespace-nowrap border-b-2 px-1 pb-3 text-sm font-medium border-blue-600 text-blue-600">Timeline</button>
                    {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) %}
                    <button data-view="analyse" class="tab-btn whitespace-nowrap border-b-2 px-1 pb-3 text-sm font-medium border-transparent text-slate-500 hover:border-slate-300 hover:text-slate-700">Analyse</button>
                    {% endif %}
                </nav>
                <div id="timelineNav" class="flex items-center gap-3 flex-shrink-0 pb-3 ml-6">
                    <button id="jumpToTodayBtn" class="rounded-full bg-white px-4 py-3 text-sm font-semibold text-slate-700 shadow-sm ring-1 ring-inset ring-slate-200 hover:bg-slate-50" title="Zur aktuellen Woche springen">
                        Heute
                    </button>
                    <div class="flex items-center rounded-full ring-1 ring-slate-200 bg-white shadow-sm">
                        <button id="prevWeekBtn" class="p-2 text-slate-500 hover:bg-slate-100 rounded-l-full disabled:opacity-50">
                            <span class="material-icons-outlined">chevron_left</span>
                        </button>
                        <span id="timelineRangeLabel" class="text-sm font-medium text-slate-700 px-4 w-32 text-center border-l border-r border-slate-200">
                            2 Wochen
                        </span>
                        <button id="nextWeekBtn" class="p-2 text-slate-500 hover:bg-slate-100 rounded-r-full disabled:opacity-50">
                            <span class="material-icons-outlined">chevron_right</span>
                        </button>
                    </div>
                </div>
            </div>

            <!-- Unterer Teil: Timeline oder Analyse (jetzt ohne Kachel) -->
            <div id="timelineContainer" class="bg-white rounded-lg">
                <!-- Inhalt wird per JS geladen -->
            </div>
            <div id="analyseContainer" style="display: none;" class="bg-white rounded-lg p-6">
                <div class="mb-6">
                    <h2 class="text-2xl font-bold text-slate-900 mb-2">Auslastungsanalyse 2026</h2>
                    <p class="text-sm text-slate-600">Übersicht der Auslastung basierend auf allen Schichten des aktuellen Kalenderjahres.</p>
                </div>
                <div class="mb-6">
                    <div class="inline-flex rounded-md shadow-sm bg-slate-100 p-1">
                        <button id="analyseViewOverviewBtn" type="button" class="analyse-toggle-btn active rounded-md px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-white">Übersicht</button>
                        <button id="analyseViewTrendBtn" type="button" class="analyse-toggle-btn rounded-md px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-white">Verlauf</button>
                        <button id="analyseViewEngineBtn" type="button" class="analyse-toggle-btn rounded-md px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-white">Engine</button>
                    </div>
                </div>
                <div id="analyseChartsContainer" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"></div>
            </div>
        </div>
    </main>
</div>
<!-- Test Cell Modal (Scrollbar Fix) -->
<div id="testCellModal" class="modal fixed inset-0 z-50 items-center justify-center bg-gray-600 bg-opacity-75 p-4 hidden">
    <!-- WICHTIG: flex flex-col und max-h-[90vh] hinzugefügt -->
    <div class="relative w-full max-w-lg transform rounded-lg bg-white shadow-xl overflow-hidden flex flex-col max-h-[90vh]">
        
        <!-- Header (Fixiert) -->
        <div class="flex items-center justify-between p-6 border-b border-gray-200 flex-shrink-0 bg-white z-10">
            <h3 id="testCellModalTitle" class="text-xl font-semibold text-gray-900"></h3>
            <div class="flex items-center space-x-2">
                <button id="editTestCellBtn" type="button" class="p-2 rounded-md hover:bg-gray-100 text-blue-600" title="Bearbeiten">
                    <span class="material-icons-outlined">edit</span>
                </button>
                <button id="deleteTestCellBtnHeader" type="button" class="p-2 rounded-md hover:bg-gray-100 text-red-500" title="Löschen">
                    <span class="material-icons-outlined">delete</span>
                </button>
                <button id="closeTestCellModalBtn" class="p-2 rounded-md hover:bg-gray-100 text-gray-400 hover:text-gray-500">
                    <span class="material-icons-outlined">close</span>
                </button>
            </div>
        </div>

        <!-- Body (Scrollbar) -->
        <!-- WICHTIG: overflow-y-auto und flex-grow hinzugefügt -->
        <div class="p-6 space-y-6 overflow-y-auto custom-scrollbar flex-grow">
            <input type="hidden" id="originalTestCellId">
            <div>
                <label for="testCellIdInput" class="block text-sm font-medium text-gray-900">Testzellen ID<span class="text-red-500">*</span></label>
                <input type="text" id="testCellIdInput" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" placeholder="z.B. 1, A, Zelle-01">
            </div>
            <div>
                <label for="testCellNameInput" class="block text-sm font-medium text-gray-900">Testzellen Name<span class="text-red-500">*</span></label>
                <input type="text" id="testCellNameInput" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" placeholder="z.B. Prüfstand 4.1">
            </div>
            
            <!-- Engine Types -->
            <div>
                <label class="block text-sm font-medium text-gray-900">Kompatible Triebwerkstypen</label>
                <div id="engineTypesContainer" class="mt-2 space-y-2"></div>
                <button id="addEngineTypeBtn" class="mt-2 text-sm font-medium text-blue-600 hover:text-blue-500" type="button">+ Weiteren Typ hinzufügen</button>
            </div>

            <!-- NEU: Abhängigkeiten (Phase 2) -->
            <div class="pt-4 border-t border-gray-100">
                <label class="block text-sm font-medium text-gray-900">Abhängige Betriebseinheiten (Infrastruktur)</label>
                <p class="text-xs text-gray-500 mb-2">Wenn eine dieser Anlagen gesperrt ist (Unit Blocker), wird diese Testzelle automatisch gesperrt.</p>
                
                <div id="requiredUnitsContainer" class="mt-2 space-y-2"></div>
                
                <div class="flex gap-2 mt-2">
                    <input list="modalFacilityUnitList" id="addRequiredUnitInput" class="block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" placeholder="Anlage suchen (z.B. 1.1)...">
                    <button id="btnAddRequiredUnit" type="button" class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700">
                        Hinzufügen
                    </button>
                </div>
                <datalist id="modalFacilityUnitList"></datalist>
            </div>
        </div>

        <!-- Footer (Fixiert) -->
        <div class="flex justify-end items-center p-6 bg-gray-50 border-t border-gray-200 rounded-b-lg flex-shrink-0 z-10">
            <button id="cancelTestCellModalBtn" type="button" class="px-4 py-2 text-sm font-medium rounded-md border border-gray-300 text-gray-900 hover:bg-gray-100 mr-3">Abbrechen</button>
            <button id="saveTestCellBtn" type="button" class="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700"></button>
        </div>
    </div>
</div>

<div id="eventModal" class="modal fixed inset-0 z-50 items-center justify-center bg-gray-600 bg-opacity-75 p-4"><div class="relative w-full max-w-lg transform rounded-lg bg-white shadow-xl overflow-hidden"><div class="flex items-center justify-between p-6 border-b border-gray-200"><h3 id="eventModalTitle" class="text-xl font-semibold text-gray-900"></h3><div class="flex items-center space-x-2"><button id="editEventBtn" type="button" class="p-2 rounded-md hover:bg-gray-100 text-blue-600" title="Bearbeiten"><span class="material-icons-outlined">edit</span></button><button id="deleteEventBtn" type="button" class="p-2 rounded-md hover:bg-gray-100 text-red-500" title="Löschen"><span class="material-icons-outlined">delete</span></button><button id="closeEventModalBtn" class="p-2 rounded-md hover:bg-gray-100 text-gray-400 hover:text-gray-500"><span class="material-icons-outlined">close</span></button></div></div><div class="p-6 space-y-6"><input type="hidden" id="eventModalEventId"><input type="hidden" id="eventModalCellId"><div><label class="block text-sm font-medium text-gray-900 mb-2">Typ</label><div id="eventTypeButtons" class="flex space-x-2"><button data-type="Maintenance" class="event-type-btn px-4 py-2 text-sm font-medium rounded-md bg-gray-100 text-gray-900 hover:bg-gray-200">Wartung</button><button data-type="Blocked" class="event-type-btn px-4 py-2 text-sm font-medium rounded-md bg-gray-100 text-gray-900 hover:bg-gray-200">Blockade</button></div></div><div><label for="eventTitleInput" class="block text-sm font-medium text-gray-900">Titel<span class="text-red-500">*</span></label><input type="text" id="eventTitleInput" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm"></div><div class="grid grid-cols-1 md:grid-cols-2 gap-6"><div><label for="eventStartDateInput" class="block text-sm font-medium text-gray-900">Start</label><div class="mt-1 relative rounded-md shadow-sm"><input type="date" id="eventStartDateInput" class="block w-full pr-20 rounded-md border-gray-300 focus:border-blue-500 focus:ring-blue-500 sm:text-sm"><div class="absolute inset-y-0 right-0 flex items-center"><select id="eventStartShiftSelect" class="h-full rounded-md border-transparent bg-transparent py-0 pl-2 pr-7 text-gray-500 focus:border-blue-500 focus:ring-blue-500 sm:text-sm"><option>AM</option><option>PM</option></select></div></div></div><div><label for="eventEndDateInput" class="block text-sm font-medium text-gray-900">Ende</label><div class="mt-1 relative rounded-md shadow-sm"><input type="date" id="eventEndDateInput" class="block w-full pr-20 rounded-md border-gray-300 focus:border-blue-500 focus:ring-blue-500 sm:text-sm"><div class="absolute inset-y-0 right-0 flex items-center"><select id="eventEndShiftSelect" class="h-full rounded-md border-transparent bg-transparent py-0 pl-2 pr-7 text-gray-500 focus:border-blue-500 focus:ring-blue-500 sm:text-sm"><option>AM</option><option>PM</option></select></div></div></div></div><div><label for="eventDescriptionInput" class="block text-sm font-medium text-gray-900">Beschreibung <span class="text-gray-500">(optional)</span></label><textarea id="eventDescriptionInput" class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" rows="4"></textarea></div>
<div id="trackingInfoContainer" class="pt-4 border-t border-gray-200 space-y-2 text-sm">
    <p class="font-medium text-gray-700">Tracking-Informationen:</p>
    <div class="grid grid-cols-2 gap-4">
        <div>
            <span class="block text-xs font-medium text-gray-500">Erstellt von:</span>
            <span id="eventCreatedBy" class="block text-gray-800">N/A</span>
        </div>
        <div>
            <span class="block text-xs font-medium text-gray-500">Erstellt am:</span>
            <span id="eventCreatedAt" class="block text-gray-800">N/A</span>
        </div>
    </div>
    <div class="grid grid-cols-2 gap-4">
        <div>
            <span class="block text-xs font-medium text-gray-500">Zuletzt geändert von:</span>
            <span id="eventModifiedBy" class="block text-gray-800">N/A</span>
        </div>
        <div>
            <span class="block text-xs font-medium text-gray-500">Zuletzt geändert am:</span>
            <span id="eventModifiedAt" class="block text-gray-800">N/A</span>
        </div>
    </div>
</div>
</div><div class="flex justify-end items-center p-6 bg-gray-50 border-t border-gray-200 rounded-b-lg"><button id="cancelEventModalBtn" type="button" class="px-4 py-2 text-sm font-medium rounded-md border border-gray-300 text-gray-900 hover:bg-gray-100 mr-3">Abbrechen</button><button id="saveEventBtn" type="button" class="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700"></button></div></div></div>

<script>
document.addEventListener('DOMContentLoaded', () => {

    const sidebar = document.getElementById('sidebar');
    const sidebarToggleBtn = document.getElementById('sidebar-toggle');

    if (sidebar && sidebarToggleBtn) {
        const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
        const applyState = (isCollapsed) => {
            if (isCollapsed) {
                sidebar.classList.add('is-collapsed');
                toggleBtnIcon.textContent = 'menu';
            } else {
                sidebar.classList.remove('is-collapsed');
                toggleBtnIcon.textContent = 'chevron_left';
            }
        };
        sidebarToggleBtn.addEventListener('click', () => {
            const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
            localStorage.setItem('sidebarCollapsed', isNowCollapsed);
            applyState(isNowCollapsed);
        });
        const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
        applyState(savedState);
    }

    let allTestCells = [], timelineStartDate = getStartOfWeek(new Date()), saveTimeout = null, isEditMode = false, currentEditingEvent = null, currentEditingCell = null, currentView = 'timeline',
        isModalInEditMode = false, currentAnalyseView = 'overview';

    const CURRENT_USER_DISPLAY_NAME = "{{ current_user_display_name }}";
    
    let chartInstances = {};
  
    const userRoles = {{ session.get('roles', []) | tojson }};
    const canManageCellMasterData = userRoles.includes('ADMIN') || userRoles.includes('PLANNER');
    const canManageCellEvents = userRoles.includes('ADMIN') || userRoles.includes('PLANNER') || userRoles.includes('TESTENGINEER') || userRoles.includes('MAINTENANCEMANAGER') || userRoles.includes('TESTCELLINSPECTOR');

    const dom = {
        cellCardsContainer: document.getElementById('cellCardsContainer'), timelineContainer: document.getElementById('timelineContainer'), analyseContainer: document.getElementById('analyseContainer'), analyseChartsContainer: document.getElementById('analyseChartsContainer'), viewTabs: document.getElementById('viewTabs'), newTestCellBtn: document.getElementById('newTestCellBtn'), prevWeekBtn: document.getElementById('prevWeekBtn'), nextWeekBtn: document.getElementById('nextWeekBtn'), jumpToTodayBtn: document.getElementById('jumpToTodayBtn'), 
        analyseViewOverviewBtn: document.getElementById('analyseViewOverviewBtn'), analyseViewTrendBtn: document.getElementById('analyseViewTrendBtn'), analyseViewEngineBtn: document.getElementById('analyseViewEngineBtn'),
        testCellModal: document.getElementById('testCellModal'), testCellModalTitle: document.getElementById('testCellModalTitle'), closeTestCellModalBtn: document.getElementById('closeTestCellModalBtn'), cancelTestCellModalBtn: document.getElementById('cancelTestCellModalBtn'), saveTestCellBtn: document.getElementById('saveTestCellBtn'), deleteTestCellBtnHeader: document.getElementById('deleteTestCellBtnHeader'), addEngineTypeBtn: document.getElementById('addEngineTypeBtn'), originalTestCellIdInput: document.getElementById('originalTestCellId'), testCellIdInput: document.getElementById('testCellIdInput'), testCellNameInput: document.getElementById('testCellNameInput'), editTestCellBtn: document.getElementById('editTestCellBtn'), engineTypesContainer: document.getElementById('engineTypesContainer'), 
        eventModal: document.getElementById('eventModal'), eventModalTitle: document.getElementById('eventModalTitle'), closeEventModalBtn: document.getElementById('closeEventModalBtn'), cancelEventModalBtn: document.getElementById('cancelEventModalBtn'), saveEventBtn: document.getElementById('saveEventBtn'), deleteEventBtn: document.getElementById('deleteEventBtn'), eventTypeButtons: document.getElementById('eventTypeButtons'), eventModalEventId: document.getElementById('eventModalEventId'), eventModalCellId: document.getElementById('eventModalCellId'), eventTitleInput: document.getElementById('eventTitleInput'), eventStartDateInput: document.getElementById('eventStartDateInput'), eventStartShiftSelect: document.getElementById('eventStartShiftSelect'), eventEndDateInput: document.getElementById('eventEndDateInput'), eventEndShiftSelect: document.getElementById('eventEndShiftSelect'), eventDescriptionInput: document.getElementById('eventDescriptionInput'), editEventBtn: document.getElementById('editEventBtn'),
        trackingInfoContainer: document.getElementById('trackingInfoContainer'),
        eventCreatedBy: document.getElementById('eventCreatedBy'),
        eventCreatedAt: document.getElementById('eventCreatedAt'),
        eventModifiedBy: document.getElementById('eventModifiedBy'),
        eventModifiedAt: document.getElementById('eventModifiedAt')
    };

    function getStartOfWeek(date) { const d = new Date(date); const day = d.getUTCDay(); const diff = d.getUTCDate() - day + (day === 0 ? -6 : 1); return new Date(Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), diff)); }
    function parseUTCDate(dateString) { return new Date(dateString + 'T00:00:00Z'); }
    function toLocalDateString(date) { if (!date) return ''; const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; }

    async function loadData() { 
        try { 
            // NEU: Wir laden direkt die kombinierten Daten (Live + Archiv)
            const response = await fetch("/api/analytics/test_cells"); 
            if (!response.ok) throw new Error('Netzwerkfehler'); 
            allTestCells = await response.json(); 
            
            renderAll(); 
        } catch (error) { 
            console.error("Fehler beim Laden der Daten:", error); 
            dom.cellCardsContainer.innerHTML = '<p class="col-span-full text-red-500">Fehler beim Laden.</p>'; 
        } 
    }
    function saveData() { 
        if (saveTimeout) clearTimeout(saveTimeout); 
        saveTimeout = setTimeout(async () => { 
            try { 
                await fetch("/api/test_cells", { 
                    method: 'POST', 
                    headers: { 'Content-Type': 'application/json' }, 
                    body: JSON.stringify(allTestCells) 
                }); 
            } catch (error) { 
                console.error('Fehler beim Speichern:', error); 
                alert('Fehler: Änderungen konnten nicht gespeichert werden.'); 
            } 
        }, 500); 
    }
    
    function renderAll() { renderCellCards(); if (currentView === 'timeline') { renderTimelineView(); } else if (currentView === 'analyse') { renderAnalyseView(); } }
    function switchView(view) { currentView = view; document.querySelectorAll('.tab-btn').forEach(btn => { btn.classList.toggle('border-blue-600', btn.dataset.view === view); btn.classList.toggle('text-blue-600', btn.dataset.view === view); btn.classList.toggle('border-transparent', btn.dataset.view !== view); btn.classList.toggle('text-slate-500', btn.dataset.view !== view); }); dom.timelineContainer.style.display = view === 'timeline' ? 'block' : 'none'; dom.analyseContainer.style.display = view === 'analyse' ? 'block' : 'none'; document.getElementById('timelineNav').style.display = view === 'timeline' ? 'flex' : 'none'; if (view === 'timeline') renderTimelineView(); else renderAnalyseView(); }
    function getCurrentStatus(cell) { 
        const now = new Date(); 
        const todayStr = toLocalDateString(now); 
        const currentShift = now.getHours() < 12 ? 'AM' : 'PM'; 
        
        // Sortierung: Blocked > Maintenance > Operational
        // Wir suchen das "schlimmste" aktive Event
        const priority = { 'Blocked': 3, 'Maintenance': 2, 'Operational': 1 };
        
        let activeEvent = null;
        let maxPrio = 0;
    
        cell.events.forEach(e => { 
            const startOk = (e.start_date < todayStr) || (e.start_date === todayStr && e.start_shift <= currentShift); 
            
            let endOk = false; 
            if (e.type === 'Operational') { 
                endOk = (e.end_date > todayStr) || (e.end_date === todayStr && e.end_shift > currentShift); 
            } else { 
                endOk = (e.end_date > todayStr) || (e.end_date === todayStr && e.end_shift >= currentShift); 
            } 
            
            if (startOk && endOk) {
                const currentPrio = priority[e.type] || 0;
                if (currentPrio > maxPrio) {
                    maxPrio = currentPrio;
                    activeEvent = e;
                }
            }
        }); 
        
        if (activeEvent) return activeEvent;
        
        return { type: 'Available', title: 'Verfügbar' }; 
    }    
    function renderCellCards() { 
        dom.cellCardsContainer.innerHTML = ''; 
        if (allTestCells.length === 0) { 
            dom.cellCardsContainer.innerHTML = '<p class="col-span-full text-center text-slate-500">Keine Testzellen konfiguriert.</p>'; 
            return; 
        } 
        
        allTestCells.forEach(cell => { 
            const statusEvent = getCurrentStatus(cell); 
            const status = statusEvent.type;
            
            const statusClasses = { 
                Available: { bg: 'bg-green-50', text: 'text-green-800', border: 'border-green-200', name: 'text-green-900', badge: 'bg-green-200' }, 
                Operational: { bg: 'bg-slate-50', text: 'text-slate-800', border: 'border-slate-200', name: 'text-slate-900', badge: 'bg-slate-200' }, 
                Maintenance: { bg: 'bg-yellow-50', text: 'text-yellow-800', border: 'border-yellow-200', name: 'text-yellow-900', badge: 'bg-yellow-200' }, 
                Blocked: { bg: 'bg-red-50', text: 'text-red-800', border: 'border-red-200', name: 'text-red-900', badge: 'bg-red-200' }, 
            }; 
            
            const c = statusClasses[status] || statusClasses.Operational; 
            
            // Titel-Logik: Bei Blocked/Maintenance den Event-Titel anzeigen, sonst Standard
            let statusLabel = status;
            let detailText = `ID: ${cell.ID}`;
            
            if (status === 'Blocked') {
                statusLabel = "GESPERRT";
                // Zeige den Grund an (z.B. "⛔ Sperre: Lüftung")
                detailText = `<span class="font-bold text-red-700">${statusEvent.title}</span>`;
            } else if (status === 'Maintenance') {
                detailText = `<span class="font-medium">${statusEvent.title}</span>`;
            } else if (status === 'Operational') {
                detailText = `Belegt: ${statusEvent.title}`;
            }
    
            const card = document.createElement('div'); 
            card.className = `cell-card cursor-pointer rounded-xl p-5 shadow-md ${c.bg} ${c.text} ${c.border} hover:border-gray-400 ${status === 'Operational' ? 'operational-pulse' : ''}`; 
            card.innerHTML = `
                <div class="flex items-center justify-between mb-3">
                    <h3 class="text-lg font-semibold ${c.name}">${cell.TESTCELL}</h3>
                    <span class="text-xs font-semibold px-3 py-1 rounded-full ${c.badge}">${statusLabel}</span>
                </div>
                <div class="text-sm font-medium min-h-[1.25rem]">${detailText}</div>
                <div class="mt-4 pt-4 border-t ${c.border}">
                    <div class="flex items-center text-sm">
                        <span class="material-icons-outlined mr-1.5" style="font-size: 18px;">engineering</span>
                        <span class="font-medium truncate">${cell.TYPES.join(', ') || 'Keine Angabe'}</span>
                    </div>
                </div>`; 
            
            card.addEventListener('click', () => openTestCellModal(cell)); 
            dom.cellCardsContainer.appendChild(card); 
        }); 
    }    
    function renderTimelineView() { const totalDays = 14; let dateHeaderHtml = ''; let shiftHeaderHtml = ''; const dayNames = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"]; const monthNames = ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]; let currentDate = new Date(timelineStartDate); for (let i = 0; i < totalDays; i++) { const day = new Date(currentDate); const isWeekend = day.getUTCDay() === 0 || day.getUTCDay() === 6; const weekendClass = isWeekend ? 'weekend-bg' : ''; dateHeaderHtml += `<th colspan="2" class="px-1 py-1 text-center text-xs font-medium text-slate-600 w-[90px] whitespace-nowrap border-r border-slate-200 last:border-r-0 align-middle ${weekendClass}">${dayNames[day.getUTCDay()]}, ${day.getUTCDate()}. ${monthNames[day.getUTCMonth()]}</th>`; for(const shift of ['AM', 'PM']) { const isCurrent = toLocalDateString(new Date()) === toLocalDateString(day) && (new Date().getHours() < 12 ? 'AM' : 'PM') === shift; const currentClass = isCurrent ? 'current-timeslot-highlight' : ''; shiftHeaderHtml += `<th class="px-1 py-1 text-center text-xs font-medium text-slate-500 w-[45px] whitespace-nowrap border-r border-slate-200 last:border-r-0 align-middle ${weekendClass} ${currentClass}">${shift}</th>`; } currentDate.setUTCDate(currentDate.getUTCDate() + 1); } let fixedBodyHtml = ''; let scrollableBodyHtml = ''; allTestCells.forEach(cell => { fixedBodyHtml += `<tr class="h-[65px]"><td class="whitespace-nowrap px-4 py-3 text-sm text-slate-700 align-middle cursor-pointer hover:bg-slate-50" data-cell-id-fixed="${cell.ID}">${cell.TESTCELL}</td></tr>`; const totalShifts = totalDays * 2; let occupiedShifts = new Array(totalShifts).fill(null); const timelineStartUTC = new Date(timelineStartDate); cell.events.forEach(event => { const start = parseUTCDate(event.start_date); const end = parseUTCDate(event.end_date); const dayInMillis = 86400000; const startDayIndex = Math.floor((start.getTime() - timelineStartUTC.getTime()) / dayInMillis); const endDayIndex = Math.floor((end.getTime() - timelineStartUTC.getTime()) / dayInMillis); const startShiftIndex = startDayIndex * 2 + (event.start_shift === 'PM' ? 1 : 0); let endShiftIndex = endDayIndex * 2 + (event.end_shift === 'PM' ? 1 : 0); if (event.type !== 'Operational') { endShiftIndex = endShiftIndex + 1; } for (let i = startShiftIndex; i < endShiftIndex; i++) { if (i >= 0 && i < totalShifts && occupiedShifts[i] === null) { occupiedShifts[i] = { event: event, isStart: i === startShiftIndex }; } } }); let rowHtml = '<tr class="h-[65px]">'; let i = 0; while (i < totalShifts) { const shiftInfo = occupiedShifts[i]; const dayIndex = Math.floor(i / 2); const shiftDate = new Date(timelineStartUTC); shiftDate.setUTCDate(shiftDate.getUTCDate() + dayIndex); const shiftType = i % 2 === 0 ? 'AM' : 'PM'; const isWeekend = shiftDate.getUTCDay() === 0 || shiftDate.getUTCDay() === 6; const weekendClass = isWeekend ? 'weekend-bg' : ''; const isCurrent = toLocalDateString(new Date()) === toLocalDateString(shiftDate) && (new Date().getHours() < 12 ? 'AM' : 'PM') === shiftType; const currentClass = isCurrent ? 'current-timeslot-highlight' : ''; if (shiftInfo && shiftInfo.isStart) { let colspan = 1; while (i + colspan < totalShifts && occupiedShifts[i + colspan] && occupiedShifts[i + colspan].event.event_id === shiftInfo.event.event_id) { colspan++; } const event = shiftInfo.event; const colorClasses = { Operational: 'bg-slate-500 hover:bg-slate-600', Maintenance: 'bg-yellow-500 hover:bg-yellow-600', Blocked: 'bg-red-500 hover:bg-red-700', }; const isReadOnly = event.type === 'Operational'; const cursorClass = isReadOnly ? 'cursor-default' : 'cursor-pointer'; let title = '';
if (event.type === 'Blocked') {
    // Hier steht jetzt unser injizierter Titel (z.B. "⛔ Sperre: Anlage X")
    title = `${event.title}\n${event.description || ''}`;
} else if (isReadOnly) {
    title = `Operativer Block: ${event.title}\n${event.description || ''}`;
} else {
    title = `Manuelle Blockade: ${event.title}`;
} rowHtml += `<td colspan="${colspan}" class="align-middle ${cursorClass}" title="${title}" data-cell-id="${cell.ID}" data-event-id="${event.event_id}" data-is-readonly="${isReadOnly}"><div class="h-full w-full rounded-md text-white text-xs flex items-center justify-center p-1 overflow-hidden ${colorClasses[event.type] || colorClasses.Operational}"><span class="truncate">${event.title}</span></div></td>`; i += colspan; } else if (shiftInfo) { i++; } else { rowHtml += `<td class="align-middle cursor-pointer ${weekendClass} ${currentClass}" data-cell-id="${cell.ID}" data-date="${toLocalDateString(shiftDate)}" data-shift="${shiftType}"></td>`; i++; } } rowHtml += '</tr>'; scrollableBodyHtml += rowHtml; }); dom.timelineContainer.innerHTML = `<div class="overflow-x-auto rounded-lg border border-slate-200"><div class="align-middle inline-block min-w-full"><div class="flex"><div class="sticky left-0 z-10 bg-white"><table class="min-w-[200px] divide-y divide-slate-200 border-r border-slate-200"><thead class="bg-slate-50"><tr><th rowspan="2" class="px-4 py-3.5 text-left text-sm font-semibold text-slate-900 align-middle">Testzelle</th></tr><tr></tr></thead><tbody class="divide-y divide-slate-200 bg-white">${fixedBodyHtml}</tbody></table></div><div class="flex-grow overflow-x-auto"><table class="min-w-full divide-y divide-slate-200 timeline-table"><thead class="bg-slate-50 sticky top-0 z-10"><tr>${dateHeaderHtml}</tr><tr>${shiftHeaderHtml}</tr></thead><tbody class="divide-y divide-slate-200 bg-white">${scrollableBodyHtml}</tbody></table></div></div></div></div>`; }
    function renderAnalyseView() { 
        if (!allTestCells || allTestCells.length === 0) { 
            dom.analyseChartsContainer.innerHTML = '<p class="col-span-full text-center text-slate-500">Keine Testzellen verfügbar.</p>'; 
            return; 
        } 
        
        dom.analyseViewOverviewBtn.classList.toggle('active', currentAnalyseView === 'overview'); 
        dom.analyseViewTrendBtn.classList.toggle('active', currentAnalyseView === 'trend'); 
        dom.analyseViewEngineBtn.classList.toggle('active', currentAnalyseView === 'engine'); 

        // Die Daten sind in allTestCells bereits komplett (inkl. Archiv)
        dom.analyseChartsContainer.innerHTML = ''; 
        const currentYear = new Date().getFullYear(); 
        
        if (currentAnalyseView === 'overview') { 
            dom.analyseChartsContainer.className = 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6'; 
            allTestCells.forEach(cell => dom.analyseChartsContainer.appendChild(createOverviewChartCard(cell, calculateYearlyStats(cell, currentYear), currentYear))); 
        } else if (currentAnalyseView === 'trend') { 
            dom.analyseChartsContainer.className = 'grid grid-cols-1 lg:grid-cols-2 gap-6'; 
            allTestCells.forEach(cell => dom.analyseChartsContainer.appendChild(createTrendChartCard(cell, calculateMonthlyStats(cell, currentYear), currentYear))); 
        } else if (currentAnalyseView === 'engine') { 
            dom.analyseChartsContainer.className = 'grid grid-cols-1 lg:grid-cols-2 gap-8'; 
            allTestCells.forEach(cell => dom.analyseChartsContainer.appendChild(createEngineAnalysisCard(cell, calculateEngineStats(cell, currentYear), currentYear))); 
        }
    } 
    
    function getStatusForDateShift(cell, dateStr, shift) { const activeEvent = cell.events.find(e => { const startOk = (e.start_date < dateStr) || (e.start_date === dateStr && e.start_shift <= shift); let endOk = false; if (e.type === 'Operational') { endOk = (e.end_date > dateStr) || (e.end_date === dateStr && e.end_shift > shift); } else { endOk = (e.end_date > dateStr) || (e.end_date === dateStr && e.end_shift >= shift); } return startOk && endOk; }); return activeEvent ? activeEvent.type : 'Available'; }
    function calculateYearlyStats(cell, year) { let totalShifts = 0, availableShifts = 0, operationalShifts = 0, maintenanceShifts = 0, blockedShifts = 0; const startDate = new Date(year, 0, 1); const endDate = new Date(year, 11, 31); let currentDate = startDate; while (currentDate <= endDate) { if (currentDate.getDay() >= 1 && currentDate.getDay() <= 5) { const dateStr = toLocalDateString(currentDate); for (const shift of ['AM', 'PM']) { totalShifts++; const status = getStatusForDateShift(cell, dateStr, shift); if (status === 'Available') availableShifts++; else if (status === 'Operational') operationalShifts++; else if (status === 'Maintenance') maintenanceShifts++; else if (status === 'Blocked') blockedShifts++; } } currentDate.setDate(currentDate.getDate() + 1); } return { total: totalShifts, available: availableShifts, operational: operationalShifts, maintenance: maintenanceShifts, blocked: blockedShifts, availablePercent: totalShifts > 0 ? ((availableShifts / totalShifts) * 100).toFixed(1) : '0.0', operationalPercent: totalShifts > 0 ? ((operationalShifts / totalShifts) * 100).toFixed(1) : '0.0', maintenancePercent: totalShifts > 0 ? ((maintenanceShifts / totalShifts) * 100).toFixed(1) : '0.0', blockedPercent: totalShifts > 0 ? ((blockedShifts / totalShifts) * 100).toFixed(1) : '0.0' }; }
    function calculateMonthlyStats(cell, year) { const monthNames = ["Jan", "Feb", "Mär", "Apr", "Mai", "Jun", "Jul", "Aug", "Sep", "Okt", "Nov", "Dez"]; const stats = { labels: monthNames, available: Array(12).fill(0), operational: Array(12).fill(0), maintenance: Array(12).fill(0), blocked: Array(12).fill(0) }; for (let month = 0; month < 12; month++) { const startDate = new Date(year, month, 1); const endDate = new Date(year, month + 1, 0); let currentDate = startDate; while (currentDate <= endDate) { if (currentDate.getDay() >= 1 && currentDate.getDay() <= 5) { const dateStr = toLocalDateString(currentDate); for (const shift of ['AM', 'PM']) { const status = getStatusForDateShift(cell, dateStr, shift); if (status === 'Available') stats.available[month]++; else if (status === 'Operational') stats.operational[month]++; else if (status === 'Maintenance') stats.maintenance[month]++; else if (status === 'Blocked') stats.blocked[month]++; } } currentDate.setDate(currentDate.getDate() + 1); } } return stats; }
    function deriveEngineTypeFromDescription(description, compatibleTypes) { if (!description || !compatibleTypes || compatibleTypes.length === 0) return "Nicht zugeordnet"; const descLower = description.toLowerCase(); for (const type of compatibleTypes) { if (descLower.includes(type.toLowerCase())) return type; } return "Nicht zugeordnet"; }
    function calculateShiftDuration(event) { let shiftCount = 0; let current = parseUTCDate(event.start_date); const end = parseUTCDate(event.end_date); while (current <= end) { if (current.getUTCDay() >= 1 && current.getUTCDay() <= 5) { const isStartDay = current.getTime() === parseUTCDate(event.start_date).getTime(); const isEndDay = current.getTime() === end.getTime(); if (isStartDay && isEndDay) shiftCount += (event.start_shift === 'AM' && event.end_shift === 'PM') ? 2 : 1; else if (isStartDay) shiftCount += (event.start_shift === 'AM' ? 2 : 1); else if (isEndDay) shiftCount += (event.end_shift === 'PM' ? 2 : 1); else shiftCount += 2; } current.setUTCDate(current.getUTCDate() + 1); } return shiftCount; }
    function calculateEngineStats(cell, year) { const stats = {}; const operationalEvents = cell.events.filter(e => e.type === 'Operational' && new Date(e.start_date).getUTCFullYear() === year); operationalEvents.forEach(event => { const engineType = deriveEngineTypeFromDescription(event.description, cell.TYPES); const workorder = event.title || 'Unbekannt'; const shifts = calculateShiftDuration(event); if (!stats[engineType]) stats[engineType] = { totalTests: 0, totalShifts: 0, avgShifts: 0, workorders: [] }; stats[engineType].totalTests++; stats[engineType].totalShifts += shifts; stats[engineType].workorders.push({ workorder: workorder, shifts: shifts }); }); for(const type in stats) { if(stats[type].totalTests > 0) stats[type].avgShifts = stats[type].totalShifts / stats[type].totalTests; } return stats; }
    function createOverviewChartCard(cell, stats, year) { const card = document.createElement('div'); card.className = 'bg-white rounded-lg shadow-md p-6 border border-slate-200'; const canvasId = `chart-overview-${cell.ID.replace(/[^a-zA-Z0-9]/g, '-')}`; card.innerHTML = `<div class="mb-4"><h3 class="text-lg font-semibold text-slate-900">${cell.TESTCELL}</h3><p class="text-sm text-slate-600">ID: ${cell.ID}</p></div><div class="relative h-64"><canvas id="${canvasId}"></canvas></div><div class="mt-4 pt-4 border-t border-slate-200"><div class="grid grid-cols-2 gap-3 text-xs"><div class="flex items-center gap-2"><div class="w-3 h-3 rounded-full bg-green-500"></div><span>Verfügbar: <strong>${stats.availablePercent}%</strong></span></div><div class="flex items-center gap-2"><div class="w-3 h-3 rounded-full bg-slate-500"></div><span>Operational: <strong>${stats.operationalPercent}%</strong></span></div><div class="flex items-center gap-2"><div class="w-3 h-3 rounded-full bg-yellow-500"></div><span>Wartung: <strong>${stats.maintenancePercent}%</strong></span></div><div class="flex items-center gap-2"><div class="w-3 h-3 rounded-full bg-red-500"></div><span>Blockiert: <strong>${stats.blockedPercent}%</strong></span></div></div><div class="mt-3 text-xs text-slate-600 text-center">Gesamt: ${stats.total} Schichten in ${year}</div></div>`; setTimeout(() => { const ctx = document.getElementById(canvasId); if (ctx) { new Chart(ctx, { type: 'doughnut', data: { labels: ['Verfügbar', 'Operational', 'Wartung', 'Blockiert'], datasets: [{ data: [stats.available, stats.operational, stats.maintenance, stats.blocked], backgroundColor: ['rgb(34, 197, 94)','rgb(100, 116, 139)','rgb(234, 179, 8)','rgb(239, 68, 68)'], borderWidth: 2, borderColor: '#ffffff' }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false }, tooltip: { callbacks: { label: function(context) { const total = context.dataset.data.reduce((a, b) => a + b, 0); const percentage = total > 0 ? ((context.parsed / total) * 100).toFixed(1) : 0; return `${context.label}: ${context.parsed} Schichten (${percentage}%)`; } } } } } }); } }, 50); return card; }
    function createTrendChartCard(cell, stats, year) { const card = document.createElement('div'); card.className = 'bg-white rounded-lg shadow-md p-6 border border-slate-200'; const canvasId = `chart-trend-${cell.ID.replace(/[^a-zA-Z0-9]/g, '-')}`; card.innerHTML = `<div class="mb-4"><h3 class="text-lg font-semibold text-slate-900">${cell.TESTCELL}</h3><p class="text-sm text-slate-600">Monatliche Auslastung in ${year}</p></div><div class="relative h-80"><canvas id="${canvasId}"></canvas></div>`; setTimeout(() => { const ctx = document.getElementById(canvasId); if (ctx) { new Chart(ctx, { type: 'line', data: { labels: stats.labels, datasets: [ { label: 'Operational', data: stats.operational, backgroundColor: 'rgba(100, 116, 139, 0.5)', borderColor: 'rgb(100, 116, 139)', fill: true, tension: 0.4 }, { label: 'Wartung', data: stats.maintenance, backgroundColor: 'rgba(234, 179, 8, 0.5)', borderColor: 'rgb(234, 179, 8)', fill: true, tension: 0.4 }, { label: 'Blockiert', data: stats.blocked, backgroundColor: 'rgba(239, 68, 68, 0.5)', borderColor: 'rgb(239, 68, 68)', fill: true, tension: 0.4 }, { label: 'Verfügbar', data: stats.available, backgroundColor: 'rgba(34, 197, 94, 0.5)', borderColor: 'rgb(34, 197, 94)', fill: true, tension: 0.4 }, ] }, options: { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false }, scales: { y: { stacked: true, beginAtZero: true, title: { display: true, text: 'Anzahl Schichten' } }, x: { title: { display: true, text: 'Monat' } } }, plugins: { legend: { position: 'bottom' }, tooltip: { callbacks: { label: function(context) { return `${context.dataset.label}: ${context.parsed.y} Schichten`; } } } } } }); } }, 50); return card; }
    function createEngineAnalysisCard(cell, engineStats, year) { const card = document.createElement('div'); card.className = 'bg-white rounded-lg shadow-md p-6 border border-slate-200'; const canvasId = `chart-engine-${cell.ID.replace(/[^a-zA-Z0-9]/g, '-')}`; let contentHtml = `<div class="mb-4"><h3 class="text-lg font-semibold text-slate-900">${cell.TESTCELL}</h3><p class="text-sm text-slate-600">Analyse der Triebwerkstests in ${year}</p></div>`; const sortedEngineTypes = Object.keys(engineStats); if (sortedEngineTypes.length === 0) { contentHtml += '<div class="text-center text-slate-500 py-8">Keine operativen Tests in diesem Jahr erfasst.</div>'; } else { contentHtml += `<div class="overflow-x-auto"><table class="min-w-full text-sm"><thead class="bg-slate-50"><tr class="text-left text-slate-600"><th class="px-4 py-2 font-semibold">Workorder</th><th class="px-4 py-2 font-semibold text-center">Belegte Schichten</th></tr></thead><tbody class="divide-y divide-slate-200">`; sortedEngineTypes.sort((a, b) => engineStats[b].totalShifts - engineStats[a].totalShifts).forEach(type => { const stats = engineStats[type]; contentHtml += `<tr class="bg-slate-100 font-semibold text-slate-800"><td colspan="2" class="px-4 py-2.5">Triebwerkstyp: ${type} <span class="font-normal text-slate-600 ml-2">(${stats.totalTests} Tests, ${stats.totalShifts} Schichten, Ø ${stats.avgShifts.toFixed(1)})</span></td></tr>`; stats.workorders.sort((a,b) => b.shifts - a.shifts).forEach(wo => { contentHtml += `<tr class="hover:bg-slate-50"><td class="pl-8 pr-4 py-2 font-medium text-slate-900">${wo.workorder}</td><td class="px-4 py-2 text-center text-slate-700">${wo.shifts}</td></tr>`; }); }); contentHtml += `</tbody></table></div><div class="mt-6 pt-6 border-t border-slate-200"><h4 class="text-md font-semibold text-slate-800 mb-3">Visuelle Effizienz-Analyse</h4><div class="relative" style="height: 250px;"><canvas id="${canvasId}"></canvas></div></div>`; } card.innerHTML = contentHtml; if (sortedEngineTypes.length > 0) { setTimeout(() => { const ctx = document.getElementById(canvasId); const chartData = sortedEngineTypes.map(type => { const stats = engineStats[type]; return { label: type, x: stats.totalTests, y: stats.avgShifts, r: Math.max(stats.totalShifts, 5) }; }); if (ctx) { new Chart(ctx, { type: 'bubble', data: { datasets: [{ label: 'Triebwerkstypen', data: chartData, backgroundColor: (context) => { const c = context.raw.y; return c > 10 ? 'rgba(239, 68, 68, 0.6)' : c > 5 ? 'rgba(234, 179, 8, 0.6)' : 'rgba(37, 99, 235, 0.6)'; }, borderColor: (context) => { const c = context.raw.y; return c > 10 ? 'rgb(239, 68, 68)' : c > 5 ? 'rgb(234, 179, 8)' : 'rgb(37, 99, 235)'; }, }] }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { beginAtZero: true, title: { display: true, text: 'Anzahl der Tests' } }, y: { beginAtZero: true, title: { display: true, text: 'Ø Schichten pro Test' } } }, plugins: { legend: { display: false }, tooltip: { callbacks: { label: (context) => [ `Typ: ${context.raw.label}`, `Anzahl Tests: ${context.raw.x}`, `Ø Schichten/Test: ${context.raw.y.toFixed(1)}`, `Gesamte Schichten: ${context.raw.r}` ] } } } } }); } }, 50); } return card; }
    function toggleModalEditState(enableEditing) { isModalInEditMode = enableEditing; dom.testCellNameInput.readOnly = !enableEditing; dom.engineTypesContainer.querySelectorAll('input').forEach(input => { input.readOnly = !enableEditing; input.classList.toggle('bg-slate-50', !enableEditing); }); dom.addEngineTypeBtn.style.display = enableEditing ? 'block' : 'none'; dom.engineTypesContainer.querySelectorAll('button').forEach(btn => { btn.style.display = enableEditing ? 'inline-flex' : 'none'; }); dom.saveTestCellBtn.style.display = enableEditing ? 'block' : 'none'; 
        dom.editTestCellBtn.style.display = enableEditing ? 'none' : (isEditMode && canManageCellMasterData ? 'block' : 'none');
        dom.deleteTestCellBtnHeader.style.display = (isEditMode && enableEditing && canManageCellMasterData) ? 'block' : 'none';
        dom.cancelTestCellModalBtn.textContent = enableEditing ? 'Abbrechen' : 'Schließen'; 
    }
    let availableFacilityUnits = [];

    // 1. Units laden (beim Start)
    async function loadModalFacilityUnits() {
        try {
            const res = await fetch('/api/facility/units');
            if (res.ok) {
                availableFacilityUnits = await res.json();
                const dl = document.getElementById('modalFacilityUnitList');
                dl.innerHTML = '';
                availableFacilityUnits.forEach(u => {
                    const opt = document.createElement('option');
                    opt.value = u.name; // "1.1 Name"
                    opt.dataset.id = u.id; // UUID
                    dl.appendChild(opt);
                });
            }
        } catch(e) { console.error(e); }
    }
    
    // 2. Unit Tag hinzufügen
    function addUnitTag(unitId, unitName, container) {
        // Prüfen ob schon da
        if (container.querySelector(`div[data-unit-id="${unitId}"]`)) return;
    
        const div = document.createElement('div');
        div.className = 'flex items-center justify-between p-2 bg-slate-50 border border-slate-200 rounded-md text-sm';
        div.dataset.unitId = unitId;
        
        div.innerHTML = `
            <span class="truncate font-medium text-slate-700">${unitName}</span>
            <button type="button" class="text-slate-400 hover:text-red-500 ml-2" onclick="this.parentElement.remove()">
                <span class="material-icons-outlined text-base">close</span>
            </button>
        `;
        container.appendChild(div);
    }
    function openTestCellModal(cell=null){ 
        isEditMode=!!cell; 
        dom.testCellModalTitle.textContent=isEditMode?`Testzelle "${cell.TESTCELL}"`:'Neue Testzelle anlegen'; 
        dom.saveTestCellBtn.textContent=isEditMode?'Änderungen speichern':'Testzelle erstellen'; 
        
        dom.editTestCellBtn.style.display = isEditMode && canManageCellMasterData ? 'block' : 'none';
        dom.deleteTestCellBtnHeader.style.display = 'none';

        if(isEditMode){ 
            currentEditingCell=cell; 
            dom.originalTestCellIdInput.value=cell.ID; 
            dom.testCellIdInput.value=cell.ID; 
            dom.testCellIdInput.readOnly=true; 
            dom.testCellIdInput.classList.add('bg-slate-50'); 
            dom.testCellNameInput.value=cell.TESTCELL; 
            renderEngineTypes(cell.TYPES); 
            toggleModalEditState(false); 
        } else { 
            currentEditingCell=null; 
            dom.originalTestCellIdInput.value=''; 
            dom.testCellIdInput.value=''; 
            dom.testCellIdInput.readOnly=false; 
            dom.testCellIdInput.classList.remove('bg-slate-50'); 
            dom.testCellNameInput.value=''; 
            renderEngineTypes(['']); 
            toggleModalEditState(true); 
            dom.editTestCellBtn.style.display = 'none';
            dom.deleteTestCellBtnHeader.style.display = 'none'; 
        } 
        dom.testCellModal.classList.add('is-open'); 
            const unitContainer = document.getElementById('requiredUnitsContainer');
        
        unitContainer.innerHTML = '';
        document.getElementById('addRequiredUnitInput').value = '';
    
        if (isEditMode && cell.REQUIRED_UNITS) {
            // REQUIRED_UNITS ist eine Liste von IDs
            cell.REQUIRED_UNITS.forEach(uid => {
                // Namen auflösen
                const unitObj = availableFacilityUnits.find(u => u.id === uid);
                const name = unitObj ? unitObj.name : "Unbekannte ID";
                addUnitTag(uid, name, unitContainer);
            });
        }
    }
    function renderEngineTypes(types){ dom.engineTypesContainer.innerHTML=''; const typesToShow=types.length>0?types:['']; typesToShow.forEach((type,index)=>{ addEngineTypeInput(type,typesToShow.length>1); }); }
    function addEngineTypeInput(value='', showRemoveBtn=false) { 
        const div = document.createElement('div'); 
        div.className = 'flex items-center gap-2'; 
        const input = document.createElement('input'); 
        input.type = 'text'; 
        input.className = 'mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm flex-grow'; 
        input.placeholder = 'z.B. CFM56'; 
        input.value = value; 
        input.setAttribute('list', 'availableEngineTypesList'); 
        input.readOnly = !isModalInEditMode; 
        if (!isModalInEditMode) { input.classList.add('bg-slate-50'); } 
        div.appendChild(input); 
        
        if(showRemoveBtn) { 
            const removeBtn = document.createElement('button'); 
            removeBtn.type = 'button'; 
            removeBtn.className = 'text-slate-400 hover:text-slate-600 p-1 items-center justify-center'; 
            removeBtn.style.display = isModalInEditMode ? 'inline-flex' : 'none'; 
            removeBtn.innerHTML = '<span class="material-icons-outlined text-xl">delete_outline</span>'; 
            removeBtn.onclick = () => div.remove(); 
            div.appendChild(removeBtn); 
        } 
        dom.engineTypesContainer.appendChild(div); 
    }    
    
    function closeTestCellModal(){ dom.testCellModal.classList.remove('is-open'); }
    function handleSaveTestCell(){ const id=dom.testCellIdInput.value.trim(); const name=dom.testCellNameInput.value.trim(); const requiredUnitIds = Array.from(document.getElementById('requiredUnitsContainer').children).map(div => div.dataset.unitId); if(!id||!name){ alert('ID und Name sind Pflichtfelder.'); return; } const engineTypes=Array.from(dom.engineTypesContainer.querySelectorAll('input')).map(input=>input.value.trim()).filter(Boolean); if(isEditMode){ const cell=allTestCells.find(c=>c.ID===dom.originalTestCellIdInput.value); if(cell){ cell.ID=id; cell.TESTCELL=name; cell.TYPES=engineTypes; cell.REQUIRED_UNITS = requiredUnitIds;} }else{ if(allTestCells.some(c=>c.ID===id)){ alert('Eine Testzelle mit dieser ID existiert bereits.'); return; } allTestCells.push({ID:id,TESTCELL:name,TYPES:engineTypes,REQUIRED_UNITS: requiredUnitIds,events:[]}); } toggleModalEditState(false); saveData(); renderAll(); closeTestCellModal(); }
    function handleDeleteTestCell(){ if(!isEditMode||!confirm(`Möchten Sie die Testzelle "${currentEditingCell.TESTCELL}" wirklich löschen?`))return; allTestCells=allTestCells.filter(c=>c.ID!==currentEditingCell.ID); saveData(); renderAll(); closeTestCellModal(); }
    function toggleEventModalEditState(enableEditing) { 
        isModalInEditMode = enableEditing; 
        const inputs = [dom.eventTitleInput, dom.eventStartDateInput, dom.eventEndDateInput, dom.eventDescriptionInput]; 
        const selects = [dom.eventStartShiftSelect, dom.eventEndShiftSelect]; 
        inputs.forEach(input => { input.readOnly = !enableEditing; input.classList.toggle('bg-slate-50', !enableEditing); }); 
        selects.forEach(select => { select.disabled = !enableEditing; select.classList.toggle('bg-slate-50', !enableEditing); }); 
        dom.eventTypeButtons.querySelectorAll('button').forEach(btn => { btn.disabled = !enableEditing; btn.classList.toggle('opacity-75', !enableEditing); btn.classList.toggle('pointer-events-none', !enableEditing); }); 
        dom.saveEventBtn.style.display = enableEditing ? 'block' : 'none'; 
        dom.editEventBtn.style.display = enableEditing ? 'none' : (isEditMode && canManageCellEvents ? 'block' : 'none');
        dom.deleteEventBtn.style.display = (isEditMode && enableEditing && canManageCellEvents) ? 'block' : 'none';
        dom.cancelEventModalBtn.textContent = enableEditing ? 'Abbrechen' : 'Schließen'; 
    }
    function openEventModal(cellId,event=null,date=null,shift=null){ 
        isEditMode=!!event; 
        currentEditingEvent=event; 
        const cell=allTestCells.find(c=>c.ID===cellId); 
        if(!cell)return; 
        dom.eventModalTitle.textContent=isEditMode?`Blockade/Wartung ansehen`:`Neue Blockade/Wartung für ${cell.TESTCELL}`; 
        dom.saveEventBtn.textContent=isEditMode?'Änderungen speichern':'Blockade erstellen'; 
        
        dom.editEventBtn.style.display = isEditMode && canManageCellEvents ? 'block' : 'none';
        dom.deleteEventBtn.style.display = 'none';

        if(isEditMode){ 
            dom.eventModalEventId.value=event.event_id; 
            dom.eventModalCellId.value=event.cell_id; 
            dom.eventTitleInput.value=event.title; 
            dom.eventStartDateInput.value=event.start_date; 
            dom.eventStartShiftSelect.value=event.start_shift; 
            dom.eventEndDateInput.value=event.end_date; 
            dom.eventEndShiftSelect.value=event.end_shift; 
            dom.eventDescriptionInput.value=event.description||''; 
            updateEventTypeButtons(event.type); 
            dom.trackingInfoContainer.style.display = 'block';
            dom.eventCreatedBy.textContent = event.created_by || 'System';
            dom.eventCreatedAt.textContent = event.created_at ? new Date(event.created_at).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short'}) : 'N/A';
            dom.eventModifiedBy.textContent = event.last_modified_by || 'N/A';
            dom.eventModifiedAt.textContent = event.last_modified_at ? new Date(event.last_modified_at).toLocaleString('de-DE', { dateStyle: 'short', timeStyle: 'short'}) : 'N/A';
            toggleEventModalEditState(false); 
        } else { 
            dom.eventModalEventId.value=''; 
            dom.eventModalCellId.value=cellId; 
            dom.eventTitleInput.value=''; 
            dom.eventStartDateInput.value=date; 
            dom.eventStartShiftSelect.value=shift; 
            dom.eventEndDateInput.value=date; 
            dom.eventEndShiftSelect.value=shift; 
            dom.eventDescriptionInput.value=''; 
            updateEventTypeButtons('Maintenance'); 
            dom.trackingInfoContainer.style.display = 'none'; 
            toggleEventModalEditState(true); 
            dom.editEventBtn.style.display = 'none'; 
            dom.deleteEventBtn.style.display = 'none'; 
        } 
        dom.eventModal.classList.add('is-open'); 
    }
    function updateEventTypeButtons(activeType){ dom.eventTypeButtons.querySelectorAll('button').forEach(btn=>{ btn.classList.toggle('bg-blue-600', btn.dataset.type===activeType); btn.classList.toggle('text-white', btn.dataset.type===activeType); }); }
    function closeEventModal(){ dom.eventModal.classList.remove('is-open'); }
    function handleSaveEvent(){ const title=dom.eventTitleInput.value.trim(); if(!title){ alert('Ein Titel ist erforderlich.'); return; } const cellId=dom.eventModalCellId.value; const cell=allTestCells.find(c=>c.ID===cellId); if(!cell)return; const nowIso = new Date().toISOString(); let eventData; if (isEditMode) { eventData = { ...currentEditingEvent, event_id: currentEditingEvent.event_id, cell_id: cellId, title: title, type: dom.eventTypeButtons.querySelector('.bg-blue-600').dataset.type, start_date: dom.eventStartDateInput.value, start_shift: dom.eventStartShiftSelect.value, end_date: dom.eventEndDateInput.value, end_shift: dom.eventEndShiftSelect.value, description: dom.eventDescriptionInput.value.trim(), last_modified_by: CURRENT_USER_DISPLAY_NAME, last_modified_at: nowIso }; } else { eventData = { event_id: `manual_${Date.now()}`, cell_id: cellId, title: title, type: dom.eventTypeButtons.querySelector('.bg-blue-600').dataset.type, start_date: dom.eventStartDateInput.value, start_shift: dom.eventStartShiftSelect.value, end_date: dom.eventEndDateInput.value, end_shift: dom.eventEndShiftSelect.value, description: dom.eventDescriptionInput.value.trim(), created_by: CURRENT_USER_DISPLAY_NAME, created_at: nowIso, last_modified_by: '', last_modified_at: '' }; } if(new Date(eventData.end_date)<new Date(eventData.start_date)){ alert('Das Enddatum darf nicht vor dem Startdatum liegen.'); return; } if(isEditMode){ const index=cell.events.findIndex(e=>e.event_id===eventData.event_id); if(index>-1)cell.events[index]=eventData; }else{ cell.events.push(eventData); } saveData(); renderAll(); closeEventModal(); }
    function handleDeleteEvent(){ if(!isEditMode||!confirm(`Möchten Sie diesen Eintrag wirklich löschen?`))return; const cell=allTestCells.find(c=>c.ID===currentEditingEvent.cell_id); if(cell) cell.events=cell.events.filter(e=>e.event_id!==currentEditingEvent.event_id); saveData(); renderAll(); closeEventModal(); }

    dom.viewTabs.addEventListener('click', (e) => { const btn = e.target.closest('.tab-btn'); if (btn && btn.dataset.view) switchView(btn.dataset.view); });
    dom.prevWeekBtn.addEventListener('click', () => { timelineStartDate.setUTCDate(timelineStartDate.getUTCDate() - 7); renderTimelineView(); });
    dom.nextWeekBtn.addEventListener('click', () => { timelineStartDate.setUTCDate(timelineStartDate.getUTCDate() + 7); renderTimelineView(); });
    dom.jumpToTodayBtn.addEventListener('click', () => { timelineStartDate = getStartOfWeek(new Date()); renderTimelineView(); });
    dom.editTestCellBtn.addEventListener('click', () => toggleModalEditState(true)); 
    if(dom.newTestCellBtn) dom.newTestCellBtn.addEventListener('click', () => openTestCellModal()); 
    dom.closeTestCellModalBtn.addEventListener('click', closeTestCellModal);
    dom.cancelTestCellModalBtn.addEventListener('click', () => { if (isModalInEditMode) { if (isEditMode) openTestCellModal(currentEditingCell); else closeTestCellModal(); } else closeTestCellModal(); });
    dom.addEngineTypeBtn.addEventListener('click', () => addEngineTypeInput('', true)); 
    dom.saveTestCellBtn.addEventListener('click', handleSaveTestCell); 
    dom.deleteTestCellBtnHeader.addEventListener('click', handleDeleteTestCell);
    dom.editEventBtn.addEventListener('click', () => { dom.eventModalTitle.textContent = 'Blockade/Wartung bearbeiten'; toggleEventModalEditState(true); });
    dom.closeEventModalBtn.addEventListener('click', closeEventModal);
    dom.cancelEventModalBtn.addEventListener('click', () => { if (isModalInEditMode) { if (isEditMode) openEventModal(currentEditingEvent.cell_id, currentEditingEvent); else closeEventModal(); } else closeEventModal(); });
    dom.saveEventBtn.addEventListener('click', handleSaveEvent); 
    dom.deleteEventBtn.addEventListener('click', handleDeleteEvent);
    dom.eventTypeButtons.addEventListener('click', e => { const btn = e.target.closest('button'); if (btn) updateEventTypeButtons(btn.dataset.type); });
    dom.analyseViewOverviewBtn.addEventListener('click', () => { if (currentAnalyseView !== 'overview') { currentAnalyseView = 'overview'; renderAnalyseView(); } });
    dom.analyseViewTrendBtn.addEventListener('click', () => { if (currentAnalyseView !== 'trend') { currentAnalyseView = 'trend'; renderAnalyseView(); } });
    dom.analyseViewEngineBtn.addEventListener('click', () => { if (currentAnalyseView !== 'engine') { currentAnalyseView = 'engine'; renderAnalyseView(); } });
    document.body.addEventListener('click', (e) => { const fixedCell = e.target.closest('[data-cell-id-fixed]'); if (fixedCell) { const cell = allTestCells.find(c => c.ID === fixedCell.dataset.cellIdFixed); if (cell) openTestCellModal(cell); } });
    dom.timelineContainer.addEventListener('click', e => { 
        const cellTd = e.target.closest('td'); 
        if (!cellTd) return; 

        if (!canManageCellEvents) return;
        
        const cellId = cellTd.dataset.cellId; 
        const eventId = cellTd.dataset.eventId; 
        
        // --- NEUE LOGIK FÜR VIRTUELLE EVENTS ---
        // Wir erkennen virtuelle Events am Präfix oder am readOnly Flag + Typ Blocked
        const isReadOnly = cellTd.dataset.isReadonly === 'true'; 
        
        if (eventId && eventId.startsWith('virt_block_')) {
             // Extrahiere Issue ID aus eventId (Format: virt_block_ISSUEID_CELLID)
             // Etwas hacky, besser wir holen das echte Event Objekt
             const cell = allTestCells.find(c => c.ID === cellId);
             const event = cell ? cell.events.find(ev => ev.event_id === eventId) : null;
             
             if (event && event.sourceIssueId) {
                 if(confirm(`Dieser Block wird durch Issue ${event.sourceIssueId} gesteuert.\nMöchten Sie zum Maintenance Center wechseln, um das Issue zu bearbeiten?`)) {
                     // Weiterleitung zum Maintenance Center mit Deep Link
                     window.location.href = `/maintenance?openId=${event.sourceIssueId}`;
                 }
                 return;
             }
        }
        // ---------------------------------------

        if (isReadOnly) { 
            alert(`Dieser Block wird durch die Projekt-Timeline gesteuert und kann hier nicht bearbeitet werden.\n${cellTd.title}`); 
            return; 
        } 
        
        const cell = allTestCells.find(c => c.ID === cellId); 
        if (!cell) return; 
        if (eventId) { 
            const event = cell.events.find(e => e.event_id == eventId); 
            if (event) openEventModal(cellId, event); 
        } else { 
            const date = cellTd.dataset.date; 
            const shift = cellTd.dataset.shift; 
            if (date && shift) openEventModal(cellId, null, date, shift); 
        } 
    });
    
    async function loadEngineTypeSuggestions() {
        try {
            const response = await fetch('/api/process_template_options');
            if (response.ok) {
                const data = await response.json();
                // Wir interessieren uns nur für die Schlüssel (Engine Types), nicht für die Events
                const engineTypes = Object.keys(data).sort();
                
                // Datalist Element erstellen oder leeren
                let datalist = document.getElementById('availableEngineTypesList');
                if (!datalist) {
                    datalist = document.createElement('datalist');
                    datalist.id = 'availableEngineTypesList';
                    document.body.appendChild(datalist);
                }
                datalist.innerHTML = ''; // Reset

                engineTypes.forEach(type => {
                    const option = document.createElement('option');
                    option.value = type;
                    datalist.appendChild(option);
                });
            }
        } catch (error) {
            console.warn("Konnte Engine-Type-Vorschläge nicht laden:", error);
        }
    }
    

    loadEngineTypeSuggestions();
    loadData();
    loadModalFacilityUnits();
    
    // Event Listener für Add Button
    document.getElementById('btnAddRequiredUnit').addEventListener('click', () => {
        const input = document.getElementById('addRequiredUnitInput');
        const val = input.value.trim();
        if(!val) return;
        
        // ID finden
        const unitObj = availableFacilityUnits.find(u => u.name === val);
        if(unitObj) {
            addUnitTag(unitObj.id, unitObj.name, document.getElementById('requiredUnitsContainer'));
            input.value = '';
        } else {
            alert("Bitte eine gültige Anlage aus der Liste wählen.");
        }
    });
});
</script>
</body>
</html>
"""

QUALIFICATIONS_TABLE_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Inter%3Awght%40400%3B500%3B700%3B900&family=Noto+Sans%3Awght%40400%3B500%3B700%3B900" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet"/>
    <title>MBET - Qualifikationsübersicht</title>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <style>
        body { font-family: 'Inter', "Noto Sans", sans-serif; }
        .material-icons-outlined, .material-symbols-outlined { font-weight: normal; font-style: normal; font-size: 24px; line-height: 1; letter-spacing: normal; text-transform: none; display: inline-block; white-space: nowrap; word-wrap: normal; direction: ltr; -webkit-font-smoothing: antialiased; }

        .table-header-qual { @apply px-3 py-3.5 text-xs font-semibold uppercase tracking-wider; background-color: #F9FAFB; color: #111827; vertical-align: middle; line-height: 1.3; }
        .table-header-qual.text-center { text-align: center; }
        .table-cell-qual { @apply px-3 py-4 whitespace-nowrap text-sm; color: #1F2937; vertical-align: middle; }
        .table-cell-qual-merkmal { @apply px-3 py-4 whitespace-nowrap text-sm text-center; vertical-align: middle; min-width: 60px; cursor: pointer; position: relative; }
        .cell-content-wrapper { display: flex; align-items: center; justify-content: center; gap: 4px; height: 100%; }
        .qual-display { display: inline-block; line-height: 1; }
        .table-cell-qual-merkmal span.status-dot { display: inline-block; width: 12px; height: 12px; border-radius: 50%; vertical-align: middle; }
        .status-dot-placeholder { display: inline-block; width: 12px; height: 12px; border-radius: 50%; background-color: transparent; border: 1px dashed #D1D5DB; vertical-align: middle; }
        .cert-icon { font-size: 16px; color: #9CA3AF; }
        .table-cell-qual-merkmal span.status-X { background-color: #10B981 !important; }
        .table-cell-qual-merkmal span.status-TS { background-color: #F59E0B !important; }
        .table-cell-qual-merkmal span.status-TT { background-color: #EF4444 !important; }
        .table-cell-qual-merkmal span.status-TP { background-color: #6B7280 !important; }
        .sticky-header th { position: sticky; top: 0; background-color: #F9FAFB; z-index: 15; border-bottom: 1px solid #E5E7EB; }
        .sticky-col-name { position: sticky; left: 0; background-color: #fff; z-index: 5; width: 14rem; min-width: 14rem; }
        .sticky-header th.sticky-col-name { background-color: #F9FAFB; z-index: 20; border-bottom: 1px solid #E5E7EB;}
        .sticky-col-initials { position: sticky; left: 14rem; background-color: #fff; z-index: 5; width: 6rem; min-width: 6rem; }
        .sticky-header th.sticky-col-initials { left: 14rem; background-color: #F9FAFB; z-index: 20; border-bottom: 1px solid #E5E7EB;}
        table tbody tr:hover .sticky-col-name, table tbody tr:hover .sticky-col-initials { background-color: #F9FAFB; }
        .sticky-col-name, .sticky-header th.sticky-col-name, .sticky-col-initials, .sticky-header th.sticky-col-initials { border-right: 1px solid #E5E7EB; }
        .hidden { display: none !important; }
        .form-input-modal, .form-select-modal { @apply block w-full px-3 py-2 bg-white border border-slate-300 rounded-lg text-sm shadow-sm placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent; }
        .form-input-modal.is-invalid { @apply border-red-500 text-red-600 focus:ring-red-500; }        
        #merkmaleListe li { user-select: none; }
        #merkmaleListe li.dragging { opacity: 0.5; background-color: #cceeff; }

        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center sticky top-0 z-50 justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Qualifikationsübersicht</h1>
                    <div class="relative">
                        <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">search</span>
                        <input id="qualificationPageSearchInput" class="w-full pl-10 pr-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Mitarbeiter oder ID suchen..." type="text"/>
                    </div>
        </header>
        <div class="p-4 sm:p-6 lg:p-8">
            <div class="container mx-auto">
                <div id="qualifications-tile" class="bg-neutral-50 p-6 rounded-lg border-white">
                    <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
                        <div>
                            <h2 class="text-2xl font-bold text-gray-800"></h2>
                            <p class="text-gray-600 mt-1">.</p>
                        </div>
                        {% if session.get('is_admin') %}
                        <button id="editMerkmaleBtn" class="px-4 py-2 bg-[#0c7ff2] text-white text-sm font-medium rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition-colors flex items-center gap-2 py-3">
                            <span class="material-icons-outlined text-sm">edit_note</span>
                            <!-- <span>Qualifikationen bearbeiten</span> -->
                        </button>
                        {% endif %}
                    </div>

                    {% if grouped_qualifications %}
                        {% for group_name, qualifications_in_group in grouped_qualifications.items() %}
                        <div class="mb-8 qualification-group" data-group-name="{{ group_name }}">
                            <h3 class="text-xl font-semibold text-gray-700 mb-4 border-b border-gray-200 pb-2">{{ group_name }}</h3>
                            <div class="overflow-x-auto rounded-md border border-gray-200">
                                <table class="min-w-full qualification-table">
                                    <thead class="sticky-header">
                                        <tr>
                                            <th scope="col" class="table-header-qual sticky-col-name pl-4 pr-3 sm:pl-6">Name</th>
                                            <th scope="col" class="table-header-qual sticky-col-initials px-3">ID</th>
                                            {% for merkmal_name in merkmale_in_order %}
                                                <th scope="col" class="table-header-qual text-center px-3 min-w-[80px]">
                                                    {% if '/' in merkmal_name %}
                                                        {% set parts = merkmal_name.split('/', 1) %}
                                                        <span style="white-space: nowrap;">{{ parts[0] }}/</span><br><span style="white-space: nowrap;">{{ parts[1] }}</span>
                                                    {% else %}
                                                        <span style="white-space: nowrap;">{{ merkmal_name }}</span>
                                                    {% endif %}
                                                </th>
                                            {% endfor %}
                                        </tr>
                                    </thead>
                                    <tbody class="divide-y divide-gray-200 bg-white">
                                        {% for qual_entry in qualifications_in_group %}
                                        <tr data-id="{{ qual_entry.id }}" data-name="{{ qual_entry.name }}" class="hover:bg-gray-100 employee-row">
                                            <td class="table-cell-qual sticky-col-name font-medium pl-4 pr-3 sm:pl-6">{{ qual_entry.name }}</td>
                                            <td class="table-cell-qual sticky-col-initials px-3">{{ qual_entry.id.upper() }}</td>
                                            {% for merkmal_name_iter in merkmale_in_order %}
                                                {% set merkmal_data = namespace(wert='', start='', end='', cert='') %}
                                                {% for m_item in qual_entry.merkmale %}
                                                    {% if m_item.merkmal == merkmal_name_iter %}
                                                        {% set merkmal_data.wert = m_item.wert %}
                                                        {% set merkmal_data.start = m_item.start_date %}
                                                        {% set merkmal_data.end = m_item.end_date %}
                                                        {% set merkmal_data.cert = m_item.cert_path %}
                                                    {% endif %}
                                                {% endfor %}
                                                <td class="table-cell-qual-merkmal editable-qual-cell" 
                                                    data-merkmal="{{ merkmal_name_iter }}" 
                                                    data-status="{{ merkmal_data.wert|trim|upper }}"
                                                    data-start="{{ merkmal_data.start }}"
                                                    data-end="{{ merkmal_data.end }}"
                                                    data-cert="{{ merkmal_data.cert }}">
                                                    <!-- NEU: Wrapper Div für Flexbox-Zentrierung -->
                                                    <div class="cell-content-wrapper">
                                                        <span class="qual-display">
                                                        {% if merkmal_data.wert and merkmal_data.wert.strip() %}
                                                            <span class="status-dot status-{{ merkmal_data.wert|trim|upper }}" title="{{ merkmal_data.wert }}"></span>
                                                        {% else %}
                                                            <span class="status-dot-placeholder" title="- Leer -"></span>
                                                        {% endif %}
                                                        </span>
                                                        {% if merkmal_data.cert %}
                                                            <span class="material-icons-outlined cert-icon" title="Zertifikat vorhanden">description</span>
                                                        {% endif %}
                                                    </div>
                                                </td>
                                            {% endfor %}
                                        </tr>
                                        {% endfor %}
                                    </tbody>
                                </table>
                            </div>
                        </div>
                        {% endfor %}
                    {% else %}
                        <p class="text-center py-10 text-gray-500">Keine Qualifikationsdaten gefunden.</p>
                    {% endif %}
                </div>
            </div>  
        </div>
    </main>
</div>
<div id="qualEditModal" class="fixed inset-0 bg-gray-600 bg-opacity-75 overflow-y-auto h-full w-full flex items-center justify-center hidden z-50 p-4">
    <div class="relative mx-auto p-6 border w-full max-w-lg shadow-xl rounded-2xl bg-white">
        <div class="flex justify-between items-center pb-4 border-b border-slate-200">
            <h3 id="qualEditModalTitle" class="text-lg leading-6 font-bold text-gray-900">Qualifikation bearbeiten</h3>
            <button id="closeQualEditModalBtn" class="text-gray-400 hover:text-gray-600 p-1 rounded-full">
                <span class="material-icons-outlined">close</span>
            </button>
        </div>
        <div class="mt-5 space-y-5">
            <input type="hidden" id="qualEditUserId">
            <input type="hidden" id="qualEditMerkmalName">

            <div>
                <label for="qualEditStatus" class="block text-sm font-medium text-gray-700 mb-1">Status</label>
                <select id="qualEditStatus" class="form-select-modal rounded-lg"></select>
            </div>
            <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
                <div>
                    <label for="qualEditStartDate" class="block text-sm font-medium text-gray-700 mb-1">Startdatum</label>
                    <input type="date" id="qualEditStartDate" class="form-input-modal rounded-lg">
                </div>
                <div>
                    <label for="qualEditEndDate" class="block text-sm font-medium text-gray-700 mb-1">Abschlussdatum</label>
                    <input type="date" id="qualEditEndDate" class="form-input-modal rounded-lg">
                </div>
            </div>
            <div>
                <label for="qualEditCertFilename" class="block text-sm font-medium text-gray-700 mb-1">Zugeordnetes Zertifikat</label>
                <div class="mt-1 flex">
                    <input type="text" id="qualEditCertFilename" class="form-input-modal rounded-l-lg flex-1 bg-slate-100 min-w-0" readonly placeholder="Kein Zertifikat">
                    <button id="certFileRemoveBtn" type="button" class="relative -ml-px inline-flex items-center border border-gray-300 bg-slate-50 px-3 py-2 text-sm font-medium text-gray-600 hover:bg-slate-100" title="Zuweisung entfernen">
                        <span class="material-icons-outlined text-base">link_off</span>
                    </button>
                    <button id="certDownloadBtn" type="button" class="relative -ml-px hidden items-center border border-gray-300 bg-slate-50 px-3 py-2 text-sm font-medium text-gray-600 hover:bg-slate-100" title="Zertifikat herunterladen">
                        <span class="material-icons-outlined text-base">download</span>
                    </button>
                    <button id="certFilePickerBtn" type="button" class="relative -ml-px inline-flex items-center space-x-2 rounded-r-lg border border-gray-300 bg-slate-50 px-4 py-2 text-sm font-medium text-gray-600 hover:bg-slate-100">
                        <span class="material-icons-outlined text-base">upload_file</span>
                        <span>Hochladen</span>
                    </button>
                </div>
                <input type="file" id="hiddenCertFileInput" class="hidden" accept=".pdf,.jpg,.jpeg,.png,.doc,.docx">
                <p class="mt-2 text-xs text-gray-500">Beim Speichern wird eine neu hochgeladene Datei die alte Zuweisung für dieses Merkmal ersetzen.</p>
            </div>
        </div>
        <div class="mt-6 pt-5 flex justify-end space-x-3 border-t border-slate-200">
            <button id="cancelQualEditBtn" type="button" class="px-5 py-2.5 bg-slate-100 text-slate-700 text-sm font-semibold rounded-lg shadow-sm hover:bg-slate-200">Abbrechen</button>
            <button id="saveQualChangesBtn" type="button" class="px-5 py-2.5 bg-blue-600 text-white text-sm font-semibold rounded-lg shadow-sm hover:bg-blue-700 disabled:opacity-50">Speichern</button>
        </div>
    </div>
</div>

<div id="merkmaleModal" class="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full flex items-center justify-center hidden z-50 p-4">
    <div class="relative mx-auto p-5 border w-full max-w-2xl shadow-lg rounded-md bg-white">
        <div class="flex justify-between items-center pb-3 border-b">
            <h3 class="text-xl leading-6 font-medium text-gray-900">Qualifikations-Merkmale bearbeiten</h3>
            <button id="closeMerkmaleModalBtnCross" class="text-gray-400 hover:text-gray-600">
                <span class="material-icons-outlined">close</span>
            </button>
        </div>
        <div class="mt-4 px-2 sm:px-7 py-3 space-y-4">
            <div class="text-left">
                <label class="block text-sm font-medium text-gray-700 mb-1">Aktuelle Merkmale (Reihenfolge änderbar)</label>
                <ul id="merkmaleListe" class="border border-gray-300 rounded-md p-1 min-h-[8rem] max-h-60 overflow-y-auto bg-gray-50 space-y-1">
                </ul>
                 <p class="text-xs text-gray-500 mt-1">Ziehe Merkmale, um die Reihenfolge zu ändern. Entferne Merkmale mit dem Papierkorb-Symbol.</p>
            </div>
            <div class="flex items-end space-x-2 pt-2 border-t border-gray-200">
                <div class="flex-grow">
                    <label for="neuesMerkmalInput" class="block text-sm font-medium text-gray-700">Neues Merkmal hinzufügen</label>
                    <input type="text" id="neuesMerkmalInput" placeholder="Name des neuen Merkmals" class="form-input-modal"/>
                </div>
                <button id="addMerkmalToListBtn" class="bg-green-500 hover:bg-green-600 text-white font-semibold py-2 px-3 rounded-md text-sm whitespace-nowrap">
                    <span class="material-icons-outlined text-sm leading-none mr-1 -ml-1">add_circle_outline</span>Hinzufügen
                </button>
            </div>
        </div>
        <div class="items-center px-4 py-3 bg-gray-50 rounded-b-md border-t">
            <div class="flex justify-end space-x-3">
                <button id="closeMerkmaleModalBtn" class="px-4 py-2 bg-gray-200 text-gray-700 text-base font-medium rounded-md shadow-sm hover:bg-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-400">
                    Abbrechen
                </button>
                <button id="saveMerkmaleBtn" class="px-4 py-2 bg-blue-600 text-white text-base font-medium rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500">
                    Änderungen speichern
                </button>
            </div>
        </div>
    </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        // --- Logik für die einklappbare Sidebar ---
        const sidebar = document.getElementById('sidebar');
        const sidebarToggleBtn = document.getElementById('sidebar-toggle');
        if (sidebar && sidebarToggleBtn) {
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
            const applyState = (isCollapsed) => {
                if (isCollapsed) {
                    sidebar.classList.add('is-collapsed');
                    toggleBtnIcon.textContent = 'menu';
                } else {
                    sidebar.classList.remove('is-collapsed');
                    toggleBtnIcon.textContent = 'chevron_left';
                }
            };
            sidebarToggleBtn.addEventListener('click', () => {
                const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
                localStorage.setItem('sidebarCollapsed', isNowCollapsed);
                applyState(isNowCollapsed);
            });
            const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
            applyState(savedState);
        }

        // --- Logik für die Qualifikationstabelle ---
        const statusOptions = [
            { value: '', text: '- Leer -' }, { value: 'X', text: 'X (Abgeschlossen)' },
            { value: 'TS', text: 'TS (Gestartet)' }, { value: 'TT', text: 'TT (Überfällig)' },
            { value: 'TP', text: 'TP (Geplant)' }
        ];

        const qualificationPageSearchInput = document.getElementById('qualificationPageSearchInput');
        if(qualificationPageSearchInput) {
            qualificationPageSearchInput.addEventListener('input', (event) => { 
                const searchTerm = event.target.value.toLowerCase();
                const qualificationGroups = document.querySelectorAll('.qualification-group');
                qualificationGroups.forEach(groupDiv => {
                    const tableBody = groupDiv.querySelector('table.qualification-table tbody');
                    if (!tableBody) return;
                    const rows = Array.from(tableBody.querySelectorAll('tr.employee-row'));
                    let groupHasVisibleRows = false;
                    rows.forEach(row => {
                        const nameCell = row.cells[0]; 
                        const idCell = row.cells[1];
                        let matchesSearch = false;
                        if (nameCell && nameCell.textContent.toLowerCase().includes(searchTerm)) matchesSearch = true;
                        if (!matchesSearch && idCell && idCell.textContent.toLowerCase().includes(searchTerm)) matchesSearch = true;
                        if (matchesSearch) { row.style.display = ''; groupHasVisibleRows = true; } 
                        else { row.style.display = 'none'; }
                    });
                    if (groupHasVisibleRows || !searchTerm) { groupDiv.style.display = ''; } 
                    else { groupDiv.style.display = 'none'; }
                });
            });
        }

        const qualificationsTile = document.getElementById('qualifications-tile'); 
        const qualEditModal = document.getElementById('qualEditModal');
        const closeQualEditModalBtn = document.getElementById('closeQualEditModalBtn');
        const cancelQualEditBtn = document.getElementById('cancelQualEditBtn');
        const saveQualChangesBtn = document.getElementById('saveQualChangesBtn');
        const qualEditModalTitle = document.getElementById('qualEditModalTitle');
        const qualEditStatusSelect = document.getElementById('qualEditStatus');
        const qualEditStartDate = document.getElementById('qualEditStartDate');
        const qualEditEndDate = document.getElementById('qualEditEndDate');
        const qualEditCertFilenameInput = document.getElementById('qualEditCertFilename');
        const certFilePickerBtn = document.getElementById('certFilePickerBtn');
        const hiddenCertFileInput = document.getElementById('hiddenCertFileInput');
        const certFileRemoveBtn = document.getElementById('certFileRemoveBtn');
        const certDownloadBtn = document.getElementById('certDownloadBtn');
        let activeEditingCell = null;
        let selectedFile = null;

        if (qualificationsTile) {
            function validateDates() {
                const start = qualEditStartDate.value;
                const end = qualEditEndDate.value;
                let isValid = true;
                qualEditStartDate.classList.remove('is-invalid');
                qualEditEndDate.classList.remove('is-invalid');
                if (start && end && new Date(end) < new Date(start)) {
                    qualEditEndDate.classList.add('is-invalid');
                    qualEditEndDate.title = "Das Abschlussdatum darf nicht vor dem Startdatum liegen.";
                    isValid = false;
                } else {
                    qualEditEndDate.title = "";
                }
                saveQualChangesBtn.disabled = !isValid;
            }

            function openQualiEditModal(cell) {
                activeEditingCell = cell;
                const row = cell.closest('tr');
                const userId = row.dataset.id;
                const userName = row.dataset.name;
                const merkmalName = cell.dataset.merkmal;

                qualEditModalTitle.textContent = `"${merkmalName}" für ${userName} bearbeiten`;
                document.getElementById('qualEditUserId').value = userId;
                document.getElementById('qualEditMerkmalName').value = merkmalName;

                qualEditStatusSelect.innerHTML = '';
                statusOptions.forEach(opt => {
                    const option = document.createElement('option');
                    option.value = opt.value;
                    option.textContent = opt.text;
                    if (opt.value === cell.dataset.status) option.selected = true;
                    qualEditStatusSelect.appendChild(option);
                });

                qualEditStartDate.value = cell.dataset.start || '';
                qualEditEndDate.value = cell.dataset.end || '';

                const currentFilename = cell.dataset.cert || '';
                qualEditCertFilenameInput.value = currentFilename;
                hiddenCertFileInput.value = '';
                selectedFile = null;

                certDownloadBtn.classList.toggle('hidden', !currentFilename);

                validateDates();
                qualEditStartDate.addEventListener('change', validateDates);
                qualEditEndDate.addEventListener('change', validateDates);
                qualEditModal.classList.remove('hidden');
            }

            function closeQualiEditModal() {
                qualEditStartDate.removeEventListener('change', validateDates);
                qualEditEndDate.removeEventListener('change', validateDates);
                qualEditModal.classList.add('hidden');
                activeEditingCell = null;
            }

            async function saveQualiChanges() {
                if (!activeEditingCell) return;
                saveQualChangesBtn.disabled = true;
                let finalCertFilename = qualEditCertFilenameInput.value;
                 if (finalCertFilename.startsWith('Wird hochgeladen: ')) {
                    finalCertFilename = ''; // Klarer Zustand, wenn nur Upload relevant ist
                }

                if (selectedFile) {
                    const uploadFormData = new FormData();
                    uploadFormData.append('certificate_file', selectedFile);
                    uploadFormData.append('employee_id', document.getElementById('qualEditUserId').value);
                    uploadFormData.append('merkmal_name', document.getElementById('qualEditMerkmalName').value);
                    try {
                        const uploadResponse = await fetch("{{ url_for('api_upload_certificate') }}", { method: 'POST', body: uploadFormData });
                        const uploadResult = await uploadResponse.json();
                        if (!uploadResponse.ok) throw new Error(uploadResult.error || 'Fehler beim Upload');
                        finalCertFilename = uploadResult.filename;
                    } catch (error) {
                        alert('Fehler beim Datei-Upload: ' + error.message);
                        saveQualChangesBtn.disabled = false;
                        return;
                    }
                }

                const payload = {
                    id: document.getElementById('qualEditUserId').value,
                    merkmal: document.getElementById('qualEditMerkmalName').value,
                    new_value: document.getElementById('qualEditStatus').value,
                    start_date: document.getElementById('qualEditStartDate').value,
                    end_date: document.getElementById('qualEditEndDate').value,
                    cert_path: finalCertFilename
                };

                try {
                    const response = await fetch("{{ url_for('api_update_qualification_status') }}", {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify(payload)
                    });
                    const result = await response.json();
                    if (!response.ok) throw new Error(result.error || 'Speichern fehlgeschlagen.');

                    activeEditingCell.dataset.status = payload.new_value;
                    activeEditingCell.dataset.start = payload.start_date;
                    activeEditingCell.dataset.end = payload.end_date;
                    activeEditingCell.dataset.cert = payload.cert_path;

                    const displaySpan = activeEditingCell.querySelector('.qual-display');
                    displaySpan.innerHTML = payload.new_value ? `<span class="status-dot status-${payload.new_value.toUpperCase()}" title="${payload.new_value}"></span>` : '<span class="status-dot-placeholder" title="- Leer -"></span>';

                    let certIcon = activeEditingCell.querySelector('.cert-icon');
                    if (payload.cert_path && !certIcon) {
                        certIcon = document.createElement('span');
                        certIcon.className = 'material-icons-outlined cert-icon';
                        certIcon.title = 'Zertifikat vorhanden';
                        certIcon.textContent = 'description';
                        activeEditingCell.querySelector('.cell-content-wrapper').appendChild(certIcon);
                    } else if (!payload.cert_path && certIcon) {
                        certIcon.remove();
                    }
                    closeQualiEditModal();
                } catch (error) {
                    alert('Fehler beim Speichern der Qualifikation: ' + error.message);
                } finally {
                    saveQualChangesBtn.disabled = false;
                }
            }

            certFilePickerBtn.addEventListener('click', () => { hiddenCertFileInput.click(); });
            hiddenCertFileInput.addEventListener('change', (event) => {
                if (event.target.files && event.target.files.length > 0) {
                    selectedFile = event.target.files[0];
                    qualEditCertFilenameInput.value = `Wird hochgeladen: ${selectedFile.name}`;
                    certDownloadBtn.classList.add('hidden');
                }
            });
            certFileRemoveBtn.addEventListener('click', () => {
                qualEditCertFilenameInput.value = '';
                hiddenCertFileInput.value = '';
                selectedFile = null;
                certDownloadBtn.classList.add('hidden');
            });
            certDownloadBtn.addEventListener('click', () => {
                const employeeId = document.getElementById('qualEditUserId').value;
                const filename = qualEditCertFilenameInput.value;
                if(employeeId && filename && !filename.startsWith('Wird hochgeladen:')) {
                    window.open(`/download_certificate/${employeeId}/${filename}`, '_blank');
                }
            });

            qualificationsTile.addEventListener('click', function(event) {
                const cell = event.target.closest('.editable-qual-cell');
                const certIcon = event.target.closest('.cert-icon');
                if (certIcon && cell) {
                    event.stopPropagation();
                    const employeeId = cell.closest('tr').dataset.id;
                    const filename = cell.dataset.cert;
                    if (employeeId && filename) {
                        window.open(`/download_certificate/${employeeId}/${filename}`, '_blank');
                    }
                } else if (cell) {
                    openQualiEditModal(cell);
                }
            });

            closeQualEditModalBtn.addEventListener('click', closeQualiEditModal);
            cancelQualEditBtn.addEventListener('click', closeQualiEditModal);
            saveQualChangesBtn.addEventListener('click', saveQualiChanges);
        }

        const editMerkmaleBtn = document.getElementById('editMerkmaleBtn');
        const merkmaleModal = document.getElementById('merkmaleModal');
        const closeMerkmaleModalBtn = document.getElementById('closeMerkmaleModalBtn');
        const closeMerkmaleModalBtnCross = document.getElementById('closeMerkmaleModalBtnCross');
        const merkmaleListeUl = document.getElementById('merkmaleListe');
        const neuesMerkmalInput = document.getElementById('neuesMerkmalInput');
        const addMerkmalToListBtn = document.getElementById('addMerkmalToListBtn');
        const saveMerkmaleBtn = document.getElementById('saveMerkmaleBtn');
        let currentMerkmaleImModal = [];
        let sortableInstance = null;

        async function fetchAndDisplayMerkmale() {
            try {
                const response = await fetch("{{ url_for('api_get_merkmale') }}"); 
                if (!response.ok) throw new Error('Netzwerkantwort war nicht ok beim Laden der Merkmale.');
                const merkmaleFromServer = await response.json();
                currentMerkmaleImModal = [...merkmaleFromServer];
                renderMerkmaleListe();
            } catch (error) {
                console.error("Fehler beim Laden der Merkmale:", error);
                merkmaleListeUl.innerHTML = '<li class="p-2 text-red-600">Fehler: Merkmale konnten nicht geladen werden.</li>';
            }
        }

        function renderMerkmaleListe() {
            merkmaleListeUl.innerHTML = '';
            currentMerkmaleImModal.forEach((merkmalText) => {
                const li = document.createElement('li');
                li.className = 'flex justify-between items-center p-2 bg-white border border-gray-200 rounded hover:bg-gray-100 cursor-grab';
                const textSpan = document.createElement('span');
                textSpan.textContent = merkmalText;
                li.appendChild(textSpan);
                const deleteBtn = document.createElement('button');
                deleteBtn.innerHTML = '<span class="material-icons-outlined text-red-500 hover:text-red-700 text-lg leading-none">delete_outline</span>';
                deleteBtn.className = 'p-1 rounded hover:bg-red-100';
                deleteBtn.title = "Merkmal entfernen";
                deleteBtn.onclick = (e) => {
                    e.stopPropagation(); 
                    currentMerkmaleImModal = currentMerkmaleImModal.filter(m => m !== merkmalText);
                    renderMerkmaleListe();
                };
                li.appendChild(deleteBtn);
                merkmaleListeUl.appendChild(li);
            });
            if (sortableInstance) sortableInstance.destroy();
            if (merkmaleListeUl.children.length > 0 && typeof Sortable !== 'undefined') {
                 sortableInstance = Sortable.create(merkmaleListeUl, {
                    animation: 150,
                    ghostClass: 'bg-blue-100',
                    onEnd: function (evt) {
                        const movedItem = currentMerkmaleImModal.splice(evt.oldDraggableIndex, 1)[0];
                        currentMerkmaleImModal.splice(evt.newDraggableIndex, 0, movedItem);
                    }
                });
            }
        }

        if (editMerkmaleBtn) {
            editMerkmaleBtn.addEventListener('click', () => {
                fetchAndDisplayMerkmale();
                merkmaleModal.classList.remove('hidden');
            });
        }

        function closeModal() {
            merkmaleModal.classList.add('hidden');
            neuesMerkmalInput.value = '';
            if (sortableInstance) { sortableInstance.destroy(); sortableInstance = null; }
        }
        if (closeMerkmaleModalBtn) closeMerkmaleModalBtn.addEventListener('click', closeModal);
        if (closeMerkmaleModalBtnCross) closeMerkmaleModalBtnCross.addEventListener('click', closeModal);

        if (addMerkmalToListBtn) {
            addMerkmalToListBtn.addEventListener('click', () => {
                const neuesMerkmal = neuesMerkmalInput.value.trim();
                if (neuesMerkmal && !currentMerkmaleImModal.some(m => m.toLowerCase() === neuesMerkmal.toLowerCase())) {
                    currentMerkmaleImModal.push(neuesMerkmal);
                    renderMerkmaleListe();
                    neuesMerkmalInput.value = '';
                    neuesMerkmalInput.focus();
                } else if (currentMerkmaleImModal.some(m => m.toLowerCase() === neuesMerkmal.toLowerCase())) {
                    alert("Dieses Merkmal existiert bereits.");
                } else {
                    alert("Bitte geben Sie einen Namen für das neue Merkmal ein.");
                }
            });
        }

        if (saveMerkmaleBtn) {
            saveMerkmaleBtn.addEventListener('click', async () => {
                const finalMerkmalOrder = [...currentMerkmaleImModal]; 
                try {
                    const response = await fetch("{{ url_for('api_update_merkmale') }}", {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ merkmale: finalMerkmalOrder })
                    });
                    const result = await response.json();
                    if (!response.ok) {
                        throw new Error(result.error || `HTTP-Fehler ${response.status}`);
                    }
                    alert(result.message || "Merkmale erfolgreich gespeichert!");
                    closeModal();
                    location.reload(); 
                } catch (error) {
                    console.error("Fehler beim Speichern der Merkmale:", error);
                    alert("Fehler: " + error.message);
                }
            });
        }
    });
</script>

</body></html>
"""

EMPLOYEE_PRESENCE_HTML_CONTENT = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700&family=Inter%3Awght@400%3B500%3B700%3B900&family=Noto+Sans%3Awght@400%3B500%3B700%3B900" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <title>MBET - Anwesenheitsübersicht</title>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <style>
        body { font-family: 'Inter', "Noto Sans", sans-serif; }
        .material-icons-outlined { font-weight: normal; font-style: normal; font-size: 24px; line-height: 1; letter-spacing: normal; text-transform: none; display: inline-block; white-space: nowrap; word-wrap: normal; direction: ltr; -webkit-font-smoothing: antialiased; }
        .presence-table-fixed th, .presence-table-fixed td { @apply px-4 py-4 text-sm; vertical-align: middle; }

        .presence-table-scrollable th, .presence-table-scrollable td { 
            @apply px-2 py-4 text-sm whitespace-nowrap align-middle; 
            text-align: center !important;
            min-width: 60px; max-width: 60px; 
        }
        .presence-table-scrollable th { @apply font-semibold text-slate-600; }

        .presence-table-scrollable th.kw-header { background-color: #F8FAFC; color: #475569; }
        .presence-table-scrollable td.kw-cell { background-color: #F8FAFC; font-weight: 500; color: #4B5563; }
        .day-cell.is-today { background-color: #FEF3C7 !important; } 
        .day-cell.is-weekend:not(.is-today) { background-color: #F8FAFC; } 
        .day-cell.is-holiday { background: #DBEAFE !important; color: #1E40AF !important; } 
        .day-cell.has-event { background: repeating-linear-gradient(45deg, #dbeafe, #dbeafe 10px, #bfdbfe 10px, #bfdbfe 20px) !important; color: #1E3A8A; font-weight: 600; }

        .sticky-header-presence th { position: sticky; top: 0; background-color: #fff; z-index: 15; border-bottom: 1px solid #e2e8f0; }
        .presence-table-fixed .sticky-col-presence { position: sticky; left: 0; background-color: #fff; z-index: 5; }
        .presence-table-fixed .sticky-header-presence th.sticky-col-presence { z-index: 20; background-color: #fff; }
        .presence-table-fixed tbody tr:hover .sticky-col-presence { background-color: #F8FAFC; }
        .presence-table-fixed .sticky-col-presence { border-right: 1px solid #e2e8f0; }
        .group-separator-row td { @apply px-4 py-2 text-left text-sm font-semibold; background-color: #f1f5f9; color: #334155; position: sticky; left: 0; z-index: 6; }

        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center sticky top-0 z-50 justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
            <h1 class="text-2xl font-bold tracking-tight text-slate-800">Anwesenheitsübersicht</h1>
            <div class="flex items-center gap-3">
                <div class="relative">
                    <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">search</span>
                    <input id="employeePresenceSearchInput" class="w-64 pl-10 pr-4 py-2 rounded-lg border border-gray-300 bg-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent" placeholder="Mitarbeiter suchen..." type="text"/>
                </div>
            </div>
        </header>
        <div class="p-4 sm:p-6 lg:p-8">
                {% if session.get('is_admin') %}
                <div id="upload-container" class="mb-6">
                     <div id="upload-drop-zone" class="flex justify-center items-center w-full px-6 py-10 border-2 border-dashed border-gray-300 rounded-lg cursor-pointer hover:border-blue-500 hover:bg-gray-50 transition-colors duration-200">
                        <div class="text-center">
                            <span class="material-icons-outlined text-4xl text-gray-400">upload_file</span>
                            <p class="mt-2 text-sm text-gray-600"><span class="font-semibold">Neue Anwesenheits-CSV hierher ziehen</span> oder klicken zum Auswählen</p>
                            <p class="text-xs text-gray-500 mt-1">Die bestehende `MBET_employees.csv` wird vollständig ersetzt.</p>
                        </div>
                        <input type="file" id="file-input" class="hidden" accept=".csv">
                    </div>
                    <div id="upload-status" class="mt-2 text-sm text-center"></div>
                </div>
                {% endif %}

            <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
                <div>
                    <p class="text-gray-600 mt-1 text-sm"></p>
                </div>
                <div class="flex items-center gap-3 mt-4 md:mt-0">
                    <button id="currentWeekBtn" class="rounded-full bg-white px-3 py-3 text-sm font-semibold text-slate-700 shadow-sm ring-1 ring-inset ring-slate-200 hover:bg-slate-50" title="Zur aktuellen Woche springen">
                        Heute
                    </button>
                    <div class="flex items-center rounded-full ring-1 ring-slate-200 bg-white shadow-sm">
                        <button id="prevWeekBtn" class="px-4 py-2 text-slate-500 hover:bg-slate-100 rounded-l-full disabled:opacity-50">
                            <span class="material-icons-outlined">chevron_left</span>
                        </button>
                        <span id="timelineRangeLabel" class="text-sm font-medium text-slate-700 px-4 w-32 text-center border-l border-r border-slate-200">
                            2 Wochen
                        </span>
                        <button id="nextWeekBtn" class="px-3 py-2 text-slate-500 hover:bg-slate-100 rounded-r-full disabled:opacity-50">
                            <span class="material-icons-outlined">chevron_right</span>
                        </button>
                    </div>
                </div>
            </div>

            <div id="presenceTableContainer" class="flex rounded-lg border border-slate-200 overflow-hidden bg-white">
                <p class="text-center py-10 text-gray-500 w-full">Lade Anwesenheitsdaten...</p>
            </div>
            <div>
                <p class="text-gray-600 mt-1 text-sm">Zuletzt geladen aus MBET_employees.csv: <span id="lastLoadedTimestamp">{{ presence_data.last_loaded }}</span></p>
            </div>
        </div>
    </main>
</div>
<script>
    document.addEventListener('DOMContentLoaded', () => {
        const sidebar = document.getElementById('sidebar');
        const sidebarToggleBtn = document.getElementById('sidebar-toggle');
        if (sidebar && sidebarToggleBtn) {
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
            const applyState = (isCollapsed) => {
                if (isCollapsed) {
                    sidebar.classList.add('is-collapsed');
                    toggleBtnIcon.textContent = 'menu';
                } else {
                    sidebar.classList.remove('is-collapsed');
                    toggleBtnIcon.textContent = 'chevron_left';
                }
            };
            sidebarToggleBtn.addEventListener('click', () => {
                const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
                localStorage.setItem('sidebarCollapsed', isNowCollapsed);
                applyState(isNowCollapsed);
            });
            const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
            applyState(savedState);
        }

    const tableContainer = document.getElementById('presenceTableContainer');
    const lastLoadedTimestampEl = document.getElementById('lastLoadedTimestamp');
    const prevWeekBtn = document.getElementById('prevWeekBtn');
    const currentWeekBtn = document.getElementById('currentWeekBtn');
    const nextWeekBtn = document.getElementById('nextWeekBtn');
    const searchInput = document.getElementById('employeePresenceSearchInput');

    let currentOffset = 0;
    const numWeeksToDisplay = 2;
    let minDateInCsv = null;
    let maxDateInCsv = null;
    const dropZone = document.getElementById('upload-drop-zone');
    const fileInput = document.getElementById('file-input');
    const statusDiv = document.getElementById('upload-status');

    if (dropZone) {
        dropZone.addEventListener('click', () => fileInput.click());
        fileInput.addEventListener('change', (e) => { if (e.target.files.length > 0) handleFileUpload(e.target.files[0]); });
        dropZone.addEventListener('dragover', (e) => { e.preventDefault(); dropZone.classList.add('border-blue-500', 'bg-blue-50'); });
        dropZone.addEventListener('dragleave', (e) => { e.preventDefault(); dropZone.classList.remove('border-blue-500', 'bg-blue-50'); });
        dropZone.addEventListener('drop', (e) => {
            e.preventDefault();
            dropZone.classList.remove('border-blue-500', 'bg-blue-50');
            if (e.dataTransfer.files.length > 0) handleFileUpload(e.dataTransfer.files[0]);
        });
        async function handleFileUpload(file) {
            if (!file.name.toLowerCase().endsWith('.csv')) {
                statusDiv.textContent = 'Fehler: Es werden nur .csv-Dateien akzeptiert.';
                statusDiv.className = 'mt-2 text-sm text-center text-red-600'; return;
            }
            statusDiv.textContent = 'Datei wird hochgeladen und verarbeitet...';
            statusDiv.className = 'mt-2 text-sm text-center text-blue-600';
            const formData = new FormData();
            formData.append('presence_file', file);
            try {
                const response = await fetch("{{ url_for('api_upload_employee_presence') }}", { method: 'POST', body: formData });
                const result = await response.json();
                if (response.ok) {
                    statusDiv.textContent = result.message;
                    statusDiv.className = 'mt-2 text-sm text-center text-green-600';
                    setTimeout(() => { location.reload(); }, 2000);
                } else { throw new Error(result.error || 'Unbekannter Fehler beim Upload.'); }
            } catch (error) {
                statusDiv.textContent = `Fehler: ${error.message}`;
                statusDiv.className = 'mt-2 text-sm text-center text-red-600';
            }
        }
    }

    async function fetchAndRenderPresenceData(offset) {
        tableContainer.innerHTML = '<p class="text-center py-10 text-gray-500 w-full">Lade Anwesenheitsdaten...</p>';
        try {
            const response = await fetch(`{{ url_for('api_get_employee_presence_data') }}?offset=${offset}&num_weeks=${numWeeksToDisplay}`);
            if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
            const data = await response.json();
            currentOffset = data.current_week_offset;
            minDateInCsv = data.min_date_in_csv_iso ? new Date(data.min_date_in_csv_iso) : null;
            maxDateInCsv = data.max_date_in_csv_iso ? new Date(data.max_date_in_csv_iso) : null;
            renderPresenceTable(data);
            if (lastLoadedTimestampEl) lastLoadedTimestampEl.textContent = data.last_loaded;
            updateNavButtonStates();
        } catch (error) {
            console.error("Fehler beim Laden der Anwesenheitsdaten:", error);
            tableContainer.innerHTML = '<p class="text-center py-10 text-red-500 w-full">Fehler beim Laden der Daten.</p>';
        }
    }

    function updateNavButtonStates() {
        prevWeekBtn.disabled = false; nextWeekBtn.disabled = false;
        function timedeltaDays(days) { return new Date(new Date().setDate(new Date().getDate() + days) - new Date()); }
        if (minDateInCsv && numWeeksToDisplay > 0) {
            const currentDisplayStartDate = new Date();
            currentDisplayStartDate.setDate(currentDisplayStartDate.getDate() - currentDisplayStartDate.getDay() + 1 + (currentOffset * 7));
            const prevDisplayPotentialStart = new Date(currentDisplayStartDate);
            prevDisplayPotentialStart.setDate(prevDisplayPotentialStart.getDate() - 7);
            if (prevDisplayPotentialStart < (minDateInCsv - timedeltaDays(minDateInCsv.getDay())) ) {
                 prevWeekBtn.disabled = true;
            }
        }
        if (maxDateInCsv && numWeeksToDisplay > 0) {
            const currentDisplayEndDate = new Date();
            currentDisplayEndDate.setDate(currentDisplayEndDate.getDate() - currentDisplayEndDate.getDay() + (currentOffset * 7) + ((numWeeksToDisplay -1) * 7) + 6 );
            const nextDisplayPotentialEnd = new Date(currentDisplayEndDate);
            nextDisplayPotentialEnd.setDate(nextDisplayPotentialEnd.getDate() + 7);
             if (nextDisplayPotentialEnd > (maxDateInCsv + timedeltaDays(6-maxDateInCsv.getDay())) ) {
                 nextWeekBtn.disabled = true;
            }
        }
    }

function renderPresenceTable(data) {
        tableContainer.innerHTML = '';
        if (!data.employee_groups || data.employee_groups.length === 0) {
            tableContainer.innerHTML = '<p class="text-center py-10 text-gray-500 w-full">Keine Anwesenheitsdaten für diesen Zeitraum.</p>';
            return;
        }

        const fixedTableDiv = document.createElement('div');
        fixedTableDiv.className = 'flex-shrink-0';
        const fixedTable = document.createElement('table');
        fixedTable.className = 'presence-table-fixed';
        fixedTableDiv.appendChild(fixedTable);
        const fixedThead = fixedTable.createTHead();
        fixedThead.className = 'sticky-header-presence';
        const fixedHeaderRow = fixedThead.insertRow();
        fixedHeaderRow.innerHTML = `<th class="sticky-col-presence text-left w-48">Name</th><th class="sticky-col-presence text-left w-24" style="left: 12rem;">Initialen</th>`;
        const fixedTbody = fixedTable.createTBody();
        fixedTbody.className = 'divide-y divide-gray-200 bg-white';

        const scrollableTableWrapper = document.createElement('div');
        scrollableTableWrapper.className = 'flex-grow overflow-x-auto';
        const scrollableTable = document.createElement('table');
        scrollableTable.className = 'min-w-full presence-table-scrollable';
        scrollableTableWrapper.appendChild(scrollableTable);
        const scrollableThead = scrollableTable.createTHead();
        scrollableThead.className = 'sticky-header-presence';
        const scrollableHeaderRow = scrollableThead.insertRow();
        data.headers.forEach(header_col => {
            const th = document.createElement('th');
            th.className = `${header_col.type === 'kw' ? 'kw-header' : ('day-header ' + (header_col.is_today ? 'is-today' : '') + (header_col.is_weekend && !header_col.is_today ? ' is-weekend' : ''))}`;
            th.textContent = header_col.text;
            scrollableHeaderRow.appendChild(th);
        });
        const scrollableTbody = scrollableTable.createTBody();
        scrollableTbody.className = 'divide-y divide-gray-200 bg-white';

        data.employee_groups.forEach(group => {
            const fixedGroupRow = fixedTbody.insertRow();
            fixedGroupRow.className = 'group-separator-row';
            fixedGroupRow.dataset.groupName = group.group_name;
            const fixedGroupCell = fixedGroupRow.insertCell();
            fixedGroupCell.colSpan = 2;
            fixedGroupCell.textContent = group.group_name;

            const scrollableGroupRow = scrollableTbody.insertRow();
            scrollableGroupRow.className = 'group-separator-row';
            scrollableGroupRow.dataset.groupName = group.group_name;
            const scrollableGroupCell = scrollableGroupRow.insertCell();
            scrollableGroupCell.colSpan = data.headers.length;
            scrollableGroupCell.innerHTML = `<span style="visibility: hidden;">${group.group_name}</span>`;

            group.employees.forEach(emp => {
                const fixedEmpRow = fixedTbody.insertRow();
                fixedEmpRow.className = 'hover:bg-gray-50 employee-data-row';
                fixedEmpRow.dataset.groupBelongsTo = group.group_name;
                fixedEmpRow.innerHTML = `
                    <td class="sticky-col-presence font-medium w-48">
                        <div class="truncate" title="${emp.name}">${emp.name}</div>
                    </td>
                    <td class="sticky-col-presence w-24" style="left: 12rem;">${emp.initials}</td>
                `;

                const scrollableEmpRow = scrollableTbody.insertRow();
                scrollableEmpRow.className = 'hover:bg-gray-50 employee-data-row';
                scrollableEmpRow.dataset.groupBelongsTo = group.group_name;
                emp.presence.forEach((presence_item, index) => {
                    const cell = scrollableEmpRow.insertCell();
                    if (presence_item.type === 'kw') {
                        cell.className = 'kw-cell';
                        cell.textContent = presence_item.shift;
                    } else if (presence_item.type === 'day') {
                        const day_header = data.headers[index];
                        cell.className = `day-cell ${day_header.is_today ? 'is-today' : ''} ${day_header.is_weekend && !day_header.is_today ? 'is-weekend' : ''} ${presence_item.is_holiday_from_data ? 'is-holiday' : ''} ${presence_item.event ? 'has-event' : ''}`;
                        cell.title = presence_item.event ? presence_item.event : (day_header.is_weekend ? 'Wochenende' : 'Anwesend');
                        cell.textContent = presence_item.event || ' '; 
                    } else { cell.textContent = '?'; }
                });
            });
        });

        tableContainer.appendChild(fixedTableDiv);
        tableContainer.appendChild(scrollableTableWrapper);
        synchronizeRowHeights();
        scrollToRelevantPosition(scrollableTableWrapper, scrollableTable);
        applySearchFilter(searchInput.value);
    }

    function synchronizeRowHeights() {
        const fixedTableBody = document.querySelector('.presence-table-fixed tbody');
        const scrollableTableBody = document.querySelector('.presence-table-scrollable tbody');
        if (fixedTableBody && scrollableTableBody) {
            const fixedRows = fixedTableBody.querySelectorAll('tr');
            const scrollableRows = scrollableTableBody.querySelectorAll('tr');
            const numRows = Math.min(fixedRows.length, scrollableRows.length);
            for (let i = 0; i < numRows; i++) {
                fixedRows[i].style.height = ''; scrollableRows[i].style.height = '';
            }
            for (let i = 0; i < numRows; i++) {
                const fixedRowHeight = fixedRows[i].offsetHeight;
                const scrollableRowHeight = scrollableRows[i].offsetHeight;
                const maxHeight = Math.max(fixedRowHeight, scrollableRowHeight);
                fixedRows[i].style.height = maxHeight + 'px';
                scrollableRows[i].style.height = maxHeight + 'px';
            }
        }
    }

    function scrollToRelevantPosition(scrollWrapper, scrollableTable) {
        if (scrollWrapper && scrollableTable) {
            const todayHeaderCell = scrollableTable.querySelector('thead th.is-today');
            if (todayHeaderCell) {
                const containerWidth = scrollWrapper.offsetWidth;
                const scrollTarget = todayHeaderCell.offsetLeft - (containerWidth / 4);
                scrollWrapper.scrollLeft = Math.max(0, scrollTarget);
            } else {
                 const firstKwHeader = scrollableTable.querySelector('thead th.kw-header');
                 if(firstKwHeader) {
                    scrollWrapper.scrollLeft = Math.max(0, firstKwHeader.offsetLeft - 20);
                 }
            }
        }
    }

    function applySearchFilter(searchTerm) {
        const term = searchTerm.toLowerCase();
        const fixedTableBody = document.querySelector('.presence-table-fixed tbody');
        const scrollableTableBody = document.querySelector('.presence-table-scrollable tbody');
        if (!fixedTableBody || !scrollableTableBody) return;
        const fixedRows = fixedTableBody.querySelectorAll('tr');
        const scrollableRows = scrollableTableBody.querySelectorAll('tr');
        fixedRows.forEach((fixedRow, index) => {
            const scrollableRow = scrollableRows[index];
            if (!scrollableRow) return;
            if (fixedRow.classList.contains('employee-data-row')) {
                const nameCell = fixedRow.cells[0];
                const initialsCell = fixedRow.cells[1];
                let matchesSearch = false;
                if (nameCell && nameCell.textContent.toLowerCase().includes(term)) matchesSearch = true;
                if (!matchesSearch && initialsCell && initialsCell.textContent.toLowerCase().includes(term)) matchesSearch = true;
                fixedRow.style.display = (matchesSearch || !term) ? '' : 'none';
                scrollableRow.style.display = (matchesSearch || !term) ? '' : 'none';
            }
        });
        fixedRows.forEach((fixedRow, index) => {
            if (fixedRow.classList.contains('group-separator-row')) {
                let nextSibling = fixedRow.nextElementSibling;
                let groupHasVisibleDataRows = false;
                while(nextSibling && nextSibling.classList.contains('employee-data-row')) {
                    if (nextSibling.style.display !== 'none') {
                        groupHasVisibleDataRows = true;
                        break;
                    }
                    nextSibling = nextSibling.nextElementSibling;
                }
                fixedRow.style.display = (groupHasVisibleDataRows || !term) ? '' : 'none';
                scrollableRows[index].style.display = (groupHasVisibleDataRows || !term) ? '' : 'none';
            }
        });
        synchronizeRowHeights();
    }

    if (searchInput) {
        searchInput.addEventListener('input', (event) => applySearchFilter(event.target.value));
    }
    prevWeekBtn.addEventListener('click', () => { fetchAndRenderPresenceData(currentOffset - 1); });
    currentWeekBtn.addEventListener('click', () => { fetchAndRenderPresenceData(0); });
    nextWeekBtn.addEventListener('click', () => { fetchAndRenderPresenceData(currentOffset + 1); });
    window.addEventListener('resize', synchronizeRowHeights);
    fetchAndRenderPresenceData(currentOffset);
});
</script>
</body></html>
"""

CONSUMABLE_ORDER_PAGE_HTML = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
    <title>MBET - Bestellsystem</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
    <style>
        body { font-family: 'Inter', sans-serif; }
        .text-primary { color: #3B82F6; }
        .bg-primary { background-color: #3B82F6; }
        .text-slate-text { color: #334155; }
        .border-slate-border { border-color: #cbd5e1; }
        .custom-scrollbar::-webkit-scrollbar { width: 8px; height: 8px; }
        .custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
        .custom-scrollbar::-webkit-scrollbar-thumb {
            background: transparent;
            border-radius: 10px;
            transition: background-color 0.2s ease-in-out;
        }
        .custom-scrollbar:hover::-webkit-scrollbar-thumb {
            background: #9ca3af;
        }
        .scroll-fade {
            -webkit-mask-image: linear-gradient(to bottom, black 90%, transparent 100%);
            mask-image: linear-gradient(to bottom, black 90%, transparent 100%);
        }

        .status-badge { padding: 0.125rem 0.625rem; font-size: 0.75rem; font-weight: 500; border-radius: 9999px; display: inline-flex; align-items: center; text-transform: capitalize; }
        .status-requested { background-color: #f1f5f9; color: #475569; }
        .status-inprogress { background-color: #dbeafe; color: #2563eb; }
        .status-completed { background-color: #dcfce7; color: #166534; }

        .kanban-column { display: flex; flex-direction: column; background-color: #e5e7eb; border-radius: 0.75rem; padding: 1rem; }
        .kanban-card-container { flex-grow: 1; min-height: 200px; overflow-y: auto; display: flex; flex-direction: column; gap: 0.75rem; height: 24rem; }
        .kanban-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; }
        .kanban-card { background-color: #ffffff; border-radius: 0.5rem; padding: 0.75rem; box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1); cursor: grab; transition: all 0.2s ease-in-out; position: relative; }
        .kanban-card:active { cursor: grabbing; }
        .kanban-card:hover { box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); transform: translateY(-1px); }
        .kanban-card.opacity-50 { background-color: #eff6ff; outline: 2px solid #3b82f6; }

        .kanban-card.is-critical { border: 1px solid #f87171; background-color: #fef2f2; }
        .critical-icon { position: absolute; top: 0.5rem; right: 0.5rem; font-size: 1rem; color: #ef4444; }

        .modal { transition: opacity 0.25s ease; }
        .modal-container { max-height: 90vh; display: flex; flex-direction: column; }
        body.modal-active { overflow-y: hidden; }

        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label,
        #sidebar.is-collapsed .sidebar-title,
        #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link,
        #sidebar.is-collapsed .sidebar-header,
        #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined,
        #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
        
        .readonly-input { background-color: #f8fafc; color: #64748b; cursor: default; }
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <main class="flex-1 overflow-y-auto bg-neutral-50 text-slate-text">
        <header class="flex items-center justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Bestellsystem für Verbrauchsmaterialien</h1>
        </header>

        <div class="p-4 sm:p-6 lg:p-8">
            <div class="mb-12">
                <h2 class="text-2xl font-semibold text-slate-800 mb-6">Bestellübersicht</h2>
                <div id="kanban-container-parent" class="grid grid-cols-1 md:grid-cols-3 gap-6">
                    <div class="kanban-column" data-status="requested"><div class="kanban-header"><h3 class="font-semibold text-slate-700">Notwendig</h3><button class="text-primary hover:text-blue-700" onclick="openModal('templateModal')"><span class="material-icons-outlined">add_circle</span></button></div><div class="kanban-card-container custom-scrollbar"></div></div>
                    <div class="kanban-column" data-status="inprogress"><div class="kanban-header"><h3 class="font-semibold text-slate-700">In Bearbeitung</h3></div><div class="kanban-card-container custom-scrollbar"></div></div>
                    <div class="kanban-column" data-status="completed"><div class="kanban-header"><h3 class="font-semibold text-slate-700">Abgeschlossen</h3></div><div class="kanban-card-container custom-scrollbar"></div></div>
                </div>
            </div>

            <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-28">
                <div class="bg-white p-5 rounded-lg shadow-md flex items-center space-x-4"><div class="p-3 bg-gray-100 rounded-full"><span class="material-icons-outlined text-gray-500 text-3xl">inbox</span></div><div><p class="text-sm text-gray-500">Notwendig</p><p id="kpi-requested" class="text-2xl font-bold text-slate-800">0</p></div></div>
                <div class="bg-white p-5 rounded-lg shadow-md flex items-center space-x-4"><div class="p-3 bg-blue-100 rounded-full"><span class="material-icons-outlined text-blue-500 text-3xl">autorenew</span></div><div><p class="text-sm text-gray-500">In Bearbeitung</p><p id="kpi-inprogress" class="text-2xl font-bold text-slate-800">0</p></div></div>
                <div class="bg-white p-5 rounded-lg shadow-md flex items-center space-x-4"><div class="p-3 bg-red-100 rounded-full"><span class="material-icons-outlined text-red-500 text-3xl">error</span></div><div><p class="text-sm text-gray-500">Kritisch & Offen</p><p id="kpi-critical" class="text-2xl font-bold text-slate-800">0</p></div></div>
                <div id="kpi-expiry-tile" class="bg-white p-5 rounded-lg shadow-md flex items-center space-x-4 transition-all duration-300 cursor-pointer hover:shadow-lg hover:-translate-y-0.5" onclick="switchToExpiryView()"><div id="kpi-expiry-icon-container" class="p-3 bg-orange-100 rounded-full transition-colors duration-300"><span class="material-icons-outlined text-orange-500 text-3xl">calendar_today</span></div><div><p class="text-sm text-gray-500">Artikel mit MHD</p><p id="kpi-expiry" class="text-2xl font-bold text-slate-800">0</p></div></div>
            </div>

            <div id="table-view" class="">
                <div class="mb-6">
                    <nav id="viewTabs" class="-mb-px flex space-x-8 border-b border-slate-200">
                        <button data-view="table" class="tab-btn whitespace-nowrap border-b-2 px-1 pb-3 text-sm font-medium border-primary text-primary">Alle Bestellungen</button>
                        <button data-view="templates" class="tab-btn whitespace-nowrap border-b-2 px-1 pb-3 text-sm font-medium border-transparent text-slate-500 hover:border-slate-300 hover:text-slate-700">Bestellvorlagen</button>
                        <button data-view="expiry" class="tab-btn whitespace-nowrap border-b-2 px-1 pb-3 text-sm font-medium border-transparent text-slate-500 hover:border-slate-300 hover:text-slate-700">Verfallsdaten</button>
                        <button data-view="analyse" class="tab-btn whitespace-nowrap border-b-2 px-1 pb-3 text-sm font-medium border-transparent text-slate-500 hover:border-slate-300 hover:text-slate-700">Analyse</button>
                    </nav>
                </div>
                <div id="tableContainer" class="bg-white rounded-lg shadow p-6">
                    <div class="flex flex-col sm:flex-row justify-between items-center mb-6 gap-4"><h2 class="text-xl font-semibold text-slate-800">Bestellhistorie</h2><div class="relative w-full sm:w-64"><span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">search</span><input id="tableSearchInput" class="w-full pl-10 pr-4 py-2 rounded-lg border border-gray-300 bg-white focus:outline-none focus:ring-2 focus:ring-primary" placeholder="Tabelle durchsuchen..." type="text"/></div></div>
                    <div class="overflow-x-auto custom-scrollbar"><table class="w-full min-w-[1200px] text-sm text-left text-slate-text"><thead class="text-xs text-gray-700 uppercase bg-gray-50">
                        <tr>
                            <th class="px-6 py-3 rounded-l-lg" scope="col">ID</th>
                            <th class="px-6 py-3" scope="col">Artikel</th>
                            <th class="px-6 py-3" scope="col">Menge</th>
                            <th class="px-6 py-3" scope="col">Einheit</th>
                            <th class="px-6 py-3" scope="col">Angefordert</th>
                            <th class="px-6 py-3" scope="col">Angefordert am</th>
                            <th class="px-6 py-3" scope="col">Status</th>
                            <th class="px-6 py-3" scope="col">Bestellt am</th>
                            <th class="px-6 py-3" scope="col">Lieferung</th>
                            <th class="px-6 py-3" scope="col">Doku.</th>
                            <th class="px-6 py-3 rounded-r-lg" scope="col"></th>
                        </tr>
                    </thead><tbody id="orderHistoryTableBody"></tbody></table></div>
                    <div id="table-footer" class="text-center mt-4"></div>
                </div>
                <div id="analyseContainer" class="bg-slate-50 rounded-lg p-0 md:p-6 hidden">
                    <div class="mb-6 px-6 md:px-0">
                        <h2 class="text-2xl font-bold text-slate-900 mb-2">Bestellanalyse</h2>
                        <p class="text-sm text-slate-600">Übersicht der Bestellvorgänge und -zeiten.</p>
                    </div>
                    <div id="analyseChartsContainer" class="grid grid-cols-1 lg:grid-cols-3 gap-6"></div>
                </div>
                
                <!-- TEMPLATES CONTAINER -->
                <div id="templatesContainer" class="bg-white rounded-lg shadow p-6 hidden">
                    <div class="flex justify-between items-center mb-6">
                        <h2 class="text-xl font-semibold text-slate-800">Gespeicherte Vorlagen</h2>
                        <!-- NEU: Button zum Anlegen -->
                        <button class="bg-primary text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-600 flex items-center gap-2 shadow-sm" onclick="openCreateTemplateModal()">
                            <span class="material-icons-outlined text-base">add</span> Neue Vorlage
                        </button>
                    </div>
                    <div class="overflow-x-auto custom-scrollbar"><table class="w-full min-w-[800px] text-sm text-left text-slate-text">
                        <thead class="text-xs text-gray-700 uppercase bg-gray-50">
                            <tr>
                                <th class="px-6 py-3 rounded-l-lg" scope="col">Artikel</th>
                                <th class="px-6 py-3" scope="col">Menge</th>
                                <th class="px-6 py-3" scope="col">Einheit</th>
                                <th class="px-6 py-3" scope="col">Lagerort</th>
                                <th class="px-6 py-3" scope="col">STD Nr.</th>
                                <th class="px-6 py-3" scope="col">Zulieferer</th>
                                <th class="px-6 py-3" scope="col">Link</th>
                                <th class="px-6 py-3 rounded-r-lg" scope="col">Aktionen</th>
                            </tr>
                        </thead>
                        <tbody id="templateTableBody"></tbody>
                    </table></div>
                </div>
                
                <div id="expiryContainer" class="bg-white rounded-lg shadow p-6 hidden"><div class="flex justify-between items-center mb-6"><h2 class="text-xl font-semibold text-slate-800">Bestand mit Verfallsdatum</h2></div><div class="overflow-x-auto custom-scrollbar"><table class="w-full min-w-[600px] text-sm text-left text-slate-text"><thead class="text-xs text-gray-700 uppercase bg-gray-50"><tr><th class="px-6 py-3 rounded-l-lg" scope="col">Artikel</th><th class="px-6 py-3" scope="col">Charge</th><th class="px-6 py-3" scope="col">Geliefert am</th><th class="px-6 py-3" scope="col">Verfällt am</th><th class="px-6 py-3 rounded-r-lg" scope="col"></th></tr></thead><tbody id="expiryTableBody"></tbody></table></div></div>
            </div>
        </div>
    </main>
</div>

<!-- MODALS -->

<!-- New Order Modal (Bestätigung/Read-Only aus Vorlage) -->
<div class="modal opacity-0 pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="newOrderModal"> 
    <div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50" onclick="closeModal('newOrderModal')"></div> 
    <div class="modal-container bg-white w-11/12 md:max-w-lg mx-auto rounded-xl shadow-lg z-50"> 
        <div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-border"><p class="text-2xl font-bold text-slate-800">Bestellung bestätigen</p><div class="modal-close cursor-pointer z-50" onclick="closeModal('newOrderModal')"><span class="material-icons-outlined text-gray-500 hover:text-gray-700">close</span></div></div> 
        <div class="flex-grow overflow-y-auto py-4 px-6 custom-scrollbar"> 
            <p class="text-sm text-slate-500 mb-4">Bitte bestätige die Bestellung für den ausgewählten Artikel.</p>
            <form id="newOrderForm" class="space-y-4"> 
                <div><label class="block text-sm font-medium text-slate-text mb-1" for="itemName">Artikel</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-slate-600 focus:outline-none text-sm readonly-input" id="itemName" name="itemName" readonly required/></div> 
                <div class="grid grid-cols-2 gap-4">
                    <div><label class="block text-sm font-medium text-slate-text mb-1" for="quantity">Menge (Anzahl)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-slate-600 focus:outline-none text-sm readonly-input" id="quantity" name="quantity" type="number" readonly required/></div> 
                    <div><label class="block text-sm font-medium text-slate-text mb-1" for="packageSize">Einheit/Größe</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-slate-600 focus:outline-none text-sm readonly-input" id="packageSize" name="packageSize" readonly/></div>
                </div>
                <div><label class="block text-sm font-medium text-slate-text mb-1" for="storageLocation">Lagerort</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-slate-600 focus:outline-none text-sm readonly-input" id="storageLocation" name="storageLocation" type="text" readonly/></div> 
                <div><label class="block text-sm font-medium text-slate-text mb-1" for="stdOrderNr">STD Order-Nr.</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-slate-600 focus:outline-none text-sm readonly-input" id="stdOrderNr" name="stdOrderNr" type="text" readonly/></div> 
                <div><label class="block text-sm font-medium text-slate-text mb-1" for="supplier">Zulieferer</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-slate-600 focus:outline-none text-sm readonly-input" id="supplier" name="supplier" type="text" readonly/></div> 
                <div><label class="block text-sm font-medium text-slate-text mb-1" for="link">Link</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-slate-600 focus:outline-none text-sm readonly-input" id="link" name="link" type="url" readonly/></div> 
                <div><label class="block text-sm font-medium text-slate-text mb-1" for="notes">Notizen</label><textarea class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-slate-600 focus:outline-none text-sm readonly-input" id="notes" name="notes" rows="3" readonly></textarea></div> 
                <div><label class="flex items-center space-x-2 text-sm font-medium text-slate-text"><input type="checkbox" id="isCritical" name="isCritical" class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"/><span>Dies ist eine kritische Bestellung (Dringend!)</span></label></div> 
                <div><label class="block text-sm font-medium text-slate-text mb-1" for="requester">Angefordert von</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-sm readonly-input" id="requester" name="requester" type="text" readonly/></div> 
            </form> 
        </div> 
        <div class="flex-shrink-0 py-4 px-6 flex justify-end items-center border-t border-slate-border space-x-3"> 
            <button class="px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200" type="button" onclick="closeModal('newOrderModal')">Abbrechen</button> 
            <button class="px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-blue-700 flex items-center space-x-2" type="submit" form="newOrderForm"><span class="material-icons-outlined text-base">send</span><span>Anfrage senden</span></button> 
        </div> 
    </div> 
</div>

<!-- Edit Order Modal (Bleibt unverändert) -->
<div class="modal opacity-0 pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="editOrderModal"> <div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50" onclick="closeModal('editOrderModal')"></div> <div class="modal-container bg-white w-11/12 md:max-w-lg mx-auto rounded-xl shadow-lg z-50"> <div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-border"><div class="flex items-center gap-3"><p class="text-2xl font-bold text-slate-800" id="editModalTitle">Bestelldetails</p><button id="switchToEditBtn" onclick="switchToEditMode()" class="p-1.5 rounded-full hover:bg-slate-100 text-slate-500" title="Bearbeiten"><span class="material-icons-outlined text-lg">edit</span></button></div><div class="modal-close cursor-pointer z-50" onclick="closeModal('editOrderModal')"><span class="material-icons-outlined text-gray-500 hover:text-gray-700">close</span></div></div> <div class="flex-grow overflow-y-auto py-4 px-6 custom-scrollbar"> 
    <div id="viewOrderContent" class="space-y-4 text-sm"> 
        <div id="viewCriticalStatus" class="hidden p-3 rounded-md bg-red-50 border border-red-200"><p class="text-sm font-semibold text-red-700 flex items-center gap-2"><span class="material-icons-outlined">error</span>Kritische Priorität</p></div> 
        <div><label class="block text-xs font-medium text-slate-500">Artikel</label><p id="viewItemName" class="font-medium text-slate-800 text-base"></p></div> 
        <div class="grid grid-cols-2 gap-4">
            <div><label class="block text-xs font-medium text-slate-500">Menge</label><p id="viewQuantity" class="text-slate-700"></p></div>
            <div><label class="block text-xs font-medium text-slate-500">Einheit/Größe</label><p id="viewPackageSize" class="text-slate-700"></p></div> 
        </div>
        <div id="viewStorageLocationContainer" class="hidden"><label class="block text-xs font-medium text-slate-500">Lagerort</label><p id="viewStorageLocation" class="text-slate-700"></p></div> <div id="viewStdOrderNrContainer" class="hidden"><label class="block text-xs font-medium text-slate-500">STD Order-Nr.</label><p id="viewStdOrderNr" class="font-mono text-slate-700"></p></div> <div id="viewSupplierContainer" class="hidden"><label class="block text-xs font-medium text-slate-500">Zulieferer</label><p id="viewSupplier" class="text-slate-700"></p></div> <div id="viewLinkContainer" class="hidden"><label class="block text-xs font-medium text-slate-500">Link</label><a id="viewLink" href="#" target="_blank" rel="noopener noreferrer" class="text-primary hover:underline break-all"></a></div> <div id="viewNotesContainer"><label class="block text-xs font-medium text-slate-500">Notizen</label><p id="viewNotes" class="text-slate-700 whitespace-pre-wrap bg-slate-50 p-2 rounded-md border"></p></div> <div id="viewInProgressDetails" class="hidden pt-4 mt-4 border-t border-slate-200 space-y-4"><h4 class="text-base font-semibold text-slate-700">Bestelldaten</h4><div><label class="block text-xs font-medium text-slate-500">Bestellt am</label><p id="viewOrderedAt" class="text-slate-700"></p></div><div><label class="block text-xs font-medium text-slate-500">Voraussichtliche Lieferung</label><p id="viewEta" class="text-slate-700"></p></div><div><label class="block text-xs font-medium text-slate-500">Bestellt von</label><p id="viewOrderedBy" class="text-slate-700"></p></div></div> <div id="viewCompletedDetails" class="hidden pt-4 mt-4 border-t border-slate-200 space-y-4"><h4 class="text-base font-semibold text-slate-700">Lieferdaten</h4><div><label class="block text-xs font-medium text-slate-500">Geliefert am</label><p id="viewDeliveredAt" class="text-slate-700"></p></div><div id="viewBatchContainer"><label class="block text-xs font-medium text-slate-500">Chargennummer</label><p id="viewBatchNumber" class="text-slate-700"></p></div><div id="viewExpiryContainer"><label class="block text-xs font-medium text-slate-500">Verfallsdatum</label><p id="viewExpiryDate" class="text-slate-700"></p></div></div> <div id="viewDocumentationDetails" class="hidden pt-4 mt-4 border-t border-slate-200 space-y-4"><h4 class="text-base font-semibold text-slate-700">Dokumentation</h4><div id="viewDocStatusContainer"><label class="block text-xs font-medium text-slate-500">Status</label><p id="viewDocStatus" class="flex items-center gap-2"></p></div><div id="viewDocFileContainer" class="hidden"><label class="block text-xs font-medium text-slate-500">Dokument</label><p id="viewDocFile" class="text-slate-700 font-mono"></p></div></div> <div class="pt-4 mt-4 border-t border-slate-200 space-y-4"><h4 class="text-base font-semibold text-slate-700">Anforderungsdetails</h4><div><label class="block text-xs font-medium text-slate-500">Bestell-ID</label><p id="viewOrderId" class="font-mono text-slate-700"></p></div><div><label class="block text-xs font-medium text-slate-500">Angefordert von</label><p id="viewRequester" class="text-slate-700"></p></div><div><label class="block text-xs font-medium text-slate-500">Erstellt am</label><p id="viewCreatedAt" class="text-slate-700"></p></div></div> 
    </div> 
    
    <form id="editOrderForm" class="space-y-4 hidden"> 
        <input type="hidden" id="editOrderId"> 
        <div><label class="block text-sm font-medium text-slate-text mb-1" for="editItemName">Artikel</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editItemName" required/></div> 
        <div class="grid grid-cols-2 gap-4">
            <div><label class="block text-sm font-medium text-slate-text mb-1" for="editQuantity">Menge</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editQuantity" type="number" required/></div> 
            <div><label class="block text-sm font-medium text-slate-text mb-1" for="editPackageSize">Einheit/Größe</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editPackageSize" /></div>
        </div>

        <div><label class="block text-sm font-medium text-slate-text mb-1" for="editStorageLocation">Lagerort (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editStorageLocation" type="text"/></div> <div><label class="block text-sm font-medium text-slate-text mb-1" for="editStdOrderNr">STD Order-Nr. (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editStdOrderNr" type="text"/></div> <div><label class="block text-sm font-medium text-slate-text mb-1" for="editSupplier">Zulieferer (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editSupplier" type="text"/></div> <div><label class="block text-sm font-medium text-slate-text mb-1" for="editLink">Link (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editLink" type="url" placeholder="https://..."/></div> <div><label class="block text-sm font-medium text-slate-text mb-1" for="editNotes">Notizen</label><textarea class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editNotes" rows="3"></textarea></div> <div><label class="flex items-center space-x-2 text-sm font-medium text-slate-text"><input type="checkbox" id="editIsCritical" name="isCritical" class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"/><span>Dies ist eine kritische Bestellung</span></label></div> <div id="editInProgressFields" class="hidden pt-4 mt-4 border-t border-slate-200 space-y-4"><h4 class="text-base font-semibold text-slate-700">Bestelldaten</h4><div><label class="block text-sm font-medium text-slate-text mb-1" for="editOrderedAt">Bestellt am</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-primary" id="editOrderedAt" type="date"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editEta">Voraussichtliche Lieferung</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-primary" id="editEta" type="date"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editOrderedBy">Bestellt von</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-sm" id="editOrderedBy" readonly/></div></div> <div id="editCompletedFields" class="hidden pt-4 mt-4 border-t border-slate-200 space-y-4"><h4 class="text-base font-semibold text-slate-700">Lieferdaten</h4><div><label class="block text-sm font-medium text-slate-text mb-1" for="editDeliveredAt">Geliefert am</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-primary" id="editDeliveredAt" type="date"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editBatchNumber">Chargennummer</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-primary" id="editBatchNumber" type="text"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editExpiryDate">Verfallsdatum</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg text-sm focus:outline-none focus:ring-1 focus:ring-primary" id="editExpiryDate" type="date"/></div></div> <div id="editDocumentationFields" class="hidden pt-4 mt-4 border-t border-slate-200 space-y-4"><h4 class="text-base font-semibold text-slate-700">Dokumentation</h4><div><label class="flex items-center space-x-2 text-sm font-medium text-slate-text"><input type="checkbox" id="editDocumentationChecked" class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"/><span>Dokumentation geprüft</span></label></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editDocumentationFile">Neues Dokument hochladen (optional)</label><input id="editDocumentationFile" type="file" class="block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-primary hover:file:bg-blue-100"/><p class="text-xs text-slate-500 mt-1 hidden">Aktuelle Datei: <span id="currentDocFile" class="font-mono"></span></p></div></div> <div class="pt-4 mt-4 border-t border-slate-200 space-y-4"><h4 class="text-base font-semibold text-slate-700">Anforderungsdetails</h4><div><label class="block text-sm font-medium text-slate-text mb-1" for="editRequester">Angefordert von</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg bg-gray-50 text-sm" id="editRequester" readonly/></div></div> <div class="mt-4 pt-4 border-t border-slate-200"><button type="button" id="saveEditAsTemplateBtn" class="w-full flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium text-primary bg-blue-50 rounded-lg hover:bg-blue-100"><span class="material-icons-outlined text-base">bookmark_add</span><span>Aktuelle Bearbeitung als Vorlage speichern</span></button></div> </form> </div> <div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-t border-slate-border"> <button id="deleteOrderBtn" type="button" class="px-4 py-2 text-sm font-medium text-red-600 bg-red-100 rounded-lg hover:bg-red-200 flex items-center gap-1 hidden"><span class="material-icons-outlined text-base">delete_outline</span><span>Löschen</span></button> <div id="editButtons" class="space-x-3 hidden ml-auto"><button class="px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200" type="button" onclick="switchToViewMode()">Abbrechen</button><button class="px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-blue-700" type="submit" form="editOrderForm">Änderungen speichern</button></div> <div id="viewButtons" class="space-x-3 ml-auto"><button class="px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-blue-700" type="button" onclick="closeModal('editOrderModal')">Schließen</button></div> </div> </div> </div>

<div class="modal opacity-0 pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="templateModal">
    <div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50" onclick="closeModal('templateModal')"></div>
    <div class="modal-container bg-white w-11/12 md:max-w-xl mx-auto rounded-xl shadow-lg z-50 flex flex-col h-[80vh]">
        <div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-border">
            <p class="text-2xl font-bold text-slate-800">Bestellung auswählen</p>
            <div class="modal-close cursor-pointer z-50" onclick="closeModal('templateModal')"><span class="material-icons-outlined text-gray-500 hover:text-gray-700">close</span></div>
        </div>
        
        <!-- Suchleiste im Header des Modals -->
        <div class="flex-shrink-0 px-6 py-3 bg-slate-50 border-b border-slate-border">
            <div class="relative">
                <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-gray-400">search</span>
                <input type="text" id="templateSearchInput" class="w-full pl-10 pr-4 py-2 rounded-lg border border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary text-sm" placeholder="Artikel suchen..." oninput="renderTemplatesForSelection()">
            </div>
        </div>

        <div id="templateListContainer" class="flex-grow overflow-y-auto py-4 px-6 space-y-2 custom-scrollbar"></div>
        
        <div class="flex-shrink-0 py-4 px-6 flex justify-end items-center border-t border-slate-border">
            <button class="px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200" type="button" onclick="closeModal('templateModal')">Abbrechen</button>
        </div>
    </div>
</div>

<div class="modal opacity-0 pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="editTemplateModal"><div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50" onclick="closeModal('editTemplateModal')"></div><div class="modal-container bg-white w-11/12 md:max-w-lg mx-auto rounded-xl shadow-lg z-50"><div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-border"><p class="text-2xl font-bold text-slate-800">Vorlage bearbeiten</p><div class="modal-close cursor-pointer z-50" onclick="closeModal('editTemplateModal')"><span class="material-icons-outlined text-gray-500 hover:text-gray-700">close</span></div></div><div class="flex-grow overflow-y-auto py-4 px-6 custom-scrollbar">
    <form id="editTemplateForm" class="space-y-4">
        <input type="hidden" id="editTemplateIndex">
        <div><label class="block text-sm font-medium text-slate-text mb-1" for="editTemplateItemName">Artikel</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editTemplateItemName" required/></div>
        <div class="grid grid-cols-2 gap-4">
            <div><label class="block text-sm font-medium text-slate-text mb-1" for="editTemplateQuantity">Menge</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editTemplateQuantity" type="number" required/></div>
            <div><label class="block text-sm font-medium text-slate-text mb-1" for="editTemplatePackageSize">Einheit/Größe</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editTemplatePackageSize" /></div>
        </div>

        <div><label class="block text-sm font-medium text-slate-text mb-1" for="editTemplateStorageLocation">Lagerort (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editTemplateStorageLocation" type="text"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editTemplateStdOrderNr">STD Order-Nr. (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editTemplateStdOrderNr" type="text"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editTemplateSupplier">Zulieferer (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editTemplateSupplier" type="text"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editTemplateLink">Link (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editTemplateLink" type="url" placeholder="https://..."/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="editTemplateNotes">Notizen (optional)</label><textarea class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="editTemplateNotes" rows="3"></textarea></div><div><label class="flex items-center space-x-2 text-sm font-medium text-slate-text"><input type="checkbox" id="editTemplateIsCritical" class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"/><span>Als kritisch markieren</span></label></div></form></div><div class="flex-shrink-0 py-4 px-6 flex justify-end items-center border-t border-slate-border space-x-3"><button class="px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200" type="button" onclick="closeModal('editTemplateModal')">Abbrechen</button><button class="px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-blue-700" type="submit" form="editTemplateForm">Änderungen speichern</button></div></div></div>

<!-- NEU: Modal zum Erstellen einer Vorlage (leer) -->
<div class="modal opacity-0 pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="createTemplateModal"><div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50" onclick="closeModal('createTemplateModal')"></div><div class="modal-container bg-white w-11/12 md:max-w-lg mx-auto rounded-xl shadow-lg z-50"><div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-border"><p class="text-2xl font-bold text-slate-800">Neue Vorlage erstellen</p><div class="modal-close cursor-pointer z-50" onclick="closeModal('createTemplateModal')"><span class="material-icons-outlined text-gray-500 hover:text-gray-700">close</span></div></div><div class="flex-grow overflow-y-auto py-4 px-6 custom-scrollbar">
    <form id="createTemplateForm" class="space-y-4">
        <div><label class="block text-sm font-medium text-slate-text mb-1" for="createTemplateItemName">Artikel</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="createTemplateItemName" required/></div>
        <div class="grid grid-cols-2 gap-4">
            <div><label class="block text-sm font-medium text-slate-text mb-1" for="createTemplateQuantity">Menge</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="createTemplateQuantity" type="number" required/></div>
            <div><label class="block text-sm font-medium text-slate-text mb-1" for="createTemplatePackageSize">Einheit/Größe</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="createTemplatePackageSize" /></div>
        </div>
        <div><label class="block text-sm font-medium text-slate-text mb-1" for="createTemplateStorageLocation">Lagerort (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="createTemplateStorageLocation" type="text"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="createTemplateStdOrderNr">STD Order-Nr. (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="createTemplateStdOrderNr" type="text"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="createTemplateSupplier">Zulieferer (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="createTemplateSupplier" type="text"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="createTemplateLink">Link (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="createTemplateLink" type="url" placeholder="https://..."/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="createTemplateNotes">Notizen (optional)</label><textarea class="w-full px-3 py-2 border border-slate-border rounded-lg focus:outline-none focus:ring-1 focus:ring-primary text-sm" id="createTemplateNotes" rows="3"></textarea></div><div><label class="flex items-center space-x-2 text-sm font-medium text-slate-text"><input type="checkbox" id="createTemplateIsCritical" class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"/><span>Als kritisch markieren</span></label></div></form></div><div class="flex-shrink-0 py-4 px-6 flex justify-end items-center border-t border-slate-border space-x-3"><button class="px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200" type="button" onclick="closeModal('createTemplateModal')">Abbrechen</button><button class="px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-blue-700" type="submit" form="createTemplateForm">Vorlage erstellen</button></div></div></div>

<div class="modal opacity-0 pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="inProgressModal"><div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50" onclick="cancelStatusChange('inProgress')"></div><div class="modal-container bg-white w-11/12 md:max-w-md mx-auto rounded-xl shadow-lg z-50"><div class="p-6"><h3 class="text-xl font-bold text-slate-800">Bestellung wird bearbeitet</h3><p class="text-sm text-slate-500 mt-1">Bitte ergänze die Bestelldaten für <strong id="inProgressItemName"></strong>.</p><form id="inProgressForm" class="mt-6 space-y-4"><input type="hidden" id="inProgressOrderId"><div><label class="block text-sm font-medium text-slate-text mb-1" for="orderedAt">Bestelldatum*</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg" id="orderedAt" type="date" required/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="eta">Voraussichtliche Lieferung</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg" id="eta" type="date"></div><div class="flex justify-end items-center pt-4 space-x-3"><button type="button" onclick="cancelStatusChange('inProgress')" class="px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200">Abbrechen</button><button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-blue-700">Speichern</button></div></form></div></div></div>
<div class="modal opacity-0 pointer-events-none fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="completedModal"><div class="modal-overlay absolute w-full h-full bg-gray-900 opacity-50" onclick="cancelStatusChange('completed')"></div><div class="modal-container bg-white w-11/12 md:max-w-md mx-auto rounded-xl shadow-lg z-50"><div class="p-6"><h3 class="text-xl font-bold text-slate-800">Bestellung abschließen</h3><p class="text-sm text-slate-500 mt-1">Bitte ergänze die Lieferdaten für <strong id="completedItemName"></strong>.</p><form id="completedForm" class="mt-6 space-y-4"><input type="hidden" id="completedOrderId"><div><label class="block text-sm font-medium text-slate-text mb-1" for="deliveredAt">Lieferdatum*</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg" id="deliveredAt" type="date" required/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="batchNumber">Chargennummer (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg" id="batchNumber" type="text"/></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="expiryDate">Verfallsdatum (optional)</label><input class="w-full px-3 py-2 border border-slate-border rounded-lg" id="expiryDate" type="date"/></div><hr/><div><label class="flex items-center space-x-2 text-sm font-medium text-slate-text"><input type="checkbox" id="documentationChecked" class="h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary"/><span>Dokumentation geprüft</span></label></div><div><label class="block text-sm font-medium text-slate-text mb-1" for="documentationFile">Dokumentation hochladen (optional)</label><input id="documentationFile" type="file" class="block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-primary hover:file:bg-blue-100"/></div><div class="flex justify-end items-center pt-4 space-x-3"><button type="button" onclick="cancelStatusChange('completed')" class="px-4 py-2 text-sm font-medium text-gray-600 bg-gray-100 rounded-lg hover:bg-gray-200">Abbrechen</button><button type="submit" class="px-4 py-2 text-sm font-medium text-white bg-green-600 hover:bg-green-700">Abschließen</button></div></form></div></div></div>

<div id="currentUser" data-name="{{ current_user_display_name }}" class="hidden"></div>
<script id="orders-data" type="application/json">{{ orders_json | safe }}</script>
<script id="templates-data" type="application/json">{{ templates_json | safe }}</script>

<script>
    // --- GLOBALE FUNKTIONEN (Außerhalb DOMContentLoaded) ---
    let orders = [];
    let templates = [];
    let chartInstances = {};
    const TABLE_ROW_LIMIT = 20;
    let isTableExpanded = false;
    let pendingStatusChange = null;

    // Diese globalen Variablen werden erst in DOMContentLoaded gefüllt
    let dom = {};
    let canManageOrders = false;
    let canViewAnalysis = false;
    let canManageTemplates = false;
    
    // Hilfsfunktion: Templates rendern
    function renderTemplatesForSelection() { 
        const container = document.getElementById('templateListContainer');
        const searchTerm = document.getElementById('templateSearchInput').value.toLowerCase();
        container.innerHTML = ''; 
        
        const filteredTemplates = templates.filter(t => t.name.toLowerCase().includes(searchTerm) || (t.notes && t.notes.toLowerCase().includes(searchTerm)));

        if (filteredTemplates.length === 0) { 
            container.innerHTML = '<p class="text-slate-500 text-center py-4">Keine passenden Vorlagen gefunden.</p>'; 
            return; 
        } 
        
        filteredTemplates.forEach((template) => { 
            const originalIndex = templates.indexOf(template);
            
            const templateEl = document.createElement('div'); 
            templateEl.className = 'p-4 border rounded-lg hover:bg-blue-50 cursor-pointer transition-colors bg-white shadow-sm mb-2'; 
            let detailsHtml = `<p class="text-sm text-slate-500 mt-1">Menge: <strong>${template.quantity}</strong> | ${template.packageSize || '-'}</p>`; 
            
            let metaInfo = [];
            if(template.storageLocation) metaInfo.push(template.storageLocation);
            if(template.stdOrderNr) metaInfo.push(template.stdOrderNr);
            if(template.supplier) metaInfo.push(template.supplier);
            
            if(metaInfo.length > 0) {
                detailsHtml += `<p class="text-xs text-slate-400 mt-2 truncate">${metaInfo.join(' • ')}</p>`;
            }
            
            templateEl.innerHTML = `<div class="flex justify-between items-start"><p class="font-bold text-slate-800">${template.name}</p>${template.isCritical ? '<span class="text-xs text-red-500 font-bold border border-red-200 bg-red-50 px-2 py-0.5 rounded-full">Kritisch</span>' : ''}</div>${detailsHtml}`; 
            templateEl.onclick = () => window.applyTemplate(originalIndex); 
            container.appendChild(templateEl); 
        }); 
    }

    // Modal Funktionen Global
    window.openModal = function(modalId) { 
        const modal = document.getElementById(modalId); 
        if(modalId === 'newOrderModal') { 
            const userEl = document.getElementById('currentUser');
            document.getElementById('requester').value = userEl ? userEl.dataset.name : 'User'; 
        } 
        if (modalId === 'templateModal') {
            document.getElementById('templateSearchInput').value = '';
            renderTemplatesForSelection(); 
        }
        if (modalId === 'createTemplateModal') {
            document.getElementById('createTemplateForm').reset();
        }
        modal.classList.remove('opacity-0', 'pointer-events-none'); 
        document.body.classList.add('modal-active'); 
    };

    window.closeModal = function(modalId) { 
        const modal = document.getElementById(modalId); 
        modal.classList.add('opacity-0', 'pointer-events-none'); 
        document.body.classList.remove('modal-active'); 
        if (modalId === 'editOrderModal') setTimeout(()=>window.switchToViewMode(true), 300); 
    };
    
    // Für oninput im HTML
    window.renderTemplatesForSelection = renderTemplatesForSelection;
    
    window.openCreateTemplateModal = function() {
        openModal('createTemplateModal');
    }

    window.applyTemplate = function(templateIndex) { 
        const template = templates[templateIndex]; 
        document.getElementById('itemName').value = template.name; 
        document.getElementById('quantity').value = template.quantity; 
        document.getElementById('packageSize').value = template.packageSize || ''; 
        document.getElementById('notes').value = template.notes || ''; 
        document.getElementById('stdOrderNr').value = template.stdOrderNr || ''; 
        document.getElementById('supplier').value = template.supplier || ''; 
        document.getElementById('link').value = template.link || ''; 
        document.getElementById('storageLocation').value = template.storageLocation || ''; 
        document.getElementById('isCritical').checked = template.isCritical; 
        
        closeModal('templateModal');
        openModal('newOrderModal');
    }
    window.saveTemplateFromData = async function(data) { 
        const { name, quantity, packageSize, notes, isCritical, stdOrderNr, supplier, link, storageLocation } = data; 
        if (!name || !quantity) { alert("Artikel und Menge sind erforderlich."); return; } 
        const alreadyExists = templates.some(t => t.name.toLowerCase() === name.toLowerCase() && t.quantity === quantity); 
        if (alreadyExists) { if (!confirm('Eine sehr ähnliche Vorlage existiert bereits. Trotzdem speichern?')) return; } 
        templates.push({ name, quantity, packageSize, notes, isCritical, stdOrderNr, supplier, link, storageLocation }); 
        await saveDataToServer(); 
        alert(`Vorlage "${name}" wurde gespeichert.`); 
        // Falls wir gerade im Template-Tab sind, Tabelle neu laden
        if (document.querySelector('#templatesContainer:not(.hidden)')) { renderTemplateTable(); } 
    }

    window.openEditTemplateModal = function(index) { 
        const template = templates[index]; 
        if (!template) return; 
        document.getElementById('editTemplateIndex').value = index; 
        document.getElementById('editTemplateItemName').value = template.name; 
        document.getElementById('editTemplateQuantity').value = template.quantity; 
        document.getElementById('editTemplatePackageSize').value = template.packageSize || ''; 
        document.getElementById('editTemplateNotes').value = template.notes || ''; 
        document.getElementById('editTemplateIsCritical').checked = template.isCritical; 
        document.getElementById('editTemplateStdOrderNr').value = template.stdOrderNr || ''; 
        document.getElementById('editTemplateSupplier').value = template.supplier || ''; 
        document.getElementById('editTemplateLink').value = template.link || ''; 
        document.getElementById('editTemplateStorageLocation').value = template.storageLocation || ''; 
        openModal('editTemplateModal'); 
    }

    window.deleteTemplate = async function(index) { 
        if (confirm(`Möchten Sie die Vorlage "${templates[index].name}" wirklich löschen?`)) { 
            templates.splice(index, 1); 
            await saveDataToServer(); 
            renderTemplateTable(); 
        } 
    }
    document.addEventListener('DOMContentLoaded', () => {
        // --- Sidebar ---
        const sidebar = document.getElementById('sidebar');
        const sidebarToggleBtn = document.getElementById('sidebar-toggle');
        if (sidebar && sidebarToggleBtn) { 
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined'); 
            const applyState = (isCollapsed) => { if (isCollapsed) { sidebar.classList.add('is-collapsed'); toggleBtnIcon.textContent = 'menu'; } else { sidebar.classList.remove('is-collapsed'); toggleBtnIcon.textContent = 'chevron_left'; } }; 
            sidebarToggleBtn.addEventListener('click', () => { const isNowCollapsed = !sidebar.classList.contains('is-collapsed'); localStorage.setItem('sidebarCollapsed', isNowCollapsed); applyState(isNowCollapsed); }); 
            const savedState = localStorage.getItem('sidebarCollapsed') === 'true'; applyState(savedState); 
        }

        // --- Init Data ---
        const userRoles = {{ session.get('roles', []) | tojson }};
        canManageOrders = userRoles.includes('ADMIN') || userRoles.includes('CONSUMABLEMANAGER');
        canViewAnalysis = userRoles.includes('ADMIN') || userRoles.includes('CONSUMABLEMANAGER') || userRoles.includes('PLANNER');
        canManageTemplates = userRoles.includes('ADMIN') || userRoles.includes('CONSUMABLEMANAGER');

        orders = JSON.parse(document.getElementById('orders-data').textContent);
        templates = JSON.parse(document.getElementById('templates-data').textContent);
        orders.forEach(order => { order.createdAt = new Date(order.createdAt); });

        // --- DOM Elements ---
        dom = { kanbanContainerParent: document.getElementById('kanban-container-parent'), tableBody: document.getElementById('orderHistoryTableBody'), newOrderForm: document.getElementById('newOrderForm'), editOrderForm: document.getElementById('editOrderForm'), kpis: { requested: document.getElementById('kpi-requested'), inprogress: document.getElementById('kpi-inprogress'), critical: document.getElementById('kpi-critical'), expiry: document.getElementById('kpi-expiry'), expiryTile: document.getElementById('kpi-expiry-tile'), expiryIconContainer: document.getElementById('kpi-expiry-icon-container') }, editModal: { title: document.getElementById('editModalTitle'), viewContent: document.getElementById('viewOrderContent'), editForm: document.getElementById('editOrderForm'), switchToEditBtn: document.getElementById('switchToEditBtn'), deleteBtn: document.getElementById('deleteOrderBtn'), editButtons: document.getElementById('editButtons'), viewButtons: document.getElementById('viewButtons'), viewOrderId: document.getElementById('viewOrderId'), viewItemName: document.getElementById('viewItemName'), viewQuantity: document.getElementById('viewQuantity'), viewPackageSize: document.getElementById('viewPackageSize'), viewRequester: document.getElementById('viewRequester'), viewCreatedAt: document.getElementById('viewCreatedAt'), viewNotesContainer: document.getElementById('viewNotesContainer'), viewNotes: document.getElementById('viewNotes'), viewCriticalStatus: document.getElementById('viewCriticalStatus'), viewInProgressDetails: document.getElementById('viewInProgressDetails'), viewOrderedAt: document.getElementById('viewOrderedAt'), viewEta: document.getElementById('viewEta'), viewOrderedBy: document.getElementById('viewOrderedBy'), viewCompletedDetails: document.getElementById('viewCompletedDetails'), viewDeliveredAt: document.getElementById('viewDeliveredAt'), viewBatchContainer: document.getElementById('viewBatchContainer'), viewBatchNumber: document.getElementById('viewBatchNumber'), viewExpiryContainer: document.getElementById('viewExpiryContainer'), viewExpiryDate: document.getElementById('viewExpiryDate'), viewStdOrderNrContainer: document.getElementById('viewStdOrderNrContainer'), viewStdOrderNr: document.getElementById('viewStdOrderNr'), viewSupplierContainer: document.getElementById('viewSupplierContainer'), viewSupplier: document.getElementById('viewSupplier'), viewLinkContainer: document.getElementById('viewLinkContainer'), viewLink: document.getElementById('viewLink'), viewStorageLocationContainer: document.getElementById('viewStorageLocationContainer'), viewStorageLocation: document.getElementById('viewStorageLocation'), viewDocumentationDetails: document.getElementById('viewDocumentationDetails'), viewDocStatusContainer: document.getElementById('viewDocStatusContainer'), viewDocStatus: document.getElementById('viewDocStatus'), viewDocFileContainer: document.getElementById('viewDocFileContainer'), viewDocFile: document.getElementById('viewDocFile'), editDocumentationFields: document.getElementById('editDocumentationFields') }, tableSearchInput: document.getElementById('tableSearchInput'), tableFooter: document.getElementById('table-footer'), viewTabs: document.getElementById('viewTabs'), tableContainer: document.getElementById('tableContainer'), analyseContainer: document.getElementById('analyseContainer'), analyseChartsContainer: document.getElementById('analyseChartsContainer'), templateListContainer: document.getElementById('templateListContainer'), templatesContainer: document.getElementById('templatesContainer'), templateTableBody: document.getElementById('templateTableBody'), editTemplateForm: document.getElementById('editTemplateForm'), saveNewAsTemplateBtn: document.getElementById('saveNewAsTemplateBtn'), saveEditAsTemplateBtn: document.getElementById('saveEditAsTemplateBtn'), expiryContainer: document.getElementById('expiryContainer'), expiryTableBody: document.getElementById('expiryTableBody') };
        
        const statusStyles = { requested:  'bg-slate-100 text-slate-700 border-slate-300', inprogress: 'bg-blue-100 text-blue-700 border-blue-300', completed:  'bg-green-100 text-green-700 border-green-300' };

        // --- Core Functions ---
        async function saveDataToServer() {
            try {
                await fetch('/api/order_system/save_data', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ orders: orders, templates: templates })
                });
            } catch (error) {
                console.error("Fehler beim Speichern der Daten:", error);
                alert("Verbindung zum Server verloren. Änderungen konnten nicht gespeichert werden.");
            }
        }

        function renderAll() { renderKanban(); renderTable(); updateKPIs(); }
        
        function renderKanban() { 
            const groupedOrders = { requested: [], inprogress: [], completed: [] }; 
            orders.sort((a, b) => b.createdAt - a.createdAt).forEach(order => { if (groupedOrders[order.status]) { groupedOrders[order.status].push(order); } }); 
            Object.keys(groupedOrders).forEach(status => { 
                const columnEl = dom.kanbanContainerParent.querySelector(`.kanban-column[data-status="${status}"]`); 
                const cardContainer = columnEl.querySelector('.kanban-card-container'); 
                cardContainer.innerHTML = ''; 
                const ordersForColumn = groupedOrders[status]; 
                ordersForColumn.forEach(order => { 
                    const card = document.createElement('div'); card.className = 'kanban-card'; 
                    if (order.isCritical && order.status !== 'completed') card.classList.add('is-critical'); 
                    card.draggable = true; card.dataset.orderId = order.id; 
                    let extraInfo = ''; 
                    if(order.status === 'inprogress' && order.orderedAt && order.eta) { const etaDate = new Date(order.eta).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }); extraInfo = `<p class="text-xs text-blue-600 pointer-events-none mt-1">Bestellt von ${order.orderedBy}, ETA: ${etaDate}</p>`; } 
                    if(order.status === 'completed' && order.deliveredAt) { const deliveredDate = new Date(order.deliveredAt).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }); extraInfo = `<p class="text-xs text-green-700 pointer-events-none mt-1">Geliefert am ${deliveredDate}</p>`; } 
                    card.innerHTML = `${order.isCritical && order.status !== 'completed' ? '<span class="critical-icon material-icons-outlined">error</span>' : ''}<p class="font-medium text-sm pointer-events-none text-slate-800">${order.name}</p><p class="text-xs text-gray-500 pointer-events-none mt-1">Menge: ${order.quantity} | Einheit: ${order.packageSize || '-'}</p><p class="text-xs text-slate-400 pointer-events-none mt-2">ID: ${order.id}</p>${extraInfo}`; 
                    card.addEventListener('dblclick', () => openOrderModal(order)); 
                    addDragAndDropEvents(card); 
                    cardContainer.appendChild(card); 
                }); 
                dom.kanbanContainerParent.querySelectorAll('.kanban-card-container').forEach(updateScrollFadeEffect); 
            }); 
        }

        function renderTable() {
            const searchTerm = dom.tableSearchInput.value.toLowerCase();
            const filteredOrders = orders.filter(order => { const searchFields = [ order.name, order.quantity, order.packageSize, order.requester, String(order.id), order.storageLocation, order.stdOrderNr, order.supplier, order.notes ]; return searchFields.some(field => (field || '').toLowerCase().includes(searchTerm)); });
            const sortedOrders = filteredOrders.sort((a, b) => b.createdAt - a.createdAt);
            const ordersToDisplay = isTableExpanded ? sortedOrders : sortedOrders.slice(0, TABLE_ROW_LIMIT);
            dom.tableBody.innerHTML = '';
            ordersToDisplay.forEach(order => {
                const row = document.createElement('tr'); row.className = 'bg-white border-b border-slate-200'; if(order.isCritical && order.status !== 'completed') row.classList.add('bg-red-50'); row.dataset.orderId = order.id;
                let deliveryDateInfo = '-'; if (order.status === 'completed' && order.deliveredAt) { deliveryDateInfo = new Date(order.deliveredAt).toLocaleDateString('de-DE'); } else if (order.status === 'inprogress' && order.eta) { deliveryDateInfo = `ca. ${new Date(order.eta).toLocaleDateString('de-DE')}`; }
                let docHtml = '-'; if (order.status === 'completed') { docHtml = '<div class="flex items-center space-x-2">'; if (order.documentationChecked) { docHtml += '<span class="material-icons-outlined text-green-600 text-base" title="Dokumentation geprüft">check_circle</span>'; } else { docHtml += '<span class="material-icons-outlined text-slate-400 text-base" title="Dokumentation nicht geprüft">cancel</span>'; } if (order.documentationFile) { docHtml += `<span class="material-icons-outlined text-slate-500 text-base" title="Dokument vorhanden: ${order.documentationFile}">attachment</span>`; } docHtml += '</div>'; }
                const statusClass = statusStyles[order.status] || statusStyles.requested;
                const disabledAttr = canManageOrders ? '' : 'disabled';
                const statusSelectHtml = `<select class="status-select text-xs font-medium rounded-full border px-3 py-1 appearance-none focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary ${statusClass}" data-order-id="${order.id}" ${disabledAttr}><option value="requested" ${order.status === 'requested' ? 'selected' : ''}>Bedarf</option><option value="inprogress" ${order.status === 'inprogress' ? 'selected' : ''}>In Bestellung</option><option value="completed" ${order.status === 'completed' ? 'selected' : ''}>Abgeschlossen</option></select>`;
                row.innerHTML = `<td class="px-6 py-4 text-slate-500 font-mono">${order.id}</td><td class="px-6 py-4 font-medium text-slate-800">${order.isCritical && order.status !== 'completed' ? '<span class="material-icons-outlined text-red-500 text-base mr-1" title="Kritisch">error</span>' : ''}${order.name}</td><td class="px-6 py-4 text-slate-600">${order.quantity}</td><td class="px-6 py-4 text-slate-600">${order.packageSize || '-'}</td><td class="px-6 py-4 text-slate-600">${order.requester}</td><td class="px-6 py-4 text-slate-600">${new Date(order.createdAt).toLocaleDateString('de-DE')}</td><td class="px-6 py-4">${statusSelectHtml}</td><td class="px-6 py-4 text-slate-600">${order.orderedAt ? new Date(order.orderedAt).toLocaleDateString('de-DE') : '-'}</td><td class="px-6 py-4 text-slate-600">${deliveryDateInfo}</td><td class="px-6 py-4">${docHtml}</td><td class="px-6 py-4 text-right"><button class="toggle-details-btn text-primary hover:underline text-sm font-medium" data-order-id="${order.id}">Details</button></td>`;
                dom.tableBody.appendChild(row);

                const detailRow = document.createElement('tr'); detailRow.className = 'hidden detail-row'; detailRow.dataset.detailForRow = order.id; let linkHtml = order.link ? `<a href="${order.link}" target="_blank" rel="noopener noreferrer" class="text-primary hover:underline break-all">${order.link}</a>` : '-'; detailRow.innerHTML = `<td colspan="11" class="p-4 bg-slate-50"><div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm"><div class="space-y-2"><p><strong class="font-medium text-slate-600">Lagerort:</strong> ${order.storageLocation || '-'}</p><p><strong class="font-medium text-slate-600">STD Order-Nr.:</strong> <span class="font-mono">${order.stdOrderNr || '-'}</span></p></div><div class="space-y-2"><p><strong class="font-medium text-slate-600">Zulieferer:</strong> ${order.supplier || '-'}</p><p><strong class="font-medium text-slate-600">Link:</strong> ${linkHtml}</p></div><div class="space-y-2"><p><strong class="font-medium text-slate-600">Notizen:</strong></p><p class="text-slate-700 whitespace-pre-wrap">${order.notes || '-'}</p></div></div><div class="mt-4 pt-4 border-t border-slate-200 text-right"><button class="open-modal-btn px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-blue-700" data-order-id="${order.id}">Vollständige Details / Bearbeiten</button></div></td>`;
                dom.tableBody.appendChild(detailRow);
            });

            dom.tableFooter.innerHTML = ''; if (sortedOrders.length > TABLE_ROW_LIMIT) { const buttonText = isTableExpanded ? 'Weniger anzeigen' : `Alle ${sortedOrders.length} anzeigen`; dom.tableFooter.innerHTML = `<button id="toggle-table-expansion" class="text-sm font-medium text-primary hover:underline">${buttonText}</button>`; }
        }

        function updateKPIs() { dom.kpis.requested.textContent = orders.filter(o => o.status === 'requested').length; dom.kpis.inprogress.textContent = orders.filter(o => o.status === 'inprogress').length; dom.kpis.critical.textContent = orders.filter(o => o.isCritical && o.status !== 'completed').length; const today = new Date(); today.setHours(0, 0, 0, 0); const expiryOrders = orders.filter(o => o.status === 'completed' && o.expiryDate); dom.kpis.expiry.textContent = expiryOrders.length; const hasExpired = expiryOrders.some(o => new Date(o.expiryDate) < today); if(hasExpired) { dom.kpis.expiryTile.classList.add('bg-red-50'); dom.kpis.expiryIconContainer.classList.remove('bg-orange-100'); dom.kpis.expiryIconContainer.classList.add('bg-red-100'); dom.kpis.expiryIconContainer.querySelector('span').classList.remove('text-orange-500'); dom.kpis.expiryIconContainer.querySelector('span').classList.add('text-red-500'); } else { dom.kpis.expiryTile.classList.remove('bg-red-50'); dom.kpis.expiryIconContainer.classList.add('bg-orange-100'); dom.kpis.expiryIconContainer.classList.remove('bg-red-100'); dom.kpis.expiryIconContainer.querySelector('span').classList.add('text-orange-500'); dom.kpis.expiryIconContainer.querySelector('span').classList.remove('text-red-500'); } }
        
        function renderTemplateTable() { dom.templateTableBody.innerHTML = ''; if(templates.length === 0) { dom.templateTableBody.innerHTML = '<tr><td colspan="8" class="text-center text-slate-500 py-8">Keine Vorlagen vorhanden.</td></tr>'; return; } templates.forEach((template, index) => { const row = document.createElement('tr'); row.className = 'bg-white border-b border-slate-200'; const linkHtml = template.link ? `<a href="${template.link}" target="_blank" rel="noopener noreferrer" class="text-primary hover:underline">Link <span class="material-icons-outlined text-xs">open_in_new</span></a>` : '-'; 
            let actionsHtml = '-';
            if (canManageTemplates) {
                actionsHtml = `<button data-index="${index}" class="edit-template-btn p-1.5 rounded-full hover:bg-slate-100 text-slate-500" title="Bearbeiten"><span class="material-icons-outlined text-lg">edit</span></button>
                               <button data-index="${index}" class="delete-template-btn p-1.5 rounded-full hover:bg-red-100 text-red-500" title="Löschen"><span class="material-icons-outlined text-lg">delete</span></button>`;
            }
            row.innerHTML = ` <td class="px-6 py-4 font-medium text-slate-800">${template.name}</td> <td class="px-6 py-4 text-slate-600">${template.quantity}</td> <td class="px-6 py-4 text-slate-600">${template.packageSize || '-'}</td> <td class="px-6 py-4 text-slate-600">${template.storageLocation || '-'}</td> <td class="px-6 py-4 text-slate-600 font-mono">${template.stdOrderNr || '-'}</td> <td class="px-6 py-4 text-slate-600">${template.supplier || '-'}</td> <td class="px-6 py-4 text-slate-600">${linkHtml}</td> <td class="px-6 py-4 text-right space-x-2">${actionsHtml}</td> `; dom.templateTableBody.appendChild(row); }); 
        }
        function renderExpiryTable() { dom.expiryTableBody.innerHTML = ''; const today = new Date(); today.setHours(0, 0, 0, 0); const oneMonthFromNow = new Date(today); oneMonthFromNow.setMonth(oneMonthFromNow.getMonth() + 1); const expiryOrders = orders.filter(o => o.status === 'completed' && o.expiryDate); if (expiryOrders.length === 0) { dom.expiryTableBody.innerHTML = '<tr><td colspan="5" class="text-center text-slate-500 py-8">Keine Artikel mit Verfallsdatum im Bestand.</td></tr>'; return; } expiryOrders.sort((a, b) => new Date(a.expiryDate) - new Date(b.expiryDate)); expiryOrders.forEach(order => { const row = document.createElement('tr'); row.className = 'bg-white border-b border-slate-200 hover:bg-slate-50 cursor-pointer'; row.dataset.orderId = order.id; const expiryDate = new Date(order.expiryDate); const isExpired = expiryDate < today; const isExpiringSoon = expiryDate < oneMonthFromNow && !isExpired; let expiryClass = 'text-slate-600'; let expiryStatusText = ''; if (isExpired) { row.classList.add('bg-red-50'); expiryClass = 'text-red-600 font-bold'; expiryStatusText = '(Abgelaufen)'; } else if (isExpiringSoon) { row.classList.add('bg-orange-50'); expiryClass = 'text-orange-600 font-semibold'; expiryStatusText = '(Läuft bald ab)'; } row.innerHTML = `<td class="px-6 py-4 font-medium ${isExpired ? 'text-red-800' : ''}">${order.name}</td><td class="px-6 py-4 text-slate-600 font-mono">${order.batchNumber || '-'}</td><td class="px-6 py-4 text-slate-600">${new Date(order.deliveredAt).toLocaleDateString('de-DE')}</td><td class="px-6 py-4 ${expiryClass}">${expiryDate.toLocaleDateString('de-DE')} ${expiryStatusText}</td><td class="px-6 py-4 text-right"><button class="open-modal-btn text-primary hover:underline text-sm font-medium" data-order-id="${order.id}">Details</button></td>`; dom.expiryTableBody.appendChild(row); }); }

        // --- Render Analysis (bleibt gleich wie zuvor gepostet) ---
        function renderAnalysis() {
            dom.analyseChartsContainer.innerHTML = '';
            Object.values(chartInstances).forEach(chart => chart.destroy());
            const itemCounts = orders.reduce((acc, order) => { acc[order.name] = (acc[order.name] || 0) + 1; return acc; }, {});
            const sortedItems = Object.entries(itemCounts).sort(([,a],[,b]) => b-a).slice(0, 10);
            createChartCard('Häufigste Bestellungen (Top 10)', 'mostOrderedChart', 'doughnut', sortedItems.map(item => item[0]), { 'Anzahl': sortedItems.map(item => item[1]) }, { legend: true });
            const deliveryTimes = {};
            orders.filter(o => o.status === 'completed' && o.createdAt && o.deliveredAt).forEach(o => { const duration = (new Date(o.deliveredAt) - new Date(o.createdAt)) / (1000 * 60 * 60 * 24); if (!deliveryTimes[o.name]) deliveryTimes[o.name] = []; deliveryTimes[o.name].push(duration); });
            const avgDeliveryLabels = Object.keys(deliveryTimes);
            const avgDeliveryData = avgDeliveryLabels.map(name => (deliveryTimes[name].reduce((a, b) => a + b, 0) / deliveryTimes[name].length).toFixed(1));
            createChartCard('Durchschnittliche Lieferzeit', 'deliveryTimeChart', 'bar', avgDeliveryLabels, { 'Tage': avgDeliveryData }, { tooltipLabel: "Tage" });
            const createdMonthly = {};
            const completedMonthly = {};
            orders.forEach(order => { const createdMonth = new Date(order.createdAt).toISOString().slice(0, 7); createdMonthly[createdMonth] = (createdMonthly[createdMonth] || 0) + 1; if (order.status === 'completed' && order.deliveredAt) { const completedMonth = new Date(order.deliveredAt).toISOString().slice(0, 7); completedMonthly[completedMonth] = (completedMonthly[completedMonth] || 0) + 1; } });
            const allMonths = [...new Set([...Object.keys(createdMonthly), ...Object.keys(completedMonthly)])].sort();
            const monthlyLabels = allMonths.map(month => new Date(month + '-02').toLocaleString('de-DE', { month: 'short', year: '2-digit' }));
            createChartCard('Bestellaufkommen vs. Lieferungen', 'monthlyOrdersChart', 'line', monthlyLabels, { 'Neue Anforderungen': allMonths.map(month => createdMonthly[month] || 0), 'Abgeschlossene Lieferungen': allMonths.map(month => completedMonthly[month] || 0) }, { fullWidth: true, legend: true });
        }

        function createChartCard(title, canvasId, type, labels, datasets, options = {}) {
            const card = document.createElement('div');
            let cardClass = 'bg-white rounded-lg shadow-md p-6 border border-slate-200';
            if(options.fullWidth) cardClass += ' lg:col-span-3';
            else if(type === 'doughnut') cardClass += ' lg:col-span-1';
            else cardClass += ' lg:col-span-2';
            card.className = cardClass;
            const colorPalette = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#ec4899', '#64748b', '#14b8a6', '#f97316', '#0ea5e9'];
            let legendHtml = '';
            if (options.legend) {
                legendHtml = '<div class="mt-4 pt-4 border-t border-slate-200"><div class="grid grid-cols-2 gap-3 text-xs">';
                if(type === 'doughnut') {
                    labels.forEach((label, index) => { legendHtml += `<div class="flex items-center gap-2"><div class="w-3 h-3 rounded-full" style="background-color: ${colorPalette[index % colorPalette.length]}"></div><span class="truncate">${label}: <strong>${Object.values(datasets)[0][index]}</strong></span></div>`; });
                } else {
                    Object.keys(datasets).forEach((datasetLabel, index) => { legendHtml += `<div class="flex items-center gap-2"><div class="w-3 h-3 rounded-full" style="background-color: ${colorPalette[index % colorPalette.length]}"></div><span class="truncate">${datasetLabel}</span></div>`; });
                }
                legendHtml += '</div></div>';
            }
            card.innerHTML = `<div class="mb-4"><h3 class="text-lg font-semibold text-slate-900">${title}</h3></div><div class="relative ${type === 'doughnut' ? 'h-64' : 'h-80'}"><canvas id="${canvasId}"></canvas></div>${legendHtml}`;
            dom.analyseChartsContainer.appendChild(card);
            setTimeout(() => {
                const ctx = document.getElementById(canvasId);
                if (ctx) {
                    const chartDatasets = Object.keys(datasets).map((label, index) => ({
                        label: label,
                        data: datasets[label],
                        backgroundColor: type === 'line' ? 'rgba(0,0,0,0)' : type === 'doughnut' ? colorPalette : colorPalette.map(c => c + 'B3'),
                        borderColor: type === 'doughnut' ? '#fff' : colorPalette[index % colorPalette.length],
                        borderWidth: type === 'doughnut' ? 2 : 2,
                        tension: 0.2,
                        fill: type === 'line'
                    }));
                    chartInstances[canvasId] = new Chart(ctx, {
                        type: type, data: { labels: labels, datasets: chartDatasets },
                        options: { responsive: true, maintainAspectRatio: false, interaction: { mode: 'index', intersect: false }, scales: (type === 'doughnut') ? {} : { y: { beginAtZero: true } }, plugins: { legend: { display: false }, tooltip: { callbacks: { label: function(context) { let label = context.dataset.label || context.label || ''; if (label) { label += ': '; } const value = context.parsed.y || context.parsed; label += value; if (options.tooltipLabel) { label += ` ${options.tooltipLabel}`; } return label; } } } } }
                    });
                }
            }, 50);
        }

        function updateScrollFadeEffect(container) {
            const isScrollable = container.scrollHeight > container.clientHeight;
            const isAtBottom = container.scrollHeight - container.scrollTop <= container.clientHeight + 1;
            container.classList.toggle('scroll-fade', isScrollable && !isAtBottom);
        }

        dom.tableSearchInput.addEventListener('input', renderTable);
        dom.tableFooter.addEventListener('click', (e) => { if (e.target.id === 'toggle-table-expansion') { isTableExpanded = !isTableExpanded; renderTable(); } });
        
        dom.viewTabs.addEventListener('click', (e) => { 
            const button = e.target.closest('button'); 
            if (!button) return; 
            const view = button.dataset.view; 
            
            if (view === 'templates' && !canManageTemplates) return;
            if (view === 'analyse' && !canViewAnalysis) return;

            dom.viewTabs.querySelectorAll('button').forEach(btn => { 
                btn.classList.remove('border-primary', 'text-primary'); 
                btn.classList.add('border-transparent', 'text-slate-500', 'hover:border-slate-300', 'hover:text-slate-700'); 
            }); 
            button.classList.add('border-primary', 'text-primary'); 
            button.classList.remove('border-transparent', 'text-slate-500'); 
            dom.tableContainer.classList.toggle('hidden', view !== 'table'); 
            dom.analyseContainer.classList.toggle('hidden', view !== 'analyse'); 
            dom.templatesContainer.classList.toggle('hidden', view !== 'templates'); 
            dom.expiryContainer.classList.toggle('hidden', view !== 'expiry'); 
            if(view === 'templates') renderTemplateTable(); 
            if(view === 'expiry') renderExpiryTable(); 
            if(view === 'analyse') renderAnalysis(); 
        });

        if (!canManageTemplates) {
            const templatesTab = document.querySelector('button[data-view="templates"]');
            if (templatesTab) templatesTab.style.display = 'none';
        }
        if (!canViewAnalysis) {
            const analyseTab = document.querySelector('button[data-view="analyse"]');
            if (analyseTab) analyseTab.style.display = 'none';
        }

        dom.kanbanContainerParent.querySelectorAll('.kanban-card-container').forEach(container => {
            container.addEventListener('scroll', () => updateScrollFadeEffect(container));
        });
        window.addEventListener('resize', () => {
            dom.kanbanContainerParent.querySelectorAll('.kanban-card-container').forEach(container => {
                updateScrollFadeEffect(container);
            });
        });

        dom.tableBody.addEventListener('click', e => { const toggleBtn = e.target.closest('.toggle-details-btn'); const openModalBtn = e.target.closest('.open-modal-btn'); if (toggleBtn) { e.preventDefault(); const orderId = toggleBtn.dataset.orderId; const detailRow = dom.tableBody.querySelector(`tr[data-detail-for-row="${orderId}"]`); const isHidden = detailRow.classList.contains('hidden'); dom.tableBody.querySelectorAll('.detail-row:not(.hidden)').forEach(row => { if(row !== detailRow) { row.classList.add('hidden'); const prevBtn = dom.tableBody.querySelector(`.toggle-details-btn[data-order-id="${row.dataset.detailForRow}"]`); if(prevBtn) prevBtn.textContent = 'Details'; } }); detailRow.classList.toggle('hidden'); toggleBtn.textContent = isHidden ? 'Details' : 'Schließen'; } else if (openModalBtn) { e.preventDefault(); const orderId = parseInt(openModalBtn.dataset.orderId); const order = orders.find(o => o.id === orderId); if (order) openOrderModal(order); } });
        dom.expiryTableBody.addEventListener('click', e => { const openModalBtn = e.target.closest('.open-modal-btn'); if(openModalBtn) { const orderId = parseInt(openModalBtn.dataset.orderId); const order = orders.find(o => o.id === orderId); if (order) openOrderModal(order); } });
        
        // Listener für Formulare
dom.newOrderForm.addEventListener('submit', async e => { 
            e.preventDefault(); 
            const newOrder = { 
                id: Date.now(), 
                name: document.getElementById('itemName').value, 
                quantity: document.getElementById('quantity').value, 
                requester: document.getElementById('requester').value, 
                status: 'requested', 
                createdAt: new Date(), // Wird beim Senden zu String, Server sollte es handhaben
                notes: document.getElementById('notes').value, 
                isCritical: document.getElementById('isCritical').checked, 
                stdOrderNr: document.getElementById('stdOrderNr').value, 
                supplier: document.getElementById('supplier').value, 
                link: document.getElementById('link').value, 
                storageLocation: document.getElementById('storageLocation').value 
            }; 
            
            // --- NEU: Dedizierte Route nutzen ---
            try {
                const response = await fetch('/api/order_system/add_request', {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify(newOrder)
                });
                
                if (response.ok) {
                    // Lokal hinzufügen für sofortiges Feedback
                    orders.push(newOrder); 
                    renderAll(); 
                    closeModal('newOrderModal'); 
                    e.target.reset();
                } else {
                    alert("Fehler beim Senden der Bestellung.");
                }
            } catch(e) { console.error(e); }
        });
        
        dom.createTemplateForm = document.getElementById('createTemplateForm');
        dom.createTemplateForm.addEventListener('submit', async e => {
            e.preventDefault();
            const data = {
                name: document.getElementById('createTemplateItemName').value,
                quantity: document.getElementById('createTemplateQuantity').value,
                packageSize: document.getElementById('createTemplatePackageSize').value,
                notes: document.getElementById('createTemplateNotes').value,
                isCritical: document.getElementById('createTemplateIsCritical').checked,
                stdOrderNr: document.getElementById('createTemplateStdOrderNr').value,
                supplier: document.getElementById('createTemplateSupplier').value,
                link: document.getElementById('createTemplateLink').value,
                storageLocation: document.getElementById('createTemplateStorageLocation').value
            };
            saveTemplateFromData(data);
            closeModal('createTemplateModal');
        });

        dom.editOrderForm.addEventListener('submit', async e => { e.preventDefault(); const orderId = parseInt(document.getElementById('editOrderId').value); const order = orders.find(o => o.id === orderId); if (order) { order.name = document.getElementById('editItemName').value; order.quantity = document.getElementById('editQuantity').value; order.packageSize = document.getElementById('editPackageSize').value; order.notes = document.getElementById('editNotes').value; order.isCritical = document.getElementById('editIsCritical').checked; order.stdOrderNr = document.getElementById('editStdOrderNr').value; order.supplier = document.getElementById('editSupplier').value; order.link = document.getElementById('editLink').value; order.storageLocation = document.getElementById('editStorageLocation').value; if (order.status === 'inprogress' || order.status === 'completed') { order.orderedAt = document.getElementById('editOrderedAt').value; order.eta = document.getElementById('editEta').value; } if (order.status === 'completed') { order.deliveredAt = document.getElementById('editDeliveredAt').value; order.batchNumber = document.getElementById('editBatchNumber').value; order.expiryDate = document.getElementById('editExpiryDate').value; order.documentationChecked = document.getElementById('editDocumentationChecked').checked; const editFileInput = document.getElementById('editDocumentationFile'); if (editFileInput.files.length > 0) { order.documentationFile = editFileInput.files[0].name; } } } await saveDataToServer(); renderAll(); closeModal('editOrderModal'); });
        dom.editModal.deleteBtn.addEventListener('click', async () => { const orderId = parseInt(document.getElementById('editOrderId').value); if (confirm('Möchten Sie diese Anforderung wirklich löschen?')) { orders = orders.filter(o => o.id !== orderId); await saveDataToServer(); renderAll(); closeModal('editOrderModal'); } });
        document.getElementById('inProgressForm').addEventListener('submit', async (e) => { e.preventDefault(); if (!pendingStatusChange) return; const order = pendingStatusChange.order; order.status = 'inprogress'; order.orderedAt = document.getElementById('orderedAt').value; order.eta = document.getElementById('eta').value; order.orderedBy = document.getElementById('currentUser').dataset.name; await saveDataToServer(); closeModal('inProgressModal'); pendingStatusChange = null; renderAll(); });
        document.getElementById('completedForm').addEventListener('submit', async (e) => { e.preventDefault(); if (!pendingStatusChange) return; const order = pendingStatusChange.order; order.status = 'completed'; order.deliveredAt = document.getElementById('deliveredAt').value; order.batchNumber = document.getElementById('batchNumber').value; order.expiryDate = document.getElementById('expiryDate').value; order.documentationChecked = document.getElementById('documentationChecked').checked; const fileInput = document.getElementById('documentationFile'); if(fileInput.files.length > 0) { order.documentationFile = fileInput.files[0].name; } else { order.documentationFile = order.documentationFile || ''; } await saveDataToServer(); closeModal('completedModal'); pendingStatusChange = null; renderAll(); });
        dom.tableBody.addEventListener('change', e => { if (e.target.classList.contains('status-select')) { const orderId = parseInt(e.target.dataset.orderId); const newStatus = e.target.value; handleStatusChange(orderId, newStatus); } });

        let draggedCard = null; 
        function addDragAndDropEvents(card) { 
            if (canManageOrders) {
                card.addEventListener('dragstart', (e) => { draggedCard = e.target; setTimeout(() => { e.target.classList.add('opacity-50'); }, 0); }); 
                card.addEventListener('dragend', (e) => { e.target.classList.remove('opacity-50'); draggedCard = null; }); 
            } else {
                card.draggable = false;
                card.style.cursor = 'pointer';
            }
        } 
        Object.values(dom.kanbanContainerParent.querySelectorAll('.kanban-card-container')).forEach(columnContainer => { 
            columnContainer.addEventListener('dragover', e => { e.preventDefault(); }); 
            columnContainer.addEventListener('drop', e => { e.preventDefault(); if (!draggedCard || !canManageOrders) return; const newStatus = columnContainer.closest('.kanban-column').dataset.status; const orderId = parseInt(draggedCard.dataset.orderId); handleStatusChange(orderId, newStatus); }); 
        });

        // Save Helpers
        async function saveTemplateFromData(data) { const { name, quantity, packageSize, notes, isCritical, stdOrderNr, supplier, link, storageLocation } = data; if (!name || !quantity) { alert("Artikel und Menge sind erforderlich."); return; } const alreadyExists = templates.some(t => t.name.toLowerCase() === name.toLowerCase() && t.quantity === quantity); if (alreadyExists) { if (!confirm('Eine sehr ähnliche Vorlage existiert bereits. Trotzdem speichern?')) return; } templates.push({ name, quantity, packageSize, notes, isCritical, stdOrderNr, supplier, link, storageLocation }); await saveDataToServer(); alert(`Vorlage "${name}" wurde gespeichert.`); if (document.body.classList.contains('modal-active') && document.querySelector('#templatesContainer:not(.hidden)')) { renderTemplateTable(); } }
        dom.saveEditAsTemplateBtn.addEventListener('click', () => { saveTemplateFromData({ name: document.getElementById('editItemName').value, quantity: document.getElementById('editQuantity').value, packageSize: document.getElementById('editPackageSize').value, notes: document.getElementById('editNotes').value, isCritical: document.getElementById('editIsCritical').checked, stdOrderNr: document.getElementById('editStdOrderNr').value, supplier: document.getElementById('editSupplier').value, link: document.getElementById('editLink').value, storageLocation: document.getElementById('editStorageLocation').value }); });
        
        function openEditTemplateModal(index) { const template = templates[index]; if (!template) return; document.getElementById('editTemplateIndex').value = index; document.getElementById('editTemplateItemName').value = template.name; document.getElementById('editTemplateQuantity').value = template.quantity; document.getElementById('editTemplatePackageSize').value = template.packageSize || ''; document.getElementById('editTemplateNotes').value = template.notes || ''; document.getElementById('editTemplateIsCritical').checked = template.isCritical; document.getElementById('editTemplateStdOrderNr').value = template.stdOrderNr || ''; document.getElementById('editTemplateSupplier').value = template.supplier || ''; document.getElementById('editTemplateLink').value = template.link || ''; document.getElementById('editTemplateStorageLocation').value = template.storageLocation || ''; openModal('editTemplateModal'); }
        async function deleteTemplate(index) { if (confirm(`Möchten Sie die Vorlage "${templates[index].name}" wirklich löschen?`)) { templates.splice(index, 1); await saveDataToServer(); renderTemplateTable(); } }
        dom.templateTableBody.addEventListener('click', e => { 
            const editBtn = e.target.closest('.edit-template-btn'); 
            const deleteBtn = e.target.closest('.delete-template-btn'); 
            if (editBtn) window.openEditTemplateModal(parseInt(editBtn.dataset.index));
            if (deleteBtn) window.deleteTemplate(parseInt(deleteBtn.dataset.index)); 
        });        
        dom.editTemplateForm.addEventListener('submit', async e => { e.preventDefault(); const index = parseInt(document.getElementById('editTemplateIndex').value); const template = templates[index]; if (template) { template.name = document.getElementById('editTemplateItemName').value; template.quantity = document.getElementById('editTemplateQuantity').value; template.packageSize = document.getElementById('editTemplatePackageSize').value; template.notes = document.getElementById('editTemplateNotes').value; template.isCritical = document.getElementById('editTemplateIsCritical').checked; template.stdOrderNr = document.getElementById('editTemplateStdOrderNr').value; template.supplier = document.getElementById('editTemplateSupplier').value; template.link = document.getElementById('editTemplateLink').value; template.storageLocation = document.getElementById('editTemplateStorageLocation').value; } await saveDataToServer(); closeModal('editTemplateModal'); renderTemplateTable(); });

        window.switchToExpiryView = function() { const expiryTabButton = document.querySelector('button[data-view="expiry"]'); if (expiryTabButton) expiryTabButton.click(); };
        window.cancelStatusChange = function(modalType) { pendingStatusChange = null; closeModal(`${modalType}Modal`); renderAll(); }
        function handleStatusChange(orderId, newStatus) { const order = orders.find(o => o.id === orderId); if (!order || order.status === newStatus) return; pendingStatusChange = { order, oldStatus: order.status, newStatus }; if (newStatus === 'inprogress') { document.getElementById('inProgressItemName').textContent = order.name; document.getElementById('inProgressOrderId').value = order.id; document.getElementById('inProgressForm').reset(); openModal('inProgressModal'); } else if (newStatus === 'completed') { document.getElementById('completedItemName').textContent = order.name; document.getElementById('completedOrderId').value = order.id; document.getElementById('completedForm').reset(); openModal('completedModal'); } else { order.status = newStatus; pendingStatusChange = null; saveDataToServer(); renderAll(); } }
        window.openOrderModal = function(order) { 
            dom.editModal.viewItemName.textContent = order.name; 
            dom.editModal.viewQuantity.textContent = order.quantity;
            dom.editModal.viewPackageSize.textContent = order.packageSize || '';
            dom.editModal.viewStorageLocationContainer.classList.toggle('hidden', !order.storageLocation); 
            dom.editModal.viewStorageLocation.textContent = order.storageLocation || ''; 
            dom.editModal.viewStdOrderNrContainer.classList.toggle('hidden', !order.stdOrderNr); 
            dom.editModal.viewStdOrderNr.textContent = order.stdOrderNr || ''; 
            dom.editModal.viewSupplierContainer.classList.toggle('hidden', !order.supplier); 
            dom.editModal.viewSupplier.textContent = order.supplier || ''; 
            dom.editModal.viewLinkContainer.classList.toggle('hidden', !order.link); 
            if(order.link) { dom.editModal.viewLink.href = order.link; dom.editModal.viewLink.textContent = order.link; } 
            dom.editModal.viewNotesContainer.classList.toggle('hidden', !order.notes); 
            dom.editModal.viewNotes.textContent = order.notes || ''; 
            dom.editModal.viewOrderId.textContent = order.id; 
            dom.editModal.viewRequester.textContent = order.requester; 
            dom.editModal.viewCreatedAt.textContent = new Date(order.createdAt).toLocaleString('de-DE', { day: '2-digit', month: 'long', year: 'numeric', hour: '2-digit', minute:'2-digit' }) + ' Uhr'; 
            dom.editModal.viewCriticalStatus.classList.toggle('hidden', !order.isCritical); 
            dom.editModal.viewInProgressDetails.classList.toggle('hidden', order.status !== 'inprogress' && order.status !== 'completed'); 
            if(order.status === 'inprogress' || order.status === 'completed') { dom.editModal.viewOrderedAt.textContent = new Date(order.orderedAt).toLocaleDateString('de-DE'); dom.editModal.viewEta.textContent = new Date(order.eta).toLocaleDateString('de-DE'); dom.editModal.viewOrderedBy.textContent = order.orderedBy; } 
            dom.editModal.viewCompletedDetails.classList.toggle('hidden', order.status !== 'completed'); 
            dom.editModal.viewDocumentationDetails.classList.toggle('hidden', order.status !== 'completed'); 
            if(order.status === 'completed') { dom.editModal.viewDeliveredAt.textContent = new Date(order.deliveredAt).toLocaleDateString('de-DE'); dom.editModal.viewBatchContainer.style.display = order.batchNumber ? 'block' : 'none'; dom.editModal.viewBatchNumber.textContent = order.batchNumber; dom.editModal.viewExpiryContainer.style.display = order.expiryDate ? 'block' : 'none'; dom.editModal.viewExpiryDate.textContent = order.expiryDate ? new Date(order.expiryDate).toLocaleDateString('de-DE') : ''; if (order.documentationChecked) { dom.editModal.viewDocStatus.innerHTML = '<span class="material-icons-outlined text-green-600 text-base">check_circle</span> Geprüft'; } else { dom.editModal.viewDocStatus.innerHTML = '<span class="material-icons-outlined text-slate-500 text-base">cancel</span> Nicht geprüft'; } dom.editModal.viewDocFileContainer.classList.toggle('hidden', !order.documentationFile); dom.editModal.viewDocFile.textContent = order.documentationFile; } 
            
            const currentUser = document.getElementById('currentUser').dataset.name;
            const canEditThisOrder = canManageOrders || (order.status === 'requested' && order.requester === currentUser);
            
            dom.editModal.switchToEditBtn.style.display = canEditThisOrder ? 'block' : 'none';
            dom.editModal.deleteBtn.style.display = canEditThisOrder ? 'block' : 'none';
            
            document.getElementById('editOrderId').value = order.id; 
            document.getElementById('editItemName').value = order.name; 
            document.getElementById('editQuantity').value = order.quantity;
            document.getElementById('editPackageSize').value = order.packageSize || '';
            document.getElementById('editStdOrderNr').value = order.stdOrderNr || ''; 
            document.getElementById('editSupplier').value = order.supplier || ''; 
            document.getElementById('editLink').value = order.link || ''; 
            document.getElementById('editStorageLocation').value = order.storageLocation || ''; 
            document.getElementById('editRequester').value = order.requester; 
            document.getElementById('editNotes').value = order.notes || ''; 
            document.getElementById('editIsCritical').checked = order.isCritical; 
            window.switchToViewMode(false); 
            openModal('editOrderModal'); 
        };
        window.switchToViewMode = function(resetForm = true) { if (resetForm) { const orderId = parseInt(document.getElementById('editOrderId').value); const order = orders.find(o => o.id === orderId); if (order) { document.getElementById('editItemName').value = order.name; document.getElementById('editQuantity').value = order.quantity; document.getElementById('editPackageSize').value = order.packageSize || ''; document.getElementById('editStdOrderNr').value = order.stdOrderNr || ''; document.getElementById('editSupplier').value = order.supplier || ''; document.getElementById('editLink').value = order.link || ''; document.getElementById('editStorageLocation').value = order.storageLocation || ''; document.getElementById('editNotes').value = order.notes || ''; document.getElementById('editIsCritical').checked = order.isCritical; } } dom.editModal.title.textContent = "Bestelldetails"; dom.editModal.viewContent.classList.remove('hidden'); dom.editModal.editForm.classList.add('hidden'); dom.editModal.switchToEditBtn.classList.remove('hidden'); dom.editModal.deleteBtn.classList.add('hidden'); dom.editModal.editButtons.classList.add('hidden'); dom.editModal.viewButtons.classList.remove('hidden'); };
        window.switchToEditMode = function() { dom.editModal.title.textContent = "Anforderung bearbeiten"; dom.editModal.viewContent.classList.add('hidden'); dom.editModal.editForm.classList.remove('hidden'); dom.editModal.switchToEditBtn.classList.add('hidden'); dom.editModal.deleteBtn.classList.remove('hidden'); dom.editModal.editButtons.classList.remove('hidden'); dom.editModal.viewButtons.classList.add('hidden'); const orderId = parseInt(document.getElementById('editOrderId').value); const order = orders.find(o => o.id === orderId); if (!order) return; dom.editModal.editDocumentationFields.classList.toggle('hidden', order.status !== 'completed'); const editInProgressFields = document.getElementById('editInProgressFields'); const editCompletedFields = document.getElementById('editCompletedFields'); editInProgressFields.classList.add('hidden'); editCompletedFields.classList.add('hidden'); if (order.status === 'inprogress' || order.status === 'completed') { editInProgressFields.classList.remove('hidden'); document.getElementById('editOrderedAt').value = order.orderedAt || ''; document.getElementById('editEta').value = order.eta || ''; document.getElementById('editOrderedBy').value = order.orderedBy || ''; } if (order.status === 'completed') { editCompletedFields.classList.remove('hidden'); document.getElementById('editDeliveredAt').value = order.deliveredAt || ''; document.getElementById('editBatchNumber').value = order.batchNumber || ''; document.getElementById('editExpiryDate').value = order.expiryDate || ''; document.getElementById('editDocumentationChecked').checked = order.documentationChecked || false; document.getElementById('editDocumentationFile').value = ''; const currentFileSpan = document.getElementById('currentDocFile'); if (order.documentationFile) { currentFileSpan.textContent = order.documentationFile; currentFileSpan.parentElement.classList.remove('hidden'); } else { currentFileSpan.parentElement.classList.add('hidden'); } } };

        renderAll();
    });
</script>
</body>
</html>
"""

PROJECT_MONITOR_HTML = """
<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Testcell Monitor</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js"></script>

<script>
    tailwind.config = {
      theme: {
        extend: {
          colors: { "primary": "#0055A4", "primary-active": "#1D88E5", "danger": "#DC2626", "background-light": "#F5F5F5", "text-light": "#333333" },
          fontFamily: { "display": ["Inter", "sans-serif"] },
          borderRadius: { "DEFAULT": "0.125rem", "lg": "0.25rem", "xl": "0.5rem", "full": "0.75rem" },
          keyframes: {
            breathingBlue: { '0%, 100%': { transform: 'scale(1)', boxShadow: '0 0 0 0 rgba(29, 136, 229, 0.5)' }, '50%': { transform: 'scale(1.1)', boxShadow: '0 0 10px 5px rgba(29, 136, 229, 0)' } },
            blinkCaret: { 'from, to': { borderColor: 'transparent' }, '50%': { borderColor: '#0055A4' } }
          },
          animation: { 'breathing-blue': 'breathingBlue 2s ease-in-out infinite', 'blink-caret': 'blinkCaret .75s step-end infinite' }
        },
      },
    }
</script>
<style>
    .material-symbols-outlined { font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24 }
    .group[data-expanded="true"] .collapsed-view-dots { display: none; }
    .group[data-expanded="false"] .collapsed-view-dots { display: flex; }
    .group[data-expanded="true"] .expand-icon { transform: rotate(180deg); }
    #loading-text .cursor { display: inline-block; border-right: 3px solid #0055A4; margin-left: 2px; animation: blink-caret .75s step-end infinite; vertical-align: bottom; height: 1.2em; }

    /* Zurück-Button unten rechts */
    .back-home-btn { position: fixed; bottom: 20px; right: 20px; background: rgba(255,255,255,0.8); padding: 10px; border-radius: 50%; box-shadow: 0 2px 10px rgba(0,0,0,0.1); cursor: pointer; transition: all 0.3s; z-index: 60; }
    .back-home-btn:hover { background: #fff; transform: scale(1.1); }
</style>
</head>
<body class="bg-background-light font-display text-text-light">

<!-- Kleiner Zurück-Button -->
<a href="{{ url_for('dashboard_page') }}" class="back-home-btn" title="Zurück zum Haupt-Dashboard">
    <span class="material-symbols-outlined text-primary">home</span>
</a>

<!-- Ladebildschirm -->
<div id="loader" class="fixed inset-0 z-50 flex flex-col items-center justify-center bg-background-light transition-opacity duration-500 ease-in-out">
    <div id="lottie-container" style="width: 200px; height: 200px;"></div>
    <p id="loading-text" class="mt-4 text-lg text-primary font-semibold"></p>
</div>

<!-- Hauptinhalt -->
<div id="dashboard-content" class="opacity-0 invisible transition-opacity duration-500 ease-in-out">
    <div class="relative flex h-auto min-h-screen w-full flex-col">
    <div class="layout-container flex h-full grow flex-col">
    <div class="flex flex-1 justify-center py-5">
    <div class="layout-content-container flex flex-col w-full max-w-7xl flex-1 px-4 sm:px-6 lg:px-8">
    <header class="flex items-center justify-between whitespace-nowrap border-b border-solid border-gray-200 py-4">
        <div class="flex items-center gap-4">
            <!-- HIER WURDE DAS LOGO EINGEFÜGT -->
            <img src="{{ url_for('static', filename='mtu-logo.png') }}" alt="MTU Logo" class="h-10 mix-blend-multiply">
            <h1 class="text-2xl font-bold leading-tight tracking-tight text-primary">Testcell Live Status</h1>
        </div>
        <div class="flex flex-1 justify-end gap-4">
            <label class="relative flex min-w-40 max-w-xs items-center">
                <div class="absolute left-3 text-gray-400"><span class="material-symbols-outlined">search</span></div>
                <input id="searchInput" class="form-input w-full min-w-0 flex-1 resize-none overflow-hidden rounded-lg border border-gray-300 bg-background-light py-2 pl-10 pr-4 text-base font-normal leading-normal placeholder-gray-400 focus:border-primary focus:ring-primary" placeholder="Search Projects" value=""/>
            </label>
        </div>
    </header>
    <div class="flex items-center justify-between mt-6 mb-4">
        <div class="flex gap-3 overflow-x-auto">
        </div>
    </div>

    <!-- Hauptcontainer für alle Projekte -->
    <div id="projects-container" class="space-y-4">
        <div id="active-projects" class="space-y-4"></div>
        <div id="completed-separator" class="hidden">
            <h3 class="text-lg font-semibold text-gray-500 pt-8 pb-4">Completed Projects</h3>
            <hr class="border-gray-300 mb-4">
        </div>
        <div id="completed-projects" class="space-y-4"></div>

    <!-- Jinja2 Makro (Korrigiert) -->
        {% macro render_step_recursive(step) %}
            <div class="mt-2 pl-1"> <!-- Kleines Padding links für Einrückung -->
                <div class="flex items-start">
                    {% if step.type == 'deviation' %}
                        <!-- Icon -->
                        <span class="material-symbols-outlined {{ 'text-danger' if step.status != 'Completed' else 'text-primary' }} text-lg -mt-0.5 mr-2 flex-shrink-0">subdirectory_arrow_right</span>
                        
                        <div class="flex-grow min-w-0">
                            <!-- Zeile 1: Titel -->
                            <p class="text-xs mb-0.5 {{ 'text-danger font-semibold' if step.status != 'Completed' else 'text-primary font-semibold' }}">
                                Deviation
                            </p>
                            
                            <!-- Zeile 2: Ticket Nummer (NEU: Eigene Zeile) -->
                                {% if step.deviation_details.hub_incident_number %}
                                    <button onclick="event.stopPropagation(); copyToClipboard('{{ step.deviation_details.hub_incident_number }}', this)" 
                                            class="ml-1 bg-slate-100 text-slate-600 px-1.5 py-0.5 rounded text-[10px] border border-slate-200 font-mono hover:bg-blue-50 hover:text-blue-600 hover:border-blue-200 transition-colors cursor-pointer inline-flex items-center gap-1 group" 
                                            title="Klicken zum Kopieren">
                                        <span class="material-symbols-outlined text-[10px] text-slate-400 group-hover:text-blue-500">content_copy</span>
                                        <span>{{ step.deviation_details.hub_incident_number }}</span>
                                    </button>
                                {% endif %}

                            <!-- Zeile 3: Grund / Beschreibung -->
                            <p class="text-xs text-gray-600 break-words leading-snug">{{ step.deviation_details.reason }}</p>
                            
                            <!-- Zeile 4: Status Text -->
                            <p class="text-[10px] italic {{ 'text-danger' if step.status != 'Completed' else 'text-primary' }} mt-0.5">
                                {{ 'Ongoing' if step.status != 'Completed' else 'Resolved' }}
                            </p>
                        </div>

                    {% else %}
                        <!-- Standard Step (Punkt) -->
                        <div class="w-2 h-2 rounded-full mr-2 mt-1.5 flex-shrink-0
                            {% if step.status == 'In Progress' %} border-2 border-primary-active bg-white animate-breathing-blue
                            {% elif step.status == 'Completed' %} bg-primary
                            {% else %} bg-gray-300 {% endif %}"></div>
                        
                        <div>
                            <p class="text-xs 
                                {% if step.status == 'In Progress' %} text-primary font-semibold
                                {% elif step.status == 'Completed' %} text-gray-600
                                {% elif step.status == 'N/R' %} text-gray-400 line-through
                                {% else %} text-gray-400 {% endif %}">
                                {{ step.name.split('(')[0] }}
                            </p>
                            <!-- Timestamp -->
                            {% if step.last_updated_at %}
                                <div class="text-gray-400 text-[10px] mt-0.5 whitespace-nowrap">
                                    {{ step.last_updated_at }}
                                </div>
                            {% endif %}
                        </div>
                    {% endif %}
                </div>

                <!-- Rekursion für Kinder -->
                {% if step.display_children %}
                    <div class="pl-4 mt-1 border-l-2 {{ 'border-red-100' if step.type == 'deviation' else 'border-gray-100' }} ml-1.5">
                        {% for child in step.display_children %}
                            {{ render_step_recursive(child) }}
                        {% endfor %}
                    </div>
                {% endif %}
            </div>
        {% endmacro %}


        <div id="initial-load-projects" class="hidden">
            {% for project in projects %}
            <div class="project-card group rounded-lg border border-gray-200 bg-white p-6 shadow-sm transition-all hover:shadow-md hover:border-primary/50" data-expanded="false" data-status="{{ project.status }}" onclick="this.setAttribute('data-expanded', this.getAttribute('data-expanded') === 'false' ? 'true' : 'false')">
                <div class="grid grid-cols-12 gap-6 items-center">
                    <div class="col-span-12 md:col-span-2">
                        <div class="flex flex-col">
                            <h2 class="project-code text-xl font-bold text-primary">{{ project.project_code }}</h2>
                            <p class="engine-type text-base font-semibold text-slate-700">{{ project.engine_type }}</p>
                            <div class="mt-2 text-sm text-slate-500 space-y-1">
                                <p><strong class="font-medium">S/N:</strong> <span class="project-serial font-mono">{{ project.engine_serial }}</span></p>
                                <p><strong class="font-medium">Customer:</strong> <span class="project-customer">{{ project.customer }}</span></p>
                            </div>
                        </div>
                    </div>

                    <div class="col-span-12 md:col-span-10">
                        <div class="flex items-center w-full gap-6">
                            <div class="flex-1 flex flex-col">
                                <div class="flex items-start w-full">
                                    {% for step in project.main_steps %}
                                        <div class="flex justify-center transition-all duration-500 
                                            group-data-[expanded=true]:flex-[{{ step.flex_weight }}_1_0%] 
                                            flex-[1_1_0%]"
                                        >
                                            <div class="flex flex-col items-center">
                                                {% if step.status == 'Completed' %}
                                                    <div class="w-5 h-5 rounded-full bg-primary z-10"></div>
                                                {% elif step.is_in_progress %}
                                                    <div class="w-5 h-5 rounded-full border-2 border-primary-active bg-white animate-breathing-blue z-10"></div>
                                                {% else %}
                                                    <div class="w-5 h-5 rounded-full bg-gray-200 z-10"></div>
                                                {% endif %}

                                                <div class="text-center">
                                                    <p class="text-xs mt-2 
                                                        {% if step.is_in_progress %} text-primary-active font-semibold
                                                        {% elif step.status == 'Completed' %} text-gray-800
                                                        {% elif step.status == 'N/R' %} text-gray-400 line-through
                                                        {% else %} text-gray-400 {% endif %}">
                                                        {{ step.name.split('(')[0] }}
                                                    </p>
                                                    {% if step.last_updated_at %}
                                                        <div class="text-[10px] text-gray-400 mt-0.5 whitespace-nowrap">
                                                            <p>{{ step.last_updated_at.split(',')[0] }}</p>
                                                            {% if ',' in step.last_updated_at %}
                                                                <p>{{ step.last_updated_at.split(',')[1] }}</p>
                                                            {% endif %}
                                                        </div>
                                                    {% endif %}
                                                </div>

                                                <div class="collapsed-view-dots justify-center mt-2 space-x-1.5">
                                                    {% for dot in step.collapsed_dots %}
                                                        <div class="w-1.5 h-1.5 rounded-full {{ dot.class }}"></div>
                                                    {% endfor %}
                                                </div>
                                            </div>
                                        </div>
                                        {% if not loop.last %}
                                            <div class="flex-1 h-0.5 {{ 'bg-primary' if step.status == 'Completed' else 'bg-gray-200' }} relative top-[10px]"></div>
                                        {% endif %}
                                    {% endfor %}
                                </div>

                                <div class="flex w-full min-h-[1px] max-h-0 opacity-0 pointer-events-none transition-all duration-500 ease-in-out group-data-[expanded=true]:max-h-[500px] group-data-[expanded=true]:opacity-100 group-data-[expanded=true]:mt-4 group-data-[expanded=true]:pointer-events-auto">
                                    {% for step in project.main_steps %}
                                        <div class="px-2 transition-all duration-500 
                                            group-data-[expanded=true]:flex-[{{ step.flex_weight }}_1_0%] 
                                            flex-[1_1_0%]"
                                        >
                                            {% for child in step.display_children %}
                                                {{ render_step_recursive(child) }}
                                            {% endfor %}
                                        </div>
                                        {% if not loop.last %}<div class="flex-1"></div>{% endif %}
                                    {% endfor %}
                                </div>
                            </div>

                            <div class="flex-shrink-0 w-24">
                                <div class="flex justify-end items-center">
                                    {% if project.status == 'completed' %}
                                        <p class="text-sm font-medium text-primary font-bold">Completed</p>
                                    {% else %}
                                        <p class="text-sm font-medium whitespace-nowrap">{{ project.progress }}%</p>
                                    {% endif %}
                                    <button class="ml-2"><span class="material-symbols-outlined text-gray-400 expand-icon transition-transform duration-300">expand_more</span></button>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
    </div>
    </div>
    </div>
    </div>
</div>

<script>

    lottie.loadAnimation({ container: document.getElementById('lottie-container'), renderer: 'svg', loop: true, autoplay: true, path: '{{ url_for('static', filename='airplane logistics.json') }}' });
    const loadingTextElement = document.getElementById('loading-text');
    const textToType = "Collecting engine data ...";
    let typeIndex = 0;
    function typeWriter() { if (typeIndex < textToType.length) { loadingTextElement.innerHTML = textToType.substring(0, typeIndex + 1) + '<span class="cursor"></span>'; typeIndex++; setTimeout(typeWriter, 100); } else { loadingTextElement.textContent = textToType; } }
    typeWriter();

    function separateProjectsByStatus() {
        const initialContainer = document.getElementById('initial-load-projects');
        const activeContainer = document.getElementById('active-projects');
        const completedContainer = document.getElementById('completed-projects');
        const separator = document.getElementById('completed-separator');
        const allProjectCards = Array.from(initialContainer.children);
        let completedCount = 0;
        allProjectCards.forEach(card => { if (card.dataset.status === 'completed') { completedContainer.appendChild(card); completedCount++; } else { activeContainer.appendChild(card); } });
        if (completedCount > 0) { separator.classList.remove('hidden'); }
        initialContainer.remove();
    }
    
    function copyToClipboard(text, btnElement) {
        navigator.clipboard.writeText(text).then(() => {
            // Visuelles Feedback
            const originalHtml = btnElement.innerHTML;
            const originalClasses = btnElement.className;
            
            // Success State
            btnElement.classList.remove('bg-slate-100', 'text-slate-600', 'border-slate-200');
            btnElement.classList.add('bg-green-100', 'text-green-700', 'border-green-200');
            btnElement.innerHTML = `<span class="material-symbols-outlined text-[10px]">check</span> Kopiert!`;
            
            // Reset nach 1.5 Sekunden
            setTimeout(() => {
                btnElement.className = originalClasses;
                btnElement.innerHTML = originalHtml;
            }, 1500);
        }).catch(err => {
            console.error('Kopieren fehlgeschlagen:', err);
        });
    }
    function showDashboard() {
        separateProjectsByStatus(); 
        const loaderDiv = document.getElementById('loader');
        const dashboard = document.getElementById('dashboard-content');
        loaderDiv.classList.add('opacity-0', 'invisible');
        dashboard.classList.remove('opacity-0', 'invisible');
    }

    setTimeout(showDashboard, 2000);

    document.addEventListener('DOMContentLoaded', function () {
        const searchInput = document.getElementById('searchInput');
        searchInput.addEventListener('keyup', function (event) {
            const searchTerm = event.target.value.toLowerCase();
            const separator = document.getElementById('completed-separator');
            let visibleCompletedCount = 0;
            const allCards = document.querySelectorAll('.project-card');
            allCards.forEach(function (card) {
                const codeText = card.querySelector('.project-code').textContent.toLowerCase();
                const serialText = card.querySelector('.project-serial').textContent.toLowerCase();
                const customerText = card.querySelector('.project-customer').textContent.toLowerCase();
                const engineTypeText = card.querySelector('.engine-type').textContent.toLowerCase();
                
                if (codeText.includes(searchTerm) || serialText.includes(searchTerm) || customerText.includes(searchTerm) || engineTypeText.includes(searchTerm)) {
                    card.style.display = 'block';
                    if (card.dataset.status === 'completed') { visibleCompletedCount++; }
                } else { card.style.display = 'none'; }
            });
            separator.style.display = visibleCompletedCount > 0 ? 'block' : 'none';
        });
    });
</script>

</body></html>
"""

TEST_EQUIPMENT_PAGE_HTML = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
    <title>MBET - Test Hardware Management</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <style>
        body { font-family: 'Inter', sans-serif; background-color: #f8fafc; }

        /* Sidebar Styles */
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label, #sidebar.is-collapsed .sidebar-title, #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link, #sidebar.is-collapsed .sidebar-header, #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined, #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }

        /* Styles für Test Equipment */
        .card {
            background-color: white;
            border-radius: 0.5rem;
            box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
            border: 1px solid #e2e8f0;
            padding: 1.5rem;
            display: flex; flex-direction: column;
        }

        /* Runde Status-Indikatoren */
        .status-indicator {
            width: 1.5rem; height: 1.5rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 700; color: white;
            display: flex; align-items: center; justify-content: center; margin-right: 0.25rem; box-shadow: 0 1px 2px rgba(0,0,0,0.1);
        }
        .bg-green-500 { background-color: #10B981 !important; }
        .bg-blue-500 { background-color: #3B82F6 !important; }
        .bg-yellow-500 { background-color: #F59E0B !important; }
        .bg-red-500 { background-color: #EF4444 !important; }

        .status-badge { padding: 0.125rem 0.625rem; font-size: 0.75rem; font-weight: 500; border-radius: 9999px; display: inline-flex; align-items: center; }
        .badge-available { background-color: #dcfce7; color: #166534; }
        .badge-maintenance { background-color: #fef9c3; color: #854d0e; }
        .badge-decommissioned { background-color: #fee2e2; color: #991b1b; }

        /* Kit & Trolley Tags */
        .kit-tag, .trolley-tag {
            display: inline-flex; align-items: center; background-color: #f1f5f9; border: 1px solid #cbd5e1;
            color: #334155; font-size: 0.75rem; font-weight: 600; margin-right: 0.5rem; margin-bottom: 0.5rem;
            padding: 0.25rem 0.75rem; border-radius: 9999px; cursor: pointer; transition: background-color 0.2s;
        }
        .kit-tag:hover, .trolley-tag:hover { background-color: #e2e8f0; }

        .kit-status-dot { width: 0.6rem; height: 0.6rem; border-radius: 50%; margin-right: 0.5rem; box-shadow: 0 0 0 1px white; }
        .bg-kit-ready { background-color: #10B981; }
        .bg-kit-partial { background-color: #F59E0B; }
        .bg-kit-missing { background-color: #EF4444; }

        .modal { transition: opacity 0.25s ease; z-index: 50; }
        body.modal-active { overflow-y: hidden; }
        .modal-backdrop { position: fixed; inset: 0; background-color: rgba(17, 24, 39, 0.5); z-index: 40; }
        .modal-content { position: relative; margin: 2.5rem auto; padding: 0; border: 1px solid #e5e7eb; width: 91.666667%; max-width: 56rem; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04); border-radius: 0.75rem; background-color: white; z-index: 50; display: flex; flex-direction: column; max-height: 90vh; }

        .custom-scrollbar::-webkit-scrollbar { width: 6px; height: 6px; }
        .custom-scrollbar::-webkit-scrollbar-track { background: #f1f5f9; }
        .custom-scrollbar::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 3px; }
        .custom-scrollbar::-webkit-scrollbar-thumb:hover { background: #94a3b8; }

        .toggle-sn-rows .material-icons-outlined { transition: transform 0.2s ease-in-out; }
        .toggle-sn-rows.is-open .material-icons-outlined { transform: rotate(90deg); }
        .pn-row:hover td { background-color: #f8fafc; }
        .sn-row { display: none; }
        .sn-row.is-visible { display: table-row; }
        .sn-row td { background-color: #f8fafc; }
        .new-kit-tag { display: flex; align-items: center; background-color: #f3f4f6; color: #374151; font-size: 0.75rem; font-weight: 500; margin-right: 0.5rem; margin-bottom: 0.5rem; padding-left: 0.75rem; padding-right: 0.5rem; padding-top: 0.25rem; padding-bottom: 0.25rem; border-radius: 9999px; border: 1px solid #e5e7eb; }

        .btn-icon-only { @apply text-blue-600 hover:text-blue-800 p-1 rounded-full hover:bg-blue-50 transition-colors; }
        .expand-all-btn { @apply p-1 rounded hover:bg-slate-200 text-slate-500 transition-colors cursor-pointer mr-2; }

        .product-type-tag { 
            display: inline-block;
            background-color: #f1f5f9; /* slate-100 */
            color: #475569;            /* slate-600 */
            font-size: 0.75rem;        /* text-xs */
            font-weight: 500;          /* font-medium */
            padding: 0.125rem 0.5rem;  /* px-2 py-0.5 */
            border-radius: 0.25rem;    /* rounded */
            border: 1px solid #e2e8f0; /* border-slate-200 */
            margin-right: 0.25rem;
            margin-bottom: 0.25rem;
        }

        /* Tag Input im Modal (Pillen-Design) */
        .tag-input-wrapper { 
            display: flex;
            flex-wrap: wrap;
            align-items: center;
            border: 1px solid #cbd5e1; /* slate-300 */
            border-radius: 0.5rem;     /* rounded-lg */
            padding: 0.375rem;         /* p-1.5 */
            background-color: white;
        }
        .tag-input-wrapper:focus-within {
            ring: 2px solid #3b82f6;   /* focus ring simulation */
            border-color: #3b82f6;
        }

        .tag-chip { 
            background-color: #dbeafe; /* blue-100 */
            color: #1e40af;            /* blue-800 */
            font-size: 0.75rem;        /* text-xs */
            font-weight: 600;          /* font-semibold */
            padding: 0.125rem 0.5rem;  /* px-2 py-0.5 */
            border-radius: 0.375rem;   /* rounded-md */
            display: flex;
            align-items: center;
            margin-right: 0.25rem;
            margin-bottom: 0.25rem;
        }

        .tag-chip-remove { 
            margin-left: 0.25rem;
            color: #3b82f6;            /* blue-500 */
            cursor: pointer;
            font-weight: bold;
        }
        .tag-chip-remove:hover { color: #1d4ed8; /* blue-700 */ }

        .tag-input-field { 
            flex-grow: 1;
            min-width: 80px;
            border: none;
            outline: none;
            font-size: 0.875rem;       /* text-sm */
            padding: 0.25rem;
        }
        .tag-input-field:focus { box-shadow: none; }

        .tag-dropdown { 
            position: absolute;
            z-index: 50;
            width: 100%;
            background-color: white;
            border: 1px solid #cbd5e1;
            border-radius: 0.5rem;
            box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
            margin-top: 0.25rem;
            max-height: 10rem;
            overflow-y: auto;
            display: none; /* Standardmäßig versteckt */
        }
        .tag-dropdown.hidden { display: none; } /* Sicherstellen, dass hidden greift */
        .tag-dropdown:not(.hidden) { display: block; }

        .tag-dropdown-item { 
            padding: 0.5rem 0.75rem;
            font-size: 0.875rem;
            color: #334155;
            cursor: pointer;
        }
        .tag-dropdown-item:hover { background-color: #f8fafc; }

        /* Styles für Maintenance Popover */
        .maint-popover-wrapper { position: relative; display: inline-block; }
        .maint-popover-content {
            visibility: hidden; opacity: 0; position: absolute; bottom: 120%; left: 50%; transform: translateX(-50%);
            width: 280px; background-color: white; border: 1px solid #e2e8f0; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
            border-radius: 0.5rem; padding: 0.5rem; z-index: 50; transition: opacity 0.2s, visibility 0.2s;
            text-align: left;
        }
        .maint-popover-wrapper:hover .maint-popover-content { visibility: visible; opacity: 1; }
        /* Kleiner Pfeil unten */
        .maint-popover-content::after {
            content: ""; position: absolute; top: 100%; left: 50%; margin-left: -5px;
            border-width: 5px; border-style: solid; border-color: white transparent transparent transparent;
        }
    </style>
</head>
<body class="text-slate-800">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200 sticky top-0 z-30">
             <div class="flex items-center gap-3">
                <h1 class="text-2xl font-bold tracking-tight text-slate-800">Hardware & Test Equipment</h1>
             </div>
             <div class="flex items-center gap-4">
             </div>
        </header>

        <div class="p-4 sm:p-6 lg:p-8">

<!-- Middle Section: Mapping, Rüstwagen & Lager -->
            <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-24">
                
                <!-- 1. Engine to Kit Mapping Table -->
                <div class="card h-full">
                    <div class="flex justify-between items-start mb-4">
                        <div>
                            <h2 class="text-lg font-bold text-slate-800">Engine Mapping</h2>
                            <p class="text-sm text-slate-500">Zuordnung von Hardware-Kits</p>
                        </div>
                        <button class="btn-icon-only" onclick="openNewMappingModal()" title="Neues Mapping">
                            <span class="material-icons-outlined text-2xl">add_circle</span>
                        </button>
                    </div>
                    <div class="overflow-x-auto border border-slate-200 rounded-lg flex-grow max-h-64 custom-scrollbar">
                        <table class="w-full min-w-[300px] text-sm text-left" id="engineKitMappingTable">
                            <thead class="bg-slate-50 text-slate-600 uppercase text-xs font-semibold sticky top-0 z-10">
                                <tr><th class="px-4 py-3 border-b bg-slate-50">Engine</th><th class="px-4 py-3 border-b bg-slate-50">Kits</th><th class="px-4 py-3 border-b text-right bg-slate-50"></th></tr>
                            </thead>
                            <tbody class="divide-y divide-slate-200 bg-white"></tbody>
                        </table>
                    </div>
                </div>

                <!-- 2. Rüstwagen (Trolleys) -->
                <div class="card h-full">
                    <div class="flex justify-between items-start mb-4">
                        <div>
                            <h2 class="text-lg font-bold text-slate-800">Rüstwagen</h2>
                            <p class="text-sm text-slate-500">Mobile Lagereinheiten</p>
                        </div>
                        <!-- WICHTIG: Hier rufen wir jetzt openStandardTrolleyModal auf -->
                        <button class="btn-icon-only" onclick="openStandardTrolleyModal(null)" title="Neuer Rüstwagen">
                            <span class="material-icons-outlined text-2xl">add_circle</span>
                        </button>
                    </div>
                    <div class="overflow-x-auto border border-slate-200 rounded-lg flex-grow max-h-64 custom-scrollbar">
                        <table class="w-full min-w-[300px] text-sm text-left" id="trolleyTable">
                            <thead class="bg-slate-50 text-slate-600 uppercase text-xs font-semibold sticky top-0 z-10">
                                <tr>
                                    <th class="px-4 py-3 border-b bg-slate-50">Engine</th>
                                    <th class="px-4 py-3 border-b bg-slate-50">Name</th>
                                    <th class="px-4 py-3 border-b text-right bg-slate-50"></th>
                                </tr>
                            </thead>
                            <tbody class="divide-y divide-slate-200 bg-white" id="trolleyTableBody"></tbody>
                        </table>
                    </div>
                </div>

                <!-- 3. NEU: Lagerorte (Storage) -->
                <div class="card h-full">
                    <div class="flex justify-between items-start mb-4">
                        <div>
                            <h2 class="text-lg font-bold text-slate-800">Lagerorte</h2>
                            <p class="text-sm text-slate-500">Regale, Schränke, Kisten</p>
                        </div>
                        <button class="btn-icon-only" onclick="openStorageModal(null)" title="Neuer Lagerort">
                            <span class="material-icons-outlined text-2xl">add_circle</span>
                        </button>
                    </div>
                    <div class="overflow-x-auto border border-slate-200 rounded-lg flex-grow max-h-64 custom-scrollbar">
                        <table class="w-full min-w-[300px] text-sm text-left" id="storageTable">
                            <thead class="bg-slate-50 text-slate-600 uppercase text-xs font-semibold sticky top-0 z-10">
                                <tr>
                                    <th class="px-4 py-3 border-b bg-slate-50">Bereich</th>
                                    <th class="px-4 py-3 border-b bg-slate-50">Name</th>
                                    <th class="px-4 py-3 border-b text-right bg-slate-50"></th>
                                </tr>
                            </thead>
                            <tbody class="divide-y divide-slate-200 bg-white" id="storageTableBody"></tbody>
                        </table>
                    </div>
                </div>
            </div>

            <!-- KPIs -->
            <!-- Das Grid wurde auf 3 Spalten angepasst (lg:grid-cols-3) und die "Assigned"-Kachel wurde entfernt -->
            <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
                <div class="card flex-row items-center border-white space-x-4 hover:shadow-md transition-shadow">
                    <div class="p-3 bg-blue-50 rounded-full text-blue-600"><span class="material-icons-outlined text-3xl">memory</span></div>
                    <div><p class="text-sm text-slate-500">Total Part Numbers</p><p class="text-2xl font-bold text-slate-800" id="totalPnCount">0</p></div>
                </div>
                <div class="card flex-row items-center border-white space-x-4 hover:shadow-md transition-shadow">
                    <div class="p-3 bg-green-50 rounded-full text-green-600"><span class="material-icons-outlined text-3xl">inventory_2</span></div>
                    <div><p class="text-sm text-slate-500">Total Serial Numbers</p><p class="text-2xl font-bold text-slate-800" id="totalSnCount">0</p></div>
                </div>
                <div class="card flex-row items-center space-x-4 hover:shadow-md transition-shadow">
                    <div class="p-3 bg-amber-50 rounded-full text-amber-600"><span class="material-icons-outlined text-3xl">build_circle</span></div>
                    <div><p class="text-sm text-slate-500">In Maintenance</p><p class="text-2xl font-bold text-slate-800" id="maintenanceCount">0</p></div>
                </div>
            </div>

            <!-- Inventory Table -->
            <div class="card">
                <div class="flex flex-col gap-4 mb-4">
                    <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
                        <div>
                            <h2 class="text-lg font-bold text-slate-800 flex items-center gap-2">
                                Hardware Inventory
                                <!-- NEU: Minimalistischer Counter -->
                                <span id="hwTableCount" class="text-xs font-normal text-slate-400 bg-slate-100 px-2 py-0.5 rounded-full hidden">0 Ergebnisse</span>
                            </h2>
                            <p class="text-sm text-slate-500">Verwaltung einzelner Hardware-Komponenten und Seriennummern</p>
                        </div>
                        
                        <div class="flex items-center gap-3 flex-wrap">
                            <!-- Search -->
                            <div class="relative">
                                <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 text-sm">search</span>
                                <input type="text" id="hardwareSearchInput" oninput="applyHardwareFilters()" placeholder="Suchen..." class="pl-9 pr-3 py-1.5 rounded-full border border-slate-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm w-40 transition-all focus:w-64">
                            </div>
                            
                        <!-- Inline Filter Chips -->
                        <div class="flex items-center gap-2 overflow-x-auto no-scrollbar">
                            
                            <!-- Type Filter -->
                            <div class="relative">
                                <!-- HINZUGEFÜGT: bg-none (entfernt den doppelten Pfeil) -->
                                <select id="filterHwType" onchange="applyHardwareFilters()" 
                                    class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                    <option value="all">Typ: Alle</option>
                                </select>
                                <!-- Unser manueller, schönerer Pfeil -->
                                <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                            </div>
                        
                            <!-- Product Filter -->
                            <div class="relative">
                                <select id="filterHwProduct" onchange="applyHardwareFilters()" 
                                    class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                    <option value="all">Produkt: Alle</option>
                                </select>
                                <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                            </div>
                        
                            <!-- Status Filter -->
                            <div class="relative">
                                <select id="filterHwStatus" onchange="applyHardwareFilters()" 
                                    class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                    <option value="all">Status: Alle</option>
                                    <option value="Available">Verfügbar</option>
                                    <option value="Maintenance">Wartung</option>
                                    <option value="In Order">Bestellt</option>
                                    <option value="Decommissioned">Ausgemustert</option>
                                </select>
                                <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                            </div>
                        
                            <!-- Location Filter -->
                            <div class="relative">
                                <select id="filterHwLocation" onchange="applyHardwareFilters()" 
                                    class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                    <option value="all">Ort: Alle</option>
                                </select>
                                <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                            </div>
                                <!-- NEU: Download Button -->
                            <button onclick="downloadFilteredInventory()" class="ml-2 p-1.5 text-slate-400 hover:text-blue-600 hover:bg-slate-100 rounded-full transition-colors" title="Aktuelle Liste exportieren">
                                <span class="material-icons-outlined text-lg">download</span>
                            </button>
                        </div>                        

                            <!-- Bulk Edit (bleibt) -->
                            <button id="bulkEditBtn" class="hidden bg-white border border-slate-300 text-slate-700 px-3 py-2 rounded-lg text-sm font-medium hover:bg-slate-50 flex items-center gap-2 shadow-sm" onclick="openBulkEditModal()">
                                <span class="material-icons-outlined text-base">edit_note</span>
                                <span id="bulkEditCount">0</span>
                            </button>

                            <!-- New Part Button -->
                            <button class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700 flex items-center space-x-2 shadow-sm transition-colors" onclick="openPartNumberModal(null)">
                                <span class="material-icons-outlined text-base">add</span><span>New</span>
                            </button>
                        </div>
                    </div>

                </div>

                <div class="overflow-x-auto border border-slate-200 rounded-lg">
                    <!-- Tabelle bleibt gleich -->
                    <table class="w-full min-w-[1200px] text-sm text-left" id="hardwareInventoryTable">
                        <thead class="bg-slate-50 text-slate-600 uppercase text-xs font-semibold">
                            <tr>
                                <th class="px-4 py-3 border-b w-10 text-center">
                                    <input type="checkbox" id="selectAllHardware" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-4 w-4">
                                </th>
                                <th class="px-6 py-3 border-b flex items-center">
                                    <button id="expandAllBtn" class="expand-all-btn" onclick="toggleAllRows()" title="Alle auf-/zuklappen">
                                        <span class="material-icons-outlined text-lg">unfold_more</span>
                                    </button>
                                    P/N
                                </th>
                                <th class="px-6 py-3 border-b">Name</th>
                                <th class="px-6 py-3 border-b">Hardware Type</th>
                                <th class="px-6 py-3 border-b">Product Type</th>
                                <th class="px-6 py-3 border-b">QTY</th>
                                <th class="px-6 py-3 border-b">Status Overview</th>
                                <th class="px-6 py-3 border-b text-right">Actions</th>
                            </tr>
                        </thead>
                        <tbody id="hardwareInventoryTbody" class="divide-y divide-slate-200 bg-white"></tbody>
                    </table>
                </div>
            </div>
        </div>
    </main>
</div>

<!-- --- MODALS --- -->

<!-- 1. Trolley Modal (Updated with View/Edit) -->
<div class="modal hidden fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="trolleyModal">
    <div class="modal-backdrop" onclick="closeTrolleyModal()"></div>
    <div class="modal-content">
        <div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-200">
            <div>
                <p class="text-xl font-bold text-slate-800" id="trolleyModalTitle">Rüstwagen Details</p>
                <p class="text-xs text-slate-500 mt-1" id="trolleyModalSubtitle">Details anzeigen</p>
            </div>
            <div class="flex items-center gap-2">
                <!-- Edit Button (nur im View Mode sichtbar) -->
                <button id="btnEditTrolley" onclick="toggleTrolleyEditMode(true)" class="text-slate-400 hover:text-blue-600 p-2 rounded-full hover:bg-slate-100 transition-colors" title="Bearbeiten">
                    <span class="material-icons-outlined">edit</span>
                </button>
                <button onclick="closeTrolleyModal()" class="text-slate-400 hover:text-slate-600 p-2 rounded-full hover:bg-slate-100"><span class="material-icons-outlined">close</span></button>
            </div>
        </div>
        <div class="flex-grow overflow-y-auto custom-scrollbar p-6">
            <form id="trolleyForm" class="space-y-6">
                <input type="hidden" id="trolleyModalOriginalName">

                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div>
                        <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Name / ID</label>
                        <input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition-colors" id="trolleyModalName" placeholder="z.B. PS001" required>
                    </div>
                    <div>
                        <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Engine Type</label>
                        <input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition-colors" id="trolleyModalEngine" required>
                    </div>
                </div>
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Verantwortlicher</label>
                    <input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500 transition-colors" id="trolleyModalResponsible" placeholder="Name der verantwortlichen Person" list="responsiblePersonsList">
                    <datalist id="responsiblePersonsList"></datalist>
                </div>

                <div class="pt-4 border-t border-slate-200">
                    <div class="flex justify-between items-center mb-2">
                        <label class="block text-xs font-semibold text-slate-500 uppercase">Bilder</label>
                        <label id="btnAddTrolleyImage" class="hidden cursor-pointer text-sm text-blue-600 hover:text-blue-800 font-medium flex items-center gap-1">
                            <span class="material-icons-outlined text-base">add_photo_alternate</span>Bild hinzufügen
                            <input type="file" accept="image/*" class="hidden" onchange="uploadTrolleyImage(this)">
                        </label>
                    </div>
                    <div id="trolleyImagesList" class="space-y-1"></div>
                    <div id="trolleyImagesEmpty" class="text-slate-400 italic text-sm">Keine Bilder.</div>
                </div>

                <div class="pt-4 border-t border-slate-200">
                    <div class="flex justify-between items-center mb-3">
                        <h3 class="text-lg font-semibold text-slate-800">Inhalt (Zuweisung)</h3>
                        <!-- Add Button (nur im Edit Mode sichtbar) -->
                        <button id="btnAddTrolleyItem" class="text-sm text-blue-600 hover:text-blue-800 font-medium flex items-center hidden" type="button" onclick="addTrolleyItem()">
                            <span class="material-icons-outlined text-base mr-1">add</span>Add Hardware
                        </button>
                    </div>

                    <div class="grid grid-cols-12 gap-2 mb-2 px-2 text-xs font-semibold text-slate-500 uppercase">
                        <div class="col-span-2">P/N</div>
                        <div class="col-span-2">S/N</div>
                        <div class="col-span-3">Name</div> <!-- NEU -->
                        <div class="col-span-1 text-center">Anz.</div>
                        <div class="col-span-3">Pos (Lvl/Box)</div> <!-- Zusammengefasst -->
                        <div class="col-span-1"></div>
                    </div>
                    <div id="trolleyItemsContainer" class="space-y-2"></div>
                    
                    <div id="trolleyEmptyState" class="hidden text-center py-8 text-slate-400 italic text-sm">
                        Dieser Rüstwagen ist leer.
                    </div>
                </div>
            </form>
        </div>
        <div class="flex-shrink-0 py-4 px-6 bg-slate-50 border-t border-slate-200 flex justify-between items-center rounded-b-xl">
            <!-- Delete Button (nur im Edit Mode sichtbar) -->
            <button id="deleteTrolleyBtn" class="hidden px-4 py-2 text-red-600 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 shadow-sm flex items-center transition-colors" type="button">
                <span class="material-icons-outlined text-base mr-1">delete</span>Delete
            </button>
            
            <div class="flex gap-3 ml-auto">
                <!-- View Mode Buttons -->
                <div id="trolleyViewButtons" class="flex gap-3">
                     <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg hover:bg-slate-50 shadow-sm" type="button" onclick="closeTrolleyModal()">Schließen</button>
                </div>
                
                <!-- Edit Mode Buttons -->
                <div id="trolleyEditButtons" class="flex gap-3 hidden">
                    <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg hover:bg-slate-50 shadow-sm" type="button" onclick="toggleTrolleyEditMode(false)">Abbrechen</button>
                    <button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 shadow-sm flex items-center" type="submit" form="trolleyForm"><span class="material-icons-outlined text-base mr-1">save</span>Speichern</button>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- 2. Part Number Modal -->
<div class="modal hidden fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="partNumberModal">
    <div class="modal-backdrop" onclick="closePartNumberModal()"></div>
    <div class="modal-content">
        <div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-200">
            <div>
                <p class="text-xl font-bold text-slate-800" id="partNumberModalTitle">Part Details</p>
                <p class="text-xs text-slate-500 mt-1" id="partNumberModalSubtitle">Details anzeigen</p>
            </div>
            <div class="flex items-center gap-2">
                <!-- Edit Button (Nur im View Mode) -->
                <button id="btnEditPart" onclick="togglePartNumberEditMode(true)" class="text-slate-400 hover:text-blue-600 p-2 rounded-full hover:bg-slate-100 transition-colors" title="Bearbeiten">
                    <span class="material-icons-outlined text-lg">edit</span>
                </button>
                <button onclick="closePartNumberModal()" class="text-slate-400 hover:text-slate-600 p-2 rounded-full hover:bg-slate-100 transition-colors">
                    <span class="material-icons-outlined">close</span>
                </button>
            </div>
        </div>
        <div class="flex-grow overflow-y-auto custom-scrollbar p-6">
            <form id="partNumberForm" class="space-y-6">
                <input type="hidden" id="pnModal_originalPartNumber">
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                    <div><label class="block text-xs font-semibold text-slate-500 uppercase mb-1" for="pnModal_partNumber">P/N (Part Number)</label><input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" id="pnModal_partNumber" required></div>
                    <div><label class="block text-xs font-semibold text-slate-500 uppercase mb-1" for="pnModal_hardwareType">Hardware Type</label><input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" id="pnModal_hardwareType"></div>
                </div>
                <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
                     <div><label class="block text-xs font-semibold text-slate-500 uppercase mb-1" for="pnModal_name">Name</label><input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" id="pnModal_name"></div>
                     <div>
                        <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Product Type(s)</label>
                        <div class="relative">
                            <div class="tag-input-wrapper" id="pnModal_productTags">
                                <input type="text" id="pnModal_productInput" class="tag-input-field" placeholder="Add type...">
                            </div>
                            <div id="pnModal_productDropdown" class="tag-dropdown"></div>
                        </div>
                     </div>
                </div>
                <div><label class="block text-xs font-semibold text-slate-500 uppercase mb-1" for="pnModal_description">Description</label><textarea class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500 focus:border-blue-500" id="pnModal_description" rows="2"></textarea></div>

                <div class="pt-6 border-t border-slate-200">
                    <div class="flex justify-between items-center mb-3">
                        <h3 class="text-lg font-semibold text-slate-800">Serial Numbers</h3>
                        <button id="btnAddSerial" class="text-sm text-blue-600 hover:text-blue-800 font-medium flex items-center" type="button" onclick="addSerialNumberBlock()"><span class="material-icons-outlined text-base mr-1">add</span>Add Serial Number</button>
                    </div>
                    <div id="serialNumbersContainer" class="space-y-3"></div>
                </div>
            </form>
        </div>
        <div class="flex-shrink-0 py-4 px-6 bg-slate-50 border-t border-slate-200 flex justify-between items-center rounded-b-xl">
            <!-- Delete Button (Nur im Edit Mode sichtbar) -->
            <button id="deletePartBtn" class="hidden px-4 py-2 text-red-600 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 shadow-sm flex items-center transition-colors" type="button">
                <span class="material-icons-outlined text-base mr-1">delete</span>Delete
            </button>
            
            <div class="flex gap-3 ml-auto">
                <!-- View Mode Buttons -->
                <div id="partViewButtons" class="flex gap-3">
                     <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg hover:bg-slate-50 shadow-sm" type="button" onclick="closePartNumberModal()">Schließen</button>
                </div>
                
                <!-- Edit Mode Buttons -->
                <div id="partEditButtons" class="flex gap-3 hidden">
                    <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg hover:bg-slate-50 shadow-sm" type="button" onclick="togglePartNumberEditMode(false)">Abbrechen</button>
                    <button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 shadow-sm flex items-center" type="submit" form="partNumberForm"><span class="material-icons-outlined text-base mr-1">save</span>Speichern</button>
                </div>
            </div>
        </div>
    </div>
</div>

<!-- 3. New Mapping Modal -->
<div class="modal hidden fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="newMappingModal">
    <div class="modal-backdrop" onclick="closeNewMappingModal()"></div>
    <div class="modal-content" style="max-width: 32rem;">
        <div class="py-4 px-6 border-b border-slate-200 flex justify-between items-center">
            <p class="text-xl font-bold text-slate-800">New Engine-Kit Mapping</p>
            <button class="text-slate-400 hover:text-slate-600" onclick="closeNewMappingModal()"><span class="material-icons-outlined">close</span></button>
        </div>
        <div class="p-6">
            <form id="newMappingForm" class="space-y-6">
                <div><label class="block text-xs font-semibold text-slate-500 uppercase mb-1" for="newEngineType">New Engine Type</label><input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500" id="newEngineType" required placeholder="e.g. LEAP-1A"></div>
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1" for="newKitNameInput">Add Kit Names</label>
                    <div class="flex gap-2">
                        <input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500" id="newKitNameInput" placeholder="e.g., Kit-LEAP-Main">
                        <button class="bg-slate-100 border border-slate-300 px-4 py-2 rounded-lg text-slate-700 hover:bg-slate-200" type="button" id="addKitTagBtn">Add</button>
                    </div>
                    <div id="newKitTagsContainer" class="mt-3 flex flex-wrap gap-2 min-h-[2rem]"></div>
                </div>
            </form>
        </div>
        <div class="py-4 px-6 bg-slate-50 border-t border-slate-200 flex justify-end gap-3 rounded-b-xl">
            <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg shadow-sm hover:bg-slate-50" type="button" onclick="closeNewMappingModal()">Cancel</button>
            <button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 shadow-sm flex items-center gap-2" type="submit" form="newMappingForm"><span class="material-icons-outlined text-base">add_circle</span><span>Create</span></button>
        </div>
    </div>
</div>

<!-- 4. Edit Mapping Modal -->
<div class="modal hidden fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="editMappingModal">
    <div class="modal-backdrop" onclick="closeEditMappingModal()"></div>
    <div class="modal-content" style="max-width: 32rem;">
        <div class="py-4 px-6 border-b border-slate-200 flex justify-between items-center">
            <p class="text-xl font-bold text-slate-800" id="editMappingModalTitle">Edit Mapping</p>
            <button class="text-slate-400 hover:text-slate-600" onclick="closeEditMappingModal()"><span class="material-icons-outlined">close</span></button>
        </div>
        <div class="p-6">
            <form id="editMappingForm" class="space-y-6">
                <input type="hidden" id="originalEngineType">
                <div><label class="block text-xs font-semibold text-slate-500 uppercase mb-1" for="editEngineType">Engine Type</label><input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500" id="editEngineType" required></div>
                <div><label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Associated Kits</label><div id="editKitTagsContainer" class="mt-2 flex flex-wrap gap-2 border border-slate-200 bg-slate-50 p-3 rounded-lg min-h-[4rem]"></div></div>
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1" for="editKitNameInput">Add New Kit</label>
                    <div class="flex gap-2">
                        <input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500" id="editKitNameInput" placeholder="Add another kit...">
                        <button class="bg-slate-100 border border-slate-300 px-4 py-2 rounded-lg text-slate-700 hover:bg-slate-200" type="button" id="addKitTagToEditBtn">Add</button>
                    </div>
                </div>
            </form>
        </div>
        <div class="py-4 px-6 bg-slate-50 border-t border-slate-200 flex justify-between items-center rounded-b-xl">
            <!-- Delete Button -->
            <button id="deleteMappingBtn" class="px-4 py-2 text-red-600 bg-red-50 border border-red-200 rounded-lg hover:bg-red-100 shadow-sm flex items-center transition-colors" type="button">
                <span class="material-icons-outlined text-base mr-1">delete</span>Delete
            </button>
            <div class="flex gap-3">
                <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg shadow-sm hover:bg-slate-50" type="button" onclick="closeEditMappingModal()">Cancel</button>
                <button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 shadow-sm flex items-center gap-2" type="submit" form="editMappingForm"><span class="material-icons-outlined text-base">save</span><span>Save Changes</span></button>
            </div>
        </div>
    </div>
</div>

<!-- 5. BOM Modal (Updated with View/Edit Mode) -->
<div class="modal hidden fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="bomModal">
    <div class="modal-backdrop" onclick="closeBomModal()"></div>
    <div class="modal-content">
        
        <!-- Header -->
        <div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-200">
            <div class="flex-grow mr-4">
                <p class="text-xl font-bold text-slate-800">Bill of Materials</p>
                <div class="flex items-center mt-1">
                    <span class="text-sm text-slate-500 mr-2 flex-shrink-0">Kit Name:</span>
                    <!-- Input: Classes werden per JS getoggled -->
                    <input type="text" id="modalKitNameInput" 
                           class="font-mono font-medium text-slate-700 bg-transparent border-b border-transparent w-full text-sm py-0.5 px-1 rounded-sm transition-all outline-none" 
                           placeholder="Kit Name eingeben..." readonly>
                </div>
                <span id="modalKitId" class="hidden"></span>
            </div>
            
            <div class="flex items-center gap-2">
                <!-- Edit Button (Nur im View Mode sichtbar) -->
                <button id="btnEditBom" onclick="toggleBomEditMode(true)" class="text-slate-400 hover:text-blue-600 p-2 rounded-full hover:bg-slate-100 transition-colors" title="Bearbeiten">
                    <span class="material-icons-outlined text-lg">edit</span>
                </button>
                <button onclick="closeBomModal()" class="text-slate-400 hover:text-slate-600 p-2 rounded-full hover:bg-slate-100 transition-colors">
                    <span class="material-icons-outlined">close</span>
                </button>
            </div>
        </div>

        <!-- Body -->
        <div class="flex-grow overflow-y-auto custom-scrollbar p-6">
            <form id="bomForm">
                <div class="space-y-2" id="bomItemsContainer">
                    <!-- Items inserted via JS -->
                </div>
                
                <!-- Add Button (Nur im Edit Mode sichtbar) -->
                <div class="mt-6 pt-4 border-t border-slate-100 hidden" id="bomAddContainer">
                    <button class="text-sm text-blue-600 hover:text-blue-800 font-medium flex items-center px-2 py-1 rounded hover:bg-blue-50 transition-colors inline-flex" onclick="addBomItem()" type="button">
                        <span class="material-icons-outlined text-base mr-1">add_circle_outline</span> Add Item
                    </button>
                </div>
            </form>
        </div>

        <!-- Footer -->
        <div class="flex-shrink-0 py-4 px-6 bg-slate-50 border-t border-slate-200 flex justify-end gap-3 rounded-b-xl">
            
            <!-- View Mode Footer -->
            <div id="bomViewFooter">
                <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg shadow-sm hover:bg-slate-50" type="button" onclick="closeBomModal()">Schließen</button>
            </div>

            <!-- Edit Mode Footer (Hidden by default) -->
            <div id="bomEditFooter" class="hidden flex gap-3">
                <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg shadow-sm hover:bg-slate-50" type="button" onclick="toggleBomEditMode(false)">Abbrechen</button>
                <button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 shadow-sm flex items-center gap-2" type="submit" form="bomForm">
                    <span class="material-icons-outlined text-base">save</span> Speichern
                </button>
            </div>
        </div>
    </div>
</div>

<!-- 6. Bulk Edit Modal -->
<div class="modal hidden fixed w-full h-full top-0 left-0 flex items-center justify-center z-[999]" id="bulkEditModal">
    <div class="modal-backdrop" onclick="closeBulkEditModal()"></div>
    <div class="modal-content" style="max-width: 30rem;">
        <div class="flex-shrink-0 py-4 px-6 flex justify-between items-center border-b border-slate-200">
            <p class="text-xl font-bold text-slate-800">Bulk Edit</p>
            <button onclick="closeBulkEditModal()" class="text-slate-400 hover:text-slate-600"><span class="material-icons-outlined">close</span></button>
        </div>
        <div class="p-6">
            <p class="text-sm text-slate-500 mb-4">Änderungen werden auf <strong id="bulkEditCountLabel">0</strong> Einträge angewendet. Leere Felder werden ignoriert (nicht geändert).</p>
            <form id="bulkEditForm" class="space-y-4">
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Hardware Type</label>
                    <input class="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-blue-500" id="bulkHardwareType" placeholder="Unverändert lassen...">
                </div>
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Product Type(s)</label>
                    <!-- Wir nutzen hier die gleiche Tag-Input Struktur wie im Einzel-Edit -->
                    <div class="relative">
                        <div class="tag-input-wrapper" id="bulkProductTags">
                            <input type="text" id="bulkProductInput" class="tag-input-field" placeholder="Typen hinzufügen (überschreibt!)...">
                        </div>
                        <div id="bulkProductDropdown" class="tag-dropdown"></div>
                    </div>
                    <p class="text-xs text-amber-600 mt-1">Hinweis: Setzt den Product Type für alle gewählten Teile neu.</p>
                </div>
            </form>
        </div>
        <div class="flex-shrink-0 py-4 px-6 bg-slate-50 border-t border-slate-200 flex justify-end gap-3 rounded-b-xl">
            <button class="px-4 py-2 text-slate-700 bg-white border border-slate-300 rounded-lg hover:bg-slate-50 shadow-sm" onclick="closeBulkEditModal()">Cancel</button>
            <button class="px-4 py-2 text-white bg-blue-600 rounded-lg hover:bg-blue-700 shadow-sm flex items-center" onclick="submitBulkEdit()">
                <span class="material-icons-outlined text-base mr-1">done_all</span>Apply
            </button>
        </div>
    </div>
</div>

<!-- LIGHTBOX MODAL (Kopie aus Maintenance) -->
<div id="lightboxModal" class="modal hidden fixed inset-0 z-[9999] bg-black bg-opacity-90 flex items-center justify-center backdrop-blur-sm transition-opacity duration-300" onclick="closeLightbox()">
    <div class="relative max-w-full max-h-full p-4 flex flex-col items-center justify-center">
        <img id="lightboxImage" src="" class="max-h-[85vh] max-w-[90vw] object-contain rounded shadow-2xl cursor-default" onclick="event.stopPropagation()">
        <button class="absolute top-6 right-6 text-white bg-white/10 hover:bg-white/20 rounded-full p-2 transition-colors backdrop-blur-md" onclick="closeLightbox()">
            <span class="material-icons-outlined text-2xl">close</span>
        </button>
        <p id="lightboxCaption" class="mt-4 text-slate-200 text-sm font-medium tracking-wide bg-black/50 px-4 py-1 rounded-full"></p>
    </div>
</div>

<script>
  // --- INITIAL DATA LOAD FROM SERVER ---
  let hardwareInventory = {{ inventory_json | safe }};
  let kitBoms = {{ boms_json | safe }};
  let engineMappings = {{ mappings_json | safe }};
  const bulkEditModal = document.getElementById('bulkEditModal');
  let selectedPartNumbers = new Set();
  let currentBulkProductTags = []; // Für das Bulk Tag Input
  if (Array.isArray(engineMappings) || !engineMappings) engineMappings = {};
  let trolleys = {{ trolleys_json | safe }};
  if (Array.isArray(trolleys) || !trolleys) trolleys = {};
  let storageUnits = {{ storage_units_json | safe }};
  if (Array.isArray(storageUnits) || !storageUnits) storageUnits = {};
  const allEmployeeNames = {{ employees_json | safe }};
  let isTrolleyEditMode = false;
  let currentEditType = 'trolley'; // 'trolley' oder 'storage'
    // --- DOKUMENTE & LIGHTBOX ---
    
    function openLightbox(url, caption) {
        const m = document.getElementById('lightboxModal');
        const i = document.getElementById('lightboxImage');
        const c = document.getElementById('lightboxCaption');
        i.src = url; c.textContent = caption;
        m.classList.remove('hidden');
    }
    function closeLightbox() { document.getElementById('lightboxModal').classList.add('hidden'); }
    
    async function uploadHardwareFile(inputElement) {
        const file = inputElement.files[0];
        if(!file) return;
        
        const container = inputElement.closest('.docs-container');
        const list = container.querySelector('.docs-list');
        
        const formData = new FormData();
        formData.append('file', file);
        
        try {
            const res = await fetch('/api/hardware/upload', { method: 'POST', body: formData });
            if(!res.ok) throw new Error();
            const data = await res.json();
            
            addDocItem(list, data.path, data.filename, data.stored_name);
            inputElement.value = ''; // Reset
        } catch(e) {
            alert("Upload fehlgeschlagen.");
        }
    }
    
    function addDocItem(listElement, path, name, storedName) {
        const div = document.createElement('div');
        div.className = "flex items-center justify-between p-2 bg-white border border-slate-200 rounded mb-1 text-sm doc-item";
        // Daten im DOM speichern für den späteren Save
        div.dataset.path = path;
        div.dataset.name = name;
        div.dataset.stored = storedName;
        
        const isImg = name.match(/\.(jpg|jpeg|png|gif|webp)$/i);
        let preview = `<span class="material-icons-outlined text-slate-400 mr-2">description</span>`;
        let clickAction = `window.open('${path}', '_blank')`;
        
        if(isImg) {
            preview = `<img src="${path}" class="w-6 h-6 object-cover rounded mr-2 border border-slate-100">`;
            clickAction = `openLightbox('${path}', '${name}')`;
        }
        
        div.innerHTML = `
            <div class="flex items-center cursor-pointer truncate" onclick="${clickAction}">
                ${preview}
                <span class="truncate" title="${name}">${name}</span>
            </div>
            <button type="button" onclick="this.parentElement.remove()" class="text-red-400 hover:text-red-600 ml-2">
                <span class="material-icons-outlined text-base">close</span>
            </button>
        `;
        listElement.appendChild(div);
    }
    
    async function saveDataToServer() {
      console.log("%c DEBUG: saveDataToServer gestartet...", "color: orange; font-weight: bold;");

      const payload = {
          inventory: hardwareInventory,
          boms: kitBoms,
          mappings: engineMappings,
          trolleys: trolleys,
          storage_units: storageUnits // WICHTIG
      };

      console.log("DEBUG: Sende folgenden Payload an Server:", payload);

      try {
          const response = await fetch('/api/test_equipment/save', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body: JSON.stringify(payload)
          });

          console.log("DEBUG: Server HTTP Status:", response.status);

          if (!response.ok) {
              const errorText = await response.text();
              console.error("DEBUG: Server antwortete mit Fehler:", errorText);
              throw new Error('Server Fehler: ' + response.status);
          }

          const result = await response.json();
          console.log("DEBUG: Server Antwort JSON:", result);

      } catch (error) {
          console.error('DEBUG: JS Fehler beim Fetch:', error);
          alert('Speichern fehlgeschlagen! Bitte Konsole (F12) prüfen.');
      }
  }

  // --- INIT ---
  document.addEventListener('DOMContentLoaded', () => {
      const sidebar = document.getElementById('sidebar');
      const sidebarToggleBtn = document.getElementById('sidebar-toggle');
      if (sidebar && sidebarToggleBtn) {
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
            const applyState = (isCollapsed) => {
                if (isCollapsed) {
                    sidebar.classList.add('is-collapsed');
                    toggleBtnIcon.textContent = 'menu';
                } else {
                    sidebar.classList.remove('is-collapsed');
                    toggleBtnIcon.textContent = 'chevron_left';
                }
            };
            sidebarToggleBtn.addEventListener('click', () => {
                const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
                localStorage.setItem('sidebarCollapsed', isNowCollapsed);
                applyState(isNowCollapsed);
            });
            const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
            applyState(savedState);
      }

      renderMappingTable();
      renderTrolleyTable();
      renderStorageTable(); 
      updateAndReassignKits();
      initHardwareFilters();
  });

  // --- GLOBALS ---
  const bomModal = document.getElementById('bomModal');
  const newMappingModal = document.getElementById('newMappingModal');
  const editMappingModal = document.getElementById('editMappingModal');
  const partNumberModal = document.getElementById('partNumberModal');
  const trolleyModal = document.getElementById('trolleyModal');
  const deletePartBtn = document.getElementById('deletePartBtn');
  const deleteTrolleyBtn = document.getElementById('deleteTrolleyBtn');
  const deleteMappingBtn = document.getElementById('deleteMappingBtn');
  const expandAllBtn = document.getElementById('expandAllBtn');

  let areAllRowsExpanded = false;

  // HILFSFUNKTIONEN
  function openModal(m) { m.classList.remove('hidden'); document.body.classList.add('modal-active'); }
  function closeModal(m) { m.classList.add('hidden'); document.body.classList.remove('modal-active'); }
  function addKitTag(name, container) { 
      if(name){ 
          const t = document.createElement('div');
          t.className = 'new-kit-tag flex items-center bg-white border border-slate-300 text-slate-700 text-xs font-medium mr-2 mb-2 pl-3 pr-2 py-1.5 rounded-full shadow-sm';
          t.dataset.kitName = name;
          t.innerHTML = `<span>${name}</span><button type="button" class="new-kit-tag-remove ml-2 text-slate-400 hover:text-red-500 flex items-center justify-center rounded-full p-0.5 transition-colors"><span class="material-icons-outlined text-sm">close</span></button>`;
          t.querySelector('button').onclick = () => t.remove();
          container.appendChild(t);
          return true;
      } 
      return false; 
  }

  // --- TAG INPUT LOGIK FÜR PRODUCT TYPE ---
  let currentProductTags = [];

  function renderProductTags() {
      const container = document.getElementById('pnModal_productTags');
      const input = document.getElementById('pnModal_productInput');

      // Entferne alle bisherigen Chips
      const chips = container.querySelectorAll('.tag-chip');
      chips.forEach(chip => chip.remove());

      currentProductTags.forEach(tag => {
          const chip = document.createElement('span');
          chip.className = 'tag-chip';
          chip.innerHTML = `${tag}<span class="tag-chip-remove" onclick="removeProductTag('${tag}')">×</span>`;
          container.insertBefore(chip, input);
      });

      input.value = '';
  }

  function addProductTag(tag) {
      if (tag && !currentProductTags.includes(tag)) {
          currentProductTags.push(tag);
          renderProductTags();
      }
      document.getElementById('pnModal_productDropdown').classList.add('hidden');
  }

  window.removeProductTag = function(tag) { 
      currentProductTags = currentProductTags.filter(t => t !== tag);
      renderProductTags();
  }

  function setupProductTagInput() {
      const input = document.getElementById('pnModal_productInput');
      const dropdown = document.getElementById('pnModal_productDropdown');

      const availableTypes = new Set(['N/A', 'Facility']);
      document.querySelectorAll('#engineKitMappingTable .engine-type-cell').forEach(cell => {
          availableTypes.add(cell.textContent.trim());
      });

      // Klonen um Listener-Stacking zu vermeiden
      const newInput = input.cloneNode(true);
      input.parentNode.replaceChild(newInput, input);

      newInput.addEventListener('input', () => {
          const val = newInput.value.toLowerCase();
          dropdown.innerHTML = '';

          const filtered = Array.from(availableTypes).filter(t => 
              t.toLowerCase().includes(val) && !currentProductTags.includes(t)
          );

          if (filtered.length > 0) {
              filtered.forEach(t => {
                  const div = document.createElement('div');
                  div.className = 'tag-dropdown-item';
                  div.textContent = t;
                  div.onclick = () => addProductTag(t);
                  dropdown.appendChild(div);
              });
              dropdown.classList.remove('hidden');
          } else {
              dropdown.classList.add('hidden');
          }
      });

      document.addEventListener('click', (e) => {
          if (!newInput.contains(e.target) && !dropdown.contains(e.target)) {
              dropdown.classList.add('hidden');
          }
      });

      newInput.addEventListener('keydown', (e) => {
          if (e.key === 'Enter') {
              e.preventDefault();
              addProductTag(newInput.value.trim());
          }
      });
  }

  function openStandardTrolleyModal(name) {
      currentEditType = 'trolley';
      openTrolleyModal(name);
  }

  function openStorageModal(name) {
      currentEditType = 'storage';
      openTrolleyModal(name);
  }
  function renderStorageTable() {
      const tbody = document.getElementById('storageTableBody');
      tbody.innerHTML = '';

      if (Object.keys(storageUnits).length === 0) {
           tbody.innerHTML = '<tr><td colspan="3" class="px-4 py-8 text-center text-slate-400 text-sm italic">Keine Lagerorte definiert.</td></tr>';
           return;
      }

      for (const name in storageUnits) {
          const data = storageUnits[name];
          const tr = document.createElement('tr');
          tr.className = 'hover:bg-slate-50 border-b border-slate-100 last:border-0';

          tr.innerHTML = `
            <td class="px-4 py-3 font-medium text-slate-900">${data.engineType || '-'}</td>
            <td class="px-4 py-3"><span class="cursor-pointer bg-indigo-50 border border-indigo-200 text-indigo-700 px-3 py-1 rounded-full text-xs font-semibold" onclick="openStorageModal('${name}')">${name}</span></td>
            <td class="px-4 py-3 text-right">
                <button class="text-slate-400 hover:text-green-600 p-1 transition-colors" onclick="downloadTrolleyBOM('${name}')" title="Inhalt exportieren">
                    <span class="material-icons-outlined text-lg">download</span>
                </button>
            </td>
          `;
          tbody.appendChild(tr);
      }
  }

  // --- EXPAND ALL ---
  function toggleAllRows() {
      areAllRowsExpanded = !areAllRowsExpanded;
      const icon = expandAllBtn.querySelector('.material-icons-outlined');
      icon.textContent = areAllRowsExpanded ? 'unfold_less' : 'unfold_more';

      document.querySelectorAll('.toggle-sn-rows').forEach(btn => {
          if(areAllRowsExpanded) btn.classList.add('is-open'); else btn.classList.remove('is-open');
      });

      document.querySelectorAll('.sn-row').forEach(row => {
          const parentPn = row.dataset.parentPn;
          const parentRow = document.querySelector(`tr.pn-row[data-pn="${parentPn}"]`);
          if (parentRow && parentRow.style.display !== 'none') {
              if (areAllRowsExpanded) row.classList.add('is-visible'); else row.classList.remove('is-visible');
          }
      });
  }

  // --- RENDER MAPPINGS ---
    function renderMappingTable() {
        const tbody = document.querySelector('#engineKitMappingTable tbody');
        tbody.innerHTML = '';
    
        for (const engine in engineMappings) {
            const kitIds = engineMappings[engine]; // Das sind jetzt UUIDs (meistens)
            const tr = document.createElement('tr');
            tr.className = 'border-b hover:bg-slate-50 last:border-0';
            
            let kitsHtml = '';
            kitIds.forEach(kitId => {
                const bom = kitBoms[kitId];
                const kitName = bom ? bom.name : kitId; // Name auflösen
                
                let statusClass = 'bg-kit-missing';
                // Status kommt jetzt direkt vom Backend-Objekt
                if (bom && bom.status === 'ready') statusClass = 'bg-kit-ready';
                if (bom && bom.status === 'partial') statusClass = 'bg-kit-partial';
                
                // WICHTIG: Wir übergeben die ID im onclick und speichern sie im Dataset
                kitsHtml += `<span class="kit-tag" data-kit-id="${kitId}" onclick="openBomModal('${kitId}')">
                                <span class="kit-status-dot ${statusClass}"></span>${kitName}
                             </span>`;
            });
            
            tr.innerHTML = `
                <td class="px-4 py-3 font-medium engine-type-cell">${engine}</td>
                <td class="px-4 py-3 kit-cell">${kitsHtml}</td>
                <td class="px-4 py-3 text-right">
                    <button class="text-slate-400 hover:text-blue-600 edit-mapping-btn">
                        <span class="material-icons-outlined text-lg">edit</span>
                    </button>
                </td>`;
            tbody.appendChild(tr);
        }
    }

  // --- TROLLEY LOGIK ---
  function renderTrolleyTable() {
      const tbody = document.querySelector('#trolleyTableBody');
      tbody.innerHTML = '';

      if (Object.keys(trolleys).length === 0) {
           tbody.innerHTML = '<tr><td colspan="3" class="px-4 py-8 text-center text-slate-400 text-sm italic">Keine Rüstwagen definiert.</td></tr>';
           return;
      }

      for (const trolleyName in trolleys) {
          const data = trolleys[trolleyName];
          const tr = document.createElement('tr');
          tr.className = 'hover:bg-slate-50 border-b border-slate-100 last:border-0';

          tr.innerHTML = `
            <td class="px-4 py-3 font-medium text-slate-900">${data.engineType}</td>
            <td class="px-4 py-3"><span class="trolley-tag cursor-pointer bg-orange-50 border-orange-200 text-orange-700 px-3 py-1 rounded-full text-xs font-semibold" onclick="openStandardTrolleyModal('${trolleyName}')">${trolleyName}</span></td>
            <td class="px-4 py-3 text-right">
                <button class="text-slate-400 hover:text-green-600 p-1 transition-colors" onclick="downloadTrolleyBOM('${trolleyName}')" title="BOM Downloaden">
                    <span class="material-icons-outlined text-lg">download</span>
                </button>
            </td>
          `;
          tbody.appendChild(tr);
      }
  }

  
  function toggleTrolleyEditMode(enable) {
      isTrolleyEditMode = enable;
      
      // Header & Buttons umschalten
      document.getElementById('btnEditTrolley').style.display = enable ? 'none' : 'block';
      document.getElementById('trolleyEditButtons').classList.toggle('hidden', !enable);
      document.getElementById('trolleyViewButtons').classList.toggle('hidden', enable);
      
      // Delete Button nur wenn wir ein bestehendes Item bearbeiten (name ist gesetzt)
      const isNew = !document.getElementById('trolleyModalOriginalName').value;
      document.getElementById('deleteTrolleyBtn').classList.toggle('hidden', !enable || isNew);
      
      document.getElementById('trolleyModalSubtitle').textContent = enable ? 'Bearbeiten' : 'Details anzeigen';
      document.getElementById('btnAddTrolleyItem').classList.toggle('hidden', !enable);
      document.getElementById('btnAddTrolleyImage').classList.toggle('hidden', !enable);

      // Inputs umschalten
      const inputs = document.querySelectorAll('#trolleyForm input');
      inputs.forEach(inp => {
          inp.readOnly = !enable;
          if (enable) {
              inp.classList.remove('bg-slate-50', 'text-slate-600', 'border-transparent');
              inp.classList.add('bg-white', 'border-slate-300');
          } else {
              inp.classList.add('bg-slate-50', 'text-slate-600', 'border-transparent');
              inp.classList.remove('bg-white', 'border-slate-300');
          }
      });
      
      // Selects umschalten
      const selects = document.querySelectorAll('#trolleyItemsContainer select');
      selects.forEach(sel => {
          sel.disabled = !enable;
          sel.classList.toggle('bg-slate-50', !enable);
          sel.classList.toggle('appearance-none', !enable); // Pfeil verstecken im View Mode
      });

      // Löschen-Buttons in den Zeilen umschalten
      document.querySelectorAll('.trolley-item-delete-btn').forEach(btn => {
          btn.style.visibility = enable ? 'visible' : 'hidden';
      });
      document.querySelectorAll('.trolley-img-delete-btn').forEach(btn => {
          btn.style.visibility = enable ? 'visible' : 'hidden';
      });
  }

function openTrolleyModal(name) {
      const form = document.getElementById('trolleyForm'); form.reset();
      document.getElementById('trolleyItemsContainer').innerHTML = '';
      document.getElementById('trolleyImagesList').innerHTML = '';
      const nameInput = document.getElementById('trolleyModalName');

      // Bestimmen der Quelle und Texte basierend auf currentEditType
      const dataSource = (currentEditType === 'storage') ? storageUnits : trolleys;
      const titlePrefix = (currentEditType === 'storage') ? 'Lagerort' : 'Rüstwagen';
      
      // Label dynamisch anpassen
      const engineLabel = document.querySelector('label[for="trolleyModalEngine"]');
      if(engineLabel) engineLabel.textContent = (currentEditType === 'storage') ? 'Bereich / Zone' : 'Engine Type';

      // Populate responsible persons datalist
      const dl = document.getElementById('responsiblePersonsList');
      dl.innerHTML = allEmployeeNames.map(n => `<option value="${n}">`).join('');

      if (name && dataSource[name]) {
          document.getElementById('trolleyModalTitle').textContent = name;
          document.getElementById('trolleyModalSubtitle').textContent = 'Details anzeigen';
          document.getElementById('trolleyModalOriginalName').value = name;
          nameInput.value = name;
          document.getElementById('trolleyModalEngine').value = dataSource[name].engineType || '';
          document.getElementById('trolleyModalResponsible').value = dataSource[name].responsible || '';

          const imagesList = document.getElementById('trolleyImagesList');
          (dataSource[name].images || []).forEach(img => addTrolleyImageItem(imagesList, img.path, img.name, img.stored_name));
          document.getElementById('trolleyImagesEmpty').style.display = (dataSource[name].images || []).length ? 'none' : 'block';

          const items = [...(dataSource[name].items || [])];
          
          if (items.length === 0) {
              document.getElementById('trolleyEmptyState').classList.remove('hidden');
          } else {
              document.getElementById('trolleyEmptyState').classList.add('hidden');
              
              // SORTIERUNG: Ebene -> Box
              items.sort((a, b) => {
                  const levelA = String(a.level || '');
                  const levelB = String(b.level || '');
                  const levelCompare = levelA.localeCompare(levelB, undefined, { numeric: true, sensitivity: 'base' });
                  if (levelCompare !== 0) return levelCompare;
                  const boxA = String(a.box || '');
                  const boxB = String(b.box || '');
                  return boxA.localeCompare(boxB, undefined, { numeric: true, sensitivity: 'base' });
              });

              items.forEach(item => addTrolleyItemRow(item));
          }

          // Löschen-Button konfigurieren (Klonen um alte Listener zu entfernen)
          const delBtn = document.getElementById('deleteTrolleyBtn');
          const newDelBtn = delBtn.cloneNode(true);
          delBtn.parentNode.replaceChild(newDelBtn, delBtn);
          newDelBtn.onclick = () => deleteTrolley(name); // Nutzt deleteTrolley als generische Löschfunktion (siehe unten)

          toggleTrolleyEditMode(false); // Start im View-Modus
      } else {
          document.getElementById('trolleyModalTitle').textContent = `Neuer ${titlePrefix}`;
          document.getElementById('trolleyModalSubtitle').textContent = 'Erstellen';
          document.getElementById('trolleyModalOriginalName').value = '';
          document.getElementById('trolleyModalResponsible').value = '';
          document.getElementById('trolleyEmptyState').classList.add('hidden');
          addTrolleyItemRow(); 
          toggleTrolleyEditMode(true); // Start im Edit-Modus
      }
      openModal(trolleyModal);
  }

  function closeTrolleyModal() { closeModal(trolleyModal); }

  async function uploadTrolleyImage(inputEl) {
      const file = inputEl.files[0];
      if (!file) return;
      const formData = new FormData();
      formData.append('file', file);
      try {
          const res = await fetch('/api/hardware/upload', { method: 'POST', body: formData });
          if (!res.ok) throw new Error();
          const data = await res.json();
          const list = document.getElementById('trolleyImagesList');
          addTrolleyImageItem(list, data.path, data.filename, data.stored_name);
          document.getElementById('trolleyImagesEmpty').style.display = 'none';
          inputEl.value = '';
      } catch(e) {
          alert('Upload fehlgeschlagen.');
      }
  }

  function addTrolleyImageItem(listEl, path, name, storedName) {
      const div = document.createElement('div');
      div.className = 'trolley-img-item flex items-center justify-between p-2 bg-white border border-slate-200 rounded text-sm';
      div.dataset.path = path;
      div.dataset.name = name;
      div.dataset.stored = storedName;
      div.innerHTML = `
          <div class="flex items-center gap-2 cursor-pointer truncate" onclick="openLightbox('${path}', '${name}')">
              <img src="${path}" class="w-8 h-8 object-cover rounded border border-slate-100 flex-shrink-0">
              <span class="truncate text-slate-700" title="${name}">${name}</span>
          </div>
          <button type="button" class="trolley-img-delete-btn text-red-400 hover:text-red-600 ml-2 flex-shrink-0" onclick="this.closest('.trolley-img-item').remove(); document.getElementById('trolleyImagesEmpty').style.display = document.querySelectorAll('#trolleyImagesList .trolley-img-item').length ? 'none' : 'block';">
              <span class="material-icons-outlined text-base">close</span>
          </button>
      `;
      // Hide delete button in view mode
      if (!isTrolleyEditMode) div.querySelector('.trolley-img-delete-btn').style.visibility = 'hidden';
      listEl.appendChild(div);
  }

  function addTrolleyItem() { addTrolleyItemRow(null, true); }

  function addTrolleyItemRow(data = null, prepend = false) {
      document.getElementById('trolleyEmptyState').classList.add('hidden');
      const container = document.getElementById('trolleyItemsContainer');
      const div = document.createElement('div');
      
      const baseClass = 'trolley-item-row grid grid-cols-12 gap-2 items-center p-2 rounded transition-colors'; 
      div.className = isTrolleyEditMode ? `${baseClass} border border-slate-100 hover:bg-slate-50` : baseClass;

      const id = data ? data.id : null; 
      const pn = data ? data.pn : '';
      const sn = data ? data.sn : ''; 
      const name = data ? data.name : ''; 
      const level = data ? data.level : '';
      const box = data && data.box ? data.box : '';
      const qty = data && data.qty ? data.qty : 1;
      
      const isLinked = !!id;
      const isLocked = isLinked && isTrolleyEditMode; 
      const lockClass = isLocked ? 'bg-slate-100 text-slate-500 cursor-not-allowed' : 'bg-white';

      const uniqueIdPN = 'list_pn_' + Math.random().toString(36).substr(2, 9);
      const uniqueIdSN = 'list_sn_' + Math.random().toString(36).substr(2, 9);

      let pnOptions = '';
      Object.keys(hardwareInventory).sort().forEach(p => { 
          const pName = hardwareInventory[p].name || '';
          pnOptions += `<option value="${p}">${p} - ${pName}</option>`; 
      });

      div.dataset.itemId = id || ''; 

      // Icon Logik für Freitext
      let nameFieldHtml = `
            <input type="text" class="w-full p-1.5 border border-slate-300 rounded text-sm trolley-name-input ${lockClass}" 
                   value="${name}" placeholder="Name / Beschreibung" ${isLocked ? 'readonly' : ''}>`;

      if (!isLinked) {
          // Freitext: Icon hinzufügen
          nameFieldHtml = `
            <div class="relative w-full">
                <input type="text" class="w-full p-1.5 pr-7 border border-slate-300 rounded text-sm trolley-name-input ${lockClass}" 
                       value="${name}" placeholder="Name / Beschreibung" ${isLocked ? 'readonly' : ''}>
                <span class="material-icons-outlined absolute right-1.5 top-1.5 text-slate-400 text-base pointer-events-none" title="Freitext / Manuell">edit_note</span>
            </div>`;
      }

      div.innerHTML = `
        <!-- P/N (2 Spalten) -->
        <div class="col-span-2 relative">
            <input type="text" list="${uniqueIdPN}" class="w-full p-1.5 border border-slate-300 rounded text-sm trolley-pn-input ${lockClass}" 
                   value="${pn}" placeholder="P/N..." onchange="updateSnOptions(this)" ${isLocked ? 'readonly' : ''}>
            <datalist id="${uniqueIdPN}">${pnOptions}</datalist>
        </div>

        <!-- S/N (2 Spalten) -->
        <div class="col-span-2 relative">
            <input type="text" list="${uniqueIdSN}" class="w-full p-1.5 border border-slate-300 rounded text-sm trolley-sn-input ${lockClass}" 
                   value="${sn}" placeholder="S/N..." onchange="onSnSelected(this)" ${!pn || isLocked ? 'disabled' : ''}>
            <datalist id="${uniqueIdSN}"></datalist>
        </div>

        <!-- Name (3 Spalten) - MIT ICON -->
        <div class="col-span-3">
            ${nameFieldHtml}
        </div>

        <!-- Menge (1 Spalte) -->
        <div class="col-span-1">
             <input type="number" min="1" class="w-full p-1.5 border border-slate-300 rounded text-sm text-center trolley-qty-input ${lockClass}" 
                    value="${qty}" placeholder="#" ${isLocked ? 'readonly' : ''}>
        </div>

        <!-- Position (3 Spalten) -->
        <div class="col-span-3 flex gap-1">
            <input type="text" class="w-1/2 p-1.5 border border-slate-300 rounded text-sm trolley-level-input" placeholder="Lvl" value="${level}" ${!isTrolleyEditMode ? 'readonly class="bg-slate-50 border-transparent"' : ''}>
            <input type="text" class="w-1/2 p-1.5 border border-slate-300 rounded text-sm trolley-box-input" placeholder="Box" value="${box}" ${!isTrolleyEditMode ? 'readonly class="bg-slate-50 border-transparent"' : ''}>
        </div>

        <!-- Delete -->
        <div class="col-span-1 flex justify-end">
            <button type="button" class="trolley-item-delete-btn text-red-400 hover:text-red-600 p-1 rounded-full hover:bg-red-50 transition-colors" onclick="this.closest('.trolley-item-row').remove()">
                <span class="material-icons-outlined text-lg">delete</span>
            </button>
        </div>
      `;
      if (prepend && container.firstChild) {
            container.insertBefore(div, container.firstChild);
            // Optional: Fokus auf das erste Eingabefeld setzen
            if (isTrolleyEditMode) {
                 const firstInput = div.querySelector('input:not([readonly]):not([disabled])');
                 if (firstInput) setTimeout(() => firstInput.focus(), 50);
            }
        } else {
            container.appendChild(div);
        }

      if (pn && !isLocked) {
          const snInput = div.querySelector('.trolley-sn-input');
          snInput.disabled = false;
          updateSnDropdownContent(snInput, pn);
      }
      
      if (!isTrolleyEditMode) {
          const inputs = div.querySelectorAll('input');
          inputs.forEach(i => { 
              i.readOnly = true; 
              // Spezielle Behandlung für das Icon-Feld: Nicht den Wrapper, sondern das Input stylen
              if (!i.parentElement.classList.contains('relative')) {
                  i.classList.add('bg-slate-50', 'border-transparent', 'text-slate-600'); 
                  i.classList.remove('bg-white', 'border-slate-300', 'bg-slate-100');
              } else {
                  // Wenn es im Wrapper ist (unser Name-Feld)
                  i.classList.add('bg-slate-50', 'border-transparent', 'text-slate-600'); 
                  i.classList.remove('bg-white', 'border-slate-300', 'bg-slate-100');
              }
          });
          div.querySelector('.trolley-item-delete-btn').style.visibility = 'hidden';
      }
  }
    function onSnSelected(input) {
        const sn = input.value.trim();
        const row = input.closest('.trolley-item-row');
        const pn = row.querySelector('.trolley-pn-input').value;
        
        // Suche im Inventar nach diesem Item
        if (hardwareInventory[pn]) {
            const serialEntry = hardwareInventory[pn].serials.find(s => s.sn === sn);
            
            if (serialEntry && serialEntry.id) {
                // TREFFER: Verknüpftes Item!
                row.dataset.itemId = serialEntry.id;
                
                // Name automatisch setzen
                const nameInput = row.querySelector('.trolley-name-input');
                nameInput.value = hardwareInventory[pn].name || '';
                
                // Felder sperren (Visuelles Feedback)
                const fieldsToLock = [input, row.querySelector('.trolley-pn-input'), nameInput, row.querySelector('.trolley-qty-input')];
                fieldsToLock.forEach(f => {
                    f.readOnly = true;
                    f.classList.add('bg-slate-100', 'text-slate-500', 'cursor-not-allowed');
                    f.classList.remove('bg-white');
                });
                
                // Menge auf 1 setzen
                row.querySelector('.trolley-qty-input').value = 1;
            }
        }
    }
function updateSnOptions(pnInput) {
      const row = pnInput.closest('.trolley-item-row');
      const snInput = row.querySelector('.trolley-sn-input');
      const pn = pnInput.value.trim();

      if (!pn) {
          snInput.value = '';
          snInput.disabled = true; // Deaktivieren wenn P/N leer
          return;
      }
      snInput.disabled = false; 
      
      if (hardwareInventory[pn]) {
          updateSnDropdownContent(snInput, pn);
      } else {
          const datalistId = snInput.getAttribute('list');
          document.getElementById(datalistId).innerHTML = '';
      }
  }

  function updateSnDropdownContent(snInput, pn) {
      const datalistId = snInput.getAttribute('list');
      const datalist = document.getElementById(datalistId);
      datalist.innerHTML = ''; // Reset

      if (!pn || !hardwareInventory[pn]) return;

      const partData = hardwareInventory[pn];
      partData.serials.forEach(s => {
          // Zeige alle verfügbaren SNs an (außer Decommissioned)
          if (s.status !== 'Decommissioned') {
               let label = s.location ? `${s.location}` : 'Lager';
               const opt = document.createElement('option');
               opt.value = s.sn;
               opt.textContent = `Standort: ${label}`; // Wird im Browser als Zusatzinfo angezeigt
               datalist.appendChild(opt);
          }
      });
  }

  function assignInventoryToTrolley(trolleyName, items) {
      Object.values(hardwareInventory).forEach(part => {
          part.serials.forEach(s => { if (s.location === trolleyName) s.location = ""; });
      });

      items.forEach(item => {
          const partData = hardwareInventory[item.pn];
          if (!partData) return;
          const specificSerial = partData.serials.find(s => s.sn === item.sn);
          if (specificSerial) specificSerial.location = trolleyName;
      });
  }

  // Diese Funktion löscht sowohl Rüstwagen als auch Lagerorte
  function deleteTrolley(name) {
      const label = (currentEditType === 'storage') ? 'Lagerort' : 'Rüstwagen';
      if(confirm(`${label} "${name}" wirklich löschen?`)) {
          // Standort aus Inventar entfernen
          Object.values(hardwareInventory).forEach(part => {
              part.serials.forEach(s => {
                  if (s.location === name) s.location = "";
              });
          });

          // Aus entsprechender DB löschen
          if (currentEditType === 'storage') {
              delete storageUnits[name];
          } else {
              delete trolleys[name];
          }

          saveDataToServer();
          renderTrolleyTable();
          renderStorageTable();
          renderHardwareTable();
          closeTrolleyModal();
      }
  }

document.getElementById('trolleyForm').addEventListener('submit', async e => {
      e.preventDefault();
      const oldName = document.getElementById('trolleyModalOriginalName').value;
      const newName = document.getElementById('trolleyModalName').value.trim();
      const engine = document.getElementById('trolleyModalEngine').value.trim();

      if(!newName) return;

      // --- NEU: UMBENENNUNG LOGIK ---
      if (oldName && oldName !== newName) {
          // Wir führen erst die Umbenennung durch
          const type = (currentEditType === 'storage') ? 'storage_units' : 'trolleys';
          try {
              const res = await fetch('/api/test_equipment/rename_storage', {
                  method: 'POST',
                  headers: {'Content-Type': 'application/json'},
                  body: JSON.stringify({ type, oldName, newName })
              });
              const result = await res.json();
              if(!res.ok) { alert(result.error); return; }
              
              // Erfolg! Wir müssen unsere lokalen Daten aktualisieren, 
              // damit der nachfolgende normale Save-Prozess nicht mit alten Namen arbeitet.
              if (currentEditType === 'storage') {
                  storageUnits[newName] = storageUnits[oldName];
                  delete storageUnits[oldName];
              } else {
                  trolleys[newName] = trolleys[oldName];
                  delete trolleys[oldName];
              }
              // Wichtig: OriginalName im Formular updaten, damit wir nicht nochmal umbenennen
              document.getElementById('trolleyModalOriginalName').value = newName;
              
              console.log(result.message);
          } catch(err) {
              alert("Fehler beim Umbenennen.");
              return;
          }
      }
      // -------------------------------

      const targetDB = (currentEditType === 'storage') ? storageUnits : trolleys;
      const items = [];
      document.querySelectorAll('.trolley-item-row').forEach(row => {
          // ... (Items einsammeln wie gehabt) ...
          // ... (Kopiere den bestehenden Code hier rein) ...
          // Beispiel (kurzfassung):
          const id = row.dataset.itemId || null;
          const pn = row.querySelector('.trolley-pn-input').value.trim();
          const sn = row.querySelector('.trolley-sn-input').value.trim(); 
          const name = row.querySelector('.trolley-name-input').value.trim();
          const level = row.querySelector('.trolley-level-input').value.trim();
          const box = row.querySelector('.trolley-box-input').value.trim();
          const qty = parseInt(row.querySelector('.trolley-qty-input').value || 1);
          if(pn || name) items.push({ id, name, pn, sn, level, box, qty });
      });

      // Update das Objekt (jetzt unter dem (neuen) Namen)
      const responsible = document.getElementById('trolleyModalResponsible').value.trim();
      const images = [];
      document.querySelectorAll('#trolleyImagesList .trolley-img-item').forEach(div => {
          images.push({ path: div.dataset.path, name: div.dataset.name, stored_name: div.dataset.stored });
      });
      targetDB[newName] = { engineType: engine, responsible: responsible, images: images, items: items };

      // Normales Speichern ausführen (für die Item-Änderungen)
      assignInventoryToTrolley(newName, items); // Inventory Update lokal
      saveDataToServer(); // Globaler Save

      renderTrolleyTable();
      renderStorageTable();
      renderHardwareTable(); 
      closeTrolleyModal();
  });

  function downloadTrolleyBOM(name) {
      // Prüfen, wo wir suchen müssen (Trolley oder Storage?)
      // Da der Name eindeutig sein sollte, suchen wir einfach in beiden oder nutzen currentEditType falls gesetzt
      let data = trolleys[name];
      let typeLabel = "RUESTWAGEN";
      
      if (!data && storageUnits[name]) {
          data = storageUnits[name];
          typeLabel = "LAGERORT";
      }
      
      if (!data) return;

      let content = `BOM - ${typeLabel}: ${name}\n`;
      content += `BEREICH / ENGINE: ${data.engineType}\n`;
      content += `DATUM: ${new Date().toLocaleDateString()}\n`;
      content += `----------------------------------------------------------------------------------------------------\n`;
      content += `LEVEL | BOX        | QTY  | PART NUMBER      | SERIAL NUMBER    | DESCRIPTION\n`;
      content += `----------------------------------------------------------------------------------------------------\n`;

      // Sortierung anwenden (selbe Logik wie im Modal)
      const items = [...(data.items || [])].sort((a, b) => {
          const levelA = String(a.level || '');
          const levelB = String(b.level || '');
          const levelCompare = levelA.localeCompare(levelB, undefined, { numeric: true, sensitivity: 'base' });
          if (levelCompare !== 0) return levelCompare;
          const boxA = String(a.box || '');
          const boxB = String(b.box || '');
          return boxA.localeCompare(boxB, undefined, { numeric: true, sensitivity: 'base' });
      });

      items.forEach(item => {
          const part = hardwareInventory[item.pn];
          const desc = part ? (part.name || "Unknown") : "Unknown";
          
          // Formatierung mit padEnd für saubere Spalten
          const levelStr = String(item.level || '').padEnd(5, ' ');
          const boxStr = String(item.box || '').padEnd(10, ' '); // 10 Zeichen für Box
          const qtyStr = String(item.qty || 1).padEnd(4, ' ');   // 4 Zeichen für Menge
          const pnStr = String(item.pn).padEnd(16, ' ');
          const snStr = String(item.sn || "---").padEnd(16, ' ');
          // Beschreibung darf den Rest nehmen, aber wir kürzen sie bei Bedarf
          const descStr = desc.substring(0, 40); 

          content += `${levelStr} | ${boxStr} | ${qtyStr} | ${pnStr} | ${snStr} | ${descStr}\n`;
      });
      
      content += `----------------------------------------------------------------------------------------------------\n`;
      content += `GESAMT POSITIONEN: ${items.length}\n`;

      const blob = new Blob([content], { type: 'text/plain' });
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement('a'); 
      a.href = url; 
      a.download = `BOM_${name}.txt`; 
      document.body.appendChild(a); 
      a.click(); 
      document.body.removeChild(a); 
      window.URL.revokeObjectURL(url);
  }
    function downloadFilteredInventory() {
        let content = `HARDWARE INVENTORY EXPORT\n`;
        content += `DATUM: ${new Date().toLocaleDateString()} ${new Date().toLocaleTimeString()}\n`;
        
        const typeVal = document.getElementById('filterHwType').value;
        const prodVal = document.getElementById('filterHwProduct').value;
        const statusVal = document.getElementById('filterHwStatus').value;
        const locVal = document.getElementById('filterHwLocation').value;
        const term = document.getElementById('hardwareSearchInput').value;
    
        content += `FILTER: Type=${typeVal}, Prod=${prodVal}, Status=${statusVal}, Loc=${locVal}, Search="${term}"\n`;
        content += `----------------------------------------------------------------------------------------------------\n`;
        content += `P/N              | NAME                                     | QTY  | SERIAL NUMBERS [LOCATION]\n`;
        content += `----------------------------------------------------------------------------------------------------\n`;
    
        let pnCount = 0;
        let snCount = 0; // Neuer Zähler für S/N
    
        const rows = document.querySelectorAll('#hardwareInventoryTbody tr.pn-row');
        
        rows.forEach(row => {
            if (row.style.display === 'none') return;
    
            const pn = row.dataset.pn;
            const part = hardwareInventory[pn];
            if (!part) return;
    
            let relevantSerials = part.serials;
            if (statusVal !== 'all') relevantSerials = relevantSerials.filter(s => s.status === statusVal);
            if (locVal !== 'all') relevantSerials = relevantSerials.filter(s => s.location === locVal);
    
            if (relevantSerials.length === 0) return;
    
            pnCount++;
            snCount += relevantSerials.length; // Addiere S/N
    
            const pnStr = pn.padEnd(16, ' ');
            const nameStr = (part.name || 'Unbekannt').substring(0, 40).padEnd(40, ' ');
            const qtyStr = String(relevantSerials.length).padEnd(4, ' ');
            
            const details = relevantSerials.map(s => {
                if (s.location && s.location.trim()) {
                    return `${s.sn} [${s.location}]`;
                }
                return s.sn;
            }).join('   |   ');
            
            content += `${pnStr} | ${nameStr} | ${qtyStr} | ${details}\n`;
        });
    
        content += `----------------------------------------------------------------------------------------------------\n`;
        // Erweiterte Statistik
        content += `GESAMT: ${pnCount} P/N  (${snCount} S/N)\n`;
    
        const blob = new Blob([content], { type: 'text/plain' });
        const url = window.URL.createObjectURL(blob);
        const a = document.createElement('a'); 
        a.href = url; 
        a.download = `Hardware_Export_${new Date().toISOString().slice(0,10)}.txt`; 
        document.body.appendChild(a); 
        a.click(); 
        document.body.removeChild(a); 
        window.URL.revokeObjectURL(url);
    }
// --- INVENTORY ---
  function renderHardwareTable() {
    const tbody = document.getElementById('hardwareInventoryTbody');
    tbody.innerHTML = '';

    // "assigned" Zählvariable entfernt
    let totalSN = 0, maintenance = 0;

    // Sortieren der Keys für konsistente Anzeige
    const sortedPnKeys = Object.keys(hardwareInventory).sort();

    for (const pn of sortedPnKeys) {
        const part = hardwareInventory[pn];
        const qty = part.serials.length;
        totalSN += qty;

        // Status Zählung für die Bubbles
        const counts = part.serials.reduce((a, s) => { a[s.status] = (a[s.status] || 0) + 1; return a; }, {});

        // Zuweisung zu "assigned" entfernt
        maintenance += counts['Maintenance'] || 0;

        // 1. Status Overview (Bubbles) generieren
        let overview = '<div class="flex items-center gap-1">';
        if (Object.keys(counts).length > 0) {
            overview += Object.entries(counts).map(([status, count]) => {
                let bgClass = 'bg-red-500'; // Default (Decommissioned/Unknown)
                if (status === 'Available') bgClass = 'bg-green-500';
                    else if (status === 'Maintenance') bgClass = 'bg-yellow-500';
                    else if (status === 'In Order') bgClass = 'bg-slate-400'; // Grau für Bestellt
                return `<div title="${count} ${status}" class="status-indicator ${bgClass}">${count}</div>`;
            }).join('');
        } else {
            overview += '<span class="text-xs italic text-slate-400">No serials</span>';
        }
        overview += '</div>';

        // 2. Product Type Tags generieren
        let prodTypeHtml = '';
        const types = Array.isArray(part.productType) ? part.productType : [part.productType];
        types.forEach(t => { if(t) prodTypeHtml += `<span class="product-type-tag">${t}</span>`; });

        // --- HAUPTZEILE (PART NUMBER) ---
        const pnRow = document.createElement('tr');
        pnRow.className = 'pn-row border-b border-slate-200';
        pnRow.dataset.pn = pn;

        const isSelected = selectedPartNumbers.has(pn);

        pnRow.innerHTML = `
            <td class="px-4 py-4 text-center border-r border-slate-100">
                <input type="checkbox" class="pn-checkbox h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500" 
                       value="${pn}" ${isSelected ? 'checked' : ''} onclick="event.stopPropagation(); togglePnSelection('${pn}')">
            </td>
            <td class="px-6 py-4">
                <button class="toggle-sn-rows flex items-center gap-2 text-slate-600 hover:text-blue-600 font-mono text-sm font-medium">
                    <span class="material-icons-outlined text-base">chevron_right</span>${pn}
                </button>
            </td>
            <td class="px-6 py-4 font-medium text-slate-800">${part.name}</td>
            <td class="px-6 py-4 text-slate-600">${part.type}</td>
            <td class="px-6 py-4 text-slate-600">${prodTypeHtml}</td>
            <td class="px-6 py-4 text-slate-600 font-mono">${qty}</td>
            <td class="px-6 py-4">${overview}</td>
            <td class="px-6 py-4 text-right">
                <button onclick="openPartNumberModal('${pn}')" class="text-slate-400 hover:text-blue-600 p-1 rounded-full hover:bg-slate-100 transition-colors">
                    <span class="material-icons-outlined text-lg">edit_note</span>
                </button>
            </td>`;
        tbody.appendChild(pnRow);

        // --- UNTERZEILEN (SERIAL NUMBERS) ---
        part.serials.forEach(s => {
            const snRow = document.createElement('tr');
            snRow.className = 'sn-row border-b border-slate-100';
            snRow.dataset.parentPn = pn;
            snRow.dataset.sn = s.sn;

            // Status Badge Farbe
            let badgeClass = 'bg-gray-100 text-gray-800';
            if (s.status === 'Available') badgeClass = 'badge-available';
            if (s.status === 'Maintenance') badgeClass = 'badge-maintenance';
            if (s.status === 'Decommissioned') badgeClass = 'badge-decommissioned';
            if (s.status === 'In Order') badgeClass = 'badge-in-order'; // NEU

            // --- NEUE LOGIK: MAINTENANCE LINKS & ISSUES ---
            let maintHtml = '<span class="text-xs text-slate-400 italic">- Keine -</span>';
            const links = s.maintenanceLinks || []; 
            const statusInfo = s.statusSummary || { light: 'GREEN', openIssues: 0, issueList: [] };

            // Migration / Fallback: Falls noch keine Liste, aber alte ID da ist
            if (links.length === 0 && s.maintenanceId && s.maintenanceId !== 'N/A') {
                links.push({ id: s.maintenanceId, lastCheck: s.lastCheck || 'N/A' });
            }

            // ANZEIGEN, WENN: Es Wartungspläne gibt ODER offene Issues
            if (links.length > 0 || statusInfo.openIssues > 0) {
                
                // --- AMPEL LOGIK ---
                let badgeColor = 'bg-green-100 text-green-700 border-green-200'; // Default Grün
                let icon = 'check_circle';

                if (statusInfo.light === 'RED') {
                    badgeColor = 'bg-red-100 text-red-700 border-red-200';
                    icon = 'error';
                } else if (statusInfo.light === 'YELLOW') {
                    badgeColor = 'bg-yellow-100 text-yellow-800 border-yellow-200';
                    icon = 'warning';
                }

                // Info-Text bauen
                let countLabel = '';
                
                if (links.length > 0) {
                    countLabel = links.length === 1 ? '1 Plan' : `${links.length} Pläne`;
                } else {
                    // Fall: Keine Pläne, aber Issues (z.B. Ad-Hoc)
                    countLabel = 'Ad-Hoc';
                }
                
                if (statusInfo.openIssues > 0) {
                    // Wenn wir Pläne haben, hängen wir die Issues an ("1 Plan (1 Issue)")
                    // Wenn nicht, zeigen wir nur die Issues ("Ad-Hoc (1 Issue)") oder direkt "1 Issue"
                    if (links.length > 0) {
                         countLabel += ` (${statusInfo.openIssues} Issue${statusInfo.openIssues > 1 ? 's' : ''})`;
                    } else {
                         countLabel = `${statusInfo.openIssues} Issue${statusInfo.openIssues > 1 ? 's' : ''}`;
                    }
                }
                // -------------------

                let popoverRows = '';
                if (links.length > 0) {
                    links.forEach(l => {
                        const targetUrl = `/maintenance?openId=${l.id}`;
                        const displayTitle = l.title || l.id;
                        
                        popoverRows += `
                            <div class="flex justify-between items-center p-2 border-b border-slate-100 last:border-0 hover:bg-slate-50">
                                <div class="overflow-hidden mr-2">
                                    <div class="font-bold text-xs text-slate-700 truncate" title="${displayTitle}">${displayTitle}</div>
                                    <div class="text-[10px] text-slate-500 font-mono">${l.id} | Last: ${l.lastCheck}</div>
                                </div>
                                <a href="${targetUrl}" target="_blank" class="text-blue-600 hover:text-blue-800 p-1 bg-white border border-slate-200 rounded hover:bg-blue-50 transition-colors flex-shrink-0" title="Zum Wartungsplan springen">
                                    <span class="material-icons-outlined text-sm align-middle">open_in_new</span>
                                </a>
                            </div>
                        `;
                    });
                } else {
                    popoverRows = '<div class="p-2 text-[10px] text-slate-400 italic">Keine verknüpften Wartungspläne.</div>';
                }

                // --- ISSUE LISTE BAUEN ---
                let issueRows = '';
                if (statusInfo.issueList && statusInfo.issueList.length > 0) {
                    // Header für Issues
                    issueRows += `<div class="text-[10px] font-bold text-red-500 uppercase mb-1 px-2 pt-2 border-t border-slate-100 bg-red-50">Offene Probleme</div>`;

                    statusInfo.issueList.forEach(iss => {
                        const targetUrl = `/maintenance?openId=${iss.id}`;
                        const prioClass = iss.priority === 'Critical' ? 'text-red-700 font-bold' : 'text-slate-700';

                        issueRows += `
                            <div class="flex justify-between items-start p-2 border-b border-red-100 last:border-0 hover:bg-red-50 bg-red-50/30">
                                <div class="overflow-hidden mr-2">
                                    <div class="font-bold text-xs ${prioClass} truncate" title="${iss.title}">${iss.title}</div>
                                    <div class="text-[10px] text-slate-500 font-mono">${iss.id}</div>
                                </div>
                                <a href="${targetUrl}" target="_blank" class="text-red-600 hover:text-red-800 p-1 bg-white border border-red-200 rounded hover:bg-red-100 transition-colors flex-shrink-0" title="Issue öffnen">
                                    <span class="material-icons-outlined text-sm align-middle">report_problem</span>
                                </a>
                            </div>
                        `;
                    });
                }

                // --- HTML ZUSAMMENBAUEN ---
                maintHtml = `
                    <div class="maint-popover-wrapper group">
                        <span class="cursor-help inline-flex items-center px-2 py-0.5 rounded text-xs font-bold border ${badgeColor}">
                            <span class="material-icons-outlined text-[10px] mr-1">${icon}</span>
                            ${countLabel}
                        </span>
                        <div class="maint-popover-content">
                            <!-- Maintenance Plans -->
                            <div class="text-[10px] font-bold text-slate-400 uppercase mb-1 px-2 pt-1 border-b border-slate-100 bg-slate-50 rounded-t">Verknüpfte Wartung</div>
                            <div class="max-h-32 overflow-y-auto custom-scrollbar">
                                ${popoverRows}
                            </div>

                            <!-- Issues (Neu) -->
                            <div class="max-h-32 overflow-y-auto custom-scrollbar">
                                ${issueRows}
                            </div>

                            ${statusInfo.nextDue && statusInfo.nextDue !== 'N/A' ? `<div class="text-[10px] text-slate-500 px-2 py-1 border-t border-slate-100 bg-slate-50 rounded-b">Nächste Fälligkeit: <strong>${new Date(statusInfo.nextDue).toLocaleDateString('de-DE')}</strong></div>` : ''}
                        </div>
                    </div>
                `;
            }

            // Location Anzeige mit Tooltip (Level/Box)
            let locationHtml = `<span class="text-slate-500 ">${s.location || '-'}</span>`;
            
            const containerData = trolleys[s.location] || storageUnits[s.location];
            
            if (containerData) {
                const isTrolley = !!trolleys[s.location];
                const badgeClass = isTrolley 
                    ? 'bg-orange-100 text-orange-800 border-orange-200  ' 
                    : 'bg-indigo-100 text-indigo-800 border-indigo-200';
                const icon = isTrolley ? 'trolley' : 'shelves';
                const itemDetail = (containerData.items || []).find(i => i.pn === pn && i.sn === s.sn);
                
                let popoverContent = '';
                
                if (itemDetail && (itemDetail.level || itemDetail.box)) {
                    popoverContent = `
                        <div class="maint-popover-content" style="width: auto; min-width: 120px; bottom: 110%;">
                            <div class="text-[10px] font-bold text-slate-400 uppercase mb-1 px-2 pt-1 border-b border-slate-100 bg-slate-50 rounded-t">Detailposition</div>
                            <div class="p-2 text-xs text-slate-700">
                                ${itemDetail.level ? `<div><strong>Ebene:</strong> ${itemDetail.level}</div>` : ''}
                                ${itemDetail.box ? `<div><strong>Box:</strong> ${itemDetail.box}</div>` : ''}
                            </div>
                        </div>
                    `;
                }

                // Wrapper für Hover-Effekt
                locationHtml = `
                    <div class="maint-popover-wrapper relative inline-block">
                        <span class="cursor-help inline-flex items-center px-2 py-0.5 rounded text-xs font-medium border ${badgeClass}">
                            ${s.location}
                        </span>
                        ${popoverContent}
                    </div>
                `;
            }
            // --- NEU: DOKUMENTE AUFBEREITEN ---
            let docsHtml = '';
            if (s.documents && s.documents.length > 0) {
                docsHtml = '<div class="flex flex-wrap gap-2 mt-1">';
                s.documents.forEach(doc => {
                    const isImg = doc.name.match(/\.(jpg|jpeg|png|gif|webp)$/i);
                    if (isImg) {
                        // BILD: Thumbnail + Lightbox
                        docsHtml += `
                            <div class="relative group cursor-zoom-in" onclick="event.stopPropagation(); openLightbox('${doc.path}', '${doc.name}')">
                                <img src="${doc.path}" class="w-8 h-8 object-cover rounded border border-slate-200 shadow-sm hover:border-blue-400 transition-colors">
                            </div>`;
                    } else {
                        // DATEI: Icon + Link
                        docsHtml += `
                            <a href="${doc.path}" target="_blank" class="flex items-center justify-center w-8 h-8 bg-slate-50 border border-slate-200 rounded text-slate-500 hover:text-blue-600 hover:border-blue-300 transition-colors" title="${doc.name}" onclick="event.stopPropagation()">
                                <span class="material-icons-outlined text-base">description</span>
                            </a>`;
                    }
                });
                docsHtml += '</div>';
            } else {
                docsHtml = '<span class="text-xs text-slate-300 italic">- Keine -</span>';
            }
            // ----------------------------------

            // --- 4. GRID LAYOUT ZUSAMMENBAUEN ---
            snRow.innerHTML = `
                <td class="px-6 py-3 sn-details-cell text-sm text-slate-700" colspan="8">
                    <div class="grid grid-cols-1 md:grid-cols-7 gap-4 items-start">
                        
                        <!-- S/N -->
                        <div>
                            <div class="text-xs text-slate-400 uppercase font-semibold mb-1">Serial No.</div>
                            <div class="font-mono font-medium text-slate-800">${s.sn}</div>
                        </div>

                        <!-- Location -->
                        <div>
                            <div class="text-xs text-slate-400 uppercase font-semibold mb-1">Location</div>
                            <div class="inline-block">${locationHtml}</div>
                        </div>

                        <!-- Wartung (Breiter) -->
                        <div class="col-span-2">
                            <div class="text-xs text-slate-400 uppercase font-semibold mb-1">Wartungs-Status</div>
                            <div class="inline-block">${maintHtml}</div>
                        </div>
                        
                        <!-- VERSCHOBEN: DOKUMENTE -->
                        <div>
                            <div class="text-xs text-slate-400 uppercase font-semibold mb-1">Dokumente</div>
                            ${docsHtml}
                        </div>

                        <!-- Kit -->
                        <div>
                            <div class="text-xs text-slate-400 uppercase font-semibold mb-1">Assigned Kit</div>
                            <div class="font-medium ${s.kitId !== 'N/A' ? 'text-blue-600' : 'text-slate-400'}">
                                ${getKitName(s.kitId)}
                            </div>
                        </div>

                        <!-- Status -->
                        <div class="flex items-center justify-end h-full">
                            <span class="status-badge ${badgeClass}">${s.status}</span>
                        </div>
                    </div>
                </td>`;

            tbody.appendChild(snRow);
        });
    }

    // KPIs aktualisieren
    document.getElementById('totalPnCount').textContent = Object.keys(hardwareInventory).length;
    document.getElementById('totalSnCount').textContent = totalSN;
    // Aktualisierung für "assignedCount" entfernt
    document.getElementById('maintenanceCount').textContent = maintenance;
  }
  document.getElementById('hardwareInventoryTbody').addEventListener('click', e => { const btn = e.target.closest('.toggle-sn-rows'); if (btn) { const pn = btn.closest('tr').dataset.pn; btn.classList.toggle('is-open'); document.querySelectorAll(`.sn-row[data-parent-pn="${pn}"]`).forEach(r => r.classList.toggle('is-visible')); } });
let isPartEditMode = false;
    function togglePartNumberEditMode(enable) {
        isPartEditMode = enable;
    
        // 1. Header & Footer
        document.getElementById('btnEditPart').style.display = enable ? 'none' : 'block';
        document.getElementById('partEditButtons').classList.toggle('hidden', !enable);
        document.getElementById('partViewButtons').classList.toggle('hidden', enable);
        document.getElementById('partNumberModalSubtitle').textContent = enable ? 'Bearbeiten' : 'Details anzeigen';
        
        // Delete Button nur wenn wir ein bestehendes Item bearbeiten (nicht bei Neu)
        const isNew = !document.getElementById('pnModal_originalPartNumber').value;
        document.getElementById('deletePartBtn').classList.toggle('hidden', !enable || isNew);
    
        // 2. Add Button
        document.getElementById('btnAddSerial').classList.toggle('hidden', !enable);
    
        // 3. Haupt-Formular Inputs (P/N, Name, Desc, Type)
        const mainInputs = document.querySelectorAll('#partNumberForm > div input, #partNumberForm > div textarea');
        mainInputs.forEach(inp => {
            inp.readOnly = !enable;
            if (enable) {
                inp.classList.remove('bg-slate-50', 'border-transparent', 'text-slate-600');
                inp.classList.add('bg-white', 'border-slate-300');
            } else {
                        inp.classList.add('bg-slate-50', 'border-slate-200', 'text-slate-700');
                        inp.classList.remove('bg-white', 'border-slate-300', 'border-transparent', 'text-slate-600');
                    }
        });
        
        // Tag Input (Product Type)
        const tagInput = document.getElementById('pnModal_productInput');
        if(tagInput) {
            tagInput.style.display = enable ? 'block' : 'none';
            // Remove-Buttons an den Tags
            document.querySelectorAll('#pnModal_productTags .tag-chip-remove').forEach(btn => {
                btn.style.display = enable ? 'inline' : 'none';
            });
        }
    
        // 4. Serial Number Blöcke
        document.querySelectorAll('.sn-block').forEach(block => {
            // Inputs im Block
            block.querySelectorAll('input, select').forEach(inp => {
                inp.disabled = !enable; // Selects müssen disabled sein
                inp.readOnly = !enable; // Inputs readonly
                
                if (enable) {
                    inp.classList.remove('bg-transparent', 'border-transparent', 'font-bold', 'text-slate-800');
                    inp.classList.add('bg-white', 'border-slate-300');
                    if(inp.tagName === 'SELECT') inp.classList.remove('appearance-none');
                } else {
                    // View Mode: Subtiler Rahmen und Hintergrund
                    inp.classList.add('bg-slate-50', 'border-slate-200', 'text-slate-700');
                    inp.classList.remove('bg-white', 'border-slate-300', 'bg-transparent', 'border-transparent');
                    
                    // Spezielles Styling für View Mode
                    if (inp.classList.contains('sn-input')) {
                        inp.classList.add('font-bold', 'text-slate-900'); // Etwas dunkler für S/N
                    }
                    if(inp.tagName === 'SELECT') inp.classList.add('appearance-none');
                }
            });
    
            // Delete Button im Block
            const delBtn = block.querySelector('.sn-delete-btn'); // Statt onclick*="remove"
            if(delBtn) delBtn.style.display = enable ? 'block' : 'none';
            
            // Upload Button
            const uploadLabel = block.querySelector('.docs-section label.cursor-pointer');
            if(uploadLabel) uploadLabel.style.display = enable ? 'inline-flex' : 'none';
            
            // Delete Buttons bei Dokumenten
            block.querySelectorAll('.doc-item button').forEach(btn => {
                btn.style.display = enable ? 'block' : 'none';
            });
        });
    }
  // --- PART NUMBER MODAL ---
  function openPartNumberModal(pn) { 
    const f = document.getElementById('partNumberForm'); 
    f.reset(); 
    document.getElementById('serialNumbersContainer').innerHTML=''; 
    
    // Tag Input Reset
    currentProductTags = [];
    renderProductTags();
    setupProductTagInput();

    if(pn){ 
        // EDIT (Startet im View Mode)
        document.getElementById('partNumberModalTitle').textContent = `${pn}`; 
        const p = hardwareInventory[pn]; 
        
        document.getElementById('pnModal_partNumber').value = pn; 
        document.getElementById('pnModal_originalPartNumber').value = pn; 
        document.getElementById('pnModal_hardwareType').value = p.type; 
        document.getElementById('pnModal_name').value = p.name || ''; 
        document.getElementById('pnModal_description').value = p.description; 
        
        currentProductTags = Array.isArray(p.productType) ? [...p.productType] : [p.productType];
        renderProductTags();
        
        p.serials.forEach(s => addSerialNumberBlock(s)); 
        
        deletePartBtn.onclick = () => deletePart(pn); 
        
        // START VIEW MODE
        togglePartNumberEditMode(false);
    }
    else{ 
        // NEW (Startet im Edit Mode)
        document.getElementById('partNumberModalTitle').textContent = 'New Part Number'; 
        document.getElementById('pnModal_originalPartNumber').value = ''; 
        
        currentProductTags = []; 
        renderProductTags();
        
        addSerialNumberBlock(); 
        
        // START EDIT MODE
        togglePartNumberEditMode(true);
        // Edit Button verstecken bei Neu
        document.getElementById('btnEditPart').style.display = 'none';
    } 
    
    openModal(partNumberModal); 
  }

  function closePartNumberModal(){closeModal(partNumberModal);}
  function deletePart(pn){ if(confirm(`Delete part "${pn}"?`)){ delete hardwareInventory[pn]; updateAndReassignKits(); saveDataToServer(); closePartNumberModal(); } }
    function addSerialNumberBlock(d=null){ 
          const c=document.getElementById('serialNumbersContainer'); 
          const b=document.createElement('div'); 
          b.className='p-4 border border-slate-200 rounded-lg bg-slate-50 space-y-3 relative sn-block'; 
          
          const s = {
              id: d?.id || '',
              sn: d?.sn || '',
              loc: d?.location || '',
              chk: d?.lastCheck || '',
              st: d?.status || 'Available',
              mid: d?.maintenanceId || 'N/A',
              // NEU: Dokumente laden
              docs: d?.documents || [] 
          };
    
          // HTML Aufbau
          b.innerHTML=`
            <input type="hidden" class="id-input" value="${s.id}">
            
            <!-- Header Buttons (Delete & Docs) -->
            <div class="absolute top-2 right-2 flex gap-1">
                <button type="button" class="text-blue-400 hover:text-blue-600 transition-colors p-1" title="Dokumente/Bilder" onclick="this.closest('.sn-block').querySelector('.docs-section').classList.toggle('hidden')">
                    <span class="material-icons-outlined text-lg">attachment</span>
                </button>
                <button type="button" class="sn-delete-btn text-slate-400 hover:text-red-500 transition-colors p-1" onclick="this.closest('.sn-block').remove()">
                    <span class="material-icons-outlined text-lg">delete</span>
                </button>
            </div>
    
            <div class="grid md:grid-cols-3 gap-4">
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">S/N</label>
                    <input type="text" class="sn-input w-full px-3 py-2 border border-slate-300 rounded-md text-sm focus:ring-blue-500" value="${s.sn}" required>
                </div>
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Location</label>
                    <input type="text" class="location-input w-full px-3 py-2 border border-slate-300 rounded-md text-sm focus:ring-blue-500" value="${s.loc}">
                </div>
                <div class="hidden"> 
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Wartung ID</label>
                    <input type="text" class="maintenanceid-input w-full px-3 py-2 border border-slate-300 rounded-md text-sm focus:ring-blue-500" value="${s.mid}">
                </div>
            </div>
            <div class="grid md:grid-cols-2 gap-4">
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Last Check</label>
                    <input type="date" class="lastcheck-input w-full px-3 py-2 border border-slate-300 rounded-md text-sm focus:ring-blue-500" value="${s.chk}">
                </div>
                <div>
                    <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Status</label>
                    <select class="status-select w-full px-3 py-2 border border-slate-300 rounded-md text-sm focus:ring-blue-500 bg-white">
                        ${['Available','Maintenance','In Order','Decommissioned'].map(o=>`<option ${s.st===o?'selected':''}>${o}</option>`).join('')}     
                    </select>
                </div>
            </div>
    
            <!-- DOKUMENTEN SEKTION (Standardmäßig versteckt) -->
            <div class="docs-section hidden mt-4 pt-3 border-t border-slate-200 bg-slate-100/50 -mx-4 px-4 pb-2 docs-container">
                <label class="block text-xs font-bold text-slate-500 uppercase mb-2">Dokumente & Bilder</label>
                <div class="docs-list mb-2"></div>
                <label class="cursor-pointer inline-flex items-center gap-2 text-xs font-medium text-blue-600 hover:text-blue-800 bg-white border border-blue-200 px-3 py-1.5 rounded transition-colors">
                    <span class="material-icons-outlined text-sm">upload</span> Datei hochladen
                    <input type="file" class="hidden" onchange="uploadHardwareFile(this)">
                </label>
            </div>
          `; 
          
          c.appendChild(b); 
          
          // Existierende Docs rendern
          const list = b.querySelector('.docs-list');
          s.docs.forEach(doc => addDocItem(list, doc.path, doc.name, doc.stored_name));
    }
document.getElementById('partNumberForm').addEventListener('submit', async e => { 
    e.preventDefault();

    console.log("DEBUG: Formular partNumberForm wird verarbeitet...");

    const opn = document.getElementById('pnModal_originalPartNumber').value; 
    const npn = document.getElementById('pnModal_partNumber').value.trim(); 

    if(!npn) {
        alert("Part Number darf nicht leer sein.");
        return;
    }

    // Prüfen auf Duplikate
    if((!opn || opn !== npn) && hardwareInventory[npn]){ 
        alert(`ACHTUNG: Die Part Number "${npn}" existiert bereits!`); 
        return; 
    }

    // Seriennummern sammeln
    const sns=[]; 
    document.querySelectorAll('.sn-block').forEach(b=>{ 
        const snInput = b.querySelector('.sn-input');
        if(snInput) {
            
            // NEU: Dokumente einsammeln
            const docs = [];
            b.querySelectorAll('.doc-item').forEach(d => {
                docs.push({
                    path: d.dataset.path,
                    name: d.dataset.name,
                    stored_name: d.dataset.stored
                });
            });

            const sn = snInput.value.trim(); 
            if(sn) sns.push({
                id: b.querySelector('.id-input').value,
                sn, 
                location: b.querySelector('.location-input').value.trim(), 
                lastCheck: b.querySelector('.lastcheck-input').value, 
                status: b.querySelector('.status-select').value, 
                maintenanceId: b.querySelector('.maintenanceid-input').value.trim()||'N/A',
                documents: docs // <--- HIER SPEICHERN
            }); 
        }
    });

    const pd = {
        name: document.getElementById('pnModal_name').value.trim(), 
        type: document.getElementById('pnModal_hardwareType').value.trim(), 
        productType: currentProductTags, // aus der globalen Variable
        description: document.getElementById('pnModal_description').value.trim(), 
        serials: sns
    };

    // Wenn umbenannt wurde, alten Eintrag löschen
    if(opn && opn !== npn){ 
        delete hardwareInventory[opn]; 
        // Optional: Hier müsste man auch BOMs aktualisieren, die auf die alte PN verweisen
    }

    // Speichern im lokalen Objekt
    hardwareInventory[npn] = pd; 

    // UI Updates
    updateAndReassignKits(); // Rendert die Tabelle neu

    // Speichern am Server
    await saveDataToServer(); 

    // Formular zurücksetzen, damit Safari nicht meckert
    e.target.reset(); 

    closePartNumberModal(); 
  });

    // --- BOM MODAL ---
    let isBomEditMode = false;
// --- NEUE FUNKTION: Toggle View/Edit Mode ---
function toggleBomEditMode(enable) {
    isBomEditMode = enable;

    // 1. Header Input Styling
    const nameInput = document.getElementById('modalKitNameInput');
    nameInput.readOnly = !enable;
    if (enable) {
        // Edit Style
        nameInput.classList.remove('bg-transparent', 'border-transparent');
        nameInput.classList.add('bg-white', 'border-slate-300', 'focus:ring-2', 'focus:ring-blue-500', 'focus:border-transparent', 'px-2', 'py-1');
    } else {
        // View Style (Reset auf den Wert vor dem Editieren wäre hier komplex, wir lassen den Wert so)
        // Besser: Beim Abbrechen (toggle false) laden wir das Modal neu oder resetten den Wert.
        // Hier kümmern wir uns nur ums Styling.
        nameInput.classList.add('bg-transparent', 'border-transparent');
        nameInput.classList.remove('bg-white', 'border-slate-300', 'focus:ring-2', 'focus:ring-blue-500', 'focus:border-transparent', 'px-2', 'py-1');
        
        // Reset Name bei Abbrechen (optional, holt Originalwert aus Kit-Objekt)
        const id = document.getElementById('modalKitId').dataset.fullId;
        if(kitBoms[id]) nameInput.value = kitBoms[id].name;
    }

    // 2. Buttons umschalten
    document.getElementById('btnEditBom').style.display = enable ? 'none' : 'block';
    document.getElementById('bomAddContainer').classList.toggle('hidden', !enable);
    
    // 3. Footer umschalten
    document.getElementById('bomViewFooter').classList.toggle('hidden', enable);
    document.getElementById('bomEditFooter').classList.toggle('hidden', !enable);

    // 4. Liste bearbeitbar machen (Inputs, Selects, Delete Buttons)
    const container = document.getElementById('bomItemsContainer');
    
    // Selects & Inputs
    container.querySelectorAll('select, input').forEach(el => {
        el.disabled = !enable;
        if (enable) {
            el.classList.remove('bg-slate-100', 'text-slate-500');
            el.classList.add('bg-white');
        } else {
            el.classList.add('bg-slate-100', 'text-slate-500');
            el.classList.remove('bg-white');
        }
    });

    // Delete Buttons
    container.querySelectorAll('.bom-delete-btn').forEach(btn => {
        btn.style.display = enable ? 'block' : 'none';
    });
}

function openBomModal(id) { 
    // ID speichern
    const idSpan = document.getElementById('modalKitId');
    idSpan.dataset.fullId = id; 
    
    // Daten holen
    const kitData = kitBoms[id];
    const displayName = kitData ? kitData.name : id;
    
    // Input Feld befüllen
    const nameInput = document.getElementById('modalKitNameInput');
    nameInput.value = displayName;
    
    document.getElementById('bomItemsContainer').innerHTML = ''; 
    
    // Fallback für neues Kit
    if(!kitBoms[id]) {
        kitBoms[id] = { id: id, name: displayName, items: [], status: 'missing' }; 
    }
    
    const items = kitBoms[id].items || []; 
    if(items.length === 0) addBomItemRow('', 1); 
    else items.forEach(i => addBomItemRow(i.partNumber, i.quantity)); 
    
    // START IM VIEW MODE
    toggleBomEditMode(false);
    
    openModal(bomModal); 
}

function addBomItemRow(partNumber, quantity) {
    const div = document.createElement('div');
    div.className = 'bom-item-row bg-slate-50 p-4 rounded-lg border border-slate-200 relative group transition-all';

    let opts = '<option value="">-- Select P/N --</option>'; 
    Object.keys(hardwareInventory).sort().forEach(p => { 
        // Product Name anzeigen
        const pName = hardwareInventory[p].name || '';
        opts += `<option value="${p}" ${p === partNumber ? 'selected' : ''}>${p} - ${pName}</option>`; 
    });

    // STATUS LOGIK (für die Anzeige auch im Edit Mode nützlich)
    let statusIcon = '';
    let statusText = '';
    let assignedSerialsList = '<span class="text-slate-400 italic text-xs">None assigned</span>';
    const kitId = document.getElementById('modalKitId').dataset.fullId; // ID nutzen!

    if (partNumber) {
        const assignedSerials = hardwareInventory[partNumber]?.serials.filter(s => s.kitId === kitId) || [];
        const assignedCount = assignedSerials.length;

        if (assignedCount >= quantity) {
            statusIcon = '<span class="material-icons-outlined text-green-500 text-2xl">check_circle</span>';
            statusText = `<span class="text-xs text-green-600 font-bold">${assignedCount}/${quantity} Ready</span>`;
        } else if (assignedCount > 0) {
            statusIcon = '<span class="material-icons-outlined text-yellow-500 text-2xl">warning</span>';
            statusText = `<span class="text-xs text-yellow-600 font-bold">${assignedCount}/${quantity} Partial</span>`;
        } else {
            statusIcon = '<span class="material-icons-outlined text-red-500 text-2xl">error</span>';
            statusText = `<span class="text-xs text-red-600 font-bold">Missing All (${quantity})</span>`;
        }
        
        if (assignedCount > 0) {
            assignedSerialsList = assignedSerials.map(s => `<span class="inline-block bg-white border border-slate-200 text-slate-700 text-xs font-mono py-0.5 px-1.5 rounded">${s.sn}</span>`).join(' ');
        }
    }

    // Styling abhängig vom Modus (wird beim Initialisieren gesetzt)
    const disabledAttr = !isBomEditMode ? 'disabled' : '';
    const inputClass = !isBomEditMode ? 'bg-slate-100 text-slate-500' : 'bg-white';
    const deleteDisplay = !isBomEditMode ? 'none' : 'block';

    div.innerHTML = `
        <button type="button" onclick="removeBomItem(this)" class="bom-delete-btn absolute top-2 right-2 text-slate-400 hover:text-red-500 transition-colors p-1 rounded-full hover:bg-slate-100" style="display: ${deleteDisplay}" title="Remove item">
            <span class="material-icons-outlined text-lg">delete</span>
        </button>
        
        <div class="grid grid-cols-12 gap-4 items-start pr-6">
            <div class="col-span-1 flex justify-center pt-7">${statusIcon}</div>
            <div class="col-span-11">
                <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
                    <div class="md:col-span-2">
                        <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Component</label>
                        <select class="w-full px-3 py-2 border border-slate-300 rounded-md text-sm focus:ring-blue-500 ${inputClass}" ${disabledAttr}>${opts}</select>
                    </div>
                    <div>
                        <label class="block text-xs font-semibold text-slate-500 uppercase mb-1">Required</label>
                        <input type="number" value="${quantity}" min="1" class="w-full px-3 py-2 border border-slate-300 rounded-md text-sm focus:ring-blue-500 ${inputClass}" ${disabledAttr}>
                    </div>
                </div>
                <div class="mt-3 flex items-start gap-4 bg-white p-2 rounded border border-slate-100">
                    <div class="flex-1">
                        <span class="text-xs font-semibold text-slate-500 uppercase">Assigned S/N</span>
                        <div class="mt-1 flex flex-wrap gap-1">${assignedSerialsList}</div>
                    </div>
                    <div class="text-right flex-shrink-0">
                        <span class="text-xs font-semibold text-slate-500 uppercase">Status</span>
                        <div class="mt-1">${statusText}</div>
                    </div>
                </div>
            </div>
        </div>`;
        
    document.getElementById('bomItemsContainer').appendChild(div);
}
  function closeBomModal(){ closeModal(bomModal); }
  function addBomItem(){ addBomItemRow('',1); }
  function removeBomItem(b){ b.closest('.bom-item-row').remove(); }
  
    document.getElementById('bomForm').addEventListener('submit', e => { 
        e.preventDefault(); 
        
        // ID holen
        const id = document.getElementById('modalKitId').dataset.fullId; 
        
        // Namen holen
        const newName = document.getElementById('modalKitNameInput').value.trim();
        if (!newName) { alert("Kit Name darf nicht leer sein."); return; }
    
        const items = []; 
        document.querySelectorAll('#bomItemsContainer .bom-item-row').forEach(r => { 
            const p = r.querySelector('select').value; 
            const q = r.querySelector('input[type=number]').value; 
            if(p && q) items.push({ partNumber: p, quantity: parseInt(q) }); 
        }); 
        
        if (kitBoms[id]) {
            kitBoms[id].items = items;
            kitBoms[id].name = newName; // Name updaten
        }
        
        closeBomModal(); 
        
        saveDataToServer().then(() => {
            // UI aktualisieren (besonders wichtig wegen Namensänderung)
            renderMappingTable(); 
            applyHardwareFilters(); // Falls in der Hardware-Liste Kit-Namen stehen
        }); 
    });

  // --- MAPPINGS ---
  function openNewMappingModal(){ document.getElementById('newMappingForm').reset(); document.getElementById('newKitTagsContainer').innerHTML=''; openModal(newMappingModal); }
  function closeNewMappingModal(){ closeModal(newMappingModal); }
  document.getElementById('addKitTagBtn').addEventListener('click',()=>{ const i=document.getElementById('newKitNameInput'); if(addKitTag(i.value.trim(), document.getElementById('newKitTagsContainer'))) i.value=''; });
    document.getElementById('newMappingForm').addEventListener('submit', e => { 
        e.preventDefault(); 
        const eng = document.getElementById('newEngineType').value.trim(); 
        const tags = document.getElementById('newKitTagsContainer').querySelectorAll('.new-kit-tag'); 
        
        if(!eng || tags.length === 0) return; 
        
        engineMappings[eng] = []; 
        
        tags.forEach(t => { 
            const name = t.dataset.kitName; 
            // Generiere neue UUID im Frontend
            const newUuid = "kit_" + Math.random().toString(36).substr(2, 9);
            
            // Objekt anlegen
            kitBoms[newUuid] = { 
                id: newUuid,
                name: name,
                items: [], 
                status: 'missing' 
            }; 
            
            // Mapping auf UUID setzen
            engineMappings[eng].push(newUuid); 
        }); 
        
        renderMappingTable(); 
        closeNewMappingModal(); 
        saveDataToServer(); 
    });

    function openEditMappingModal(btn){ 
          document.getElementById('editMappingForm').reset(); 
          const r = btn.closest('tr'); 
          const eng = r.querySelector('.engine-type-cell').textContent.trim(); 
          
          document.getElementById('editMappingModalTitle').textContent = `Edit: ${eng}`; 
          document.getElementById('editEngineType').value = eng; 
          document.getElementById('originalEngineType').value = eng; 
          
          const c = document.getElementById('editKitTagsContainer'); 
          c.innerHTML = ''; 
          
          const kitIds = engineMappings[eng] || [];
          kitIds.forEach(uuid => {
              // Name auflösen für Anzeige
              const name = kitBoms[uuid] ? kitBoms[uuid].name : uuid;
              addKitTag(name, c); 
          }); 
          
          deleteMappingBtn.onclick = () => deleteMapping(eng); 
          openModal(editMappingModal); 
    }
  function closeEditMappingModal(){ closeModal(editMappingModal); }
  function deleteMapping(e) { if(confirm(`Mapping "${e}" löschen?`)){ delete engineMappings[e]; saveDataToServer(); renderMappingTable(); closeEditMappingModal(); } }
  document.getElementById('addKitTagToEditBtn').addEventListener('click',()=>{ const i=document.getElementById('editKitNameInput'); if(addKitTag(i.value.trim(), document.getElementById('editKitTagsContainer'))) i.value=''; });

  document.getElementById('editMappingForm').addEventListener('submit', e => { 
      e.preventDefault(); 
      const oEng = document.getElementById('originalEngineType').value; 
      const nEng = document.getElementById('editEngineType').value.trim(); 
      if(!nEng) return; 

      if(oEng !== nEng) {
          delete engineMappings[oEng];
          // ... (Product Type Rename Logik bleibt gleich) ...
      }

      const newKitIds = []; 
      
      document.getElementById('editKitTagsContainer').querySelectorAll('.new-kit-tag').forEach(t => { 
          const name = t.dataset.kitName; 
          
          // Checken: Existiert schon ein Kit mit diesem Namen?
          // (Wir suchen in den Values von kitBoms)
          let existingId = Object.keys(kitBoms).find(key => kitBoms[key].name === name);
          
          if (existingId) {
              newKitIds.push(existingId);
          } else {
              // Neues Kit wurde im Edit-Fenster hinzugefügt -> UUID generieren
              const newUuid = "kit_" + Math.random().toString(36).substr(2, 9);
              kitBoms[newUuid] = { 
                  id: newUuid,
                  name: name,
                  items: [], 
                  status: 'missing' 
              };
              newKitIds.push(newUuid);
          }
      }); 
      
      engineMappings[nEng] = newKitIds; 
      
      renderMappingTable(); 
      closeEditMappingModal(); 
      saveDataToServer(); 
  });
  document.getElementById('engineKitMappingTable').addEventListener('click',e=>{ if(e.target.closest('.edit-mapping-btn')) openEditMappingModal(e.target.closest('.edit-mapping-btn')); });

    // --- HARDWARE FILTER LOGIK ---
    
    function toggleFilterPanelHw(btn) {
        const panel = document.getElementById('filterPanelHw');
        if (panel.classList.contains('hidden')) {
            panel.classList.remove('hidden');
            btn.classList.add('bg-blue-50', 'text-blue-700', 'border-blue-200');
            btn.classList.remove('bg-white', 'text-slate-600', 'border-slate-300');
        } else {
            panel.classList.add('hidden');
            btn.classList.remove('bg-blue-50', 'text-blue-700', 'border-blue-200');
            btn.classList.add('bg-white', 'text-slate-600', 'border-slate-300');
        }
    }
    
    // Initialisiert die Dropdowns basierend auf den echten Daten
    function initHardwareFilters() {
        const types = new Set();
        const products = new Set();
        const locations = new Set();
        
        // Durch Inventar iterieren und Werte sammeln
        Object.values(hardwareInventory).forEach(part => {
            if (part.type) types.add(part.type.trim());
            
            // Product Type (kann Array oder String sein)
            if (Array.isArray(part.productType)) {
                part.productType.forEach(p => { if(p) products.add(p.trim()); });
            } else if (part.productType) {
                products.add(part.productType.trim());
            }
            
            // Locations aus Serialnummern sammeln
            if (part.serials) {
                part.serials.forEach(s => {
                    if (s.location && s.location.trim()) locations.add(s.location.trim());
                });
            }
        });
    
        const fillSelect = (id, items) => {
            const el = document.getElementById(id);
            if(!el) return;
            // Reset auf Option "Alle"
            el.innerHTML = el.options[0].outerHTML;
            
            Array.from(items).sort().forEach(item => {
                const opt = document.createElement('option');
                opt.value = item;
                opt.textContent = item;
                el.appendChild(opt);
            });
        };
    
        fillSelect('filterHwType', types);
        fillSelect('filterHwProduct', products);
        fillSelect('filterHwLocation', locations);
    }
    
    // Die eigentliche Filter-Funktion
    function applyHardwareFilters() {
        const term = document.getElementById('hardwareSearchInput').value.toLowerCase();
        // Reset: Alles zuklappen vor der Suche
        document.querySelectorAll('.sn-row').forEach(r => r.classList.remove('is-visible'));
        document.querySelectorAll('.toggle-sn-rows').forEach(btn => btn.classList.remove('is-open'));
        areAllRowsExpanded = false; // Globalen State resetten
        const expandIcon = document.getElementById('expandAllBtn')?.querySelector('.material-icons-outlined');
        if(expandIcon) expandIcon.textContent = 'unfold_more';
        
        // IDs der Chips
        const filters = {
            type: document.getElementById('filterHwType'),
            prod: document.getElementById('filterHwProduct'),
            status: document.getElementById('filterHwStatus'),
            loc: document.getElementById('filterHwLocation')
        };
    
        // Werte holen (Safeguard falls Elemente beim Laden kurz fehlen)
        const typeVal = filters.type ? filters.type.value : 'all';
        const prodVal = filters.prod ? filters.prod.value : 'all';
        const statusVal = filters.status ? filters.status.value : 'all';
        const locVal = filters.loc ? filters.loc.value : 'all';
    
        // 1. Visual Feedback für Chips (Blau färben wenn aktiv)
        Object.values(filters).forEach(selectEl => {
            if (!selectEl) return;
            if (selectEl.value !== 'all') {
                // Aktiv Style: Blau & Fett
                selectEl.classList.remove('bg-white', 'text-slate-600', 'border-slate-300');
                selectEl.classList.add('bg-blue-50', 'text-blue-700', 'border-blue-200', 'font-bold');
            } else {
                // Inaktiv Style: Grau & Standard
                selectEl.classList.add('bg-white', 'text-slate-600', 'border-slate-300');
                selectEl.classList.remove('bg-blue-50', 'text-blue-700', 'border-blue-200', 'font-bold');
            }
        });
    
        let visiblePnCount = 0;
        let visibleSnCount = 0;
        let totalPnCount = 0;
    
        // 2. Durch die Tabelle iterieren
        document.querySelectorAll('#hardwareInventoryTbody tr.pn-row').forEach(row => {
            const pn = row.dataset.pn;
            const part = hardwareInventory[pn];
            totalPnCount++;
    
            if (!part) return;
    
            let matches = true;
    
            // A. Text Suche (P/N, Name, Desc, Type, S/Ns)
            if (term) {
                const serialsString = part.serials.map(s => s.sn).join(' ');
                const textContent = `${pn} ${part.name} ${part.description || ''} ${part.type || ''} ${serialsString}`.toLowerCase();
                if (!textContent.includes(term)) matches = false;
            }
    
            // B. Hardware Type
            if (matches && typeVal !== 'all') {
                if (part.type !== typeVal) matches = false;
            }
    
            // C. Product Type
            if (matches && prodVal !== 'all') {
                const pTypes = Array.isArray(part.productType) ? part.productType : [part.productType];
                if (!pTypes.includes(prodVal)) matches = false;
            }
    
            // D. Status (Prüft, ob mindestens eine S/N diesen Status hat)
            if (matches && statusVal !== 'all') {
                if (!part.serials.some(s => s.status === statusVal)) matches = false;
            }
    
            // E. Location (Prüft, ob mindestens eine S/N an diesem Ort ist)
            if (matches && locVal !== 'all') {
                if (!part.serials.some(s => s.location === locVal)) matches = false;
            }
    
            // 3. Ergebnis anzeigen/verstecken
            if (matches) {
                row.style.display = '';
                visiblePnCount++;
                
                // Intelligente S/N Zählung für die Statistik
                // Wenn wir nach Status oder Location filtern, zählen wir nur die S/Ns, die auch wirklich passen.
                let matchingSns = 0;
                if (statusVal === 'all' && locVal === 'all') {
                    matchingSns = part.serials.length;
                } else {
                    matchingSns = part.serials.filter(s => 
                        (statusVal === 'all' || s.status === statusVal) &&
                        (locVal === 'all' || s.location === locVal)
                    ).length;
                }
                visibleSnCount += matchingSns;
    
                // Optional: Wenn der User explizit filtert, könnte man die Unterzeilen (S/Ns)
                // hier auch filtern/highlighten, aber das ist komplexer.
                // Aktuell zeigen wir die Hauptzeile (P/N), wenn IRGENDWAS passt.
                
            } else {
                row.style.display = 'none';
            }
        });
    
        // 4. Counter Update
        const counterEl = document.getElementById('hwTableCount');
        if (counterEl) {
            const isFilterActive = (typeVal !== 'all' || prodVal !== 'all' || statusVal !== 'all' || locVal !== 'all');
            
            // Zeige Counter, wenn gefiltert wird ODER gesucht wird ODER nicht alle Ergebnisse sichtbar sind
            if (visiblePnCount < totalPnCount || term || isFilterActive) {
                counterEl.textContent = `${visiblePnCount} P/N (${visibleSnCount} S/N)`;
                counterEl.classList.remove('hidden');
                
                // Roter Hintergrund, wenn 0 Ergebnisse
                if (visiblePnCount === 0) {
                    counterEl.classList.remove('bg-slate-100', 'text-slate-500');
                    counterEl.classList.add('bg-red-100', 'text-red-600');
                } else {
                    counterEl.classList.add('bg-slate-100', 'text-slate-500');
                    counterEl.classList.remove('bg-red-100', 'text-red-600');
                }
            } else {
                counterEl.classList.add('hidden');
            }
        }
    }
  document.getElementById('hardwareSearchInput').addEventListener('input', applyHardwareFilters);
  document.getElementById('hardwareStatusFilter').addEventListener('change', applyHardwareFilters);
  window.addEventListener('keydown', e => { if (e.key === 'Escape') { closeModal(bomModal); closeModal(partNumberModal); closeModal(newMappingModal); closeModal(editMappingModal); closeModal(trolleyModal); } });

  // --- KIT LOGIC ---
    function getKitName(id) {
        if (!id) return '-';
        // Fall 1: Es ist eine UUID und wir finden sie in den BOMs
        if (kitBoms[id] && kitBoms[id].name) {
            return kitBoms[id].name;
        }
        // Fall 2: Fallback (falls Migration noch nicht lief oder es ein alter String ist)
        return id; 
    }
    function getKitStatusClass(s) { return s === 'ready' ? 'bg-kit-ready' : s === 'partial' ? 'bg-kit-partial' : 'bg-kit-missing'; }
    function updateAndReassignKits() {
        // Wir berechnen NICHTS mehr selbst. 
        // Wir vertrauen darauf, dass 'hardwareInventory' (kitId) und 'kitBoms' (status) 
        // vom Server korrekt befüllt wurden.
        
        renderHardwareTable();
        updateAllKitStatuses(); // Zeigt nur die Farben an
        applyHardwareFilters();
        renderMappingTable();
    }
    
    function updateAllKitStatuses() {
        // Färbt die Punkte in der Mapping-Tabelle basierend auf dem Server-Status
        document.querySelectorAll('.kit-tag').forEach(t => { 
            const kitId = t.dataset.kitId; // Wir speichern die ID jetzt im Dataset
            const bomData = kitBoms[kitId]; 
            
            if(bomData && bomData.status) { 
                t.querySelector('.kit-status-dot').className = `kit-status-dot ${getKitStatusClass(bomData.status)}`; 
            } 
        });
    }

// --- SELECTION LOGIC ---
  function togglePnSelection(pn) {
      if (selectedPartNumbers.has(pn)) selectedPartNumbers.delete(pn);
      else selectedPartNumbers.add(pn);
      updateBulkUI();
  }

  document.getElementById('selectAllHardware').addEventListener('change', (e) => {
      const isChecked = e.target.checked;
      const visibleRows = document.querySelectorAll('#hardwareInventoryTbody tr.pn-row:not([style*="display: none"])');

      visibleRows.forEach(row => {
          const pn = row.dataset.pn;
          if (isChecked) selectedPartNumbers.add(pn);
          else selectedPartNumbers.delete(pn);
          row.querySelector('.pn-checkbox').checked = isChecked;
      });
      updateBulkUI();
  });

  function updateBulkUI() {
      const btn = document.getElementById('bulkEditBtn');
      const countSpan = document.getElementById('bulkEditCount');
      const count = selectedPartNumbers.size;

      if (count > 0) {
          btn.classList.remove('hidden');
          countSpan.textContent = `${count} Selected`;
      } else {
          btn.classList.add('hidden');
      }
  }

  // --- BULK EDIT LOGIC ---

  // Wiederverwendbare Tag-Logik für Bulk Modal (Kopie der Logik vom PartModal, aber auf Bulk-Elemente gemappt)
  function renderBulkProductTags() {
      const container = document.getElementById('bulkProductTags');
      const input = document.getElementById('bulkProductInput');
      // Bestehende Chips entfernen (außer Input)
      Array.from(container.children).forEach(c => { if(c !== input) c.remove(); });

      currentBulkProductTags.forEach(tag => {
          const chip = document.createElement('span');
          chip.className = 'tag-chip';
          chip.innerHTML = `${tag}<span class="tag-chip-remove" onclick="removeBulkProductTag('${tag}')">×</span>`;
          container.insertBefore(chip, input);
      });
      input.value = '';
  }

  function addBulkProductTag(tag) {
      if (tag && !currentBulkProductTags.includes(tag)) {
          currentBulkProductTags.push(tag);
          renderBulkProductTags();
      }
      document.getElementById('bulkProductDropdown').classList.add('hidden');
  }

  window.removeBulkProductTag = function(tag) {
      currentBulkProductTags = currentBulkProductTags.filter(t => t !== tag);
      renderBulkProductTags();
  }

  function setupBulkTagInput() {
      // ... (Analog zu setupProductTagInput, nur mit IDs 'bulkProductInput' und 'bulkProductDropdown') ...
      // (Du kannst hier exakt den Code von setupProductTagInput kopieren und die IDs anpassen)
      const input = document.getElementById('bulkProductInput');
      const dropdown = document.getElementById('bulkProductDropdown');
      const availableTypes = new Set(['N/A', 'Facility']);
      document.querySelectorAll('#engineKitMappingTable .engine-type-cell').forEach(cell => availableTypes.add(cell.textContent.trim()));

      const newInput = input.cloneNode(true);
      input.parentNode.replaceChild(newInput, input);

      newInput.addEventListener('input', () => {
          const val = newInput.value.toLowerCase();
          dropdown.innerHTML = '';
          const filtered = Array.from(availableTypes).filter(t => t.toLowerCase().includes(val) && !currentBulkProductTags.includes(t));
          if (filtered.length > 0) {
              filtered.forEach(t => {
                  const div = document.createElement('div');
                  div.className = 'tag-dropdown-item';
                  div.textContent = t;
                  div.onclick = () => addBulkProductTag(t);
                  dropdown.appendChild(div);
              });
              dropdown.classList.remove('hidden');
          } else dropdown.classList.add('hidden');
      });
      document.addEventListener('click', (e) => { if (!newInput.contains(e.target) && !dropdown.contains(e.target)) dropdown.classList.add('hidden'); });
      newInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); addBulkProductTag(newInput.value.trim()); } });
  }

  function openBulkEditModal() {
      document.getElementById('bulkEditCountLabel').textContent = selectedPartNumbers.size;
      document.getElementById('bulkHardwareType').value = '';
      currentBulkProductTags = [];
      renderBulkProductTags();
      setupBulkTagInput();
      openModal(bulkEditModal);
  }

  function closeBulkEditModal() { closeModal(bulkEditModal); }

  function submitBulkEdit() {
      const newHwType = document.getElementById('bulkHardwareType').value.trim();
      const newProdTypes = currentBulkProductTags; // Array

      if (!newHwType && newProdTypes.length === 0) {
          closeBulkEditModal();
          return;
      }

      if (!confirm(`Änderungen auf ${selectedPartNumbers.size} Teile anwenden?`)) return;

      selectedPartNumbers.forEach(pn => {
          if (hardwareInventory[pn]) {
              if (newHwType) hardwareInventory[pn].type = newHwType;
              if (newProdTypes.length > 0) hardwareInventory[pn].productType = [...newProdTypes];
          }
      });

      saveDataToServer();
      renderHardwareTable();

      // Reset Selection
      selectedPartNumbers.clear();
      document.getElementById('selectAllHardware').checked = false;
      updateBulkUI();

      closeBulkEditModal();
  }
</script>

</body></html>
"""

MAINTENANCE_PAGE_HTML = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <title>MBET - Maintenance (SWAT)</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <script src="https://cdn.jsdelivr.net/npm/sortablejs@latest/Sortable.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <style>
        body { font-family: 'Inter', sans-serif; background-color: #f8fafc; }
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label, #sidebar.is-collapsed .sidebar-title, #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link, #sidebar.is-collapsed .sidebar-header, #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined, #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .text-primary { color: #3B82F6; }
        .bg-primary { background-color: #3B82F6; }
        .custom-scrollbar::-webkit-scrollbar { width: 8px; height: 8px; }
        .custom-scrollbar::-webkit-scrollbar-track { background: transparent; }
        .custom-scrollbar::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 4px; }
        .custom-scrollbar:hover::-webkit-scrollbar-thumb { background: #94a3b8; }
        .modal { transition: opacity 0.2s ease; z-index: 50; }
        body.modal-active { overflow-y: hidden; }
        .modal-backdrop { position: fixed; inset: 0; background-color: rgba(0,0,0,0.5); z-index: 40; }
        .modal-content { position: relative; margin: 2rem auto; border-radius: 0.5rem; background-color: white; box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); z-index: 50; display: flex; flex-direction: column; max-height: 90vh; }
        .attachment-item { @apply flex items-center gap-2 p-2 bg-slate-50 rounded border border-slate-200 text-sm; }
        .attachment-item img { @apply w-8 h-8 object-cover rounded; }

        .checklist-item { display: flex; align-items: center; gap: 12px; padding: 12px; margin-bottom: 8px; border-radius: 8px; border: 1px solid #e2e8f0; background-color: #ffffff; cursor: pointer;
            transition: all 0.2s;
            width: 100%;
            box-shadow: 0 1px 2px rgba(0,0,0,0.05);
        }

        .checklist-item:hover {
            border-color: #60a5fa; /* blue-400 */
            background-color: #f8fafc; /* slate-50 */
        }

        .checklist-item:last-child { margin-bottom: 0; }
        .checklist-item input[type="checkbox"] { 
            width: 20px;
            height: 20px;
            border-radius: 4px;
            border: 1px solid #cbd5e1;
            color: #2563eb; /* blue-600 */
            margin-right: 4px;
            flex-shrink: 0;
            cursor: pointer;
        }
        .checklist-item span { font-size: 0.875rem; font-weight: 500; color: #334155; line-height: 1.4; flex-grow: 1;}

        .checklist-item.checked { background-color: #eff6ff !important; border-color: #bfdbfe !important;}
        .checklist-item.checked span { color: #1e3a8a; font-weight: 600;}

        .nav-chip { @apply inline-flex items-center gap-1 px-2 py-0.5 rounded text-[10px] font-medium border cursor-pointer transition-colors; }
        .nav-chip:hover { @apply opacity-80; }

        .drag-handle { cursor: grab; color: #94a3b8; }
        .drag-handle:active { cursor: grabbing; }
        .task-row { @apply flex items-center p-3 border-b border-slate-100 hover:bg-slate-50 transition-colors cursor-pointer; }
        .task-row:last-child { @apply border-b-0; }
        .task-meta-col { @apply flex-shrink-0 flex items-center gap-4 text-right; }
        .tag-chip { display: inline-flex; align-items: center; padding: 0.125rem 0.625rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 500; background-color: #dbeafe; color: #1e40af; border: 1px solid #bfdbfe; margin-right: 0.5rem; margin-bottom: 0.5rem; }
        .tag-chip-remove { margin-left: 0.375rem; color: #2563eb; cursor: pointer; font-weight: 700; font-size: 1rem; line-height: 1; }
        .tag-chip-remove:hover { color: #1e40af; }
        .analyse-toggle-btn { transition: all 0.2s; }
        .analyse-toggle-btn.active {
        background-color: #ffffff;
        color: #0369a1; /* sky-700 */
        font-weight: 600;
        box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    }
    </style>
</head>
<body class="bg-white text-slate-800">
<div class="flex h-screen bg-white">
    {% include 'sidebar.html' %}
    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200 sticky top-0 z-30">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Maintenance Center</h1>
             <div class="flex items-center gap-4">
                <div class="text-sm text-slate-600">Angemeldet als: <strong>{{ current_user_display_name }}</strong></div>
                {% if session.get('is_admin') %}
                <span class="bg-purple-100 text-purple-800 text-xs font-medium px-2.5 py-0.5 rounded border border-purple-200">Admin</span>
                {% endif %}
             </div>
        </header>
        <div class="p-4 sm:p-6 lg:p-8">
            <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
                <div class="bg-white p-5 rounded-lg shadow-md flex items-center space-x-4 border border-slate-200">
                    <div class="p-3 bg-blue-50 rounded-full text-blue-600"><span class="material-icons-outlined text-3xl">assignment</span></div>
                    <div><p class="text-sm text-slate-500">Offene Aufträge</p><p class="text-2xl font-bold text-slate-800" id="openTasksCount">0</p></div>
                </div>
                <div class="bg-white p-5 rounded-lg shadow-md flex items-center space-x-4 border border-slate-200">
                    <div class="p-3 bg-yellow-50 rounded-full text-yellow-600"><span class="material-icons-outlined text-3xl">warning</span></div>
                    <div><p class="text-sm text-slate-500">Offene Issues</p><p class="text-2xl font-bold text-slate-800" id="openIssuesCount">0</p></div>
                </div>
                <div class="bg-white p-5 rounded-lg shadow-md flex items-center space-x-4 border border-slate-200">
                    <div class="p-3 bg-green-50 rounded-full text-green-600"><span class="material-icons-outlined text-3xl">done_all</span></div>
                    <div><p class="text-sm text-slate-500">Erledigt (Gesamt)</p><p class="text-2xl font-bold text-slate-800" id="completedTasksCount">0</p></div>
                </div>
            </div>

            <div class="mb-6">
                <!-- Jinja Logik: Wer darf die erweiterten Tabs sehen? -->
                {% set show_admin_tabs = 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles',[]) or 'MAINTENANCEMANAGER' in session.get('roles',[]) or 'TESTENGINEER' in session.get('roles',[]) %}
                
                <nav class="flex space-x-6 border-b border-slate-200 overflow-x-auto">
                    <button class="tab-btn px-1 pb-3 text-sm font-medium text-primary border-b-2 border-primary whitespace-nowrap" onclick="switchTab('active', this)">Aktive Aufträge</button>
                    
                    {% if show_admin_tabs %}
                    <button class="tab-btn px-1 pb-3 text-sm font-medium text-slate-500 hover:text-slate-700 border-b-2 border-transparent hover:border-slate-300 whitespace-nowrap" onclick="switchTab('definitions', this)">Wartungsplan</button>
                    {% endif %}
                    
                    <button class="tab-btn px-1 pb-3 text-sm font-medium text-slate-500 hover:text-slate-700 border-b-2 border-transparent hover:border-slate-300 whitespace-nowrap" onclick="switchTab('logbook', this)">Logbuch</button>
                    
                    {% if show_admin_tabs %}
                    <button class="tab-btn px-1 pb-3 text-sm font-medium text-slate-500 hover:text-slate-700 border-b-2 border-transparent hover:border-slate-300 whitespace-nowrap" onclick="switchTab('history', this)">Historie / Archiv</button>
                    <button class="tab-btn px-1 pb-3 text-sm font-medium text-slate-500 hover:text-slate-700 border-b-2 border-transparent hover:border-slate-300 whitespace-nowrap" onclick="switchTab('companies', this)">Externe Firmen</button>
                    <button class="tab-btn px-1 pb-3 text-sm font-medium text-slate-500 hover:text-slate-700 border-b-2 border-transparent hover:border-slate-300 whitespace-nowrap" onclick="switchTab('analysis', this)">Analyse</button>
                    {% endif %}
                </nav>
            </div>

            <div id="tab-active" class="tab-content">
                <div class="flex flex-col gap-4 mb-6">
                    <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
                        <h2 class="text-xl font-bold text-slate-800">Wartungsübersicht</h2>
                        
                        <div class="flex items-center gap-3 flex-wrap">
                            <!-- Suche -->
                            <div class="relative">
                                <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 text-sm">search</span>
                                <input type="text" id="searchActiveOrders" oninput="applyFiltersActive()" placeholder="Aufträge suchen..." class="pl-9 pr-3 py-1.5 rounded-full border border-slate-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm w-40 transition-all focus:w-64">
                            </div>             
                            <!-- CHIPS -->
                            <div class="flex items-center gap-2 overflow-x-auto no-scrollbar">
                                
                                <!-- Typ -->
                                <div class="relative">
                                    <select id="filterTypeActive" onchange="applyFiltersActive()" class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                        <option value="all">Typ: Alle</option>
                                        <option value="M">Wartung</option>
                                        <option value="I">Issue</option>
                                        <option value="A">Maßnahme</option>
                                    </select>
                                    <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                                </div>
                
                                <!-- Prio -->
                                <div class="relative">
                                    <select id="filterPrioActive" onchange="applyFiltersActive()" class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                        <option value="all">Prio: Alle</option>
                                    </select>
                                    <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                                </div>
                                
                                <!-- Blocker Filter -->
                                <div class="relative">
                                    <select id="filterBlockerActive" onchange="applyFiltersActive()" class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                        <option value="all">Sperre: Alle</option>
                                        <option value="any">Ja (Alle Blocker)</option>
                                        <option value="unit">Nur Anlage (Unit)</option>
                                        <option value="hardware">Nur Hardware</option>
                                        <option value="none">Nein (Keine Sperre)</option>
                                    </select>
                                    <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                                </div>
                
                                <!-- Bereich -->
                                <div class="relative">
                                    <select id="filterAreaActive" onchange="applyFiltersActive()" class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                        <option value="all">Bereich: Alle</option>
                                    </select>
                                    <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                                </div>
                
                                <!-- Anlage -->
                                <div class="relative">
                                    <select id="filterPlantActive" onchange="applyFiltersActive()" class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                        <option value="all">Anlage: Alle</option>
                                    </select>
                                    <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                                </div>
                            </div>
              
                            <!-- Action Button -->
                            <button class="bg-yellow-500 text-white px-3 py-1.5 rounded-full text-xs font-medium hover:bg-yellow-600 flex items-center gap-1 shadow-sm ml-2" onclick="openAdHocContextModal()">
                                <span class="material-icons-outlined text-sm">report_problem</span> Issue melden
                            </button>
                        </div>
                    </div>
                </div>
                <div id="groupOverdue" class="mb-6 bg-white rounded-lg shadow border border-red-200 hidden">
                    <button class="w-full flex justify-between items-center p-4 bg-red-50 hover:bg-red-100 rounded-t-lg transition-colors text-left" onclick="toggleGroup('bodyOverdue', this)">
                        <div class="flex items-center gap-2 text-red-800 font-bold">
                            <span class="material-icons-outlined">warning</span> Überfällig / Kritisch / Unbewertet 
                            <span id="countOverdue" class="ml-2 px-2 py-0.5 bg-red-200 text-red-900 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-red-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyOverdue" class="p-0 hidden"></div>
                </div>
                
                <div id="groupUpcoming" class="mb-6 bg-white rounded-lg shadow border border-slate-200 hidden">
                    <button class="w-full flex justify-between items-center p-4 bg-slate-50 hover:bg-slate-100 rounded-t-lg transition-colors text-left" onclick="toggleGroup('bodyUpcoming', this)">
                        <div class="flex items-center gap-2 text-slate-700 font-bold">
                            <span class="material-icons-outlined text-blue-600">event</span> Anstehend (14 Tage) 
                            <span id="countUpcoming" class="ml-2 px-2 py-0.5 bg-slate-200 text-slate-700 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-slate-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyUpcoming" class="p-0 hidden"></div>
                </div>
                
                <div id="groupOutlook" class="mb-6 bg-white rounded-lg shadow border border-slate-200 hidden">
                    <button class="w-full flex justify-between items-center p-4 bg-white hover:bg-slate-50 rounded-t-lg transition-colors text-left border-b border-slate-100" onclick="toggleGroup('bodyOutlook', this)">
                        <div class="flex items-center gap-2 text-slate-600 font-semibold">
                            <span class="material-icons-outlined">date_range</span> Vorschau (Woche 3-8) 
                            <span id="countOutlook" class="ml-2 px-2 py-0.5 bg-slate-100 text-slate-600 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-slate-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyOutlook" class="p-0 hidden"></div>
                </div>
                
                <div id="groupFuture" class="mb-6 bg-white rounded-lg shadow border border-slate-200 hidden">
                    <button class="w-full flex justify-between items-center p-4 bg-white hover:bg-slate-50 rounded-t-lg transition-colors text-left border-b border-slate-100" onclick="toggleGroup('bodyFuture', this)">
                        <div class="flex items-center gap-2 text-slate-500 font-medium">
                            <span class="material-icons-outlined">update</span> Zukunft (> 8 Wochen) 
                            <span id="countFuture" class="ml-2 px-2 py-0.5 bg-slate-100 text-slate-600 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-slate-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyFuture" class="p-0 hidden"></div>
                </div>
                
                <div id="groupIssues" class="mb-6 bg-white rounded-lg shadow border border-orange-200 hidden">
                    <button class="w-full flex justify-between items-center p-4 bg-orange-50 hover:bg-orange-100 rounded-t-lg transition-colors text-left border-b border-orange-100" onclick="toggleGroup('bodyIssues', this)">
                        <div class="flex items-center gap-2 text-orange-800 font-bold">
                            <span class="material-icons-outlined">report_problem</span> Offene Mängel / Störungen 
                            <span id="countIssues" class="ml-2 px-2 py-0.5 bg-orange-200 text-orange-900 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-orange-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyIssues" class="p-0 hidden"></div>
                </div>
                <div id="emptyStateActive" class="hidden p-12 text-center bg-white rounded-lg border border-dashed border-slate-300"><span class="material-icons-outlined text-4xl text-slate-300 mb-3">task_alt</span><p class="text-slate-500">Keine offenen Aufträge gefunden.</p></div>
            </div>

            <div id="tab-history" class="tab-content hidden">
                <div class="bg-white rounded-lg shadow p-6 border border-slate-200">
                    <h2 class="text-lg font-bold text-slate-800 mb-4">Abgeschlossene Aufträge</h2>
                    <div class="overflow-x-auto custom-scrollbar">
                        <table class="w-full text-sm text-left"><thead class="text-xs text-gray-700 uppercase bg-gray-50"><tr><th class="px-6 py-3">ID</th><th class="px-6 py-3">Aufgabe</th><th class="px-6 py-3">Erledigt am</th><th class="px-6 py-3">Durch</th><th class="px-6 py-3 text-right">Protokoll</th></tr></thead><tbody id="historyOrdersBody" class="divide-y divide-slate-100"></tbody></table>
                    </div>
                </div>
            </div>

            <!-- TAB DEFINITIONS (Wartungsplan) -->
            <div id="tab-definitions" class="tab-content hidden">
                <div class="flex flex-col gap-4 mb-6">
                    <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
                        <h2 class="text-xl font-bold text-slate-800">Wartungsplan Definitionen</h2>
                        
                        <div class="flex items-center gap-3 flex-wrap">                            
                            <!-- CHIPS -->
                            <div class="flex items-center gap-2 overflow-x-auto no-scrollbar">
                                
                                <!-- Intervall -->
                                <div class="relative">
                                    <select id="filterIntervalDef" onchange="applyFiltersDefinitions()" class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                        <option value="all">Intervall: Alle</option>
                                    </select>
                                    <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                                </div>
                
                                <!-- Bereich -->
                                <div class="relative">
                                    <select id="filterAreaDef" onchange="applyFiltersDefinitions()" class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                        <option value="all">Bereich: Alle</option>
                                    </select>
                                    <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                                </div>
                
                                <!-- Anlage -->
                                <div class="relative">
                                    <select id="filterPlantDef" onchange="applyFiltersDefinitions()" class="filter-chip appearance-none bg-none pl-3 pr-8 py-1.5 rounded-full border border-slate-300 text-xs font-medium text-slate-600 bg-white hover:bg-slate-50 cursor-pointer focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 transition-colors">
                                        <option value="all">Anlage: Alle</option>
                                    </select>
                                    <span class="material-icons-outlined absolute right-2 top-1/2 -translate-y-1/2 text-slate-400 text-sm pointer-events-none">expand_more</span>
                                </div>
                            </div>
                            <!-- Suche -->
                            <div class="relative">
                                <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400 text-sm">search</span>
                                <input type="text" id="searchDefinitions" oninput="applyFiltersDefinitions()" placeholder="Pläne suchen..." class="pl-9 pr-3 py-1.5 rounded-full border border-slate-300 focus:ring-2 focus:ring-blue-500 focus:border-transparent text-sm w-40 transition-all focus:w-64">
                            </div>                
                            <!-- Action Button -->
                            <button class="bg-primary text-white px-3 py-1.5 rounded-full text-xs font-medium hover:bg-blue-600 flex items-center gap-1 shadow-sm ml-2" onclick="openDefinitionModal()">
                                <span class="material-icons-outlined text-sm">add</span> Neue Vorlage
                            </button>
                        </div>
                    </div>
                </div>
            
                <!-- Gruppe 1: Wöchentlich -->
                <div id="groupDefWeekly" class="mb-6 bg-white rounded-lg shadow border border-slate-200">
                    <button class="w-full flex justify-between items-center p-4 bg-slate-50 hover:bg-slate-100 rounded-t-lg transition-colors text-left border-b border-slate-200" onclick="toggleGroup('bodyDefWeekly', this)">
                        <div class="flex items-center gap-2 text-slate-700 font-bold">
                            <span class="material-icons-outlined text-slate-400">date_range</span> Wöchentliche Wartungen 
                            <span id="countDefWeekly" class="ml-2 px-2 py-0.5 bg-slate-200 text-slate-700 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-slate-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyDefWeekly" class="p-0 overflow-x-auto custom-scrollbar hidden"></div>
                </div>
                
                <!-- Gruppe 2: Monatlich -->
                <div id="groupDefMonthly" class="mb-6 bg-white rounded-lg shadow border border-slate-200">
                    <button class="w-full flex justify-between items-center p-4 bg-slate-50 hover:bg-slate-100 rounded-t-lg transition-colors text-left border-b border-slate-200" onclick="toggleGroup('bodyDefMonthly', this)">
                        <div class="flex items-center gap-2 text-slate-700 font-bold">
                            <span class="material-icons-outlined text-slate-400">calendar_month</span> Monatliche Wartungen 
                            <span id="countDefMonthly" class="ml-2 px-2 py-0.5 bg-slate-200 text-slate-700 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-slate-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyDefMonthly" class="p-0 overflow-x-auto custom-scrollbar hidden"></div>
                </div>
                
                <!-- Gruppe 3: Jährlich -->
                <div id="groupDefYearly" class="mb-6 bg-white rounded-lg shadow border border-slate-200">
                    <button class="w-full flex justify-between items-center p-4 bg-slate-50 hover:bg-slate-100 rounded-t-lg transition-colors text-left border-b border-slate-200" onclick="toggleGroup('bodyDefYearly', this)">
                        <div class="flex items-center gap-2 text-slate-700 font-bold">
                            <span class="material-icons-outlined text-slate-400">event_repeat</span> Jährliche Wartungen 
                            <span id="countDefYearly" class="ml-2 px-2 py-0.5 bg-slate-200 text-slate-700 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-slate-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyDefYearly" class="p-0 overflow-x-auto custom-scrollbar hidden"></div>
                </div>
                
                <!-- Gruppe 4: Sonstige -->
                <div id="groupDefOther" class="mb-6 bg-white rounded-lg shadow border border-slate-200 hidden">
                    <button class="w-full flex justify-between items-center p-4 bg-slate-50 hover:bg-slate-100 rounded-t-lg transition-colors text-left border-b border-slate-200" onclick="toggleGroup('bodyDefOther', this)">
                        <div class="flex items-center gap-2 text-slate-700 font-bold">
                            <span class="material-icons-outlined text-slate-400">help_outline</span> Sonstige Intervalle 
                            <span id="countDefOther" class="ml-2 px-2 py-0.5 bg-slate-200 text-slate-700 text-xs rounded-full">0</span>
                        </div>
                        <span class="material-icons-outlined text-slate-400 transform rotate-180 transition-transform duration-200">expand_less</span>
                    </button>
                    <div id="bodyDefOther" class="p-0 overflow-x-auto custom-scrollbar hidden"></div>
                </div>
            </div>

            <div id="tab-companies" class="tab-content hidden">
                <div class="bg-white rounded-lg shadow p-6 border border-slate-200">
                    <div class="flex justify-between items-center mb-4">
                        <h2 class="text-lg font-bold text-slate-800">Externe Dienstleister</h2>
                        <button class="bg-primary text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-600 flex items-center gap-2 shadow-sm" onclick="openCompanyModal()">
                            <span class="material-icons-outlined text-base">add</span> Neue Firma
                        </button>
                    </div>
                    <div class="overflow-x-auto custom-scrollbar">
                        <table class="w-full text-sm text-left">
                            <thead class="text-xs text-gray-700 uppercase bg-gray-50">
                                <tr>
                                    <th class="px-6 py-3">Name</th>
                                    <th class="px-6 py-3">Ansprechpartner</th>
                                    <th class="px-6 py-3">Kontakt (Mail/Tel)</th>
                                    <th class="px-6 py-3">Leistungen</th>
                                    <th class="px-6 py-3 text-right">Aktionen</th>
                                </tr>
                            </thead>
                            <tbody id="companiesBody" class="divide-y divide-slate-100"></tbody>
                        </table>
                    </div>
                </div>
            </div>
            <div id="tab-analysis" class="tab-content hidden bg-white rounded-lg p-6"> <!-- bg-white und padding hier hinzugefügt für den Wrapper-Look -->
                
                <!-- Header -->
                <div class="mb-6">
                    <h2 class="text-2xl font-bold text-slate-900 mb-2">Maintenance Intelligence</h2> <!-- mb-2 für Abstand -->
                    <p class="text-sm text-slate-600">Datenbasierte Einblicke in Wartung und Betrieb.</p>
                </div>
            
                <!-- Sub-Navigation (Linksbündig, wie im Screenshot) -->
                <div class="mb-6">
                    <div class="inline-flex rounded-md shadow-sm bg-slate-100 p-1">
                        <button onclick="switchAnalysisView('operational', this)" class="analyse-toggle-btn active rounded-md px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-white">Operativ</button>
                        <button onclick="switchAnalysisView('performance', this)" class="analyse-toggle-btn rounded-md px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-white">Performance</button>
                        <button onclick="switchAnalysisView('history', this)" class="analyse-toggle-btn rounded-md px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-white">Historie</button> <!-- NEU -->
                        <button onclick="switchAnalysisView('partner', this)" class="analyse-toggle-btn rounded-md px-3 py-1.5 text-sm font-medium text-slate-700 hover:bg-white">Partner</button>
                    </div>
                </div>
            
                <!-- VIEW 1: OPERATIV -->
                <div id="view-operational" class="analysis-subview">
                    <!-- Reihe 1: KPI Pulse (Reduziert auf 2) -->
                    <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
                        <div class="bg-white p-5 rounded-xl shadow-sm border border-slate-100 flex items-center gap-4">
                            <div class="w-12 h-12 rounded-full bg-blue-50 flex items-center justify-center text-blue-500"><span class="material-icons-outlined">timer</span></div>
                            <div><p class="text-xs font-bold text-slate-400 uppercase tracking-wider">Ø Lösungszeit (Issues)</p><p class="text-2xl font-bold text-slate-800" id="kpi-avg-time">0 Tage</p></div>
                        </div>
                        <div class="bg-white p-5 rounded-xl shadow-sm border border-slate-100 flex items-center gap-4">
                            <div class="w-12 h-12 rounded-full bg-green-50 flex items-center justify-center text-green-500"><span class="material-icons-outlined">check_circle</span></div>
                            <div><p class="text-xs font-bold text-slate-400 uppercase tracking-wider">Wartungs-Compliance (On-Time)</p><p class="text-2xl font-bold text-slate-800" id="kpi-ontime">0%</p></div>
                        </div>
                    </div>
                
                    <!-- Reihe 2: Tabelle und Product Type Donut -->
                    <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
                        <!-- Asset Hotspots Tabelle (2/3) -->
                        <div class="lg:col-span-2 bg-white p-6 rounded-xl shadow-sm border border-slate-100 flex flex-col">
                            <!-- ... Header (Suche) bleibt gleich, kopiere ihn rein oder lass ihn ... -->
                            <div class="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-4 gap-3">
                                <h3 class="text-lg font-bold text-slate-800 flex items-center gap-2">
                                    <span class="material-icons-outlined text-orange-500">inventory_2</span> 
                                    Hardware Störungen
                                </h3>
                                <div class="relative">
                                    <span class="material-icons-outlined absolute left-2.5 top-1/2 -translate-y-1/2 text-slate-400 text-sm">search</span>
                                    <input type="text" id="asset-search-input" oninput="renderAssetTable()" placeholder="Asset suchen..." class="pl-8 pr-3 py-1.5 text-sm border border-slate-200 rounded-md focus:ring-blue-500 focus:border-blue-500 w-48 transition-all focus:w-64">
                                </div>
                            </div>
                            <div class="overflow-x-auto flex-grow custom-scrollbar">
                                <table class="w-full text-sm text-left">
                                    <thead class="text-xs text-slate-500 uppercase bg-slate-50 border-b border-slate-200">
                                        <tr>
                                            <th class="px-4 py-3 font-bold">Asset / Komponente</th>
                                            <th class="px-4 py-3 font-bold text-center">Issues</th>
                                            <th class="px-4 py-3 font-bold text-center">Kritisch</th>
                                            <th class="px-4 py-3 font-bold text-right">Letzte Meldung</th>
                                        </tr>
                                    </thead>
                                    <tbody id="asset-hotspot-table-body" class="divide-y divide-slate-100"></tbody>
                                </table>
                                <div id="asset-table-footer" class="mt-3 text-xs text-center text-slate-400 italic hidden"></div>
                            </div>
                        </div>
                    
                        <!-- NEU: Product Type Chart (1/3) -->
                        <div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100">
                            <h3 class="text-lg font-bold text-slate-800 mb-4">Issues nach Product Type</h3>
                            <div class="relative h-64 w-full flex justify-center">
                                <canvas id="chart-product-types"></canvas> <!-- Neue ID -->
                            </div>
                            <p class="text-xs text-slate-400 text-center mt-4">Basiert auf den verknüpften Assets der Issues.</p>
                        </div>
                    </div>
                    
                    <!-- Reihe 3: Bereichs-Performance (Tabelle & Donut) -->
                    <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
                        <!-- Tabelle (2/3) -->
                        <div class="lg:col-span-2 bg-white p-6 rounded-xl shadow-sm border border-slate-100 flex flex-col">
                            <h3 class="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2">
                                <span class="material-icons-outlined text-blue-500">category</span> 
                                Performance nach Bereich
                            </h3>
                            <div class="overflow-x-auto flex-grow custom-scrollbar">
                                <table class="w-full text-sm text-left">
                                    <thead class="text-xs text-slate-500 uppercase bg-slate-50 border-b border-slate-200">
                                        <tr>
                                            <th class="px-4 py-3 font-bold">Bereich</th>
                                            <th class="px-4 py-3 font-bold text-center">Issues</th>
                                            <th class="px-4 py-3 font-bold text-center">Kritisch</th>
                                            <th class="px-4 py-3 font-bold text-center">Ø Zeit</th>
                                            <th class="px-4 py-3 font-bold w-1/4">On-Time</th>
                                        </tr>
                                    </thead>
                                    <tbody id="area-performance-table-body" class="divide-y divide-slate-100 text-slate-700"></tbody>
                                </table>
                            </div>
                        </div>
                    
                        <!-- Donut (1/3) -->
                        <div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100">
                            <h3 class="text-lg font-bold text-slate-800 mb-4">Issues nach Bereich</h3>
                            <div class="relative h-64 w-full flex justify-center">
                                <canvas id="chart-areas"></canvas> <!-- Alte ID wiederverwendet -->
                            </div>
                            <p class="text-xs text-slate-400 text-center mt-4">Anteil der gemeldeten Issues.</p>
                        </div>
                    </div>
                </div>
                
                <!-- VIEW 2: PERFORMANCE -->
                <div id="view-performance" class="analysis-subview hidden">
                    <!-- HIER IST DIE ÄNDERUNG: lg:grid-cols-2 sorgt für das Nebeneinander -->
                    <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
                        
                        <!-- Trend (Links) -->
                        <div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100">
                            <h3 class="text-lg font-bold text-slate-800 mb-4">Pünktlichkeits-Trend (12 Monate)</h3>
                            <!-- Höhe auf 450px fixiert für Symmetrie -->
                            <div class="relative h-[450px] w-full">
                                <canvas id="chart-trend"></canvas>
                            </div>
                        </div>
                
                        <!-- Intervall (Rechts) -->
                        <div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100">
                            <h3 class="text-lg font-bold text-slate-800 mb-4">Intervall-Treue (Soll vs. Ist)</h3>
                            <p class="text-xs text-slate-500 mb-4">Punkte oberhalb der Linie zeigen verspätete Wartungen.</p>
                            <!-- Höhe auf 450px fixiert für Symmetrie -->
                            <div class="relative h-[450px] w-full">
                                <canvas id="chart-intervals"></canvas>
                            </div>
                        </div>
                
                    </div>
                </div>
         
                <!-- VIEW: HISTORIE (Nach Betriebsbereich) -->
                <div id="view-history" class="analysis-subview hidden">
                    <div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-8">
                        
                        <!-- Tabelle (2/3) -->
                        <div class="lg:col-span-2 bg-white p-6 rounded-xl shadow-sm border border-slate-100 flex flex-col">
                            <h3 class="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2">
                                <span class="material-icons-outlined text-purple-500">history</span> 
                                Historie nach Betriebsbereich
                            </h3>
                            <div class="overflow-x-auto flex-grow custom-scrollbar">
                                <table class="w-full text-sm text-left">
                                    <thead class="text-xs text-slate-500 uppercase bg-slate-50 border-b border-slate-200">
                                        <tr>
                                            <th class="px-4 py-3 font-bold">Betriebsbereich (Unit)</th>
                                            <th class="px-4 py-3 font-bold text-center">Aufträge (Gesamt)</th>
                                            <th class="px-4 py-3 font-bold text-center">Davon Issues</th>
                                            <th class="px-4 py-3 font-bold text-right">Letzter Abschluss</th>
                                        </tr>
                                    </thead>
                                    <tbody id="history-unit-table-body" class="divide-y divide-slate-100"></tbody>
                                </table>
                            </div>
                        </div>
                
                        <!-- Donut (1/3) -->
                        <div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100">
                            <h3 class="text-lg font-bold text-slate-800 mb-4">Arbeitsverteilung (Bereiche)</h3>
                            <div class="relative h-64 w-full flex justify-center">
                                <canvas id="chart-history-units"></canvas>
                            </div>
                            <p class="text-xs text-slate-400 text-center mt-4">Anteil abgeschlossener Aufträge.</p>
                        </div>
                    </div>
                </div>            
                <!-- VIEW 3: PARTNER -->
                <div id="view-partner" class="analysis-subview hidden">
                    <div class="bg-white p-6 rounded-xl shadow-sm border border-slate-100">
                        <h3 class="text-lg font-bold text-slate-800 mb-4 flex items-center gap-2"><span class="material-icons-outlined text-indigo-500">business</span> Externe Partner Performance</h3>
                        <div class="overflow-x-auto">
                            <table class="w-full text-sm text-left">
                                <thead class="text-xs text-slate-500 uppercase bg-slate-50 border-b border-slate-200">
                                    <tr>
                                        <th class="px-6 py-3 font-bold">Firma</th>
                                        <th class="px-6 py-3 font-bold text-center">Aufträge (12M)</th>
                                        <th class="px-6 py-3 font-bold text-center">Ø Dauer</th>
                                        <th class="px-6 py-3 font-bold w-1/3">Pünktlichkeit</th>
                                        <th class="px-6 py-3 font-bold text-center">Aktuell Offen</th>
                                    </tr>
                                </thead>
                                <tbody id="partner-table-body" class="divide-y divide-slate-100 text-slate-700"></tbody>
                            </table>
                        </div>
                    </div>
                </div>
            </div>
                   <!-- TAB LOGBOOK -->
                <div id="tab-logbook" class="tab-content hidden">
                    <div class="flex justify-between items-center mb-6">
                        <h2 class="text-xl font-bold text-slate-800">Betriebstagebuch</h2>
                        <div class="flex gap-3">
                             <div class="relative">
                                <span class="material-icons-outlined absolute left-2.5 top-1/2 -translate-y-1/2 text-slate-400 text-sm">search</span>
                                <input type="text" id="logbook-search" oninput="renderLogbook()" placeholder="Einträge durchsuchen..." class="pl-8 pr-3 py-2 text-sm border border-slate-200 rounded-lg focus:ring-blue-500 focus:border-blue-500 w-64">
                            </div>
                            <button class="bg-indigo-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-indigo-700 flex items-center gap-2 shadow-sm" onclick="openCreateLogModal()">
                                <span class="material-icons-outlined text-base">edit_note</span> Neuer Eintrag
                            </button>
                        </div>
                    </div>
                    
                    <div id="logbook-container" class="space-y-6">
                        <!-- Wird per JS gefüllt (Monats-Gruppen) -->
                    </div>
                </div>
                
                <!-- LOG ENTRY MODAL (Create & Edit) -->
                <div id="createLogModal" class="modal hidden fixed inset-0 z-[60] flex items-center justify-center bg-gray-900 bg-opacity-50">
                    <div class="modal-backdrop" onclick="closeCreateLogModal()"></div>
                    <div class="modal-content max-w-lg">
                        <div class="flex justify-between items-center p-6 border-b border-slate-200 bg-white rounded-t-lg">
                            <h3 class="text-xl font-bold text-slate-800" id="logModalTitle">Logbucheintrag</h3>
                            <div class="flex gap-2">
                                <button id="btnDeleteLog" onclick="deleteLogEntry()" class="text-slate-400 hover:text-red-600 p-1.5 rounded-full hover:bg-red-50 transition-colors hidden" title="Löschen">
                                    <span class="material-icons-outlined">delete</span>
                                </button>
                                <button onclick="closeCreateLogModal()" class="text-slate-400 hover:text-slate-600 p-1.5 rounded-full hover:bg-slate-100 transition-colors">
                                    <span class="material-icons-outlined">close</span>
                                </button>
                            </div>
                        </div>
                        <div class="p-6 bg-slate-50 overflow-y-auto max-h-[70vh]">
                            <form id="createLogForm" class="space-y-4">
                                <input type="hidden" id="logId"> <!-- Hidden ID für Edit -->
                                
                                <div>
                                    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Titel / Ereignis*</label>
                                    <input class="w-full border-slate-300 rounded-md text-sm" id="logTitle" required>
                                </div>
                                <div class="grid grid-cols-2 gap-4">
                                    <div>
                                        <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Datum</label>
                                        <input type="date" class="w-full border-slate-300 rounded-md text-sm" id="logDate">
                                    </div>
                                    <div>
                                        <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Bereich</label>
                                        <select class="w-full border-slate-300 rounded-md text-sm bg-white" id="logCategory"></select>
                                    </div>
                                        <div class="col-span-2">
                                        <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Betriebsbereich (Anlage)</label>
                                        <input list="facilityUnitList" class="w-full border-slate-300 rounded-md text-sm" id="logPlantIndex" placeholder="z.B. 1.1 Pumpenraum">
                                    </div>
                                </div>
                                <div>
                                    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Beschreibung</label>
                                    <textarea class="w-full border-slate-300 rounded-md text-sm" id="logDesc" rows="6"></textarea>
                                </div>
                                
                                <!-- Info-Bereich (nur bei Edit sichtbar) -->
                                <div id="logMetaInfo" class="hidden pt-4 mt-2 border-t border-slate-200 text-xs text-slate-400">
                                    Erstellt von <span id="logCreatedBy"></span> am <span id="logCreatedAt"></span>
                                </div>
                            </form>
                        </div>
                        <div class="p-4 border-t border-slate-200 bg-white flex justify-end gap-3 rounded-b-lg">
                            <button class="px-4 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded" onclick="closeCreateLogModal()">Abbrechen</button>
                            <button id="btnSaveLog" class="px-4 py-2 text-sm bg-indigo-600 text-white rounded hover:bg-indigo-700 font-medium shadow-sm flex items-center gap-2" onclick="submitLogEntry()">
                                <span class="material-icons-outlined text-sm">save</span> Speichern
                            </button>
                        </div>
                    </div>
                </div>
    </main>
</div>

<!-- COMPANY MODAL -->
<div id="companyModal" class="modal hidden fixed inset-0 z-50 flex items-center justify-center bg-gray-900 bg-opacity-50">
    <div class="modal-backdrop" onclick="closeCompanyModal()"></div>
    <div class="modal-content max-w-lg w-full">
        <div class="flex justify-between items-center p-6 border-b border-slate-200 bg-white rounded-t-lg">
            <h3 class="text-lg font-bold text-slate-800" id="compModalTitle">Firma anlegen</h3>
            <button onclick="closeCompanyModal()" class="text-slate-400 hover:text-slate-600"><span class="material-icons-outlined">close</span></button>
        </div>
        <div class="p-6 bg-slate-50">
            <form id="companyForm" class="space-y-4">
                <input type="hidden" id="compId">
                <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Firmenname*</label><input class="w-full border-slate-300 rounded-md text-sm" id="compName" required placeholder="z.B. Müller Service GmbH"></div>
                <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Ansprechpartner</label><input class="w-full border-slate-300 rounded-md text-sm" id="compContact" placeholder="z.B. Hr. Schmidt"></div>
                <div class="grid grid-cols-2 gap-4"><div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">E-Mail</label><input class="w-full border-slate-300 rounded-md text-sm" id="compEmail" type="email"></div><div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Telefon</label><input class="w-full border-slate-300 rounded-md text-sm" id="compPhone" type="tel"></div></div>
                <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Leistungen / Notizen</label><textarea class="w-full border-slate-300 rounded-md text-sm" id="compServices" rows="3" placeholder="z.B. Kalibrierung, Elektroarbeiten..."></textarea></div>
            </form>
        </div>
        <div class="p-4 border-t border-slate-200 bg-white flex justify-end gap-3 rounded-b-lg"><button class="px-4 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded" onclick="closeCompanyModal()">Abbrechen</button><button class="px-4 py-2 text-sm bg-primary text-white rounded hover:bg-blue-700 font-medium shadow-sm" onclick="saveCompany()">Speichern</button></div>
    </div>
</div>
<!-- AD-HOC CONTEXT MODAL -->
<div id="adHocContextModal" class="modal hidden fixed inset-0 z-[80] flex items-center justify-center bg-gray-900 bg-opacity-70 backdrop-blur-sm">
    <div class="modal-backdrop" onclick="closeAdHocContextModal()"></div>
    <div class="modal-content max-w-2xl w-full">
        <div class="flex justify-between items-center p-6 border-b border-slate-200 bg-white rounded-t-lg">
            <h3 class="text-xl font-bold text-slate-800 flex items-center gap-2">
                <span class="material-icons-outlined text-blue-600">link</span> Worauf bezieht sich das Problem?
            </h3>
            <button onclick="closeAdHocContextModal()" class="text-slate-400 hover:text-slate-600"><span class="material-icons-outlined">close</span></button>
        </div>
        <div class="p-6 overflow-y-auto bg-slate-50">
            <div class="relative mb-4">
                <span class="material-icons-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400">search</span>
                <input type="text" id="contextSearchInput" placeholder="Hardware-Asset nach S/N, P/N oder Name suchen..." class="w-full pl-10 pr-4 py-3 border border-slate-300 rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500 shadow-sm">
            </div>
            <div id="contextSearchResults" class="bg-white border border-slate-200 rounded-lg max-h-64 overflow-y-auto custom-scrollbar">
                <!-- Suchergebnisse werden hier eingefügt -->
                <p class="p-8 text-center text-slate-400 text-sm">Bitte geben Sie einen Suchbegriff ein.</p>
            </div>
            <div class="mt-6 text-center">
                 <p class="text-xs text-slate-400 mb-2">Oder, falls nicht zuordenbar:</p>
                 <button onclick="createUnlinkedIssue()" class="text-sm text-slate-600 hover:text-primary font-medium px-4 py-2 bg-white border border-slate-300 rounded-lg shadow-sm hover:border-primary">
                    Allgemeines Problem ohne Asset-Bezug melden
                </button>
            </div>
        </div>
    </div>
</div>
<!-- WORK ORDER MODAL (REDESIGNED with EDIT MODE) -->
<div id="workOrderModal" class="modal hidden fixed inset-0 z-50 bg-gray-900 bg-opacity-60 flex items-center justify-center backdrop-blur-sm">
    <div class="modal-content bg-white w-full max-w-6xl h-[90vh] rounded-xl shadow-2xl flex flex-col overflow-hidden">

        <!-- HEADER (FIXED) -->
        <div class="border-b border-slate-200 bg-white sticky top-0 z-20 flex-shrink-0">
            <!-- ZEILE 1: Navigation / Breadcrumbs -->
            <div class="flex justify-between items-center px-6 py-2 bg-slate-50 text-xs border-b border-slate-100">
                <div id="navParentContainer" class="flex items-center text-slate-500 truncate mr-4"></div>
                <div id="navChildrenContainer" class="flex items-center gap-2 overflow-x-auto no-scrollbar"></div>
            </div>

            <!-- ZEILE 2: Haupt-Titel & Meta -->
            <div class="px-6 py-4">
                <div class="flex justify-between items-start mb-2">
                    <div class="flex items-center gap-3">
                        <!-- ANZEIGE-MODUS TITEL -->
                        <h2 id="woTitle" class="text-xl font-bold text-slate-800 leading-tight"></h2>
                        <!-- BEARBEITUNGS-MODUS TITEL -->
                        <div id="woEditTitleContainer" class="hidden">
                            <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Titel (nur zur Info)</label>
                            <input id="woEditTitle" type="text" readonly class="w-full bg-slate-100 border-slate-300 rounded-md text-sm font-bold text-slate-600">
                        </div>
                        <span id="woStatusBadge" class="px-2 py-0.5 rounded text-xs font-bold uppercase tracking-wide"></span>
                        <div id="woReviewIcon" class="hidden text-purple-600" title="4-Augen-Prinzip (Review) erforderlich"><span class="material-icons-outlined text-lg">visibility</span></div>
                        <span id="woPrioBadge"></span>
                        <div id="woBlockerBadges" class="flex gap-2"></div>
                    </div>
                    <div class="flex items-center gap-2">
                        <button id="btnEditOrder" onclick="switchToWoEditMode()" class="text-slate-400 hover:text-blue-600 p-1.5 rounded-full hover:bg-blue-50 transition-colors" title="Bearbeiten"><span class="material-icons-outlined">edit</span></button>
                        <button id="btnDeleteOrder" onclick="deleteCurrentOrder()" class="text-slate-400 hover:text-red-600 p-1.5 rounded-full hover:bg-red-50 transition-colors" title="Löschen"><span class="material-icons-outlined">delete_outline</span></button>
                        <button onclick="closeWorkOrderModal()" class="text-slate-400 hover:text-slate-600 p-1.5 rounded-full hover:bg-slate-100 transition-colors"><span class="material-icons-outlined">close</span></button>
                    </div>
                </div>
                <div class="flex flex-wrap items-center gap-x-6 gap-y-2 text-sm text-slate-600">
                     <div class="flex items-center gap-1.5 text-slate-500" title="ID"><span class="font-mono text-xs border border-slate-200 px-1 rounded bg-slate-50" id="woId"></span></div>
                     <div class="flex items-center gap-1.5" title="Fälligkeit">
                        <span class="material-icons-outlined text-slate-400 text-base">event</span>
                        <!-- ANZEIGE-MODUS DATUM -->
                        <span id="woDateDisplay"></span>

                        <!-- NEU: Separater Span für Abschlussdatum -->
                        <span id="woCompletionDisplay" class="hidden text-green-700 font-medium ml-1 flex items-center gap-1"></span>
                    
                        <!-- BEARBEITUNGS-MODUS DATUM (Start) -->
                        <div id="woEditDateContainer" class="hidden flex items-center gap-1">
                            <input type="date" id="woEditDate" class="border-slate-300 rounded-md text-sm p-1">
                            <select id="woEditStartShift" class="border-slate-300 rounded-md text-sm p-1 bg-white">
                                <option value="AM">AM</option>
                                <option value="PM">PM</option>
                            </select>
                        </div>
                    </div>
                     <div class="flex items-center gap-1.5 min-w-0">
                        <span class="material-icons-outlined text-slate-400 text-base flex-shrink-0">place</span>
                        
                        <!-- View Mode -->
                        <div id="woLocationView" class="flex items-center gap-1 truncate">
                            <span id="woTestbench"></span> 
                            <span id="woIndex" class="font-bold text-slate-700"></span>
                            
                            <!-- HIER WIEDER EINFÜGEN: -->
                            <span class="text-slate-300 mx-1 hidden" id="woTopicSeparator">|</span>
                            <span id="woTopic" class="text-blue-600 font-medium"></span>
                        </div>

                        <!-- Edit Mode (NEU) -->
                        <div id="woLocationEdit" class="hidden flex-grow max-w-md relative">
                            <input list="facilityUnitList" id="woEditPlantIndex" 
                                   onchange="onFacilityUnitSelected(this, 'woEditFacilityUnitId')"
                                   class="w-full border-slate-300 rounded-md text-xs py-1 px-2 focus:ring-2 focus:ring-blue-100 focus:border-blue-400 bg-white shadow-sm transition-all" 
                                   placeholder="Betriebsbereich (z.B. 1.1)">
                            <input type="hidden" id="woEditFacilityUnitId">
                        </div>
                     </div>
                </div>
            </div>
        </div>

        <!-- BODY (SPLIT VIEW) -->
        <div class="flex-grow flex overflow-hidden">
            <div class="w-7/12 border-r border-slate-200 overflow-y-auto custom-scrollbar p-6 bg-white space-y-8">
                <!-- ERSETZE diesen Block in der linken Spalte des workOrderModal -->
                <div>
                    <h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">Beschreibung / Auftrag</h3>
                    <div class="p-4 bg-slate-50 border border-slate-200 rounded-lg shadow-sm">
                        <div id="woAssetTags" class="flex flex-wrap empty:hidden"></div>
                        
                        <!-- ANZEIGE-MODUS (VIEW) -->
                        <div id="woDescriptionViewContainer" class="text-sm text-slate-700 leading-relaxed gap-1.5 mt-3 pt-3 border-t border-slate-200/60 whitespace-pre-wrap">
                            <!-- Beschreibungstext wird hier von JS eingefügt -->
                        </div>
                
                        <!-- BEARBEITUNGS-MODUS (EDIT) -->
                        <div id="woEditDetailsContainer" class="hidden space-y-4 mt-3 pt-3 border-t border-slate-200/60">
                            <div>
                                <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Beschreibung</label>
                                <textarea id="woEditDescription" class="w-full border-slate-300 rounded-md text-sm" rows="4"></textarea>
                            </div>
                            <!-- NEU: Abschluss-Daten bearbeiten (Nur für Completed Orders) -->
                            <div id="woEditCompletionContainer" class="hidden mb-4 p-3 bg-green-50 border border-green-200 rounded-md">
                                <label class="block text-xs font-bold text-green-800 uppercase mb-2">Abschlussdatum korrigieren</label>
                                <div class="flex items-center gap-2">
                                    <input type="date" id="woEditCompletedDate" class="border-green-300 rounded-md text-sm p-1 w-full text-green-900 focus:ring-green-500 focus:border-green-500">
                                    <select id="woEditCompletedShift" class="border-green-300 rounded-md text-sm p-1 bg-white text-green-900 focus:ring-green-500 focus:border-green-500">
                                        <option value="AM">AM</option>
                                        <option value="PM">PM</option>
                                    </select>
                                </div>
                                <p class="text-[10px] text-green-700 mt-1">Ändert die Historie und gibt blockierte Zeiten in der Timeline frei.</p>
                            </div>
                            <!-- Diese Felder sind nur für Issues & Actions relevant -->
                            <div id="woEditMetaFields" class="grid grid-cols-2 gap-4">
                                <div>
                                    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Priorität</label>
                                    <select class="w-full border-slate-300 rounded-md text-sm bg-white" id="woEditPriority">
                                        <option value="Low">Niedrig</option>
                                        <option value="Medium">Mittel</option>
                                        <option value="High">Hoch</option>
                                        <option value="Critical">Kritisch</option>
                                    </select>
                                </div>
                                <div>
                                    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Bereich</label>
                                    <select class="w-full border-slate-300 rounded-md text-sm bg-white" id="woEditCategory"></select>
                                </div>
                            </div>
                            <!-- NEUER BLOCK FÜR ASSET-BEARBEITUNG -->
                            <div id="woEditAssetContainer" class="hidden">
                                <label class="block text-xs font-bold text-slate-500 uppercase mb-2">Betroffene Assets</label>
                                <div class="relative">
                                    <div class="tag-input-wrapper" id="woEditAssetTags">
                                        <input type="text" id="woEditAssetInput" class="tag-input-field" placeholder="Asset hinzufügen...">
                                    </div>
                                    <div id="woEditAssetDropdown" class="tag-dropdown"></div>
                                </div>
                            </div>
                            <div id="woEditBlockerSettings" class="hidden mt-4 pt-4 border-t border-slate-100 bg-red-50/50 p-3 rounded-md">
                                <label class="block text-xs font-bold text-red-800 uppercase mb-3">Betriebs-Sperren & Sicherheit</label>
                                
                                <!-- 1. Unit Blocker -->
                                <label class="flex items-center gap-2 cursor-pointer mb-4">
                                    <input type="checkbox" id="woEditUnitBlocker" class="rounded border-red-300 text-red-600 focus:ring-red-500 w-4 h-4">
                                    <div>
                                        <span class="text-sm font-bold text-slate-700">Anlage sperren (Unit Blocker)</span>
                                        <p class="text-[10px] text-slate-500">Raum wird im 3D-Modell rot markiert.</p>
                                    </div>
                                </label>
                            
                                <!-- 2. Hardware Blocker Liste -->
                                <div>
                                    <span class="text-xs font-semibold text-slate-600 block mb-1">Gesperrte Hardware auswählen:</span>
                                    <div id="woEditBlockedAssetsList" class="space-y-1 max-h-32 overflow-y-auto custom-scrollbar border border-slate-200 bg-white rounded p-2">
                                        <!-- Wird per JS gefüllt -->
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
                <div id="woChecklistSection"><h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-3">Checkliste</h3><div id="woChecklistContainer" class="space-y-2"></div></div>
                <div class="grid grid-cols-2 gap-6"><div id="woConsumablesSection"><h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">Material</h3><div id="woConsumables" class="flex flex-wrap gap-2 text-sm text-slate-600"></div></div><div><h3 class="text-xs font-bold text-slate-400 uppercase tracking-wider mb-2">Dokumente</h3><div id="woAttachmentsView" class="space-y-2"></div></div></div>
            </div>
            <!-- RECHTE SPALTE bleibt unverändert... -->
            <div class="w-5/12 flex flex-col bg-slate-50">
                 <div id="woQuickActions" class="p-3 border-b border-slate-200 bg-white flex items-center justify-between gap-2 shadow-sm z-10"><div class="flex gap-2 w-full"><button id="btnTriage" onclick="openTriageModal()" class="hidden flex-1 bg-indigo-600 text-white px-3 py-2 rounded text-xs font-bold uppercase tracking-wide hover:bg-indigo-700 shadow-sm flex items-center justify-center gap-2 transition-colors"><span class="material-icons-outlined text-base">rule</span> Bewerten</button><button id="btnCreateIssue" onclick="createLinkedIssue()" class="flex-1 bg-amber-50 text-amber-700 border border-amber-200 px-3 py-2 rounded text-xs font-bold uppercase tracking-wide hover:bg-amber-100 flex items-center justify-center gap-2 transition-colors"><span class="material-icons-outlined text-base">report_problem</span> Issue</button><button id="btnCreateAction" onclick="createLinkedAction()" class="flex-1 bg-blue-50 text-blue-700 border border-blue-200 px-3 py-2 rounded text-xs font-bold uppercase tracking-wide hover:bg-blue-100 flex items-center justify-center gap-2 transition-colors"><span class="material-icons-outlined text-base">build_circle</span> Maßnahme</button></div></div>
                 <div class="flex-grow overflow-y-auto custom-scrollbar p-4 space-y-4" id="auditTrailContainer"></div>
                 <div class="p-4 bg-white border-t border-slate-200 shadow-[0_-4px_6px_-1px_rgba(0,0,0,0.05)] z-10">
                    <div class="mb-3"><textarea id="logNoteInput" class="w-full border-slate-300 rounded-lg text-sm focus:ring-primary focus:border-primary bg-slate-50 p-3 min-h-[80px]" placeholder="Kommentar oder Ergebnis protokollieren..."></textarea><div class="flex justify-between items-center mt-2"><button class="text-xs text-slate-400 hover:text-primary flex items-center gap-1 transition-colors"><span class="material-icons-outlined text-base">attach_file</span> Datei</button><button onclick="addLogEntry()" class="bg-slate-800 text-white px-4 py-1.5 rounded-md text-xs font-bold uppercase tracking-wide hover:bg-slate-700 transition-colors">Senden</button></div></div>
                    <!-- NEUE BUTTONS FÜR BEARBEITUNGSMODUS -->
                    <div id="woEditButtons" class="hidden pt-3 border-t border-slate-100 grid grid-cols-2 gap-4">
                        <button onclick="switchToWoViewMode()" class="w-full py-3 rounded-lg border-2 border-slate-200 bg-slate-50 text-slate-700 font-bold uppercase tracking-wider flex items-center justify-center gap-2 hover:bg-slate-100">
                            <span class="material-icons-outlined text-lg">cancel</span> Abbrechen
                        </button>
                        <button onclick="saveWorkOrderChanges()" class="w-full py-3 rounded-lg border-2 border-blue-100 bg-blue-50 text-blue-700 font-bold uppercase tracking-wider flex items-center justify-center gap-2 hover:bg-blue-100">
                            <span class="material-icons-outlined text-lg">save</span> Speichern
                        </button>
                    </div>
                    <div id="statusActions" class="pt-3 border-t border-slate-100 grid grid-cols-2 gap-4"></div>
                 </div>
            </div>
        </div>
    </div>
</div>

<!-- DEFINITION MODAL -->
<div id="definitionModal" class="modal hidden fixed inset-0 z-50 flex items-center justify-center bg-gray-900 bg-opacity-50">
    <div class="modal-backdrop" onclick="closeDefinitionModal()"></div>
    <div class="modal-content max-w-4xl flex flex-col max-h-[90vh]">
        <div class="flex justify-between items-center p-6 border-b border-slate-200 bg-white rounded-t-lg flex-shrink-0">
            <div class="flex items-center gap-3"><h3 class="text-lg font-bold text-slate-800" id="defModalTitle">Wartungsvorlage</h3><div id="defHeaderBadgeView" class="hidden"></div></div>
            <div class="flex items-center gap-4">
                <label id="defHeaderToggleEdit" class="inline-flex items-center cursor-pointer hidden"><input type="checkbox" id="defActive" class="sr-only peer" checked><div class="relative w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 rounded-full peer peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-green-600"></div><span class="ms-3 text-sm font-medium text-gray-700">Aktiv</span></label>
                <button id="btnSwitchToDefEdit" onclick="switchToDefEditMode()" class="text-slate-400 hover:text-blue-600 p-1.5 rounded-full hover:bg-slate-100 transition-colors" title="Bearbeiten"><span class="material-icons-outlined">edit</span></button>
                <button onclick="closeDefinitionModal()" class="text-slate-400 hover:text-slate-600 p-1.5 rounded-full hover:bg-slate-100 transition-colors"><span class="material-icons-outlined">close</span></button>
            </div>
        </div>
        <div class="p-6 overflow-y-auto custom-scrollbar bg-slate-50 flex-grow">
            <div id="defViewContainer" class="space-y-6">
                <div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm grid grid-cols-12 gap-4 text-sm">
                    <div class="col-span-12 flex justify-between border-b pb-2 mb-1"><h4 class="font-bold text-slate-800">Stammdaten</h4><span id="defIdView" class="font-mono text-xs text-slate-400"></span></div>
                    <div class="col-span-8"><p class="text-xs font-bold text-slate-400 uppercase mb-1">Aufgabe</p><p id="defTaskView" class="font-medium text-slate-800 text-base"></p></div>
                    <div class="col-span-4"><p class="text-xs font-bold text-slate-400 uppercase mb-1">Bereich</p><p id="defTopicView" class="text-slate-700"></p></div>
                    <div class="col-span-4"><p class="text-xs font-bold text-slate-400 uppercase mb-1">Startdatum</p><p id="defStartDateView" class="text-slate-700"></p></div>
                    <div class="col-span-4"><p class="text-xs font-bold text-slate-400 uppercase mb-1">Intervall</p><p id="defIntervalView" class="text-slate-700"></p></div>
                    <div class="col-span-4"><p class="text-xs font-bold text-slate-400 uppercase mb-1">Dauer</p><p id="defDurationView" class="text-slate-700"></p></div>
                    <div class="col-span-12 border-t border-slate-100 pt-3 mt-1 grid grid-cols-2 gap-4"><div><p class="text-xs font-bold text-slate-400 uppercase mb-1">Ausführung durch</p><p id="defExternalCompanyView" class="text-slate-700 font-medium"></p></div><div><p class="text-xs font-bold text-slate-400 uppercase mb-1">Review</p><p id="defReviewView" class="text-slate-700"></p></div></div>
                </div>
                <div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm grid grid-cols-12 gap-4 text-sm">
                    <div class="col-span-12"><h4 class="font-bold text-slate-800 mb-2 border-b pb-1">Ort & Objekt</h4></div>
                    <div class="col-span-4"><p class="text-xs font-bold text-slate-400 uppercase mb-1">Prüfstand</p><p id="defTestbenchView" class="text-slate-700"></p></div>
                    <div class="col-span-2"><p class="text-xs font-bold text-slate-400 uppercase mb-1">Index</p><p id="defIndexView" class="text-slate-700"></p></div>
                    <div class="col-span-6"><p class="text-xs font-bold text-slate-400 uppercase mb-1">Assets</p><div id="defAssetsView" class="flex flex-wrap gap-1 mt-1"></div></div>
                </div>
                <div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm space-y-4 text-sm">
                    <h4 class="font-bold text-slate-800 mb-2 border-b pb-1">Durchführung</h4>
                    <div><p class="text-xs font-bold text-slate-400 uppercase mb-2">Checkliste</p><ul id="defChecklistView" class="space-y-1 pl-1"></ul></div>
                    <div><p class="text-xs font-bold text-slate-400 uppercase mb-1">Beschreibung</p><div id="defDescView" class="text-slate-700 whitespace-pre-wrap bg-slate-50 p-2 rounded border border-slate-100"></div></div>
                    <div><p class="text-xs font-bold text-slate-400 uppercase mb-1">Verbrauchsmaterial</p><div id="defConsumablesView" class="flex flex-wrap gap-1 mt-1"></div></div>
                </div>
                <div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm"><label class="block text-xs font-bold text-slate-500 uppercase mb-3">Anhänge</label><div id="defAttachmentsView" class="grid grid-cols-2 gap-4"></div></div>
            </div>
            <form id="definitionForm" class="space-y-6 hidden">
                <input type="hidden" id="defId">
                <div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm grid grid-cols-12 gap-4">
                    <div class="col-span-12"><h4 class="text-sm font-bold text-slate-800 mb-2 border-b pb-1">Stammdaten Bearbeiten</h4></div>
                    <div class="col-span-8"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Aufgabe / Titel</label><input class="w-full border-slate-300 rounded-md text-sm" id="defTask" required></div>
                    <div class="col-span-4"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Bereich</label><select class="w-full border-slate-300 rounded-md text-sm bg-white" id="defTopic"></select></div>
                    <div class="col-span-4"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Startdatum (Erste Fälligkeit)</label><input type="date" class="w-full border-slate-300 rounded-md text-sm" id="defStartDate" required></div>
                    <div class="col-span-4"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Intervall</label><div class="flex gap-2"><input type="number" class="w-full border-slate-300 rounded-md text-sm" id="defIntVal" required><select class="w-full border-slate-300 rounded-md text-sm bg-white" id="defIntUnit"><option>Monate</option><option>Jahre</option><option>Wochen</option></select></div></div>
                    <div class="col-span-4"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Geplante Dauer</label><input type="text" class="w-full border-slate-300 rounded-md text-sm" id="defDuration" placeholder="z.B. 2 Std"></div>
                    <div class="col-span-12 border-t border-slate-100 pt-3 mt-1"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Ausführung durch</label><select class="w-full border-slate-300 rounded-md text-sm bg-white" id="defExternalCompany"><option value="">Intern / Internes Team</option></select><p class="text-[10px] text-slate-400 mt-1">Wählen Sie eine externe Firma, falls die Wartung ausgelagert wird.</p></div>
                    <div class="col-span-12 border-t border-slate-100 pt-3 mt-1"><label class="flex items-center gap-2 text-xs font-bold uppercase text-slate-600 cursor-pointer"><input type="checkbox" id="defReqReview" class="rounded border-slate-300 text-purple-600 focus:ring-purple-500" onchange="toggleReviewerInput()"> 4-Augen-Prinzip erforderlich (Review)</label><div id="defReviewerBox" class="hidden mt-2 pl-6"><label class="block text-xs text-slate-400 mb-1">Designierter Prüfer (ID) - Optional</label><input id="defReviewer" class="w-1/2 border-slate-300 rounded-md text-sm" placeholder="z.B. mmuster"></div></div>
                </div>
                <div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm grid grid-cols-12 gap-4">
                    <div class="col-span-12"><h4 class="text-sm font-bold text-slate-800 mb-2 border-b pb-1">Ort & Objekt</h4></div>
                    <div class="col-span-4"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Prüfstand</label><select class="w-full border-slate-300 rounded-md text-sm bg-white" id="defTestbench"></select></div>
                    <div class="col-span-2">
                        <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Betriebseinheit</label>
                        <input list="facilityUnitList" class="w-full border-slate-300 rounded-md text-sm" id="defIndex" onchange="onFacilityUnitSelected(this, 'defFacilityUnitId')"> <!-- onchange NEU -->
                        <input type="hidden" id="defFacilityUnitId"> <!-- NEU: Hidden ID Field -->
                        <datalist id="facilityUnitList"></datalist>
                    </div>
                    <div class="col-span-6"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Wartungsobjekte (Assets)</label><div class="flex gap-2 mb-2"><input list="hardwareList" class="flex-1 border-slate-300 rounded-md text-sm" id="defAssetInput" placeholder="Hardware hinzufügen..."><datalist id="hardwareList"></datalist><button type="button" onclick="addAsset()" class="bg-slate-100 hover:bg-slate-200 px-3 rounded border border-slate-300 text-slate-600"><span class="material-icons-outlined text-base align-middle">add</span></button></div><div id="defAssetContainer" class="flex flex-wrap min-h-[24px]"></div></div>
                </div>
                <div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm space-y-4">
                    <h4 class="text-sm font-bold text-slate-800 mb-2 border-b pb-1">Durchführung</h4>
                    <div><label class="block text-xs font-bold text-slate-500 uppercase mb-2">Checkliste (Drag & Drop)</label><div class="flex gap-2 mb-2"><input type="text" id="newChecklistItem" class="flex-1 border-slate-300 rounded-md text-sm" placeholder="Neuer Schritt..."><button type="button" onclick="addChecklistItem()" class="bg-slate-100 hover:bg-slate-200 text-slate-600 px-3 py-1 rounded border border-slate-300"><span class="material-icons-outlined text-base align-middle">add</span></button></div><ul id="defChecklist" class="space-y-1 bg-slate-50 p-2 rounded border border-slate-100 min-h-[3rem]"></ul></div>
                    <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Zusätzliche Beschreibung / Hinweise</label><textarea class="w-full border-slate-300 rounded-md text-sm h-20" id="defDesc" placeholder="Allgemeine Hinweise, Sicherheit..."></textarea></div>
                    <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Verbrauchsmaterial / Consumables</label><div class="flex gap-2 mb-2"><input class="flex-1 border-slate-300 rounded-md text-sm" id="defConsumableInput" placeholder="Material hinzufügen..."><button type="button" onclick="addConsumable()" class="bg-slate-100 hover:bg-slate-200 px-3 rounded border border-slate-300 text-slate-600"><span class="material-icons-outlined text-base align-middle">add</span></button></div><div id="defConsumablesContainer" class="flex flex-wrap min-h-[24px]"></div></div>
                </div>
                <div class="bg-white p-5 rounded-lg border border-slate-200 shadow-sm"><label class="block text-xs font-bold text-slate-500 uppercase mb-3">Anhänge & Dokumentation</label><div class="grid grid-cols-2 gap-4"><div><label class="cursor-pointer flex items-center justify-center gap-2 p-3 border-2 border-dashed border-slate-300 rounded-lg hover:border-primary hover:bg-blue-50 transition-colors text-slate-500"><span class="material-icons-outlined">add_photo_alternate</span> Bilder hochladen <input type="file" class="hidden" id="defImageInput" multiple accept="image/*"></label><div id="defImagePreview" class="mt-2 space-y-1"></div></div><div><label class="cursor-pointer flex items-center justify-center gap-2 p-3 border-2 border-dashed border-slate-300 rounded-lg hover:border-primary hover:bg-blue-50 transition-colors text-slate-500"><span class="material-icons-outlined">description</span> Dokumente hochladen <input type="file" class="hidden" id="defDocInput" multiple></label><div id="defDocPreview" class="mt-2 space-y-1"></div></div></div></div>
            </form>
        </div>
        <div class="p-4 border-t border-slate-200 bg-white flex justify-end gap-3 rounded-b-lg flex-shrink-0">
            <div id="defFooterView" class="flex w-full justify-end"><button class="px-4 py-2 text-sm font-medium text-white bg-slate-800 hover:bg-slate-700 rounded shadow-sm" onclick="closeDefinitionModal()">Schließen</button></div>
            <div id="defFooterEdit" class="flex gap-3 hidden w-full justify-between">
                 <button id="btnDefDelete" type="button" class="px-4 py-2 text-red-600 bg-red-50 border border-red-200 rounded hover:bg-red-100 text-sm font-medium flex items-center gap-1" onclick="deleteDefinitionFromModal()"><span class="material-icons-outlined text-sm">delete</span> Löschen</button>
                 <div class="flex gap-3 ml-auto"><button class="px-4 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded border border-slate-300" onclick="cancelDefEdit()">Abbrechen</button><button class="px-4 py-2 text-sm bg-primary text-white rounded hover:bg-blue-700 font-medium shadow-sm" onclick="saveDefinition()">Speichern</button></div>
            </div>
        </div>
    </div>
</div>

<!-- HISTORY & CREATE MODALS -->
<div id="historyListModal" class="modal hidden fixed inset-0 z-50 flex items-center justify-center bg-gray-900 bg-opacity-50 backdrop-blur-sm"><div class="modal-backdrop" onclick="closeHistoryListModal()"></div><div class="modal-content max-w-4xl w-full flex flex-col max-h-[85vh]"><div class="flex-shrink-0 px-6 py-5 border-b border-slate-200 bg-white rounded-t-lg flex justify-between items-start"><div class="flex-grow pr-4"> <!-- Flex-grow damit er Platz nimmt, aber Button rechts bleibt -->
    <!-- Container für Titel und Badge in einer Zeile -->
    <div class="flex items-center flex-wrap gap-2" id="historyTitleContainer">
        <h3 class="text-xl font-bold text-slate-800" id="historyModalTitle">Ausführungshistorie</h3>
        <div id="historyStatsBadge"></div> <!-- Platzhalter für die Pille -->
    </div>
    <p class="text-sm text-slate-500 mt-1">Ausführungshistorie</p>
</div><button onclick="closeHistoryListModal()" class="text-slate-400 hover:text-slate-600 p-1 bg-slate-50 hover:bg-slate-100 rounded-full transition-colors"><span class="material-icons-outlined">close</span></button></div><div class="flex-grow overflow-y-auto custom-scrollbar bg-slate-50 p-6"><div class="bg-white rounded-lg border border-slate-200 shadow-sm overflow-hidden"><table class="w-full text-sm text-left"><thead class="bg-slate-50 text-slate-500 text-xs font-bold uppercase tracking-wider border-b border-slate-200"><tr><th class="px-6 py-3">Status / ID</th><th class="px-6 py-3">Abschlussdatum</th><th class="px-6 py-3">Durchgeführt von</th><th class="px-6 py-3 text-right">Aktion</th></tr></thead><tbody id="historyListBody" class="divide-y divide-slate-100"></tbody></table></div></div><div class="flex-shrink-0 px-6 py-4 border-t border-slate-200 bg-white rounded-b-lg flex justify-end"><button onclick="closeHistoryListModal()" class="px-4 py-2 bg-slate-100 text-slate-700 font-medium rounded-lg hover:bg-slate-200 transition-colors">Schließen</button></div></div></div>

<!-- TRIAGE MODAL -->
<div id="triageModal" class="modal hidden fixed inset-0 z-[70] flex items-center justify-center bg-gray-900 bg-opacity-50 backdrop-blur-sm">
    <div class="modal-backdrop" onclick="closeTriageModal()"></div>
    <div class="modal-content max-w-lg w-full transform transition-all scale-100 rounded-xl shadow-2xl">
        <div class="flex justify-between items-center p-6 border-b border-slate-200 bg-white rounded-t-xl">
            <h3 class="text-xl font-bold text-slate-800 flex items-center gap-2">
                <span class="material-icons-outlined text-blue-600">rule</span> Issue Bewerten
            </h3>
            <button onclick="closeTriageModal()" class="text-slate-400 hover:text-slate-600 transition-colors"><span class="material-icons-outlined">close</span></button>
        </div>
        <div class="p-6 bg-white space-y-6">
            <div class="bg-slate-50 p-4 rounded-lg border border-slate-200 text-sm">
                <p class="text-xs font-bold text-slate-400 uppercase mb-1">Betroffenes Issue</p>
                <p class="font-medium text-slate-800" id="triageIssueTitle"></p>
                <p class="text-slate-500 mt-1 truncate" id="triageIssueDesc"></p>
            </div>
            <form id="triageForm" class="space-y-6">
                <!-- 1. Priorität -->
                <div>
                    <label class="block text-sm font-bold text-slate-700 mb-2">Priorität festlegen *</label>
                    <div class="grid grid-cols-4 gap-2">
                        <label class="cursor-pointer"><input type="radio" name="triagePriority" value="Low" class="peer sr-only"><div class="text-center py-2 border rounded-md text-xs font-bold uppercase tracking-wider text-slate-600 bg-white hover:bg-slate-50 peer-checked:bg-slate-100 peer-checked:text-slate-800 peer-checked:border-slate-400 peer-checked:ring-1 peer-checked:ring-slate-400 transition-all">Niedrig</div></label>
                        <label class="cursor-pointer"><input type="radio" name="triagePriority" value="Medium" class="peer sr-only"><div class="text-center py-2 border rounded-md text-xs font-bold uppercase tracking-wider text-yellow-600 bg-white hover:bg-yellow-50 peer-checked:bg-yellow-50 peer-checked:border-yellow-400 peer-checked:ring-1 peer-checked:ring-yellow-400 transition-all">Mittel</div></label>
                        <label class="cursor-pointer"><input type="radio" name="triagePriority" value="High" class="peer sr-only"><div class="text-center py-2 border rounded-md text-xs font-bold uppercase tracking-wider text-orange-600 bg-white hover:bg-orange-50 peer-checked:bg-orange-50 peer-checked:border-orange-400 peer-checked:ring-1 peer-checked:ring-orange-400 transition-all">Hoch</div></label>
                        <label class="cursor-pointer"><input type="radio" name="triagePriority" value="Critical" class="peer sr-only"><div class="text-center py-2 border rounded-md text-xs font-bold uppercase tracking-wider text-red-600 bg-white hover:bg-red-50 peer-checked:bg-red-50 peer-checked:border-red-400 peer-checked:ring-1 peer-checked:ring-red-400 transition-all">Kritisch</div></label>
                    </div>
                </div>
            
                <hr class="border-slate-200">
            
                <!-- 2. Anlagen-Sperre -->
                <div>
                    <label class="flex items-start gap-3 p-3 rounded-lg border border-red-100 bg-red-50 cursor-pointer hover:bg-red-100 transition-colors">
                        <input id="triageUnitBlocker" type="checkbox" class="mt-1 w-5 h-5 text-red-600 border-gray-300 rounded focus:ring-red-500">
                        <div>
                            <span class="block font-bold text-red-800 text-sm">Betriebseinheit sperren (Anlagen-Blocker)</span>
                            <span class="block text-xs text-red-600 mt-1">Die gesamte Anlage/Testzelle ist nicht betriebsbereit (Anzeige im 3D-Viewer).</span>
                        </div>
                    </label>
                </div>
            
                <!-- 3. Hardware-Sperre (Dynamisch) -->
                <div id="triageAssetSection" class="hidden">
                    <label class="block text-sm font-bold text-slate-700 mb-2">Betroffene Hardware sperren</label>
                    <p class="text-xs text-slate-500 mb-2">Markierte Geräte werden im Inventar auf "Maintenance" gesetzt.</p>
                    <div id="triageAssetList" class="space-y-2 max-h-40 overflow-y-auto custom-scrollbar border border-slate-200 rounded-md p-2 bg-slate-50">
                        <!-- Wird per JS gefüllt -->
                    </div>
                </div>
            </form>
        </div>
        <div class="p-4 border-t border-slate-200 bg-white rounded-b-xl flex justify-end gap-3">
            <button onclick="closeTriageModal()" class="px-4 py-2 text-sm font-medium text-slate-600 hover:bg-slate-100 rounded-lg transition-colors">Abbrechen</button>
            <button onclick="submitTriage()" class="px-4 py-2 text-sm font-bold text-white bg-blue-600 hover:bg-blue-700 rounded-lg shadow-sm transition-colors flex items-center gap-2"><span class="material-icons-outlined text-base">save</span> Speichern</button>
        </div>
    </div>
</div>

<!-- CREATE ISSUE MODAL -->
<div id="createIssueModal" class="modal hidden fixed inset-0 z-[60] flex items-center justify-center bg-gray-900 bg-opacity-50"><div class="modal-backdrop" onclick="closeCreateIssueModal()"></div><div class="modal-content max-w-2xl"><div class="flex justify-between items-center p-6 border-b border-slate-200 bg-white rounded-t-lg"><h3 class="text-xl font-bold text-slate-800 flex items-center gap-2"><span class="material-icons-outlined text-yellow-600">report_problem</span> Mangel / Issue melden</h3><button onclick="closeCreateIssueModal()" class="text-slate-400 hover:text-slate-600"><span class="material-icons-outlined">close</span></button></div><div class="p-6 overflow-y-auto custom-scrollbar bg-slate-50 max-h-[70vh]"><form id="createIssueForm" class="space-y-5">
    <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Titel (Kurzbeschreibung)*</label><input class="w-full border-slate-300 rounded-md text-sm focus:ring-yellow-500 focus:border-yellow-500" id="issueTitle" required placeholder="z.B. Leckage an Hydraulikpumpe"></div>
    <div id="editIssueMetaContainer" class="hidden grid grid-cols-2 gap-4 p-4 bg-white border border-slate-200 rounded-lg">
        <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Priorität</label><select class="w-full border-slate-300 rounded-md text-sm bg-white" id="issuePriority"><option value="Low">Niedrig</option><option value="Medium">Mittel</option><option value="High">Hoch</option><option value="Critical">Kritisch</option></select></div>
        <div class="flex items-center mt-6"><input type="checkbox" id="issueBlocker" class="rounded border-red-300 text-red-600 focus:ring-red-500 w-5 h-5 mr-2"><label for="issueBlocker" class="text-sm font-medium text-red-800 cursor-pointer">Betriebsblocker</label></div>
    </div>
    <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Bereich</label><select class="w-full border-slate-300 rounded-md text-sm bg-white" id="issueCategory"></select></div>
    <div id="issueAssetContainer" class="hidden mt-4"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Betroffene Assets (Mehrfachauswahl)</label><div id="issueAssetList" class="border border-slate-300 rounded-md bg-white p-2 max-h-32 overflow-y-auto space-y-1"></div><p class="text-[10px] text-slate-400 mt-1">Ausgewählte Geräte werden als "Defekt" markiert.</p></div>
    <div class="mt-4">
        <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Betriebsbereich (Anlage)</label>
        <input list="facilityUnitList" class="w-full border-slate-300 rounded-md text-sm" id="issuePlantIndex" onchange="onFacilityUnitSelected(this, 'issueFacilityUnitId')">
        <input type="hidden" id="issueFacilityUnitId"> 
    </div>
    <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Beschreibung</label><textarea class="w-full border-slate-300 rounded-md text-sm" id="issueDescription" rows="4"></textarea></div>
    <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Foto / Anhang</label><input type="file" id="issueAttachment" class="block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-yellow-50 file:text-yellow-700 hover:file:bg-yellow-100"></div></form></div><div class="p-4 border-t border-slate-200 bg-white flex justify-end gap-3 rounded-b-lg">
        <button class="px-4 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded" onclick="closeCreateIssueModal()">Abbrechen</button>
        <button id="btnSaveIssue" class="px-4 py-2 text-sm bg-yellow-600 text-white rounded hover:bg-yellow-700 font-medium shadow-sm flex items-center gap-2" onclick="submitCreateIssue()"><span class="material-icons-outlined text-sm">save</span> Mangel anlegen</button>
    </div></div></div>

<!-- CREATE ACTION MODAL -->
<div id="createActionModal" class="modal hidden fixed inset-0 z-[60] flex items-center justify-center bg-gray-900 bg-opacity-50"><div class="modal-backdrop" onclick="closeCreateActionModal()"></div><div class="modal-content max-w-2xl"><div class="flex justify-between items-center p-6 border-b border-slate-200 bg-white rounded-t-lg"><h3 class="text-xl font-bold text-slate-800 flex items-center gap-2"><span class="material-icons-outlined text-green-600">build_circle</span> Maßnahme planen</h3><button onclick="closeCreateActionModal()" class="text-slate-400 hover:text-slate-600"><span class="material-icons-outlined">close</span></button></div><div class="p-6 overflow-y-auto custom-scrollbar bg-slate-50 max-h-[70vh]"><form id="createActionForm" class="space-y-5"><div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Titel der Maßnahme*</label><input class="w-full border-slate-300 rounded-md text-sm focus:ring-green-500 focus:border-green-500" id="actionTitle" required></div><div class="grid grid-cols-2 gap-4"><div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Fälligkeit</label><input type="date" class="w-full border-slate-300 rounded-md text-sm" id="actionDueDate"></div><div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Aufwand</label><input type="text" class="w-full border-slate-300 rounded-md text-sm" id="actionEffort"></div></div><div class="mt-4"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Bereich</label><select class="w-full border-slate-300 rounded-md text-sm bg-white" id="actionCategory"></select></div><div class="mt-4">
    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Betriebsbereich (Anlage)</label>
    <input list="facilityUnitList" class="w-full border-slate-300 rounded-md text-sm" id="actionPlantIndex" onchange="onFacilityUnitSelected(this, 'actionFacilityUnitId')">
    <input type="hidden" id="actionFacilityUnitId">
</div><div id="actionAssetContainer" class="hidden mt-4"><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Betroffene Assets (Mehrfachauswahl)</label><div id="actionAssetList" class="border border-slate-300 rounded-md bg-white p-2 max-h-32 overflow-y-auto space-y-1"></div><p class="text-[10px] text-slate-400 mt-1">Wählen Sie die Assets aus, an denen diese Maßnahme durchgeführt wird.</p></div><div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Details</label><textarea class="w-full border-slate-300 rounded-md text-sm" id="actionInstructions" rows="4"></textarea></div><div class="bg-blue-50 p-3 rounded border border-blue-100"><label class="block text-xs font-bold text-blue-800 uppercase mb-1">Material</label><textarea class="w-full border-blue-200 rounded-md text-sm focus:border-blue-400 focus:ring-blue-400" id="actionSpareParts" rows="2"></textarea></div></form></div><div class="p-4 border-t border-slate-200 bg-white flex justify-end gap-3 rounded-b-lg">
        <button class="px-4 py-2 text-sm text-slate-600 hover:bg-slate-100 rounded" onclick="closeCreateActionModal()">Abbrechen</button>
        <button id="btnSaveAction" class="px-4 py-2 text-sm bg-green-600 text-white rounded hover:bg-green-700 font-medium shadow-sm flex items-center gap-2" onclick="submitCreateAction()"><span class="material-icons-outlined text-sm">save</span> Speichern</button>
    </div></div></div>

<!-- LIGHTBOX MODAL -->
<div id="lightboxModal" class="modal hidden fixed inset-0 z-[9999] bg-black bg-opacity-90 flex items-center justify-center backdrop-blur-sm transition-opacity duration-300" onclick="closeLightbox()">
    <div class="relative max-w-full max-h-full p-4 flex flex-col items-center justify-center">
        <!-- Bild Container -->
        <img id="lightboxImage" src="" class="max-h-[85vh] max-w-[90vw] object-contain rounded shadow-2xl cursor-default" onclick="event.stopPropagation()">
        
        <!-- Schließen Button (schwebend) -->
        <button class="absolute top-6 right-6 text-white bg-white/10 hover:bg-white/20 rounded-full p-2 transition-colors backdrop-blur-md" onclick="closeLightbox()">
            <span class="material-icons-outlined text-2xl">close</span>
        </button>
        
        <!-- Titel / Dateiname (Optional) -->
        <p id="lightboxCaption" class="mt-4 text-slate-200 text-sm font-medium tracking-wide bg-black/50 px-4 py-1 rounded-full"></p>
    </div>
</div>
<div id="assetHistoryModal" class="modal hidden fixed inset-0 z-[80] flex items-center justify-center bg-gray-900 bg-opacity-60 backdrop-blur-sm">
    <div class="modal-backdrop" onclick="closeAssetHistoryModal()"></div>
    <div class="modal-content max-w-2xl w-full flex flex-col max-h-[85vh] bg-white rounded-xl shadow-2xl">
        <div class="flex justify-between items-center p-6 border-b border-slate-100 bg-white rounded-t-xl z-10">
            <div>
                <h3 class="text-xl font-bold text-slate-800" id="assetHistoryTitle">Asset Historie</h3>
                <p class="text-sm text-slate-500 mt-1" id="assetHistorySubtitle">Lade Daten...</p>
            </div>
            <button onclick="closeAssetHistoryModal()" class="text-slate-400 hover:text-slate-600 p-2 hover:bg-slate-50 rounded-full transition-colors"><span class="material-icons-outlined">close</span></button>
        </div>
        <div class="p-6 overflow-y-auto custom-scrollbar bg-slate-50/50 flex-grow">
            <!-- Timeline Container -->
            <div id="assetHistoryTimeline" class="relative border-l-2 border-slate-200 ml-3 space-y-8 pb-4">
            </div>
        </div>
    </div>
</div>
<div id="currentUser" 
     data-name="{{ current_user_display_name }}" 
     data-id="{{ session.get('user_id', '').lower() }}"
     data-is-admin="{{ 'true' if session.get('is_admin') else 'false' }}"
     data-roles='{{ session.get("roles", []) | tojson }}' 
     class="hidden">
</div>
<script>

    let maintenanceOrders = {{ orders_json | safe }};
    let taskDefinitions = {{ definitions_json | safe }};
    let companies = {{ companies_json | safe }}; 
    let maintenanceLogs = [];

    if (!maintenanceOrders) maintenanceOrders = [];
    if (!taskDefinitions) taskDefinitions = [];
    if (!companies) companies = [];

    let hardwareInventoryList = [];
    let adHocContext = null;
    let isWorkOrderInEditMode = false;
    let currentAssetTags = [];
    let availableAssetOptions = [];
    let analysisCharts = {};
    let globalAssetStats = [];
    
    // Globale Variablen für Modal
    let currentDefImages = []; 
    let currentDefDocs = []; 
    let currentChecklist = []; 
    let currentDefAssets = []; 
    let currentDefConsumables = []; 
    let sortableChecklist = null;
    let currentOrderId = null;
    
    const currentUser = "{{ current_user_display_name }}";
    const currentUserId = "{{ session.get('user_id', '').lower() }}";
    const currentUserIsAdmin = {{ 'true' if session.get('is_admin') else 'false' }};

    const allHardwareAssets = {{ hardware_assets_json | safe }};
    const serverPermissions = {{ permissions_json | safe }};

    // --- FILTER LOGIK & UI ---
    
    // 1. Panel Toggle
    function toggleFilterPanel(panelId, btn) {
        const panel = document.getElementById(panelId);
        if (panel.classList.contains('hidden')) {
            panel.classList.remove('hidden');
            btn.classList.add('bg-blue-50', 'text-blue-700', 'border-blue-200');
            btn.classList.remove('bg-white', 'text-slate-600', 'border-slate-200');
        } else {
            panel.classList.add('hidden');
            btn.classList.remove('bg-blue-50', 'text-blue-700', 'border-blue-200');
            btn.classList.add('bg-white', 'text-slate-600', 'border-slate-200');
        }
    }
    
    // 2. Dropdowns initialisieren (Wird bei DOMContentLoaded aufgerufen)
    function initFilterDropdowns() {
        
        // --- 1. BEREICHE (AREAS) ---
        const areaSet = new Set([
        ]);
    
        if (typeof taskDefinitions !== 'undefined') {
            taskDefinitions.forEach(def => { if (def.topic && def.topic.trim()) areaSet.add(def.topic.trim()); });
        }
        if (typeof maintenanceOrders !== 'undefined') {
            maintenanceOrders.forEach(ord => { 
                const val = ord.topic || ord.category;
                if (val && val.trim()) areaSet.add(val.trim()); 
            });
        }
        const areas = Array.from(areaSet).sort();
    
    
        // --- 2. BETRIEBSBEREICHE (ROOTS) ---
        const rootMap = new Map();
        if (typeof facilityUnitMap !== 'undefined') {
            Object.keys(facilityUnitMap).forEach(fullName => {
                const rootMatch = fullName.match(/^(\d+)\s+(.*)$/);
                if (rootMatch) {
                    rootMap.set(rootMatch[1], fullName); 
                } else {
                    const childMatch = fullName.match(/^(\d+)\./);
                    if (childMatch && !rootMap.has(childMatch[1])) {
                        rootMap.set(childMatch[1], `Bereich ${childMatch[1]}`);
                    }
                }
            });
        }
        const sortedRootIndices = Array.from(rootMap.keys()).sort((a,b) => parseInt(a) - parseInt(b));
    
    
        // --- 3. PRIORITÄTEN (PRIORITIES) ---
        // Defaults, damit die Reihenfolge stimmt
        const prioSet = new Set(["Critical", "High", "Medium", "Low", "OPEN"]);
        
        if (typeof maintenanceOrders !== 'undefined') {
            maintenanceOrders.forEach(ord => {
                if (ord.priority && ord.priority.trim()) {
                    prioSet.add(ord.priority.trim());
                }
            });
        }
    
        // Logische Sortierung (Kritisch oben) statt Alphabetisch
        const prioOrder = { "Critical": 0, "High": 1, "Medium": 2, "Low": 3, "OPEN": 4 };
        const priorities = Array.from(prioSet).sort((a, b) => {
            const valA = prioOrder[a] !== undefined ? prioOrder[a] : 99;
            const valB = prioOrder[b] !== undefined ? prioOrder[b] : 99;
            return valA - valB;
        });
    
    
        // --- 4. INTERVALLE (INTERVALS) ---
        const intervalSet = new Set(["Wochen", "Monate", "Jahre"]);
        
        if (typeof taskDefinitions !== 'undefined') {
            taskDefinitions.forEach(def => {
                if (def.intervalUnit && def.intervalUnit.trim()) {
                    intervalSet.add(def.intervalUnit.trim());
                }
            });
        }
        // Alphabetisch sortieren
        const intervals = Array.from(intervalSet).sort();
    
    
        // --- FILL HELPER ---
        const fill = (id, items, isRootMode=false) => {
            const el = document.getElementById(id);
            if(!el) return;
            el.innerHTML = el.options[0].outerHTML; // Reset keep first
            
            items.forEach(item => {
                let val = item;
                let text = item;
                if (isRootMode) {
                    val = item;
                    text = rootMap.get(item);
                }
                // Übersetzung für Priorities (Optional, falls gewünscht)
                // const trans = { "Critical": "Kritisch", "High": "Hoch", "Medium": "Mittel", "Low": "Niedrig", "OPEN": "Unbewertet" };
                // if (trans[text]) text = trans[text];
    
                const opt = document.createElement('option');
                opt.value = val;
                opt.textContent = text;
                el.appendChild(opt);
            });
        };
    
        // --- EXECUTE ---
        fill('filterAreaActive', areas);
        fill('filterAreaDef', areas);
        fill('filterPlantActive', sortedRootIndices, true);
        fill('filterPlantDef', sortedRootIndices, true);
        
        // Neue Felder füllen
        fill('filterPrioActive', priorities);
        fill('filterIntervalDef', intervals);
    }
    
    // 3. Filter Logik für AKTIVE AUFTRÄGE
    function applyFiltersActive() {
        const term = document.getElementById('searchActiveOrders').value.toLowerCase();
        const typeVal = document.getElementById('filterTypeActive').value;
        const prioVal = document.getElementById('filterPrioActive').value;
        const blockerVal = document.getElementById('filterBlockerActive').value;
        const areaVal = document.getElementById('filterAreaActive').value;
        const plantVal = document.getElementById('filterPlantActive').value;
    
        // 1. Alles zurücksetzen (Initial verstecken)
        document.querySelectorAll('.task-group').forEach(g => g.classList.add('hidden'));
        document.querySelectorAll('[class*="child-container-"]').forEach(c => c.classList.add('hidden'));
        document.querySelectorAll('.task-row button .material-icons-outlined').forEach(icon => icon.classList.remove('rotate-90'));
    
        const matchedTaskIds = new Set();
        const visibleTaskIds = new Set(); // Enthält Treffer + deren Eltern
    
        // 2. Treffer ermitteln
        maintenanceOrders.forEach(order => {
            if (order.status === 'Completed') return;
    
            let isMatch = true;
    
            // Text-Suche
            if (term) {
                const text = `${order.id} ${order.task} ${order.description || ''} ${order.plantIndex || ''}`.toLowerCase();
                if (!text.includes(term)) isMatch = false;
            }
            // Typ-Filter
            if (isMatch && typeVal !== 'all' && order.type !== typeVal) isMatch = false;
            // Prio-Filter
            if (isMatch && prioVal !== 'all' && order.priority !== prioVal) isMatch = false;
            // Blocker-Filter (Anlage/Hardware)
            if (isMatch && blockerVal !== 'all') {
                const isUnit = order.isUnitBlocker === true || order.isUnitBlocker === "true";
                const isHw = order.blockedAssetIds && order.blockedAssetIds.length > 0;
                const isAny = isUnit || isHw || (order.isBlocker === true && !isUnit && !isHw);
                if (blockerVal === 'any' && !isAny) isMatch = false;
                else if (blockerVal === 'unit' && !isUnit) isMatch = false;
                else if (blockerVal === 'hardware' && !isHw) isMatch = false;
                else if (blockerVal === 'none' && isAny) isMatch = false;
            }
            // Bereichs-Filter
            if (isMatch && areaVal !== 'all') {
                const oArea = order.topic || order.category;
                if (oArea !== areaVal) isMatch = false;
            }
            // Anlagen-Filter (Root)
            if (isMatch && plantVal !== 'all') {
                const pIndex = order.plantIndex || '';
                if (!pIndex.startsWith(plantVal + '.')) isMatch = false;
            }
    
            if (isMatch) {
                matchedTaskIds.add(order.id);
                
                // Den Pfad nach oben berechnen (mit Endlosschleifen-Schutz)
                let current = order;
                visibleTaskIds.add(current.id);
                const visitedParents = new Set([current.id]); // Sicherung
                
                while (current && (current.sourceTask || current.linkedIssue)) {
                    const parentId = current.sourceTask || current.linkedIssue;
                    
                    // Wenn wir diesen Parent schon hatten -> Zirkelbezug! Abbruch.
                    if (visitedParents.has(parentId)) {
                        console.warn("Zirkelbezug bei Auftrag entdeckt:", parentId);
                        break;
                    }
                    
                    visitedParents.add(parentId);
                    visibleTaskIds.add(parentId);
                    
                    current = maintenanceOrders.find(o => o.id === parentId);
                }
            }
        });
    
        // 3. DOM aktualisieren (Sichtbarkeit & Struktur)
        visibleTaskIds.forEach(id => {
            // Finde das task-group Element für diese ID
            const row = document.querySelector(`.task-row[onclick*="'${id}'"]`);
            if (row) {
                const group = row.closest('.task-group');
                if (group) group.classList.remove('hidden');
    
                // Wenn diese ID ein ELTER eines anderen sichtbaren Elements ist,
                // müssen wir seinen Kinder-Container öffnen
                const childrenContainer = document.querySelector(`.child-container-${id}`);
                if (childrenContainer) {
                    // Checken, ob mindestens eines der Kinder in visibleTaskIds ist
                    const hasVisibleChild = maintenanceOrders.some(o => 
                        (o.sourceTask === id || o.linkedIssue === id) && visibleTaskIds.has(o.id)
                    );
                    
                    if (hasVisibleChild) {
                        childrenContainer.classList.remove('hidden');
                        // Pfeil des Vaters drehen
                        const icon = row.querySelector('button .material-icons-outlined');
                        if (icon) icon.classList.add('rotate-90');
                    }
                }
            }
        });
    
        // 4. Kachel-Sichtbarkeit (Buckets) aktualisieren
        const bucketIds = ['groupOverdue', 'groupIssues', 'groupUpcoming', 'groupOutlook', 'groupFuture'];
        const hasAnyFilter = term || typeVal !== 'all' || prioVal !== 'all' || areaVal !== 'all' || plantVal !== 'all' || blockerVal !== 'all';
    
        bucketIds.forEach(bucketId => {
            const bucket = document.getElementById(bucketId);
            if (!bucket) return;
            const bodyId = bucketId.replace('group', 'body');
            const body = document.getElementById(bodyId);
            const visibleInBucket = body.querySelectorAll('.task-group:not(.hidden)');
    
            if (visibleInBucket.length > 0) {
                bucket.classList.remove('hidden');
                if (hasAnyFilter) {
                    body.classList.remove('hidden');
                    const icon = bucket.querySelector('button .material-icons-outlined:last-child');
                    if (icon) icon.classList.remove('rotate-180');
                }
            } else {
                bucket.classList.add('hidden');
            }
        });
    
        // 5. Chips Styling
        const chipIds = ['filterTypeActive', 'filterPrioActive', 'filterBlockerActive', 'filterAreaActive', 'filterPlantActive'];
        chipIds.forEach(id => {
            const el = document.getElementById(id);
            if (el && el.value !== 'all') {
                el.classList.add('bg-blue-50', 'text-blue-700', 'border-blue-200', 'font-bold');
            } else if (el) {
                el.classList.remove('bg-blue-50', 'text-blue-700', 'border-blue-200', 'font-bold');
            }
        });
    }
    
    // 4. Filter Logik für DEFINITIONEN
    function applyFiltersDefinitions() {
        const term = document.getElementById('searchDefinitions').value.toLowerCase();
        
        const filters = {
            interval: document.getElementById('filterIntervalDef'),
            area: document.getElementById('filterAreaDef'),
            plant: document.getElementById('filterPlantDef')
        };
    
        const intervalVal = filters.interval ? filters.interval.value : 'all';
        const areaVal = filters.area ? filters.area.value : 'all';
        const plantVal = filters.plant ? filters.plant.value : 'all';
    
        // 1. Chips färben
        Object.values(filters).forEach(selectEl => {
            if (!selectEl) return;
            if (selectEl.value !== 'all') {
                selectEl.classList.remove('bg-white', 'text-slate-600', 'border-slate-300');
                selectEl.classList.add('bg-blue-50', 'text-blue-700', 'border-blue-200', 'font-bold');
            } else {
                selectEl.classList.add('bg-white', 'text-slate-600', 'border-slate-300');
                selectEl.classList.remove('bg-blue-50', 'text-blue-700', 'border-blue-200', 'font-bold');
            }
        });
    
        const groupIds = ['bodyDefWeekly', 'bodyDefMonthly', 'bodyDefYearly', 'bodyDefOther'];
    
        groupIds.forEach(bodyId => {
            const bodyEl = document.getElementById(bodyId);
            if (!bodyEl) return;
    
            const rows = bodyEl.querySelectorAll('tbody tr');
            let hasVisibleItems = false;
    
            rows.forEach(row => {
                const id = row.cells[0].textContent.trim();
                const def = taskDefinitions.find(d => d.id === id);
                
                if(!def) return;
    
                let matches = true;
    
                // 1. Text
                if (term && !row.textContent.toLowerCase().includes(term)) matches = false;
                
                // 2. Intervall
                if (matches && intervalVal !== 'all' && def.intervalUnit !== intervalVal) matches = false;
                
                // 3. Bereich
                if (matches && areaVal !== 'all' && def.topic !== areaVal) matches = false;
    
                // 4. Anlage
                if (matches && plantVal !== 'all') {
                    const pIndex = def.plantIndex || '';
                    if (!pIndex.startsWith(plantVal + '.')) matches = false;
                }
    
                if (matches) {
                    row.classList.remove('hidden');
                    hasVisibleItems = true;
                } else {
                    row.classList.add('hidden');
                }
            });
    
            // Kachel-Verhalten
            const hasActiveFilter = term || intervalVal!=='all' || areaVal!=='all' || plantVal!=='all';
    
            if (hasActiveFilter && hasVisibleItems) {
                bodyEl.classList.remove('hidden');
                const btn = bodyEl.parentElement.querySelector('button');
                const icon = btn.querySelector('.material-icons-outlined:last-child');
                if (icon) icon.classList.remove('rotate-180');
            } else if (hasActiveFilter && !hasVisibleItems) {
                bodyEl.classList.add('hidden');
                const btn = bodyEl.parentElement.querySelector('button');
                const icon = btn.querySelector('.material-icons-outlined:last-child');
                if (icon) icon.classList.add('rotate-180');
            }
        });
    }

    function canUserReview(order) {
        // 1. Admin darf immer (Client-Side Check als Fallback/Speed)
        const userEl = document.getElementById('currentUser');
        if (userEl.dataset.isAdmin === 'true') return true;

        // 2. Server-Entscheidung nutzen
        return serverPermissions[order.id] === true;
    } 
    function switchAnalysisView(viewName, btn) {
        // 1. Buttons umschalten
        document.querySelectorAll('.analyse-toggle-btn').forEach(b => b.classList.remove('active'));
        if(btn) btn.classList.add('active');
        
        // 2. Container umschalten
        document.querySelectorAll('.analysis-subview').forEach(el => el.classList.add('hidden'));
        document.getElementById('view-' + viewName).classList.remove('hidden');
        
        // 3. WICHTIG: Charts neu rendern/resizen, da sie sonst in versteckten Containern oft kaputt gehen
        // Wir rufen einfach die Haupt-Render-Funktion auf, die ist idempotent genug.
        renderMaintenanceAnalysis();
    }
    function openAssetHistory(assetName) {
        const stats = globalAssetStats.find(a => a.name === assetName);
        if(!stats) return;
    
        document.getElementById('assetHistoryTitle').textContent = assetName;
        document.getElementById('assetHistorySubtitle').textContent = `${stats.total} Issues gesamt | ${stats.critical} Kritisch`;
        
        const container = document.getElementById('assetHistoryTimeline');
        container.innerHTML = '';
    
        // Zugehörige Orders finden und sortieren (Neu -> Alt)
        const historyOrders = maintenanceOrders.filter(o => stats.history.includes(o.id));
        historyOrders.sort((a,b) => new Date(b.date) - new Date(a.date));
    
        historyOrders.forEach(o => {
            const dateObj = new Date(o.date);
            const dateStr = dateObj.toLocaleDateString('de-DE', { day: '2-digit', month: 'short', year: 'numeric' });
            
            // Farben nach Prio
            let dotColor = "bg-blue-500 ring-blue-200";
            let icon = "info";
            if(o.priority === 'Critical' || o.isBlocker) { dotColor = "bg-red-500 ring-red-200"; icon = "report_problem"; }
            else if(o.priority === 'High') { dotColor = "bg-orange-500 ring-orange-200"; icon = "warning"; }
            else if(o.status === 'Completed') { dotColor = "bg-green-500 ring-green-200"; icon = "check_circle"; }
    
            const item = document.createElement('div');
            item.className = "ml-6 relative";
            item.innerHTML = `
                <span class="absolute -left-[31px] top-1 flex h-6 w-6 items-center justify-center rounded-full ring-4 ring-white ${dotColor} text-white">
                    <span class="material-icons-outlined text-[14px]">${icon}</span>
                </span>
                <div class="bg-white p-4 rounded-lg border border-slate-200 shadow-sm hover:shadow-md transition-shadow cursor-pointer" onclick="openWorkOrderModal('${o.id}')">
                    <div class="flex justify-between items-start mb-1">
                        <span class="text-xs font-bold text-slate-400 uppercase tracking-wider">${dateStr}</span>
                        <span class="text-[10px] bg-slate-100 text-slate-600 px-2 py-0.5 rounded-full border border-slate-200">${o.status}</span>
                    </div>
                    <h4 class="font-bold text-slate-800 text-sm mb-1">${o.task}</h4>
                    <p class="text-xs text-slate-500 line-clamp-2">${o.description || 'Keine Beschreibung'}</p>
                    <div class="mt-2 flex items-center gap-2 text-[10px] text-slate-400 border-t border-slate-100 pt-2">
                        <span>ID: ${o.id}</span>
                        <span>•</span>
                        <span>Von: ${o.logs?.[0]?.user || 'System'}</span>
                    </div>
                </div>
            `;
            container.appendChild(item);
        });
    
        // Modal öffnen
        const modal = document.getElementById('assetHistoryModal');
        modal.classList.remove('hidden');
        // document.body.classList.add('modal-active'); // Optional, wenn Body-Lock gewünscht
    }
    
    function closeAssetHistoryModal() {
        document.getElementById('assetHistoryModal').classList.add('hidden');
    }
    
    function renderChart(id, type, data, options = {}) {
        const ctx = document.getElementById(id);
        if (analysisCharts[id]) analysisCharts[id].destroy(); // Alten Chart löschen
        
        analysisCharts[id] = new Chart(ctx, {
            type: type,
            data: data,
            options: {
                responsive: true,
                ...options
            }
        });
    }
    
    // --- LIGHTBOX FUNKTIONEN ---
    function openLightbox(url, caption = '') {
        const modal = document.getElementById('lightboxModal');
        const img = document.getElementById('lightboxImage');
        const cap = document.getElementById('lightboxCaption');
        
        if (modal && img) {
            img.src = url;
            if(cap) cap.textContent = caption;
            modal.classList.remove('hidden');
            document.body.classList.add('modal-active'); // Scrollen verhindern
        }
    }
    
    function closeLightbox() {
        const modal = document.getElementById('lightboxModal');
        if (modal) {
            modal.classList.add('hidden');
            document.getElementById('lightboxImage').src = '';
            // Nur entfernen, wenn kein anderes Modal offen ist (einfacher Check)
            if (!document.querySelector('.modal:not(.hidden):not(#lightboxModal)')) {
                 document.body.classList.remove('modal-active');
            }
        }
    }
    
    document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape' && !document.getElementById('lightboxModal').classList.contains('hidden')) {
            closeLightbox();
        }
    });   
     
    function setupAssetTagInput(order, initialParsedAssets = []) {
        currentAssetTags = initialParsedAssets.map(asset => asset.label); 

        if (order.sourceTask) {
            const parentOrder = maintenanceOrders.find(p => p.id === order.sourceTask);
            availableAssetOptions = parentOrder ? (parentOrder.maintenanceObjects || []) : [];
            availableAssetOptions = availableAssetOptions.map(assetStr => {
                try { if(assetStr.startsWith('{')) return JSON.parse(assetStr).label; } catch(e) {}
                return assetStr;
            });
        } else {
            availableAssetOptions = allHardwareAssets;
        }
        
        renderAssetTags(); // Rendert die Tags basierend auf den `currentAssetTags`
    
        const input = document.getElementById('woEditAssetInput');
        const dropdown = document.getElementById('woEditAssetDropdown');
        const newInput = input.cloneNode(true);
        input.parentNode.replaceChild(newInput, input);
    
        newInput.addEventListener('input', () => {
            const val = newInput.value.toLowerCase();
            dropdown.innerHTML = '';
            if (!val) { dropdown.classList.add('hidden'); return; }
    
            const filtered = availableAssetOptions.filter(
                asset => asset.toLowerCase().includes(val) && !currentAssetTags.includes(asset)
            ).slice(0, 10);
    
            if (filtered.length > 0) {
                filtered.forEach(asset => {
                    const div = document.createElement('div');
                    div.className = 'tag-dropdown-item';
                    div.textContent = asset;
                    div.onclick = () => addAssetTag(asset);
                    dropdown.appendChild(div);
                });
                dropdown.classList.remove('hidden');
            } else {
                dropdown.classList.add('hidden');
            }
        });
        
        newInput.addEventListener('keydown', (e) => {
            if (e.key === 'Enter') {
                e.preventDefault();
                addAssetTag(newInput.value.trim());
            }
        });
    }
    
    function renderAssetTags() {
        const container = document.getElementById('woEditAssetTags');
        const input = document.getElementById('woEditAssetInput');
        Array.from(container.children).forEach(c => { if(c.classList.contains('tag-chip')) c.remove(); });
        currentAssetTags.forEach(tag => {
            const chip = document.createElement('span');
            chip.className = 'tag-chip';
            chip.innerHTML = `${tag}<span class="tag-chip-remove" onclick="removeAssetTag('${tag}')">×</span>`;
            container.insertBefore(chip, input);
        });
        input.value = '';
    }
    
    function addAssetTag(tag) {
        if (tag && !currentAssetTags.includes(tag)) {
            currentAssetTags.push(tag);
            renderAssetTags();
            
            // NEU: Liste unten aktualisieren (falls wir im Edit Mode eines Auftrags sind)
            const order = maintenanceOrders.find(o => o.id === currentOrderId);
            if (order && isWorkOrderInEditMode) {
                 renderEditBlockerList(order, currentAssetTags);
            }
        }
        document.getElementById('woEditAssetDropdown').classList.add('hidden');
        document.getElementById('woEditAssetInput').focus();
    }
    
    window.removeAssetTag = function(tag) {
        currentAssetTags = currentAssetTags.filter(t => t !== tag);
        renderAssetTags();
        
        const order = maintenanceOrders.find(o => o.id === currentOrderId);
        if (order && isWorkOrderInEditMode) {
             renderEditBlockerList(order, currentAssetTags);
        }
    }
    function switchToWoEditMode() {
        isWorkOrderInEditMode = true;
        
        // Aktuelles Order-Objekt laden
        const order = maintenanceOrders.find(o => o.id === currentOrderId);
        if (!order) return;
    
        // Berechtigung prüfen: Admin darf immer, andere nur wenn nicht abgeschlossen
        const canEdit = order.status !== 'Completed' || currentUserIsAdmin;
    
        // --- 1. HEADER BEREICH (Titel & Start-Datum) ---
        
        // Titel Input zeigen
        document.getElementById('woTitle').classList.add('hidden');
        document.getElementById('woEditTitleContainer').classList.remove('hidden');
        const titleInput = document.getElementById('woEditTitle');
        titleInput.readOnly = !canEdit;
        if(!canEdit) titleInput.classList.add('bg-slate-100', 'text-slate-500');
    
        // Datums-Anzeigen ausblenden
        const dateDisplay = document.getElementById('woDateDisplay');
        if(dateDisplay) dateDisplay.classList.add('hidden');
        
        const compDisplay = document.getElementById('woCompletionDisplay');
        if(compDisplay) compDisplay.classList.add('hidden');
    
        // Start-Datum Input (Header) einblenden & entsperren
        const dateContainer = document.getElementById('woEditDateContainer');
        dateContainer.classList.remove('hidden');
        
        const startDateInput = document.getElementById('woEditDate');
        const startShiftInput = document.getElementById('woEditStartShift');
        
        startDateInput.disabled = !canEdit;
        startShiftInput.disabled = !canEdit;
        
        if (canEdit) {
            startDateInput.classList.remove('bg-slate-100', 'text-slate-500');
            startShiftInput.classList.remove('bg-slate-100', 'text-slate-500');
        }
    
        // --- 2. DETAILS BEREICH (Beschreibung & Metadaten) ---
        
        document.getElementById('woDescriptionViewContainer').classList.add('hidden');
        document.getElementById('woEditDetailsContainer').classList.remove('hidden');
        
        const descInput = document.getElementById('woEditDescription');
        descInput.readOnly = !canEdit;
        
        // Location (Ort)
        document.getElementById('woLocationView').classList.add('hidden');
        document.getElementById('woLocationEdit').classList.remove('hidden');
        const plantInput = document.getElementById('woEditPlantIndex');
        plantInput.readOnly = !canEdit;
    
        // Flexible Felder (Prio, Kat, Assets) - Nur für Issues/Actions
        const isFlexible = order.type === 'I' || order.type === 'A';
        const metaContainer = document.getElementById('woEditMetaFields');
        const assetContainer = document.getElementById('woEditAssetContainer');
        
        if (metaContainer) metaContainer.style.display = isFlexible ? 'grid' : 'none';
        if (assetContainer) assetContainer.classList.toggle('hidden', !isFlexible);
    
        if (isFlexible) {
            document.getElementById('woEditPriority').disabled = !canEdit;
            document.getElementById('woEditCategory').disabled = !canEdit;
            
            // Asset Tags initialisieren
            // HINWEIS: Wir rufen hier setupAssetTagInput auf, um sicherzustellen, 
            // dass die Tags für diesen Edit-Vorgang frisch sind.
            const parsedAssets = (order.affectedAssets || []).map(assetStr => {
                try { if(assetStr.trim().startsWith('{')) return JSON.parse(assetStr); } catch(e){}
                const item = hardwareInventoryList.find(h => h.label === assetStr);
                return { id: item ? item.id : null, label: assetStr };
            });
            setupAssetTagInput(order, parsedAssets);
            
            const assetInput = document.getElementById('woEditAssetInput');
            if(assetInput) assetInput.disabled = !canEdit;
        }
    
        // --- 3. BLOCKER SETTINGS (Nur für Issues) ---
        
        const blockerSettings = document.getElementById('woEditBlockerSettings');
        if (blockerSettings) {
            if (order.type === 'I') {
                blockerSettings.classList.remove('hidden');
                
                // Unit Blocker Checkbox
                const unitCb = document.getElementById('woEditUnitBlocker');
                const isUnit = order.isUnitBlocker === true || order.isUnitBlocker === "true";
                unitCb.checked = isUnit;
                unitCb.disabled = !canEdit;
                
                // Hardware Liste rendern
                renderEditBlockerList(order, currentAssetTags);
                
                // Falls nicht editierbar, Checkboxen in der Liste sperren
                if (!canEdit) {
                    setTimeout(() => {
                        document.querySelectorAll('.edit-blocker-cb').forEach(cb => cb.disabled = true);
                    }, 50);
                }
            } else {
                blockerSettings.classList.add('hidden');
            }
        }
    
        // --- 4. ABSCHLUSS DATEN (Nur wenn Completed) ---
        
        const compEditContainer = document.getElementById('woEditCompletionContainer');
        if (compEditContainer) {
            if (order.status === 'Completed') {
                compEditContainer.classList.remove('hidden');
                // Abschlussdaten sollten für Admins/Manager immer editierbar sein, 
                // auch wenn der Auftrag "completed" ist (dafür ist das Feld ja da).
                // Wir nutzen hier 'canEdit' (was true ist, wenn Admin, oder false wenn User).
                // Falls User seine eigenen Fehler korrigieren soll, müsste man canEdit anpassen.
                // Aktuell: Nur Admins können abgeschlossene ändern.
                
                document.getElementById('woEditCompletedDate').disabled = !canEdit;
                document.getElementById('woEditCompletedShift').disabled = !canEdit;
            } else {
                compEditContainer.classList.add('hidden');
            }
        }
    
        // --- 5. BUTTONS UMSCHALTEN ---
        
        document.getElementById('statusActions').classList.add('hidden');
        document.getElementById('woEditButtons').classList.remove('hidden');
        
        const btnEdit = document.getElementById('btnEditOrder');
        if(btnEdit) btnEdit.classList.add('hidden');
        
        document.getElementById('woQuickActions').classList.add('hidden');
        
        // Visuelles Feedback für Inputs (Optional)
        const allEditInputs = document.querySelectorAll('#workOrderModal input:not([type="hidden"]), #workOrderModal textarea, #workOrderModal select');
        allEditInputs.forEach(el => {
            if (!el.readOnly && !el.disabled) {
                el.classList.add('ring-2', 'ring-blue-100', 'border-blue-400', 'bg-blue-50/20');
                el.classList.remove('border-slate-300');
            }
        });
    }
    
    function switchToWoViewMode() {
        isWorkOrderInEditMode = false;
        
        // Header Titel
        document.getElementById('woTitle').classList.remove('hidden');
        document.getElementById('woEditTitleContainer').classList.add('hidden');
        
        const dateDisplay = document.getElementById('woDateDisplay');
        if (dateDisplay) dateDisplay.classList.remove('hidden');
        
        // Completion Display wieder einblenden (falls Inhalt da ist)
        const compDisplay = document.getElementById('woCompletionDisplay');
        if (compDisplay && compDisplay.innerHTML !== '') {
            compDisplay.classList.remove('hidden');
        }
        
        // Edit-Container für Zeit ausblenden
        document.getElementById('woEditDateContainer').classList.add('hidden');
        // ------------------------------------------
        
        document.getElementById('woDescriptionViewContainer').classList.remove('hidden');
        document.getElementById('woEditDetailsContainer').classList.add('hidden');
        
        // Falls vorhanden (Blocker-Settings)
        const blockerSettings = document.getElementById('woEditBlockerSettings');
        if (blockerSettings) blockerSettings.classList.add('hidden');
        
        document.getElementById('woLocationView').classList.remove('hidden');
        document.getElementById('woLocationEdit').classList.add('hidden');
    
        document.getElementById('statusActions').classList.remove('hidden');
        document.getElementById('woEditButtons').classList.add('hidden');
        
        const btnEdit = document.getElementById('btnEditOrder');
        if (btnEdit) btnEdit.classList.remove('hidden');
        
        document.getElementById('woQuickActions').classList.remove('hidden');
        
        // Reset Styles der Inputs
        const allInputs = document.querySelectorAll('#workOrderModal input, #workOrderModal textarea, #workOrderModal select');
        allInputs.forEach(el => {
            el.classList.remove('ring-2', 'ring-blue-100', 'border-blue-400', 'bg-blue-50/20');
            el.classList.add('border-slate-300');
        });
        
        // Abschluss-Edit-Container ausblenden
        const compEditContainer = document.getElementById('woEditCompletionContainer');
        if (compEditContainer) compEditContainer.classList.add('hidden');
    }

    async function saveWorkOrderChanges() {
        const order = maintenanceOrders.find(o => o.id === currentOrderId);
        if (!order) return;
        
        let changes = [];
        
        // 1. ALLE Werte auslesen (Fix für ReferenceError)
        const newDate = document.getElementById('woEditDate').value;
        const newStartShift = document.getElementById('woEditStartShift').value;
        const newDesc = document.getElementById('woEditDescription').value.trim(); // Hier war der Fehler!
        
        // 2. Vergleiche Startdaten
        if (order.date !== newDate || (order.startShift || 'AM') !== newStartShift) {
            changes.push(`Startzeit geändert auf ${newDate} ${newStartShift}.`);
            order.date = newDate;
            order.startShift = newStartShift;
        }
        
        // 3. Vergleiche Beschreibung
        if ((order.description || '') !== newDesc) {
            changes.push("Beschreibung aktualisiert.");
            order.description = newDesc;
        }
    
        // 4. Abschlussdaten (Nur wenn Completed)
        if (order.status === 'Completed') {
            const newCompDate = document.getElementById('woEditCompletedDate').value;
            const newCompShift = document.getElementById('woEditCompletedShift').value;
            
            // Prüfung auf Änderung
            if (order.completedDate !== newCompDate || (order.completedShift || 'PM') !== newCompShift) {
                changes.push(`Abschlusszeit korrigiert auf ${newCompDate} ${newCompShift}.`);
                order.completedDate = newCompDate;
                order.completedShift = newCompShift;
            }
        }
    
        // 5. Flexible Tasks (Issues/Actions) Metadaten
        if (order.type === 'I' || order.type === 'A') {
            const newTitle = document.getElementById('woEditTitle').value.trim();
            const newPrio = document.getElementById('woEditPriority').value;
            const newCat = document.getElementById('woEditCategory').value;
            
            if (order.task !== newTitle) {
                changes.push("Titel geändert.");
                order.task = newTitle;
            }
            if (order.priority !== newPrio) {
                changes.push(`Priorität auf "${newPrio}" geändert.`);
                order.priority = newPrio;
            }
            const oldCat = order.topic || order.category;
            if (oldCat !== newCat) {
                changes.push(`Bereich geändert.`);
                order.topic = newCat;
                order.category = newCat;
            }
        }
    
        // 6. Blocker Logik (Issues)
        if (order.type === 'I') {
            const newUnitBlocker = document.getElementById('woEditUnitBlocker').checked;
            if (order.isUnitBlocker !== newUnitBlocker) {
                changes.push(`Anlagen-Sperre ${newUnitBlocker ? 'aktiviert' : 'aufgehoben'}.`);
                order.isUnitBlocker = newUnitBlocker;
            }
    
            // Hardware Blocker
            const oldBlockedIds = new Set(order.blockedAssetIds || []);
            const newBlockedIds = new Set(
                Array.from(document.querySelectorAll('.edit-blocker-cb:checked')).map(cb => cb.value)
            );
            
            const idsToBlock = [...newBlockedIds].filter(x => !oldBlockedIds.has(x));
            const idsToFree = [...oldBlockedIds].filter(x => !newBlockedIds.has(x));
            
            if (idsToBlock.length > 0 || idsToFree.length > 0) {
                changes.push(`Hardware-Sperren aktualisiert (+${idsToBlock.length} / -${idsToFree.length}).`);
                order.blockedAssetIds = [...newBlockedIds];
                
                // API Calls
                if (idsToBlock.length > 0) {
                    await fetch('/api/hardware/set_status', {
                        method: 'POST', headers: {'Content-Type': 'application/json'},
                        body: JSON.stringify({ ids: idsToBlock, status: 'Maintenance' })
                    });
                }
                if (idsToFree.length > 0) {
                    await fetch('/api/hardware/set_status', {
                        method: 'POST', headers: {'Content-Type': 'application/json'},
                        body: JSON.stringify({ ids: idsToFree, status: 'Available' })
                    });
                }
            }
            order.isBlocker = order.isUnitBlocker || order.blockedAssetIds.length > 0;
        }
    
        // 7. Ort (Facility Unit)
        const newPlantIndex = document.getElementById('woEditPlantIndex').value.trim();
        let newFacilityUnitId = document.getElementById('woEditFacilityUnitId').value;
        if (!newFacilityUnitId && facilityUnitMap[newPlantIndex]) {
            newFacilityUnitId = facilityUnitMap[newPlantIndex];
        }
    
        if (order.plantIndex !== newPlantIndex || order.facilityUnitId !== newFacilityUnitId) {
            changes.push(`Ort geändert.`);
            order.plantIndex = newPlantIndex;
            order.facilityUnitId = newFacilityUnitId; 
        }
      
        // 8. Speichern & Refresh
        if (changes.length > 0) {
            order.logs = order.logs || [];
            order.logs.push({
                user: currentUser,
                timestamp: new Date().toLocaleString(),
                note: `Auftrag bearbeitet: ${changes.join(' ')}`
            });
            await saveOrders();
            renderOrdersTable();
        }
        
        switchToWoViewMode();
        openWorkOrderModal(currentOrderId);
    }
    // --- SUCHFUNKTIONEN ---
    

    async function saveAllMaintenanceData() {
        try {
            await fetch('/api/maintenance/save_all', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    orders: maintenanceOrders,
                    definitions: taskDefinitions
                })
            });
        } catch (e) {
            console.error("Fehler beim Speichern der kombinierten Daten:", e);
            alert("Fehler beim Speichern der Daten. Bitte Konsole prüfen.");
        }
    }    

    function openModal(modalId) {
        const modal = document.getElementById(modalId);
        if (modal) {
            modal.classList.remove('hidden');
            // Optional: Body-Scroll sperren
            document.body.classList.add('modal-active');
        }
    }

    function closeModal(modalId) {
        const modal = document.getElementById(modalId);
        if (modal) {
            modal.classList.add('hidden');
            // Prüfen, ob noch andere Modale offen sind, bevor der Body-Scroll freigegeben wird
            const anyModalOpen = document.querySelector('.modal:not(.hidden)');
            if (!anyModalOpen) {
                document.body.classList.remove('modal-active');
            }
        }
    }
    async function saveOrders() { try { await fetch('/api/maintenance/save_orders', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({orders: maintenanceOrders}) }); } catch (e) { console.error("Fehler beim Speichern der Orders:", e); } }
    async function saveDefinitions() { try { await fetch('/api/maintenance/save_definitions', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({definitions: taskDefinitions}) }); } catch (e) { console.error("Fehler beim Speichern der Definitionen:", e); } }
    async function saveCompaniesData() { try { await fetch('/api/maintenance/save_companies', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({companies: companies}) }); } catch (e) { console.error("Fehler beim Speichern der Firmen:", e); } }

    function openAdHocContextModal() {
        document.getElementById('contextSearchInput').value = '';
        document.getElementById('contextSearchResults').innerHTML = '<p class="p-8 text-center text-slate-400 text-sm">Bitte geben Sie einen Suchbegriff ein.</p>';
        openModal('adHocContextModal');
    }

    function closeAdHocContextModal() {
        closeModal('adHocContextModal');
    }

    function selectAdHocContext(assetItem) {
        let valueToStore = assetItem;
        if (typeof assetItem === 'string') {
             valueToStore = { id: null, label: assetItem };
        }

        adHocContext = {
            type: 'asset',
            value: valueToStore // Speichert {id: "...", label: "..."}
        };
        closeAdHocContextModal();
        createLinkedIssue(); 
    }
    
    function createUnlinkedIssue() {
        adHocContext = {
            type: 'unlinked'
        };
        closeAdHocContextModal();
        createLinkedIssue();
    }

    document.getElementById('contextSearchInput').addEventListener('input', (e) => {
        const searchTerm = e.target.value.toLowerCase();
        const resultsContainer = document.getElementById('contextSearchResults');
        
        if (searchTerm.length < 2) {
            resultsContainer.innerHTML = '<p class="p-8 text-center text-slate-400 text-sm">Bitte geben Sie einen Suchbegriff ein.</p>';
            return;
        }

        // NEU: Filtern auf Objekt-Basis (item.label)
        const filteredAssets = hardwareInventoryList.filter(item => {
            // item ist jetzt ein Objekt {id, label, ...}
            // Fallback für alte Strings, falls noch welche da sind
            const text = item.label || item; 
            return text.toLowerCase().includes(searchTerm);
        });

        if (filteredAssets.length === 0) {
            resultsContainer.innerHTML = '<p class="p-4 text-center text-slate-500">Kein passendes Asset gefunden.</p>';
            return;
        }

        resultsContainer.innerHTML = '';
        
        filteredAssets.forEach(item => {
            const div = document.createElement('div');
            div.className = 'px-4 py-3 border-b border-slate-100 last:border-0 hover:bg-blue-50 cursor-pointer text-sm';
            
            // Text für die Anzeige
            const displayText = item.label || item;
            
            div.textContent = displayText;
            
            // Beim Klick übergeben wir jetzt das Objekt (oder den String, wenn alt)
            // Wir nutzen JSON.stringify, um das Objekt sicher als Argument zu übergeben
            // ABER: Da wir das Objekt global haben, reicht es, den Identifier zu übergeben.
            // Einfacher: Wir bauen eine Wrapper-Funktion im onclick, die den Scope nutzt.
            div.onclick = () => selectAdHocContext(item);
            
            resultsContainer.appendChild(div);
        });
    });
    function renderCompaniesTable() { const tbody = document.getElementById('companiesBody'); tbody.innerHTML = ''; if(companies.length === 0) { tbody.innerHTML = '<tr><td colspan="5" class="px-6 py-8 text-center text-slate-400 italic">Keine Firmen angelegt.</td></tr>'; return; } companies.forEach(comp => { const tr = document.createElement('tr'); tr.className = "hover:bg-slate-50 border-b border-slate-100"; tr.innerHTML = `<td class="px-6 py-3 font-medium text-slate-800">${comp.name}</td><td class="px-6 py-3 text-slate-600 text-xs">${comp.contact_person || '-'}</td><td class="px-6 py-3 text-slate-500 text-xs">${comp.email ? `<div><span class="material-icons-outlined text-[10px] align-middle mr-1">mail</span>${comp.email}</div>` : ''}${comp.phone ? `<div><span class="material-icons-outlined text-[10px] align-middle mr-1">phone</span>${comp.phone}</div>` : ''}</td><td class="px-6 py-3 text-slate-500 text-xs truncate max-w-xs" title="${comp.services || ''}">${comp.services || '-'}</td><td class="px-6 py-3 text-right space-x-1"><button class="text-slate-400 hover:text-blue-600 p-1 transition-colors" onclick="openCompanyModal('${comp.id}')"><span class="material-icons-outlined text-lg">edit</span></button><button class="text-slate-400 hover:text-red-600 p-1 transition-colors" onclick="deleteCompany('${comp.id}')"><span class="material-icons-outlined text-lg">delete_outline</span></button></td></tr>`; tbody.appendChild(tr); }); updateCompanyDropdown(); }
    function updateCompanyDropdown() { const select = document.getElementById('defExternalCompany'); if(!select) return; const firstOpt = select.options[0]; select.innerHTML = ''; select.appendChild(firstOpt); companies.forEach(comp => { const opt = document.createElement('option'); opt.value = comp.id; opt.textContent = comp.name; select.appendChild(opt); }); }
    function openCompanyModal(id = null) { document.getElementById('companyForm').reset(); document.getElementById('compId').value = ''; document.getElementById('compModalTitle').textContent = 'Firma anlegen'; if(id) { const comp = companies.find(c => c.id === id); if(comp) { document.getElementById('compId').value = comp.id; document.getElementById('compName').value = comp.name; document.getElementById('compContact').value = comp.contact_person || ''; document.getElementById('compEmail').value = comp.email || ''; document.getElementById('compPhone').value = comp.phone || ''; document.getElementById('compServices').value = comp.services || ''; document.getElementById('compModalTitle').textContent = 'Firma bearbeiten'; } } openModal('companyModal'); }
    function closeCompanyModal() { closeModal('companyModal'); }
    function saveCompany() { const id = document.getElementById('compId').value; const name = document.getElementById('compName').value.trim(); if(!name) { alert("Name ist erforderlich."); return; } const data = { id: id || `COMP-${Date.now()}`, name: name, contact_person: document.getElementById('compContact').value.trim(), email: document.getElementById('compEmail').value.trim(), phone: document.getElementById('compPhone').value.trim(), services: document.getElementById('compServices').value.trim() }; if(id) { const idx = companies.findIndex(c => c.id === id); if(idx > -1) companies[idx] = data; } else { companies.push(data); } saveCompaniesData(); renderCompaniesTable(); closeCompanyModal(); }
    function deleteCompany(id) { if(confirm("Firma wirklich löschen?")) { companies = companies.filter(c => c.id !== id); saveCompaniesData(); renderCompaniesTable(); } }

    async function loadTestBenchOptions() { const select = document.getElementById('defTestbench'); if (!select) return; try { const response = await fetch('/api/test_cells'); let cells = []; if (response.ok) cells = await response.json(); select.innerHTML = ''; const facilityOpt = document.createElement('option'); facilityOpt.value = "Facility"; facilityOpt.textContent = "Facility"; select.appendChild(facilityOpt); const sortedCells = cells.sort((a, b) => a.TESTCELL.localeCompare(b.TESTCELL)); sortedCells.forEach(cell => { if (cell.TESTCELL === "Facility") return; const opt = document.createElement('option'); opt.value = cell.TESTCELL; opt.textContent = cell.TESTCELL; select.appendChild(opt); }); } catch (error) { select.innerHTML = '<option>Facility</option>'; } }
    async function loadHardwareInventory() { 
        try { 
            const response = await fetch('/api/test_equipment/data'); 
            if (response.ok) {
                // API liefert jetzt [{id:..., label:..., sn:..., pn:...}]
                hardwareInventoryList = await response.json();
                populateHardwareDatalist(); 
            }
        } catch(e) { 
            hardwareInventoryList = []; 
        } 
    }    
    function populateHardwareDatalist() { 
            const dl = document.getElementById('hardwareList'); 
            dl.innerHTML = ''; 
            hardwareInventoryList.forEach(item => { 
                const opt = document.createElement('option'); 
                // Wir setzen das Label als Value, damit der User danach suchen kann
                opt.value = item.label; 
                // Wir speichern die ID in einem Dataset-Attribut (für späteren Lookup)
                opt.dataset.id = item.id;
                dl.appendChild(opt); 
            }); 
        }
    function switchTab(name, btn) { document.querySelectorAll('.tab-content').forEach(e => e.classList.add('hidden')); document.getElementById('tab-' + name).classList.remove('hidden'); document.querySelectorAll('.tab-btn').forEach(b => { b.classList.remove('text-primary', 'border-primary'); b.classList.add('text-slate-500', 'border-transparent'); }); btn.classList.remove('text-slate-500', 'border-transparent'); btn.classList.add('text-primary', 'border-primary'); if(name === 'active' || name === 'history') renderOrdersTable(); if(name === 'definitions') renderDefinitionsTable(); if(name === 'companies') renderCompaniesTable(); if(name === 'analysis') renderMaintenanceAnalysis(); if(name === 'logbook')renderLogbook() }
    function toggleGroup(bodyId, btn) { const body = document.getElementById(bodyId); const icon = btn.querySelector('.material-icons-outlined:last-child'); if (body.classList.contains('hidden')) { body.classList.remove('hidden'); icon.classList.remove('rotate-180'); } else { body.classList.add('hidden'); icon.classList.add('rotate-180'); } }
    function toggleTaskRows(taskId, btn) { const container = document.querySelector(`.child-container-${taskId}`); const icon = btn.querySelector('.material-icons-outlined'); if (container.classList.contains('hidden')) { container.classList.remove('hidden'); icon.classList.add('rotate-90'); } else { container.classList.add('hidden'); icon.classList.remove('rotate-90'); } }

function renderOrdersTable() {
        const historyTbody = document.getElementById('historyOrdersBody'); historyTbody.innerHTML = ''; let doneCount = 0;
        
        // --- 1. NEUE BUCKET STRUKTUR ---
        const buckets = { overdue: [], issues: [], upcoming: [], outlook: [], future: [] };
        
        const today = new Date(); today.setHours(0,0,0,0);
        const date14 = new Date(today); date14.setDate(today.getDate() + 14);
        const date60 = new Date(today); date60.setDate(today.getDate() + 60);
        
        const taskMap = new Map(); 
        const childrenMap = new Map(); 
        const rootTasks = [];
        
        maintenanceOrders.forEach(t => taskMap.set(t.id, t));
        
        // --- 2. HIERARCHIE & HISTORY ---
        maintenanceOrders.forEach(t => {
            if(t.status === 'Completed') {
                doneCount++;
                const tr = document.createElement('tr'); tr.className = "hover:bg-slate-50 group border-b border-slate-100";
                tr.innerHTML = `<td class="px-6 py-3 font-mono text-xs text-slate-500">${t.id}</td><td class="px-6 py-3 text-sm font-medium text-slate-700">${t.task}</td><td class="px-6 py-3 text-xs text-slate-500">${t.completedDate || t.date}</td><td class="px-6 py-3 text-xs text-slate-500">${t.completedBy || '-'}</td><td class="px-6 py-3 text-right"><button class="text-primary hover:underline text-xs font-medium" onclick="openWorkOrderModal('${t.id}')">Protokoll</button></td>`;
                historyTbody.appendChild(tr);
            } else {
                const parentId = t.sourceTask || t.linkedIssue;
                if(parentId && taskMap.has(parentId) && taskMap.get(parentId).status !== 'Completed') { 
                    if(!childrenMap.has(parentId)) childrenMap.set(parentId, []); 
                    childrenMap.get(parentId).push(t); 
                } else { 
                    rootTasks.push(t); 
                }
            }
        });

        // Helper für Abhängigkeiten
        const hasOverdueChildren = (taskId) => {
            if (!childrenMap.has(taskId)) return false;
            return childrenMap.get(taskId).some(child => {
                const childDate = new Date(child.date);
                if ((child.type === 'A' || child.type === 'M') && childDate < today) return true;
                return hasOverdueChildren(child.id);
            });
        };

        // --- 3. SORTIERUNG IN BUCKETS ---
        rootTasks.forEach(t => { 
            const d = new Date(t.date); 
            
            if (t.type === 'I') {
                // Issues: Überfällig nur wenn kritisch oder Kinder überfällig sind
                if (t.priority === 'Critical' || t.isBlocker || hasOverdueChildren(t.id)) {
                    buckets.overdue.push(t); 
                } else {
                    buckets.issues.push(t); // Neuer Bucket "Offene Mängel"
                }
            } else {
                // Wartung/Maßnahme: Klassisch nach Datum
                if (d < today) buckets.overdue.push(t);
                else if (d <= date14) buckets.upcoming.push(t); 
                else if (d <= date60) buckets.outlook.push(t); 
                else buckets.future.push(t); 
            }
        });

        const sorter = (a, b) => new Date(a.date) - new Date(b.date); 
        Object.values(buckets).forEach(b => b.sort(sorter));
        
        // 1. Alle offenen Aufträge (Wartungen, Issues, Maßnahmen), die nicht 'Completed' sind
        let totalOpenCount = maintenanceOrders.filter(t => t.status !== 'Completed').length;
        
        // 2. Alle offenen Issues (Typ 'I'), die nicht 'Completed' sind
        let totalIssueCount = maintenanceOrders.filter(t => t.type === 'I' && t.status !== 'Completed').length;
        
        const countChildrenTypes = (taskId) => { let counts = { issues: 0, actions: 0 }; if (childrenMap.has(taskId)) { childrenMap.get(taskId).forEach(child => { if (child.type === 'I') counts.issues++; if (child.type === 'A') counts.actions++; const subCounts = countChildrenTypes(child.id); counts.issues += subCounts.issues; counts.actions += subCounts.actions; }); } return counts; };
        
        // --- 4. RENDER FUNKTION ---
        const renderBucket = (bucketData, containerId, bodyId, countId) => {
            const container = document.getElementById(containerId); 
            const body = document.getElementById(bodyId); 
            const countBadge = document.getElementById(countId);
            
            if (bucketData.length === 0) { container.classList.add('hidden'); return; }
            container.classList.remove('hidden'); 
            
            // --- NEUE REKURSIVE ZÄHLUNG (Mit Endlosschleifen-Schutz) ---
            const countAllInTree = (tasks, visited = new Set()) => {
                let count = 0;
                tasks.forEach(t => {
                    // Verhindere Zirkelbezüge!
                    if (visited.has(t.id)) return;
                    visited.add(t.id);
                    
                    count++; 
                    if (childrenMap.has(t.id)) {
                        count += countAllInTree(childrenMap.get(t.id), visited); 
                    }
                });
                return count;
            };
            countBadge.textContent = countAllInTree(bucketData);
            // -----------------------------------------------------------
            
            let listHtml = `<div class="flex flex-col bg-white divide-y divide-slate-100">`;
            
            const renderRowsRec = (tasks, level) => {
                let html = '';
                tasks.forEach(task => {
                    const hasChildren = childrenMap.has(task.id); 
                    const childCounts = countChildrenTypes(task.id);
                    
                    let indicatorsHtml = '';
                    // Blocker Badges
                    if (task.isUnitBlocker) {
                        indicatorsHtml += `<span class="ml-2 inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold bg-red-600 text-white border border-red-700 uppercase tracking-wider" title="Betriebseinheit gesperrt">ANLAGE GESPERRT</span>`;
                    }
                    if (task.blockedAssetIds && task.blockedAssetIds.length > 0) {
                        indicatorsHtml += `<span class="ml-2 inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold bg-orange-100 text-orange-800 border border-orange-200 uppercase tracking-wider" title="${task.blockedAssetIds.length} Assets gesperrt">HW SPERRE</span>`;
                    }
                    // Legacy Fallback (falls alte Daten noch nur 'isBlocker' haben)
                    if (task.isBlocker && !task.isUnitBlocker && (!task.blockedAssetIds || task.blockedAssetIds.length === 0)) {
                         indicatorsHtml += `<span class="ml-2 inline-flex items-center px-1.5 py-0.5 rounded text-[9px] font-bold bg-red-100 text-red-700 border border-red-200 uppercase tracking-wider">BLOCKER</span>`;
                    }
                    if (childCounts.issues > 0) indicatorsHtml += `<span class="ml-2 inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-yellow-100 text-yellow-700"><span class="material-icons-outlined text-[10px] mr-0.5">warning</span>${childCounts.issues}</span>`;
                    if (childCounts.actions > 0) indicatorsHtml += `<span class="ml-2 inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-green-100 text-green-700"><span class="material-icons-outlined text-[10px] mr-0.5">build</span>${childCounts.actions}</span>`;
                    
                    let iconHtml = '';
                    const parseAssetLabel = (val) => {
                        try { if (val.trim().startsWith('{')) return JSON.parse(val).label || val; } catch(e) {}
                        return val;
                    };

                    if(task.type === 'I') { 
                        if (task.priority === 'OPEN') iconHtml = `<div class="w-5 h-5 rounded border border-slate-300 bg-slate-100 text-slate-500 flex items-center justify-center" title="Unbewertet"><span class="material-icons-outlined text-sm">question_mark</span></div>`;
                        else if (task.status === 'Review Ready') iconHtml = `<div class="w-5 h-5 rounded border border-purple-500 bg-purple-50 text-purple-600 flex items-center justify-center"><span class="material-icons-outlined text-sm">visibility</span></div>`; 
                        else if (task.status === 'In Progress') iconHtml = `<div class="w-5 h-5 rounded-full border-2 border-yellow-500 bg-yellow-50 text-yellow-600 flex items-center justify-center"><span class="material-icons-outlined text-sm">play_arrow</span></div>`;
                        else iconHtml = `<div class="w-5 h-5 rounded-full border-2 border-slate-300"></div>`; 
                    } else if (task.type === 'A') { 
                        if (task.status === 'In Progress') iconHtml = `<div class="w-5 h-5 rounded-full border-2 border-green-500 bg-green-50 text-green-600 flex items-center justify-center"><span class="material-icons-outlined text-sm">play_arrow</span></div>`;
                        else iconHtml = `<div class="w-5 h-5 rounded-full border-2 border-slate-300"></div>`; 
                    } else { 
                        if(task.status === 'In Progress') iconHtml = `<div class="w-5 h-5 rounded-full border-2 border-blue-500 bg-blue-50 text-blue-600 flex items-center justify-center"><span class="material-icons-outlined text-sm">play_arrow</span></div>`; 
                        else if(task.status === 'Review Ready') iconHtml = `<div class="w-5 h-5 rounded-full border-2 border-purple-500 bg-purple-50 text-purple-600 flex items-center justify-center"><span class="material-icons-outlined text-sm">visibility</span></div>`; 
                        else iconHtml = `<div class="w-5 h-5 rounded-full border-2 border-slate-300"></div>`; 
                    }

                    let metaParts = [`<span class="font-mono font-semibold text-slate-500">${task.id}</span>`];
                    const displayArea = task.topic || task.category;
                    if (displayArea) { metaParts.push(`<span class="text-blue-600 font-medium">${displayArea}</span>`); }
                    let locationString = task.testbench || '';
                    if (task.plantIndex) {
                        if (locationString) locationString += ` (${task.plantIndex})`;
                        else locationString = task.plantIndex;
                    }
                    if (locationString) {
                        metaParts.push(locationString);
                    }                    
                    if ((task.type === 'I' || task.type === 'A') && task.affectedAssets && task.affectedAssets.length > 0) { 
                        const firstLabel = parseAssetLabel(task.affectedAssets[0]);
                        const count = task.affectedAssets.length; 
                        const label = count === 1 ? firstLabel : `${count} Assets`; 
                        const tooltip = task.affectedAssets.map(parseAssetLabel).join(', '); 
                        metaParts.push(`<span class="font-mono text-slate-600 bg-slate-100 px-1 rounded cursor-help" title="${tooltip}">${label}</span>`); 
                    }
                    else if ((task.type === 'I' || task.type === 'A') && task.affectedAsset) { 
                        metaParts.push(`<span class="font-mono text-slate-600 bg-slate-100 px-1 rounded" title="Betroffenes Asset">${parseAssetLabel(task.affectedAsset)}</span>`); 
                    } 
                    else if (task.maintenanceObjects && task.maintenanceObjects.length) { 
                        const firstLabel = parseAssetLabel(task.maintenanceObjects[0]);
                        const suffix = (task.maintenanceObjects.length > 1 ? ` +${task.maintenanceObjects.length-1}` : '');
                        metaParts.push(firstLabel + suffix); 
                    }
                    else if (task.maintenanceObject) { 
                        metaParts.push(parseAssetLabel(task.maintenanceObject)); 
                    }

                    const metaString = metaParts.join(' <span class="mx-2 text-slate-300">|</span> ');
                    
                    // --- NEUES DATUMS LABEL DESIGN ---
                    const dObj = new Date(task.date);
                    const dateDisplay = dObj.toLocaleDateString('de-DE', { day: '2-digit', month: 'short' });
                    let dateLabel = '';
                    
                    if (task.type === 'I') {
                         dateLabel = `<div class="flex flex-col items-end"><span class="text-[10px] text-slate-400 uppercase tracking-wider font-bold">Gemeldet</span><span class="text-xs text-slate-600 font-medium">${dateDisplay}</span></div>`;
                    } else {
                         const isLate = dObj < today && task.status !== 'Completed';
                         const colorClass = isLate ? 'text-red-600 font-bold bg-red-50 px-2 py-0.5 rounded' : 'text-slate-600 font-medium';
                         dateLabel = `<div class="flex flex-col items-end"><span class="text-[10px] text-slate-400 uppercase tracking-wider font-bold">Fällig</span><span class="text-xs ${colorClass}">${dateDisplay}</span></div>`;
                    }
                    // ---------------------------------

                    const toggleBtn = hasChildren ? `<button onclick="event.stopPropagation(); toggleTaskRows('${task.id}', this)" class="p-1 -ml-2 mr-2 text-slate-400 hover:text-slate-600 hover:bg-slate-100 rounded-full transition-colors"><span class="material-icons-outlined text-lg transition-transform duration-200">chevron_right</span></button>` : `<span class="w-6 mr-2 inline-block"></span>`;
                    const paddingLeft = level * 40; const childContainerClass = `child-container-${task.id} hidden`;

                    let prioBadge = '';
                    if (task.type === 'I' && task.priority) { 
                        const p = task.priority; 
                        let pClass = "bg-gray-100 text-gray-600 border-gray-200"; 
                        if (p === 'OPEN') { pClass = "bg-slate-200 text-slate-600 border-slate-300 font-bold border-dashed"; }
                        if (p === 'Medium') pClass = "bg-blue-50 text-blue-700 border-blue-200"; 
                        if (p === 'High') pClass = "bg-orange-50 text-orange-700 border-orange-200"; 
                        if (p === 'Critical') pClass = "bg-red-50 text-red-700 border-red-200 font-bold"; 
                        const pMap = { 'Low': 'Niedrig', 'Medium': 'Mittel', 'High': 'Hoch', 'Critical': 'Kritisch', 'OPEN': 'UNBEWERTET' }; 
                        prioBadge = `<span class="ml-2 px-1.5 py-0.5 rounded-[3px] text-[9px] uppercase tracking-wider border ${pClass}">${pMap[p] || p}</span>`; 
                    }

                    let externBadge = '';
                    if (task.externalCompanyId) { const comp = companies.find(c => c.id === task.externalCompanyId); const compName = comp ? comp.name : 'Unbekannt'; externBadge = `<span class="ml-2 px-1.5 py-0.5 rounded text-[9px] font-bold bg-indigo-100 text-indigo-700 border border-indigo-200 uppercase tracking-wider" title="Ausführung durch: ${compName}">Extern</span>`; }

                    html += `<div class="task-group"><div class="task-row group flex items-center p-3 hover:bg-slate-50 transition-colors cursor-pointer relative" style="padding-left: ${12 + paddingLeft}px;" onclick="openWorkOrderModal('${task.id}')">
                                ${level > 0 ? `<div class="absolute left-0 top-0 bottom-0 border-l border-slate-200" style="left: ${paddingLeft - 18}px;"></div> <div class="absolute w-4 border-t border-slate-200" style="left: ${paddingLeft - 18}px; top: 50%;"></div>` : ''}
                                <div class="flex items-center flex-grow min-w-0"><div class="flex-shrink-0 w-8">${toggleBtn}</div><div class="mr-3 flex-shrink-0">${iconHtml}</div><div class="flex-grow min-w-0"><div class="flex items-center gap-2"><span class="text-sm font-bold text-slate-800 truncate">${task.task}</span>${task.type === 'I' ? `<span class="px-1.5 py-0.5 rounded text-[10px] font-bold bg-yellow-100 text-yellow-700 border border-yellow-200 uppercase">ISSUE</span>` : ''}${task.type === 'A' ? `<span class="px-1.5 py-0.5 rounded text-[10px] font-bold bg-green-100 text-green-700 border border-green-200 uppercase">ACTION</span>` : ''}${prioBadge}${externBadge}${indicatorsHtml}</div><div class="text-xs text-slate-500 mt-0.5 flex items-center truncate">${metaString}</div></div></div>
                                <div class="task-meta-col pl-4 text-right">${dateLabel}</div></div>${hasChildren ? `<div class="child-container-${task.id} hidden">${renderRowsRec(childrenMap.get(task.id), level + 1)}</div>` : ''}</div>`;

                });
                return html;
            };
            
            listHtml += renderRowsRec(bucketData, 0); 
            listHtml += `</div>`; 
            body.innerHTML = listHtml;
        };

        // Die Bucket-Render-Aufrufe (mit der rekursiven Zählung, die wir vorhin eingebaut haben)
        renderBucket(buckets.overdue, 'groupOverdue', 'bodyOverdue', 'countOverdue');
        renderBucket(buckets.issues, 'groupIssues', 'bodyIssues', 'countIssues');
        renderBucket(buckets.upcoming, 'groupUpcoming', 'bodyUpcoming', 'countUpcoming');
        renderBucket(buckets.outlook, 'groupOutlook', 'bodyOutlook', 'countOutlook');
        renderBucket(buckets.future, 'groupFuture', 'bodyFuture', 'countFuture');
        
        const emptyState = document.getElementById('emptyStateActive'); 
        if(totalOpenCount === 0) emptyState.classList.remove('hidden'); else emptyState.classList.add('hidden');
        
        // Die Werte in die Kacheln schreiben
        document.getElementById('openTasksCount').textContent = totalOpenCount; 
        document.getElementById('openIssuesCount').textContent = totalIssueCount; 
        document.getElementById('completedTasksCount').textContent = doneCount;

        applyFiltersActive();
    }

    function renderDefinitionsTable() { 
        // 1. Daten in Gruppen sortieren
        const buckets = { weekly: [], monthly: [], yearly: [], other: [] };
        
        taskDefinitions.forEach(def => {
            const unit = (def.intervalUnit || '').toLowerCase();
            if (unit.includes('woche')) { buckets.weekly.push(def); }
            else if (unit.includes('monat')) { buckets.monthly.push(def); }
            else if (unit.includes('jahr')) { buckets.yearly.push(def); }
            else { buckets.other.push(def); }
        });

        // 2. Sortieren innerhalb der Gruppen (Nach Topic, dann nach Titel)
        const sorter = (a, b) => (a.topic || '').localeCompare(b.topic || '') || a.task.localeCompare(b.task);
        Object.values(buckets).forEach(b => b.sort(sorter));

        // 3. Render Helper für eine Gruppe
        const renderGroup = (tasks, bodyId, countId, groupId) => {
            const container = document.getElementById(bodyId);
            const countBadge = document.getElementById(countId);
            const groupDiv = document.getElementById(groupId);
            
            countBadge.textContent = tasks.length;
            
            if (tasks.length === 0) {
                // Leere Gruppen ausblenden (außer vielleicht Sonstige, wenn man will)
                if (groupId === 'groupDefOther') groupDiv.classList.add('hidden');
                container.innerHTML = '<p class="text-center py-4 text-sm text-slate-400 italic">Keine Einträge in dieser Kategorie.</p>';
                return;
            } else {
                groupDiv.classList.remove('hidden');
            }

            // Tabellenkopf
            let html = `
                <table class="w-full text-sm text-left table-fixed"> <!-- table-fixed ist wichtig! -->
                    <thead class="text-xs text-gray-700 uppercase bg-slate-50 border-b border-slate-100">
                        <tr>
                            <th class="px-6 py-3 w-36">ID</th> <!-- Fest 8rem -->
                            <th class="px-6 py-3 w-1/4">Titel</th> <!-- 25% -->
                            <th class="px-6 py-3 w-1/6">Bereich</th> <!-- ~16% -->
                            <th class="px-6 py-3 w-32">Intervall</th>
                            <th class="px-6 py-3 w-1/6">Index/Ort</th>
                            <th class="px-6 py-3 w-24 text-center">Assets</th>
                            <th class="px-6 py-3 w-32 text-center">Extern</th>
                            <th class="px-6 py-3 w-24 text-center">Status</th>
                            <th class="px-6 py-3 w-36 text-right">Aktionen</th>
                        </tr>
                    </thead>
                    <tbody class="divide-y divide-slate-100">`;

            tasks.forEach(def => {
                let locDisplay = def.testbench || ''; 
                if(def.plantIndex) locDisplay += ` (${def.plantIndex})`; 
                const isActive = def.active !== false; 
                const statusBadge = isActive 
                    ? '<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-[10px] font-bold bg-green-100 text-green-800 border border-green-200">AKTIV</span>' 
                    : '<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-[10px] font-bold bg-slate-100 text-slate-500 border border-slate-200">INAKTIV</span>'; 
                
                let externInfo = '-'; 
                if(def.externalCompanyId) { 
                    const comp = companies.find(c => c.id === def.externalCompanyId); 
                    externInfo = comp ? `<span class="text-xs font-medium text-indigo-600 truncate max-w-[100px] block" title="${comp.name}">${comp.name}</span>` : 'Extern'; 
                }

                // Asset Tooltip
                const assets = def.maintenanceObjects || [];
                const assetCount = assets.length;
                let assetHtml = '-';
                if (assetCount > 0) {
                    const tooltipLabels = assets.map(assetStr => {
                        try { if (assetStr.trim().startsWith('{')) return JSON.parse(assetStr).label || assetStr; } catch(e){}
                        return assetStr;
                    });
                    const tooltipText = tooltipLabels.join('\\n'); 
                    assetHtml = `<span class="inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800 border border-blue-200 cursor-help" title="${tooltipText}">
                                    <span class="material-icons-outlined text-xs mr-1">inventory_2</span>${assetCount}
                                 </span>`;
                }

                html += `
                    <tr class="hover:bg-slate-50 border-b border-slate-50 transition-colors last:border-0">
                        <td class="px-6 py-3 font-mono text-xs font-medium text-slate-400">${def.id}</td>
                        <td class="px-6 py-3 font-medium text-slate-800">${def.task}</td>
                        <td class="px-6 py-3 text-slate-600 text-xs">${def.topic || '-'}</td>
                        <td class="px-6 py-3 text-slate-500 text-xs font-mono">${def.intervalValue} ${def.intervalUnit}</td>
                        <td class="px-6 py-3 text-slate-500 text-xs">${locDisplay}</td>
                        <td class="px-6 py-3 text-center">${assetHtml}</td>
                        <td class="px-6 py-3 text-center">${externInfo}</td>
                        <td class="px-6 py-3 text-center">${statusBadge}</td>
                        <td class="px-6 py-3 text-right space-x-1">
                            <button class="text-slate-400 hover:text-purple-600 p-1 transition-colors" onclick="openHistoryListModal('${def.id}')" title="Historie anzeigen"><span class="material-icons-outlined text-lg">history</span></button>
                            <button class="text-slate-400 hover:text-blue-600 p-1 transition-colors" onclick="openDefinitionModal('${def.id}')" title="Bearbeiten"><span class="material-icons-outlined text-lg">edit</span></button>
                            <button class="text-slate-400 hover:text-red-600 p-1 transition-colors" onclick="deleteDefinition('${def.id}')" title="Löschen"><span class="material-icons-outlined text-lg">delete_outline</span></button>
                        </td>
                    </tr>`;
            });

            html += `</tbody></table>`;
            container.innerHTML = html;
        };

        // 4. Rendern der 4 Gruppen
        renderGroup(buckets.weekly, 'bodyDefWeekly', 'countDefWeekly', 'groupDefWeekly');
        renderGroup(buckets.monthly, 'bodyDefMonthly', 'countDefMonthly', 'groupDefMonthly');
        renderGroup(buckets.yearly, 'bodyDefYearly', 'countDefYearly', 'groupDefYearly');
        renderGroup(buckets.other, 'bodyDefOther', 'countDefOther', 'groupDefOther');

        applyFiltersDefinitions();
    }
    function deleteDefinition(id) { const ordersToDelete = maintenanceOrders.filter(o => { if (o.parentTaskId !== id) return false; if (o.status !== 'Pending') return false; const hasChildren = maintenanceOrders.some(child => child.sourceTask === o.id || child.linkedIssue === o.id); return !hasChildren; }); let confirmMsg = `Wartungsplan ${id} wirklich löschen?`; if (ordersToDelete.length > 0) confirmMsg += `\\n\\nACHTUNG: Es werden automatisch ${ordersToDelete.length} offene (Pending) Aufträge mitgelöscht!`; if(confirm(confirmMsg)) { taskDefinitions = taskDefinitions.filter(d => d.id !== id); saveDefinitions(); if (ordersToDelete.length > 0) { const idsToDelete = ordersToDelete.map(o => o.id); maintenanceOrders = maintenanceOrders.filter(o => !idsToDelete.includes(o.id)); saveOrders(); renderOrdersTable(); } renderDefinitionsTable(); } }


    function openWorkOrderModal(id, forceType) {
        if(!id && forceType === 'I') { createLinkedIssue(); return; }
    
        currentOrderId = id;
        const order = maintenanceOrders.find(o => o.id === id);
        if(!order) return;
    
        // --- Navigation / Breadcrumbs ---
        const navParent = document.getElementById('navParentContainer');
        const navChildren = document.getElementById('navChildrenContainer');
        navParent.innerHTML = '';
        navChildren.innerHTML = '';
    
        const parentId = order.sourceTask || order.linkedIssue;
        if (parentId) {
            navParent.innerHTML = `<button onclick="openWorkOrderModal('${parentId}')" class="flex items-center hover:text-blue-600 transition-colors"><span class="material-icons-outlined text-sm mr-1">arrow_back</span> ${parentId}</button>`;
        } else if (order.type === 'M' && order.parentTaskId) {
            const def = taskDefinitions.find(d => d.id === order.parentTaskId);
            const defTitle = def ? def.task : "Unbekannt";
            const defLabel = `Def: ${defTitle} (${order.parentTaskId})`;
            navParent.innerHTML = `<span class="text-slate-500 cursor-default" title="Basierend auf Wartungsplan"><span class="material-icons-outlined text-xs align-middle mr-1">content_paste</span>${defLabel}</span>`;
        } else {
            navParent.innerHTML = '<span class="text-slate-400 cursor-default">Start</span>';
        }
    
        const children = maintenanceOrders.filter(c => c.sourceTask === order.id || c.linkedIssue === order.id);
        if (children.length > 0) {
            children.forEach(child => {
                let colorClass = 'bg-slate-100 text-slate-600 border-slate-200';
                let icon = 'subdirectory_arrow_right';
                if (child.type === 'I') { colorClass = 'bg-yellow-50 text-yellow-700 border-yellow-200'; icon = 'report_problem'; }
                if (child.type === 'A') { colorClass = 'bg-green-50 text-green-700 border-green-200'; icon = 'build'; }
                let check = '';
                if (child.status === 'Completed') { colorClass = 'bg-slate-50 text-slate-400 border-slate-200 line-through decoration-slate-400'; check = '✓ '; }
                const chip = document.createElement('div');
                chip.className = `nav-chip ${colorClass}`;
                chip.title = child.task;
                chip.onclick = () => openWorkOrderModal(child.id);
                chip.innerHTML = `<span class="material-icons-outlined text-[10px] mr-1">${icon}</span> ${check}${child.id}`;
                navChildren.appendChild(chip);
            });
        }
    
        // --- Befüllen der Anzeige- UND Bearbeitungsfelder ---
        document.getElementById('woId').textContent = order.id;
        document.getElementById('woTitle').textContent = order.task;
        
        // Beschreibung
        document.getElementById('woDescriptionViewContainer').textContent = order.description || (order.type === 'I' ? "Keine Beschreibung." : (order.type === 'A' ? "Keine Anweisung." : ""));
        
        // Titel Input (für Edit Mode)
        document.getElementById('woEditTitle').value = order.task;
        
        // --- ZEIT-LOGIK (KORRIGIERT) ---
        
        // 1. Start-Datum (Immer anzeigen)
        const startShiftDisplay = order.startShift || 'AM';
        document.getElementById('woDateDisplay').textContent = `${order.date} (${startShiftDisplay})`;
        
        // 2. Abschluss-Datum (Nur anzeigen, wenn Completed)
        const compDisplay = document.getElementById('woCompletionDisplay');
        
        if (order.status === 'Completed' && order.completedDate) {
            const endShiftDisplay = order.completedShift || 'PM';
            // Pfeil + Check-Icon + Datum
            compDisplay.innerHTML = `<span class="text-slate-400">&rarr;</span> <span class="material-icons-outlined text-sm">check_circle</span> ${order.completedDate} (${endShiftDisplay})`;
            compDisplay.classList.remove('hidden');
        } else {
            // Ausblenden, wenn nicht fertig
            compDisplay.innerHTML = '';
            compDisplay.classList.add('hidden');
        }
        
        // --- EDIT INPUTS INITIALISIEREN ---
        
        // Start Inputs (immer füllen)
        document.getElementById('woEditDate').value = order.date;
        document.getElementById('woEditStartShift').value = startShiftDisplay;
        
        // Abschluss Inputs (immer füllen oder resetten, damit sie bereit sind)
        if (order.completedDate) {
            document.getElementById('woEditCompletedDate').value = order.completedDate;
            document.getElementById('woEditCompletedShift').value = order.completedShift || 'PM';
        } else {
            // WICHTIG: Resetten, damit keine alten Daten drin stehen, wenn man auf "Edit" klickt
            document.getElementById('woEditCompletedDate').value = ''; 
            document.getElementById('woEditCompletedShift').value = 'PM';
        }
        document.getElementById('woEditDescription').value = order.description || '';
        
        // Befüllen der neuen Felder (nur für Issues/Actions relevant)
        if (order.type === 'I' || order.type === 'A') {
            document.getElementById('woEditPriority').value = order.priority || 'Medium';
            // Sicherstellen, dass die Dropdowns existieren bevor wir den Wert setzen
            const categorySelect = document.getElementById('woEditCategory');
            if (categorySelect) categorySelect.value = order.topic || order.category || 'Allgemein';
        }
    
    
        const locString = (order.testbench || '') + (order.plantIndex ? ` (${order.plantIndex})` : '');
        document.getElementById('woTestbench').textContent = locString ? locString : '-';
        document.getElementById('woIndex').textContent = '';
        document.getElementById('woEditPlantIndex').value = order.plantIndex || '';
        document.getElementById('woEditFacilityUnitId').value = order.facilityUnitId || '';
        const topicEl = document.getElementById('woTopic');
        const sepEl = document.getElementById('woTopicSeparator');
        
        if (topicEl && order.topic) {
            topicEl.textContent = order.topic;
            topicEl.parentElement.classList.remove('hidden'); // Parent ist der LocationView, das passt nicht ganz
            // Besser:
            topicEl.style.display = 'inline';
            if(sepEl) sepEl.classList.remove('hidden');
        } else {
            if(topicEl) topicEl.textContent = "";
            if(sepEl) sepEl.classList.add('hidden');
        }
    
        const assetContainer = document.getElementById('woAssetTags');
        assetContainer.innerHTML = '';
        let assets = order.maintenanceObjects || (order.maintenanceObject ? [order.maintenanceObject] : []);
        if ((order.type === 'I' || order.type === 'A') && (!assets || assets.length === 0)) {
             assets = order.affectedAssets || (order.affectedAsset ? [order.affectedAsset] : []);
        }
        if (assets.length > 0) {
            assets.forEach(asset => {
                let displayLabel = asset;
                // Parse JSON falls nötig
                try {
                    if (asset.trim().startsWith('{')) {
                        const obj = JSON.parse(asset);
                        displayLabel = obj.label || asset;
                    }
                } catch(e) {}

                assetContainer.innerHTML += `<span class="inline-flex items-center px-2 py-0.5 rounded bg-white text-slate-600 border border-slate-200 text-[12px] font-mono tracking-tight shadow-sm cursor-help" title="Asset: ${displayLabel}"><span class="material-icons-outlined text-[10px] text-slate-400 mr-1">precision_manufacturing</span>${displayLabel}</span>`;
            });
        } else {
            assetContainer.innerHTML = '';
        }
    
        const prioBadge = document.getElementById('woPrioBadge');
        const reviewIcon = document.getElementById('woReviewIcon');
        const btnAction = document.getElementById('btnCreateAction');
        const btnIssue = document.getElementById('btnCreateIssue');
        const btnDelete = document.getElementById('btnDeleteOrder');
        const btnTriage = document.getElementById('btnTriage');
        btnAction.classList.add('hidden'); btnIssue.classList.add('hidden'); btnTriage.classList.add('hidden');
        const btnEdit = document.getElementById('btnEditOrder');
            if(btnEdit) {
                btnEdit.onclick = () => switchToWoEditMode();

                const isUnevaluatedIssue = order.type === 'I' && order.priority === 'OPEN';
                
                if (isUnevaluatedIssue) {
                    btnEdit.style.display = 'none';
                    // Optional: Falls du Admins trotzdem das Editieren erlauben willst:
                    // if (currentUserIsAdmin) btnEdit.style.display = 'inline-block';
                } else {
                    // Standard-Logik: Anzeigen, wenn nicht abgeschlossen oder User Admin ist
                    const canEdit = order.status !== 'Completed' || currentUserIsAdmin;
                    btnEdit.style.display = canEdit ? 'inline-block' : 'none';
                }
            }
    
        prioBadge.innerHTML = '';
        if(order.type === 'M') { btnIssue.classList.remove('hidden'); } 
        else if(order.type === 'I') { 
            if (order.priority === 'OPEN') {
                 prioBadge.className = "px-2 py-0.5 rounded text-xs font-bold bg-slate-200 text-slate-600 tracking-wider border border-slate-300";
                 prioBadge.textContent = "UNBEWERTET";
                 btnTriage.classList.remove('hidden');
            } else {
                let pClass = 'bg-gray-100 text-gray-800';
                if(order.priority === 'Medium') pClass = 'bg-yellow-100 text-yellow-800';
                if(order.priority === 'High') pClass = 'bg-orange-100 text-orange-800';
                if(order.priority === 'Critical') pClass = 'bg-red-100 text-red-800';
                prioBadge.className = `px-2 py-0.5 rounded text-xs font-bold tracking-wider ${pClass}`;
                prioBadge.textContent = order.priority.toUpperCase();
                btnAction.classList.remove('hidden'); 
            }
        } else if(order.type === 'A') { 
            prioBadge.className = "px-2 py-0.5 rounded text-xs font-bold bg-green-100 text-green-700 tracking-wider border border-green-200"; 
            prioBadge.textContent = "ACTION";
        }
    
        if (order.requiresReview === true || order.requiresReview === "true") { reviewIcon.classList.remove('hidden'); } else { reviewIcon.classList.add('hidden'); }
        if (order.status === 'In Progress') { btnIssue.disabled = false; btnIssue.classList.remove('opacity-50', 'cursor-not-allowed'); btnAction.disabled = false; btnAction.classList.remove('opacity-50', 'cursor-not-allowed'); } else { btnIssue.disabled = true; btnIssue.classList.add('opacity-50', 'cursor-not-allowed'); btnAction.disabled = true; btnAction.classList.add('opacity-50', 'cursor-not-allowed'); }
        if (order.status === 'Completed' && !currentUserIsAdmin) { btnDelete.style.display = 'none'; } else { btnDelete.style.display = 'inline-block'; }
    
        const clContainer = document.getElementById('woChecklistContainer');
        clContainer.innerHTML = '';
        if (order.type !== 'I' && order.type !== 'A') {
            document.getElementById('woChecklistSection').classList.remove('hidden');
            const isExecutionActive = (order.status === 'In Progress');
            if (order.checklist && order.checklist.length > 0) {
                order.checklist.forEach((item, idx) => {
                    const isChecked = order.checklistState && order.checklistState[idx] === true;
                    const checkedAttr = isChecked ? 'checked' : '';
                    const disabledAttr = isExecutionActive ? '' : 'disabled';
                    const cursorClass = isExecutionActive ? 'cursor-pointer' : 'cursor-not-allowed';
                    const div = document.createElement('label');
                    div.className = `checklist-item ${isChecked ? 'checked' : ''} ${cursorClass}`;
                    div.id = `chk-item-${idx}`;
                    div.innerHTML = `<input type="checkbox" ${checkedAttr} ${disabledAttr} onchange="toggleChecklistItem(${idx}, this.checked)" class="${cursorClass} transition-colors"><span class="text-sm select-none flex-grow">${item}</span>`;
                    clContainer.appendChild(div);
                });
            } else { clContainer.innerHTML = '<span class="text-xs text-slate-400 italic">Keine Checkliste definiert.</span>'; }
        } else { document.getElementById('woChecklistSection').classList.add('hidden'); }
    
        const partsContainer = document.getElementById('woConsumables');
        partsContainer.innerHTML = '';
        if (order.type !== 'I') {
             document.getElementById('woConsumablesSection').classList.remove('hidden');
             let rawCons = [];
             if (order.consumablesList && order.consumablesList.length > 0) {
                 rawCons = order.consumablesList;
             } else if (order.consumables) {
                 rawCons = order.consumables.split(',');
             } else if (order.spareParts) {
                 rawCons = order.spareParts.split(',');
             }
             let cons = rawCons.map(s => s.trim()).filter(s => s);
             if(cons.length > 0) {
                cons.forEach(p => { 
                    partsContainer.innerHTML += `<span class="inline-flex items-center px-2 py-1 rounded-md text-xs font-mono bg-slate-100 text-slate-600 border border-slate-200">${p}</span>`; 
                });
             } else { 
                 partsContainer.innerHTML = '<span class="text-xs text-slate-400 italic">Kein Material.</span>'; 
             }
        } else {
             document.getElementById('woConsumablesSection').classList.add('hidden');}
    
        const attachContainer = document.getElementById('woAttachmentsView');
        attachContainer.innerHTML = '';
        
        let attachments = [];
        // 1. Anhänge aus der Definition holen (falls es eine Wartung ist)
        if(order.type === 'M' && order.parentTaskId) { 
            const def = taskDefinitions.find(d => d.id === order.parentTaskId); 
            if(def) { 
                if(def.images) attachments.push(...def.images); 
                if(def.documents) attachments.push(...def.documents); 
            } 
        }
        // 2. Anhänge aus dem Auftrag selbst (z.B. Fotos bei Issues)
        if (order.attachments) attachments.push(...order.attachments);
    
        if(attachments.length > 0) {
            attachments.forEach(a => { 
                const div = document.createElement('div'); 
                div.className = 'attachment-item group hover:border-blue-300 transition-colors'; 
                
                // NEU: Wir prüfen auf Dateiendungen ODER data:image
                const isImage = a.dataUrl && (
                    a.dataUrl.startsWith('data:image') || 
                    /\.(jpg|jpeg|png|gif|webp)$/i.test(a.name)
                );
    
                if(isImage) { 
                    // BILD: Klickbar machen für Lightbox
                    div.innerHTML = `
                        <img src="${a.dataUrl}" class="cursor-zoom-in hover:opacity-80 transition-opacity" 
                             onclick="event.stopPropagation(); openLightbox('${a.dataUrl}', '${a.name}')">
                        <span class="truncate text-xs cursor-pointer hover:text-blue-600" 
                              onclick="openLightbox('${a.dataUrl}', '${a.name}')">${a.name}</span>
                    `; 
                } else { 
                    // DOKUMENT: Nur Icon und Name (Nicht klickbar für Lightbox)
                    // Optional: Hier könnte man einen Download-Link einbauen
                    div.innerHTML = `
                        <a href="${a.dataUrl}" target="_blank" class="flex items-center gap-2 w-full text-slate-600 hover:text-blue-600">
                            <span class="material-icons-outlined text-slate-400">description</span>
                            <span class="truncate text-xs">${a.name}</span>
                        </a>
                    `; 
                } 
                attachContainer.appendChild(div); 
            });
        } else { 
            attachContainer.innerHTML = '<p class="text-xs text-slate-400 italic">Keine Dokumente.</p>'; 
        }
        renderStatusActions(order);
        renderAuditTrail(order);
        
        // Sicherstellen, dass der Ansichtsmodus aktiv ist, wenn das Modal geöffnet wird
        switchToWoViewMode();
        
        // --- NEU: Blocker Badges rendern ---
        const blockerContainer = document.getElementById('woBlockerBadges');
        blockerContainer.innerHTML = '';
    
        if (order.isUnitBlocker === true || order.isUnitBlocker === "true") {
            blockerContainer.innerHTML += `<span class="px-2 py-0.5 rounded text-[10px] font-bold bg-red-600 text-white border border-red-700 uppercase tracking-wider shadow-sm">ANLAGE GESPERRT</span>`;
        }
        
        if (order.blockedAssetIds && order.blockedAssetIds.length > 0) {
            blockerContainer.innerHTML += `<span class="px-2 py-0.5 rounded text-[10px] font-bold bg-orange-100 text-orange-800 border border-orange-200 uppercase tracking-wider">HW SPERRE (${order.blockedAssetIds.length})</span>`;
        }
        // Legacy Fallback
        if ((order.isBlocker === true || order.isBlocker === "true") && !order.isUnitBlocker && (!order.blockedAssetIds || order.blockedAssetIds.length === 0)) {
             blockerContainer.innerHTML += `<span class="px-2 py-0.5 rounded text-[10px] font-bold bg-red-100 text-red-700 border border-red-200 uppercase tracking-wider">BLOCKER</span>`;
        }
    
        openModal('workOrderModal');
    }
    function renderEditBlockerList(order, currentAssetTags) {
        const list = document.getElementById('woEditBlockedAssetsList');
        list.innerHTML = '';
        
        // Aktuell gespeicherte Sperren
        const blockedSet = new Set(order.blockedAssetIds || []);
        
        if (currentAssetTags.length === 0) {
            list.innerHTML = '<p class="text-xs text-slate-400 italic">Keine Assets verknüpft.</p>';
            return;
        }
    
        currentAssetTags.forEach(assetLabel => {
            // Wir müssen die ID finden (über das globale Inventar)
            const inventoryItem = hardwareInventoryList.find(h => h.label === assetLabel);
            
            if (inventoryItem && inventoryItem.id) {
                const isChecked = blockedSet.has(inventoryItem.id) ? 'checked' : '';
                const div = document.createElement('label');
                div.className = "flex items-center gap-2 hover:bg-slate-50 p-1 rounded cursor-pointer";
                div.innerHTML = `
                    <input type="checkbox" value="${inventoryItem.id}" ${isChecked} class="edit-blocker-cb rounded border-gray-300 text-orange-600 focus:ring-orange-500 w-4 h-4">
                    <span class="text-xs text-slate-700 truncate">${assetLabel}</span>
                `;
                list.appendChild(div);
            } else {
                // Asset ohne ID (Freitext) kann nicht technisch geblockt werden, nur informativ anzeigen?
                // Wir lassen es hier weg, da wir den Status nicht in der DB setzen können.
            }
        });
    }

    function toggleChecklistItem(index, isChecked) {
        const order = maintenanceOrders.find(o => o.id === currentOrderId);
        if (!order) return;
        if (order.status !== 'In Progress') {
            const checkboxes = document.querySelectorAll('#woChecklistContainer input[type="checkbox"]');
            if(checkboxes[index]) { checkboxes[index].checked = !isChecked; }
            alert("Checklisten können nur bearbeitet werden, wenn der Auftrag den Status 'In Progress' hat.");
            return;
        }
        if (!order.checklistState || typeof order.checklistState !== 'object') { order.checklistState = {}; }
        order.checklistState[index] = isChecked;
        const row = document.getElementById(`chk-item-${index}`);
        if (row) { if (isChecked) row.classList.add('checked'); else row.classList.remove('checked'); }
        saveOrders();
    }

    function closeWorkOrderModal() { document.getElementById('workOrderModal').classList.add('hidden'); }
    function renderStatusActions(order) { 
        const badge = document.getElementById('woStatusBadge'); 
        const actions = document.getElementById('statusActions'); 
        actions.innerHTML = ''; 

        // Hilfsfunktion für den neuen Button-Stil
        // farbe: 'blue', 'green', 'yellow', 'red', 'purple'
        const createBtn = (label, icon, color, onclick, disabled = false) => {
            const opacity = disabled ? 'opacity-50 cursor-not-allowed' : 'hover:shadow-md active:scale-[0.98] transition-all';
            return `
            <button onclick="${onclick}" ${disabled ? 'disabled' : ''} 
                class="w-full py-3 rounded-lg border-2 border-${color}-100 bg-${color}-50 text-${color}-700 font-bold uppercase tracking-wider flex items-center justify-center gap-2 ${opacity}">
                <span class="material-icons-outlined text-lg">${icon}</span>
                ${label}
            </button>`;
        };

        // Leerer Platzhalter für das Grid Layout
        const emptySlot = `<div></div>`;

        if (order.status === 'Completed') { 
            badge.className = "px-3 py-1 rounded-full text-xs font-bold bg-green-100 text-green-800 border border-green-200"; 
            badge.textContent = "COMPLETED"; 
            
            // Layout: Links Wiedereröffnen, Rechts Leer (oder andersrum, je nach Wunsch)
            // Hier: Wiedereröffnen Links
            actions.innerHTML = `
                ${createBtn('Wieder öffnen', 'replay', 'slate', "changeStatus('In Progress')")}
                ${emptySlot}
            `;

        } else if (order.status === 'Review Ready') { 
            badge.className = "px-3 py-1 rounded-full text-xs font-bold bg-purple-100 text-purple-800 border border-purple-200"; 
            badge.textContent = "REVIEW READY"; 
            
            // NEU: Berechtigungs-Check
            const canApprove = canUserReview(order);
            
            // Wenn der User es selbst ausgeführt hat, darf er (in strengen Systemen) nicht selbst prüfen.
            // Sollen wir das erzwingen? (order.executedBy !== currentUserId)
            // Für jetzt lassen wir es offen, aber der Check ist hier möglich.
            
            actions.innerHTML = `
                ${createBtn('Ablehnen', 'thumb_down', 'red', "changeStatus('In Progress')", !canApprove)}
                ${createBtn('Freigabe', 'verified', 'purple', "changeStatus('Completed')", !canApprove)}
            `;

        } else if (order.status === 'In Progress') { 
            badge.className = "px-3 py-1 rounded-full text-xs font-bold bg-blue-100 text-blue-800 border border-blue-200"; 
            badge.textContent = "IN PROGRESS"; 
            
            const finishLabel = order.requiresReview ? "Zur Prüfung" : "Abschließen"; 
            const finishIcon = order.requiresReview ? "send" : "check_circle"; 
            
            // Layout: Links Pause (Gelb), Rechts Fertig (Grün)
            actions.innerHTML = `
                ${createBtn('Pause', 'pause', 'yellow', "changeStatus('Pending')")}
                ${createBtn(finishLabel, finishIcon, 'green', "changeStatus('Completed')")}
            `;

        } else { 
            // START ZUSTAND (Pending / OPEN)
            let btnText = "Starten"; 
            if (order.status === 'OPEN') btnText = " Starten"; 
            
            badge.className = "px-3 py-1 rounded-full text-xs font-bold bg-yellow-100 text-yellow-800 border border-yellow-200"; 
            badge.textContent = order.status.toUpperCase(); 
            
            if (order.type === 'I' && order.priority === 'OPEN') {
                const canTriage = canUserReview(order);
                
                if (canTriage) {
                    // Darf bewerten -> Button zeigen
                    btnTriage.classList.add('hidden');
                    
                    // Wir zeigen den Button im Grid anstelle des Info-Textes
                    actions.innerHTML = `
                        <div class="col-span-2">
                             <button onclick="openTriageModal()" class="w-full py-3 rounded-lg border-2 border-indigo-100 bg-indigo-50 text-indigo-700 font-bold uppercase tracking-wider flex items-center justify-center gap-2 hover:bg-indigo-100 transition-colors">
                                <span class="material-icons-outlined text-lg">rule</span>
                                Issue Bewerten
                            </button>
                        </div>`;
                } else {
                    // Darf NICHT bewerten -> Info Text
                    btnTriage.classList.add('hidden');
                    actions.innerHTML = `
                        <div class="col-span-2 flex items-center justify-center p-3 bg-slate-50 border border-slate-200 rounded-lg text-slate-400 text-xs italic">
                            <span class="material-icons-outlined text-sm mr-2">lock</span>
                            Warten auf Bewertung durch verantwortliches Engineering
                        </div>`;
                }
            } else {
                // Layout: Links Leer, Rechts Starten (Blau)
                actions.innerHTML = `
                    ${emptySlot}
                    ${createBtn(btnText, 'play_arrow', 'blue', "changeStatus('In Progress')")}
                `;
            }
        } 
    }
    function renderAuditTrail(order) { 
        const c = document.getElementById('auditTrailContainer'); 
        c.innerHTML = ''; 
        const logs = order.logs || []; 
        
        if(logs.length === 0) { 
            c.innerHTML = '<div class="flex h-full items-center justify-center text-slate-400 text-sm italic">Keine Einträge.</div>'; 
            return; 
        } 
        
        [...logs].reverse().forEach(l => { 
            const isSys = l.user === 'System'; 
            const div = document.createElement('div'); 
            div.className = `flex flex-col gap-1 ${isSys ? 'items-center my-4' : 'items-start mb-4'}`; 
            
            // --- NEU: Datum formatieren (Uhrzeit entfernen) ---
            let dateDisplay = l.timestamp;
            try {
                // Versuch 1: Wenn es ein Standard-String ist "DD.MM.YYYY, HH:MM:SS"
                if (dateDisplay.includes(',')) {
                    dateDisplay = dateDisplay.split(',')[0];
                } 
                // Versuch 2: Falls es ein ISO-String ist oder anders
                else {
                    const d = new Date(l.timestamp);
                    if (!isNaN(d.getTime())) {
                        dateDisplay = d.toLocaleDateString('de-DE');
                    }
                }
            } catch(e) {
                // Fallback: Original anzeigen, wenn Parsing fehlschlägt
            }
            // --------------------------------------------------
    
            if(isSys) { 
                div.innerHTML = `<span class="text-[10px] font-bold text-slate-400 uppercase tracking-wider bg-slate-100 px-2 py-0.5 rounded-full border border-slate-200">${l.note} &bull; ${dateDisplay}</span>`; 
            } else { 
                div.innerHTML = `<div class="flex items-baseline gap-2"><span class="text-xs font-bold text-slate-700">${l.user}</span><span class="text-[10px] text-slate-400">${dateDisplay}</span></div><div class="bg-white p-3 rounded-tr-lg rounded-br-lg rounded-bl-lg border border-slate-200 shadow-sm text-sm text-slate-800 whitespace-pre-wrap max-w-[90%]">${l.note}</div>`; 
            } 
            c.appendChild(div); 
        }); 
    }

    function addLogEntry() { const input = document.getElementById('logNoteInput'); const val = input.value.trim(); if(!val) return; const order = maintenanceOrders.find(o => o.id === currentOrderId); if(order) { order.logs.push({ user: currentUser, timestamp: new Date().toLocaleString(), note: val }); saveOrders(); renderAuditTrail(order); input.value = ''; } }
    async function changeStatus(newStatus) {
        const order = maintenanceOrders.find(o => o.id === currentOrderId);
        if (!order) return;

        const wasBlockerIssue = order.type === 'I' && order.isBlocker;

        if (newStatus === 'Completed' && order.requiresReview && order.status !== 'Review Ready') {
            newStatus = 'Review Ready';
            order.executedBy = currentUserId;
            order.logs.push({ user: 'System', timestamp: new Date().toLocaleString(), note: `Fertig gemeldet. Warten auf Freigabe.` });
        }
        if (newStatus === 'Completed' || newStatus === 'Review Ready') {
            if (order.checklist && order.checklist.length > 0) {
                const allChecked = order.checklist.every((_, idx) => order.checklistState && order.checklistState[idx]);
                if (!allChecked) {
                    if (!confirm("Die Checkliste ist noch nicht vollständig abgehakt. Trotzdem abschließen?")) return;
                }
            }
        }
        if (order.status === 'Review Ready' && newStatus === 'In Progress') {
            order.logs.push({ user: currentUser, timestamp: new Date().toLocaleString(), note: `Freigabe abgelehnt. Zurück zur Bearbeitung.` });
        } else if (newStatus !== 'Review Ready') {
            order.logs.push({ user: 'System', timestamp: new Date().toLocaleString(), note: `Status geändert zu ${newStatus}` });
        }
        order.status = newStatus;
        let definitionsHaveChanged = false;
    
        // ... (Logik für M-Type Completed & Folgeauftrag bleibt identisch) ...
        if (newStatus === 'Completed') {
            order.completedBy = currentUser;
            order.completedDate = new Date().toISOString().split('T')[0];
            
            if (order.type === 'M' && order.parentTaskId) {
                const def = taskDefinitions.find(d => d.id === order.parentTaskId);
                if (def && def.active !== false) {
                    const nextDate = calculateNextDueDate(def.intervalValue, def.intervalUnit, order.completedDate);
                    createOrderFromDef(def.id, nextDate, true);
                    def.startDate = order.completedDate;
                    definitionsHaveChanged = true;
                    order.logs.push({ user: 'System', timestamp: new Date().toLocaleString(), note: `Folgeauftrag für ${nextDate} geplant.` });
                }
            }
        }
    
        // ... (Logik für A-Type Linked Issue bleibt identisch) ...
        if ((newStatus === 'Completed' || newStatus === 'Review Ready') && order.type === 'A' && order.linkedIssue) {
            const parentIssue = maintenanceOrders.find(p => p.id === order.linkedIssue);
            if (parentIssue && parentIssue.status !== 'Completed') {
                const siblingActions = maintenanceOrders.filter(s => s.linkedIssue === parentIssue.id && s.id !== order.id);
                const allSiblingsDone = siblingActions.every(s => s.status === 'Completed');
                if (allSiblingsDone) {
                    if (parentIssue.status !== 'Review Ready') {
                        parentIssue.status = 'Review Ready';
                        parentIssue.logs.push({ user: 'System', timestamp: new Date().toLocaleString(), note: `Alle Maßnahmen abgeschlossen. Status auf 'Review Ready' gesetzt.` });
                    }
                }
            }
        }
    
        if (definitionsHaveChanged) {
            await saveAllMaintenanceData();
        } else {
            await saveOrders();
        }
        
        // --- NEU: HARDWARE STATUS RESET (UUID-BASIERT) ---
        if (newStatus === 'Completed' && wasBlockerIssue) {
            const targetIds = [];
            
            (order.affectedAssets || []).forEach(assetStr => {
                try {
                    if (assetStr.trim().startsWith('{')) {
                        const obj = JSON.parse(assetStr);
                        if (obj.id) targetIds.push(obj.id);
                    }
                } catch(e) {}
            });

            if (targetIds.length > 0) {
                try {
                    const response = await fetch('/api/hardware/set_status', {
                        method: 'POST',
                        headers: {'Content-Type': 'application/json'},
                        body: JSON.stringify({
                            ids: targetIds, // Sende IDs
                            status: 'Available'
                        })
                    });
                    if (!response.ok) { throw new Error('Server-Antwort war nicht OK.'); }
                    console.log("Hardware-Status erfolgreich zurückgesetzt.");
                } catch (error) {
                    console.error("Fehler:", error);
                    alert("Hinweis: Hardware-Status konnte nicht automatisch zurückgesetzt werden.");
                }
            }
        }
        // -------------------------------------------------
   
        renderStatusActions(order);
        renderAuditTrail(order);
        renderOrdersTable();
        renderDefinitionsTable();
        openWorkOrderModal(currentOrderId);
    }
    function deleteCurrentOrder() { if (!currentOrderId) return; const order = maintenanceOrders.find(o => o.id === currentOrderId); if (!order) return; if (order.status === 'Completed' && !currentUserIsAdmin) { alert("Abgeschlossene Aufträge können nur von Admins gelöscht werden."); return; } if (confirm(`Möchten Sie den Auftrag "${order.task}" (${order.id}) wirklich unwiderruflich löschen?`)) { maintenanceOrders = maintenanceOrders.filter(o => o.id !== currentOrderId); saveOrders(); closeWorkOrderModal(); renderOrdersTable(); } }
    function openTriageModal() { 
        const order = maintenanceOrders.find(o => o.id === currentOrderId); 
        if(!order) return; 
        
        document.getElementById('triageIssueTitle').textContent = order.task; 
        document.getElementById('triageIssueDesc').textContent = order.description || "Keine Beschreibung"; 
        
        // Prio setzen
        const currentPrio = order.priority || "OPEN";
        const radio = document.querySelector(`input[name="triagePriority"][value="${currentPrio}"]`);
        if(radio) radio.checked = true;
        else document.querySelectorAll('input[name="triagePriority"]').forEach(r => r.checked = false);
    
        // Unit Blocker setzen (Rückwärtskompatibel: altes 'isBlocker' wird als Unit Blocker interpretiert, falls isUnitBlocker fehlt)
        const isUnitBlocked = order.isUnitBlocker === true || order.isUnitBlocker === "true" || order.isBlocker === true;
        document.getElementById('triageUnitBlocker').checked = isUnitBlocked; 
    
        // Hardware Liste bauen
        const assetSection = document.getElementById('triageAssetSection');
        const assetList = document.getElementById('triageAssetList');
        assetList.innerHTML = '';
    
        const assets = [];
        if (order.affectedAssets) assets.push(...order.affectedAssets);
        if (order.maintenanceObjects) assets.push(...order.maintenanceObjects);
    
        if (assets.length > 0) {
            assetSection.classList.remove('hidden');
            const blockedIds = new Set(order.blockedAssetIds || []); // Bereits gesperrte IDs
    
            assets.forEach(assetStr => {
                let label = assetStr;
                let id = null;
                try {
                    if (assetStr.trim().startsWith('{')) {
                        const obj = JSON.parse(assetStr);
                        label = obj.label;
                        id = obj.id;
                    } else {
                        // Fallback für alte Strings: ID im globalen Inventar suchen
                        const item = hardwareInventoryList.find(h => h.label === assetStr);
                        if(item) id = item.id;
                    }
                } catch(e){}
    
                if (id) {
                    const isChecked = blockedIds.has(id) ? 'checked' : '';
                    const div = document.createElement('div');
                    div.className = "flex items-center gap-3 p-2 bg-white border border-slate-200 rounded hover:bg-blue-50";
                    div.innerHTML = `
                        <input type="checkbox" value="${id}" ${isChecked} class="triage-asset-cb w-4 h-4 text-orange-600 border-gray-300 rounded focus:ring-orange-500">
                        <span class="text-sm text-slate-700 truncate" title="${label}">${label}</span>
                    `;
                    assetList.appendChild(div);
                }
            });
            
            // Wenn keine validen IDs gefunden wurden, Hinweis zeigen
            if (assetList.children.length === 0) {
                assetList.innerHTML = '<p class="text-xs text-slate-400 italic p-2">Keine verknüpfte Hardware mit gültiger ID gefunden.</p>';
            }
    
        } else {
            assetSection.classList.add('hidden');
        }
    
        openModal('triageModal'); 
    }

    function closeTriageModal() { 
        closeModal('triageModal'); 
    }    
    async function submitTriage() { 
        const order = maintenanceOrders.find(o => o.id === currentOrderId); 
        if(!order) return; 
    
        const priorityEl = document.querySelector('input[name="triagePriority"]:checked'); 
        if (!priorityEl) { alert("Bitte eine Priorität wählen."); return; } 
        
        // 1. Werte setzen
        order.priority = priorityEl.value; 
        order.isUnitBlocker = document.getElementById('triageUnitBlocker').checked;
        
        // Legacy Feld 'isBlocker' synchron halten für Abwärtskompatibilität
        // Ein Auftrag ist ein "Blocker", wenn Unit gesperrt ist ODER Hardware gesperrt ist.
        
        // 2. Hardware Status aktualisieren
        const checkboxes = document.querySelectorAll('.triage-asset-cb');
        const idsToBlock = [];
        const idsToFree = [];
        
        checkboxes.forEach(cb => {
            if (cb.checked) idsToBlock.push(cb.value);
            else idsToFree.push(cb.value);
        });
        
        order.blockedAssetIds = idsToBlock; // Speichern welche WIR blockieren
        order.isBlocker = order.isUnitBlocker || idsToBlock.length > 0;
    
        // API Calls für Hardware Status
        if (idsToBlock.length > 0) {
            await fetch('/api/hardware/set_status', {
                method: 'POST', headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({ ids: idsToBlock, status: 'Maintenance' })
            });
        }
        if (idsToFree.length > 0) {
            // Nur freigeben, wenn sie vorher blockiert waren (Status 'Maintenance')
            // Das übernimmt das Backend idealerweise oder wir setzen 'Available'.
            await fetch('/api/hardware/set_status', {
                method: 'POST', headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({ ids: idsToFree, status: 'Available' })
            });
        }
    
        order.logs.push({ 
            user: currentUser, 
            timestamp: new Date().toLocaleString(), 
            note: `Triage: ${order.priority}, Unit-Block: ${order.isUnitBlocker ? 'Ja' : 'Nein'}, HW-Block: ${idsToBlock.length} Teile.` 
        }); 
        
        await saveOrders(); 
        renderOrdersTable(); 
        closeTriageModal(); 
        openWorkOrderModal(currentOrderId); 
    }
    function createLinkedIssue() {
        document.getElementById('createIssueForm').reset();
        
        // 1. Definitionen ZUERST
        const pInput = document.getElementById('issuePlantIndex');
        const idInput = document.getElementById('issueFacilityUnitId');
        
        // --- FIX: parentOrder definieren, BEVOR wir es nutzen ---
        // Achtung: Wenn wir ein Ad-Hoc Issue machen, ist currentOrderId vielleicht null/undefined.
        // Das .find() gibt dann undefined zurück. Wir müssen also prüfen, ob parentOrder existiert.
        const parentOrder = currentOrderId ? maintenanceOrders.find(o => o.id === currentOrderId) : null;
        // ---------------------------------------------------------

        // 2. Dann benutzen
        if(pInput) pInput.value = '';
        if(idInput) idInput.value = '';
        
        const assetList = document.getElementById('issueAssetList');
        const assetContainer = document.getElementById('issueAssetContainer');
        assetList.innerHTML = '';
        assetContainer.classList.add('hidden');
        document.getElementById('issuePlantIndex').value = '';

        // NEU: Reset
        document.getElementById('issueFacilityUnitId').value = '';

        // NEU: Plant Index UND ID übernehmen (NUR WENN parentOrder existiert)
        if (parentOrder && parentOrder.plantIndex && pInput) {
            pInput.value = parentOrder.plantIndex;
            // ID übernehmen (falls Parent eine hat)
            if(parentOrder.facilityUnitId && idInput) {
                idInput.value = parentOrder.facilityUnitId;
            }
        }  
        
        let sourceAssets = []; 

    
        if (adHocContext) {
            // FALL A: AD-HOC (Vom Button "Issue melden")
            if (adHocContext.type === 'asset') {
                // adHocContext.value ist jetzt das Objekt {id, label} (dank unserem Fix)
                sourceAssets = [ adHocContext.value ];
            }
            // Wenn type 'unlinked' ist, bleibt die Liste leer (korrekt)
            
        } else if (parentOrder) {
            // FALL B: AUS AUFTRAG (Vom Button "Issue" im Auftrag)
            
            // Kategorie übernehmen
            const parentArea = parentOrder.topic || parentOrder.category;
            if (parentArea) document.getElementById('issueCategory').value = parentArea;
            if (parentOrder.plantIndex && pInput) {
                pInput.value = parentOrder.plantIndex;
            }
            
            // Assets aus dem Parent holen und normalisieren
            const rawAssets = parentOrder.maintenanceObjects || (parentOrder.maintenanceObject ? [parentOrder.maintenanceObject] : []);
            
            rawAssets.forEach(assetStr => {
                try {
                    // Ist es schon JSON? (Neues Format)
                    if (assetStr.trim().startsWith('{')) {
                        sourceAssets.push(JSON.parse(assetStr));
                    } else {
                        // Nein, alter String -> Versuche ID im globalen Inventar zu finden
                        const item = hardwareInventoryList.find(h => h.label === assetStr);
                        sourceAssets.push({ 
                            id: item ? item.id : null, 
                            label: assetStr 
                        });
                    }
                } catch(e) {
                     // Parsing Fehler -> Fallback auf String ohne ID
                     sourceAssets.push({ id: null, label: assetStr });
                }
            
            });
        }
    
        if (sourceAssets.length > 0) {
            assetContainer.classList.remove('hidden');
            sourceAssets.forEach(asset => {
                // WICHTIG: Das Value für die Checkbox muss der valide JSON-String sein!
                // Damit ist es kompatibel mit dem Speicher-Format der Datenbank.
                const valueString = JSON.stringify({ id: asset.id, label: asset.label });
                
                // Checkbox anhaken?
                let isChecked = '';
                
                // Fall 1: Wir kommen aus der Ad-Hoc Suche -> Das gefundene Element direkt anhaken
                if (adHocContext && adHocContext.type === 'asset') {
                    // Vergleich auf ID (wenn vorhanden) oder Label (Fallback)
                    if (asset.id && adHocContext.value.id && asset.id === adHocContext.value.id) {
                        isChecked = 'checked';
                    } else if (asset.label === adHocContext.value.label) {
                        isChecked = 'checked';
                    }
                }
                
                // (Fall 2: Aus Auftrag -> Hier haken wir standardmäßig nichts an, 
                //  der User soll wählen, was defekt ist. Oder man könnte 'checked' setzen, 
                //  wenn es nur ein Asset gibt. Das ist Geschmackssache. Aktuell: Leer lassen.)

                const div = document.createElement('div');
                div.className = "flex items-start gap-2 p-1 hover:bg-slate-50 rounded";
                // Wir nutzen single quotes '...' für das value Attribut, damit das JSON (double quotes) reinpasst
                div.innerHTML = `<input type="checkbox" value='${valueString}' ${isChecked} class="mt-1 w-4 h-4 text-yellow-600 border-gray-300 rounded focus:ring-yellow-500"><span class="text-sm text-slate-700 break-all">${asset.label}</span>`;
                assetList.appendChild(div);
            });
        }
        
        openModal('createIssueModal');
    }
    function closeCreateIssueModal(skipReturn = false) {
        // IMMER den Ad-hoc-Kontext zurücksetzen, wenn das Modal geschlossen wird
        adHocContext = null;
        
        closeModal('createIssueModal');
        
        document.getElementById('createIssueForm').reset();
        const btn = document.getElementById('btnSaveIssue');
        if(btn) {
            btn.onclick = submitCreateIssue;
            btn.innerHTML = '<span class="material-icons-outlined text-sm">save</span> Mangel anlegen';
        }
        document.getElementById('editIssueMetaContainer').classList.add('hidden');
        
        // Nur zum Parent zurückkehren, wenn es nicht explizit übersprungen wird UND ein Parent existiert
        if (!skipReturn && currentOrderId) {
            const exists = maintenanceOrders.find(o => o.id === currentOrderId);
            if(exists) openWorkOrderModal(currentOrderId);
        }
    }

    async function submitCreateIssue() {
        const title = document.getElementById('issueTitle').value.trim();
        if (!title) {
            alert("Titel ist Pflicht.");
            return;
        }

        const isAdHoc = adHocContext !== null;
        const sourceTaskId = isAdHoc ? null : currentOrderId;
        const pIndex = document.getElementById('issuePlantIndex').value.trim();
        const parentOrder = sourceTaskId ? maintenanceOrders.find(o => o.id === sourceTaskId) : null;
        
        const desc = document.getElementById('issueDescription').value.trim();
        const prio = "OPEN"; // Ad-hoc Issues starten immer als "OPEN" zur Bewertung
        const isBlocker = false;
        const cat = document.getElementById('issueCategory').value;
        const affectedAssets = Array.from(document.querySelectorAll('#issueAssetList input:checked')).map(cb => cb.value);
        const fileInput = document.getElementById('issueAttachment');
        let attachments = [];
        if (fileInput.files.length > 0) {
            const res = await handleUpload(fileInput.files[0]);
            if (res) attachments.push({ name: fileInput.files[0].name, dataUrl: res.path });
        }
        const newId = `ISS-${new Date().getFullYear()}-${String(Date.now()).slice(-4)}`;
        const reviewerId = ""; // Bleibt vorerst leer
    
        const logMessage = sourceTaskId 
            ? `Issue erstellt aus Auftrag ${sourceTaskId}. Status: Unbewertet.`
            : `Ad-hoc Issue erstellt. Status: Unbewertet.`;
    
        const newIssue = {
            id: newId,
            type: 'I',
            sourceTask: sourceTaskId, // Wird hier korrekt gesetzt (oder eben nicht)
            task: title,
            description: desc,
            priority: prio,
            category: cat,
            isBlocker: isBlocker,
            topic: cat,
            affectedAssets: affectedAssets,
            testbench: sourceTaskId ? (parentOrder?.testbench || '') : '',
            plantIndex: pIndex,
            facilityUnitId: document.getElementById('issueFacilityUnitId').value,
            assigned: '',
            date: new Date().toISOString().split('T')[0], // Fälligkeit ist heute
            status: 'OPEN',
            requiresReview: true,
            designatedReviewer: reviewerId,
            attachments: attachments,
            logs: [{ user: currentUser, timestamp: new Date().toLocaleString(), note: logMessage }]
        };
    
        maintenanceOrders.push(newIssue);
        await saveOrders();
        renderOrdersTable();
    
        // Wichtig: Das Modal ohne Rückkehr zum Eltern-Auftrag schließen
        closeCreateIssueModal(true); 
    
        // Optional: Direkt das neue Issue-Modal öffnen
        openWorkOrderModal(newId);
    
        // Wenn es einen Eltern-Auftrag gab, dort noch einen Log-Eintrag hinzufügen
        if (sourceTaskId) {
            const parent = maintenanceOrders.find(o => o.id === sourceTaskId);
            if(parent) {
                parent.logs.push({ user: 'System', timestamp: new Date().toLocaleString(), note: `Issue ${newId} gemeldet.` });
                // Kein erneutes `saveOrders` hier nötig, da wir es oben schon gemacht haben.
                // Der Audit-Trail wird beim nächsten Öffnen des Parent-Modals korrekt sein.
            }
        }
    }
    function createLinkedAction() {
        document.getElementById('createActionForm').reset();
        const tomorrow = new Date();
        tomorrow.setDate(tomorrow.getDate() + 1);
        document.getElementById('actionDueDate').value = tomorrow.toISOString().split('T')[0];
        const pInput = document.getElementById('actionPlantIndex');
        if(pInput) pInput.value = '';
        const assetList = document.getElementById('actionAssetList');
        const assetContainer = document.getElementById('actionAssetContainer');
        assetList.innerHTML = '';
        assetContainer.classList.add('hidden');
        const parentIssue = maintenanceOrders.find(o => o.id === currentOrderId);
        document.getElementById('actionFacilityUnitId').value = '';

        if (parentIssue.plantIndex && pInput) {
            pInput.value = parentIssue.plantIndex;
            if(parentIssue.facilityUnitId) {
                document.getElementById('actionFacilityUnitId').value = parentIssue.facilityUnitId;
            }
        }

        
        if (parentIssue) {
            const parentArea = parentIssue.topic || parentIssue.category;
            if (parentArea) document.getElementById('actionCategory').value = parentArea;

            // NEU: Plant Index übernehmen
            if (parentIssue.plantIndex && pInput) {
                pInput.value = parentIssue.plantIndex;
            }

            // Logik zum Sammeln der Assets (kann komplex sein, da es von Issues kommt)
            let sourceAssets = []; 
            (parentIssue.affectedAssets || []).forEach(assetStr => {
                try {
                    if (assetStr.trim().startsWith('{')) {
                        sourceAssets.push(JSON.parse(assetStr));
                    } else {
                        // Fallback für alte Daten
                        const item = hardwareInventoryList.find(h => h.label === assetStr);
                        sourceAssets.push({ id: item ? item.id : null, label: assetStr });
                    }
                } catch(e) {
                    sourceAssets.push({ id: null, label: assetStr });
                }
            });

            if (sourceAssets.length > 0) {
                assetContainer.classList.remove('hidden');
                sourceAssets.forEach(asset => {
                    const valueString = JSON.stringify(asset);
                    const div = document.createElement('div');
                    div.className = "flex items-start gap-2 p-1 hover:bg-slate-50 rounded";
                    div.innerHTML = `<input type="checkbox" value='${valueString}' class="mt-1 w-4 h-4 text-green-600 border-gray-300 rounded focus:ring-green-500"><span class="text-sm text-slate-700 break-all">${asset.label}</span>`;
                    assetList.appendChild(div);
                });
            }
        }
        openModal('createActionModal');
    }
    function closeCreateActionModal(skipReturn = false) { 
        closeModal('createActionModal');
    
        document.getElementById('createActionForm').reset();
        const btn = document.getElementById('btnSaveAction');
        if(btn) {
            btn.onclick = submitCreateAction;
            btn.innerHTML = '<span class="material-icons-outlined text-sm">save</span> Speichern';
        }
        if (!skipReturn && currentOrderId) {
            const exists = maintenanceOrders.find(o => o.id === currentOrderId);
            if(exists) openWorkOrderModal(currentOrderId);
        }
    }    
async function submitCreateAction() { 
        const title = document.getElementById('actionTitle').value.trim(); 
        if (!title) { alert("Titel ist Pflicht."); return; } 
        
        const instructions = document.getElementById('actionInstructions').value.trim(); 
        const dueDate = document.getElementById('actionDueDate').value; 
        const effort = document.getElementById('actionEffort').value.trim(); 
        const spares = document.getElementById('actionSpareParts').value.trim(); 
        const cat = document.getElementById('actionCategory').value; 
        const affectedAssets = Array.from(document.querySelectorAll('#actionAssetList input:checked')).map(cb => cb.value);
        const parentIssue = maintenanceOrders.find(o => o.id === currentOrderId);

        const newId = `ACT-${new Date().getFullYear()}-${String(Date.now()).slice(-4)}`; 
        
        // 1. Priorität: Eingabe im Modal (falls geändert)
        let pIndex = document.getElementById('actionPlantIndex').value.trim();
        let fUnitId = document.getElementById('actionFacilityUnitId').value;
        let tBench = "";

        // 2. Fallback: Erbe vom Parent (wenn Input leer ist oder gleich Parent)
        if (parentIssue) {
            if (!pIndex) {
                pIndex = parentIssue.plantIndex || '';
                fUnitId = parentIssue.facilityUnitId || '';
            }
            tBench = parentIssue.testbench || '';
        }
        // -------------------------------
        
        const newAction = { 
            id: newId, 
            type: 'A', 
            linkedIssue: currentOrderId, 
            task: title, 
            description: instructions, 
            estimatedTime: effort, 
            spareParts: spares, 
            topic: cat, 
            affectedAssets: affectedAssets,
            testbench: tBench, // Sauberer Wert aus Parent
            plantIndex: pIndex,
            facilityUnitId: fUnitId,
            assigned: '', 
            date: dueDate || new Date().toISOString().split('T')[0], 
            status: 'Pending', 
            logs: [{ user: currentUser, timestamp: new Date().toLocaleString(), note: `Maßnahme für Issue ${currentOrderId} definiert.` }] 
        }; 
        
        maintenanceOrders.push(newAction); 
        await saveOrders(); 
        renderOrdersTable(); 
        closeCreateActionModal(true); 
        
        if(parentIssue) { 
            parentIssue.logs.push({ user: 'System', timestamp: new Date().toLocaleString(), note: `Abstellmaßnahme ${newId} angelegt.` }); 
            renderAuditTrail(parentIssue); 
            openWorkOrderModal(currentOrderId); 
        } 
    }
    function editCurrentOrder(type) { 
        const order = maintenanceOrders.find(o => o.id === currentOrderId); 
        if(!order) return; 
        
        // Prüfen, ob abgeschlossene Aufträge von Nicht-Admins bearbeitet werden
        if (order.status === 'Completed' && !currentUserIsAdmin) { 
            alert("Abgeschlossene Aufträge können nicht mehr bearbeitet werden."); 
            return; 
        } 
        
        closeWorkOrderModal(); 
        
        if (type === 'I') { 
            document.getElementById('issueTitle').value = order.task; 
            document.getElementById('issueDescription').value = order.description; 
            document.getElementById('issueCategory').value = order.category; 
            
            const editMetaContainer = document.getElementById('editIssueMetaContainer'); 
            if (order.priority === 'OPEN') { 
                editMetaContainer.classList.add('hidden'); 
            } else { 
                editMetaContainer.classList.remove('hidden'); 
                document.getElementById('issuePriority').value = order.priority; 
                document.getElementById('issueBlocker').checked = order.isBlocker; 
            }
    
            const assetList = document.getElementById('issueAssetList'); 
            const assetContainer = document.getElementById('issueAssetContainer'); 
            assetList.innerHTML = ''; 
            assetContainer.classList.add('hidden'); 
            const parentOrder = maintenanceOrders.find(p => p.id === order.sourceTask); 
            if (parentOrder) { 
                const assets = parentOrder.maintenanceObjects || (parentOrder.maintenanceObject ? [parentOrder.maintenanceObject] : []); 
                if (assets.length > 0) { 
                    assetContainer.classList.remove('hidden'); 
                    const savedAssets = order.affectedAssets || (order.affectedAsset ? [order.affectedAsset] : []); 
                    assets.forEach(asset => { 
                        const isChecked = savedAssets.includes(asset) ? 'checked' : ''; 
                        const div = document.createElement('div'); 
                        div.className = "flex items-start gap-2 p-1 hover:bg-slate-50 rounded"; 
                        div.innerHTML = `<input type="checkbox" value="${asset}" ${isChecked} class="mt-1 w-4 h-4 text-yellow-600 border-gray-300 rounded focus:ring-yellow-500"><span class="text-sm text-slate-700 break-all">${asset}</span>`; 
                        assetList.appendChild(div); 
                    }); 
                } 
            }
    
            const btn = document.getElementById('btnSaveIssue'); 
            btn.onclick = () => submitUpdateIssue(order.id); 
            btn.innerHTML = '<span class="material-icons-outlined text-sm">save</span> Änderungen speichern'; 
            
            openModal('createIssueModal'); 
    
        } else if (type === 'A') { 
            document.getElementById('actionTitle').value = order.task; 
            document.getElementById('actionInstructions').value = order.description; 
            document.getElementById('actionDueDate').value = order.date; 
            document.getElementById('actionEffort').value = order.estimatedTime || ''; 
            document.getElementById('actionSpareParts').value = order.spareParts || ''; 
            document.getElementById('actionCategory').value = order.topic || "Allgemein";
    
            const assetList = document.getElementById('actionAssetList'); 
            const assetContainer = document.getElementById('actionAssetContainer'); 
            assetList.innerHTML = ''; 
            assetContainer.classList.add('hidden'); 
            const parentIssue = maintenanceOrders.find(p => p.id === order.linkedIssue); 
            let sourceAssets = []; 
            if (parentIssue) { 
                if (parentIssue.affectedAssets && parentIssue.affectedAssets.length > 0) sourceAssets = parentIssue.affectedAssets; 
                else if (parentIssue.affectedAsset) sourceAssets = [parentIssue.affectedAsset]; 
                else if (parentIssue.maintenanceObjects && parentIssue.maintenanceObjects.length > 0) sourceAssets = parentIssue.maintenanceObjects; 
                else { 
                    const grandParent = maintenanceOrders.find(gp => gp.id === parentIssue.sourceTask); 
                    if (grandParent) sourceAssets = grandParent.maintenanceObjects || (grandParent.maintenanceObject ? [grandParent.maintenanceObject] : []); 
                } 
            } 
            if (sourceAssets.length > 0) { 
                assetContainer.classList.remove('hidden'); 
                const savedAssets = order.affectedAssets || []; 
                sourceAssets.forEach(asset => { 
                    const isChecked = savedAssets.includes(asset) ? 'checked' : ''; 
                    const div = document.createElement('div'); 
                    div.className = "flex items-start gap-2 p-1 hover:bg-slate-50 rounded"; 
                    div.innerHTML = `<input type="checkbox" value="${asset}" ${isChecked} class="mt-1 w-4 h-4 text-green-600 border-gray-300 rounded focus:ring-green-500"><span class="text-sm text-slate-700 break-all">${asset}</span>`; 
                    assetList.appendChild(div); 
                }); 
            }
    
            const btn = document.getElementById('btnSaveAction'); 
            btn.onclick = () => submitUpdateAction(order.id); 
            btn.innerHTML = '<span class="material-icons-outlined text-sm">save</span> Änderungen speichern'; 
            
            openModal('createActionModal'); 
    
        } else { 
            alert("Maintenance-Aufträge werden über die Vorlagen verwaltet."); 
        } 
    }

    async function submitUpdateIssue(id) { const order = maintenanceOrders.find(o => o.id === id); if(!order) return; order.task = document.getElementById('issueTitle').value.trim(); order.description = document.getElementById('issueDescription').value.trim(); order.category = document.getElementById('issueCategory').value; const editMetaContainer = document.getElementById('editIssueMetaContainer'); if (!editMetaContainer.classList.contains('hidden')) { order.priority = document.getElementById('issuePriority').value; order.isBlocker = document.getElementById('issueBlocker').checked; } order.topic = order.category; order.affectedAssets = Array.from(document.querySelectorAll('#issueAssetList input:checked')).map(cb => cb.value); delete order.affectedAsset; const fileInput = document.getElementById('issueAttachment'); if(fileInput.files.length > 0) { const res = await handleUpload(fileInput.files[0]); if(res) { if(!order.attachments) order.attachments = []; order.attachments.push({ name: fileInput.files[0].name, dataUrl: res.path }); } } order.logs.push({ user: currentUser, timestamp: new Date().toLocaleString(), note: `Auftrag bearbeitet.` }); saveOrders(); renderOrdersTable(); closeCreateIssueModal(true); openWorkOrderModal(id); }
    async function submitUpdateAction(id) { const order = maintenanceOrders.find(o => o.id === id); if(!order) return; order.task = document.getElementById('actionTitle').value.trim(); order.description = document.getElementById('actionInstructions').value.trim();const affectedAssets = Array.from(document.querySelectorAll('#actionAssetList input:checked')).map(cb => cb.value); order.date = document.getElementById('actionDueDate').value; order.estimatedTime = document.getElementById('actionEffort').value.trim(); order.spareParts = document.getElementById('actionSpareParts').value.trim(); order.topic = document.getElementById('actionCategory').value; order.affectedAssets = Array.from(document.querySelectorAll('#actionAssetList input:checked')).map(cb => cb.value); order.logs.push({ user: currentUser, timestamp: new Date().toLocaleString(), note: `Maßnahme bearbeitet.` }); saveOrders(); renderOrdersTable(); closeCreateActionModal(true); openWorkOrderModal(id); }

    function switchToDefView() { document.getElementById('defViewContainer').classList.remove('hidden'); document.getElementById('definitionForm').classList.add('hidden'); document.getElementById('defHeaderBadgeView').classList.remove('hidden'); document.getElementById('defHeaderToggleEdit').classList.add('hidden'); document.getElementById('btnSwitchToDefEdit').classList.remove('hidden'); document.getElementById('defModalTitle').textContent = "Wartungsvorlage (Ansicht)"; document.getElementById('defFooterView').classList.remove('hidden'); document.getElementById('defFooterEdit').classList.add('hidden'); }
    function switchToDefEditMode() { document.getElementById('defViewContainer').classList.add('hidden'); document.getElementById('definitionForm').classList.remove('hidden'); document.getElementById('defHeaderBadgeView').classList.add('hidden'); document.getElementById('defHeaderToggleEdit').classList.remove('hidden'); document.getElementById('btnSwitchToDefEdit').classList.add('hidden'); document.getElementById('defModalTitle').textContent = "Wartungsvorlage bearbeiten"; document.getElementById('defFooterView').classList.add('hidden'); document.getElementById('defFooterEdit').classList.remove('hidden'); const id = document.getElementById('defId').value; if(id) document.getElementById('btnDefDelete').classList.remove('invisible'); else document.getElementById('btnDefDelete').classList.add('invisible'); }
    function cancelDefEdit() { const id = document.getElementById('defId').value; if(id) { switchToDefView(); } else { closeDefinitionModal(); } }
    function deleteDefinitionFromModal() { const id = document.getElementById('defId').value; if(id) { deleteDefinition(id); closeDefinitionModal(); } }
    function openDefinitionModal(id = null) { 
        document.getElementById('definitionForm').reset(); 
        document.getElementById('defFacilityUnitId').value = ''; 
        document.getElementById('defImagePreview').innerHTML = ''; 
        document.getElementById('defDocPreview').innerHTML = ''; 
        document.getElementById('defChecklist').innerHTML = ''; 
        document.getElementById('defAssetContainer').innerHTML = ''; 
        document.getElementById('defConsumablesContainer').innerHTML = ''; 
        document.getElementById('defReqReview').checked = false; 
        document.getElementById('defReviewerBox').classList.add('hidden'); 
        document.getElementById('defReviewer').value = ''; 
        document.getElementById('defActive').checked = true; 
        document.getElementById('defIdView').textContent = ''; 
        document.getElementById('defAssetsView').innerHTML = ''; 
        document.getElementById('defConsumablesView').innerHTML = ''; 
        document.getElementById('defChecklistView').innerHTML = ''; 
        document.getElementById('defAttachmentsView').innerHTML = ''; 
        currentDefImages = []; currentDefDocs = []; currentChecklist = []; currentDefAssets = []; currentDefConsumables = []; 
        updateCompanyDropdown(); 
        if(id) { 
            const def = taskDefinitions.find(d => d.id === id);  
            document.getElementById('defId').value = def.id; 
            document.getElementById('defTask').value = def.task; 
            document.getElementById('defDesc').value = def.description || ''; 
            document.getElementById('defTopic').value = def.topic; 
            document.getElementById('defTestbench').value = def.testbench; 
            document.getElementById('defIndex').value = def.plantIndex || ''; 
            document.getElementById('defIntVal').value = def.intervalValue; 
            document.getElementById('defIntUnit').value = def.intervalUnit; 
            document.getElementById('defStartDate').value = def.startDate || ''; 
            document.getElementById('defActive').checked = def.active !== false; 
            document.getElementById('defDuration').value = def.duration || ''; 
            if(def.externalCompanyId) document.getElementById('defExternalCompany').value = def.externalCompanyId; 
            if (def.requiresReview) { 
                document.getElementById('defReqReview').checked = true; 
                document.getElementById('defReviewerBox').classList.remove('hidden'); 
                document.getElementById('defReviewer').value = def.designatedReviewer || ''; 
            } 
            if(def.images) currentDefImages = [...def.images]; 
            if(def.documents) currentDefDocs = [...def.documents]; 
            if(def.checklist) currentChecklist = [...def.checklist]; 
            if(def.maintenanceObjects) currentDefAssets = [...def.maintenanceObjects]; 
            else if(def.maintenanceObject) currentDefAssets.push(def.maintenanceObject); 
            if(def.consumablesList) currentDefConsumables = [...def.consumablesList]; 
            else if(def.consumables) currentDefConsumables = def.consumables.split(',').map(s=>s.trim()).filter(s=>s); 
            renderDefAttachments(); 
            renderChecklist(); 
            renderAssets(); 
            renderConsumables(); 
            document.getElementById('defIdView').textContent = def.id; 
            document.getElementById('defTaskView').textContent = def.task; 
            document.getElementById('defTopicView').textContent = def.topic; 
            let startD = def.startDate; 
            try { startD = new Date(def.startDate).toLocaleDateString('de-DE'); } catch(e){} 
            document.getElementById('defStartDateView').textContent = startD; 
            document.getElementById('defIntervalView').textContent = `${def.intervalValue} ${def.intervalUnit}`; 
            document.getElementById('defDurationView').textContent = def.duration || '-'; 
            let compName = "Intern / Internes Team"; 
            if(def.externalCompanyId) { const c = companies.find(x => x.id === def.externalCompanyId); if(c) compName = c.name + " (Extern)"; } 
            document.getElementById('defExternalCompanyView').textContent = compName; 
            let revTxt = "Nicht erforderlich"; 
            if(def.requiresReview) { revTxt = "Erforderlich"; if(def.designatedReviewer) revTxt += ` (durch ${def.designatedReviewer})`; } 
            document.getElementById('defReviewView').textContent = revTxt; 
            document.getElementById('defTestbenchView').textContent = def.testbench || '-'; 
            document.getElementById('defIndexView').textContent = def.plantIndex || '-';
            document.getElementById('defFacilityUnitId').value = def.facilityUnitId || ''; 
            const assetsDiv = document.getElementById('defAssetsView'); 
            if(currentDefAssets.length > 0) { 
                currentDefAssets.forEach(a => {
                    let lbl = a;
                    try { if(a.startsWith('{')) lbl = JSON.parse(a).label; } catch(e){}
                    assetsDiv.innerHTML += `<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-50 text-blue-700 border border-blue-100">${lbl}</span>`; 
                }); 
            }
            else assetsDiv.innerHTML = '<span class="text-xs text-slate-400 italic">-</span>'; 
            const consDiv = document.getElementById('defConsumablesView'); 
            if(currentDefConsumables.length > 0) { currentDefConsumables.forEach(c => consDiv.innerHTML += `<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-slate-100 text-slate-600 border border-slate-200">${c}</span>`); } 
            else consDiv.innerHTML = '<span class="text-xs text-slate-400 italic">-</span>'; 
            const clUl = document.getElementById('defChecklistView'); 
            if(currentChecklist.length > 0) { currentChecklist.forEach(item => { clUl.innerHTML += `<li class="flex items-start gap-2 text-xs text-slate-600"><span class="material-icons-outlined text-[10px] mt-0.5 text-slate-400">check_box_outline_blank</span><span>${item}</span></li>`; }); } 
            else clUl.innerHTML = '<li class="text-xs text-slate-400 italic">Keine Checkliste.</li>'; 
            document.getElementById('defDescView').textContent = def.description || 'Keine Beschreibung.'; 
            const attachDiv = document.getElementById('defAttachmentsView'); 
            const allAttach = [...currentDefImages, ...currentDefDocs]; 
            if(allAttach.length > 0) { allAttach.forEach(a => { let icon = 'description'; if(a.dataUrl && a.dataUrl.startsWith('data:image')) icon = 'image'; attachDiv.innerHTML += `<div class="flex items-center gap-2 p-2 bg-slate-50 border rounded text-xs text-slate-600"><span class="material-icons-outlined text-sm">${icon}</span><span class="truncate flex-1">${a.name}</span></div>`; }); } 
            else attachDiv.innerHTML = '<span class="text-xs text-slate-400 italic col-span-2">Keine Anhänge.</span>'; 
            const badge = document.getElementById('defHeaderBadgeView'); 
            if(def.active !== false) { badge.className = "px-2 py-0.5 rounded text-xs font-bold bg-green-100 text-green-800 border border-green-200"; badge.textContent = "AKTIV"; } 
            else { badge.className = "px-2 py-0.5 rounded text-xs font-bold bg-slate-100 text-slate-500 border border-slate-200"; badge.textContent = "INAKTIV"; } 
            switchToDefView(); 
        } else { 
            document.getElementById('defId').value = ''; 
            document.getElementById('defStartDate').value = new Date().toISOString().split('T')[0]; 
            switchToDefEditMode(); 
            document.getElementById('btnDefDelete').classList.add('invisible'); 
        } 
        const el = document.getElementById('defChecklist'); 
        if(sortableChecklist) sortableChecklist.destroy(); 
        sortableChecklist = Sortable.create(el, { handle: '.drag-handle', animation: 150, onEnd: function (evt) { const item = currentChecklist.splice(evt.oldIndex, 1)[0]; currentChecklist.splice(evt.newIndex, 0, item); }, }); 
        
        // HIER IST DIE KORREKTUR
        openModal('definitionModal'); 
    }
    function closeDefinitionModal() { 
        closeModal('definitionModal'); 
    }    
    function toggleReviewerInput() { const cb = document.getElementById('defReqReview'); const box = document.getElementById('defReviewerBox'); if(cb.checked) box.classList.remove('hidden'); else box.classList.add('hidden'); }
    function addChecklistItem() { const input = document.getElementById('newChecklistItem'); const val = input.value.trim(); if(val) { currentChecklist.push(val); input.value = ''; renderChecklist(); } }
    function removeChecklistItem(idx) { currentChecklist.splice(idx, 1); renderChecklist(); }
    function updateChecklistItem(idx, newValue) {
        currentChecklist[idx] = newValue.trim();
    }
    function renderChecklist() {
        const ul = document.getElementById('defChecklist');
        ul.innerHTML = '';
        
        currentChecklist.forEach((item, idx) => {
            const li = document.createElement('li');
            li.className = "flex items-center gap-2 bg-white p-1 rounded border-b border-slate-100 last:border-0 group";
            
            // Drag Handle
            const handle = document.createElement('span');
            handle.className = "material-icons-outlined text-base text-slate-300 cursor-grab drag-handle group-hover:text-slate-500";
            handle.textContent = "drag_indicator";
            
            // Input Feld (Inline Edit)
            const input = document.createElement('input');
            input.type = "text";
            input.value = item;
            input.className = "flex-grow text-xs text-slate-700 bg-transparent border-none p-1 focus:bg-slate-50 focus:ring-1 focus:ring-blue-200 rounded";
            // Event: Beim Tippen/Ändern das Array updaten
            input.oninput = (e) => updateChecklistItem(idx, e.target.value);
            
            // Delete Button
            const delBtn = document.createElement('button');
            delBtn.type = "button";
            delBtn.className = "text-slate-300 hover:text-red-500 transition-colors p-1";
            delBtn.innerHTML = "×";
            delBtn.onclick = () => removeChecklistItem(idx);
            
            li.appendChild(handle);
            li.appendChild(input);
            li.appendChild(delBtn);
            ul.appendChild(li);
        });
    }    
    function addAsset() { 
        const input = document.getElementById('defAssetInput'); 
        const val = input.value.trim(); 
        if(!val) return;

        // Versuche, das Item in der Liste zu finden
        const foundItem = hardwareInventoryList.find(i => i.label === val);

        let entryToSave;
        if (foundItem) {
            // Treffer! Wir speichern ein JSON-Objekt als String
            entryToSave = JSON.stringify({ id: foundItem.id, label: foundItem.label });
        } else {
            // Kein Treffer (Freitext oder Altdaten-Format). Wir speichern es so wie es ist.
            // Das erhält die Abwärtskompatibilität!
            entryToSave = val;
        }

        if(!currentDefAssets.includes(entryToSave)) { 
            currentDefAssets.push(entryToSave); 
            input.value = ''; 
            renderAssets(); 
        } 
    }    
    function removeAsset(val) { 
        // Wir filtern einfach den Array. Da 'val' der exakte String (ggf. JSON) ist, klappt das.
        currentDefAssets = currentDefAssets.filter(a => a !== val); 
        renderAssets(); 
    }

    function renderAssets() {
        const container = document.getElementById('defAssetContainer');
        container.innerHTML = ''; // Leere den Container wie bisher
    
        currentDefAssets.forEach(dataValue => {
            let displayText = dataValue;
    
            // Versuch, JSON zu parsen, um den schönen Namen zu erhalten
            try {
                if (dataValue.startsWith('{')) {
                    const obj = JSON.parse(dataValue);
                    displayText = obj.label;
                }
            } catch (e) {
                // Wenn das Parsen fehlschlägt, zeige den Roh-String an
                console.warn("Konnte Asset-String nicht als JSON parsen:", dataValue);
            }
    
            // --- NEU: Elemente programmatisch erstellen ---
    
            // 1. Das äußere 'tag-chip' Span-Element erstellen
            const tagChip = document.createElement('span');
            tagChip.className = 'tag-chip';
            tagChip.title = displayText;
            tagChip.textContent = displayText; // Fügt den Namen hinzu
    
            // 2. Den 'remove' Button (das 'x') erstellen
            const removeButton = document.createElement('span');
            removeButton.className = 'tag-chip-remove';
            removeButton.innerHTML = '×'; // Das 'x'-Symbol
            
            // 3. Den Event-Listener programmatisch hinzufügen
            // Hier wird 'dataValue' direkt als Variable übergeben, ohne String-Probleme!
            removeButton.addEventListener('click', () => {
                removeAsset(dataValue);
            });
    
            // 4. Den Button zum Tag hinzufügen
            tagChip.appendChild(removeButton);
    
            // 5. Das komplette Tag zum Container hinzufügen
            container.appendChild(tagChip);
        });
    }
    function addConsumable() { const input = document.getElementById('defConsumableInput'); const val = input.value.trim(); if(val && !currentDefConsumables.includes(val)) { currentDefConsumables.push(val); input.value = ''; renderConsumables(); } }
    function removeConsumable(val) { currentDefConsumables = currentDefConsumables.filter(a => a !== val); renderConsumables(); }
    function renderConsumables() { const c = document.getElementById('defConsumablesContainer'); c.innerHTML = ''; currentDefConsumables.forEach(val => { c.innerHTML += `<span class="tag-chip bg-gray-100 text-gray-700 border-gray-200">${val}<span class="tag-chip-remove text-gray-500 hover:text-gray-700" onclick="removeConsumable('${val}')">×</span></span>`; }); }
    async function handleUpload(file) { const formData = new FormData(); formData.append('file', file); try { const res = await fetch('/api/maintenance/upload', { method: 'POST', body: formData }); if(!res.ok) throw new Error(); return await res.json(); } catch(e) { alert('Upload fehlgeschlagen'); return null; } }
    document.getElementById('defImageInput').addEventListener('change', async (e) => { for(let file of e.target.files) { const result = await handleUpload(file); if(result) { currentDefImages.push({ name: file.name, dataUrl: result.path }); } } renderDefAttachments(); });
    document.getElementById('defDocInput').addEventListener('change', async (e) => { for(let file of e.target.files) { const result = await handleUpload(file); if(result) { currentDefDocs.push({ name: file.name, path: result.path }); } } renderDefAttachments(); });
    function renderDefAttachments() { 
        const iCon = document.getElementById('defImagePreview'); 
        const dCon = document.getElementById('defDocPreview'); 
        iCon.innerHTML = ''; 
        dCon.innerHTML = ''; 
    
        // Bilder rendern (mit Lightbox)
        currentDefImages.forEach((img, idx) => { 
            iCon.innerHTML += `
                <div class="flex items-center gap-2 bg-slate-50 p-1 rounded border border-slate-200 text-xs group hover:border-blue-300 transition-colors">
                    <!-- Bildvorschau: Klick öffnet Lightbox -->
                    <img src="${img.dataUrl}" class="w-8 h-8 object-cover rounded cursor-zoom-in hover:opacity-80 transition-opacity" 
                         onclick="event.stopPropagation(); openLightbox('${img.dataUrl}', '${img.name}')">
                    
                    <!-- Dateiname: Klick öffnet ebenfalls Lightbox -->
                    <span class="truncate flex-1 cursor-pointer hover:text-blue-600" 
                          onclick="openLightbox('${img.dataUrl}', '${img.name}')">${img.name}</span>
                    
                    <!-- Löschen Button -->
                    <span class="text-red-400 cursor-pointer hover:text-red-600 font-bold px-2" 
                          onclick="currentDefImages.splice(${idx},1); renderDefAttachments()">×</span>
                </div>`; 
        }); 
    
        // Dokumente rendern (ohne Lightbox, nur Icon)
        currentDefDocs.forEach((doc, idx) => { 
            dCon.innerHTML += `
                <div class="flex items-center gap-2 bg-slate-50 p-1 rounded border border-slate-200 text-xs hover:border-blue-300 transition-colors">
                    <span class="material-icons-outlined text-sm text-slate-400">description</span>
                    <span class="truncate flex-1">${doc.name}</span>
                    <span class="text-red-400 cursor-pointer hover:text-red-600 font-bold px-2" 
                          onclick="currentDefDocs.splice(${idx},1); renderDefAttachments()">×</span>
                </div>`; 
        }); 
    }    
function saveDefinition() {
        const id = document.getElementById('defId').value;
        const startDateVal = document.getElementById('defStartDate').value;
        if (!startDateVal) { alert("Bitte ein Startdatum wählen."); return; }
        
        const reqRev = document.getElementById('defReqReview').checked;
        const revUser = document.getElementById('defReviewer').value.trim();
        const isActive = document.getElementById('defActive').checked;
        const extComp = document.getElementById('defExternalCompany').value;
        
        const data = { 
            task: document.getElementById('defTask').value, 
            description: document.getElementById('defDesc').value, 
            topic: document.getElementById('defTopic').value, 
            testbench: document.getElementById('defTestbench').value, 
            plantIndex: document.getElementById('defIndex').value, 
            facilityUnitId: document.getElementById('defFacilityUnitId').value, 
            maintenanceObjects: currentDefAssets, 
            consumablesList: currentDefConsumables, 
            intervalValue: document.getElementById('defIntVal').value, 
            intervalUnit: document.getElementById('defIntUnit').value, 
            duration: document.getElementById('defDuration').value, 
            checklist: currentChecklist, 
            startDate: startDateVal, 
            requiresReview: reqRev, 
            designatedReviewer: revUser, 
            active: isActive, 
            externalCompanyId: extComp, 
            images: currentDefImages, 
            documents: currentDefDocs 
        };
        
        const existingDef = id ? taskDefinitions.find(d => d.id === id) : null;
        
        const wasInactive = existingDef && existingDef.active === false;
        const isNew = !existingDef;

        // Inaktiv-Logik (Löschen von zukünftigen Aufträgen)
        if (!isActive && id) { 
             const ordersToDelete = maintenanceOrders.filter(o => { 
                 if (o.parentTaskId !== id) return false; 
                 if (o.status !== 'Pending') return false; 
                 const hasChildren = maintenanceOrders.some(child => child.sourceTask === o.id || child.linkedIssue === o.id); 
                 return !hasChildren; 
             }); 
             
             if (ordersToDelete.length > 0) { 
                 if(confirm(`Wartungsplan inaktiv gesetzt. ${ordersToDelete.length} offene (Pending) Aufträge ohne Verknüpfungen werden gelöscht. Fortfahren?`)) { 
                     const idsToDelete = ordersToDelete.map(o => o.id); 
                     maintenanceOrders = maintenanceOrders.filter(o => !idsToDelete.includes(o.id)); 
                     saveOrders(); 
                     renderOrdersTable(); 
                 } else { 
                     document.getElementById('defActive').checked = true; 
                     return; 
                 } 
             } 
        }

        // Speichern / Update im Array
        if(existingDef) { 
            const idx = taskDefinitions.indexOf(existingDef); 
            taskDefinitions[idx] = { ...taskDefinitions[idx], ...data }; 
            
            // --- NEU: SYNC DOWN zu Pending Aufträgen ---
            // Wir aktualisieren alle noch nicht begonnenen Aufträge mit den neuen Stammdaten
            let updatedCount = 0;
            maintenanceOrders.forEach(order => {
                if (order.parentTaskId === id && order.status === 'Pending') {
                    order.task = data.task;
                    order.description = data.description;
                    order.topic = data.topic;
                    order.testbench = data.testbench;
                    order.plantIndex = data.plantIndex;
                    order.facilityUnitId = data.facilityUnitId;
                    order.maintenanceObjects = [...data.maintenanceObjects]; // Kopie!
                    order.consumablesList = [...data.consumablesList];     // Kopie!
                    order.requiresReview = data.requiresReview;
                    order.designatedReviewer = data.designatedReviewer;
                    order.externalCompanyId = data.externalCompanyId;
                    // Checkliste nur updaten, wenn noch nichts abgehakt wurde (einfacher Check)
                    if (!order.checklistState || Object.keys(order.checklistState).length === 0) {
                        order.checklist = [...data.checklist];
                    }
                    updatedCount++;
                }
            });
            if (updatedCount > 0) {
                console.log(`Info: ${updatedCount} offene Aufträge wurden aktualisiert.`);
                saveOrders(); // Speichert die aktualisierten Aufträge
            }
            // -------------------------------------------

        } else { 
            const now = Date.now(); 
            const year = new Date().getFullYear(); 
            const uniqueSuffix = String(now).slice(-5); 
            const newId = `MT-${year}-${uniqueSuffix}`; 
            taskDefinitions.push({ id: newId, ...data }); 
        }
        
        saveDefinitions(); 
        
        // Trigger Logik (Neu erstellen)
        if ((isNew && isActive) || (!isNew && wasInactive && isActive)) { 
            let nextDate = startDateVal; 
            if (new Date(startDateVal) < new Date()) { 
                nextDate = calculateNextValidDateFromStart(startDateVal, data.intervalValue, data.intervalUnit); 
            } 
            
            const targetId = existingDef ? existingDef.id : taskDefinitions[taskDefinitions.length - 1].id; 
            createOrderFromDef(targetId, nextDate, true); 
            
            const msg = isNew ? "Vorlage erstellt" : "Wartungsplan reaktiviert"; 
            alert(`${msg} und Auftrag für ${nextDate} geprüft/generiert.`); 
        }
        
        renderDefinitionsTable(); 
        renderOrdersTable(); 
        closeDefinitionModal();
    }
    function calculateNextValidDateFromStart(startDateStr, intervalVal, intervalUnit) { 
        let d = new Date(startDateStr); 
        const now = new Date(); 
        const v = parseInt(intervalVal); 
        
        while (d < now) { 
            // Hier stand vorher fälschlicherweise "unit" statt "intervalUnit"
            if(intervalUnit === 'Wochen') d.setDate(d.getDate() + v * 7); 
            if(intervalUnit === 'Monate') d.setMonth(d.getMonth() + v); 
            if(intervalUnit === 'Jahre') d.setFullYear(d.getFullYear() + v); 
        } 
        return d.toISOString().split('T')[0]; 
    }    
    function createOrderFromDef(defId, dateOverride = null, silent = false) { 
        const def = taskDefinitions.find(d => d.id === defId); 
        if (!def) return;
        
        // --- DUPLIKAT-SCHUTZ ---
        const existingOpenOrder = maintenanceOrders.find(o => 
            o.parentTaskId === defId && o.status !== 'Completed'
        );

        if (existingOpenOrder) {
            if (!silent) {
                console.log(`Info: Es existiert bereits ein offener Auftrag (${existingOpenOrder.id}) für Plan ${defId}. Überspringe Erstellung.`);
            }
            return; // Abbruch
        }

        const newId = `ORD-${new Date().getFullYear()}-${String(Date.now()).slice(-4)}`; 
        const dueDate = dateOverride || new Date().toISOString().split('T')[0]; 
        
        const newOrder = { 
            id: newId, 
            type: 'M', 
            parentTaskId: def.id, 
            task: def.task, 
            description: def.description, 
            checklist: def.checklist ? [...def.checklist] : [], 
            checklistState: {}, 
            topic: def.topic, 
            testbench: def.testbench, 
            plantIndex: def.plantIndex,
            facilityUnitId: def.facilityUnitId, 
            maintenanceObjects: def.maintenanceObjects, 
            consumablesList: def.consumablesList, 
            requiresReview: def.requiresReview, 
            designatedReviewer: def.designatedReviewer, 
            externalCompanyId: def.externalCompanyId, 
            assigned: '', 
            date: dueDate, 
            status: 'Pending', 
            logs: [{ user: 'System', timestamp: new Date().toLocaleString(), note: 'Automatisch generiert aus Vorlage.' }] 
        }; 
        
        maintenanceOrders.push(newOrder); 
        saveOrders(); 
        renderOrdersTable(); 
    }
        
function openHistoryListModal(defId) {
        const def = taskDefinitions.find(d => d.id === defId);
        const title = def ? def.task : "Unbekannter Wartungsplan";
        
        // Titel setzen
        document.getElementById('historyModalTitle').textContent = title;

        const list = maintenanceOrders
            .filter(o => o.parentTaskId === defId && (o.status === 'Completed' || o.status === 'Review Ready'))
            .sort((a,b) => new Date(b.completedDate || b.date) - new Date(a.completedDate || a.date));
    
        // Statistik HTML berechnen
        let statsHtml = '';
        if (def && list.length >= 2) {
            const newestDate = new Date(list[0].completedDate || list[0].date);
            const oldestDate = new Date(list[list.length - 1].completedDate || list[list.length - 1].date);
            const diffMs = newestDate - oldestDate;
            const diffDays = diffMs / (1000 * 60 * 60 * 24);
            const avgDays = Math.round(diffDays / (list.length - 1));
            
            let targetDays = def.intervalValue * (def.intervalUnit === 'Jahre' ? 365 : (def.intervalUnit === 'Wochen' ? 7 : 30));
            let valueClass = "text-slate-700";
            if (avgDays > targetDays * 1.15) valueClass = "text-red-600 font-bold";
            else if (avgDays < targetDays * 0.85) valueClass = "text-blue-600 font-bold";
            
            // NUR der Inhalt der Pille (ohne Container-Div drumrum, wenn möglich, oder mit)
            // Hier nutzen wir direkt das Badge-Styling
            statsHtml = `
                <div class="inline-flex items-center px-3 py-1 rounded-full border border-slate-200 bg-slate-100 text-xs font-medium text-slate-500">
                    <span class="mr-2">Plan: ${def.intervalValue} ${def.intervalUnit}</span>
                    <span class="border-l border-slate-300 h-3 mx-2"></span>
                    <span>Ø Ist: <span class="${valueClass}">${avgDays} Tage</span></span>
                </div>`;
        } else if (def) {
            statsHtml = `
                <div class="inline-flex items-center px-3 py-1 rounded-full border border-slate-100 bg-slate-50 text-xs text-slate-400">
                    Plan: ${def.intervalValue} ${def.intervalUnit} (Basis < 2)
                </div>`;
        }

        // Badge befüllen (SICHER, da wir nur diesen Container anfassen)
        document.getElementById('historyStatsBadge').innerHTML = statsHtml;
    
        // ... (Rest der Funktion: Tabelle befüllen etc. bleibt gleich) ...
        const tbody = document.getElementById('historyListBody');
        tbody.innerHTML = '';
        
        if(list.length === 0) {
            tbody.innerHTML = '<tr><td colspan="4" class="px-6 py-12 text-center text-slate-400 italic flex flex-col items-center gap-2"><span class="material-icons-outlined text-3xl opacity-50">history_toggle_off</span><span>Keine abgeschlossenen Aufträge gefunden.</span></td></tr>';
        } else {
             list.forEach((o, index) => {
                let dateDisplay = o.completedDate || o.date;
                let intervalInfo = '';

                if (index < list.length - 1) {
                    const curr = new Date(o.completedDate || o.date);
                    const prev = new Date(list[index+1].completedDate || list[index+1].date);
                    const diff = Math.round((curr - prev) / (1000 * 60 * 60 * 24));
                    intervalInfo = `<div class="text-[10px] text-slate-400 mt-0.5">Δ ${diff} Tage</div>`;
                }

                try {
                    const d = new Date(dateDisplay);
                    if(!isNaN(d)) {
                        dateDisplay = d.toLocaleDateString('de-DE', { weekday: 'short', year: 'numeric', month: '2-digit', day: '2-digit' });
                    }
                } catch(e){}
    
                const user = o.completedBy || o.executedBy || 'Unbekannt';
                const initials = user.split(' ').map(n => n[0]).join('').substring(0, 2).toUpperCase();
    
                let statusHtml = '<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800 border border-green-200">Erledigt</span>';
                if (o.status === 'Review Ready') {
                    statusHtml = '<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800 border border-purple-200">Prüfung</span>';
                }
    
                tbody.innerHTML += `
                    <tr class="hover:bg-slate-50 transition-colors group">
                        <td class="px-6 py-4">
                            <div class="flex flex-col">
                                ${statusHtml}
                                <span class="text-[10px] font-mono text-slate-400 mt-1">${o.id}</span>
                            </div>
                        </td>
                        <td class="px-6 py-4">
                            <div class="flex flex-col">
                                <div class="flex items-center text-slate-700 font-medium">
                                    <span class="material-icons-outlined text-slate-400 text-sm mr-2">event_available</span>
                                    ${dateDisplay}
                                </div>
                                ${intervalInfo}
                            </div>
                        </td>
                        <td class="px-6 py-4">
                            <div class="flex items-center gap-3">
                                <div class="w-8 h-8 rounded-full bg-blue-100 text-blue-700 flex items-center justify-center text-xs font-bold border border-blue-200">${initials}</div>
                                <div class="text-sm text-slate-600">${user}</div>
                            </div>
                        </td>
                        <td class="px-6 py-4 text-right">
                            <button class="bg-white border border-slate-200 text-slate-600 hover:text-primary hover:border-primary hover:bg-blue-50 px-3 py-1.5 rounded-md text-xs font-medium transition-all shadow-sm flex items-center gap-2 ml-auto" onclick="closeHistoryListModal(); openWorkOrderModal('${o.id}')">
                                <span>Protokoll öffnen</span>
                                <span class="material-icons-outlined text-sm">open_in_new</span>
                            </button>
                        </td>
                    </tr>
                `;
            });
        }
    
        openModal('historyListModal');
    }
    function closeHistoryListModal() {
        closeModal('historyListModal');
    }
    let facilityUnitMap = {};
    
    async function loadFacilityUnitSuggestions() {
        try {
            const response = await fetch('/api/facility/units');
            if (response.ok) {
                const units = await response.json();
                
                let datalist = document.getElementById('facilityUnitList');
                if (!datalist) return; 
    
                datalist.innerHTML = ''; 
                facilityUnitMap = {}; // Reset
    
                units.forEach(unit => {
                    // Wir speichern das Mapping, um beim "Change"-Event die ID wiederzufinden
                    facilityUnitMap[unit.name] = unit.id;
                    
                    const option = document.createElement('option');
                    option.value = unit.name; 
                    // data-id im option Tag funktioniert in datalist leider nicht in allen Browsern zuverlässig beim Auslesen,
                    // daher nutzen wir die globale Map.
                    datalist.appendChild(option);
                });
            }
        } catch (error) {
            console.warn("Konnte Facility Units nicht laden:", error);
        }
    
    setTimeout(initFilterDropdowns, 500);
    }
    window.onFacilityUnitSelected = function(inputElement, targetIdField) {
        const selectedName = inputElement.value;
        const hiddenIdField = document.getElementById(targetIdField);
        
        if (hiddenIdField && facilityUnitMap[selectedName]) {
            hiddenIdField.value = facilityUnitMap[selectedName];
        } else if (hiddenIdField) {
            hiddenIdField.value = '';
        }
    };

    
    // --- HAUPTFUNKTION (Wird beim Tab-Wechsel aufgerufen) ---
    function renderMaintenanceAnalysis() {
        // --- DATENVORBEREITUNG ---
        const now = new Date();
        const oneYearAgo = new Date();
        oneYearAgo.setFullYear(now.getFullYear() - 1);
    
        // --- 1. KPI BERECHNUNG (REDUZIERT) ---
        let totalTime = 0;
        let timeCount = 0;
        let onTimeCount = 0;
        let totalMaint = 0;
    
        maintenanceOrders.forEach(o => {
            // Ø Lösungszeit (nur completed Issues)
            if(o.type === 'I' && o.status === 'Completed' && o.completedDate) {
                const start = new Date(o.date); 
                const end = new Date(o.completedDate);
                const diff = (end - start) / (1000 * 60 * 60 * 24);
                if(diff >= 0) { totalTime += diff; timeCount++; }
            }
            // On-Time Rate (nur completed Maintenance)
            if(o.type === 'M' && o.status === 'Completed' && o.completedDate) {
                const planned = new Date(o.date);
                const actual = new Date(o.completedDate);
                // Toleranz: 3 Tage
                const diff = (actual - planned) / (1000 * 60 * 60 * 24);
                if(diff <= 3) onTimeCount++;
                totalMaint++;
            }
        });
    
        const avgTime = timeCount > 0 ? (totalTime / timeCount).toFixed(1) : 0;
        const onTimeRate = totalMaint > 0 ? Math.round((onTimeCount / totalMaint) * 100) : 0;
    
        // DOM Updates KPIs
        const kpiAvgEl = document.getElementById('kpi-avg-time');
        const kpiOntimeEl = document.getElementById('kpi-ontime');
        if(kpiAvgEl) kpiAvgEl.textContent = avgTime + " Tage";
        if(kpiOntimeEl) kpiOntimeEl.textContent = onTimeRate + "%";
    
        // --- 2. ASSET HOTSPOT TABELLE (INTERAKTIV) ---
        // Wir rufen hier die neue Helper-Funktion auf, die Daten vorbereitet UND rendert
        prepareAssetData();
    
        // --- 3. PRODUCT TYPE CHART (Ersetzt Bereichs-Donut) ---
        const assetProductTypeMap = {};
        if (hardwareInventoryList) {
            hardwareInventoryList.forEach(h => {
                // Wir speichern unter Label UND ID, um sicher zu gehen
                if(h.label) assetProductTypeMap[h.label] = h.productType;
                if(h.id) assetProductTypeMap[h.id] = h.productType;
            });
        }
        
        const typeCounts = {};
        maintenanceOrders.filter(o => o.type === 'I').forEach(o => {
            const assets = [];
            if (o.affectedAssets) assets.push(...o.affectedAssets);
            if (o.maintenanceObjects) assets.push(...o.maintenanceObjects);
            
            assets.forEach(assetStr => {
                let lookupKey = assetStr;
                try { 
                    if(assetStr.startsWith('{')) {
                        const obj = JSON.parse(assetStr);
                        lookupKey = obj.id || obj.label; // Versuche erst ID, dann Label
                    }
                } catch(e){}
        
                const types = assetProductTypeMap[lookupKey] || ['Unbekannt/Sonstige'];
                const typeArray = Array.isArray(types) ? types : [types];
                
                typeArray.forEach(t => {
                    if(!t) t = 'Unbekannt';
                    typeCounts[t] = (typeCounts[t] || 0) + 1;
                });
            });
        });
        
        const sortedTypes = Object.entries(typeCounts).sort((a,b) => b[1] - a[1]);
        
        renderChart('chart-product-types', 'doughnut', {
            labels: sortedTypes.map(i => i[0]),
            datasets: [{
                data: sortedTypes.map(i => i[1]),
                backgroundColor: ['#3b82f6', '#f59e0b', '#10b981', '#ef4444', '#8b5cf6', '#64748b', '#06b6d4', '#f97316'],
                borderWidth: 0,
                cutout: '65%'
            }]
        }, { 
            plugins: { 
                legend: { position: 'right', labels: { usePointStyle: true, boxWidth: 8, font: { size: 11 } } } 
            } 
        });
        
        
        // --- NEU: BEREICHS-PERFORMANCE & CHART ---
        const areaStats = {};
        // Alle Aufträge durchgehen
        maintenanceOrders.forEach(o => {
            const area = o.topic || o.category || 'Allgemein';
            if (!areaStats[area]) {
                areaStats[area] = { totalIssues: 0, critical: 0, timeSum: 0, timeCount: 0, onTimeOk: 0, onTimeTotal: 0 };
            }
            const stats = areaStats[area];
        
            // Issues (Gesamt & Kritisch)
            if (o.type === 'I') {
                stats.totalIssues++;
                if (o.priority === 'Critical' || o.isBlocker) {
                    stats.critical++;
                }
                // Lösungszeit (nur wenn fertig)
                if (o.status === 'Completed' && o.completedDate) {
                    const diff = (new Date(o.completedDate) - new Date(o.date)) / (1000 * 60 * 60 * 24);
                    if(diff >= 0) { stats.timeSum += diff; stats.timeCount++; }
                }
            }
        
            // On-Time (Wartung)
            if (o.type === 'M' && o.status === 'Completed' && o.completedDate) {
                const diff = (new Date(o.completedDate) - new Date(o.date)) / (1000 * 60 * 60 * 24);
                if(diff <= 3) stats.onTimeOk++;
                stats.onTimeTotal++;
            }
        });
        
        // A) Tabelle füllen
        const areaTbody = document.getElementById('area-performance-table-body');
        if(areaTbody) {
            areaTbody.innerHTML = '';
            // Sortieren nach Total Issues (absteigend)
            Object.entries(areaStats).sort((a,b) => b[1].totalIssues - a[1].totalIssues).forEach(([area, s]) => {
                const avgTime = s.timeCount > 0 ? (s.timeSum / s.timeCount).toFixed(1) + 'd' : '-';
                const onTimePct = s.onTimeTotal > 0 ? Math.round((s.onTimeOk / s.onTimeTotal) * 100) : 0;
                
                let barColor = 'bg-green-500';
                if (onTimePct < 80) barColor = 'bg-yellow-500';
                if (onTimePct < 50) barColor = 'bg-red-500';
                
                const critBadge = s.critical > 0 
                    ? `<span class="bg-red-100 text-red-700 px-2 py-0.5 rounded-full text-[10px] font-bold">${s.critical}</span>`
                    : `<span class="text-slate-300">-</span>`;
        
                const tr = document.createElement('tr');
                tr.className = "hover:bg-slate-50 transition-colors border-b border-slate-100 last:border-0";
                tr.innerHTML = `
                    <td class="px-4 py-3 font-medium text-slate-800">${area}</td>
                    <td class="px-4 py-3 text-center font-bold text-slate-600">${s.totalIssues}</td>
                    <td class="px-4 py-3 text-center">${critBadge}</td>
                    <td class="px-4 py-3 text-center text-slate-600">${avgTime}</td>
                    <td class="px-4 py-3 align-middle">
                        <div class="flex items-center gap-3">
                            <div class="w-full bg-slate-200 rounded-full h-1.5 flex-grow">
                                <div class="${barColor} h-1.5 rounded-full" style="width: ${s.onTimeTotal > 0 ? onTimePct + '%' : '0%'}"></div>
                            </div>
                            <span class="text-xs font-medium text-slate-500 w-8 text-right">${s.onTimeTotal > 0 ? onTimePct + '%' : '-'}</span>
                        </div>
                    </td>
                `;
                areaTbody.appendChild(tr);
            });
        }
        
        // B) Donut Chart (Wiederhergestellt)
        const sortedAreas = Object.entries(areaStats).sort((a,b) => b[1].totalIssues - a[1].totalIssues);
        renderChart('chart-areas', 'doughnut', {
            labels: sortedAreas.map(i => i[0]),
            datasets: [{
                data: sortedAreas.map(i => i[1].totalIssues),
                backgroundColor: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444', '#64748b'],
                borderWidth: 0,
                cutout: '70%'
            }]
        }, { 
            plugins: { 
                legend: { position: 'bottom', labels: { usePointStyle: true, padding: 20, font: { size: 11 } } } 
            } 
        });
        
    
        // --- 4. TREND-CHART (Line) ---
        const trendData = {};
        maintenanceOrders.filter(o => o.status === 'Completed' && o.completedDate).forEach(o => {
            const d = new Date(o.completedDate);
            if(d < oneYearAgo) return;
            const key = d.toLocaleString('de-DE', { month: 'short', year: '2-digit' });
            
            if(!trendData[key]) trendData[key] = { delaySum: 0, count: 0 };
            const planned = new Date(o.date);
            const diff = (d - planned) / (1000 * 60 * 60 * 24);
            trendData[key].delaySum += Math.max(0, diff);
            trendData[key].count++;
        });
        
        const trendLabels = [];
        const trendValues = [];
        for(let i=11; i>=0; i--) {
            const d = new Date();
            d.setMonth(d.getMonth() - i);
            const key = d.toLocaleString('de-DE', { month: 'short', year: '2-digit' });
            trendLabels.push(key);
            const data = trendData[key];
            trendValues.push(data ? (data.delaySum / data.count).toFixed(1) : 0);
        }
    
        renderChart('chart-trend', 'line', {
            labels: trendLabels,
            datasets: [{
                label: 'Ø Verspätung (Tage)',
                data: trendValues,
                borderColor: '#3b82f6',
                backgroundColor: 'rgba(59, 130, 246, 0.1)',
                fill: true,
                tension: 0.4,
                pointRadius: 4,
                pointHoverRadius: 6
            }]
        }, { maintainAspectRatio: false });
    
        // --- 5. INTERVALL-CHART (Scatter) ---
        const scatterData = [];
        taskDefinitions.forEach(def => {
            const orders = maintenanceOrders
                .filter(o => o.parentTaskId === def.id && o.status === 'Completed')
                .sort((a,b) => new Date(a.completedDate) - new Date(b.completedDate));
            
            if(orders.length >= 2) {
                const newest = new Date(orders[orders.length-1].completedDate);
                const oldest = new Date(orders[0].completedDate);
                const diffDays = (newest - oldest) / (1000 * 60 * 60 * 24);
                const avgIst = diffDays / (orders.length - 1);
                let avgSoll = def.intervalValue * (def.intervalUnit === 'Jahre' ? 365 : (def.intervalUnit === 'Wochen' ? 7 : 30));
                
                scatterData.push({ x: avgSoll, y: avgIst, label: def.task });
            }
        });
    
        renderChart('chart-intervals', 'scatter', {
            datasets: [
                { label: 'Wartungspläne', data: scatterData, backgroundColor: '#8b5cf6', pointRadius: 5, pointHoverRadius: 7 },
                { label: 'Ideal-Linie', data: [{x: 0, y: 0}, {x: 365, y: 365}], type: 'line', borderColor: '#e5e7eb', borderDash: [5, 5], pointRadius: 0 }
            ]
        }, {
            maintainAspectRatio: false,
            plugins: {
                tooltip: {
                    callbacks: {
                        label: function(context) {
                            const p = context.raw;
                            if(!p.label) return '';
                            const abw = Math.round(((p.y - p.x) / p.x) * 100);
                            return `${p.label}: Soll ${p.x}d / Ist ${Math.round(p.y)}d (${abw > 0 ? '+' : ''}${abw}%)`;
                        }
                    }
                }
            }
        });
    // --- NEU: HISTORIE NACH BETRIEBSBEREICH ---
        const historyUnitStats = {};
        
        maintenanceOrders.filter(o => o.status === 'Completed').forEach(o => {
            // Wir gruppieren nach plantIndex. Falls leer -> "Ohne Zuordnung"
            const unit = o.plantIndex || 'Ohne Zuordnung';
            
            if (!historyUnitStats[unit]) {
                historyUnitStats[unit] = { name: unit, total: 0, issues: 0, lastDate: null, history: [] };
            }
            
            historyUnitStats[unit].total++;
            if (o.type === 'I') historyUnitStats[unit].issues++;
            
            // Datum des Abschlusses für "Zuletzt am"
            const doneDate = new Date(o.completedDate || o.date);
            if(!historyUnitStats[unit].lastDate || doneDate > historyUnitStats[unit].lastDate) {
                historyUnitStats[unit].lastDate = doneDate;
            }
            
            // Wir speichern die ID für das Modal
            historyUnitStats[unit].history.push(o.id);
        });
    
        // A) Tabelle rendern
        const historyTbody = document.getElementById('history-unit-table-body');
        if(historyTbody) {
            historyTbody.innerHTML = '';
            const sortedHistory = Object.values(historyUnitStats).sort((a,b) => b.total - a.total);
            
            if(sortedHistory.length === 0) {
                historyTbody.innerHTML = '<tr><td colspan="4" class="py-8 text-center text-slate-400 italic">Keine Historie vorhanden</td></tr>';
            }
    
            sortedHistory.forEach(stat => {
                const tr = document.createElement('tr');
                tr.className = "hover:bg-slate-50 transition-colors border-b border-slate-100 last:border-0 group cursor-pointer";
                // Wir nutzen eine neue Funktion openUnitHistory, die intern aber das assetModal nutzt
                tr.onclick = () => openUnitHistory(stat.name, stat.history);
                
                tr.innerHTML = `
                    <td class="px-4 py-3">
                        <div class="font-medium text-purple-600 group-hover:text-purple-800 flex items-center gap-2">
                            ${stat.name}
                            <span class="material-icons-outlined text-xs opacity-0 group-hover:opacity-100 transition-opacity">visibility</span>
                        </div>
                    </td>
                    <td class="px-4 py-3 text-center font-bold text-slate-600">${stat.total}</td>
                    <td class="px-4 py-3 text-center text-slate-500">${stat.issues > 0 ? stat.issues : '-'}</td>
                    <td class="px-4 py-3 text-right text-xs text-slate-500">${stat.lastDate ? stat.lastDate.toLocaleDateString('de-DE') : '-'}</td>
                `;
                historyTbody.appendChild(tr);
            });
            
            // B) Donut Chart rendern
            // Wir nehmen nur die Top 10 für den Chart, sonst wird es zu bunt
            const chartData = sortedHistory.slice(0, 10);
            
            renderChart('chart-history-units', 'doughnut', {
                labels: chartData.map(i => i.name),
                datasets: [{
                    data: chartData.map(i => i.total),
                    backgroundColor: ['#8b5cf6', '#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#64748b', '#06b6d4', '#f97316'],
                    borderWidth: 0,
                    cutout: '65%'
                }]
            }, { 
                plugins: { 
                    legend: { position: 'right', labels: { usePointStyle: true, boxWidth: 8, font: { size: 10 } } } 
                } 
            });
        }    
        // --- 6. PARTNER TABELLE ---
        renderPartnerTable();
    }
    // Öffnet das Asset-History-Modal, aber für einen ganzen Betriebsbereich (Unit)
    function openUnitHistory(unitName, orderIds) {
        // Titel setzen
        document.getElementById('assetHistoryTitle').textContent = unitName;
        document.getElementById('assetHistorySubtitle').textContent = `${orderIds.length} abgeschlossene Aufträge`;
        
        const container = document.getElementById('assetHistoryTimeline');
        container.innerHTML = '';
    
        // Orders holen und sortieren (Neu -> Alt)
        const historyOrders = maintenanceOrders.filter(o => orderIds.includes(o.id));
        historyOrders.sort((a,b) => new Date(b.completedDate || b.date) - new Date(a.completedDate || a.date));
    
        // Rendering-Logik ist exakt gleich wie bei Assets, daher kopieren wir den Block oder nutzen Helper.
        // Hier der Einfachheit halber der Block (da er kurz ist):
        historyOrders.forEach(o => {
            const dateObj = new Date(o.completedDate || o.date);
            const dateStr = dateObj.toLocaleDateString('de-DE', { day: '2-digit', month: 'short', year: 'numeric' });
            
            let dotColor = "bg-green-500 ring-green-200";
            let icon = "check_circle";
            if(o.type === 'I') { dotColor = "bg-orange-500 ring-orange-200"; icon = "build"; } // Issue gefixt
            
            const item = document.createElement('div');
            item.className = "ml-6 relative";
            item.innerHTML = `
                <span class="absolute -left-[31px] top-1 flex h-6 w-6 items-center justify-center rounded-full ring-4 ring-white ${dotColor} text-white">
                    <span class="material-icons-outlined text-[14px]">${icon}</span>
                </span>
                <div class="bg-white p-4 rounded-lg border border-slate-200 shadow-sm hover:shadow-md transition-shadow cursor-pointer" onclick="openWorkOrderModal('${o.id}')">
                    <div class="flex justify-between items-start mb-1">
                        <span class="text-xs font-bold text-slate-400 uppercase tracking-wider">${dateStr}</span>
                        <span class="text-[10px] bg-slate-100 text-slate-600 px-2 py-0.5 rounded-full border border-slate-200">Erledigt</span>
                    </div>
                    <h4 class="font-bold text-slate-800 text-sm mb-1">${o.task}</h4>
                    <div class="mt-2 flex items-center gap-2 text-[10px] text-slate-400 border-t border-slate-100 pt-2">
                        <span>${o.type === 'I' ? 'Issue Behebung' : 'Planmäßige Wartung'}</span>
                        <span>•</span>
                        <span>Von: ${o.completedBy || 'Unbekannt'}</span>
                    </div>
                </div>
            `;
            container.appendChild(item);
        });
    
        document.getElementById('assetHistoryModal').classList.remove('hidden');
    }    
    // --- HELPER 1: Daten aufbereiten für Asset Tabelle ---
    function prepareAssetData() {
        const assetStats = {};
        maintenanceOrders.filter(o => o.type === 'I').forEach(o => {
            const assets = [];
            if (o.affectedAssets) assets.push(...o.affectedAssets);
            if (o.maintenanceObjects) assets.push(...o.maintenanceObjects);
            
            assets.forEach(assetStr => {
                let label = assetStr;
                try { if(assetStr.startsWith('{')) label = JSON.parse(assetStr).label; } catch(e){}
                
                if(!assetStats[label]) {
                    assetStats[label] = { name: label, total: 0, critical: 0, lastDate: null, history: [] };
                }
                assetStats[label].total++;
                if(o.priority === 'Critical' || o.isBlocker) assetStats[label].critical++;
                
                const orderDate = new Date(o.date);
                if(!assetStats[label].lastDate || orderDate > assetStats[label].lastDate) {
                    assetStats[label].lastDate = orderDate;
                }
                assetStats[label].history.push(o.id);
            });
        });
    
        globalAssetStats = Object.values(assetStats).sort((a,b) => b.total - a.total || b.critical - a.critical);
        renderAssetTable();
    }
    
    // --- HELPER 2: Asset Tabelle rendern (mit Suche) ---
    function renderAssetTable() {
        const searchTerm = document.getElementById('asset-search-input')?.value.toLowerCase() || '';
        const tableBody = document.getElementById('asset-hotspot-table-body');
        if(!tableBody) return;
        
        tableBody.innerHTML = '';
        
        const filtered = globalAssetStats.filter(a => a.name.toLowerCase().includes(searchTerm));
        const displayList = filtered.slice(0, 50);
    
        if (displayList.length === 0) {
            tableBody.innerHTML = '<tr><td colspan="4" class="py-8 text-center text-slate-400 italic">Keine passenden Assets gefunden</td></tr>';
            return;
        }
    
        displayList.forEach(stat => {
            const tr = document.createElement('tr');
            tr.className = "hover:bg-slate-50 transition-colors border-b border-slate-100 last:border-0 group cursor-pointer";
            tr.onclick = () => openAssetHistory(stat.name);
            
            const critBadge = stat.critical > 0 
                ? `<span class="bg-red-100 text-red-700 px-2 py-0.5 rounded-full text-[10px] font-bold">${stat.critical}</span>`
                : `<span class="text-slate-300">-</span>`;
    
            tr.innerHTML = `
                <td class="px-4 py-3">
                    <div class="font-medium text-blue-600 group-hover:text-blue-800 flex items-center gap-2">
                        ${stat.name}
                        <span class="material-icons-outlined text-xs opacity-0 group-hover:opacity-100 transition-opacity">visibility</span>
                    </div>
                </td>
                <td class="px-4 py-3 text-center font-bold text-slate-600">${stat.total}</td>
                <td class="px-4 py-3 text-center">${critBadge}</td>
                <td class="px-4 py-3 text-right text-xs text-slate-500">${stat.lastDate ? stat.lastDate.toLocaleDateString('de-DE') : '-'}</td>
            `;
            tableBody.appendChild(tr);
        });
        
        // --- NEU: FOOTER UPDATE ---
        const footer = document.getElementById('asset-table-footer');
        if(footer) {
            if (filtered.length > 50) {
                footer.textContent = `Zeige Top 50 von ${filtered.length} Assets. Bitte Suche verfeinern.`;
                footer.classList.remove('hidden');
            } else {
                footer.textContent = '';
                footer.classList.add('hidden');
            }
        }
    }
    function renderPartnerTable() {
        const tbody = document.getElementById('partner-table-body');
        if(!tbody) return;
        tbody.innerHTML = '';
        
        // Daten aggregieren
        const partnerStats = {};
        maintenanceOrders.forEach(o => {
            if(!o.externalCompanyId) return;
            
            if(!partnerStats[o.externalCompanyId]) {
                partnerStats[o.externalCompanyId] = { count: 0, delaySum: 0, delayCount: 0, open: 0 };
            }
            const stats = partnerStats[o.externalCompanyId];
            
            if(o.status === 'Completed' && o.completedDate) {
                stats.count++;
                const plan = new Date(o.date);
                const done = new Date(o.completedDate);
                const delay = (done - plan) / (1000*60*60*24);
                stats.delaySum += delay; 
                stats.delayCount++;
            } else if (o.status !== 'Completed') {
                stats.open++;
            }
        });
    
        if(companies.length === 0) {
            tbody.innerHTML = '<tr><td colspan="5" class="py-8 text-center text-slate-400 italic">Keine externen Firmen definiert</td></tr>';
            return;
        }
    
        // Rendern
        companies.forEach(comp => {
            const stats = partnerStats[comp.id] || { count: 0, delaySum: 0, delayCount: 0, open: 0 };
            const avgDelay = stats.delayCount > 0 ? (stats.delaySum / stats.delayCount) : 0;
            
            // Pünktlichkeits-Balken Logik
            let barColor = 'bg-green-500';
            let barWidth = '50%'; // Mitte = Pünktlich (0 Tage)
            
            // Mapping: -5 Tage (Früh) bis +10 Tage (Spät)
            // Wir verschieben den Nullpunkt (50%) je nach Delay
            // Ein Tag = 5% Verschiebung
            let shift = avgDelay * 5; 
            let widthVal = 50 + shift;
            
            if (widthVal > 100) widthVal = 100;
            if (widthVal < 0) widthVal = 0;
            
            if (avgDelay > 2) barColor = 'bg-yellow-500';
            if (avgDelay > 7) barColor = 'bg-red-500';
            if (avgDelay <= 0) barColor = 'bg-green-600';
    
            barWidth = widthVal + '%';
    
            const tr = document.createElement('tr');
            tr.className = "hover:bg-slate-50 border-b border-slate-100 last:border-0 transition-colors";
            tr.innerHTML = `
                <td class="px-6 py-4 font-medium text-slate-800">${comp.name}</td>
                <td class="px-6 py-4 text-center">${stats.count}</td>
                <td class="px-6 py-4 text-center text-slate-500">${stats.delayCount > 0 ? Math.round(avgDelay) + 'd' : '-'}</td>
                <td class="px-6 py-4 align-middle">
                    <div class="w-full bg-slate-200 rounded-full h-1.5 relative">
                        <!-- Marker für "Heute/Pünktlich" in der Mitte -->
                        <div class="absolute left-1/2 top-0 bottom-0 w-0.5 bg-slate-400 -ml-[1px]"></div>
                        <div class="${barColor} h-1.5 rounded-full transition-all duration-500" style="width: ${barWidth}"></div>
                    </div>
                </td>
                <td class="px-6 py-4 text-center font-bold ${stats.open > 0 ? 'text-blue-600' : 'text-slate-400'}">${stats.open}</td>
            `;
            tbody.appendChild(tr);
        });
    }
    loadFacilityUnitSuggestions();
    function calculateNextDueDate(val, unit, baseDateStr) { let d = baseDateStr ? new Date(baseDateStr) : new Date(); if(isNaN(d.getTime())) d = new Date(); const v = parseInt(val); if(unit === 'Wochen') d.setDate(d.getDate() + v * 7); if(unit === 'Monate') d.setMonth(d.getMonth() + v); if(unit === 'Jahre') d.setFullYear(d.getFullYear() + v); return d.toISOString().split('T')[0]; }
    async function loadLogs() {
        try {
            const res = await fetch('/api/maintenance/logs');
            if(res.ok) maintenanceLogs = await res.json();
        } catch(e) { console.error(e); }
    }
    function renderLogbook() {
        const container = document.getElementById('logbook-container');
        const searchTerm = document.getElementById('logbook-search').value.toLowerCase();
        container.innerHTML = '';
    
        // Filtern

        const filtered = maintenanceLogs.filter(l => 
            (l.task && l.task.toLowerCase().includes(searchTerm)) || 
            (l.description && l.description.toLowerCase().includes(searchTerm)) ||
            (l.category && l.category.toLowerCase().includes(searchTerm)) ||
            (l.plantIndex && l.plantIndex.toLowerCase().includes(searchTerm)) // <--- NEU
        );
    
        // Sortieren (Neu -> Alt)
        filtered.sort((a,b) => new Date(b.date) - new Date(a.date));
    
        // Gruppieren nach Monat (YYYY-MM)
        const groups = {};
        filtered.forEach(log => {
            const d = new Date(log.date);
            const key = d.toLocaleString('de-DE', { month: 'long', year: 'numeric' });
            if(!groups[key]) groups[key] = [];
            groups[key].push(log);
        });
    
        if (Object.keys(groups).length === 0) {
            container.innerHTML = '<p class="text-center text-slate-400 italic py-8">Keine Einträge gefunden.</p>';
            return;
        }
    
        // Rendern
        for (const [month, logs] of Object.entries(groups)) {
            const groupHtml = `
                <div class="bg-white rounded-lg shadow border border-slate-200 overflow-hidden">
                    <div class="bg-slate-50 px-4 py-2 border-b border-slate-200 font-bold text-slate-700 sticky top-0">
                        ${month} <span class="ml-2 text-xs font-normal text-slate-500 bg-white px-2 py-0.5 rounded border border-slate-200">${logs.length}</span>
                    </div>
                    <div class="divide-y divide-slate-100">
                        ${logs.map(l => {
                            const dateStr = new Date(l.date).toLocaleDateString('de-DE');
                            const user = l.createdBy || 'Unbekannt';
                            const initials = user.split(' ').map(n=>n[0]).join('').substring(0,2).toUpperCase();
                            
                            let plantBadge = '';
                            if (l.plantIndex) {
                                plantBadge = `<span class="text-[10px] px-2 py-0.5 bg-slate-100 text-slate-600 rounded border border-slate-200 font-mono" title="Betriebsbereich"><span class="material-icons-outlined text-[10px] align-middle mr-1">place</span>${l.plantIndex}</span>`;
                            }
    
                            return `
                            <div class="p-4 hover:bg-slate-50 transition-colors cursor-pointer" onclick="openLogEntry('${l.id}')">
                                <div class="flex justify-between items-start mb-1">
                                    <div class="flex items-center gap-2 flex-wrap">
                                        <span class="text-sm font-bold text-slate-800">${l.task}</span>
                                        <span class="text-[10px] px-2 py-0.5 bg-indigo-50 text-indigo-700 rounded border border-indigo-100">${l.category || 'Allgemein'}</span>
                                        ${plantBadge} <!-- NEU -->
                                    </div>
                                    <span class="text-xs text-slate-400 whitespace-nowrap ml-2">${dateStr}</span>
                                </div>
                                <p class="text-sm text-slate-600 mb-2 whitespace-pre-wrap">${l.description || ''}</p>
                                <div class="flex items-center gap-2 text-xs text-slate-400">
                                    <div class="w-5 h-5 rounded-full bg-slate-200 text-slate-600 flex items-center justify-center font-bold text-[10px]">${initials}</div>
                                    <span>${user}</span>
                                </div>
                            </div>`;
                        }).join('')}
                    </div>
                </div>`;
            container.innerHTML += groupHtml;
        }
    }
    
    function openCreateLogModal() {
        openLogEntry(null);
    }
    function openLogEntry(logId = null) {
        document.getElementById('createLogForm').reset();
        document.getElementById('logId').value = '';
        document.getElementById('btnDeleteLog').classList.add('hidden');
        document.getElementById('logMetaInfo').classList.add('hidden');
        document.getElementById('logPlantIndex').value = ''; 
        
        // Kategorie Dropdown füllen (falls noch leer)
        // ... (Logik wie bisher) ...
    
        if (logId) {
            // EDIT MODE
            const log = maintenanceLogs.find(l => l.id === logId);
            if(!log) return;
    
            document.getElementById('logModalTitle').textContent = "Eintrag bearbeiten";
            document.getElementById('logId').value = log.id;
            document.getElementById('logTitle').value = log.task;
            document.getElementById('logDate').value = log.date;
            document.getElementById('logCategory').value = log.category || 'Allgemein';
            document.getElementById('logDesc').value = log.description;
            document.getElementById('logPlantIndex').value = log.plantIndex || '';
            
            // Meta-Info
            document.getElementById('logCreatedBy').textContent = log.createdBy || 'Unbekannt';
            document.getElementById('logCreatedAt').textContent = new Date(log.createdAt).toLocaleString('de-DE');
            document.getElementById('logMetaInfo').classList.remove('hidden');
    
            // Löschen erlauben (für Admin oder Ersteller)
            if(currentUserIsAdmin || log.createdBy === currentUser) {
                 document.getElementById('btnDeleteLog').classList.remove('hidden');
            }
        } else {
            // CREATE MODE
            document.getElementById('logModalTitle').textContent = "Neuer Logbucheintrag";
            document.getElementById('logDate').value = new Date().toISOString().split('T')[0];
        }
        
        openModal('createLogModal');
    }
        
    function closeCreateLogModal() { closeModal('createLogModal'); }
    
    async function submitLogEntry() {
        const title = document.getElementById('logTitle').value.trim();
        if(!title) { alert("Titel fehlt."); return; }
    
        const id = document.getElementById('logId').value;
        
        if (id) {
            // UPDATE
            const log = maintenanceLogs.find(l => l.id === id);
            if(log) {
                log.task = title;
                log.date = document.getElementById('logDate').value;
                log.category = document.getElementById('logCategory').value;
                log.description = document.getElementById('logDesc').value;
                log.plantIndex = document.getElementById('logPlantIndex').value; 
            }
        } else {
            // CREATE
            const newLog = {
                id: `LOG-${Date.now()}`,
                type: 'L',
                task: title,
                description: document.getElementById('logDesc').value,
                category: document.getElementById('logCategory').value,
                date: document.getElementById('logDate').value,
                plantIndex: document.getElementById('logPlantIndex').value,
                createdBy: currentUser, 
                createdAt: new Date().toISOString()
            };
            maintenanceLogs.unshift(newLog);
        }
        
        // Speichern
        try {
            await fetch('/api/maintenance/logs', {
                method: 'POST', 
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({ logs: maintenanceLogs })
            });
            closeCreateLogModal();
            renderLogbook();
        } catch(e) { alert("Fehler beim Speichern"); }
    }
    async function deleteLogEntry() {
        const id = document.getElementById('logId').value;
        if(!id) return;
        
        if(confirm("Diesen Eintrag wirklich löschen?")) {
            maintenanceLogs = maintenanceLogs.filter(l => l.id !== id);
            
            try {
                await fetch('/api/maintenance/logs', {
                    method: 'POST', 
                    headers: {'Content-Type': 'application/json'},
                    body: JSON.stringify({ logs: maintenanceLogs })
                });
                closeCreateLogModal();
                renderLogbook();
            } catch(e) { alert("Fehler beim Löschen"); }
        }
    }
    // --- 4. INITIALISIERUNG (Wenn die Seite geladen ist) ---
    document.addEventListener('DOMContentLoaded', () => {
        console.log("Daten geladen, starte Initialisierung...");

        // --- SIDEBAR LOGIC (NEU EINFÜGEN) ---
        const sidebar = document.getElementById('sidebar');
        const sidebarToggleBtn = document.getElementById('sidebar-toggle');
        if (sidebar && sidebarToggleBtn) {
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
            
            const applyState = (isCollapsed) => {
                if (isCollapsed) {
                    sidebar.classList.add('is-collapsed');
                    if(toggleBtnIcon) toggleBtnIcon.textContent = 'menu';
                } else {
                    sidebar.classList.remove('is-collapsed');
                    if(toggleBtnIcon) toggleBtnIcon.textContent = 'chevron_left';
                }
            };

            sidebarToggleBtn.addEventListener('click', () => {
                const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
                localStorage.setItem('sidebarCollapsed', isNowCollapsed);
                applyState(isNowCollapsed);
            });

            // Initialen Status laden
            const savedState = localStorage.getItem('sidebarCollapsed') === 'true';
            applyState(savedState);
        }

        const GLOBAL_AREAS =[
            "Anlagen / Mechanik", "Elektrik / Messtechnik", "Test Engineering",
            "Facility Management", "IT / Software", "Allgemein"
        ];
        
        function initAreaDropdowns() {
            const targets = ['defTopic', 'issueCategory', 'actionCategory', 'woEditCategory', 'logCategory'];
            targets.forEach(id => {
                const el = document.getElementById(id);
                if(el) {
                    const currentVal = el.value; 
                    el.innerHTML = '';
                    GLOBAL_AREAS.forEach(area => {
                        const opt = document.createElement('option');
                        opt.value = area; opt.textContent = area; el.appendChild(opt);
                    });
                    if(currentVal) el.value = currentVal;
                }
            });
        }

        initAreaDropdowns();

        // Hier werden die Listen gefüllt (Dein bestehender Code)
        loadTestBenchOptions();
        loadHardwareInventory();
        loadFacilityUnitSuggestions();
        loadLogs();
        
        // Tabellen initial rendern
        renderOrdersTable();
        renderDefinitionsTable();
        initFilterDropdowns(); // Wichtig für deine neuen Filter-Chips!

        // --- 5. DEEP LINK HANDLING (Innerhalb von DOMContentLoaded aufrufen) ---
        handleDeepLink();
    });

function handleDeepLink() {
        // Wir warten einen Moment, bis das initiale Rendering der Seite fertig ist
        setTimeout(() => {
            try {
                const urlParams = new URLSearchParams(window.location.search);
                const openId = urlParams.get('openId');

                if (openId) {
                    console.log("Deep Link: ID gefunden", openId);

                    // URL bereinigen
                    const cleanUrl = window.location.pathname;
                    window.history.replaceState({}, document.title, cleanUrl);

                    // Daten suchen
                    const def = taskDefinitions.find(d => d.id === openId);
                    const order = maintenanceOrders.find(o => o.id === openId);
                    
                    let targetTabName = 'active';

                    if (def) {
                        targetTabName = 'definitions';
                    } else if (order) {
                        if (order.status === 'Completed') {
                            targetTabName = 'history';
                        }
                    }

                    // --- SCHRITT 1: MODAL SOFORT ÖFFNEN ---
                    // Wir öffnen das Modal unabhängig vom Tab-Status.
                    // Das Overlay liegt eh über allem.
                    console.log("Deep Link: Öffne Modal PRIORITÄT 1");
                    
                    if (def) {
                        if(typeof openDefinitionModal === 'function') openDefinitionModal(openId);
                    } else if (order) {
                        if(typeof openWorkOrderModal === 'function') {
                            currentOrderId = openId;
                            openWorkOrderModal(openId);
                        }
                    }

                    // --- SCHRITT 2: TAB WECHSEL (Verzögert) ---
                    // Wir machen das erst, wenn das Modal sicher offen ist.
                    // Wenn das hier crasht, sieht der User wenigstens schon das Modal.
                    setTimeout(() => {
                        console.log(`Deep Link: Versuche Tab im Hintergrund zu wechseln auf '${targetTabName}'`);
                        try {
                            // Button finden für Styling
                            const allTabs = Array.from(document.querySelectorAll('.tab-btn'));
                            let targetBtn = allTabs.find(btn => btn.getAttribute('onclick')?.includes(`'${targetTabName}'`));
                            
                            // Fallback
                            if (!targetBtn && targetTabName === 'history') targetBtn = allTabs[3];

                            if (typeof switchTab === 'function') {
                                switchTab(targetTabName, targetBtn);
                            }
                        } catch (tabErr) {
                            console.warn("Deep Link: Tab-Wechsel im Hintergrund fehlgeschlagen (nicht kritisch):", tabErr);
                        }
                    }, 800); // 800ms warten - Modal ist dann schon da
                }
            } catch (e) {
                console.error("Deep Link CRITICAL ERROR:", e);
            }
        }, 100);
    }
</script>
</body>
</html>
"""
FACILITY_REPORT_TEMPLATE = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <title>Anlagen-Audit Report</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <style>
        body { font-family: "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 12px; color: #333; padding: 20px; }
        h1 { margin-bottom: 5px; font-size: 24px; color: #0055A4; border-bottom: 2px solid #0055A4; padding-bottom: 10px; }
        h2 { margin-top: 30px; margin-bottom: 10px; font-size: 18px; background: #eee; padding: 8px; border-left: 5px solid #0055A4; }
        .meta-info { color: #666; margin-bottom: 20px; font-size: 11px; }

        table { width: 100%; border-collapse: collapse; margin-bottom: 20px; page-break-inside: auto; }
        th, td { border: 1px solid #ddd; padding: 8px; vertical-align: top; text-align: left; }
        th { background-color: #f8f9fa; font-weight: bold; color: #495057; }
        tr { page-break-inside: avoid; page-break-after: auto; }

        .node-name { font-weight: 600; font-size: 13px; }
        .level-0 { padding-left: 0px; }
        .level-1 { padding-left: 15px; border-left: 3px solid #eee; }
        .level-2 { padding-left: 30px; border-left: 3px solid #ccc; }
        .level-3 { padding-left: 45px; border-left: 3px solid #999; }

        .tag { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 10px; margin-right: 3px; margin-bottom: 2px; border: 1px solid #ccc; font-weight: 500; }
        .tag-hw { background: #e0f2ff; border-color: #b8daff; color: #004085; }
        .tag-resp { background: #e0ffef; border-color: #b3e6ce; color: #0f5132; }
        .tag-store { background: #fff3cd; border-color: #ffeeba; color: #856404; } /* NEU: Gelb für Lager */

        .section-label { font-size: 9px; text-transform: uppercase; color: #999; font-weight: bold; margin-bottom: 2px; margin-top: 6px; }
        .section-label:first-child { margin-top: 0; }

        .flow-io { font-size: 11px; margin-bottom: 4px; }
        .flow-label { font-weight: bold; color: #555; }
        .flow-in { color: #28a745; } 
        .flow-out { color: #dc3545; } 

        .page-break { page-break-before: always; }

        @media print { 
            .no-print { display: none; } 
            body { padding: 0; } 
            th { background-color: #eee !important; -webkit-print-color-adjust: exact; } 
            .tag { -webkit-print-color-adjust: exact; }
        }
    </style>
</head>
<body>
    <div class="no-print" style="margin-bottom: 20px;">
        <button onclick="window.print()" style="padding: 10px 20px; cursor: pointer; font-size: 14px; background: #0055A4; color: white; border: none; border-radius: 4px;">🖨️ Bericht drucken</button>
    </div>

    <h1>Anlagen-Strukturbericht</h1>
    <div class="meta-info">Erstellt am: {{ date }} | MBET Facility Management</div>

    <!-- TEIL 1: BETRIEBSEINHEITEN -->
    <h2>1. Betriebseinheiten ({{ count_units }} Elemente)</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 40px;">Idx</th>
                <th style="width: 250px;">Bezeichnung / Hierarchie</th>
                <th>Verantwortlichkeit & Assets</th>
                <th>Prozessflüsse</th>
                <th>Details</th>
            </tr>
        </thead>
        <tbody>
            {% for item in flat_data_units %}
            <tr>
                <td>{{ item.index_str }}</td>
                <td><div class="level-{{ item.level if item.level < 4 else 3 }}"><span class="node-name">{{ item.name }}</span></div></td>
                <td>
                    {% if item.responsibility %}
                        <div class="section-label">Verantwortlich:</div>
                        {% for resp in item.responsibility %}<span class="tag tag-resp">{{ resp }}</span>{% endfor %}
                    {% endif %}

                    {% if item.hardware %}
                        <div class="section-label">Hardware:</div>
                        {% for hw in item.hardware %}<span class="tag tag-hw">{{ hw }}</span>{% endfor %}
                    {% endif %}

                    {% if item.storageLink %}
                        <div class="section-label">Lager:</div>
                        <span class="tag tag-store">📦 {{ item.storageLink }}</span>
                    {% endif %}
                </td>
                <td>
                    {% for flow in item.flows_in %}<div class="flow-io"><span class="flow-label flow-in">⬇ IN:</span> {{ flow.name }} ({{ flow.medium }})</div>{% endfor %}
                    {% for flow in item.flows_out %}<div class="flow-io"><span class="flow-label flow-out">⬆ OUT:</span> {{ flow.name }} ({{ flow.medium }})</div>{% endfor %}
                    {% for flow in item.flows_assoc %}<div class="flow-io"><span class="flow-label">● LINK:</span> {{ flow.name }}</div>{% endfor %}
                </td>
                <td>{% for k, v in item.details.items() %}<div><strong>{{ k }}:</strong> {{ v }}</div>{% endfor %}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>

    <div class="page-break"></div>

    <!-- TEIL 2: GEBÄUDE -->
    <h2>2. Gebäudestruktur ({{ count_buildings }} Elemente)</h2>
    <table>
        <thead>
            <tr>
                <th style="width: 40px;">Idx</th>
                <th style="width: 250px;">Bezeichnung / Hierarchie</th>
                <th>Verantwortlichkeit & Ausstattung</th>
                <th>Prozessflüsse</th>
                <th>Details</th>
            </tr>
        </thead>
        <tbody>
            {% for item in flat_data_buildings %}
            <tr>
                <td>{{ item.index_str }}</td>
                <td><div class="level-{{ item.level if item.level < 4 else 3 }}"><span class="node-name">{{ item.name }}</span></div></td>
                <td>
                    {% if item.responsibility %}
                        <div class="section-label">Verantwortlich:</div>
                        {% for resp in item.responsibility %}<span class="tag tag-resp">{{ resp }}</span>{% endfor %}
                    {% endif %}

                    {% if item.hardware %}
                        <div class="section-label">Ausstattung:</div>
                        {% for hw in item.hardware %}<span class="tag tag-hw">{{ hw }}</span>{% endfor %}
                    {% endif %}

                    {% if item.storageLink %}
                        <div class="section-label">Lager:</div>
                        <span class="tag tag-store">📦 {{ item.storageLink }}</span>
                    {% endif %}
                </td>
                <td>
                    {% for flow in item.flows_in %}<div class="flow-io"><span class="flow-label flow-in">⬇ IN:</span> {{ flow.name }} ({{ flow.medium }})</div>{% endfor %}
                    {% for flow in item.flows_out %}<div class="flow-io"><span class="flow-label flow-out">⬆ OUT:</span> {{ flow.name }} ({{ flow.medium }})</div>{% endfor %}
                    {% for flow in item.flows_assoc %}<div class="flow-io"><span class="flow-label">● LINK:</span> {{ flow.name }}</div>{% endfor %}
                </td>
                <td>{% for k, v in item.details.items() %}<div><strong>{{ k }}:</strong> {{ v }}</div>{% endfor %}</td>
            </tr>
            {% endfor %}
        </tbody>
    </table>
    <div class="page-break"></div>

<!-- TEIL 3: RÜSTWAGEN -->
    <h2>3. Mobile Rüstwagen ({{ trolleys|length }} Einheiten)</h2>
    {% if trolleys|length > 0 %}
        {% for name, data in trolleys.items() %}
        <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h3 style="margin: 0 0 5px 0; font-size: 14px; border-bottom: 1px solid #ccc; padding-bottom: 5px;">
                🛒 {{ name }} <span style="font-weight: normal; font-size: 11px; color: #666; margin-left: 10px;">(Engine Type: {{ data.engineType }})</span>
            </h3>
            
            <!-- HIER WAR DER FEHLER: data['items'] statt data.items -->
            {% if data['items']|length > 0 %}
            <table style="margin-top: 5px; font-size: 11px;">
                <thead>
                    <tr>
                        <th style="width: 100px;">P/N</th>
                        <th style="width: 100px;">S/N</th>
                        <th>Bezeichnung / Name</th>
                        <th style="width: 40px; text-align: center;">Menge</th>
                        <th style="width: 60px;">Pos (L/B)</th>
                    </tr>
                </thead>
                <tbody>
                    <!-- HIER EBENFALLS: data['items'] -->
                    {% for item in data['items'] %}
                    <tr>
                        <td style="font-family: monospace;">{{ item.pn }}</td>
                        <td style="font-family: monospace;">{{ item.sn }}</td>
                        <td>{{ item.name or 'Unbekannt' }}</td>
                        <td style="text-align: center;">{{ item.qty }}</td>
                        <td>{{ item.level }}/{{ item.box }}</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
            {% else %}
            <div style="font-style: italic; color: #999; font-size: 11px; padding: 5px;">Leer / Keine Items.</div>
            {% endif %}
        </div>
        {% endfor %}
    {% else %}
        <p>Keine Rüstwagen definiert.</p>
    {% endif %}

    <div class="page-break"></div>

    <!-- TEIL 4: LAGERORTE -->
    <h2>4. Feste Lagerorte ({{ storage|length }} Einheiten)</h2>
    {% if storage|length > 0 %}
        {% for name, data in storage.items() %}
        <div style="margin-bottom: 25px; page-break-inside: avoid;">
            <h3 style="margin: 0 0 5px 0; font-size: 14px; border-bottom: 1px solid #ccc; padding-bottom: 5px;">
                📦 {{ name }} <span style="font-weight: normal; font-size: 11px; color: #666; margin-left: 10px;">(Bereich: {{ data.engineType }})</span>
            </h3>
            
            <!-- HIER WAR DER FEHLER: data['items'] statt data.items -->
            {% if data['items']|length > 0 %}
            <table style="margin-top: 5px; font-size: 11px;">
                <thead>
                    <tr>
                        <th style="width: 100px;">P/N</th>
                        <th style="width: 100px;">S/N</th>
                        <th>Bezeichnung / Name</th>
                        <th style="width: 40px; text-align: center;">Menge</th>
                        <th style="width: 60px;">Pos (L/B)</th>
                    </tr>
                </thead>
                <tbody>
                    <!-- HIER EBENFALLS: data['items'] -->
                    {% for item in data['items'] %}
                    <tr>
                        <td style="font-family: monospace;">{{ item.pn }}</td>
                        <td style="font-family: monospace;">{{ item.sn }}</td>
                        <td>{{ item.name or 'Unbekannt' }}</td>
                        <td style="text-align: center;">{{ item.qty }}</td>
                        <td>{{ item.level }}/{{ item.box }}</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
            {% else %}
            <div style="font-style: italic; color: #999; font-size: 11px; padding: 5px;">Leer / Keine Items.</div>
            {% endif %}
        </div>
        {% endfor %}
    {% else %}
        <p>Keine Lagerorte definiert.</p>
    {% endif %}
</body>
</html>
"""

FACILITY_VIEWER_HTML = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Interaktive Anlagen-Visualisierung</title>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <style>
        :root { --panel-width: 500px; --panel-top-margin: 20px; --panel-left-margin: 20px; --button-size: 44px; }
        body { margin: 0; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background-color: #f4f6f8; }

        #webgl-canvas-container, #css-label-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; }
        #css-label-container { pointer-events: none; }
        body.picking-mode, body.picking-mode #webgl-canvas-container { cursor: crosshair !important; }

        /* STATUS LABELS */
        .status-label { background-color: rgba(255, 255, 255, 0.9); color: #333; padding: 5px 12px; border-radius: 8px; font-size: 13px; font-weight: 500; backdrop-filter: blur(10px); box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); border: 1px solid rgba(0, 0, 0, 0.05); pointer-events: auto; cursor: pointer; transition: all 0.2s ease; text-align: left; }
        .status-label:hover { transform: scale(1.05) translateY(-2px); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); }
        .status-label.expanded { min-width: 180px; padding: 10px 15px; }
        .status-label hr { border: none; border-top: 1px solid #eee; margin: 8px 0; }
        .status-label .detail-item { display: flex; justify-content: space-between; font-size: 12px; margin-top: 4px; }
        .status-label .detail-item strong { color: #555; font-weight: 600; }

        /* SIDEBAR CONTAINER */
        #treeview-container { position: fixed; top: 20px; left: 74px; width: var(--panel-width); max-height: calc(100vh - 40px); background-color: rgba(255, 255, 255, 0.95); backdrop-filter: blur(12px); border: 1px solid rgba(0,0,0,0.08); border-radius: 12px; box-shadow: 0 8px 30px rgba(0,0,0,0.12); padding: 0 15px 15px 15px; box-sizing: border-box; display: flex; flex-direction: column; overflow: hidden; z-index: 20; opacity: 0; transform: translateX(-20px); transition: opacity 0.3s ease, transform 0.3s ease; pointer-events: none; transition: width 0.3s ease, opacity 0.3s ease, transform 0.3s ease; }
        #treeview-container.visible { opacity: 1; transform: translateX(0); pointer-events: auto; }

        #toggle-treeview-btn { position: fixed; top: 20px; left: 20px; width: 44px; height: 44px; background-color: #fff; border: 1px solid #ddd; border-radius: 8px; cursor: pointer; z-index: 30; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); font-size: 24px; line-height: 1; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; }
        #toggle-treeview-btn:hover { background-color: #f8f8f8; transform: translateY(-2px); }

        /* HEADER */
        .treeview-header { position: relative; display: flex; justify-content: space-between; align-items: center; padding: 15px 0 10px 0; flex-shrink: 0; border-bottom: 1px solid transparent; transition: border-color 0.2s; }
        .treeview-header.with-border { border-bottom: 1px solid #eee; margin-bottom: 10px; }
        .header-title-group { display: flex; align-items: center; gap: 10px; flex-shrink: 0; flex-grow: 1; overflow: hidden; }
        .treeview-header h3 { margin: 0; font-size: 18px; color: #333; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .header-actions { display: flex; gap: 6px; flex-shrink: 0; margin-left: 10px; align-items: center; }
        .header-btn-round { background: #e9ecef; border: 1px solid #dee2e6; color: #495057; border-radius: 50%; cursor: pointer; width: 30px; height: 30px; font-size: 14px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; padding: 0; flex-shrink: 0; }
        .header-btn-round:hover { background: #ced4da; border-color: #adb5bd; color: #212529; }
        .header-btn-round.active { background: #007bff; color: white; border-color: #0056b3; }
        .action-icon-text { font-size: 18px; color: #666; cursor: pointer; padding: 0 4px; display: inline-block; line-height: 1; }
        .action-icon-text:hover { color: #000; }
        #save-indicator { font-size: 11px; color: #28a745; opacity: 0; transition: opacity 0.5s ease; pointer-events: none; margin-right: 10px; white-space: nowrap; }

        .sidebar-view { display: flex; flex-direction: column; height: 100%; overflow: hidden; flex: 1; }
        .sidebar-view.hidden { display: none !important; }

        #search-container { margin-bottom: 10px; flex-shrink: 0; background: #f8f9fa; padding: 10px; border-radius: 8px; border: 1px solid #eee; display: none; }
        #search-container.visible { display: block; animation: fadeIn 0.2s ease-in-out; }
        @keyframes fadeIn { from { opacity: 0; transform: translateY(-5px); } to { opacity: 1; transform: translateY(0); } }
        #search-input { width: 100%; padding: 8px 12px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; box-sizing: border-box; transition: border-color 0.2s; background: #fff; }
        #search-input:focus { border-color: #007bff; outline: none; }
        .search-filters { display: flex; gap: 5px; margin-top: 5px; }
        .filter-btn { border: 1px solid #ddd; background: #fff; font-size: 11px; padding: 2px 8px; border-radius: 10px; cursor: pointer; color: #666; transition: all 0.1s; }
        .filter-btn:hover { background: #eee; }
        .filter-btn.active { background: #007bff; color: white; border-color: #007bff; }

        #tree { flex-grow: 1; overflow-y: auto; padding-right: 5px; margin-top: 5px; border-top: 1px solid #eee; padding-top: 10px; }
        .treeview ul { list-style-type: none; padding-left: 17px; margin: 0; border-left: 1px solid #e8e8e8; }
        #tree > ul { border-left: none; padding-left: 0; }
        #tree > ul > li > ul { border-left-color: #dddddd; }
        .treeview li { padding: 1px 0; }

        .tree-row { display: flex; align-items: center; padding: 5px 8px; border-radius: 6px; transition: background-color 0.2s ease; position: relative; font-size: 14px; cursor: pointer; border: 2px solid transparent; }
        .tree-row:hover { background-color: #f0f0f0; } 
        .tree-row.active { background-color: #e0eaf3; }
        .tree-row.search-match { background-color: #fff8e1; border-color: #ffe58f; }

        .tree-row.drag-over-middle { background-color: #e6f2ff; border: 2px solid #007bff; }
        .tree-row.drag-over-top { border-top: 2px solid #007bff; }
        .tree-row.drag-over-bottom { border-bottom: 2px solid #007bff; }

        .tree-toggler { cursor: pointer; width: 18px; text-align: center; z-index: 5; }
        .tree-toggler::before { content: '▶'; font-size: 0.7em; transition: transform 0.2s ease; display: inline-block; }
        .tree-item.open > .tree-row > .tree-toggler::before { transform: rotate(90deg); }
        .tree-toggler.empty { cursor: default; } .tree-toggler.empty::before { content: ''; }
        .tree-item > ul { display: none; }
        .tree-item.open > ul { display: block; }
        .tree-number { width: 45px; flex-shrink: 0; text-align: right; margin-right: 8px; color: #888; font-size: 13px; }
        .tree-label { flex-grow: 1; user-select: none; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
        .tree-leaf .tree-label { font-weight: 500; }
        .tree-row.active .tree-label, .tree-row.active .tree-number { color: #0056b3; }

        .tree-actions { display: none; margin-left: auto; padding-left: 10px; gap: 4px; z-index: 5; align-items: center; }
        #tree.editing .tree-row:hover .tree-actions { display: flex; }

        .info-icon { display: inline-flex; justify-content: center; align-items: center; width: 18px; height: 18px; border-radius: 50%; background-color: #17a2b8; color: white; font-size: 11px; font-weight: bold; font-family: serif; cursor: help; margin-right: 5px; opacity: 0.7; transition: opacity 0.2s; }
        .info-icon:hover { opacity: 1; }
        .tree-row .info-icon-container { margin-left: auto; display: flex; align-items: center; }

        #tree-tooltip { position: fixed; top: 0; left: 0; z-index: 9999; background-color: rgba(30, 30, 30, 0.95); color: #fff; padding: 10px 15px; border-radius: 6px; font-size: 12px; pointer-events: none; display: none; max-width: 300px; box-shadow: 0 5px 20px rgba(0,0,0,0.2); backdrop-filter: blur(4px); line-height: 1.4; }
        #tree-tooltip strong { color: #61bfff; display: block; margin-bottom: 2px; margin-top: 6px; }
        #tree-tooltip strong:first-child { margin-top: 0; }
        #tree-tooltip ul { margin: 0; padding-left: 15px; list-style-type: disc; }
        #tree-tooltip li { margin-bottom: 2px; }

        .sidebar-form-group { margin-bottom: 12px; }
        .sidebar-form-group label { display: block; font-weight: 600; font-size: 13px; color: #555; margin-bottom: 4px; }
        .sidebar-form-group input[type="text"], .sidebar-form-group select { width: 100%; padding: 8px; border: 1px solid #ccc; border-radius: 6px; font-size: 14px; box-sizing: border-box; }
        .sidebar-form-group input[type="color"] { width: 100%; padding: 2px; height: 36px; border: 1px solid #ccc; border-radius: 6px; cursor: pointer; box-sizing: border-box; }

        .flow-filter-container { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; background: #f8f9fa; padding: 10px; border-radius: 6px; border: 1px solid #eee; flex-shrink: 0; }
        .flow-filter-container input, .flow-filter-container select { font-size: 13px; padding: 6px; border: 1px solid #ddd; border-radius: 4px; flex: 1; min-width: 100px; box-sizing: border-box; }
        .flow-editor-list-container { flex-grow: 1; overflow-y: auto; padding-right: 2px; }
        .flow-editor-list { list-style: none; padding: 0; margin: 0; }
        .flow-editor-list li { display: flex; justify-content: space-between; align-items: flex-start; padding: 10px; border-bottom: 1px solid #eee; transition: background 0.1s; border-radius: 6px; margin-bottom: 2px; }
        .flow-editor-list li:hover { background-color: #f9f9f9; }
        .flow-editor-list li:last-child { border-bottom: none; }
        .flow-name { cursor: pointer; flex-grow: 1; display: flex; flex-direction: column; gap: 4px; }
        .flow-name strong { color: #333; font-weight: 500; }
        .flow-name:hover strong { color: #007bff; }
        .flow-badges { display: flex; flex-wrap: wrap; gap: 4px; }
        .flow-unit-badge { font-size: 10px; color: #666; background: #e9ecef; padding: 2px 6px; border-radius: 4px; border: 1px solid #dee2e6; }
        .flow-medium-badge { font-size: 10px; color: #004085; background: #cce5ff; padding: 2px 6px; border-radius: 4px; border: 1px solid #b8daff; }
        .flow-actions { display: none; gap: 4px; margin-left: 8px; align-self: center; }
        #view-process-list.editing .flow-actions { display: flex; }
        .add-flow-btn { display: none; width: 100%; padding: 10px; margin-top: 10px; background-color: #28a745; color: white; border: none; border-radius: 6px; cursor: pointer; font-size: 15px; flex-shrink: 0; }
        .add-flow-btn:hover { background-color: #218838; }
        #view-process-list.editing .add-flow-btn { display: block; }

        .action-icon { cursor: pointer; font-size: 14px; width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; border-radius: 4px; transition: background-color 0.1s; font-weight: normal; }
        .action-icon.add { color: #28a745; } .action-icon.add:hover { background-color: #e6f5e9; }
        .action-icon.delete { color: #dc3545; } .action-icon.delete:hover { background-color: #f8e8e9; }
        .action-icon.edit { color: #007bff; font-size: 16px; } .action-icon.edit:hover { background-color: #e0eaf3; }

        #view-process-edit, #view-structure-edit { overflow-y: auto; padding-right: 5px; }
        #flow-path-builder { display: flex; gap: 5px; align-items: center; }
        #flow-path-segments { list-style-type: none; padding: 0; margin-top: 8px; border: 1px solid #eee; border-radius: 6px; max-height: 150px; overflow-y: auto; background: #fcfcfc; }
        #flow-path-segments li { display: flex; justify-content: space-between; align-items: center; padding: 6px 10px; border-bottom: 1px solid #eee; font-size: 13px; }
        #flow-path-segments li:last-child { border-bottom: none; }
        #flow-path-segments button { background: none; border: none; color: #dc3545; cursor: pointer; font-size: 16px; }

        .sidebar-footer { display: flex; justify-content: flex-end; gap: 10px; margin-top: 15px; border-top: 1px solid #eee; padding-top: 15px; flex-shrink: 0; }
        .sidebar-footer button { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; font-weight: 500; }
        .sidebar-footer .btn-primary { background-color: #007bff; color: white; }
        .sidebar-footer .btn-secondary { background-color: #f0f0f0; color: #333; }

        #details-editor .detail-row { display: flex; gap: 10px; margin-bottom: 5px; }
        #details-editor .detail-row input { flex: 1; width: 100%; padding: 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 13px; }
        #details-editor .btn-delete-detail { background:none; border:none; color:#dc3545; cursor:pointer; }

        .tags-container { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 8px; min-height: 20px; }
        .mesh-tag { background: #e9ecef; border: 1px solid #dee2e6; padding: 2px 6px; border-radius: 4px; font-size: 12px; display: inline-flex; align-items: center; gap: 5px; }
        .mesh-tag .remove-tag { color: #dc3545; font-weight: bold; cursor: pointer; }
        .hardware-tag { background: #e0f2ff; border: 1px solid #b8daff; color: #004085; padding: 2px 6px; border-radius: 4px; font-size: 12px; display: inline-flex; align-items: center; gap: 5px; }
        .hardware-tag .remove-tag { color: #004085; font-weight: bold; cursor: pointer; opacity: 0.7; }
        .responsibility-tag { background: #e0ffef; border: 1px solid #b3e6ce; color: #0f5132; padding: 2px 6px; border-radius: 4px; font-size: 12px; display: inline-flex; align-items: center; gap: 5px; }
        .responsibility-tag .remove-tag { color: #0f5132; font-weight: bold; cursor: pointer; opacity: 0.7; }
        .remove-tag:hover { opacity: 1; }

        #btn-pick-mesh { padding: 0 12px; border: 1px solid #007bff; background: white; color: #007bff; border-radius: 4px; cursor: pointer; transition: all 0.2s; font-size: 18px; }
        #btn-pick-mesh:hover { background: #e6f2ff; }
        #btn-pick-mesh.active { background: #007bff; color: white; box-shadow: inset 0 2px 4px rgba(0,0,0,0.2); }

        #view-toggle-button { position: fixed; bottom: 20px; right: 20px; z-index: 25; padding: 10px 20px; font-size: 14px; font-weight: 500; background-color: #fff; color: #333; border: 1px solid #ddd; border-radius: 8px; cursor: pointer; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); transition: all 0.2s ease-in-out; }
        #view-toggle-button:hover { background-color: #f8f8f8; transform: translateY(-2px); box-shadow: 0 6px 20px rgba(0, 0, 0, 0.12); }

        /* EDIT-BUTTON */
        #switch-to-editor-btn { position: fixed; top: 74px; left: 20px; width: 44px; height: 44px; background-color: #333; color: white; border: 1px solid #000; border-radius: 8px; cursor: pointer; z-index: 30; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); font-size: 20px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; text-decoration: none; }
        #switch-to-editor-btn:hover { background-color: #555; transform: translateY(-2px); }

        #active-flow-indicator { position: fixed; top: 20px; left: 50%; transform: translateX(-50%); background: rgba(255, 255, 255, 0.9); backdrop-filter: blur(8px); padding: 6px 16px; border-radius: 20px; border: 1px solid #ccc; font-size: 14px; font-weight: 500; color: #333; display: none; z-index: 50; box-shadow: 0 4px 20px rgba(0,0,0,0.1); pointer-events: auto; display: flex; align-items: center; gap: 8px; }
        #active-flow-indicator span { font-weight: 700; color: #007bff; }
        #active-flow-indicator .medium-tag { font-size: 11px; color: #666; background: #eee; padding: 2px 6px; border-radius: 4px; font-weight: normal; }
        #active-flow-indicator button { background: none; border: none; color: #999; cursor: pointer; font-size: 18px; line-height: 1; padding: 0 4px; transition: color 0.2s; }
        #active-flow-indicator button:hover { color: #333; }
        #close-flow-btn:hover { color: #dc3545 !important; }

        .hidden { display: none !important; }

        /* ZURÜCK-BUTTON für den Viewer */
        .back-home-btn { 
            position: fixed; bottom: 20px; left: 20px; 
            background: rgba(255,255,255,0.8); padding: 10px; border-radius: 50%; 
            box-shadow: 0 2px 10px rgba(0,0,0,0.1); cursor: pointer; transition: all 0.3s; z-index: 60; 
            text-decoration: none; display: flex; justify-content: center; align-items: center;
        }
        .back-home-btn:hover { background: #fff; transform: scale(1.1); }

        #switch-to-editor-btn { 
            position: fixed; 
            top: 74px; /* Unter dem Burger-Menü */
            left: 20px; 
            width: 44px; 
            height: 44px; 
            background-color: #fff; /* Weiß statt Schwarz */
            color: #555; /* Dunkelgraues Icon */
            border: 1px solid #ddd; 
            border-radius: 8px; 
            cursor: pointer; 
            z-index: 30; 
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); 
            display: flex; 
            align-items: center; 
            justify-content: center; 
            transition: all 0.2s ease; 
            text-decoration: none; 
        }
        #switch-to-editor-btn:hover { 
            background-color: #f8f8f8; 
            transform: translateY(-2px); 
            color: #007bff; /* Blau beim Hover */
        }
    </style>
</head>
<body>
    <div id="webgl-canvas-container"></div>
    <div id="css-label-container"></div>

    <div id="tree-tooltip"></div>

    <div id="active-flow-indicator" style="display: none;"></div>

    <button id="view-toggle-button">Architektur-Ansicht</button>
    <button id="toggle-treeview-btn" title="Panel ein/ausblenden">☰</button>

    <!-- LINK ZURÜCK ZUR VIEW (Mit Initialen) -->
    <a href="{{ url_for('dashboard_page') }}" class="back-home-btn" title="Zurück zum Dashboard">
        <span style="font-weight: 700; font-size: 16px; color: #0055A4;">{{ current_user_initials }}</span>
    </a>

    <!-- LINK ZUM EDITOR -->
    {% if session.get('is_admin') or 'PLANNER' in session.get('roles', []) %}
    <a href="{{ url_for('serve_facility_editor') }}" id="switch-to-editor-btn" title="Zum 3D-Layout-Editor wechseln">
        <span class="material-icons">build</span>
    </a>
    {% endif %}

    <div id="treeview-container">
        <div class="treeview-header" id="panel-header">

            <div id="header-back-action" style="display: none; margin-right: 10px;">
                <span class="header-btn-round" id="header-back-btn" title="Zurück">←</span>
            </div>

            <div class="header-title-group">
                <h3 id="sidebar-title">Betriebseinheiten</h3>
            </div>

            <div id="save-indicator">Gespeichert</div>

            <div class="header-actions">
                <!-- STRUCTURE ACTIONS -->
                <div id="structure-actions" style="display: flex; gap: 6px; align-items: center;">

                    <!-- EXPORT BUTTON (Hidden by default) -->
                    <span id="btn-export-audit" class="header-btn-round" title="Audit-Bericht erstellen" style="display: none;">🖨️</span>

                    {% if session.get('is_admin') or 'PLANNER' in session.get('roles', []) %}
                    <span id="toggle-structure-edit-mode-btn" class="header-btn-round" title="Struktur bearbeiten / Sortieren">✎</span>
                    {% endif %}

                    <span id="toggle-search-btn" class="header-btn-round" title="Suche">🔍</span>

                    <span class="action-icon-text" id="add-root-btn" title="Überpunkt hinzufügen" style="display:none;">+</span>
                    <span class="action-icon-text" id="expand-all-btn" title="Alles ausklappen">⊞</span>
                    <span class="action-icon-text" id="collapse-all-btn" title="Alles einklappen">⊟</span>
                </div>

                <!-- PROCESS ACTIONS -->
                <div id="process-actions" style="display: none; gap: 6px; align-items: center;">
                    {% if session.get('is_admin') or 'PLANNER' in session.get('roles', []) %}
                    <span id="toggle-flow-edit-mode-btn" class="header-btn-round" title="Bearbeitungsmodus">✎</span>
                    {% endif %}
                </div>

                <!-- MODE SWITCH (Right Group) -->
                <div id="app-mode-actions" style="border-left: 1px solid #ddd; padding-left: 8px; margin-left: 4px; display: flex; gap: 6px; align-items: center;">
                    <span id="tree-view-switch-btn" class="header-btn-round" title="Gebäude/Einheiten wechseln">⇆</span>
                    <span id="app-mode-switch-btn" class="header-btn-round" title="Zu Prozessflüssen wechseln">➔</span>
                </div>
            </div>
        </div>

        <!-- VIEW 1: STRUKTUR -->
        <div id="view-structure" class="sidebar-view">
            <div id="search-container">
                <input type="text" id="search-input" placeholder="Anlage durchsuchen...">
                <div class="search-filters">
                    <span class="filter-btn active" data-filter="all">Alle</span>
                    <span class="filter-btn" data-filter="critical">Kritisch</span>
                    <span class="filter-btn" data-filter="active">Aktiv</span>
                </div>
            </div>
            <div id="tree" class="treeview"></div>
        </div>

        <!-- VIEW 1.5: STRUKTUR EDITOR -->
        <div id="view-structure-edit" class="sidebar-view hidden">
            <div class="sidebar-form-group">
                <label for="struct-edit-name">Name</label>
                <input type="text" id="struct-edit-name">
            </div>
            <div class="sidebar-form-group">
                <label for="struct-edit-label-name">Name in Statuskachel (optional)</label>
                <input type="text" id="struct-edit-label-name" placeholder="Leer = Hauptname">
            </div>
            <div class="sidebar-form-group">
                <label style="display: flex; align-items: center; gap: 8px;">
                    <input type="checkbox" id="struct-edit-show-label" style="width: auto;"> 
                    Statuskachel anzeigen
                </label>
            </div>
            <hr style="border: 0; border-top: 1px solid #eee; margin: 15px 0;">

            <div class="sidebar-form-group">
                <label>Verbundene 3D-Objekte (Meshes)</label>
                <div id="mesh-tags-container" class="tags-container"></div>
                <div style="display:flex; gap:5px; margin-top:5px;">
                    <select id="mesh-dropdown" style="flex-grow:1"></select>
                    <button id="btn-pick-mesh" title="Objekt aus 3D-Szene wählen">🎯</button>
                </div>
            </div>

            <hr style="border: 0; border-top: 1px solid #eee; margin: 15px 0;">

            <!-- HARDWARE ZUWEISUNG -->
            <div class="sidebar-form-group">
                <label>Hardware-Komponenten</label>
                <div id="hardware-tags-container" class="tags-container"></div>
                <div style="display:flex; gap:5px; margin-top:5px;">
                    <input list="hardware-datalist" id="hardware-input" placeholder="Hardware suchen..." style="flex-grow:1; padding:8px; border:1px solid #ccc; border-radius:6px; font-size:14px;">
                    <datalist id="hardware-datalist"></datalist>
                    <!-- Kleiner Plus-Button, da 'Change' bei Datalists manchmal tricky ist -->
                    <button id="btn-add-hardware" style="padding:0 10px; cursor:pointer;">+</button>
                </div>
            </div>
            <!-- NEU: LAGERORT VERKNÜPFUNG -->
            <div class="sidebar-form-group">
                <label>Verknüpftes Lager / Rüstwagen</label>
                <div style="display:flex; gap:5px;">
                    <select id="storage-link-dropdown" style="flex-grow:1">
                        <option value="">-- Kein Lager verknüpft --</option>
                    </select>
                </div>
                <p style="font-size:10px; color:#888; margin-top:2px;">Zeigt den Inhalt dieses Lagers in der 3D-Ansicht an.</p>
            </div>

            <hr style="border: 0; border-top: 1px solid #eee; margin: 15px 0;">
            
            <!-- VERANTWORTLICHKEITSBEREICH -->
            <div class="sidebar-form-group">
                <label>Verantwortlichkeitsbereiche</label>
                <div id="responsibility-tags-container" class="tags-container"></div>
                <div style="display:flex; gap:5px; margin-top:5px;">
                    <select id="responsibility-dropdown" style="flex-grow:1">
                        <option value="">-- Bereich wählen --</option>
                    </select>
                </div>
            </div>

            <hr style="border: 0; border-top: 1px solid #eee; margin: 15px 0;">

            <div class="sidebar-form-group">
                <label>Technische Details</label>
                <div id="details-editor"></div>
                <button id="add-detail-btn" style="margin-top:5px; font-size:12px;">+ Detail hinzufügen</button>
            </div>
            <div class="sidebar-footer">
                <button id="btn-cancel-struct" class="btn-secondary">Zurück</button>
                <button id="btn-save-struct" class="btn-primary">Speichern</button>
            </div>
        </div>

        <!-- VIEW 2: PROZESS LISTE -->
        <div id="view-process-list" class="sidebar-view hidden">
            <div class="flow-filter-container">
                <input type="text" id="flow-filter-text" placeholder="Name suchen...">
                <select id="flow-filter-unit"><option value="">Alle Einheiten</option></select>
                <select id="flow-filter-medium"><option value="">Alle Medien</option></select>
            </div>
            <div class="flow-editor-list-container">
                <ul id="flow-editor-list" class="flow-editor-list"></ul>
            </div>
            <button class="add-flow-btn" id="btn-create-new-flow">+ Neuen Prozessfluss erstellen</button>
        </div>

        <!-- VIEW 3: PROZESS EDITOR -->
        <div id="view-process-edit" class="sidebar-view hidden">
            <div class="sidebar-form-group">
                <label for="flow-detail-name">Name des Flusses</label>
                <input type="text" id="flow-detail-name">
            </div>
            <div class="sidebar-form-group">
                <label for="flow-detail-unit">Zugeordnete Betriebseinheit</label>
                <select id="flow-detail-unit"></select>
            </div>
            <div class="sidebar-form-group">
                <label for="flow-detail-medium">Medium</label>
                <input type="text" id="flow-detail-medium" placeholder="z.B. Wasser">
            </div>
            <div class="sidebar-form-group">
                <label for="flow-detail-color">Farbe</label>
                <input type="color" id="flow-detail-color" value="#ff851b">
            </div>
            <div class="sidebar-form-group">
                <label>Pfadsegmente</label>
                <div id="flow-path-builder">
                    <select id="flow-from-select"></select>
                    <span>→</span>
                    <select id="flow-to-select"></select>
                    <button id="add-flow-segment-btn">+</button>
                </div>
                <ul id="flow-path-segments"></ul>
            </div>
            <div class="sidebar-footer">
                <button id="btn-cancel-edit-flow" class="btn-secondary">Zurück</button>
                <button id="btn-save-flow" class="btn-primary">Speichern</button>
            </div>
        </div>
    </div>

    <script type="importmap">{ "imports": { "three": "https://unpkg.com/three@0.158.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.158.0/examples/jsm/" } }</script>
    <script>const initialData = {{ structure_data|tojson|safe }};</script>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
        import { CSS2DRenderer, CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';

        let scene, camera, renderer, labelRenderer, controls, raycaster, mouse;
        let allLabels = [], allMaterials = [], meshMap = new Map();
        let isStatusView = true;
        let highlightedObjects = [];
        let targetCameraPosition = null, targetControlsTarget = null;
        let flowLines = [];
        let activeFlowTextures = []; 
        let isPickingMode = false;

        const NEUTRAL_COLOR = new THREE.Color(0xcccccc);
        const NEUTRAL_OPACITY = 0.5;
        const SEARCH_HIGHLIGHT_COLOR = new THREE.Color(0x00ffff);
        const highlightMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00, emissive: 0xccaa00, transparent: true, opacity: 0.4, depthWrite: false, side: 2 }); 
        const searchMaterial = new THREE.MeshStandardMaterial({ color: SEARCH_HIGHLIGHT_COLOR, emissive: 0x004444, transparent: false, opacity: 0.9 });

        const STATUS_COLORS = { 'frei': new THREE.Color(0x2ecc40), 'heiss': new THREE.Color(0xff4136), 'in_Betrieb': new THREE.Color(0x0074d9), 'Wartung_geplant': new THREE.Color(0xff851b), 'Standby': new THREE.Color(0xdddddd), 'aktiv': new THREE.Color(0x39cccc), 'N/A': new THREE.Color(0xeeeeee), 'default': new THREE.Color(0x888888) };

        const HARDWARE_TYPES = [
            "Sensor: Temperatur (PT100)", "Sensor: Durchfluss", "Sensor: Druck",
            "Aktor: Ventil", "Aktor: Pumpe",
            "Steuerung: SPS Modul", "Steuerung: IoT Gateway",
            "Kamera: Überwachung", "Sicherheit: Not-Aus", "Netzwerk: Switch"
        ];

        const RESPONSIBILITY_AREAS = [
            "Facility Management", "MBET"
        ];

        const allStructureData = initialData;

        // --- APP STATE VARIABLES ---
        let currentView = 'betriebseinheiten'; 
        let appMode = 'structure'; 
        let isStructureEditMode = false;
        let currentlyEditingFlowId = null;

        const viewMaps = {
            betriebseinheiten: { meshToPathMap: new Map(), meshDetailsMap: new Map() },
            gebaeude: { meshToPathMap: new Map(), meshDetailsMap: new Map() }
        };

        let currentlyExpandedLabel = null;
        let currentlyEditingPath = null;
        let availableMeshNames = [];
        let flowTexture;
        let activeSearchTerm = "";
        let draggedSourcePath = null;

        function getCurrentStructureData() { return allStructureData[currentView]; }
        function getCurrentViewMaps() { return viewMaps[currentView]; }
        const debounce = (func, delay) => { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => func(...args), delay); }; };

        function findNodeById(nodes, id) { for (const node of nodes) { if (node.id === id) return node; if (node.children) { const found = findNodeById(node.children, id); if (found) return found; } } return null; }

        function createFlowTexture() { 
            const size = 64;
            const canvas = document.createElement('canvas'); 
            canvas.width = size; canvas.height = size; 
            const ctx = canvas.getContext('2d'); 
            ctx.fillStyle = 'rgba(255, 255, 255, 0.2)'; ctx.fillRect(0, 0, size, size);
            ctx.fillStyle = 'rgba(255, 255, 255, 1.0)';
            ctx.beginPath();
            ctx.moveTo(0, 0); ctx.lineTo(32, 32); ctx.lineTo(0, 64);
            ctx.lineTo(16, 64); ctx.lineTo(48, 32); ctx.lineTo(16, 0);
            ctx.fill();
            const texture = new THREE.CanvasTexture(canvas); 
            texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; 
            return texture; 
        }
        async function loadStorageOptions() {
            try {
                const response = await fetch('/api/facility/storage_options');
                const options = await response.json();
                
                const select = document.getElementById('storage-link-dropdown');
                // Ersten Eintrag (Empty) behalten
                select.innerHTML = '<option value="">-- Kein Lager verknüpft --</option>';
                
                options.forEach(opt => {
                    const el = document.createElement('option');
                    el.value = opt.name;
                    el.textContent = `${opt.type}: ${opt.name}`;
                    select.appendChild(el);
                });
            } catch (e) {
                console.error("Fehler beim Laden der Storage Options:", e);
            }
        }
        async function saveStructure() { const indicator = document.getElementById('save-indicator'); indicator.style.opacity = '1'; indicator.textContent = 'Speichern...'; try { const response = await fetch('/api/facility/save_structure', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(allStructureData) }); if (!response.ok) throw new Error('Speichern fehlgeschlagen'); indicator.textContent = 'Gespeichert'; setTimeout(() => { indicator.style.opacity = '0'; }, 2000); } catch (error) { console.error('Fehler:', error); indicator.textContent = 'Fehler!'; indicator.style.color = 'red'; } }
        const debouncedSave = debounce(saveStructure, 1500);
        async function loadHardwareAssets() {
            try {
                const response = await fetch('/api/test_equipment/data');
                const assets = await response.json();
                
                const datalist = document.getElementById('hardware-datalist');
                datalist.innerHTML = '';
                
                assets.forEach(asset => {
                    const opt = document.createElement('option');
                    // Zeige den lesbaren Namen im Dropdown an
                    opt.value = asset.label; 
                    // Speichere die ID in einem data-Attribut für späteren Zugriff
                    opt.dataset.id = asset.id; 
                    datalist.appendChild(opt);
                });
            } catch (e) {
                console.error("Fehler beim Laden der Hardware:", e);
            }
        }
        function init() {
            scene = new THREE.Scene(); scene.background = new THREE.Color(0xf4f6f8);
            camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(50, 40, 50); camera.lookAt(0,0,0);
            const canvasContainer = document.getElementById('webgl-canvas-container');
            renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
            renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio);
            canvasContainer.appendChild(renderer.domElement);

            labelRenderer = new CSS2DRenderer();
            labelRenderer.setSize(window.innerWidth, window.innerHeight);
            document.getElementById('css-label-container').appendChild(labelRenderer.domElement);

            scene.add(new THREE.AmbientLight(0xffffff, 0.8));
            const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
            dirLight.position.set(20, 30, 15); scene.add(dirLight);
            controls = new OrbitControls(camera, renderer.domElement);
            controls.enableDamping = true;

            raycaster = new THREE.Raycaster();
            mouse = new THREE.Vector2();

            buildMeshDetailsMap(allStructureData.betriebseinheiten, 'betriebseinheiten');
            buildMeshDetailsMap(allStructureData.gebaeude, 'gebaeude');
            flowTexture = createFlowTexture();

            // Populate Dropdowns
            loadHardwareAssets();
            loadStorageOptions()
            const respSelect = document.getElementById('responsibility-dropdown');
            RESPONSIBILITY_AREAS.forEach(area => { const opt = document.createElement('option'); opt.value = area; opt.textContent = area; respSelect.appendChild(opt); });

            loadModel(); animate(); redrawTree(); setupTreeEventListeners();
            setupSearchListeners();

            // --- EVENT LISTENERS ---
            document.getElementById('app-mode-switch-btn').addEventListener('click', toggleAppMode);
            document.getElementById('tree-view-switch-btn').addEventListener('click', switchTreeView);
            document.getElementById('toggle-treeview-btn').addEventListener('click', () => { document.getElementById('treeview-container').classList.toggle('visible'); });
            document.getElementById('view-toggle-button').addEventListener('click', toggleViewMode);
            document.getElementById('add-root-btn').addEventListener('click', addTopLevelNode);
            document.getElementById('expand-all-btn').addEventListener('click', expandAll);
            document.getElementById('collapse-all-btn').addEventListener('click', collapseAll);
            document.getElementById('toggle-search-btn').addEventListener('click', () => { document.getElementById('search-container').classList.toggle('visible'); });
            document.getElementById('header-back-btn').addEventListener('click', goBackFromEdit);

            // Edit Mode Button nur wenn vorhanden (Admins)
            const toggleStructEditBtn = document.getElementById('toggle-structure-edit-mode-btn');
            if(toggleStructEditBtn) toggleStructEditBtn.addEventListener('click', toggleStructureEditMode);

            // Export Listener
            document.getElementById('btn-export-audit').addEventListener('click', () => { window.open('/facility/audit_report', '_blank'); });

            // Process Logic
            const toggleFlowEditBtn = document.getElementById('toggle-flow-edit-mode-btn');
            if(toggleFlowEditBtn) toggleFlowEditBtn.addEventListener('click', toggleFlowListEditMode);

            document.getElementById('btn-create-new-flow').addEventListener('click', () => switchSidebarView('process-edit', null));
            document.getElementById('btn-cancel-edit-flow').addEventListener('click', () => switchSidebarView('process-list'));
            document.getElementById('btn-save-flow').addEventListener('click', saveFlowDetails);

            // Structure Edit Logic
            document.getElementById('btn-save-struct').addEventListener('click', saveStructureChanges);
            document.getElementById('btn-cancel-struct').addEventListener('click', () => switchSidebarView('structure'));
            document.getElementById('add-detail-btn').addEventListener('click', () => createDetailRow('', ''));
            document.getElementById('details-editor').addEventListener('click', (e) => { if (e.target.classList.contains('btn-delete-detail')) e.target.parentElement.remove(); });
            document.getElementById('mesh-dropdown').addEventListener('change', handleAddMeshTag);
            document.getElementById('mesh-tags-container').addEventListener('click', (e) => { if(e.target.classList.contains('remove-tag')) handleRemoveMeshTag(e.target.parentElement); });

            // Hardware Logic
            const hwInput = document.getElementById('hardware-input');
            const hwAddBtn = document.getElementById('btn-add-hardware');
            
            // Hinzufügen per Button
            hwAddBtn.addEventListener('click', () => {
                const val = hwInput.value.trim();
                if(val) {
                    handleAddHardwareTag({ target: { value: val } }); // Simuliert das Event-Objekt für die bestehende Funktion
                    hwInput.value = '';
                }
            });
            
            // Hinzufügen per Enter-Taste
            hwInput.addEventListener('keydown', (e) => {
                if(e.key === 'Enter') {
                    e.preventDefault();
                    hwAddBtn.click();
                }
            });

            // Remove-Listener bleibt gleich
            document.getElementById('hardware-tags-container').addEventListener('click', (e) => { if(e.target.classList.contains('remove-tag')) handleRemoveHardwareTag(e.target.parentElement); });
            
            // Responsibility Logic
            document.getElementById('responsibility-dropdown').addEventListener('change', handleAddResponsibilityTag);
            document.getElementById('responsibility-tags-container').addEventListener('click', (e) => { if(e.target.classList.contains('remove-tag')) handleRemoveResponsibilityTag(e.target.parentElement); });

            // Picker Logic
            document.getElementById('btn-pick-mesh').addEventListener('click', togglePickingMode);
            renderer.domElement.addEventListener('click', on3DClick);

            // Process Filters
            document.getElementById('flow-filter-text').addEventListener('input', () => populateFlowEditorList());
            document.getElementById('flow-filter-unit').addEventListener('change', () => populateFlowEditorList());
            document.getElementById('flow-filter-medium').addEventListener('change', () => populateFlowEditorList());

            // Path Builder
            document.getElementById('add-flow-segment-btn').addEventListener('click', addSegmentFromSelects);
            document.getElementById('flow-path-segments').addEventListener('click', (e) => { if(e.target.tagName === 'BUTTON') e.target.parentElement.remove(); });

            window.addEventListener('resize', onWindowResize);
        }

        function animate() { 
            requestAnimationFrame(animate); 
            activeFlowTextures.forEach(tex => { tex.offset.x -= 0.01; });
            if (targetCameraPosition && targetControlsTarget) { camera.position.lerp(targetCameraPosition, 0.08); controls.target.lerp(targetControlsTarget, 0.08); if (camera.position.distanceTo(targetCameraPosition) < 0.1) { camera.position.copy(targetCameraPosition); controls.target.copy(targetControlsTarget); targetCameraPosition = null; targetControlsTarget = null; } } controls.update(); renderer.render(scene, camera); labelRenderer.render(scene, camera); 
        }
        function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); labelRenderer.setSize(window.innerWidth, window.innerHeight); }

        function togglePickingMode() {
            isPickingMode = !isPickingMode;
            const btn = document.getElementById('btn-pick-mesh');
            if (isPickingMode) { btn.classList.add('active'); document.body.classList.add('picking-mode'); } else { btn.classList.remove('active'); document.body.classList.remove('picking-mode'); }
        }

        function on3DClick(event) {
            if (!isPickingMode) return;
            mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
            mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
            raycaster.setFromCamera(mouse, camera);
            const intersects = raycaster.intersectObjects(scene.children, true);
            if (intersects.length > 0) {
                const hit = intersects.find(i => i.object.isMesh);
                if (hit) { handleAddMeshTag({target: {value: hit.object.name}}); togglePickingMode(); }
            }
        }

        function toggleAppMode() {
            const switchBtn = document.getElementById('app-mode-switch-btn');
            if (appMode === 'structure') {
                appMode = 'processes';
                switchBtn.classList.add('active'); 
                switchSidebarView('process-list');
                document.getElementById('view-process-list').classList.remove('editing');
                const flowBtn = document.getElementById('toggle-flow-edit-mode-btn');
                if(flowBtn) flowBtn.classList.remove('active');
                populateFlowFilterOptions();
                populateFlowEditorList();
            } else {
                appMode = 'structure';
                switchBtn.classList.remove('active');
                switchSidebarView('structure');
                if(isStructureEditMode) toggleStructureEditMode();
                clearFlowView();
            }
        }

        function toggleStructureEditMode() {
            isStructureEditMode = !isStructureEditMode;
            const btn = document.getElementById('toggle-structure-edit-mode-btn');
            const printerBtn = document.getElementById('btn-export-audit');
            const treeContainer = document.getElementById('tree');
            const addRootBtn = document.getElementById('add-root-btn');
            const root = document.documentElement;

            if (isStructureEditMode) { 
                btn.classList.add('active'); 
                treeContainer.classList.add('editing'); 
                addRootBtn.style.display = 'inline-block';
                printerBtn.style.display = 'inline-flex'; // Drucker zeigen
                root.style.setProperty('--panel-width', '600px'); // Panel breiter
            } 
            else { 
                btn.classList.remove('active'); 
                treeContainer.classList.remove('editing'); 
                addRootBtn.style.display = 'none';
                printerBtn.style.display = 'none'; // Drucker weg
                root.style.setProperty('--panel-width', '500px'); // Panel normal
            }
            redrawTree(); 
        }

        function goBackFromEdit() { if (appMode === 'processes') switchSidebarView('process-list'); else switchSidebarView('structure'); }

function switchSidebarView(viewName, dataId = null) {
            const viewStructure = document.getElementById('view-structure');
            const viewStructureEdit = document.getElementById('view-structure-edit');
            const viewProcessList = document.getElementById('view-process-list');
            const viewProcessEdit = document.getElementById('view-process-edit');
            const header = document.getElementById('panel-header');
            const structActions = document.getElementById('structure-actions');
            const procActions = document.getElementById('process-actions');
            const appModeActions = document.getElementById('app-mode-actions');
            const backAction = document.getElementById('header-back-action');

            // Referenz auf den Button, den wir ausblenden wollen
            const treeViewSwitchBtn = document.getElementById('tree-view-switch-btn');

            viewStructure.classList.add('hidden');
            viewStructureEdit.classList.add('hidden');
            viewProcessList.classList.add('hidden');
            viewProcessEdit.classList.add('hidden');

            header.classList.remove('with-border');
            structActions.style.display = 'none';
            procActions.style.display = 'none';
            appModeActions.style.display = 'flex'; 
            backAction.style.display = 'none'; 

            // Standardmäßig sichtbar machen (für Struktur-Ansicht)
            treeViewSwitchBtn.style.display = 'inline-flex';

            if (viewName === 'structure') {
                viewStructure.classList.remove('hidden'); structActions.style.display = 'flex';
                document.getElementById('sidebar-title').textContent = (currentView === 'betriebseinheiten') ? 'Betriebseinheiten' : 'Gebäude';

            } else if (viewName === 'structure-edit') {
                viewStructureEdit.classList.remove('hidden'); header.classList.add('with-border');
                appModeActions.style.display = 'none'; backAction.style.display = 'block'; 
                populateStructureEditor(dataId);

            } else if (viewName === 'process-list') {
                viewProcessList.classList.remove('hidden'); header.classList.add('with-border');
                procActions.style.display = 'flex';
                document.getElementById('sidebar-title').textContent = 'Prozessflüsse';

                // HIER: Gebäude-Switch-Button ausblenden
                treeViewSwitchBtn.style.display = 'none';

                populateFlowEditorList();

            } else if (viewName === 'process-edit') {
                viewProcessEdit.classList.remove('hidden'); header.classList.add('with-border');
                appModeActions.style.display = 'none'; backAction.style.display = 'block'; 
                currentlyEditingFlowId = dataId;
                document.getElementById('sidebar-title').textContent = dataId ? 'Prozess bearbeiten' : 'Neuer Prozess';
                populateFlowDetailForm(dataId);
            }
        }

        function openStructureEditor(path) { currentlyEditingPath = path; switchSidebarView('structure-edit', path); }

        function populateStructureEditor(path) {
            const node = getNodeByPath(path);
            if (!node) return;

            const pathParts = path.split('.');
            let displayIndex = "";
            pathParts.forEach(part => { if (!isNaN(part)) { displayIndex += (parseInt(part) + 1) + "."; } });
            if (displayIndex.length > 0) displayIndex = displayIndex.slice(0, -1);

            document.getElementById('sidebar-title').textContent = `${displayIndex} Eintrag bearbeiten`;
            document.getElementById('struct-edit-name').value = node.name || '';
            document.getElementById('struct-edit-label-name').value = node.labelName || '';
            document.getElementById('struct-edit-show-label').checked = node.showStatusLabel !== false;
            document.getElementById('storage-link-dropdown').value = node.storageLink || '';
            const detailsEditor = document.getElementById('details-editor'); detailsEditor.innerHTML = '';
            if (node.details) { for (const key in node.details) { createDetailRow(key, node.details[key]); } }

            populateMeshEditor(node.meshNames || []);
            populateHardwareEditor(node.hardware || []);
            populateResponsibilityEditor(node.responsibility || []);
        }

        function saveStructureChanges() {
            if (!currentlyEditingPath) return;
            const node = getNodeByPath(currentlyEditingPath);
            if (!node) return;

            node.name = document.getElementById('struct-edit-name').value;
            const labelName = document.getElementById('struct-edit-label-name').value.trim();
            if (labelName) node.labelName = labelName; else delete node.labelName;
            node.showStatusLabel = document.getElementById('struct-edit-show-label').checked;

            const newDetails = {};
            document.querySelectorAll('#details-editor .detail-row').forEach(row => {
                const key = row.querySelector('.detail-key').value.trim();
                const value = row.querySelector('.detail-value').value.trim();
                if (key) newDetails[key] = value;
            });
            node.details = newDetails;
            node.meshNames = getCurrentModalMeshNames();
            node.hardware = getCurrentHardwareNames();
            node.responsibility = getCurrentResponsibilityNames();
            node.storageLink = document.getElementById('storage-link-dropdown').value;
            
            buildMeshDetailsMap(getCurrentStructureData(), currentView);
            redrawTree();
            updateAllLabels();
            debouncedSave();
            switchSidebarView('structure');
        }

        function setupSearchListeners() {
            const searchInput = document.getElementById('search-input');
            searchInput.addEventListener('input', (e) => { activeSearchTerm = e.target.value.toLowerCase(); filterTreeAndScene(activeSearchTerm); });
            document.querySelectorAll('.filter-btn').forEach(btn => {
                btn.addEventListener('click', (e) => {
                    document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active')); e.target.classList.add('active');
                    const filter = e.target.dataset.filter;
                    if(filter === 'all') { searchInput.value = ""; activeSearchTerm = ""; }
                    else if(filter === 'critical') { searchInput.value = "heiss"; activeSearchTerm = "heiss"; }
                    else if(filter === 'active') { searchInput.value = "betrieb"; activeSearchTerm = "betrieb"; }
                    filterTreeAndScene(activeSearchTerm);
                });
            });
        }

        function filterTreeAndScene(term) {
            const treeItems = document.querySelectorAll('#tree .tree-item');
            const matchingMeshNames = new Set();
            treeItems.forEach(item => {
                const label = item.querySelector('.tree-label').textContent.toLowerCase();
                const path = item.dataset.path;
                const node = getNodeByPath(path);

                let detailsMatch = false;
                if(node.details) { for(let k in node.details) if(String(node.details[k]).toLowerCase().includes(term)) detailsMatch = true; }

                let hardwareMatch = false;
                if(node.hardware) { node.hardware.forEach(h => { if(h.toLowerCase().includes(term)) hardwareMatch = true; }); }

                let respMatch = false;
                if(node.responsibility) { node.responsibility.forEach(r => { if(r.toLowerCase().includes(term)) respMatch = true; }); }

                if (term === "" || label.includes(term) || detailsMatch || hardwareMatch || respMatch) {
                    item.style.display = "";
                    let parent = item.parentElement.closest('.tree-item');
                    while(parent) { parent.style.display = ""; parent.classList.add('open'); parent = parent.parentElement.closest('.tree-item'); }
                    if(term !== "") item.querySelector('.tree-row').classList.add('search-match'); else item.querySelector('.tree-row').classList.remove('search-match');
                    if(node.meshNames) node.meshNames.forEach(m => matchingMeshNames.add(m));
                } else { item.style.display = "none"; item.querySelector('.tree-row').classList.remove('search-match'); }
            });
            if(term === "") { meshMap.forEach(mesh => { if(mesh.userData.originalMaterial) mesh.material = mesh.userData.originalMaterial; mesh.visible = true; }); } 
            else { meshMap.forEach((mesh, name) => { if(matchingMeshNames.has(name)) { mesh.visible = true; mesh.material = searchMaterial; } else { mesh.material = new THREE.MeshBasicMaterial({ color: 0xcccccc, transparent: true, opacity: 0.1, depthWrite: false }); } }); }
        }

        function switchTreeView() {
            currentView = (currentView === 'betriebseinheiten') ? 'gebaeude' : 'betriebseinheiten';
            const switchBtn = document.getElementById('tree-view-switch-btn');
            const title = document.getElementById('sidebar-title');
            if (currentView === 'betriebseinheiten') { title.textContent = 'Betriebseinheiten'; switchBtn.title = 'Zu Gebäuden wechseln'; } else { title.textContent = 'Gebäude'; switchBtn.title = 'Zu Betriebseinheiten wechseln'; }
            highlightObjects([]); redrawTree(); updateAllLabels();
            document.getElementById('search-input').value = "";
            filterTreeAndScene("");
        }

        function clearFlowView() {
            flowLines.forEach(line => { scene.remove(line); line.geometry.dispose(); line.material.map?.dispose(); line.material.dispose(); });
            flowLines = [];
            activeFlowTextures.forEach(t => t.dispose()); activeFlowTextures = [];
            meshMap.forEach(mesh => { if (mesh.userData.originalMaterial) { mesh.material = mesh.userData.originalMaterial; } });
            allMaterials.forEach(item => { item.material.opacity = 0.8; item.material.transparent = true; });
            highlightObjects([]);
            if(isStatusView) { allLabels.forEach(label => { label.visible = true; label.element.style.opacity = "1"; label.element.style.pointerEvents = "auto"; }); }
            document.getElementById('active-flow-indicator').style.display = 'none';
        }

        function showFlow(flowId) {
            clearFlowView();
            const flow = allStructureData.fluesse.find(f => f.id === flowId);
            if (!flow) return;
            const indicator = document.getElementById('active-flow-indicator');
            let indicatorHtml = `Aktiver Prozess: <span>${flow.name}</span>`;
            if(flow.medium) indicatorHtml += `<span class="medium-tag">${flow.medium}</span>`;
            indicatorHtml += `<button id="reopen-flow-editor-btn" title="Zurück zur Liste" style="margin-left:15px; font-size:16px;">☰</button>`;
            indicatorHtml += `<button id="close-flow-btn" title="Ansicht schließen">×</button>`;
            indicator.innerHTML = indicatorHtml;
            indicator.style.display = 'flex';
            document.getElementById('close-flow-btn').onclick = clearFlowView;
            document.getElementById('reopen-flow-editor-btn').onclick = () => { if(appMode !== 'processes') toggleAppMode(); document.getElementById('treeview-container').classList.add('visible'); };
            let allNodeIdsInFlow = new Set();
            flow.path.forEach(segment => { allNodeIdsInFlow.add(segment.from); allNodeIdsInFlow.add(segment.to); });
            const meshesInFlow = [];
            allNodeIdsInFlow.forEach(nodeId => { const node = findNodeById(allStructureData.betriebseinheiten, nodeId); if (node && node.meshNames) { meshesInFlow.push(...node.meshNames); } });
            const flowSet = new Set(meshesInFlow);
            allLabels.forEach(label => {
                if (flowSet.has(label.userData.meshName)) { label.visible = true; label.element.style.opacity = "1"; label.element.style.pointerEvents = "auto"; } 
                else { label.visible = false; label.element.style.opacity = "0"; label.element.style.pointerEvents = "none"; }
            });
            const combinedBox = new THREE.Box3();
            let hasObjects = false;
            flowSet.forEach(meshName => { const obj = meshMap.get(meshName); if (obj) { combinedBox.expandByObject(obj); hasObjects = true; } });
            if (hasObjects) {
                const center = combinedBox.getCenter(new THREE.Vector3()); const size = combinedBox.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y, size.z); const cameraDistance = maxDim * 1.5;
                targetControlsTarget = center; targetCameraPosition = new THREE.Vector3(center.x + cameraDistance * 0.6, center.y + cameraDistance * 0.8, center.z + cameraDistance * 0.8);
            }
            meshMap.forEach((mesh, name) => { if (flowSet.has(name)) { if (mesh.userData.originalMaterial) mesh.material = mesh.userData.originalMaterial; } else { mesh.material = new THREE.MeshStandardMaterial({ color: 0xcccccc, opacity: 0.05, transparent: true, depthWrite: false }); } });
            const adjacency = {}; flow.path.forEach(seg => { adjacency[seg.from] = seg.to; });
            const visited = new Set(); const chains = [];
            flow.path.forEach(seg => { if(!visited.has(seg.from)) { let currentChain = [seg.from]; let curr = seg.from; visited.add(curr); while(adjacency[curr]) { let next = adjacency[curr]; currentChain.push(next); visited.add(next); curr = next; } if(currentChain.length > 1) chains.push(currentChain); } });
            chains.forEach(chain => {
                const points = []; let validChain = true;
                chain.forEach(nodeId => { const node = findNodeById(allStructureData.betriebseinheiten, nodeId); const meshName = node?.meshNames?.[0]; const obj = meshName ? meshMap.get(meshName) : null; if(obj) { const center = new THREE.Box3().setFromObject(obj).getCenter(new THREE.Vector3()); center.y = 0.5; points.push(center); } else { validChain = false; } });
                if(validChain && points.length > 1) {
                    const curve = new THREE.CatmullRomCurve3(points); curve.curveType = 'catmullrom'; curve.tension = 0.1; const length = curve.getLength();
                    const instanceTexture = flowTexture.clone(); instanceTexture.repeat.set(length / 8, 1); instanceTexture.needsUpdate = true; activeFlowTextures.push(instanceTexture);
                    const tubeMaterial = new THREE.MeshBasicMaterial({ color: new THREE.Color(flow.color), map: instanceTexture, transparent: true, opacity: 0.8, side: THREE.DoubleSide });
                    const tubeGeo = new THREE.TubeGeometry(curve, Math.floor(length * 2), 0.6, 8, false); const tube = new THREE.Mesh(tubeGeo, tubeMaterial);
                    flowLines.push(tube); scene.add(tube);
                }
            });
        }

        function toggleFlowListEditMode() {
            const container = document.getElementById('view-process-list'); const btn = document.getElementById('toggle-flow-edit-mode-btn');
            if(container.classList.contains('editing')) { container.classList.remove('editing'); btn.classList.remove('active'); } 
            else { container.classList.add('editing'); btn.classList.add('active'); }
        }

        function populateFlowFilterOptions() {
            const unitSelect = document.getElementById('flow-filter-unit'); const selectedUnit = unitSelect.value;
            unitSelect.innerHTML = '<option value="">Alle Einheiten</option>';
            const allUnits = flattenTreeForSelect(allStructureData.betriebseinheiten);
            allUnits.forEach(u => { const opt = document.createElement('option'); opt.value = u.id; opt.textContent = u.name; unitSelect.appendChild(opt); });
            unitSelect.value = selectedUnit;
            const mediumSelect = document.getElementById('flow-filter-medium'); const selectedMedium = mediumSelect.value;
            mediumSelect.innerHTML = '<option value="">Alle Medien</option>';
            const mediaSet = new Set();
            (allStructureData.fluesse || []).forEach(f => { if(f.medium && f.medium.trim() !== '') mediaSet.add(f.medium.trim()); });
            Array.from(mediaSet).sort().forEach(m => { const opt = document.createElement('option'); opt.value = m; opt.textContent = m; mediumSelect.appendChild(opt); });
            mediumSelect.value = selectedMedium;
        }

        function populateFlowEditorList() {
            const list = document.getElementById('flow-editor-list'); list.innerHTML = '';
            const filterText = document.getElementById('flow-filter-text').value.toLowerCase();
            const filterUnit = document.getElementById('flow-filter-unit').value;
            const filterMedium = document.getElementById('flow-filter-medium').value;
            (allStructureData.fluesse || []).forEach(flow => {
                if (filterText && !flow.name.toLowerCase().includes(filterText)) return;
                if (filterUnit && flow.associatedUnitId !== filterUnit) return;
                if (filterMedium && flow.medium !== filterMedium) return;
                const li = document.createElement('li');
                let badgesHtml = '<div class="flow-badges">';
                if (flow.associatedUnitId) { const unitNode = findNodeById(allStructureData.betriebseinheiten, flow.associatedUnitId); if (unitNode) { badgesHtml += `<span class="flow-unit-badge">Einheit: ${unitNode.name}</span>`; } }
                if (flow.medium) { badgesHtml += `<span class="flow-medium-badge">Medium: ${flow.medium}</span>`; }
                badgesHtml += '</div>';
                li.innerHTML = `<span class="flow-name" data-flow-id="${flow.id}"><strong>${flow.name}</strong>${badgesHtml}</span> <div class="flow-actions"><span class="action-icon edit" data-action="edit" data-flow-id="${flow.id}">&#9998;</span> <span class="action-icon delete" data-action="delete" data-flow-id="${flow.id}">×</span></div>`;
                list.appendChild(li);
            });
            list.onclick = (e) => {
                const target = e.target;
                const flowNameSpan = target.closest('.flow-name');
                if(flowNameSpan) { showFlow(flowNameSpan.dataset.flowId); return; }
                const actionTarget = target.closest('.action-icon');
                if(actionTarget?.dataset.action === 'edit') { switchSidebarView('process-edit', actionTarget.dataset.flowId); }
                if(actionTarget?.dataset.action === 'delete') { deleteFlow(actionTarget.dataset.flowId); }
            };
        }

        function deleteFlow(flowId) { if (!confirm("Sicher?")) return; allStructureData.fluesse = allStructureData.fluesse.filter(f => f.id !== flowId); debouncedSave(); populateFlowFilterOptions(); populateFlowEditorList(); }

        function flattenTreeForSelect(nodes, namePrefix = '', numberPrefix = '', result = []) {
            nodes.forEach((node, index) => {
                const currentNumber = numberPrefix + (index + 1); result.push({ id: node.id, name: `${currentNumber} ${namePrefix}${node.name}` });
                if (node.children) { flattenTreeForSelect(node.children, namePrefix + '  ', currentNumber + '.', result); }
            });
            return result;
        }

        function populateFlowDetailForm(flowId) {
            const flow = flowId ? allStructureData.fluesse.find(f => f.id === flowId) : null;
            document.getElementById('flow-detail-name').value = flow ? flow.name : '';
            document.getElementById('flow-detail-medium').value = flow ? flow.medium || '' : ''; 
            document.getElementById('flow-detail-color').value = flow ? flow.color : '#ff851b';
            const unitSelect = document.getElementById('flow-detail-unit'); unitSelect.innerHTML = '<option value="">-- Keine Zuordnung --</option>';
            const allUnits = flattenTreeForSelect(allStructureData.betriebseinheiten);
            allUnits.forEach(u => { const opt = document.createElement('option'); opt.value = u.id; opt.textContent = u.name; unitSelect.appendChild(opt); });
            if (flow && flow.associatedUnitId) { unitSelect.value = flow.associatedUnitId; }
            const fromSelect = document.getElementById('flow-from-select'); const toSelect = document.getElementById('flow-to-select');
            fromSelect.innerHTML = toSelect.innerHTML = '<option value="">-- Wählen --</option>';
            allUnits.forEach(opt => { const optionEl = document.createElement('option'); optionEl.value = opt.id; optionEl.textContent = opt.name; fromSelect.appendChild(optionEl.cloneNode(true)); toSelect.appendChild(optionEl); });
            const segmentsList = document.getElementById('flow-path-segments'); segmentsList.innerHTML = '';
            if (flow && flow.path) { flow.path.forEach(segment => addSegmentToList(segment.from, segment.to)); }
        }

        function addSegmentFromSelects() { const fromId = document.getElementById('flow-from-select').value; const toId = document.getElementById('flow-to-select').value; if (fromId && toId && fromId !== toId) { addSegmentToList(fromId, toId); } }
        function addSegmentToList(fromId, toId) { const fromNode = findNodeById(allStructureData.betriebseinheiten, fromId); const toNode = findNodeById(allStructureData.betriebseinheiten, toId); if (!fromNode || !toNode) return; const segmentsList = document.getElementById('flow-path-segments'); const li = document.createElement('li'); li.dataset.fromId = fromId; li.dataset.toId = toId; li.innerHTML = `<span>${fromNode.name} → ${toNode.name}</span><button>×</button>`; segmentsList.appendChild(li); }

        function saveFlowDetails() {
            const newName = document.getElementById('flow-detail-name').value.trim();
            const newMedium = document.getElementById('flow-detail-medium').value.trim();
            const associatedUnitId = document.getElementById('flow-detail-unit').value; 
            if (!newName) { alert("Bitte Namen eingeben."); return; }
            const newPath = []; document.querySelectorAll('#flow-path-segments li').forEach(li => { newPath.push({ from: li.dataset.fromId, to: li.dataset.toId }); });
            const newFlow = { id: currentlyEditingFlowId || crypto.randomUUID(), name: newName, medium: newMedium, associatedUnitId: associatedUnitId, color: document.getElementById('flow-detail-color').value, path: newPath };
            if (currentlyEditingFlowId) { const index = allStructureData.fluesse.findIndex(f => f.id === currentlyEditingFlowId); allStructureData.fluesse[index] = newFlow; } else { if (!allStructureData.fluesse) allStructureData.fluesse = []; allStructureData.fluesse.push(newFlow); }
            debouncedSave(); switchSidebarView('process-list');
        }

        function getMeshNamesRecursively(node) { let names = []; if (node.meshNames) { names.push(...node.meshNames); } if (node.children) { node.children.forEach(child => { names.push(...getMeshNamesRecursively(child)); }); } return [...new Set(names)]; }
        function highlightObjects(namesArray = []) { highlightedObjects.forEach(obj => { if(obj.userData.originalMaterial) obj.material = obj.userData.originalMaterial; }); highlightedObjects = []; namesArray.forEach(name => { const objectToHighlight = meshMap.get(name); if (objectToHighlight) { if (!objectToHighlight.userData.originalMaterial) { objectToHighlight.userData.originalMaterial = objectToHighlight.material; } objectToHighlight.material = highlightMaterial; highlightedObjects.push(objectToHighlight); } }); }
        function flyToAndHighlight(path) { if (!path) return; clearFlowView(); const node = getNodeByPath(path); if (!node) return; document.getElementById('treeview-container').classList.add('visible'); document.querySelectorAll('.tree-row.active').forEach(el => el.classList.remove('active')); const treeItem = document.querySelector(`.tree-item[data-path="${path}"]`); if (treeItem) { const treeRow = treeItem.querySelector('.tree-row'); treeRow.classList.add('active'); let parent = treeItem.parentElement.closest('.tree-item'); while (parent) { parent.classList.add('open'); parent = parent.parentElement.closest('.tree-item'); } treeRow.scrollIntoView({ behavior: 'smooth', block: 'center' }); } const meshNames = getMeshNamesRecursively(node); highlightObjects(meshNames); if (meshNames.length === 0) return; const combinedBox = new THREE.Box3(); meshNames.forEach(name => { const targetObject = meshMap.get(name); if (targetObject) { combinedBox.expandByObject(targetObject); } }); if (combinedBox.isEmpty()) return; const center = combinedBox.getCenter(new THREE.Vector3()); const size = combinedBox.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y, size.z); const cameraDistance = maxDim * 2.5; targetControlsTarget = center; targetCameraPosition = new THREE.Vector3(center.x + cameraDistance / 2, center.y + cameraDistance / 2, center.z + cameraDistance); }
        function createDetailRow(key, value) { const editor = document.getElementById('details-editor'); const row = document.createElement('div'); row.className = 'detail-row'; row.innerHTML = `<input type="text" class="detail-key" placeholder="Schlüssel" value="${key}"><input type="text" class="detail-value" placeholder="Wert" value="${value}"><button class="btn-delete-detail">×</button>`; editor.appendChild(row); }
        function populateMeshEditor(currentMeshNames = []) { const tagsContainer = document.getElementById('mesh-tags-container'); const dropdown = document.getElementById('mesh-dropdown'); tagsContainer.innerHTML = ''; dropdown.innerHTML = '<option value="">-- Mesh hinzufügen --</option>'; const assigned = new Set(currentMeshNames); currentMeshNames.forEach(name => { const tag = document.createElement('span'); tag.className = 'mesh-tag'; tag.textContent = name; tag.dataset.meshName = name; tag.innerHTML += ' <span class="remove-tag">×</span>'; tagsContainer.appendChild(tag); }); availableMeshNames.filter(name => !assigned.has(name)).forEach(name => { const option = document.createElement('option'); option.value = name; option.textContent = name; dropdown.appendChild(option); }); }
        function handleAddMeshTag(event) { const selectedName = event.target.value; if (!selectedName) return; populateMeshEditor([...getCurrentModalMeshNames(), selectedName]); event.target.value = ""; }
        function handleRemoveMeshTag(tagElement) { const nameToRemove = tagElement.dataset.meshName; populateMeshEditor(getCurrentModalMeshNames().filter(name => name !== nameToRemove)); }
        function getCurrentModalMeshNames() { return Array.from(document.querySelectorAll('#mesh-tags-container .mesh-tag')).map(tag => tag.dataset.meshName); }

        // --- HARDWARE MANAGEMENT LOGIC ---
        function populateHardwareEditor(currentHardware = []) {
            const tagsContainer = document.getElementById('hardware-tags-container'); 
            tagsContainer.innerHTML = '';
            
            currentHardware.forEach(entry => { 
                let name = entry;
                let id = null;
        
                // Versuche JSON zu parsen
                try {
                    if (entry.startsWith('{')) {
                        const obj = JSON.parse(entry);
                        name = obj.label;
                        id = obj.id;
                    }
                } catch(e) {
                    // Fallback: Alter Plain-Text String
                }
        
                const tag = document.createElement('span'); 
                tag.className = 'hardware-tag'; 
                tag.textContent = name; 
                tag.dataset.hwName = name; 
                if (id) tag.dataset.hwId = id; // WICHTIG: ID speichern
        
                tag.innerHTML += ' <span class="remove-tag">×</span>'; 
                tagsContainer.appendChild(tag); 
            });
        }
        function handleAddHardwareTag(event) {
            const selectedLabel = event.target.value;
            if (!selectedLabel) return;
        
            // Finde die Option im Datalist, um an die ID zu kommen
            const datalist = document.getElementById('hardware-datalist');
            const selectedOption = Array.from(datalist.options).find(opt => opt.value === selectedLabel);
            const selectedId = selectedOption ? selectedOption.dataset.id : null;
        
            const currentNames = getCurrentHardwareNames();
            if (currentNames.includes(selectedLabel)) {
                event.target.value = ""; // Eingabe zurücksetzen, wenn schon vorhanden
                return;
            }
        
            const tagsContainer = document.getElementById('hardware-tags-container');
            const tag = document.createElement('span');
            tag.className = 'hardware-tag';
            tag.textContent = selectedLabel;
            tag.dataset.hwName = selectedLabel; // Der lesbare Name
            if (selectedId) {
                tag.dataset.hwId = selectedId; // Die UUID
            }
            tag.innerHTML += ' <span class="remove-tag">×</span>';
            tagsContainer.appendChild(tag);
            
            event.target.value = ""; // Eingabe zurücksetzen
        }        
        function handleRemoveHardwareTag(tagElement) {
            // 1. Identifiziere, was gelöscht werden soll (ID oder Name)
            const idToRemove = tagElement.dataset.hwId;
            const nameToRemove = tagElement.dataset.hwName;
        
            // 2. Hole den aktuellen Zustand (Mischung aus JSON-Strings und Text)
            const currentList = getCurrentHardwareNames();
        
            // 3. Filtere die Liste intelligent
            const newList = currentList.filter(entry => {
                let entryId = null;
                let entryName = entry;
        
                // Versuche, den Eintrag zu parsen, falls es JSON ist
                try {
                    if (entry.startsWith('{')) {
                        const obj = JSON.parse(entry);
                        entryId = obj.id;
                        entryName = obj.label;
                    }
                } catch(e) {}
        
                // Logik:
                // Wenn wir eine ID zum Löschen haben, muss die ID matchen (stärkeres Kriterium).
                if (idToRemove) {
                    return entryId !== idToRemove; 
                }
                
                // Wenn wir keine ID haben (Altdaten/Freitext), muss der Name matchen.
                // Wichtig: Wir löschen nur, wenn auch der Eintrag KEINE ID hat, um Verwechslungen zu vermeiden.
                if (!idToRemove && !entryId) {
                     return entryName !== nameToRemove;
                }
        
                // Sonst behalten
                return true;
            });
        
            // 4. Neu rendern
            populateHardwareEditor(newList);
        }        
        function getCurrentHardwareNames() { 
            return Array.from(document.querySelectorAll('#hardware-tags-container .hardware-tag')).map(tag => {
                if (tag.dataset.hwId) {
                    // Wenn wir eine ID haben, speichern wir das strukturierte JSON
                    return JSON.stringify({ id: tag.dataset.hwId, label: tag.dataset.hwName });
                }
                // Fallback für alte Einträge oder Freitext ohne ID
                return tag.dataset.hwName;
            }); 
        }

        // --- RESPONSIBILITY MANAGEMENT LOGIC (New) ---
        function populateResponsibilityEditor(currentResp = []) {
            const tagsContainer = document.getElementById('responsibility-tags-container'); const dropdown = document.getElementById('responsibility-dropdown'); tagsContainer.innerHTML = '';
            const respArray = Array.isArray(currentResp) ? currentResp : (currentResp ? [currentResp] : []);
            respArray.forEach(name => { const tag = document.createElement('span'); tag.className = 'responsibility-tag'; tag.textContent = name; tag.dataset.respName = name; tag.innerHTML += ' <span class="remove-tag">×</span>'; tagsContainer.appendChild(tag); });
            dropdown.value = "";
        }
        function handleAddResponsibilityTag(event) { const selectedName = event.target.value; if (!selectedName) return; populateResponsibilityEditor([...getCurrentResponsibilityNames(), selectedName]); }
        function handleRemoveResponsibilityTag(tagElement) { const nameToRemove = tagElement.dataset.respName; const current = getCurrentResponsibilityNames(); const idx = current.indexOf(nameToRemove); if(idx > -1) current.splice(idx, 1); populateResponsibilityEditor(current); }
        function getCurrentResponsibilityNames() { return Array.from(document.querySelectorAll('#responsibility-tags-container .responsibility-tag')).map(tag => tag.dataset.respName); }

        function buildMeshDetailsMap(nodes, viewKey, pathPrefix = '', numberPrefix = '') { 
            if (!pathPrefix) { 
                viewMaps[viewKey].meshToPathMap.clear(); 
                viewMaps[viewKey].meshDetailsMap.clear(); 
            } 
            
            nodes.forEach((node, index) => { 
                if(!node.id) node.id = crypto.randomUUID(); 
                
                const currentPath = pathPrefix ? `${pathPrefix}.children.${index}` : `${index}`; 
                const currentNumber = numberPrefix + (index + 1);
                const displayName = node.labelName || node.name;
                const fullDisplayName = `${currentNumber} ${displayName}`;
                node._runtime_fullName = fullDisplayName;
        
                if (node.meshNames) { 
                    node.meshNames.forEach(meshName => { 
                        if (!viewMaps[viewKey].meshToPathMap.has(meshName)) { 
                            viewMaps[viewKey].meshToPathMap.set(meshName, currentPath); 
                        } 
                        viewMaps[viewKey].meshDetailsMap.set(meshName, node); 
                    }); 
                } 
                
                if (node.children) { 
                    buildMeshDetailsMap(node.children, viewKey, currentPath, currentNumber + '.'); 
                } 
            }); 
        }        
        function addTopLevelNode() { getCurrentStructureData().push({ id: crypto.randomUUID(), name: "Neuer Überpunkt", children: [], details: {}, hardware: [], responsibility: [] }); buildMeshDetailsMap(getCurrentStructureData(), currentView); redrawTree(); debouncedSave(); }
        function expandAll() { document.querySelectorAll('#tree .tree-item').forEach(li => { if(li.querySelector('ul')) li.classList.add('open'); }); }
        function collapseAll() { document.querySelectorAll('#tree .tree-item.open').forEach(li => { li.classList.remove('open'); }); }
        function getParentAndIndexByPath(pathString) { const structure = getCurrentStructureData(); const pathParts = pathString.split('.children.'); const index = parseInt(pathParts.pop()); if (pathParts.length === 0) { return { parent: structure, index }; } const parentPath = pathParts.join('.children.'); const parentNode = getNodeByPath(parentPath); return { parent: parentNode ? parentNode.children : null, index }; }
        function getNodeByPath(pathString) { if (!pathString) return null; const path = pathString.split('.children.'); let current = { children: getCurrentStructureData() }; for(const part of path) { if (current && current.children && typeof current.children[part] !== 'undefined') { current = current.children[part]; } else { return null; } } return current; }
        function addNode(path) { const parentNode = getNodeByPath(path); if (!parentNode) return; if (!parentNode.children) parentNode.children = []; parentNode.children.push({ id: crypto.randomUUID(), name: "Neues Bauteil", children: [], details: {}, hardware: [], responsibility: [] }); buildMeshDetailsMap(getCurrentStructureData(), currentView); redrawTree(path); debouncedSave(); }
        function deleteNode(path) { if (!confirm("Sicher?")) return; const { parent, index } = getParentAndIndexByPath(path); if (parent && index > -1) { parent.splice(index, 1); buildMeshDetailsMap(getCurrentStructureData(), currentView); redrawTree(); debouncedSave(); } }
        function redrawTree(pathHint = null) { 
            const openPaths = new Set(Array.from(document.querySelectorAll('.tree-item.open')).map(li => li.dataset.path)); 
            if (pathHint) openPaths.add(pathHint); 
            const treeContainer = document.getElementById('tree'); treeContainer.innerHTML = ''; 
            createTreeView(getCurrentStructureData(), treeContainer); 
            openPaths.forEach(path => { const li = treeContainer.querySelector(`li[data-path="${path}"]`); if (li) li.classList.add('open'); }); 
            if(document.getElementById('search-input').value) filterTreeAndScene(document.getElementById('search-input').value.toLowerCase());
        }

        function moveNode(sourcePath, targetPath, dropPosition) {
            if (sourcePath === targetPath) return;
            if (targetPath.startsWith(sourcePath + '.')) return alert("Kann nicht in eigenes Kind-Element verschoben werden.");
            const sourceNodeClone = JSON.parse(JSON.stringify(getNodeByPath(sourcePath)));
            const { parent: sourceParent, index: sourceIndex } = getParentAndIndexByPath(sourcePath);
            sourceParent.splice(sourceIndex, 1);
            if (dropPosition === 'inside') { const targetNode = getNodeByPath(targetPath); if (!targetNode.children) targetNode.children = []; targetNode.children.push(sourceNodeClone); } 
            else {
                const { parent: targetParent, index: targetIndex } = getParentAndIndexByPath(targetPath);
                let insertIndex = targetIndex; if (dropPosition === 'after') insertIndex++;
                if (sourceParent === targetParent && sourceIndex < targetIndex) insertIndex--; 
                targetParent.splice(insertIndex, 0, sourceNodeClone);
            }
            buildMeshDetailsMap(getCurrentStructureData(), currentView); redrawTree(); debouncedSave();
        }

        function createTreeView(nodes, parentElement, numberPrefix = '', pathPrefix = '') { 
            const ul = document.createElement('ul'); 
            nodes.forEach((node, index) => { 
                const li = document.createElement('li'); li.classList.add('tree-item'); li.draggable = isStructureEditMode; 
                const currentNumber = numberPrefix + (index + 1); 
                const currentPath = pathPrefix ? `${pathPrefix}.children.${index}` : `${index}`; 
                li.dataset.path = currentPath; 

                const rowDiv = document.createElement('div'); rowDiv.classList.add('tree-row'); 
                const toggler = document.createElement('span'); toggler.classList.add('tree-toggler'); 
                const numberSpan = document.createElement('span'); numberSpan.classList.add('tree-number'); numberSpan.textContent = currentNumber; 
                const labelSpan = document.createElement('span'); labelSpan.classList.add('tree-label'); labelSpan.textContent = node.name; 

                // INFO ICON (NEU)
                const infoContainer = document.createElement('div');
                infoContainer.className = 'info-icon-container';
                if ((node.hardware && node.hardware.length > 0) || (node.responsibility && node.responsibility.length > 0)) {
                    const infoIcon = document.createElement('span');
                    infoIcon.className = 'info-icon';
                    infoIcon.textContent = 'i';

                    // Tooltip Logic
                    infoIcon.addEventListener('mouseenter', (e) => {
                        const tooltip = document.getElementById('tree-tooltip');
                        let content = '';
                        if (node.responsibility && node.responsibility.length > 0) {
                            content += `<strong>Verantwortlich:</strong><ul>${node.responsibility.map(r => `<li>${r}</li>`).join('')}</ul>`;
                        }
                        if (node.hardware && node.hardware.length > 0) {
                            const cleanHardware = node.hardware.map(h => {
                                try {
                                    if (h.startsWith('{')) {
                                        return JSON.parse(h).label;
                                    }
                                } catch(e) {}
                                return h;
                            });
                            
                            content += `<strong>Hardware:</strong><ul>${cleanHardware.map(h => `<li>${h}</li>`).join('')}</ul>`;
                        }
                        tooltip.innerHTML = content;
                        tooltip.style.display = 'block';
                        updateTooltipPosition(e);
                    });
                    infoIcon.addEventListener('mousemove', updateTooltipPosition);
                    infoIcon.addEventListener('mouseleave', () => { document.getElementById('tree-tooltip').style.display = 'none'; });

                    infoContainer.appendChild(infoIcon);
                }

                const actionsDiv = document.createElement('div'); actionsDiv.classList.add('tree-actions'); 
                const editIcon = document.createElement('span'); editIcon.classList.add('action-icon', 'edit'); editIcon.innerHTML = '&#9998;'; editIcon.title = 'Eintrag bearbeiten'; editIcon.dataset.action = 'edit'; actionsDiv.appendChild(editIcon); 
                const addIcon = document.createElement('span'); addIcon.classList.add('action-icon', 'add'); addIcon.textContent = '+'; addIcon.title = 'Unterpunkt hinzufügen'; addIcon.dataset.action = 'add'; actionsDiv.appendChild(addIcon); 
                if (!node.children || node.children.length === 0) { const deleteIcon = document.createElement('span'); deleteIcon.classList.add('action-icon', 'delete'); deleteIcon.textContent = '×'; deleteIcon.title = 'Element löschen'; deleteIcon.dataset.action = 'delete'; actionsDiv.appendChild(deleteIcon); } 

                rowDiv.appendChild(toggler); rowDiv.appendChild(numberSpan); rowDiv.appendChild(labelSpan); 
                rowDiv.appendChild(infoContainer); // INFO ICON
                rowDiv.appendChild(actionsDiv); 
                li.appendChild(rowDiv); 

                if (node.children && node.children.length > 0) { rowDiv.classList.add('tree-node'); createTreeView(node.children, li, currentNumber + '.', currentPath); } 
                else { toggler.classList.add('empty'); li.classList.add('tree-leaf');} 
                ul.appendChild(li); 
            }); 
            parentElement.appendChild(ul); 
        }

        function updateTooltipPosition(e) {
            const tooltip = document.getElementById('tree-tooltip');
            const x = e.clientX + 15;
            const y = e.clientY + 15;
            // Prevent going off screen
            if (y + tooltip.offsetHeight > window.innerHeight) tooltip.style.top = (e.clientY - tooltip.offsetHeight - 5) + 'px';
            else tooltip.style.top = y + 'px';
            tooltip.style.left = x + 'px';
        }

        function setupTreeEventListeners() { 
            const treeContainer = document.getElementById('tree'); 
            treeContainer.addEventListener('click', (event) => { 
                const row = event.target.closest('.tree-row'); if (!row) return; 
                const li = row.parentElement; const path = li.dataset.path; 
                const actionIcon = event.target.closest('.action-icon'); const toggler = event.target.closest('.tree-toggler'); 
                if (actionIcon) { 
                    event.stopPropagation(); const action = actionIcon.dataset.action; 
                    if (action === 'add') addNode(path); else if (action === 'delete') deleteNode(path); else if (action === 'edit') openStructureEditor(path); 
                } else if (toggler) { event.stopPropagation(); if (row.classList.contains('tree-node')) { li.classList.toggle('open'); } } 
                else { flyToAndHighlight(path); } 
            });
            treeContainer.addEventListener('dragstart', (e) => { if (!isStructureEditMode) { e.preventDefault(); return; } const li = e.target.closest('.tree-item'); if(li) { draggedSourcePath = li.dataset.path; e.stopPropagation(); } });
            treeContainer.addEventListener('dragover', (e) => {
                e.preventDefault(); if (!isStructureEditMode) return; const row = e.target.closest('.tree-row'); if(!row) return;
                const rect = row.getBoundingClientRect(); const relY = e.clientY - rect.top; const height = rect.height;
                row.classList.remove('drag-over-top', 'drag-over-bottom', 'drag-over-middle');
                document.querySelectorAll('.tree-row').forEach(el => { if(el !== row) el.classList.remove('drag-over-top', 'drag-over-bottom', 'drag-over-middle'); });
                if (relY < height * 0.25) { row.classList.add('drag-over-top'); row.dataset.dropPos = 'before'; } 
                else if (relY > height * 0.75) { row.classList.add('drag-over-bottom'); row.dataset.dropPos = 'after'; } 
                else { row.classList.add('drag-over-middle'); row.dataset.dropPos = 'inside'; }
            });
            treeContainer.addEventListener('dragleave', (e) => {});
            treeContainer.addEventListener('drop', (e) => {
                e.preventDefault(); if (!isStructureEditMode) return; const row = e.target.closest('.tree-row');
                document.querySelectorAll('.tree-row').forEach(el => { el.classList.remove('drag-over-top', 'drag-over-bottom', 'drag-over-middle'); });
                if(row && draggedSourcePath) {
                    const li = row.parentElement; const targetPath = li.dataset.path; const dropPos = row.dataset.dropPos || 'inside';
                    if(targetPath !== draggedSourcePath) { moveNode(draggedSourcePath, targetPath, dropPos); }
                } draggedSourcePath = null;
            });
        }

        function setLabelViewState(labelDiv, isExpanded) {
            const { name, status, details, storageLink } = labelDiv.dataset;
            let html = `<strong>${name}</strong>`;
            let dotsHtml = '';
        
            // A) Status-Punkt (Wartung/Issue)
            // Rot: Kritisch / Blocker
            if (status === 'heiss') {
                dotsHtml += '<span style="display:inline-block; width:10px; height:10px; background-color:#dc3545; border-radius:50%; margin-right:6px;" title="Kritischer Status"></span>';
            } 
            // Blau: Aktive Arbeit (In Progress)
            else if (status === 'in_Betrieb') {
                dotsHtml += '<span style="display:inline-block; width:10px; height:10px; background-color:#0d6efd; border-radius:50%; margin-right:6px;" title="Wartung läuft"></span>';
            } 
            // Grau Halbkreis: Wartung geplant
            else if (status === 'Wartung_geplant') {
                dotsHtml += '<span style="display:inline-block; width:8px; height:8px; border: 2px solid #6c757d; border-radius:50%; margin-right:6px;" title="Wartung in den nächsten 14 Tagen"></span>';
            }

            // B) Lager-Punkt (Lila)
            if (storageLink) {
                dotsHtml += '<span style="display:inline-block; width:10px; height:10px; background-color:#bebebe; border-radius:50%; margin-right:6px;" title="Lager/Rüstwagen vorhanden"></span>';
            }
            if (dotsHtml) {
                html += `<div style="margin-top:5px; line-height:1;">${dotsHtml}</div>`;
            }
            
            // 3. Erweiterte Ansicht (Details beim Aufklappen)
            if (isExpanded) {
                html += `<hr>`;
                html += `<div id="live-data-${labelDiv.id}" class="live-data-container" style="font-size:11px;">
                            <span style="color:#666; font-style:italic;">Lade Daten...</span>
                         </div>`;
                if (details && details !== 'null' && details !== '{}') {
                    const detailsObj = JSON.parse(details);
                    html += '<div style="margin-top:8px; border-top:1px dashed #eee; padding-top:4px;">';
                    for (const key in detailsObj) {
                        html += `<div class="detail-item" style="color:#888;"><strong>${key}:</strong> <span>${detailsObj[key]}</span></div>`;
                    }
                    html += '</div>';
                }
                
                labelDiv.classList.add('expanded');
                setTimeout(() => loadLiveDetails(name, `live-data-${labelDiv.id}`), 10);
                
            } else {
                labelDiv.classList.remove('expanded');
            }
            
            labelDiv.innerHTML = html;
            labelDiv.isExpanded = isExpanded;
        }

        async function loadLiveDetails(unitName, containerId) {
            const container = document.getElementById(containerId);
            if(!container) return;

            const labelDiv = document.querySelector(`.status-label[data-name="${unitName}"]`);
            let storageLink = null;
            if (labelDiv && labelDiv.dataset.storageLink) {
                storageLink = labelDiv.dataset.storageLink;
            }

            let html = '';
            let hasContent = false;

            try {
                // 1. Wartungsaufträge laden
                const response = await fetch(`/api/facility/unit_orders?unit=${encodeURIComponent(unitName)}`);
                const orders = await response.json();
                
                if (orders.length > 0) {
                    hasContent = true;
                    html += '<div style="margin-bottom:8px; font-weight:bold; color:#555;">Wartung & Issues</div>';
                    html += '<div style="display:flex; flex-direction:column; gap:6px;">';
                    orders.forEach(o => {
                        let icon = 'build'; let color = '#007bff';
                        if (o.type === 'I') {
                            icon = 'warning'; color = '#ffc107';
                            if (o.priority === 'High') color = '#fd7e14';
                            if (o.priority === 'Critical' || o.isBlocker) { icon = 'error'; color = '#dc3545'; }
                        }
                        const link = `/maintenance?openId=${o.id}`;
                        html += `
                            <a href="${link}" target="_blank" style="text-decoration:none; color:inherit; display:flex; align-items:start; gap:6px; background:rgba(255,255,255,0.5); padding:4px; border-radius:4px; transition:background 0.2s;" onmouseover="this.style.background='white'" onmouseout="this.style.background='rgba(255,255,255,0.5)'">
                                <span class="material-icons" style="font-size:14px; color:${color}; margin-top:2px;">${icon}</span>
                                <div>
                                    <div style="font-weight:600; font-size:11px; line-height:1.2;">${o.title}</div>
                                    <div style="font-size:9px; color:#666; font-family:monospace;">${o.id}</div>
                                </div>
                            </a>`;
                    });
                    html += '</div>';
                }

// 2. Lagerbestand laden (wenn verknüpft)
                if (storageLink) {
                    const storRes = await fetch(`/api/facility/storage_content?name=${encodeURIComponent(storageLink)}`);
                    const items = await storRes.json();
                    
                    if (items.length > 0) {
                        if (hasContent) html += '<hr style="border:0; border-top:1px dashed #ccc; margin:8px 0;">';
                        hasContent = true;
                        
                        html += `<div style="margin-bottom:4px; font-weight:bold; color:#004085;">📦 ${storageLink}</div>`;
                        
                        // Wrapper für horizontales Scrollen bei breiten Tabellen
                        html += '<div style="overflow-x:auto; max-width:100%;">';
                        html += '<table style="width:100%; font-size:10px; border-collapse:collapse; white-space:nowrap;">';
                        
                        // NEUE HEADER: Anz. | Name | P/N | S/N | Ebene | Box
                        html += `
                            <tr style="color:#888; text-align:left; font-size:9px; border-bottom:1px solid #ddd;">
                                <th style="padding:2px 4px;">Anz.</th>
                                <th style="padding:2px 4px;">Name</th>
                                <th style="padding:2px 4px;">P/N</th>
                                <th style="padding:2px 4px;">S/N</th>
                                <th style="padding:2px 4px;">Ebene</th>
                                <th style="padding:2px 4px;">Box</th>
                            </tr>`;
                        
                        items.forEach(item => {
                            const hwLink = `/test_equipment`; 
                            // Name kürzen, damit Tabelle nicht explodiert
                            const shortDesc = item.desc.length > 15 ? item.desc.substring(0, 15) + '...' : item.desc;
                            
                            html += `
                                <tr style="border-bottom:1px solid #eee;">
                                    <td style="font-weight:bold; padding:2px 4px; text-align:center;">${item.qty}</td>
                                    
                                    <td style="padding:2px 4px;">
                                        <a href="${hwLink}" target="_blank" style="text-decoration:none; color:#007bff; font-weight:500;" title="${item.desc}">
                                            ${shortDesc}
                                        </a>
                                    </td>
                                    
                                    <td style="padding:2px 4px; font-family:monospace; color:#555;">${item.pn}</td>
                                    <td style="padding:2px 4px; font-family:monospace; color:#555;">${item.sn || '-'}</td>
                                    
                                    <td style="padding:2px 4px; text-align:center;">${item.level || '-'}</td>
                                    <td style="padding:2px 4px; text-align:center;">${item.box || '-'}</td>
                                </tr>
                            `;
                        });
                        html += '</table>';
                        html += '</div>'; // Ende Wrapper
                    }
                }
                
                if (!hasContent) {
                    container.innerHTML = '<span style="color:#28a745;">✓ Keine Meldungen / Lager leer</span>';
                } else {
                    container.innerHTML = html;
                }
                
            } catch (e) {
                console.error(e);
                container.innerHTML = '<span style="color:red;">Fehler beim Laden.</span>';
            }
        }
        function createStatusLabel(mesh, status) { 
            const nodeData = getCurrentViewMaps().meshDetailsMap.get(mesh.name); 
            if (!nodeData || nodeData.showStatusLabel === false) { return; } 
            
            const labelDiv = document.createElement('div'); 
            labelDiv.id = `label-${Math.random().toString(36).substr(2, 9)}`;
            labelDiv.className = 'status-label'; 
            
            // NEU: Wir nutzen den vorbrechneten vollen Namen (Index + Name)
            // Fallback auf normalen Namen, falls irgendwas schiefging
            const displayName = nodeData._runtime_fullName || nodeData.labelName || nodeData.name; 
            
            labelDiv.dataset.name = displayName; 
            labelDiv.dataset.status = status; 
            labelDiv.dataset.details = nodeData.details ? JSON.stringify(nodeData.details) : null; 
            if (nodeData.storageLink) { labelDiv.dataset.storageLink = nodeData.storageLink;} 
            
            labelDiv.isExpanded = false; 
            setLabelViewState(labelDiv, false); 
            
            labelDiv.style.borderLeft = `5px solid ${(STATUS_COLORS[status] || STATUS_COLORS['default']).getStyle()}`; 
            
            labelDiv.addEventListener('click', (event) => { 
                event.stopPropagation(); 
                const path = getCurrentViewMaps().meshToPathMap.get(mesh.name); 
                flyToAndHighlight(path); 
                const wasExpanded = labelDiv.isExpanded; 
                if (currentlyExpandedLabel && currentlyExpandedLabel !== labelDiv) { 
                    setLabelViewState(currentlyExpandedLabel, false); 
                } 
                setLabelViewState(labelDiv, !wasExpanded); 
                currentlyExpandedLabel = wasExpanded ? null : labelDiv; 
            }); 
            
            const label = new CSS2DObject(labelDiv); 
            label.userData = { meshName: mesh.name }; 
            const box = new THREE.Box3().setFromObject(mesh); 
            const center = box.getCenter(new THREE.Vector3()); 
            label.position.copy(center); 
            scene.add(label); 
            allLabels.push(label); 
        }

        function updateAllLabels() { allLabels.forEach(label => { label.removeFromParent(); }); allLabels = []; fetchRoomStatusAndUpdateColors(scene, true); }
        function loadModel() { 
            const loader = new GLTFLoader(); 
            const url = '/facility/model/current?t=' + new Date().getTime();
            loader.load(url, (gltf) => { scene.add(gltf.scene); gltf.scene.traverse(child => { if (child.isMesh) meshMap.set(child.name, child); }); availableMeshNames = Array.from(meshMap.keys()).sort(); fetchRoomStatusAndUpdateColors(gltf.scene); }, undefined, (error) => console.error('FEHLER:', error)); 
        }
        async function fetchRoomStatusAndUpdateColors(model, onlyUpdateLabels = false) { try { const response = await fetch('/api/facility/room_status'); const roomStatusData = await response.json(); model.traverse((child) => { if (child.isMesh) { const roomName = child.name; if (getCurrentViewMaps().meshDetailsMap.has(roomName)) { const status = roomStatusData[roomName] || 'N/A'; const color = STATUS_COLORS[status] || STATUS_COLORS['default']; if (!onlyUpdateLabels) { const material = new THREE.MeshStandardMaterial({ color: color, transparent: true, opacity: 0.8 }); allMaterials.push({ material: material, originalColor: color.clone() }); child.material = material; child.userData.originalMaterial = material; } createStatusLabel(child, status); } else if (!onlyUpdateLabels) { child.material = new THREE.MeshStandardMaterial({ color: 0xadc5d9, transparent: true, opacity: 0.1, depthWrite: false }); child.userData.originalMaterial = child.material; } } }); } catch (error) { console.error('Fehler:', error); } }
        function toggleViewMode() { isStatusView = !isStatusView; const button = document.getElementById('view-toggle-button'); if (isStatusView) { button.textContent = "Architektur-Ansicht"; allLabels.forEach(label => label.visible = true); clearFlowView(); allMaterials.forEach(item => { item.material.color.copy(item.originalColor); item.material.opacity = 0.8; }); } else { button.textContent = "Status-Ansicht"; allLabels.forEach(label => label.visible = false); clearFlowView(); allMaterials.forEach(item => { item.material.color.copy(NEUTRAL_COLOR); item.material.opacity = NEUTRAL_OPACITY; }); } if (highlightedObjects.length > 0) { highlightObjects([]); document.querySelectorAll('.tree-row.active').forEach(el => el.classList.remove('active')); } }
        init();
    </script>
</body>
</html>
"""

TEMPLATE_EDITOR = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ultimativer 3D Layout-Editor mit Baugruppen (Parenting)</title>
    <script src="https://unpkg.com/lucide@latest"></script>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
    <style>
        :root {
            --panel-bg: rgba(255, 255, 255, 0.95); --panel-blur: 12px;
            --border-color: rgba(0, 0, 0, 0.1); --shadow-color: rgba(0, 0, 0, 0.15);
            --text-color: #333; --primary-color: #007bff; --scene-bg: #f0f2f5;
        }
        body { margin: 0; overflow: hidden; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background-color: var(--scene-bg); color: var(--text-color); }
        #canvas-container { position: fixed; top: 0; left: 0; width: 100%; height: 100%; }
        .panel { background: var(--panel-bg); backdrop-filter: blur(var(--panel-blur)); border: 1px solid var(--border-color); border-radius: 12px; box-shadow: 0 8px 30px var(--shadow-color); padding: 15px; box-sizing: border-box; z-index: 10; }
        #left-panel-container {
            position: fixed; top: 74px; left: 20px; z-index: 20;
            display: flex; flex-direction: column; gap: 10px;
            width: 260px;
            max-height: calc(100vh - 94px);
            overflow-y: auto;
            transform: translateX(-120%); opacity: 0;
            transition: transform 0.3s ease, opacity 0.3s ease;
            pointer-events: none;
        }
        #left-panel-container.visible { transform: translateX(0); opacity: 1; pointer-events: auto; }
        #left-panel-container::-webkit-scrollbar { width: 6px; }
        #left-panel-container::-webkit-scrollbar-thumb { background-color: #ccc; border-radius: 3px; }
        #control-buttons { position: fixed; top: 20px; left: 20px; z-index: 30; }
        .control-btn { width: 44px; height: 44px; background-color: #fff; border: 1px solid #ddd; border-radius: 8px; cursor: pointer; box-shadow: 0 4px 15px rgba(0,0,0,0.1); display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; }
        .control-btn:hover { background-color: #f8f8f8; transform: translateY(-2px); }
        .control-btn[disabled] { cursor: not-allowed; opacity: 0.5; background-color: #f5f5f5; }
        .control-btn[disabled]:hover { transform: none; }
        .control-btn svg { width: 24px; height: 24px; color: #555; }
        #alignment-panel { position: fixed; top: 20px; right: 20px; width: 280px; display: none; }
        .panel-header { display: flex; justify-content: space-between; align-items: center; cursor: pointer; user-select: none; }
        .panel-header h2 { margin: 0; font-size: 16px; color: var(--text-color); border-bottom: none; padding-bottom: 0; }
        .panel-header .chevron { transition: transform 0.3s ease; }
        .panel-content { max-height: 2000px; overflow: hidden; transition: max-height 0.4s ease-out, margin-top 0.4s ease-out; margin-top: 15px; }
        .panel.collapsed .panel-content { max-height: 0; margin-top: 0; }
        .panel.collapsed .panel-header .chevron { transform: rotate(-90deg); }
        .panel h3 { font-size: 14px; margin: 0 0 15px 0; color: var(--text-color); border-bottom: 1px solid #eee; padding-bottom: 10px; }
        .button-group { display: grid; grid-template-columns: repeat(auto-fit, minmax(60px, 1fr)); gap: 8px; margin-bottom: 15px; }
        .btn { padding: 8px 12px; border: 1px solid #ccc; background-color: #fff; color: var(--text-color); border-radius: 6px; cursor: pointer; font-weight: 500; transition: all 0.2s ease; text-align: center; display: flex; align-items: center; justify-content: center; gap: 6px; font-size: 12px; }
        .btn:hover { background-color: #f0f0f0; border-color: #999; }
        .btn[disabled] { cursor: not-allowed; opacity: 0.5; background-color: #f5f5f5; }
        .btn.active { background-color: var(--primary-color); color: white; border-color: var(--primary-color); }
        .btn-fullwidth { grid-column: 1 / -1; }
        .btn-success { background-color: #28a745; border-color: #28a745; color: white; }
        .btn-primary-outline { background-color: transparent; border-color: var(--primary-color); color: var(--primary-color); }
        .btn-primary-outline:hover { background-color: rgba(0, 123, 255, 0.1); }
        .btn svg { width: 16px; height: 16px; }
        .form-group { margin-bottom: 15px; }
        .form-group label { display: block; font-size: 14px; font-weight: 500; margin-bottom: 5px; color: #555; }
        .form-group input { width: 100%; padding: 8px 10px; border: 1px solid #ccc; border-radius: 6px; font-size: 14px; box-sizing: border-box; text-align: right; }
        #info-text { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); color: white; padding: 10px 20px; border-radius: 8px; font-size: 14px; pointer-events: none; opacity: 0; transition: opacity 0.3s; }
        #info-text.visible { opacity: 1; }

        .align-info { font-size: 13px; margin-bottom: 10px; display: flex; justify-content: space-between; border-bottom: 1px solid #eee; padding-bottom: 5px; }
        .align-info span { font-weight: bold; }
        .name-primary { color: #cccc00; text-shadow: 0 0 1px #333; }
        .name-target { color: #ff9900; text-shadow: 0 0 1px #333;}

        .align-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 4px; }
        .align-grid label { grid-column: 1 / -1; font-size: 11px; font-weight: 700; text-transform: uppercase; color: #888; margin-top: 10px; margin-bottom: 2px; }
        .align-grid .btn { padding: 6px 0; min-width: 0; justify-content: center; }
        .align-grid .btn svg { width: 18px; height: 18px; color: #555; }
        .align-grid .btn:hover svg { color: #000; }
        .align-grid .btn svg.lucide-circle { width: 14px; height: 14px; fill: #555; }

        #tree-list, #tree-list ul { list-style: none; padding: 0; margin: 0; }
        #tree-list { max-height: 300px; overflow-y: auto; }
        #tree-list ul { padding-left: 20px; }
        .tree-item { display: flex; align-items: center; padding: 6px 10px; border-radius: 6px; cursor: pointer; font-size: 14px; transition: background-color 0.2s; position: relative; }
        .tree-item:hover { background-color: #f0f0f0; }
        .tree-item.active { background-color: var(--primary-color); color: white; }
        .tree-item.locked { color: #999; font-style: italic; }
        .tree-item-label { flex-grow: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; }
        .tree-item-label input { width: 100%; background: #fff; border: 1px solid var(--primary-color); color: black; border-radius: 4px; padding: 2px 4px; font-size: 14px; }
        .tree-item-icon { margin-right: 6px; color: #aaa; }
        .tree-item-icon svg { width: 14px; height: 14px; }
        .tree-item-actions { display: flex; align-items: center; margin-left: auto; }
        .btn-icon { background: none; border: none; cursor: pointer; opacity: 0.6; padding: 2px 4px; border-radius: 4px; display: flex; align-items: center; justify-content: center;}
        .btn-icon:hover { opacity: 1; background-color: rgba(0,0,0,0.1); }
        .btn-icon.btn-delete-item { color: #dc3545; }
        .btn-icon.btn-lock.locked { color: var(--primary-color); opacity: 1; }
        .btn-icon svg { width: 16px; height: 16px; }
        .dimension-inputs { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }

        /* BACK BTN STYLING */
        #back-to-viewer-btn { position: fixed; top: 20px; left: 74px; width: 44px; height: 44px; background-color: #333; color: white; border: 1px solid #000; border-radius: 8px; cursor: pointer; z-index: 30; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); font-size: 20px; display: flex; align-items: center; justify-content: center; transition: all 0.2s ease; text-decoration: none; }
        #back-to-viewer-btn:hover { background-color: #555; transform: translateY(-2px); }

        /* STARTUP MODAL */
        #startup-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); backdrop-filter: blur(5px); z-index: 100; display: flex; align-items: center; justify-content: center; }
        .modal-card { background: white; padding: 30px; border-radius: 12px; box-shadow: 0 10px 40px rgba(0,0,0,0.3); max-width: 400px; text-align: center; }
        .modal-card h2 { margin-top: 0; color: #333; }
        .modal-card p { color: #666; line-height: 1.5; margin-bottom: 25px; }
        .modal-actions { display: flex; gap: 10px; justify-content: center; }
        .btn-large { padding: 12px 24px; font-size: 16px; cursor: pointer; border-radius: 6px; border: none; font-weight: 600; transition: transform 0.1s; }
        .btn-large:hover { transform: translateY(-2px); }
        .btn-large-primary { background-color: #007bff; color: white; box-shadow: 0 4px 12px rgba(0,123,255,0.3); }
        .btn-large-secondary { background-color: #f0f0f0; color: #555; }
        #back-to-viewer-btn { 
            position: fixed; 
            top: 20px; 
            left: 74px; /* Rechts neben dem Toolbox-Button */
            width: 44px; 
            height: 44px; 
            background-color: #fff; 
            color: #555; 
            border: 1px solid #ddd; 
            border-radius: 8px; 
            cursor: pointer; 
            z-index: 30; 
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); 
            display: flex; 
            align-items: center; 
            justify-content: center; 
            transition: all 0.2s ease; 
            text-decoration: none; 
        }
        #back-to-viewer-btn:hover { 
            background-color: #f8f8f8; 
            transform: translateY(-2px); 
            color: #007bff; 
        }
    </style>
</head>
<body>
    <div id="canvas-container"></div>
    <input type="file" id="glb-file-input" style="display: none;" accept=".glb">

    <div id="control-buttons">
        <button id="toggle-toolbox-btn" class="control-btn" title="Werkzeuge ein-/ausblenden"><i data-lucide="sliders-horizontal"></i></button>
    </div>

    <!-- LINK ZURÜCK ZUR VIEW -->
    <a href="{{ url_for('serve_facility_viewer') }}" id="back-to-viewer-btn" title="Zurück zur Ansicht">
        <span class="material-icons">visibility</span>
    </a>

    <!-- STARTUP MODAL -->
    <div id="startup-modal">
        <div class="modal-card">
            <h2>Editor Starten</h2>
            <p>Möchtest du das aktuelle Live-Modell vom Server laden, um es zu bearbeiten?</p>
            <div class="modal-actions">
                <button class="btn-large btn-large-primary" id="btn-modal-load-server">Ja, Modell laden</button>
                <button class="btn-large btn-large-secondary" id="btn-modal-empty">Nein, leere Szene</button>
            </div>
        </div>
    </div>

    <div id="left-panel-container">
        <div id="toolbox-panel" class="panel">
            <div class="panel-header"><h2>Toolbox</h2><i data-lucide="chevron-down" class="chevron"></i></div>
            <div class="panel-content">
                <div class="form-group"> <label>Verlauf</label> <div class="button-group" style="grid-template-columns: 1fr 1fr;"> <button id="btn-undo" class="btn" title="Letzte Aktion rückgängig machen (Strg+Z)"><i data-lucide="undo-2"></i>Zurück</button> <button id="btn-redo" class="btn" title="Letzte Aktion wiederholen (Strg+Y)"><i data-lucide="redo-2"></i>Wiederh.</button> </div> </div>
                <div class="form-group"> <label>Neues Objekt</label> <div class="button-group"> <button id="btn-add-box" class="btn"><i data-lucide="box"></i>Würfel</button> <button id="btn-add-sphere" class="btn"><i data-lucide="circle"></i>Kugel</button> <button id="btn-add-cylinder" class="btn"><i data-lucide="cylinder"></i>Zyl.</button> </div> </div>
                <div class="form-group"> <label>Modus (Q, W, E, R)</label> <div class="button-group" style="grid-template-columns: 1fr 1fr 1fr 1fr;"> 
                    <button id="btn-select" class="btn" title="Auswählen (Q)"><i data-lucide="mouse-pointer-2"></i></button>
                    <button id="btn-translate" class="btn active" title="Verschieben (W)"><i data-lucide="move"></i></button> 
                    <button id="btn-rotate" class="btn" title="Rotieren (E)"><i data-lucide="rotate-3d"></i></button> 
                    <button id="btn-scale" class="btn" title="Skalieren (R)"><i data-lucide="scale"></i></button> 
                </div> </div>
                <div class="form-group" id="boolean-operations-panel" style="display: none;">
                    <label>Boolesche Operationen</label>
                    <div class="button-group" style="grid-template-columns: 1fr 1fr;"> <button id="btn-union" class="btn" title="Beide Objekte zu einem verbinden"><i data-lucide="unite"></i>Vereinigen</button> <button id="btn-subtract" class="btn" title="Zielobjekt vom primären Objekt abziehen"><i data-lucide="minus-square"></i>Subtrahieren</button> <button id="btn-intersect" class="btn btn-fullwidth" title="Nur den überlappenden Teil behalten"><i data-lucide="intersect"></i>Schnittmenge</button> </div>
                </div>
                <div class="form-group"> <label for="grid-size-input">Gitter & Snap Größe</label> <input type="number" id="grid-size-input" value="1" step="0.1" min="0.1"> </div>

                <div class="form-group">
                    <label>Import / Export</label>
                    <input type="text" id="export-filename" placeholder="Dateiname (z.B. raum1)" style="margin-bottom: 8px;">
                    <div class="button-group" style="grid-template-columns: 1fr 1fr;">
                        <button id="btn-load-glb" class="btn btn-primary-outline"><i data-lucide="upload"></i>Laden</button>
                        <button id="btn-export-glb" class="btn btn-secondary"><i data-lucide="download"></i>DL</button>
                    </div>
                    <!-- SAVE TO SERVER BUTTON -->
                    <button id="btn-save-server" class="btn btn-success btn-fullwidth" style="margin-top: 5px;"><i data-lucide="cloud-upload"></i>Auf Server speichern (Live)</button>

                    <div id="loaded-file-info" style="font-size: 11px; color: #666; margin-top: 5px; text-align: center; font-style: italic;">Keine Datei geladen</div>
                </div>
            </div>
        </div>
        <div id="scene-tree-panel" class="panel">
            <div class="panel-header"><h2>Szenen-Objekte</h2><i data-lucide="chevron-down" class="chevron"></i></div>
            <div class="panel-content">
                <ul id="tree-list"></ul>
                <div id="properties-panel" style="display: none;">
                    <h3>Eigenschaften</h3>
                    <div class="form-group"> <label>Größe (X, Y, Z)</label> <div class="dimension-inputs"> <input type="number" id="prop-size-x" step="0.1" placeholder="X"> <input type="number" id="prop-size-y" step="0.1" placeholder="Y"> <input type="number" id="prop-size-z" step="0.1" placeholder="Z"> </div> </div>
                    <div class="form-group" style="margin-top: 15px;"> <button id="btn-interior-mode" class="btn btn-fullwidth"><i data-lucide="scan"></i>Innenraum-Modus</button> </div>
                </div>
            </div>
        </div>
    </div>

    <div id="alignment-panel" class="panel">
        <h2>Ausrichten</h2>
        <div class="align-info"><span>Bewegt:</span> <span id="align-primary-name" class="name-primary"></span></div>
        <div class="align-info"><span>Ziel:</span> <span id="align-target-name" class="name-target"></span></div>

        <div class="align-grid">
            <label>X (Links/Rechts)</label>
            <button class="btn" id="align-x-min-outer" title="Links neben Ziel"><i data-lucide="chevrons-left"></i></button> 
            <button class="btn" id="align-x-min-inner" title="Links bündig"><i data-lucide="chevron-left"></i></button> 
            <button class="btn" id="align-x-center" title="Zentriert"><i data-lucide="circle"></i></button> 
            <button class="btn" id="align-x-max-inner" title="Rechts bündig"><i data-lucide="chevron-right"></i></button> 
            <button class="btn" id="align-x-max-outer" title="Rechts neben Ziel"><i data-lucide="chevrons-right"></i></button>

            <label>Y (Unten/Oben)</label>
            <button class="btn" id="align-y-min-outer" title="Unter Ziel"><i data-lucide="chevrons-down"></i></button> 
            <button class="btn" id="align-y-min-inner" title="Unten bündig"><i data-lucide="chevron-down"></i></button> 
            <button class="btn" id="align-y-center" title="Zentriert"><i data-lucide="circle"></i></button> 
            <button class="btn" id="align-y-max-inner" title="Oben bündig"><i data-lucide="chevron-up"></i></button> 
            <button class="btn" id="align-y-max-outer" title="Auf Ziel"><i data-lucide="chevrons-up"></i></button>

            <label>Z (Tiefe)</label>
            <button class="btn" id="align-z-min-outer" title="Vor Ziel (Kameranäher)"><i data-lucide="chevrons-down"></i></button> 
            <button class="btn" id="align-z-min-inner" title="Vorne bündig"><i data-lucide="chevron-down"></i></button> 
            <button class="btn" id="align-z-center" title="Zentriert"><i data-lucide="circle"></i></button> 
            <button class="btn" id="align-z-max-inner" title="Hinten bündig"><i data-lucide="chevron-up"></i></button> 
            <button class="btn" id="align-z-max-outer" title="Hinter Ziel (Kameraferner)"><i data-lucide="chevrons-up"></i></button>
        </div>
    </div>
    <div id="info-text"></div>

    <script type="importmap">{ "imports": { "three": "https://unpkg.com/three@0.158.0/build/three.module.js", "three/addons/": "https://unpkg.com/three@0.158.0/examples/jsm/", "three-csg-ts": "https://esm.sh/three-csg-ts" } }</script>
    <script type="module">
        import * as THREE from 'three';
        import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
        import { TransformControls } from 'three/addons/controls/TransformControls.js';
        import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
        import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
        import { CSG } from 'three-csg-ts';

        let scene, camera, renderer, orbitControls, transformControls, raycaster, pointer;
        let primarySelection = null, targetSelection = null;
        let editableObjects = [];
        let gridHelper, gridSize = 1;
        let axisScene, axisCamera; const axisElements = [];
        let targetPosition = null, targetLookAt = null, isAnimatingCamera = false;
        let isScaling = false; let preTransformData = {};
        let isShiftDown = false;
        let historyStack = []; let redoStack = [];
        let isInteriorMode = false; let interiorModeObject = null;
        let originalGridSize = 1;
        let floorplanMesh = null;
        let interiorGridHelper = null;
        let isRestoringState = false;
        let gizmoEnabled = true;

        const defaultMaterial = new THREE.MeshStandardMaterial({ color: 0xcccccc, metalness: 0.2, roughness: 0.6, transparent: true, opacity: 0.85 });
        const primaryHighlightMaterial = new THREE.MeshStandardMaterial({ color: 0xffff00, emissive: 0x333300 });
        const targetHighlightMaterial = new THREE.MeshStandardMaterial({ color: 0xffa500, emissive: 0x332200 });
        const ui = { alignmentPanel: document.getElementById('alignment-panel'), booleanOperationsPanel: document.getElementById('boolean-operations-panel'), treeList: document.getElementById('tree-list'), leftPanelContainer: document.getElementById('left-panel-container'), infoText: document.getElementById('info-text'), propertiesPanel: document.getElementById('properties-panel'), propSizeX: document.getElementById('prop-size-x'), propSizeY: document.getElementById('prop-size-y'), propSizeZ: document.getElementById('prop-size-z'), toggleToolboxBtn: document.getElementById('toggle-toolbox-btn'), gridSizeInput: document.getElementById('grid-size-input'), btnSaveServer: document.getElementById('btn-save-server'),  undoBtn: document.getElementById('btn-undo'), redoBtn: document.getElementById('btn-redo'), btnInteriorMode: document.getElementById('btn-interior-mode'), btnLoadGlb: document.getElementById('btn-load-glb'), glbFileInput: document.getElementById('glb-file-input'), exportFilename: document.getElementById('export-filename'), loadedFileInfo: document.getElementById('loaded-file-info') };

        function init() {
            scene = new THREE.Scene(); scene.background = new THREE.Color(0xf0f2f5);
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(8, 10, 15); camera.lookAt(0,0,0);
            const container = document.getElementById('canvas-container');
            renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            container.appendChild(renderer.domElement);
            scene.add(new THREE.AmbientLight(0xffffff, 0.8));
            scene.add(new THREE.DirectionalLight(0xffffff, 1.0).position.set(10, 20, 5));
            orbitControls = new OrbitControls(camera, renderer.domElement); orbitControls.enableDamping = true;
            transformControls = new TransformControls(camera, renderer.domElement); scene.add(transformControls);
            transformControls.setTranslationSnap(gridSize); transformControls.setRotationSnap(THREE.MathUtils.degToRad(15)); transformControls.setScaleSnap(0.25);
            raycaster = new THREE.Raycaster(); pointer = new THREE.Vector2();
            updateGrid(); initAxisHelper(); setupEventListeners(); animate();
            lucide.createIcons();

            initModalLogic();

            window.addEventListener('beforeunload', (e) => { e.preventDefault(); e.returnValue = ''; });
        }

        // --- STARTUP MODAL LOGIC ---
        function initModalLogic() {
            const modal = document.getElementById('startup-modal');
            const btnLoad = document.getElementById('btn-modal-load-server');
            const btnEmpty = document.getElementById('btn-modal-empty');

            btnLoad.addEventListener('click', () => {
                modal.style.display = 'none';
                showInfoText("Lade Modell vom Server...", 5000);
                loadGLBFromUrl('/facility/model/current?t=' + Date.now(), "Live-Modell");
            });

            btnEmpty.addEventListener('click', () => {
                modal.style.display = 'none';
                showInfoText("Leere Szene gestartet.", 3000);
                saveState();
            });
        }

        function loadGLBFromUrl(url, displayName) {
            const loader = new GLTFLoader();
            loader.load(url, (gltf) => {
                processLoadedScene(gltf, displayName);
            }, undefined, (err) => {
                console.error(err);
                showInfoText("Fehler: Konnte Modell nicht laden (existiert es?).", 5000);
            });
        }

        function saveState() {
            if (isRestoringState) return;
            const state = editableObjects.map(obj => ({
                uuid: obj.uuid,
                parentUuid: obj.parent !== scene ? obj.parent.uuid : null,
                geometry: obj.geometry.toJSON(),
                position: obj.position.clone(),
                rotation: obj.rotation.clone(),
                scale: obj.scale.clone(),
                name: obj.name,
                userData: JSON.parse(JSON.stringify(obj.userData)) 
            }));
            historyStack.push(state);
            redoStack = [];
            updateHistoryButtons();
        }

        function restoreState(state) {
            isRestoringState = true; 
            if (isInteriorMode) exitInteriorMode();
            selectObject(null, null);
            transformControls.detach();
            while (editableObjects.length > 0) { removeObjectAndChildren(editableObjects[0]); } 

            const geometryLoader = new THREE.BufferGeometryLoader();
            const objectMap = new Map();

            for (const objData of state) {
                const geometry = geometryLoader.parse(objData.geometry);
                const mesh = new THREE.Mesh(geometry, defaultMaterial.clone());
                mesh.uuid = objData.uuid; 
                mesh.position.copy(objData.position);
                mesh.rotation.copy(objData.rotation);
                mesh.scale.copy(objData.scale);
                mesh.name = objData.name;
                mesh.userData = JSON.parse(JSON.stringify(objData.userData));
                mesh.userData.originalMaterial = mesh.material;
                editableObjects.push(mesh);
                objectMap.set(mesh.uuid, mesh);
            }

            for (const objData of state) {
                const obj = objectMap.get(objData.uuid);
                if (objData.parentUuid) {
                    const parent = objectMap.get(objData.parentUuid);
                    if (parent) parent.add(obj);
                } else {
                    scene.add(obj);
                }
            }
            updateTreeView();
            isRestoringState = false; 
        }

        function undo() { if (historyStack.length <= 1) return; const currentState = historyStack.pop(); redoStack.push(currentState); const prevState = historyStack[historyStack.length - 1]; restoreState(prevState); updateHistoryButtons(); }
        function redo() { if (redoStack.length === 0) return; const nextState = redoStack.pop(); historyStack.push(nextState); restoreState(nextState); updateHistoryButtons(); }
        function updateHistoryButtons() { ui.undoBtn.disabled = historyStack.length <= 1; ui.redoBtn.disabled = redoStack.length === 0; }

        function setupEventListeners() {
            ui.toggleToolboxBtn.addEventListener('click', () => ui.leftPanelContainer.classList.toggle('visible'));
            ui.undoBtn.addEventListener('click', undo); ui.redoBtn.addEventListener('click', redo);
            ui.btnInteriorMode.addEventListener('click', () => { if (isInteriorMode) { exitInteriorMode(); } else if (primarySelection) { enterInteriorMode(primarySelection); } });
            document.getElementById('btn-add-box').addEventListener('click', () => addPrimitive('box')); document.getElementById('btn-add-sphere').addEventListener('click', () => addPrimitive('sphere')); document.getElementById('btn-add-cylinder').addEventListener('click', () => addPrimitive('cylinder'));
            document.getElementById('btn-export-glb').addEventListener('click', exportGLB);

            // SAVE TO SERVER BTN
            document.getElementById('btn-save-server').addEventListener('click', saveToServer);

            ui.btnLoadGlb.addEventListener('click', () => ui.glbFileInput.click());
            ui.glbFileInput.addEventListener('change', (event) => loadGLB(event));
            document.querySelectorAll('.panel-header').forEach(header => { header.addEventListener('click', () => { header.parentElement.classList.toggle('collapsed'); }); });

            const setMode = (mode, btn) => { 
                document.querySelectorAll('#toolbox-panel .button-group .btn').forEach(b => b.classList.remove('active')); 
                if(btn) btn.classList.add('active'); 

                if (mode === 'select') {
                    gizmoEnabled = false;
                    transformControls.detach();
                } else {
                    gizmoEnabled = true;
                    transformControls.setMode(mode);
                    if (primarySelection && !transformControls.object) {
                        if (!isInteriorMode || primarySelection !== interiorModeObject) {
                             transformControls.attach(primarySelection);
                        }
                    }
                }
            };

            document.getElementById('btn-select').addEventListener('click', (e) => setMode('select', e.currentTarget));
            document.getElementById('btn-translate').addEventListener('click', (e) => setMode('translate', e.currentTarget)); 
            document.getElementById('btn-rotate').addEventListener('click', (e) => setMode('rotate', e.currentTarget)); 
            document.getElementById('btn-scale').addEventListener('click', (e) => setMode('scale', e.currentTarget));

            document.getElementById('btn-union').addEventListener('click', () => applyBooleanOperation('union')); document.getElementById('btn-subtract').addEventListener('click', () => applyBooleanOperation('subtract')); document.getElementById('btn-intersect').addEventListener('click', () => applyBooleanOperation('intersect'));
            const alignModes = ['min-outer', 'min-inner', 'center', 'max-inner', 'max-outer'];
            ['x', 'y', 'z'].forEach(axis => { alignModes.forEach(mode => { document.getElementById(`align-${axis}-${mode}`).addEventListener('click', () => {alignObjects(axis, mode); saveState();}); }); });
            ui.gridSizeInput.addEventListener('change', () => { 
                const newSize = parseFloat(ui.gridSizeInput.value); 
                if (newSize > 0) { 
                    gridSize = newSize; 
                    transformControls.setTranslationSnap(gridSize); 
                    if (isInteriorMode) updateFloorplan(); else updateGrid();
                } 
            });
            ui.treeList.addEventListener('click', (event) => { const item = event.target.closest('li > .tree-item'); if (!item) return; const object = editableObjects.find(obj => obj.uuid === item.parentElement.dataset.uuid); if (event.target.closest('.btn-rename')) { if (object) startRename(object, item.querySelector('.tree-item-label')); } else if (event.target.closest('.btn-lock')) { if (object) { toggleLock(object); saveState(); } } else if (event.target.closest('.btn-delete-item')) { if (object) { deleteObject(object); } } else { if (object && !object.userData.isLocked) { selectObject(object, isShiftDown ? primarySelection : null); } } });
            const setupDimensionInput = (inputElement, axis) => { inputElement.addEventListener('change', (event) => { if (!primarySelection) return; const newSize = parseFloat(event.target.value); if (isNaN(newSize) || newSize <= 0) { updatePropertiesPanel(); return; } const geoSize = primarySelection.userData.geometrySize; const newScale = newSize / geoSize[axis]; primarySelection.scale[axis] = newScale; enforceGroundConstraint(primarySelection); updatePropertiesPanel(); saveState(); }); };
            setupDimensionInput(ui.propSizeX, 'x'); setupDimensionInput(ui.propSizeY, 'y'); setupDimensionInput(ui.propSizeZ, 'z');
            transformControls.addEventListener('mouseDown', onTransformStart);
            transformControls.addEventListener('objectChange', () => { onTransform(); enforceGroundConstraint(); updatePropertiesPanel(); if (isInteriorMode) updateFloorplan(); });
            transformControls.addEventListener('mouseUp', onTransformEnd);
            transformControls.addEventListener('dragging-changed', e => { orbitControls.enabled = !e.value; });
            window.addEventListener('keydown', e => { if (e.key === 'Shift') isShiftDown = true; }); window.addEventListener('keyup', e => { if (e.key === 'Shift') isShiftDown = false; });
            renderer.domElement.addEventListener('pointerdown', onPointerDown, false);
            window.addEventListener('keydown', onKeyDown);
            window.addEventListener('resize', onWindowResize);
        }

        // --- COMMON SCENE LOADING LOGIC ---
        function processLoadedScene(gltf, filename) {
            ui.loadedFileInfo.textContent = `Geladen: ${filename}`;
            let nodesToProcess = [...gltf.scene.children];
            let didUnwrap = false;

            for (let i=0; i<2; i++) {
                if (nodesToProcess.length === 1) {
                    const root = nodesToProcess[0];
                    if (!root.isMesh && root.children.length > 0) {
                        console.log("Löse Container auf:", root.name);
                        scene.add(root);
                        root.updateMatrixWorld(true);
                        const children = [...root.children];
                        nodesToProcess = []; 
                        children.forEach(child => {
                            scene.attach(child); 
                            nodesToProcess.push(child);
                        });
                        scene.remove(root); 
                        didUnwrap = true;
                    }
                }
            }

            let loadedCount = 0;
            nodesToProcess.forEach(node => {
                scene.add(node); 
                if (!didUnwrap && node.position.lengthSq() < 0.001) {
                    const distance = 15;
                    const pos = new THREE.Vector3(0, 0, -distance).applyMatrix4(camera.matrixWorld);
                    node.position.set(pos.x, 0, pos.z); 
                }
                node.traverse((n) => {
                    if (!n.name) n.name = `Node_${Math.random().toString(36).substr(2, 5)}`;
                    if (!editableObjects.includes(n)) {
                        if (n.isMesh) {
                            n.material = defaultMaterial.clone();
                            n.userData.originalMaterial = n.material.clone();
                            n.userData.isLocked = false;
                            if(!n.geometry.boundingBox) n.geometry.computeBoundingBox();
                            n.userData.geometrySize = n.geometry.boundingBox.getSize(new THREE.Vector3());
                            editableObjects.push(n);
                            loadedCount++;
                        } else if (n.type === 'Group' || n.type === 'Object3D') {
                            n.userData = n.userData || {};
                            n.userData.isLocked = false;
                            editableObjects.push(n);
                        }
                    }
                });
                if(node.isMesh) enforceGroundConstraint(node);
            });

            if (loadedCount > 0 || nodesToProcess.length > 0) {
                showInfoText(`Objekte geladen.`, 4000);
                updateTreeView(); 
                saveState();
            } else { showInfoText("Keine Objekte gefunden.", 5000); }
        }

        function loadGLB(event) {
            const file = event.target.files[0]; if (!file) return;
            const reader = new FileReader();
            reader.onload = (e) => {
                const contents = e.target.result; const loader = new GLTFLoader();
                loader.parse(contents, '', (gltf) => processLoadedScene(gltf, file.name), (error) => { console.error('Fehler:', error); showInfoText("Fehler beim Laden.", 5000); });
            };
            reader.readAsArrayBuffer(file); ui.glbFileInput.value = '';
        }

        // --- EXPORT & SAVE LOGIC ---
        function exportGLB() { 
            performExport(false);
        }

        function saveToServer() {
            if(!confirm("Möchtest du das Live-Modell auf dem Server überschreiben? Ein Backup der alten Version wird erstellt.")) return;
            showInfoText("Speichere auf Server...", 5000);
            performExport(true);
        }

        function performExport(uploadToServer) {
            if (editableObjects.length === 0) { alert("Die Szene ist leer."); return; } 
            const exportCandidates = editableObjects.filter(obj => obj.parent === scene);
            if(exportCandidates.length === 0) { alert("Keine exportierbaren Objekte."); return; }

            // Reset materials for clean export
            editableObjects.forEach(obj => { if(obj.userData.originalMaterial) obj.material = obj.userData.originalMaterial }); 

            new GLTFExporter().parse(exportCandidates, result => { 
                const blob = new Blob([result], { type: 'application/octet-stream' }); 

                if (uploadToServer) {
                    const formData = new FormData();
                    formData.append('file', blob);

                    fetch('/api/facility/save_model', {
                        method: 'POST',
                        body: formData
                    })
                    .then(response => response.json())
                    .then(data => {
                        if(data.status === 'success') {
                            if(confirm("Speichern erfolgreich! Zurück zum Viewer?")) {
                                window.location.href = "{{ url_for('serve_facility_viewer') }}";
                            } else {
                                showInfoText("Live-Modell aktualisiert.", 3000);
                            }
                        } else {
                            alert("Fehler beim Speichern: " + data.message);
                        }
                    })
                    .catch(err => {
                        console.error(err);
                        alert("Netzwerkfehler beim Speichern.");
                    })
                    .finally(() => {
                         // Restore highlight if selected
                        if (primarySelection) primarySelection.material = primaryHighlightMaterial; 
                        if (targetSelection) targetSelection.material = targetHighlightMaterial; 
                    });
                } else {
                    // Download
                    let fileName = ui.exportFilename.value.trim();
                    if (!fileName) fileName = 'layout';
                    if (!fileName.toLowerCase().endsWith('.glb')) fileName += '.glb';

                    const link = document.createElement('a'); 
                    link.href = URL.createObjectURL(blob); 
                    link.download = fileName; 
                    link.click(); 
                    URL.revokeObjectURL(link.href); 

                    if (primarySelection) primarySelection.material = primaryHighlightMaterial; 
                    if (targetSelection) targetSelection.material = targetHighlightMaterial; 
                }
            }, error => console.error('Export fehlgeschlagen:', error), { binary: true }); 
        }

        function enforceGroundConstraint(object) { 
            const obj = object || transformControls.object; 
            if (!obj) return; 

            if (obj.parent && obj.parent !== scene) {
                if (!obj.parent.geometry.boundingBox) obj.parent.geometry.computeBoundingBox();
                if (!obj.geometry.boundingBox) obj.geometry.computeBoundingBox();

                const parentFloorY = obj.parent.geometry.boundingBox.min.y;
                const childHalfHeight = (obj.geometry.boundingBox.max.y - obj.geometry.boundingBox.min.y) * obj.scale.y * 0.5;
                const childBottomY = obj.position.y - childHalfHeight;

                if (childBottomY < parentFloorY) {
                    obj.position.y = parentFloorY + childHalfHeight;
                }
            } 
            else {
                obj.updateWorldMatrix(true, false);
                const boundingBox = new THREE.Box3().setFromObject(obj);
                const groundLevel = 0;
                if (boundingBox.min.y < groundLevel) {
                    obj.position.y += groundLevel - boundingBox.min.y;
                }
            }
        }

        function onTransformEnd(event) { isScaling = false; preTransformData = {}; saveState(); }

        function addPrimitive(type) {
            let name = type.charAt(0).toUpperCase() + type.slice(1);
            let sizeScale = isInteriorMode ? 0.2 : 1;

            const mesh = new THREE.Mesh( 
                type === 'sphere' ? new THREE.SphereGeometry(1 * sizeScale, 32, 16) : 
                type === 'cylinder' ? new THREE.CylinderGeometry(1 * sizeScale, 1 * sizeScale, 2 * sizeScale, 32) : 
                new THREE.BoxGeometry(2 * sizeScale, 2 * sizeScale, 2 * sizeScale), 
                defaultMaterial.clone() 
            );
            mesh.name = `${name}_${editableObjects.length + 1}`;

            mesh.userData.originalMaterial = mesh.material; mesh.userData.isLocked = false;
            mesh.geometry.computeBoundingBox();
            mesh.userData.geometrySize = mesh.geometry.boundingBox.getSize(new THREE.Vector3());
            editableObjects.push(mesh); 

            if (isInteriorMode && interiorModeObject) {
                interiorModeObject.add(mesh);

                if (!interiorModeObject.geometry.boundingBox) interiorModeObject.geometry.computeBoundingBox();
                const parentFloorY = interiorModeObject.geometry.boundingBox.min.y;
                const childHalfHeight = mesh.userData.geometrySize.y * mesh.scale.y * 0.5;

                mesh.position.set(0, parentFloorY + childHalfHeight, 0);
            } else {
                scene.add(mesh); 
                mesh.position.set(0, 0, 0);
            }

            selectObject(mesh, null); enforceGroundConstraint(mesh); updateTreeView(); saveState();
        }

        function removeObjectAndChildren(object) {
            while (object.children.length > 0) {
                const child = object.children[0];
                if (child === interiorGridHelper || child === floorplanMesh) {
                    object.remove(child); 
                    continue;
                }
                removeObjectAndChildren(child);
            }
            object.geometry.dispose();
            if (Array.isArray(object.material)) { object.material.forEach(m => m.dispose()); } else { object.material.dispose(); }
            if(object.userData.originalMaterial) object.userData.originalMaterial.dispose();

            const index = editableObjects.findIndex(obj => obj.uuid === object.uuid);
            if (index > -1) editableObjects.splice(index, 1);

            if (object.parent) object.parent.remove(object);
        }
        function deleteObject(objectToDelete, doSaveState = true) {
            if (!objectToDelete) return; if (isInteriorMode && objectToDelete === interiorModeObject) exitInteriorMode();
            if (transformControls.object && (transformControls.object === objectToDelete || transformControls.object.parent === objectToDelete)) transformControls.detach();
            if (primarySelection === objectToDelete) primarySelection = null; if (targetSelection === objectToDelete) targetSelection = null;

            removeObjectAndChildren(objectToDelete);

            if (doSaveState) saveState();
            updateTreeView(); updateSelectionUI();
        }

        function alignObjects(axis, mode) {
            if (!primarySelection || !targetSelection) return;

            let primaryBox, targetBox;
            const isLocalMode = primarySelection.parent && primarySelection.parent !== scene;

            if (isLocalMode) {
                primarySelection.updateMatrix();
                targetSelection.updateMatrix();

                if (!primarySelection.geometry.boundingBox) primarySelection.geometry.computeBoundingBox();
                if (!targetSelection.geometry.boundingBox) targetSelection.geometry.computeBoundingBox();

                primaryBox = primarySelection.geometry.boundingBox.clone();
                primaryBox.applyMatrix4(primarySelection.matrix);

                targetBox = targetSelection.geometry.boundingBox.clone();
                targetBox.applyMatrix4(targetSelection.matrix);
            } else {
                primaryBox = new THREE.Box3().setFromObject(primarySelection);
                targetBox = new THREE.Box3().setFromObject(targetSelection);
            }

            const primarySize = primaryBox.getSize(new THREE.Vector3()); 
            const primaryCenter = primaryBox.getCenter(new THREE.Vector3());
            const targetCenter = targetBox.getCenter(new THREE.Vector3()); 
            const positionOffset = primaryCenter.clone().sub(primarySelection.position);

            let targetPrimaryCenterValue;
            switch (mode) {
                case 'center': targetPrimaryCenterValue = targetCenter[axis]; break;
                case 'min-inner': targetPrimaryCenterValue = targetBox.min[axis] + primarySize[axis] / 2; break;
                case 'max-inner': targetPrimaryCenterValue = targetBox.max[axis] - primarySize[axis] / 2; break;
                case 'min-outer': targetPrimaryCenterValue = targetBox.min[axis] - primarySize[axis] / 2; break;
                case 'max-outer': targetPrimaryCenterValue = targetBox.max[axis] + primarySize[axis] / 2; break;
            }

            if (targetPrimaryCenterValue === undefined) return;
            primarySelection.position[axis] = targetPrimaryCenterValue - positionOffset[axis];
            enforceGroundConstraint(primarySelection);
        }

        function enterInteriorMode(object) {
            if (!object) return; isInteriorMode = true; interiorModeObject = object;
            const descendants = [];
            object.traverse(child => descendants.push(child));

            editableObjects.forEach(obj => { obj.visible = descendants.includes(obj); });

            transformControls.setSpace('local'); 

            originalGridSize = parseFloat(ui.gridSizeInput.value) || 1;
            gridSize = parseFloat((originalGridSize / 10).toPrecision(4));
            ui.gridSizeInput.value = gridSize; 
            transformControls.setTranslationSnap(gridSize);

            gridHelper.visible = false; 
            const floorplanGeo = new THREE.PlaneGeometry(1, 1); const floorplanMat = new THREE.MeshBasicMaterial({ color: 0xffa500, transparent: true, opacity: 0.5, side: THREE.DoubleSide });
            floorplanMesh = new THREE.Mesh(floorplanGeo, floorplanMat); floorplanMesh.rotation.x = -Math.PI / 2; 

            interiorModeObject.add(floorplanMesh); 

            updateFloorplan();
            const box = new THREE.Box3().setFromObject(object); const center = box.getCenter(new THREE.Vector3()); const size = box.getSize(new THREE.Vector3()); const maxDim = Math.max(size.x, size.y, size.z);
            orbitControls.target.copy(center); targetPosition = new THREE.Vector3(center.x, center.y + maxDim * 0.75, center.z + maxDim * 1.5); targetLookAt = center.clone(); isAnimatingCamera = true;
            showInfoText("Innenraum-Modus: Gitter ist 10x feiner.", 4000);
            selectObject(interiorModeObject, null);

            if(ui.btnSaveServer) {
                ui.btnSaveServer.disabled = true;
                ui.btnSaveServer.title = "Speichern im Innenraum-Modus nicht möglich";
                ui.btnSaveServer.classList.add('opacity-50', 'cursor-not-allowed');
            }
        }

        function exitInteriorMode() {
            if (!isInteriorMode) return;

            if (floorplanMesh) { floorplanMesh.parent.remove(floorplanMesh); floorplanMesh.geometry.dispose(); floorplanMesh.material.dispose(); floorplanMesh = null; }
            if (interiorGridHelper) { interiorGridHelper.parent.remove(interiorGridHelper); interiorGridHelper.geometry.dispose(); interiorGridHelper.material.dispose(); interiorGridHelper = null; }

            editableObjects.forEach(obj => { obj.visible = true; });

            transformControls.setSpace('world');

            gridSize = originalGridSize; 
            ui.gridSizeInput.value = gridSize; 
            transformControls.setTranslationSnap(gridSize);

            gridHelper.visible = true;

            isInteriorMode = false; 
            interiorModeObject = null;

            selectObject(null, null); 
            transformControls.detach();

            showInfoText("Innenraum-Modus deaktiviert.", 3000);
            if(ui.btnSaveServer) {
                ui.btnSaveServer.disabled = false;
                ui.btnSaveServer.title = "";
                ui.btnSaveServer.classList.remove('opacity-50', 'cursor-not-allowed');
            }
        }

        function updateFloorplan() {
            if (!isInteriorMode || !interiorModeObject) return;
            if (!interiorModeObject.geometry.boundingBox) interiorModeObject.geometry.computeBoundingBox();
            const bbox = interiorModeObject.geometry.boundingBox;
            const sizeX = bbox.max.x - bbox.min.x;
            const sizeZ = bbox.max.z - bbox.min.z;
            const localY = bbox.min.y;

            if(floorplanMesh) { 
                floorplanMesh.scale.set(sizeX, sizeZ, 1); 
                floorplanMesh.position.set(0, localY + 0.01, 0); 
            }
            if (interiorGridHelper) { 
                interiorGridHelper.parent.remove(interiorGridHelper); 
                interiorGridHelper.geometry.dispose(); 
                interiorGridHelper.material.dispose(); 
            }

            const interiorGridSize = Math.max(sizeX, sizeZ); 
            const divisions = Math.max(2, Math.round(interiorGridSize / gridSize));
            interiorGridHelper = new THREE.GridHelper(interiorGridSize, divisions, 0xffffff, 0xaaaaaa); 

            interiorGridHelper.position.set(0, localY + 0.02, 0);
            interiorModeObject.add(interiorGridHelper); 
        }

        function applyBooleanOperation(operation) { if (isInteriorMode) exitInteriorMode(); if (!primarySelection || !targetSelection) { showInfoText("Bitte wählen Sie genau zwei Objekte aus."); return; } primarySelection.updateMatrix(); targetSelection.updateMatrix(); let resultMesh; try { let csgResult; switch (operation) { case 'union': csgResult = CSG.union(primarySelection, targetSelection); break; case 'subtract': csgResult = CSG.subtract(primarySelection, targetSelection); break; case 'intersect': csgResult = CSG.intersect(primarySelection, targetSelection); break; default: throw new Error("Unbekannte Operation"); } resultMesh = csgResult; resultMesh.material = defaultMaterial.clone(); } catch (e) { console.error("Boolesche Operation fehlgeschlagen:", e); showInfoText("Operation fehlgeschlagen.", 5000); return; } const name = `${operation.charAt(0).toUpperCase() + operation.slice(1)}_${editableObjects.length + 1}`; resultMesh.name = name; resultMesh.userData.originalMaterial = resultMesh.material; resultMesh.userData.isLocked = false; resultMesh.geometry.computeBoundingBox(); resultMesh.userData.geometrySize = resultMesh.geometry.boundingBox.getSize(new THREE.Vector3()); const oldP = primarySelection; const oldT = targetSelection; deleteObject(oldP, false); deleteObject(oldT, false); scene.add(resultMesh); editableObjects.push(resultMesh); selectObject(resultMesh, null); enforceGroundConstraint(resultMesh); updateTreeView(); saveState(); }

        function updatePropertiesPanel() { 
            if (primarySelection) { 
                ui.propertiesPanel.style.display = 'block'; 
                const size = primarySelection.userData.geometrySize.clone().multiply(primarySelection.scale); 
                ui.propSizeX.value = size.x.toFixed(3); 
                ui.propSizeY.value = size.y.toFixed(3); 
                ui.propSizeZ.value = size.z.toFixed(3); 

                const interiorModeGroup = ui.btnInteriorMode.parentElement; 
                if (interiorModeGroup) { 
                    interiorModeGroup.style.display = (primarySelection && !targetSelection) ? 'block' : 'none'; 
                    if (isInteriorMode) {
                        ui.btnInteriorMode.innerHTML = `<i data-lucide="scan-off"></i>Modus verlassen`;
                    } else {
                        ui.btnInteriorMode.innerHTML = `<i data-lucide="scan"></i>Innenraum-Modus`;
                    }
                    lucide.createIcons({ nodes: [ui.btnInteriorMode] });
                } 
            } else { 
                ui.propertiesPanel.style.display = 'none'; 
            } 
        }

        function selectObject(primary, target) {
            if (isInteriorMode) {
                let current = primary; let isInGroup = false;
                while(current) { if(current === interiorModeObject) { isInGroup = true; break; } current = current.parent; }
                if (primary && !isInGroup) {
                    // Option: Selection outside group ignored
                }
            }

            if (primary && primary.userData.isLocked) return;

            if (primarySelection) primarySelection.material = primarySelection.userData.originalMaterial; 
            if (targetSelection) targetSelection.material = targetSelection.userData.originalMaterial; 

            primarySelection = primary; 
            targetSelection = target;

            if (primarySelection) { 
                // ### FIX: Check gizmoEnabled flag ###
                if (isInteriorMode && primarySelection === interiorModeObject) {
                    primarySelection.material = primaryHighlightMaterial; 
                    transformControls.detach(); 
                } else if (gizmoEnabled) {
                    primarySelection.material = primaryHighlightMaterial; 
                    transformControls.attach(primarySelection); 
                } else {
                    // Gizmo disabled (Select Mode)
                    primarySelection.material = primaryHighlightMaterial;
                    transformControls.detach();
                }
            } else { 
                transformControls.detach(); 
            }

            if (targetSelection) { targetSelection.material = targetHighlightMaterial; } 
            updateSelectionUI(); updateTreeView(); updatePropertiesPanel();

            if (ui.alignmentPanel.style.display === 'block') {
                requestAnimationFrame(() => lucide.createIcons({ nodes: [ui.alignmentPanel] }));
            }
        }

        function onTransformStart(event) { const object = transformControls.object; if (!object) return; if (transformControls.mode === 'scale') { isScaling = true; preTransformData = { position: object.position.clone(), scale: object.scale.clone() }; showInfoText("Halte SHIFT für symmetrische Skalierung"); } }
        function onTransform() { const object = transformControls.object; if (!object || !isScaling || isShiftDown || !object.userData.geometrySize) return; const geoSize = object.userData.geometrySize; const initialSize = geoSize.clone().multiply(preTransformData.scale); const currentSize = geoSize.clone().multiply(object.scale); const deltaSize = currentSize.clone().sub(initialSize); const positionOffset = deltaSize.clone().divideScalar(2); positionOffset.applyQuaternion(object.quaternion); object.position.copy(preTransformData.position).add(positionOffset); }
        function showInfoText(text, duration = 4000) { ui.infoText.textContent = text; ui.infoText.classList.add('visible'); setTimeout(() => ui.infoText.classList.remove('visible'), duration); }
        function updateGrid() { if (gridHelper) scene.remove(gridHelper); const totalGridSize = 200; const divisions = totalGridSize / gridSize; gridHelper = new THREE.GridHelper(totalGridSize, divisions, 0xaaaaaa, 0xdddddd); scene.add(gridHelper); }
        function initAxisHelper() { axisScene = new THREE.Scene(); axisCamera = new THREE.PerspectiveCamera(50, 1, 0.1, 1000); axisCamera.up = camera.up; const axes = new THREE.AxesHelper(5); axes.material.linewidth = 3; axisScene.add(axes); const sphereGeo = new THREE.SphereGeometry(0.5, 16, 8); const makeAxisEnd = (name, position, color) => { const mesh = new THREE.Mesh(sphereGeo, new THREE.MeshBasicMaterial({ color, toneMapped: false })); mesh.name = name; mesh.position.copy(position); axisScene.add(mesh); axisElements.push(mesh); }; makeAxisEnd("axis_x", new THREE.Vector3(5, 0, 0), 0xff0000); makeAxisEnd("axis_y", new THREE.Vector3(0, 5, 0), 0x00ff00); makeAxisEnd("axis_z", new THREE.Vector3(0, 0, 5), 0x0000ff); }

        function updateSelectionUI() { 
            const hasTwoSelections = primarySelection && targetSelection; 
            ui.alignmentPanel.style.display = hasTwoSelections ? 'block' : 'none'; 
            ui.booleanOperationsPanel.style.display = hasTwoSelections ? 'block' : 'none'; 
            if (hasTwoSelections) { 
                document.getElementById('align-primary-name').textContent = primarySelection.name; 
                document.getElementById('align-target-name').textContent = targetSelection.name; 
                requestAnimationFrame(() => lucide.createIcons({ nodes: [ui.alignmentPanel] }));
            } 
        }

        function toggleLock(object) { object.userData.isLocked = !object.userData.isLocked; if (object.userData.isLocked && primarySelection === object) { selectObject(null, null); } updateTreeView(); }
        function startRename(object, labelElement) { labelElement.innerHTML = `<input type="text" class="nodename-input-inline" value="${object.name}">`; const input = labelElement.querySelector('input'); input.focus(); input.select(); const finishEdit = () => { const newName = input.value.trim(); if (newName) object.name = newName; input.removeEventListener('blur', finishEdit); updateTreeView(); saveState(); }; input.addEventListener('blur', finishEdit); input.addEventListener('keydown', (e) => { if (e.key === 'Enter') finishEdit(); if(e.key === 'Escape') { input.removeEventListener('blur', finishEdit); updateTreeView(); } }); }
        function duplicateSelected() {
            if (!primarySelection) return;
            const original = primarySelection;
            const clone = original.clone(true); 
            clone.traverse((node) => {
                if (node.isMesh) {
                    node.name = node.name + "_Kopie";
                    node.material = defaultMaterial.clone();
                    node.userData = JSON.parse(JSON.stringify(node.userData));
                    node.userData.originalMaterial = node.material;
                    node.userData.isLocked = false;
                    node.uuid = THREE.MathUtils.generateUUID(); 
                    if (!node.geometry.boundingBox) node.geometry.computeBoundingBox();
                    editableObjects.push(node);
                }
            });
            clone.position.x += 2; clone.position.z += 2;
            if (original.parent) { original.parent.add(clone); } else { scene.add(clone); }
            enforceGroundConstraint(clone); selectObject(clone, null); updateTreeView(); saveState(); showInfoText("Objekt dupliziert", 2000);
        }

        function focusCameraOnSelection() {
            if (!primarySelection) return;
            const box = new THREE.Box3().setFromObject(primarySelection);
            if (box.isEmpty()) return;
            const center = box.getCenter(new THREE.Vector3());
            const size = box.getSize(new THREE.Vector3());
            const maxDim = Math.max(size.x, size.y, size.z);
            targetLookAt = center.clone();
            const direction = camera.position.clone().sub(orbitControls.target).normalize();
            const distance = Math.max(maxDim * 2.5, 5); 
            targetPosition = center.clone().add(direction.multiplyScalar(distance));
            isAnimatingCamera = true; orbitControls.enabled = false;
        }

        function onKeyDown(event) { 
            if(event.ctrlKey && event.key.toLowerCase() === 'z') { event.preventDefault(); undo(); return; } 
            if((event.ctrlKey && event.key.toLowerCase() === 'y') || (event.ctrlKey && event.shiftKey && event.key.toLowerCase() === 'z')) { event.preventDefault(); redo(); return; } 
            if (document.activeElement.tagName === 'INPUT') return; 
            switch (event.key.toLowerCase()) { 
                case 'q': document.getElementById('btn-select').click(); break; 
                case 'w': document.getElementById('btn-translate').click(); break; 
                case 'e': document.getElementById('btn-rotate').click(); break; 
                case 'r': document.getElementById('btn-scale').click(); break; 
                case 'delete': case 'backspace': if(primarySelection) { deleteObject(primarySelection); } break; 
                case 'escape': selectObject(null, null); break; 
                case 'f': focusCameraOnSelection(); break;
                case 'd': if (event.ctrlKey) { event.preventDefault(); duplicateSelected(); } break;
            } 
        }
        function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }
        function animateCameraToView(axis) { const distance = camera.position.distanceTo(orbitControls.target); targetLookAt = new THREE.Vector3(0, 0, 0); if (axis === 'x') targetPosition = new THREE.Vector3(distance, 0, 0); else if (axis === 'y') targetPosition = new THREE.Vector3(0, distance, 0.01); else if (axis === 'z') targetPosition = new THREE.Vector3(0, 0, distance); isAnimatingCamera = true; orbitControls.enabled = false; }

        function buildTreeItemHTML(object) {
            const isLocked = object.userData.isLocked; const isActive = primarySelection && object.uuid === primarySelection.uuid;
            const lockIcon = isLocked ? 'lock' : 'unlock'; const lockTitle = isLocked ? 'Entsperren' : 'Sperren';
            const hierarchyIcon = object.parent !== scene ? `<span class="tree-item-icon"><i data-lucide="corner-down-right"></i></span>` : '';

            let html = `<li data-uuid="${object.uuid}" class="${isLocked ? 'locked' : ''}"><div class="tree-item ${isActive ? 'active' : ''}"><span class="tree-item-label">${hierarchyIcon}${object.name}</span><div class="tree-item-actions"><button class="btn-icon btn-rename" title="Umbenennen"><i data-lucide="pencil"></i></button><button class="btn-icon btn-lock ${isLocked ? 'locked' : ''}" title="${lockTitle}"><i data-lucide="${lockIcon}"></i></button><button class="btn-icon btn-delete-item" title="Löschen"><i data-lucide="trash-2"></i></button></div></div>`;

            const children = object.children.filter(child => editableObjects.includes(child));
            if (children.length > 0) {
                html += '<ul>';
                children.forEach(child => { html += buildTreeItemHTML(child); });
                html += '</ul>';
            }
            html += '</li>'; return html;
        }

        function updateTreeView() {
            const topLevelObjects = editableObjects.filter(obj => obj.parent === scene);
            ui.treeList.innerHTML = topLevelObjects.map(obj => buildTreeItemHTML(obj)).join('');
            lucide.createIcons({ nodes: [ui.treeList] });
        }

        function onPointerDown(event) { 
            if (isAnimatingCamera) return; 
            const size = 150; 
            const isClickOnAxisHelper = event.clientX > window.innerWidth - size && event.clientY < size; 
            if (isClickOnAxisHelper) { 
                pointer.x = ((event.clientX - (window.innerWidth - size)) / size) * 2 - 1; 
                pointer.y = -(event.clientY / size) * 2 + 1; 
                raycaster.setFromCamera(pointer, axisCamera); 
                const intersects = raycaster.intersectObjects(axisElements, false); 
                if (intersects.length > 0) { const axis = intersects[0].object.name.split('_')[1]; animateCameraToView(axis); } 
            } else { 
                if (transformControls.dragging) return; 
                pointer.x = (event.clientX / window.innerWidth) * 2 - 1; 
                pointer.y = - (event.clientY / window.innerHeight) * 2 + 1; 
                raycaster.setFromCamera(pointer, camera); 

                const intersects = raycaster.intersectObjects(editableObjects, false); 

                const object = intersects.length > 0 ? intersects[0].object : null; 
                if (object && object.userData.isLocked) return; 
                if (isShiftDown) { 
                    if (object && object !== primarySelection) selectObject(primarySelection, object); 
                    else selectObject(primarySelection, null); 
                } else { 
                    selectObject(object, null); 
                } 
            } 
        }
        function animate() { requestAnimationFrame(animate); if (isAnimatingCamera && targetPosition && targetLookAt) { camera.position.lerp(targetPosition, 0.1); orbitControls.target.lerp(targetLookAt, 0.1); if (camera.position.distanceTo(targetPosition) < 0.1) { camera.position.copy(targetPosition); orbitControls.target.copy(targetLookAt); targetPosition = null; targetLookAt = null; isAnimatingCamera = false; orbitControls.enabled = true; } } orbitControls.update(); renderer.autoClear = true; renderer.setViewport(0, 0, window.innerWidth, window.innerHeight); renderer.render(scene, camera); renderer.autoClear = false; renderer.clearDepth(); const size = 150; axisCamera.position.copy(camera.position).sub(orbitControls.target).setLength(15); axisCamera.lookAt(axisScene.position); renderer.setViewport(window.innerWidth - size, 0, size, size); renderer.render(axisScene, axisCamera); }

        init();
    </script>
</body>
</html>
"""
TRAINING_PAGE_HTML = """
<!DOCTYPE html>
<html lang="de">
<head>
    <meta charset="utf-8"/>
    <title>MBET - Training Center</title>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet"/>
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet"/>
    <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
    <style>
        body { font-family: 'Inter', sans-serif; }
        #sidebar { transition: width 0.3s ease-in-out; }
        #sidebar.is-collapsed { width: 5rem; }
        #sidebar.is-collapsed .sidebar-label, #sidebar.is-collapsed .sidebar-title, #sidebar.is-collapsed .user-info { display: none; }
        #sidebar.is-collapsed .sidebar-link, #sidebar.is-collapsed .sidebar-header, #sidebar.is-collapsed .user-profile { justify-content: center; }
        #sidebar.is-collapsed .sidebar-link .material-icons-outlined, #sidebar.is-collapsed .sidebar-header .material-icons-outlined { margin-right: 0; }
        .sidebar-label { transition: opacity 0.2s ease-in-out; }
        .sidebar-link.active { background-color: #e5e7eb; color: #0c4a6e; font-weight: 600; }
        .sidebar-link.active .material-icons-outlined { color: #0c4a6e; }
        .sidebar-separator { display: none; }
        #sidebar.is-collapsed .sidebar-separator { display: block; height: 1px; background-color: #e2e8f0; margin: 0.5rem 0.75rem; }
        .sidebar-link:hover { background-color: #f3f4f6; color: #0c4a6e; }
        .sidebar-link:hover .material-icons-outlined { color: #0c4a6e; }
    </style>
</head>
<body class="bg-white">
<div class="flex h-screen bg-white">

    {% include 'sidebar.html' %}

    <main class="flex-1 overflow-y-auto bg-neutral-50">
        <header class="flex items-center sticky top-0 z-50 justify-between whitespace-nowrap px-6 py-4 bg-white border-b border-slate-200">
             <h1 class="text-2xl font-bold tracking-tight text-slate-800">Training Center</h1>
        </header>

        <div class="p-4 sm:p-6 lg:p-8">

            <!-- Klassische Tab-Navigation -->
            <div class="mb-6">
                <nav class="flex space-x-6 border-b border-slate-200 overflow-x-auto" aria-label="Tabs">
                    <button onclick="switchView('dashboard', this)" id="tab-dashboard" class="px-1 pb-3 text-sm font-medium text-blue-600 border-b-2 border-blue-600 whitespace-nowrap">Terminübersicht</button>
                    {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) or 'TESTENGINEER' in session.get('roles', []) or 'MEASUREMENTENGINEER' in session.get('roles', [])  %}
                        <button onclick="switchView('catalog', this)" id="tab-catalog" class="px-1 pb-3 text-sm font-medium text-slate-500 hover:text-slate-700 border-b-2 border-transparent hover:border-slate-300 whitespace-nowrap">Trainings-Katalog</button>
                    {% endif %}    
                    {% if 'ADMIN' in session.get('roles', []) or 'PLANNER' in session.get('roles', []) or 'TESTCELLINSPECTOR' in session.get('roles', []) %}
                        <button onclick="switchView('relations', this)" class="px-1 pb-3 text-sm font-medium text-blue-600 border-b-2 border-blue-600 whitespace-nowrap" id="tab-relations">Buddy & Teams</button>
                    {% endif %}                
                </nav>
            </div>

            <!-- VIEW 1: DASHBOARD (Termine) -->
            <div id="view-dashboard" class="space-y-6">
                <div class="flex justify-between items-center">
                    <h2 class="text-lg font-bold text-slate-800">Geplante Sessions</h2>
                    <button onclick="openSessionModal()" class="bg-blue-600 text-white px-4 py-2 rounded-lg text-sm font-medium hover:bg-blue-700 flex items-center gap-2 shadow-sm">
                        <span class="material-icons-outlined text-base">event</span> Neue Session planen
                    </button>
                </div>
                <div id="sessions-list" class="space-y-4"></div>
            </div>

            <!-- VIEW 2: KATALOG -->
            <div id="view-catalog" class="hidden space-y-6">
                 <div class="flex justify-between items-center">
                    <h2 class="text-lg font-bold text-slate-800">Verfügbare Trainingsmodule</h2>
                    <button onclick="openCatalogModal()" class="bg-white border border-slate-300 text-slate-700 px-4 py-2 rounded-lg text-sm font-medium hover:bg-slate-50 flex items-center gap-2 shadow-sm">
                        <span class="material-icons-outlined text-base">add</span> Neues Modul
                    </button>
                </div>
                <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6" id="catalog-grid"></div>
            </div>

            <!-- VIEW 3: RELATIONS (Buddy) -->
            <div id="view-relations" class="hidden">
                <div class="bg-white rounded-xl shadow-sm border border-slate-200 overflow-hidden">
                    <div class="px-6 py-4 border-b border-slate-200 bg-slate-50/50">
                        <h3 class="font-bold text-slate-800">Buddy-System & Team-Zuordnung</h3>
                    </div>
                    <table class="w-full text-sm text-left">
                        <thead class="bg-slate-50 text-xs text-slate-500 uppercase border-b border-slate-200">
                            <tr>
                                <th class="px-6 py-4 font-bold">Mitarbeiter</th>
                                <th class="px-6 py-4 font-bold">Buddy (Mentor)</th>
                                <th class="px-6 py-4 font-bold">Präferenz-Gruppen (Teams)</th>
                                <th class="px-6 py-4 text-right">Status</th>
                            </tr>
                        </thead>
                        <tbody id="relations-table-body" class="divide-y divide-slate-100"></tbody>
                    </table>
                </div>
            </div>

        </div>
    </main>
</div>

<!-- CATALOG MODAL (Create & Edit) -->
<div id="catalogModal" class="hidden fixed inset-0 bg-gray-900 bg-opacity-50 z-[60] flex items-center justify-center backdrop-blur-sm">
    <div class="bg-white rounded-xl shadow-2xl w-full max-w-lg overflow-hidden">
        <div class="px-6 py-4 border-b border-slate-100 flex justify-between items-center bg-slate-50/50">
            <h3 class="font-bold text-lg text-slate-800" id="catModalTitle">Trainingsmodul</h3>
            <div class="flex gap-2">
                <button id="btnDeleteCatalog" onclick="deleteCatalogItem()" class="text-red-400 hover:text-red-600 hidden"><span class="material-icons-outlined">delete</span></button>
                <button onclick="document.getElementById('catalogModal').classList.add('hidden')" class="text-slate-400 hover:text-slate-600"><span class="material-icons-outlined">close</span></button>
            </div>
        </div>
        <div class="p-6 space-y-4">
            <input type="hidden" id="catId">
            <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Titel</label><input id="catTitle" class="w-full border-slate-300 rounded-md text-sm"></div>
            <div class="grid grid-cols-2 gap-4">
                <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Dauer (Std)</label><input type="number" id="catDuration" class="w-full border-slate-300 rounded-md text-sm"></div>
                <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Kategorie</label><select id="catCategory" class="w-full border-slate-300 rounded-md text-sm bg-white"><option>Allgemein</option><option>Technik</option><option>Sicherheit</option><option>Qualität</option></select></div>
            </div>
            <div><label class="block text-xs font-bold text-slate-500 uppercase mb-1">Beschreibung</label><textarea id="catDesc" class="w-full border-slate-300 rounded-md text-sm" rows="3"></textarea></div>
        </div>
            <div>
                <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Titelbild (Optional)</label>
                <div class="flex items-center gap-3">
                    <input type="file" id="catImageInput" class="block w-full text-sm text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-blue-50 file:text-blue-700 hover:file:bg-blue-100" accept="image/*">
                    <input type="hidden" id="catImagePath">
                    <img id="catImagePreview" class="h-10 w-10 object-cover rounded hidden">
                </div>
            </div>
        <div class="px-6 py-4 bg-slate-50 border-t border-slate-100 flex justify-end gap-3">
            <button onclick="document.getElementById('catalogModal').classList.add('hidden')" class="px-4 py-2 text-slate-600 hover:bg-white rounded-lg text-sm font-medium border border-transparent hover:border-slate-200">Abbrechen</button>
            <button onclick="saveCatalogItem()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium shadow-sm">Speichern</button>
        </div>
    </div>
</div>

<!-- HISTORY MODAL -->
<div id="historyModal" class="hidden fixed inset-0 bg-gray-900 bg-opacity-50 z-[70] flex items-center justify-center backdrop-blur-sm">
    <div class="bg-white rounded-xl shadow-2xl w-full max-w-2xl overflow-hidden max-h-[80vh] flex flex-col">
        <div class="px-6 py-4 border-b border-slate-100 flex justify-between items-center bg-slate-50/50 flex-shrink-0">
            <div>
                <h3 class="font-bold text-lg text-slate-800" id="histTitle">Verlauf</h3>
                <p class="text-xs text-slate-500 mt-1">Vergangene Sessions</p>
            </div>
            <button onclick="document.getElementById('historyModal').classList.add('hidden')" class="text-slate-400 hover:text-slate-600"><span class="material-icons-outlined">close</span></button>
        </div>
        <div class="p-6 overflow-y-auto custom-scrollbar flex-grow" id="historyList"></div>
    </div>
</div>

<!-- SESSION MODAL (View & Edit) -->
<div id="sessionModal" class="hidden fixed inset-0 bg-gray-900 bg-opacity-50 z-[60] flex items-center justify-center backdrop-blur-sm">
    <div class="bg-white rounded-xl shadow-2xl w-full max-w-lg overflow-hidden flex flex-col max-h-[90vh]">
        <!-- Header -->
        <div class="px-6 py-4 border-b border-slate-100 flex justify-between items-center bg-slate-50/50 flex-shrink-0">
            <h3 class="font-bold text-lg text-slate-800" id="sessModalTitle">Session Details</h3>
            <div class="flex gap-2 items-center">
                <button id="btnEditSession" onclick="switchToSessionEdit()" class="text-slate-400 hover:text-blue-600 p-1.5 rounded-full hover:bg-slate-100 transition-colors" title="Bearbeiten">
                    <span class="material-icons-outlined text-lg">edit</span>
                </button>
                <button onclick="document.getElementById('sessionModal').classList.add('hidden')" class="text-slate-400 hover:text-slate-600 p-1.5 rounded-full hover:bg-slate-100 transition-colors">
                    <span class="material-icons-outlined text-lg">close</span>
                </button>
            </div>
        </div>

        <!-- Content: Scrollbar -->
        <div class="p-6 overflow-y-auto custom-scrollbar flex-grow">
            
            <!-- VIEW MODE -->
            <div id="viewSessionContent" class="space-y-6">
                <!-- Titel & Zeit -->
                <div>
                    <h2 id="viewSessTitle" class="text-xl font-bold text-slate-900 mb-1"></h2>
                    <div class="flex items-center gap-4 text-sm text-slate-600">
                        <span class="flex items-center gap-1"><span class="material-icons-outlined text-base text-slate-400">event</span> <span id="viewSessDate"></span></span>
                        <span class="flex items-center gap-1"><span class="material-icons-outlined text-base text-slate-400">schedule</span> <span id="viewSessTime"></span></span>
                    </div>
                </div>

                <!-- Info Grid -->
                <div class="grid grid-cols-2 gap-4 bg-slate-50 p-4 rounded-lg border border-slate-100 text-sm">
                    <div>
                        <p class="text-xs font-bold text-slate-400 uppercase">Ort</p>
                        <p id="viewSessLocation" class="font-medium text-slate-700"></p>
                    </div>
                    <div>
                        <p class="text-xs font-bold text-slate-400 uppercase">Trainer</p>
                        <p id="viewSessTrainer" class="font-medium text-slate-700"></p>
                    </div>
                </div>

                <!-- Beschreibung -->
                <div>
                    <p class="text-xs font-bold text-slate-400 uppercase mb-1">Inhalt</p>
                    <p id="viewSessDesc" class="text-sm text-slate-600 leading-relaxed whitespace-pre-wrap"></p>
                </div>

                <!-- Teilnehmer -->
                <div>
                    <div class="flex justify-between items-center mb-2">
                        <p class="text-xs font-bold text-slate-400 uppercase">Teilnehmer</p>
                        <span id="viewSessCount" class="text-xs bg-slate-100 px-2 py-0.5 rounded text-slate-500 font-mono"></span>
                    </div>
                    <div id="viewSessParticipants" class="flex flex-wrap gap-2"></div>
                </div>
            </div>

            <!-- EDIT MODE (Formular) -->
            <div id="editSessionContent" class="space-y-4 hidden">
                <input type="hidden" id="sessId">
                <div>
                    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Training wählen</label>
                    <select id="sessCatalogId" class="w-full border-slate-300 rounded-md text-sm bg-white focus:ring-blue-500"></select>
                </div>
                <div class="grid grid-cols-2 gap-4">
                    <div>
                        <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Datum</label>
                        <input type="date" id="sessDate" class="w-full border-slate-300 rounded-md text-sm">
                    </div>
                    <div>
                        <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Uhrzeit</label>
                        <input type="time" id="sessTime" class="w-full border-slate-300 rounded-md text-sm">
                    </div>
                </div>
                <div>
                    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Ort</label>
                    <input id="sessLocation" class="w-full border-slate-300 rounded-md text-sm" placeholder="z.B. Schulungsraum 1">
                </div>
                <div>
                    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Trainer</label>
                    <select id="sessTrainer" class="w-full border-slate-300 rounded-md text-sm bg-white"></select>
                </div>
                <div>
                    <label class="block text-xs font-bold text-slate-500 uppercase mb-1">Teilnehmer (Mehrfachauswahl)</label>
                    <div id="sessParticipants" class="border border-slate-300 rounded-md max-h-32 overflow-y-auto p-2 bg-white space-y-1"></div>
                </div>
            </div>
        </div>

        <!-- Footer -->
        <div class="px-6 py-4 bg-slate-50 border-t border-slate-100 flex justify-between items-center flex-shrink-0" id="sessFooter">
            <!-- View Mode Footer -->
            <div id="viewSessFooter" class="w-full flex justify-end gap-3">
                <button id="btnJoinSession" onclick="toggleParticipation(true)" class="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 text-sm font-medium shadow-sm transition-colors hidden">Teilnehmen</button>
                <button id="btnLeaveSession" onclick="toggleParticipation(false)" class="px-4 py-2 bg-red-50 text-red-600 border border-red-200 rounded-lg hover:bg-red-100 text-sm font-medium transition-colors hidden">Absagen</button>
                <button onclick="document.getElementById('sessionModal').classList.add('hidden')" class="px-4 py-2 text-slate-600 hover:bg-white rounded-lg text-sm font-medium border border-transparent hover:border-slate-200">Schließen</button>
            </div>

            <!-- Edit Mode Footer -->
            <div id="editSessFooter" class="w-full flex justify-between gap-3 hidden">
                <button id="btnDeleteSession" onclick="deleteSession()" class="text-red-500 hover:text-red-700 font-medium text-sm flex items-center gap-1"><span class="material-icons-outlined text-base">delete</span> Löschen</button>
                <div class="flex gap-2">
                    <button onclick="switchToSessionView()" class="px-4 py-2 text-slate-600 hover:bg-white rounded-lg text-sm font-medium border border-transparent hover:border-slate-200">Abbrechen</button>
                    <button onclick="saveSession()" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 text-sm font-medium shadow-sm">Speichern</button>
                </div>
            </div>
        </div>
    </div>
</div>

<script>
    let users = {{ users | tojson }};
    let relations = {};
    let trainingData = { catalog: [], sessions: [] };
    const currentUserId = "{{ session.get('user_id', '').lower() }}"; // Template Variable
    
    document.addEventListener('DOMContentLoaded', () => {
        const sidebar = document.getElementById('sidebar');
        const sidebarToggleBtn = document.getElementById('sidebar-toggle');
        if(sidebar && sidebarToggleBtn) { 
            const toggleBtnIcon = sidebarToggleBtn.querySelector('.material-icons-outlined');
            const applyState = (isCollapsed) => {
                if (isCollapsed) { sidebar.classList.add('is-collapsed'); if(toggleBtnIcon) toggleBtnIcon.textContent = 'menu'; } 
                else { sidebar.classList.remove('is-collapsed'); if(toggleBtnIcon) toggleBtnIcon.textContent = 'chevron_left'; }
            };
            sidebarToggleBtn.addEventListener('click', () => { 
                const isNowCollapsed = !sidebar.classList.contains('is-collapsed');
                localStorage.setItem('sidebarCollapsed', isNowCollapsed); 
                applyState(isNowCollapsed);
            }); 
            if(localStorage.getItem('sidebarCollapsed') === 'true') { applyState(true); }
        }
        init();
    });

    async function init() {
        try {
            const relRes = await fetch('/api/training/relations');
            relations = await relRes.json();
            const trainRes = await fetch('/api/training/data');
            trainingData = await trainRes.json();
            if(!trainingData.catalog) trainingData.catalog = [];
            if(!trainingData.sessions) trainingData.sessions = [];
        } catch(e) {}
        renderRelations(); renderCatalog(); renderSessions();
    }

    function switchView(viewName, btn) {
        // 1. Content umschalten
        ['dashboard', 'catalog', 'relations'].forEach(v => {
            document.getElementById('view-' + v).classList.add('hidden');
            
            // Buttons zurücksetzen (Inaktiv-Style)
            const b = document.getElementById('tab-' + v);
            b.classList.remove('text-blue-600', 'border-blue-600');
            b.classList.add('text-slate-500', 'border-transparent', 'hover:text-slate-700', 'hover:border-slate-300');
        });
        
        // 2. Aktiven Content zeigen
        document.getElementById('view-' + viewName).classList.remove('hidden');
        
        // 3. Aktiven Button stylen
        // Falls btn nicht übergeben wurde (z.B. externer Aufruf), suchen wir ihn
        const activeBtn = btn || document.getElementById('tab-' + viewName);
        activeBtn.classList.remove('text-slate-500', 'border-transparent', 'hover:text-slate-700', 'hover:border-slate-300');
        activeBtn.classList.add('text-blue-600', 'border-blue-600');
    }

    // --- SESSIONS ---
    function renderSessions() {
        const list = document.getElementById('sessions-list');
        list.innerHTML = '';
        const today = new Date().toISOString().split('T')[0];

        // Nur Zukunft anzeigen (Vergangenheit im Archiv)
        const upcoming = trainingData.sessions.filter(s => s.date >= today);
        upcoming.sort((a,b) => new Date(a.date) - new Date(b.date));

        if (upcoming.length === 0) { list.innerHTML = '<div class="text-center py-12 bg-slate-50 rounded-xl border border-dashed border-slate-300 text-slate-400 italic">Keine anstehenden Termine.</div>'; return; }

        upcoming.forEach(sess => {
            const dateObj = new Date(sess.date);
            const month = dateObj.toLocaleString('de-DE', { month: 'short' }).toUpperCase();
            const day = dateObj.getDate();
            const catalogItem = trainingData.catalog.find(c => c.id == sess.catalogId) || { title: 'Unbekannt' };

            let avatars = '';
            sess.participants.slice(0, 5).forEach(pid => {
                avatars += `<div class="inline-flex h-6 w-6 items-center justify-center rounded-full ring-2 ring-white bg-slate-200 text-xs font-bold text-slate-600" title="${getUserName(pid)}">${getUserInitials(pid)}</div>`;
            });

            list.innerHTML += `
                <div class="flex items-start gap-4 p-4 bg-white rounded-lg border border-slate-200 shadow-sm hover:shadow-md transition-shadow cursor-pointer" onclick="openSessionModal('${sess.id}')">
                    <div class="flex-shrink-0 text-center w-12 bg-slate-50 rounded border border-slate-100 p-1">
                        <span class="block text-[10px] text-red-500 font-bold uppercase tracking-wider">${month}</span>
                        <span class="block text-xl font-bold text-slate-800 leading-none mt-1">${day}</span>
                    </div>
                    <div>
                        <h3 class="font-bold text-slate-800 text-sm">${catalogItem.title}</h3>
                        <p class="text-xs text-slate-500 mt-1 flex items-center gap-3">
                            <span><span class="material-icons-outlined text-[14px] align-text-bottom">schedule</span> ${sess.time}</span>
                            <span><span class="material-icons-outlined text-[14px] align-text-bottom">place</span> ${sess.location}</span>
                            <span><span class="material-icons-outlined text-[14px] align-text-bottom">person</span> ${getUserName(sess.trainerId)}</span>
                        </p>
                        <div class="mt-2 flex -space-x-2 overflow-hidden items-center">${avatars}</div>
                    </div>
                </div>`;
        });
    }

    function openSessionModal(sessId = null) {
        // Daten vorbereiten (Selects füllen) wie bisher...
        const catSelect = document.getElementById('sessCatalogId'); catSelect.innerHTML = '';
        trainingData.catalog.forEach(c => catSelect.innerHTML += `<option value="${c.id}">${c.title}</option>`);
        const trainerSelect = document.getElementById('sessTrainer'); trainerSelect.innerHTML = '';
        users.forEach(u => trainerSelect.innerHTML += `<option value="${u.id}">${u.nachname}, ${u.vorname}</option>`);

        if(sessId) {
            // EXISTING -> View Mode
            const sess = trainingData.sessions.find(s => s.id == sessId);
            if(sess) {
                // Populate View
                const catItem = trainingData.catalog.find(c => c.id == sess.catalogId) || {};
                document.getElementById('viewSessTitle').textContent = catItem.title || 'Unbekannt';
                document.getElementById('viewSessDesc').textContent = catItem.description || '';
                
                const d = new Date(sess.date);
                document.getElementById('viewSessDate').textContent = d.toLocaleDateString('de-DE', { weekday:'long', day:'2-digit', month:'long', year:'numeric' });
                document.getElementById('viewSessTime').textContent = sess.time + ' Uhr';
                document.getElementById('viewSessLocation').textContent = sess.location || '-';
                document.getElementById('viewSessTrainer').textContent = getUserName(sess.trainerId);
                
                // Teilnehmer Liste View
                const pContainer = document.getElementById('viewSessParticipants');
                pContainer.innerHTML = '';
                (sess.participants || []).forEach(pid => {
                    pContainer.innerHTML += `<span class="inline-flex items-center px-2 py-1 rounded bg-slate-100 text-slate-600 text-xs border border-slate-200">${getUserName(pid)}</span>`;
                });
                document.getElementById('viewSessCount').textContent = (sess.participants || []).length;

                // Buttons logik (Teilnehmen/Absagen)
                const amIParticipant = (sess.participants || []).includes(currentUserId);
                if(amIParticipant) {
                    document.getElementById('btnJoinSession').classList.add('hidden');
                    document.getElementById('btnLeaveSession').classList.remove('hidden');
                } else {
                    document.getElementById('btnJoinSession').classList.remove('hidden');
                    document.getElementById('btnLeaveSession').classList.add('hidden');
                }

                // Populate Edit Form (Hidden)
                document.getElementById('sessId').value = sess.id;
                document.getElementById('sessDate').value = sess.date;
                document.getElementById('sessTime').value = sess.time;
                document.getElementById('sessLocation').value = sess.location;
                document.getElementById('sessCatalogId').value = sess.catalogId;
                setTimeout(() => document.getElementById('sessTrainer').value = sess.trainerId, 0);
                
                // Teilnehmer Checkboxen (für Edit)
                const partList = document.getElementById('sessParticipants'); partList.innerHTML = '';
                users.forEach(u => {
                    const isChecked = (sess.participants || []).includes(u.id) ? 'checked' : '';
                    partList.innerHTML += `<label class="flex items-center space-x-2 p-1 hover:bg-slate-50 rounded cursor-pointer"><input type="checkbox" value="${u.id}" ${isChecked} class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-4 w-4"><span class="text-sm text-slate-700">${u.nachname}, ${u.vorname}</span></label>`;
                });

                switchToSessionView();
            }
        } else {
            // NEW -> Edit Mode direkt
            document.getElementById('sessId').value = '';
            document.getElementById('sessDate').value = new Date().toISOString().split('T')[0];
            
            // Checkboxen leer
            const partList = document.getElementById('sessParticipants'); partList.innerHTML = '';
            users.forEach(u => partList.innerHTML += `<label class="flex items-center space-x-2 p-1 hover:bg-slate-50 rounded cursor-pointer"><input type="checkbox" value="${u.id}" class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 h-4 w-4"><span class="text-sm text-slate-700">${u.nachname}, ${u.vorname}</span></label>`);
            
            switchToSessionEdit();
            document.getElementById('btnEditSession').classList.add('hidden'); // Kein zurück zu View bei Neu
        }
        
        document.getElementById('sessionModal').classList.remove('hidden');
    }
    function switchToSessionView() {
        document.getElementById('viewSessionContent').classList.remove('hidden');
        document.getElementById('editSessionContent').classList.add('hidden');
        document.getElementById('viewSessFooter').classList.remove('hidden');
        document.getElementById('editSessFooter').classList.add('hidden');
        document.getElementById('btnEditSession').classList.remove('hidden');
        document.getElementById('sessModalTitle').textContent = "Session Details";
    }

    function switchToSessionEdit() {
        document.getElementById('viewSessionContent').classList.add('hidden');
        document.getElementById('editSessionContent').classList.remove('hidden');
        document.getElementById('viewSessFooter').classList.add('hidden');
        document.getElementById('editSessFooter').classList.remove('hidden');
        document.getElementById('btnEditSession').classList.add('hidden');
        document.getElementById('sessModalTitle').textContent = "Session bearbeiten";
    }

    async function toggleParticipation(join) {
        const sessId = document.getElementById('sessId').value;
        const sess = trainingData.sessions.find(s => s.id == sessId);
        if(!sess) return;
        
        if(join) {
            if(!sess.participants.includes(currentUserId)) sess.participants.push(currentUserId);
        } else {
            sess.participants = sess.participants.filter(id => id !== currentUserId);
        }
        
        await saveTrainingData();
        // Reload Modal Data (einfacher: schließe und öffne neu oder update view)
        openSessionModal(sessId); 
        renderSessions();
    }
    async function saveSession() {
        const id = document.getElementById('sessId').value;
        const parts = Array.from(document.querySelectorAll('#sessParticipants input:checked')).map(cb => cb.value);
        const data = {
            id: id ? parseInt(id) : Date.now(),
            catalogId: document.getElementById('sessCatalogId').value,
            date: document.getElementById('sessDate').value,
            time: document.getElementById('sessTime').value,
            location: document.getElementById('sessLocation').value,
            trainerId: document.getElementById('sessTrainer').value,
            participants: parts
        };

        if(id) {
            const idx = trainingData.sessions.findIndex(s => s.id == id);
            if(idx > -1) trainingData.sessions[idx] = data;
        } else {
            trainingData.sessions.push(data);
        }

        await saveTrainingData();
        document.getElementById('sessionModal').classList.add('hidden');
        renderSessions();
    }

    async function deleteSession() {
        const id = document.getElementById('sessId').value;
        if(confirm("Session löschen?")) {
            trainingData.sessions = trainingData.sessions.filter(s => s.id != id);
            await saveTrainingData();
            document.getElementById('sessionModal').classList.add('hidden');
            renderSessions();
        }
    }

    // --- CATALOG LOGIC ---
    function renderCatalog() {
        const grid = document.getElementById('catalog-grid'); grid.innerHTML = '';
        if(trainingData.catalog.length === 0) { grid.innerHTML = '<div class="col-span-full text-center py-12 bg-slate-50 rounded-xl border border-dashed border-slate-300 text-slate-400">Keine Trainings.</div>'; return; }

        trainingData.catalog.forEach(item => {
            grid.innerHTML += `
                <div class="bg-white p-6 rounded-xl shadow-sm border border-slate-200 hover:shadow-md transition-shadow group flex flex-col h-full relative overflow-hidden">
                    <div class="absolute top-0 left-0 w-1 h-full bg-blue-500"></div>
                    <div class="flex justify-between items-start mb-3 pl-2">
                        <span class="px-2 py-0.5 rounded text-[10px] font-bold bg-blue-50 text-blue-700 uppercase tracking-wider border border-blue-100">${item.category || 'Allgemein'}</span>
                        <div class="flex gap-1">
                            <button onclick="openHistoryModal('${item.id}', '${item.title}')" class="text-slate-300 hover:text-purple-600 transition-colors" title="Historie"><span class="material-icons-outlined text-lg">history</span></button>
                            <button onclick="openCatalogModal('${item.id}')" class="text-slate-300 hover:text-blue-600 transition-colors"><span class="material-icons-outlined text-lg">edit</span></button>
                        </div>
                    </div>
                    <h3 class="font-bold text-slate-800 text-lg mb-2 pl-2">${item.title}</h3>
                    <p class="text-sm text-slate-500 line-clamp-3 mb-6 flex-grow pl-2">${item.description}</p>
                    <div class="pt-4 border-t border-slate-100 flex justify-between items-center text-xs text-slate-400 pl-2">
                        <span class="flex items-center gap-1"><span class="material-icons-outlined text-sm">schedule</span> ${item.duration || '?'} Std</span>
                        <button onclick="switchView('dashboard'); openSessionModal()" class="flex items-center gap-1 hover:text-blue-600 transition-colors font-medium">Planen <span class="material-icons-outlined text-sm">arrow_forward</span></button>
                    </div>
                </div>`;
        });
    }

    function openCatalogModal(id = null) {
        document.getElementById('catId').value = '';
        document.getElementById('catTitle').value = '';
        document.getElementById('catDesc').value = '';
        document.getElementById('catDuration').value = '';
        document.getElementById('catModalTitle').textContent = "Neues Training";
        document.getElementById('btnDeleteCatalog').classList.add('hidden');
        document.getElementById('catImageInput').value = ''; // Reset File Input
        document.getElementById('catImagePath').value = '';
        document.getElementById('catImagePreview').classList.add('hidden');

        if(id) {
            const item = trainingData.catalog.find(c => c.id == id);
            if(item) {
                document.getElementById('catId').value = item.id;
                document.getElementById('catTitle').value = item.title;
                document.getElementById('catDesc').value = item.description;
                document.getElementById('catDuration').value = item.duration;
                document.getElementById('catCategory').value = item.category;
                document.getElementById('catModalTitle').textContent = "Training bearbeiten";
                document.getElementById('btnDeleteCatalog').classList.remove('hidden');
            }
            if(item.image) {
                document.getElementById('catImagePath').value = item.image;
                document.getElementById('catImagePreview').src = item.image;
                document.getElementById('catImagePreview').classList.remove('hidden');
        }
        }
        document.getElementById('catalogModal').classList.remove('hidden');
    }

    async function saveCatalogItem() {
        const id = document.getElementById('catId').value;
        // 1. Upload zuerst
        const fileInput = document.getElementById('catImageInput');
        let imagePath = document.getElementById('catImagePath').value;
        
        if(fileInput.files.length > 0) {
            const formData = new FormData();
            formData.append('file', fileInput.files[0]);
            try {
                const res = await fetch('/api/training/upload', { method: 'POST', body: formData });
                if(res.ok) {
                    const data = await res.json();
                    imagePath = data.path;
                }
            } catch(e) { console.error("Upload fail"); }
        }
        
        // 2. Speichern mit Bild-Pfad
        const data = {
            id: id ? parseInt(id) : Date.now(),
            title: document.getElementById('catTitle').value,
            description: document.getElementById('catDesc').value,
            duration: document.getElementById('catDuration').value,
            category: document.getElementById('catCategory').value,
            image: imagePath
        };

        if(id) {
            const idx = trainingData.catalog.findIndex(c => c.id == id);
            if(idx > -1) trainingData.catalog[idx] = data;
        } else {
            trainingData.catalog.push(data);
        }
        await saveTrainingData();
        document.getElementById('catalogModal').classList.add('hidden');
        renderCatalog();
    }

    async function deleteCatalogItem() {
        const id = document.getElementById('catId').value;
        if(confirm("Training löschen?")) {
            trainingData.catalog = trainingData.catalog.filter(c => c.id != id);
            await saveTrainingData();
            document.getElementById('catalogModal').classList.add('hidden');
            renderCatalog();
        }
    }

    // --- HISTORY ---
    function openHistoryModal(catalogId, title) {
        document.getElementById('histTitle').textContent = title;
        const list = document.getElementById('historyList');
        list.innerHTML = '';

        // Finde vergangene Sessions
        const today = new Date().toISOString().split('T')[0];
        const pastSessions = trainingData.sessions.filter(s => s.catalogId == catalogId && s.date < today);
        pastSessions.sort((a,b) => new Date(b.date) - new Date(a.date)); // Neu -> Alt

        if(pastSessions.length === 0) {
            list.innerHTML = '<p class="text-slate-400 italic text-center">Keine vergangenen Sessions.</p>';
        } else {
            pastSessions.forEach(s => {
                const d = new Date(s.date).toLocaleDateString('de-DE');
                const parts = s.participants.map(id => getUserName(id)).join(', ');
                list.innerHTML += `
                    <div class="mb-4 pb-4 border-b border-slate-100 last:border-0">
                        <div class="flex justify-between font-bold text-slate-700 text-sm">
                            <span>${d}</span>
                            <span class="text-slate-500 font-normal">Trainer: ${getUserName(s.trainerId)}</span>
                        </div>
                        <div class="mt-2 text-xs text-slate-600">
                            <strong>Teilnehmer (${s.participants.length}):</strong> ${parts}
                        </div>
                    </div>`;
            });
        }
        document.getElementById('historyModal').classList.remove('hidden');
    }

    // --- COMMONS ---
    function renderRelations() {
        const tbody = document.getElementById('relations-table-body');
        tbody.innerHTML = '';
        users.forEach(u => {
            const rel = relations[u.id] || { buddy: '', groups: [] };
            let buddyOpt = `<option value="">- Kein Buddy -</option>`;
            users.forEach(b => { if(b.id !== u.id) { const sel = rel.buddy === b.id ? 'selected' : ''; buddyOpt += `<option value="${b.id}" ${sel}>${b.nachname}, ${b.vorname}</option>`; } });
            tbody.innerHTML += `<tr class="hover:bg-slate-50 transition-colors border-b border-slate-100 last:border-0"><td class="px-6 py-4 font-medium text-slate-800">${u.nachname}, ${u.vorname}</td><td class="px-6 py-4"><select class="border-slate-300 rounded-md text-sm w-full focus:ring-blue-500 focus:border-blue-500 bg-white py-1.5" onchange="updateRelation('${u.id}', 'buddy', this.value)">${buddyOpt}</select></td><td class="px-6 py-4"><input type="text" class="border-slate-300 rounded-md text-sm w-full focus:ring-blue-500 focus:border-blue-500 py-1.5" value="${rel.groups.join(', ')}" placeholder="Team A..." onblur="updateRelation('${u.id}', 'groups', this.value)"></td><td class="px-6 py-4 text-right"><span class="inline-flex items-center text-[10px] text-green-700 bg-green-50 px-2 py-0.5 rounded border border-green-200 uppercase tracking-wider font-bold">Active</span></td></tr>`;
        });
    }
    async function updateRelation(userId, key, value) {
        if(!relations[userId]) relations[userId] = { buddy: '', groups: [] };
        if(key === 'groups') relations[userId].groups = value.split(',').map(s => s.trim()).filter(s => s);
        else relations[userId][key] = value;
        await fetch('/api/training/relations', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(relations) });
    }
    async function saveTrainingData() { await fetch('/api/training/data', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(trainingData) }); }
    function getUserName(id) { const u = users.find(x => x.id === id); return u ? `${u.nachname}, ${u.vorname}` : id; }
    function getUserInitials(id) { const u = users.find(x => x.id === id); return u ? (u.vorname[0] + u.nachname[0]).toUpperCase() : '??'; }
</script>
</body>
</html>
"""

def sync_facility_names_to_maintenance(structure_data):
    """
    Synchronisiert Umbenennungen von Facility-Units (3D) zu den Wartungsdaten.
    Basis ist die UUID (facilityUnitId).
    """
    print("--- DEBUG: Starte Sync von Facility-Namen zu Maintenance ---")
    try:
        # 1. Map aufbauen: ID -> Voller Name (Index + Name)
        id_to_name_map = {}

        def build_map(nodes, prefix=""):
            for i, node in enumerate(nodes):
                current_index_str = f"{prefix}{i + 1}"
                full_name = f"{current_index_str} {node.get('name', '')}"
                if node.get('id'):
                    id_to_name_map[node['id']] = full_name

                if 'children' in node:
                    build_map(node['children'], current_index_str + ".")

        build_map(structure_data.get('betriebseinheiten', []))
        build_map(structure_data.get('gebaeude', []))

        # 2. Wartungsdaten laden
        m_data = load_maintenance_data()
        definitions = m_data.get('definitions', [])
        orders = m_data.get('orders', [])

        changes_defs = False
        changes_orders = False

        # 3. Definitionen aktualisieren
        for d in definitions:
            f_id = d.get('facilityUnitId')
            if f_id and f_id in id_to_name_map:
                current_name_in_def = d.get('plantIndex')
                new_name = id_to_name_map[f_id]

                if current_name_in_def != new_name:
                    print(f"INFO: Aktualisiere Wartungsplan {d.get('id')}: '{current_name_in_def}' -> '{new_name}'")
                    d['plantIndex'] = new_name
                    changes_defs = True

        # 4. Aufträge aktualisieren (Optional, aber gut für Konsistenz offener Aufträge)
        for o in orders:
            # Nur offene Aufträge aktualisieren? Oder alle?
            # Da wir hier UUIDs nutzen, ist es sicher, auch historische zu ändern,
            # aber für die Historie ist der alte Name oft besser.
            # Empfehlung: Nur offene (nicht Completed).
            if o.get('status') == 'Completed': continue

            f_id = o.get('facilityUnitId')
            if f_id and f_id in id_to_name_map:
                current_name = o.get('plantIndex')
                new_name = id_to_name_map[f_id]

                if current_name != new_name:
                    print(f"INFO: Aktualisiere Auftrag {o.get('id')}: '{current_name}' -> '{new_name}'")
                    o['plantIndex'] = new_name
                    changes_orders = True

        # 5. Speichern
        if changes_defs:
            save_maintenance_definitions(definitions)

        if changes_orders:
            save_maintenance_orders(orders)

        if changes_defs or changes_orders:
            print("--- DEBUG: Wartungsdaten erfolgreich synchronisiert. ---")
        else:
            print("--- DEBUG: Keine Namensänderungen erforderlich. ---")

    except Exception as e:
        print(f"!!! FEHLER beim Facility-Maintenance-Sync: {e}")
        traceback.print_exc()


def sync_trolleys_and_storage(hardware_data):
    """
    1. Aktualisiert Namen/Daten in Rüstwagen/Lagern basierend auf IDs (Sync).
    2. Entfernt Items aus alten Orten, wenn sie verschoben wurden (Clean-Up).
    3. Entfernt Items komplett, wenn sie im Master-Inventar gelöscht wurden (Hard Delete).
    """
    print("--- DEBUG: Starte Sync & Cleanup für Trolleys/Storage ---")

    trolleys = hardware_data.get('trolleys', {})
    storage = hardware_data.get('storage_units', {})
    inventory = hardware_data.get('inventory', {})

    id_to_part_map = {}
    id_to_location_map = {}

    for pn, part in inventory.items():
        for s in part.get('serials', []):
            if s.get('id'):
                id_to_part_map[s['id']] = {'name': part.get('name', ''), 'pn': pn, 'sn': s.get('sn', '')}
                id_to_location_map[s['id']] = s.get('location', '')

    changes = False

    def process_containers(containers_dict):
        nonlocal changes
        for container_name, container_data in containers_dict.items():
            new_items = []
            for item in container_data.get('items', []):
                item_id = item.get('id')

                # Fall 1: Manuelles Item (Keine ID) -> Behalten
                if not item_id:
                    new_items.append(item)
                    continue

                # Fall 2: Verknüpftes Item

                # Check A: Existiert das Item noch in der DB?
                if item_id not in id_to_part_map:
                    # --- ÄNDERUNG: HARD DELETE ---
                    # Item im Master gelöscht -> Hier auch löschen!
                    print(f"INFO: Entferne gelöschtes Item (ID: {item_id}) aus {container_name}")
                    changes = True
                    continue  # NICHT hinzufügen

                # Check B: Ist das Item HIER am richtigen Ort?
                real_location = id_to_location_map.get(item_id)
                if real_location != container_name:
                    print(f"INFO: Entferne Item {item.get('sn')} aus {container_name} (ist jetzt in {real_location})")
                    changes = True
                    continue

                    # Check C: Daten-Update
                live_data = id_to_part_map[item_id]
                if (item.get('name') != live_data['name'] or
                        item.get('pn') != live_data['pn'] or
                        item.get('sn') != live_data['sn']):
                    item['name'] = live_data['name']
                    item['pn'] = live_data['pn']
                    item['sn'] = live_data['sn']
                    changes = True

                new_items.append(item)

            container_data['items'] = new_items

    process_containers(trolleys)
    process_containers(storage)

    if changes:
        save_test_equipment_data_to_file(hardware_data)
        print("--- DEBUG: Trolleys/Storage erfolgreich synchronisiert und bereinigt. ---")

def sync_hardware_names_to_facility(hardware_data):
    """
    Aktualisiert Hardware-Namen in der ANLAGENSTRUKTUR (structure.json).
    Arbeitet rekursiv durch den Baum.
    """
    print("--- DEBUG: Starte Sync von Hardware-Namen zu Facility-Struktur ---")
    try:
        # 1. Lookup-Maps erstellen (Identisch zur anderen Sync-Funktion)
        id_to_label_map = {}
        sn_to_id_map = {}
        inventory = hardware_data.get('inventory', {})

        for pn, item in inventory.items():
            part_name = item.get('name', 'Unbekannt')
            for serial_entry in item.get('serials', []):
                hw_id = serial_entry.get('id')
                sn = serial_entry.get('sn')
                if hw_id and sn:
                    display_label = f"{sn} ({pn} - {part_name})"
                    id_to_label_map[hw_id] = display_label
                    sn_to_id_map[sn] = hw_id

        # 2. Struktur laden
        structure_data = {}
        if os.path.exists(FACILITY_STRUCTURE_FILE):
            with open(FACILITY_STRUCTURE_FILE, 'r', encoding='utf-8') as f:
                structure_data = json.load(f)
        else:
            return

        changes_made = False

        # Rekursive Funktion zum Aktualisieren eines Knotens und seiner Kinder
        def update_node_hardware(nodes):
            node_changed = False
            for node in nodes:
                # Hardware-Liste des aktuellen Knotens prüfen
                if 'hardware' in node and isinstance(node['hardware'], list):
                    new_hw_list = []
                    for hw_str in node['hardware']:
                        if not hw_str: continue

                        current_id = None
                        old_label = None

                        # A) JSON Parsing
                        if hw_str.strip().startswith('{'):
                            try:
                                asset_obj = json.loads(hw_str)
                                current_id = asset_obj.get('id')
                                old_label = asset_obj.get('label')
                            except:
                                pass

                        # B) Legacy Fallback
                        if not current_id:
                            old_label = hw_str
                            current_sn = hw_str.split(' ')[0]
                            current_id = sn_to_id_map.get(current_sn)

                        # Update Logik
                        if current_id and current_id in id_to_label_map:
                            new_label = id_to_label_map[current_id]
                            if new_label != old_label or not hw_str.strip().startswith('{'):
                                new_hw_json = json.dumps({"id": current_id, "label": new_label})
                                new_hw_list.append(new_hw_json)
                                node_changed = True
                                print(
                                    f"INFO: Aktualisiere Facility-Node '{node.get('name')}': '{old_label}' -> '{new_label}'")
                            else:
                                new_hw_list.append(hw_str)

                        elif current_id and current_id not in id_to_label_map:
                            node_changed = True
                            print(
                                f"INFO: Entferne gelöschtes Asset '{old_label}' aus Facility-Node '{node.get('name')}'")

                        else:
                            new_hw_list.append(hw_str)

                    if node_changed:
                        node['hardware'] = new_hw_list

                # Rekursion für Kinder
                if 'children' in node and isinstance(node['children'], list):
                    if update_node_hardware(node['children']):
                        node_changed = True

            return node_changed

        # 3. Beide Bäume durchlaufen
        if update_node_hardware(structure_data.get('betriebseinheiten', [])): changes_made = True
        if update_node_hardware(structure_data.get('gebaeude', [])): changes_made = True

        # 4. Speichern
        if changes_made:
            if save_json_file(FACILITY_STRUCTURE_FILE, structure_data):
                print("--- DEBUG: Facility-Struktur erfolgreich synchronisiert. ---")
            else:
                print("!!! FEHLER: Konnte Facility-Struktur nicht speichern.")
        else:
            print("--- DEBUG: Keine Änderungen in Facility-Struktur nötig. ---")

    except Exception as e:
        print(f"!!! FEHLER beim Facility-Sync: {e}")
        traceback.print_exc()

def sync_hardware_names_to_definitions(hardware_data):
    """
    (Version 2) Aktualisiert die Anzeigenamen in den Wartungs-DEFINITIONEN.
    Unterstützt sowohl neue JSON-String-Assets als auch alte Plain-String-Assets
    und migriert alte bei Bedarf.
    """
    print("--- DEBUG: Starte Sync von Hardware-Namen zu Wartungs-Definitionen (Version 2) ---")
    try:
        # 1. Zwei Lookup-Maps erstellen:
        # a) ID -> "SN (PN - Name)" (Primärer Mechanismus)
        # b) SN -> ID (Für die Migration von Altdaten)
        id_to_label_map = {}
        sn_to_id_map = {}
        inventory = hardware_data.get('inventory', {})

        for pn, item in inventory.items():
            part_name = item.get('name', 'Unbekannt')
            for serial_entry in item.get('serials', []):
                hw_id = serial_entry.get('id')
                sn = serial_entry.get('sn')
                if hw_id and sn:
                    display_label = f"{sn} ({pn} - {part_name})"
                    id_to_label_map[hw_id] = display_label
                    sn_to_id_map[sn] = hw_id

        # 2. Definitionen laden
        definitions_data = []
        if os.path.exists(MAINTENANCE_DEFINITIONS_FILE):
            with open(MAINTENANCE_DEFINITIONS_FILE, 'r', encoding='utf-8') as f:
                definitions_data = json.load(f)

        if not definitions_data:
            return

        changes_made = False

        # 3. Durch Definitionen iterieren und Assets prüfen/aktualisieren
        for definition in definitions_data:
            if 'maintenanceObjects' in definition and isinstance(definition['maintenanceObjects'], list):
                new_asset_list = []
                for asset_str in definition['maintenanceObjects']:
                    if not asset_str: continue

                    current_id = None
                    old_label = None

                    # A) Versuche, als neuer JSON-String zu parsen
                    if asset_str.strip().startswith('{'):
                        try:
                            asset_obj = json.loads(asset_str)
                            current_id = asset_obj.get('id')
                            old_label = asset_obj.get('label')
                        except json.JSONDecodeError:
                            # Ungültiges JSON, behandle es als alten String
                            pass

                    # B) Fallback: Behandle als alten Plain-String
                    if not current_id:
                        old_label = asset_str
                        current_sn = asset_str.split(' ')[0]
                        current_id = sn_to_id_map.get(current_sn)

                    # Wenn wir eine ID haben, können wir den aktuellen Namen nachschlagen
                    if current_id and current_id in id_to_label_map:
                        new_label = id_to_label_map[current_id]
                        # Wenn sich das Label geändert hat ODER wir einen alten String migrieren
                        if new_label != old_label or not asset_str.strip().startswith('{'):
                            # Erstelle IMMER den neuen, korrekten JSON-String
                            new_asset_json_str = json.dumps({"id": current_id, "label": new_label})
                            new_asset_list.append(new_asset_json_str)
                            changes_made = True
                            print(
                                f"INFO: Aktualisiere Asset in Def {definition.get('id')}: '{old_label}' -> '{new_label}' (migriert zu JSON)")
                        else:
                            new_asset_list.append(asset_str)  # Keine Änderung
                    else:
                        # Asset nicht (mehr) im Inventar gefunden, behalte den alten Wert
                        new_asset_list.append(asset_str)

                definition['maintenanceObjects'] = new_asset_list

        # 4. Speichern, falls Änderungen vorliegen
        if changes_made:
            if save_maintenance_definitions(definitions_data):
                print("--- DEBUG: Wartungs-Definitionen erfolgreich mit neuen Namen aktualisiert. ---")
            else:
                print("!!! FEHLER: Konnte die aktualisierten Definitionen nicht speichern.")
        else:
            print("--- DEBUG: Keine Namensänderungen in den Definitionen erforderlich. ---")

    except Exception as e:
        print(f"!!! FEHLER beim Sync der Hardware-Namen: {e}")
        traceback.print_exc()

def init_facility_data():
    """Erstellt Ordner und Standard-Strukturdatei, falls nicht vorhanden."""
    if not os.path.exists(FACILITY_DATA_DIR):
        os.makedirs(FACILITY_DATA_DIR)

    if not os.path.exists(FACILITY_MODELS_DIR):
        os.makedirs(FACILITY_MODELS_DIR)

    if not os.path.exists(FACILITY_BACKUP_DIR):
        os.makedirs(FACILITY_BACKUP_DIR)

    if not os.path.exists(FACILITY_STRUCTURE_FILE):
        print(f"INFO: Erstelle leere {FACILITY_STRUCTURE_FILE}")
        try:
            with open(FACILITY_STRUCTURE_FILE, 'w', encoding='utf-8') as f:
                json.dump(FACILITY_EMPTY_DATA, f, ensure_ascii=False, indent=4)
        except Exception as e:
            print(f"FEHLER beim Initialisieren der Facility-Daten: {e}")

def load_test_equipment_data():
    """Lädt Hardware-Daten."""
    # 1. Wenn Datei existiert, versuche sie zu laden
    if os.path.exists(TEST_EQUIPMENT_DATA_FILE):
        try:
            with open(TEST_EQUIPMENT_DATA_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except Exception as e:
            print(f"KRITISCHER FEHLER beim Laden der Test-Equipment Daten: {e}")
            return None

    # 2. Nur wenn Datei NICHT existiert (erster Start), Standardwerte laden
    # Fallback, falls Datei existiert, aber alt ist
    data = json.load(f)
    if "storage_units" not in data:
        data["storage_units"] = {}
    return data

    print("INFO: Keine Hardware-Datei gefunden. Lade Standardwerte.")
    return {
        "inventory": {}, "boms": {}, "mappings": {}, "trolleys": {},
        "storage_units": {}
    }


def save_test_equipment_data_to_file(data):
    """Speichert Hardware-Daten atomar."""
    print("--- DEBUG: save_test_equipment_data_to_file (Atomic) gestartet ---")

    success = save_json_file(TEST_EQUIPMENT_DATA_FILE, data)

    if success:
        print("--- DEBUG: Hardware-Datei erfolgreich und sicher gespeichert. ---")
    else:
        print("!!! DEBUG: Fehler beim Speichern der Hardware-Daten.")

    return success


def load_project_data_monitor():
    """
    Liest alle JSON-Dateien aus dem PROJECT_STATUS_DIR, verarbeitet sie
    und gibt sie in einer für das Template aufbereiteten Form zurück.
    """
    projects = []
    if not os.path.exists(PROJECT_STATUS_DIR):
        return []

    for filename in os.listdir(PROJECT_STATUS_DIR):
        if filename.endswith(".json"):
            filepath = os.path.join(PROJECT_STATUS_DIR, filename)
            try:
                with open(filepath, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    processed_project = process_single_project_monitor(data)
                    projects.append(processed_project)
            except Exception as e:
                print(f"Fehler beim Laden oder Verarbeiten der Datei {filename}: {e}")

    projects.sort(key=lambda p: (p['status'] == 'completed', p.get('due_date_obj', datetime.max)))
    return projects

def process_single_project_monitor(project_json):
    """
    Bereitet ein einzelnes Projekt-JSON für das Frontend-Template auf.
    """
    status = "completed" if project_json.get("progress_percent") == 100 else "active"
    all_steps = project_json.get("steps", [])

    step_map = {s['id_str']: s for s in all_steps}
    main_steps = []

    for step in step_map.values():
        step['children'] = []
        step['display_children'] = []

    for step in step_map.values():
        parent_id = step.get("parent_id_str")
        if parent_id and parent_id in step_map:
            step_map[parent_id]['children'].append(step)
        elif not parent_id:
            main_steps.append(step)

    for step in all_steps:
        step['children'].sort(key=lambda s: [int(p) for p in s['id_str'].split('.')])
        step['display_children'] = [
            child for child in step['children']
            if child.get('type') not in ['troubleshooting', 'solution']
        ]

    main_steps.sort(key=lambda s: [int(p) for p in s['id_str'].split('.')])

    def check_if_in_progress(step):
        if step.get('status') == 'In Progress':
            return True
        return any(check_if_in_progress(child) for child in step.get('children', []))

    for main_step in main_steps:
        main_step['is_in_progress'] = check_if_in_progress(main_step)
        flex_weight = 0.5
        if main_step.get('display_children'):
            flex_weight = max(1, len(main_step['display_children']))
            if "test" in main_step.get('name', '').lower():
                flex_weight = max(flex_weight, 3)
        main_step['flex_weight'] = flex_weight

        main_step['collapsed_dots'] = []
        for child in main_step.get('children', []):
            dot = {'class': 'bg-gray-300'}
            if child.get('status') == 'Completed':
                dot['class'] = 'bg-primary'
            elif check_if_in_progress(child):
                dot['class'] = 'border-2 border-primary-active'
            if child.get('type') == 'deviation' and child.get('status') != 'Completed':
                dot['class'] = 'bg-danger'
            main_step['collapsed_dots'].append(dot)

    due_date_obj = datetime.max
    try:
        due_date_str = project_json.get("due_date_display")
        if due_date_str and due_date_str != "N/A":
            due_date_obj = datetime.strptime(due_date_str.split(',')[0], "%d. %b %Y")
    except (ValueError, TypeError):
        pass

    return {
        "project_code": project_json.get('project_code', 'N/A'),
        "engine_serial": project_json.get('engine_serial', 'N/A'),
        "customer": project_json.get('customer', 'N/A'),
        "engine_type": project_json.get("engine_type", "N/A"),
        "progress": project_json.get("progress_percent", 0),
        "status": status,
        "due_date_obj": due_date_obj,
        "main_steps": main_steps
    }


def parse_date_from_string(date_str: str, format_str: str = "%Y-%m-%d") -> Optional[date]:
    if not date_str: return None
    try:
        return datetime.strptime(date_str, format_str).date()
    except ValueError:
        try:
            return datetime.strptime(date_str, "%d.%m.%Y").date()
        except ValueError:
            try:
                return datetime.strptime(date_str.split()[0], "%d.%m.%Y").date()
            except (ValueError, IndexError):
                # print(f"Warnung: Konnte Datum '{date_str}' nicht parsen.")
                return None


def format_date_to_string(date_obj: Optional[date], format_str: str = "%Y-%m-%d") -> str:
    if isinstance(date_obj, date):
        return date_obj.strftime(format_str)
    return ""


def get_datetime_from_date_shift(date_obj: Optional[date], shift_str: str) -> Optional[datetime]:
    if not isinstance(date_obj, date):
        return None
    hour_val = TIME_MAPPING.get(shift_str.upper())
    if hour_val is None:
        return None
    try:
        time_obj_val = time(hour_val, 0)
        return datetime.combine(date_obj, time_obj_val)
    except Exception as e:
        print(f"Fehler in get_datetime_from_date_shift: {e}")
        return None


def load_timeline_projects_for_flask(file_paths: List[str]) -> List[Dict]:
    """Lädt Projekte aus einer oder mehreren CSV-Dateien."""
    loaded_projects = []
    # Set, um Duplikate anhand von (project_code, engine_serial) zu vermeiden
    seen_projects = set()

    if not isinstance(file_paths, list):
        file_paths = [file_paths]

    for file_path in file_paths:
        if not os.path.exists(file_path):
            print(f"WARNUNG: Projektdatei {file_path} nicht gefunden, wird übersprungen.")
            continue
        try:
            with open(file_path, newline='', encoding='utf-8-sig') as csvfile:
                reader = csv.reader(csvfile)
                for row_num, row in enumerate(reader, start=1):
                    if not row or not any(field.strip() for field in row) or len(row) < 10:
                        continue

                    project_code = row[2].strip()
                    engine_serial = row[3].strip()
                    project_key = (project_code, engine_serial)

                    if project_key in seen_projects:
                        continue  # Überspringe Duplikat
                    seen_projects.add(project_key)

                    project_name, engine_type, _, _, customer, be_str, ts_str, te_str, sa_str, active_str = [r.strip()
                                                                                                             for r in
                                                                                                             row[:10]]
                    active = active_str == '1'
                    events_data = row[10:]
                    events = []
                    for i in range(0, len(events_data), 6):
                        if i + 5 >= len(events_data):
                            break
                        title, start_str_csv, start_shift, end_str_csv, end_shift, color = [events_data[i + j].strip()
                                                                                            for j in range(6)]
                        if not title: continue
                        start_date_obj = parse_date_from_string(start_str_csv)
                        end_date_obj = parse_date_from_string(end_str_csv)
                        if not start_date_obj or not end_date_obj: continue
                        events.append({'title': title, 'start': format_date_to_string(start_date_obj),
                                       'start_shift': start_shift.upper() or 'AM',
                                       'end': format_date_to_string(end_date_obj),
                                       'end_shift': end_shift.upper() or 'PM', 'color': color or 'blue'})

                    loaded_projects.append(
                        {'name': project_name, 'engine_type': engine_type, 'project_code': project_code,
                         'engine_serial': engine_serial, 'customer': customer, 'BE': be_str, 'TS': ts_str, 'TE': te_str,
                         'SA': sa_str, 'active': active, 'events': events})
            print(f"INFO: {len(loaded_projects)} Projekte aus {file_paths} geladen.")
        except Exception as e:
            print(f"FEHLER beim Lesen der Projekt-CSV-Datei ({file_path}): {e}")
            traceback.print_exc()

    return loaded_projects


def save_projects_for_flask(file_path: str, projects: List[Dict], silent: bool = True):
    try:
        with open(file_path, 'w', newline='', encoding='utf-8-sig') as csvfile:
            writer = csv.writer(csvfile)
            for project in projects:
                row_data = [
                    project.get('name', ''), project.get('engine_type', ''), project.get('project_code', ''),
                    project.get('engine_serial', ''), project.get('customer', ''), project.get('BE', ''),
                    project.get('TS', ''), project.get('TE', ''), project.get('SA', ''),
                    '1' if project.get('active', True) else '0'
                ]
                for event in project.get('events', []):
                    start_date_obj = parse_date_from_string(event.get('start', ''))
                    end_date_obj = parse_date_from_string(event.get('end', ''))
                    row_data.extend([
                        event.get('title', ''), format_date_to_string(start_date_obj),
                        event.get('start_shift', 'AM').upper(), format_date_to_string(end_date_obj),
                        event.get('end_shift', 'PM').upper(), event.get('color', 'blue')
                    ])
                writer.writerow(row_data)
        if not silent:
            print(f"INFO: Projekte erfolgreich in {file_path} gespeichert.")
        return True
    except Exception as e:
        print(f"FEHLER beim Speichern der Projekt-CSV-Datei ({file_path}): {e}")
        traceback.print_exc()
        return False


def load_qualifications_from_csv(file_path, parent=None):
    global MERKMALE_IN_ORDER
    qualifications = []

    if not os.path.exists(file_path):
        print(f"WARNUNG: Qualifikationsdatei {file_path} nicht gefunden.")
        if not MERKMALE_IN_ORDER:
            MERKMALE_IN_ORDER = ["CFM56/Mechanic", "PW1500G/Operator"]  # Fallback
        return [], MERKMALE_IN_ORDER

    try:
        with open(file_path, newline='', encoding='utf-8-sig') as csvfile:
            reader = csv.DictReader(csvfile)
            header = reader.fieldnames

            if not header:
                print(f"WARNUNG: Die Qualifikationsdatei {file_path} ist leer oder hat keinen Header.")
                return [], []

            fixed_cols = ['Quali', 'Name', 'ID']
            local_merkmale_in_order = [h for h in header if h not in fixed_cols and '_' not in h]

            for row_num, row in enumerate(reader, start=2):
                if not any(row.values()):
                    continue

                merkmale_list = []
                for merkmal_name in local_merkmale_in_order:
                    status_raw = row.get(merkmal_name)
                    start_date_raw = row.get(f'{merkmal_name}_Start')
                    end_date_raw = row.get(f'{merkmal_name}_End')
                    cert_path_raw = row.get(f'{merkmal_name}_Cert')

                    status = status_raw.strip() if isinstance(status_raw, str) else ""
                    start_date = start_date_raw.strip() if isinstance(start_date_raw, str) else ""
                    end_date = end_date_raw.strip() if isinstance(end_date_raw, str) else ""
                    cert_path = cert_path_raw.strip() if isinstance(cert_path_raw, str) else ""

                    # NEU: Dynamischen Status berechnen
                    dynamic_status = calculate_dynamic_status(status, start_date, end_date)

                    merkmale_list.append({
                        'merkmal': merkmal_name,
                        'wert': dynamic_status,  # <-- Hier wird der dynamische Wert verwendet
                        'start_date': start_date,
                        'end_date': end_date,
                        'cert_path': cert_path
                    })

                user_id = row.get('ID', '').strip()
                if not user_id:
                    print(f"WARNUNG: Zeile {row_num} in {file_path} wird übersprungen, da keine ID vorhanden ist.")
                    continue

                qualifications.append({
                    'quali': row.get('Quali', '').strip(),
                    'name': row.get('Name', '').strip(),
                    'id': row.get('ID', '').strip().lower(),  # <-- KORREKTUR
                    'merkmale': merkmale_list
                })

        if local_merkmale_in_order:
            MERKMALE_IN_ORDER = local_merkmale_in_order
        elif not MERKMALE_IN_ORDER:
            MERKMALE_IN_ORDER = ["CFM56/Mechanic", "PW1500G/Operator"]
            print(f"WARNUNG: Keine Merkmale in CSV gefunden, verwende Default-Merkmale: {MERKMALE_IN_ORDER}")

        print(
            f"INFO (korrigiert): {len(qualifications)} Qualifikationen aus '{file_path}' geladen. Identifizierte Merkmale: {len(MERKMALE_IN_ORDER)}")
        return qualifications, MERKMALE_IN_ORDER
    except Exception as e:
        print(f"FEHLER beim Laden der Qualifikationen (Version 4): {e}")
        traceback.print_exc()
        if not MERKMALE_IN_ORDER: MERKMALE_IN_ORDER = ["CFM56/Mechanic", "PW1500G/Operator"]
        return [], MERKMALE_IN_ORDER


def parse_german_date_from_header(date_str_raw, year_from_kw_header_suffix, fallback_year):
    date_str_cleaned = date_str_raw.replace('\n', ' ').strip()
    day_month_match = re.search(r'(\d{2})\.(\d{2})', date_str_cleaned)
    if not day_month_match:
        return None

    day = int(day_month_match.group(1))
    month = int(day_month_match.group(2))

    year_to_use_int = 0

    year_in_date_str_match = re.search(r'(?:Jan|Feb|Mär|Apr|Mai|Jun|Jul|Aug|Sep|Okt|Nov|Dez)\s*(\d{2})',
                                       date_str_cleaned, re.IGNORECASE)
    if year_in_date_str_match:
        year_to_use_int = int("20" + year_in_date_str_match.group(1))
    elif year_from_kw_header_suffix:
        year_to_use_int = int("20" + year_from_kw_header_suffix)
    else:
        year_to_use_int = fallback_year

    try:
        return date(year_to_use_int, month, day)
    except ValueError:
        try:
            if month == 12 and date(year_to_use_int - 1, month, day): return date(year_to_use_int - 1, month, day)
            if month == 1 and date(year_to_use_int + 1, month, day): return date(year_to_use_int + 1, month, day)
        except ValueError:
            pass
        return None


def load_employees_from_csv(file_path, merkmale_in_order_param=None, parent=None):
    global ALL_EMPLOYEES, ALL_USERS

    if not os.path.exists(file_path):
        print(f"WARNUNG: Mitarbeiterdatei {file_path} nicht gefunden.")
        ALL_EMPLOYEES = []
        return []

    if not ALL_USERS:
        load_all_users_on_startup()

    user_map_by_name = {u.get('normalized_name'): u for u in ALL_USERS if u.get('normalized_name')}

    parsed_employees = []
    try:
        with open(file_path, mode='r', newline='', encoding='utf-8-sig') as file:
            reader = csv.reader(file)
            rows = list(reader)

            if len(rows) < 2:
                print(f"WARNUNG: Mitarbeiterdatei {file_path} enthält nicht genügend Zeilen.")
                ALL_EMPLOYEES = []
                return []

            header = rows[0]
            data_rows = rows[1:]

            name_idx = 1  # Spalte B in Excel ist Index 1

            header_info = []
            current_kw_str_base = None
            active_year_suffix = None
            fallback_year_for_dates = date.today().year

            for col_idx, header_cell_raw in enumerate(header):
                header_cell_cleaned = header_cell_raw.replace('\n', ' ').strip()
                if col_idx == 0: header_info.append({'type': 'quali_group', 'index': col_idx}); continue
                if col_idx == 1: header_info.append({'type': 'name', 'index': col_idx}); continue
                if col_idx == 2: header_info.append({'type': 'initials_col', 'index': col_idx}); continue
                year_in_current_date_cell_match = re.search(
                    r'(?:Jan|Feb|Mär|Apr|Mai|Jun|Jul|Aug|Sep|Okt|Nov|Dez)\s*(\d{2})', header_cell_cleaned,
                    re.IGNORECASE)
                if year_in_current_date_cell_match: active_year_suffix = year_in_current_date_cell_match.group(1)
                kw_match = re.match(r'KW\s*(\d{1,2})(?:_(\d{2}))?', header_cell_cleaned, re.IGNORECASE)
                if kw_match:
                    kw_num_str = kw_match.group(1)
                    year_suffix_in_kw = kw_match.group(2)
                    current_kw_str_base = f"KW{kw_num_str}"
                    if year_suffix_in_kw: active_year_suffix = year_suffix_in_kw
                    full_kw_str_for_header_info = current_kw_str_base
                    if active_year_suffix: full_kw_str_for_header_info += f"_{active_year_suffix}"
                    header_info.append({'type': 'kw_shift_col', 'kw': full_kw_str_for_header_info, 'index': col_idx})
                else:
                    parsed_date_obj = parse_german_date_from_header(header_cell_raw, active_year_suffix,
                                                                    fallback_year_for_dates)
                    if parsed_date_obj and current_kw_str_base:
                        full_kw_str_for_date_context = current_kw_str_base
                        if active_year_suffix: full_kw_str_for_date_context += f"_{active_year_suffix}"
                        header_info.append({'type': 'day_event_col', 'kw_context': full_kw_str_for_date_context,
                                            'date': parsed_date_obj, 'index': col_idx,
                                            'is_holiday_header': "feier" in header_cell_cleaned.lower()})
                    else:
                        header_info.append({'type': 'unknown', 'text': header_cell_cleaned, 'index': col_idx})

            for emp_row_idx, emp_data_list in enumerate(data_rows):
                if not any(d.strip() for d in emp_data_list): continue

                quali_val = emp_data_list[0].strip()
                name_val = emp_data_list[name_idx].strip()
                initials_val = emp_data_list[2].strip()

                name_parts = [p.strip().lower() for p in name_val.split(',')]
                normalized_name_emp = ", ".join(name_parts) if len(name_parts) == 2 else name_val.lower()

                if not initials_val and name_val:
                    parts = name_val.split(',')
                    if len(parts) > 1:
                        initials_val = (parts[1].strip()[0] + parts[0].strip()[0]).upper()
                    elif len(name_val.split()) > 1:
                        name_parts = name_val.split();
                        initials_val = (name_parts[0][0] + name_parts[-1][0]).upper()

                user_obj = user_map_by_name.get(normalized_name_emp)
                employee_id = user_obj.get('id', '') if user_obj else ''
                if not employee_id:
                    print(
                        f"WARNUNG (employees.csv): Konnte keine eindeutige ID für '{name_val}' finden. Mitarbeiter wird evtl. nicht korrekt verarbeitet.")

                employee_dict = {
                    'name': name_val,
                    'initials': initials_val,
                    'quali': quali_val,
                    'normalized_name': normalized_name_emp,
                    'id': employee_id,  # <-- KORREKTUR (kommt bereits klein an)
                    'weeks': []
                }
                current_week_details_for_emp = None

                for col_info in header_info:
                    if col_info['type'] in ['quali_group', 'name', 'initials_col', 'unknown'] or col_info[
                        'index'] >= len(emp_data_list): continue
                    cell_value = emp_data_list[col_info['index']].strip()
                    if col_info['type'] == 'kw_shift_col':
                        if current_week_details_for_emp: employee_dict['weeks'].append(current_week_details_for_emp)
                        current_week_details_for_emp = {'KW': col_info['kw'], 'Schicht': cell_value, 'days': []}
                    elif col_info['type'] == 'day_event_col' and current_week_details_for_emp:
                        if current_week_details_for_emp['KW'] == col_info['kw_context']:
                            current_week_details_for_emp['days'].append(
                                {'date': col_info['date'], 'ereignis': cell_value,
                                 'holiday': col_info.get('is_holiday_header', False)})
                if current_week_details_for_emp: employee_dict['weeks'].append(current_week_details_for_emp)

                parsed_employees.append(employee_dict)

        ALL_EMPLOYEES = parsed_employees
        print(
            f"INFO: {len(ALL_EMPLOYEES)} Mitarbeiter-Anwesenheitsdaten aus {file_path} geladen und mit IDs angereichert.")
        return parsed_employees

    except FileNotFoundError:
        print(f"FEHLER: Mitarbeiterdatei {file_path} nicht gefunden.")
        ALL_EMPLOYEES = []
        return []
    except Exception as e:
        print(f"FEHLER beim Laden und Verarbeiten der Mitarbeiter-Anwesenheitsdatei: {e}")
        traceback.print_exc()
        ALL_EMPLOYEES = []
        return []


def save_qualifications_to_csv(file_path, qualifications_data, merkmale_order, parent=None, silent=False):
    try:
        header = ['Quali', 'Name', 'ID']
        for merkmal_name in merkmale_order:
            header.append(merkmal_name)  # Hauptspalte für Status
            header.extend([
                f'{merkmal_name}_Start',
                f'{merkmal_name}_End',
                f'{merkmal_name}_Cert'
            ])

        with open(file_path, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file, fieldnames=header)
            writer.writeheader()

            for qual_entry in qualifications_data:
                row_to_write = {
                    'Quali': qual_entry.get('quali', ''),
                    'Name': qual_entry.get('name', ''),
                    'ID': qual_entry.get('id', '')
                }
                merkmale_werte_map = {m.get('merkmal'): m for m in qual_entry.get('merkmale', [])}
                for merkmal_name in merkmale_order:
                    merkmal_data = merkmale_werte_map.get(merkmal_name, {})
                    row_to_write[merkmal_name] = merkmal_data.get('wert', '')
                    row_to_write[f'{merkmal_name}_Start'] = merkmal_data.get('start_date', '')
                    row_to_write[f'{merkmal_name}_End'] = merkmal_data.get('end_date', '')
                    row_to_write[f'{merkmal_name}_Cert'] = merkmal_data.get('cert_path', '')

                writer.writerow(row_to_write)

        if not silent:
            print(f"INFO: Qualifikationen erfolgreich in {file_path} im neuen Format gespeichert.")
        return True
    except Exception as e:
        print(f"FEHLER beim Speichern der Qualifikationen im neuen Format: {e}")
        traceback.print_exc()
        return False


def synchronize_qualifications_with_employees_logic(qualifications_list, employees_list, merkmale_order_list):
    if not employees_list:
        return False

    valid_merkmale_order = merkmale_order_list if isinstance(merkmale_order_list, list) else []
    if not valid_merkmale_order:
        print(
            "WARNUNG (Sync): merkmale_order_list ist leer oder ungültig. Neue Quali-Einträge werden ohne spezifische Merkmale erstellt.")

    quali_map_by_id = {q.get('id', '').strip().lower(): q for q in qualifications_list if q.get('id')}
    user_map_by_name = {u.get('normalized_name'): u for u in ALL_USERS if u.get('normalized_name')}

    new_qualifications_added_flag = False
    for emp in employees_list:
        emp_normalized_name = emp.get('normalized_name')
        if not emp_normalized_name:
            continue

        user_obj = user_map_by_name.get(emp_normalized_name)
        if not user_obj:
            print(
                f"WARNUNG (Sync): Mitarbeiter '{emp.get('name')}' aus Schichtplan nicht in Benutzerliste gefunden. Überspringe.")
            continue

        emp_id = user_obj.get('id', '').strip().lower()
        if not emp_id:
            print(f"WARNUNG (Sync): Benutzer '{emp.get('name')}' hat keine ID in user_display.csv. Überspringe.")
            continue

        if emp_id not in quali_map_by_id:
            print(
                f"INFO (Sync): Neuer Mitarbeiter '{emp.get('name')}' mit ID '{emp_id}' ohne Quali-Eintrag. Erstelle Standardeintrag.")

            new_qual_name = f"{user_obj.get('nachname').upper()}, {user_obj.get('vorname').capitalize()}"

            new_qual_entry = {
                'quali': emp.get('quali', '').strip(),
                'name': new_qual_name,
                'id': emp_id,
                'merkmale': []
            }
            for merkmal_name_iter in valid_merkmale_order:
                new_qual_entry['merkmale'].append({
                    'merkmal': merkmal_name_iter,
                    'wert': '',
                    'start_date': '',
                    'end_date': '',
                    'cert_path': ''
                })

            qualifications_list.append(new_qual_entry)
            quali_map_by_id[emp_id] = new_qual_entry
            new_qualifications_added_flag = True

    return new_qualifications_added_flag


def load_and_prepare_qualifications_data():
    global ALL_QUALIFICATIONS, ALL_EMPLOYEES, MERKMALE_IN_ORDER

    if not ALL_USERS:
        load_all_users_on_startup()

    loaded_qualis, loaded_merkmale = load_qualifications_from_csv(QUALIFICATIONS_FILE_PATH)
    ALL_QUALIFICATIONS = loaded_qualis
    ALL_EMPLOYEES = load_employees_from_csv(EMPLOYEES_FILE_PATH)

    if loaded_merkmale:
        MERKMALE_IN_ORDER = loaded_merkmale
    elif not MERKMALE_IN_ORDER:
        MERKMALE_IN_ORDER = ["CFM56-7B", "PW1500G", "LEAP-1A", "Training Allg."]  # Notfall-Fallback
        print(f"WARNUNG: MERKMALE_IN_ORDER ist leer, verwende Default: {MERKMALE_IN_ORDER}")

    made_changes = synchronize_qualifications_with_employees_logic(ALL_QUALIFICATIONS, ALL_EMPLOYEES, MERKMALE_IN_ORDER)
    if made_changes:
        save_qualifications_to_csv(QUALIFICATIONS_FILE_PATH, ALL_QUALIFICATIONS, MERKMALE_IN_ORDER, silent=True)
        ALL_QUALIFICATIONS, reloaded_merkmale = load_qualifications_from_csv(QUALIFICATIONS_FILE_PATH)
        if reloaded_merkmale:
            MERKMALE_IN_ORDER = reloaded_merkmale

    ALL_QUALIFICATIONS.sort(key=lambda q: (
        str(q.get('quali', 'zzzz')).lower(),
        str(q.get('name', '')).lower()
    ))

    grouped_qualifications = {}
    for qual_entry in ALL_QUALIFICATIONS:
        group_key = qual_entry.get('quali', 'Sonstige')
        if not group_key.strip(): group_key = 'Sonstige'
        if group_key not in grouped_qualifications:
            grouped_qualifications[group_key] = []
        grouped_qualifications[group_key].append(qual_entry)

    return grouped_qualifications, MERKMALE_IN_ORDER


# 3. Decorators und Hilfsfunktionen
def load_all_users_on_startup():
    global ALL_USERS
    try:
        if os.path.exists(USERS_FILE_PATH):
            with open(USERS_FILE_PATH, mode='r', newline='', encoding='utf-8-sig') as file:
                # Wichtig: fieldnames explizit übergeben, falls alte Spalten noch da sind
                reader = csv.DictReader(file)
                temp_users = []
                for row in reader:
                    nachname_norm = row.get('NACHNAME', '').strip().lower()
                    vorname_norm = row.get('VORNAME', '').strip().lower()
                    normalized_name_key = f"{nachname_norm}, {vorname_norm}"

                    # Neue Logik: Rollen aus der ROLES-Spalte lesen
                    roles_str = row.get('ROLES', '')
                    roles_list = [r.strip().upper() for r in roles_str.split(',') if r.strip()]

                    # Fallback-Logik für Abwärtskompatibilität, falls ROLES leer ist
                    is_admin_from_col = row.get('ADMIN', '').strip().lower() == 'x'
                    if Roles.ADMIN not in roles_list and is_admin_from_col:
                        roles_list.append(Roles.ADMIN)

                    if not roles_list:  # Wenn immer noch leer, gib minimalen Observer-Status
                        roles_list.append(Roles.OBSERVER)

                    units_str = row.get('UNITS', '')
                    units_list = [u.strip() for u in units_str.split(',') if u.strip()]

                    user = {
                        'nachname': row.get('NACHNAME', '').strip(),
                        'vorname': row.get('VORNAME', '').strip(),
                        'id': row.get('ID', '').strip().lower(),
                        'normalized_name': normalized_name_key,
                        'level': row.get('LEVEL', 'Standardbenutzer').strip(),
                        'password': row.get('PASSWORD', '').strip(),
                        'roles': sorted(list(set(roles_list))),
                        'units': units_list
                    }
                    temp_users.append(user)
                ALL_USERS = temp_users
                print(f"INFO: {len(ALL_USERS)} Benutzer (mit Rollen) aus {USERS_FILE_PATH} geladen.")
        else:
            print(f"WARNUNG: {USERS_FILE_PATH} nicht gefunden. Erstelle leere Benutzerliste.")
            ALL_USERS = []
    except Exception as e:
        print(f"FEHLER beim Laden der Benutzer: {e}")
        traceback.print_exc()
        ALL_USERS = []


def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            return redirect(url_for('login_page', next=request.url))
        return f(*args, **kwargs)

    return decorated_function


def admin_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            return redirect(url_for('login_page', next=request.url))
        if not session.get('is_admin'):
            if request.path.startswith('/api/'):
                return jsonify({"error": "Keine Berechtigung für diese Aktion."}), 403
            return redirect(url_for('dashboard_page'))
        return f(*args, **kwargs)

    return decorated_function


def get_monday_of_current_week():
    today = datetime.now()
    return (today - timedelta(days=today.weekday())).replace(hour=0, minute=0, second=0, microsecond=0)


def get_project_status_details(project_name, all_projects_data_for_status):
    project_data = None
    for p in all_projects_data_for_status:
        # Der Projektname wird hier bereits geprüft, aber zur Sicherheit
        if p.get('project_name') == project_name and p.get('project_typ') == "ENGINE":
            project_data = p
            break

    if not project_data:
        return None

    project_tasks_for_status = project_data.get('tasks', [])
    due_date = None

    if project_tasks_for_status:
        project_tasks_for_status.sort(key=lambda t: datetime.fromisoformat(t['end_datetime_iso']))
        last_task_end_dt = datetime.fromisoformat(project_tasks_for_status[-1]['end_datetime_iso'])
        due_date = last_task_end_dt.date()

    due_date_display = due_date.strftime("%d. %b %Y") if due_date else "N/A"

    current_step_index, last_task_name_for_step = -1, ""
    if project_tasks_for_status:
        sorted_tasks_by_start_desc = sorted(project_tasks_for_status,
                                            key=lambda t: datetime.fromisoformat(t['start_datetime_iso']), reverse=True)
        for task in sorted_tasks_by_start_desc:
            task_name_lower = task['task_name'].lower().strip()
            mapped_step_for_task = next(
                (step_name for keyword, step_name in TASK_TO_STEP_MAPPING.items() if keyword in task_name_lower), None)
            if mapped_step_for_task and mapped_step_for_task in DEFAULT_ENGINE_PROJECT_STEPS:
                step_idx_in_default = DEFAULT_ENGINE_PROJECT_STEPS.index(mapped_step_for_task)
                if step_idx_in_default > current_step_index:
                    current_step_index = step_idx_in_default
                    last_task_name_for_step = task['task_name']

    if project_tasks_for_status and current_step_index == -1:
        current_step_index = 0

    steps_with_status, current_step_for_notes_obj = [], None
    main_step_id_counter = 1
    for i, step_name in enumerate(DEFAULT_ENGINE_PROJECT_STEPS):
        status, status_detail = ("Upcoming", "")
        if current_step_index == -1 and not project_tasks_for_status:
            status = "Upcoming"
        elif i < current_step_index:
            status = "Completed"
        elif i == current_step_index:
            status = "In Progress"
            status_detail = f"Task: {last_task_name_for_step}" if last_task_name_for_step else ""
        else:
            status = "Upcoming"

        if status == "In Progress": current_step_for_notes_obj = {"name": step_name,
                                                                  "id_str": str(main_step_id_counter)}

        last_updated_by_demo, last_updated_at_demo = None, None
        if status == "Completed":
            last_updated_by_demo = "System"
            last_updated_at_demo = (
                    datetime.now() - timedelta(days=len(DEFAULT_ENGINE_PROJECT_STEPS) - i, hours=i * 2)).strftime(
                '%d. %b %Y, %H:%M')
        elif status == "In Progress":
            last_updated_by_demo = LOGGED_IN_USER_DISPLAY_NAME_FOR_PYTHON
            last_updated_at_demo = (datetime.now() - timedelta(hours=i)).strftime('%d. %b %Y, %H:%M')

        steps_with_status.append({
            "id_str": str(main_step_id_counter),
            "parent_id_str": None,
            "type": "main",
            "is_expanded": True,
            "name": step_name, "status": status, "status_detail": status_detail,
            "last_updated_by": last_updated_by_demo, "last_updated_at": last_updated_at_demo,
            "history": []
        })
        main_step_id_counter += 1

    progress_percent = 0
    total_steps = len(DEFAULT_ENGINE_PROJECT_STEPS)
    if total_steps > 0:
        if current_step_index == -1 and not project_tasks_for_status:
            progress_percent = 0
        elif current_step_index == total_steps - 1 and steps_with_status[current_step_index]['status'] == "Completed":
            progress_percent = 100
        elif current_step_index == total_steps - 1 and steps_with_status[current_step_index]['status'] == "In Progress":
            progress_percent = 99
        elif current_step_index >= 0:
            completed_main_steps = current_step_index
            if steps_with_status[current_step_index]['status'] == "In Progress":
                progress_percent = int(((completed_main_steps + 0.5) / total_steps) * 100)
            elif steps_with_status[current_step_index]['status'] == "Completed":
                progress_percent = int(((completed_main_steps + 1) / total_steps) * 100)
            else:
                progress_percent = int((completed_main_steps / total_steps) * 100)
            progress_percent = min(progress_percent, 99)

    return {
        "name": project_name, "engine_type": project_data.get("engine_type", "N/A"),
        "engine_serial": project_data.get("engine_serial", "N/A"),
        "due_date_display": due_date_display, "progress_percent": progress_percent,
        "steps": steps_with_status, "current_step_for_notes": current_step_for_notes_obj
    }


def get_current_username_from_session(): return session.get('username_display', "Gast")


def get_current_user_full_name_from_session():
    user_id = session.get('user_id')
    if user_id:
        user_data = next((user for user in ALL_USERS if user['id'] == user_id), None)
        if user_data:
            return f"{user_data.get('vorname', '')} {user_data.get('nachname', '')}".strip()
    return get_current_username_from_session()


DEFAULT_PAST_WEEKS_PRESENCE = 0  # Standardmäßig keine Wochen in der Vergangenheit initial anzeigen
DEFAULT_FUTURE_WEEKS_PRESENCE = 1  # Standardmäßig die aktuelle + 1 nächste Woche (also 2 Wochen total)


def get_employee_presence_data_for_web(employees_list, current_week_offset=0, num_weeks_to_display=2):
    """
    Bereitet Anwesenheitsdaten für die Web-Ansicht auf.
    current_week_offset: 0 für aktuelle Woche, 1 für nächste Woche, -1 für vorherige usw.
    num_weeks_to_display: Wie viele Wochen ab der offset_week angezeigt werden sollen (z.B. 2 für offset_week und offset_week+1)
    """
    if not employees_list:
        return {"headers": [], "employee_groups": [], "last_loaded": "N/A", "current_week_offset": current_week_offset,
                "total_weeks_in_data": 0}

    employees_list.sort(key=lambda e: (str(e.get('quali', '')).lower(), str(e.get('name', '')).lower()))
    grouped_employees_by_quali = {}
    for emp in employees_list:
        quali_key = emp.get('quali', 'Unbekannt')
        if not quali_key.strip(): quali_key = 'Unbekannt'
        if quali_key not in grouped_employees_by_quali:
            grouped_employees_by_quali[quali_key] = []
        grouped_employees_by_quali[quali_key].append(emp)

    today = date.today()
    # Montag der aktuellen, realen Woche
    start_of_actual_current_week = today - timedelta(days=today.weekday())

    # Startdatum der *ersten anzuzeigenden Woche* basierend auf dem Offset
    display_period_start_week_monday = start_of_actual_current_week + timedelta(weeks=current_week_offset)
    # Enddatum der *letzten anzuzeigenden Woche*
    display_period_end_date = display_period_start_week_monday + timedelta(weeks=num_weeks_to_display) - timedelta(
        days=1)

    all_dates_in_period = []
    current_d = display_period_start_week_monday
    while current_d <= display_period_end_date:
        all_dates_in_period.append(current_d)
        current_d += timedelta(days=1)

    # Finde die Grenzen der gesamten verfügbaren Daten in employees.csv, um Navigationslimits zu setzen
    min_date_in_csv = None
    max_date_in_csv = None
    if employees_list:
        for emp in employees_list:
            for week_data in emp.get('weeks', []):
                for day_data in week_data.get('days', []):
                    d = day_data.get('date')
                    if isinstance(d, date):
                        if min_date_in_csv is None or d < min_date_in_csv:
                            min_date_in_csv = d
                        if max_date_in_csv is None or d > max_date_in_csv:
                            max_date_in_csv = d

    total_weeks_in_data = 0
    if min_date_in_csv and max_date_in_csv:
        # Montag der ersten Woche in CSV
        min_week_start_csv = min_date_in_csv - timedelta(days=min_date_in_csv.weekday())
        # Montag der letzten Woche in CSV
        max_week_start_csv = max_date_in_csv - timedelta(days=max_date_in_csv.weekday())
        if max_week_start_csv >= min_week_start_csv:
            total_weeks_in_data = ((max_week_start_csv - min_week_start_csv).days // 7) + 1

    date_to_kw_map = {}
    for d_obj in all_dates_in_period:  # Nur die Daten für den anzuzeigenden Zeitraum
        year, week_num, _ = d_obj.isocalendar()
        date_to_kw_map.setdefault(f"KW{week_num}_{str(year)[-2:]}", []).append(d_obj)

    def parse_kw_year_key(kw_year_str):
        match = re.match(r"KW(\d+)_(\d+)", kw_year_str)
        if match: return int("20" + match.group(2)), int(match.group(1))
        return 0, 0

    sorted_kw_year_keys = sorted(date_to_kw_map.keys(), key=parse_kw_year_key)

    web_headers = []
    date_meta_info = {d.isoformat(): {"is_today": d == today, "is_holiday": False} for d in all_dates_in_period}

    for kw_year_key_str in sorted_kw_year_keys:
        web_headers.append({"type": "kw", "text": kw_year_key_str})
        dates_in_this_week = sorted(date_to_kw_map[kw_year_key_str])
        for d_obj in dates_in_this_week:
            web_headers.append({
                "type": "day", "text": d_obj.strftime('%d.%m'), "date_iso": d_obj.isoformat(),
                "is_today": date_meta_info[d_obj.isoformat()]["is_today"],
                "is_holiday": date_meta_info[d_obj.isoformat()]["is_holiday"],
                "is_weekend": d_obj.weekday() >= 5
            })

    employee_groups_for_web = []
    for quali_name, emps_in_group in grouped_employees_by_quali.items():
        group_data = {"group_name": quali_name, "employees": []}
        for emp in emps_in_group:
            emp_web_data = {"name": emp.get('name', ''), "initials": emp.get('initials', ''), "presence": []}
            emp_presence_map_by_date = {}
            emp_shift_map_by_kw_str = {}

            for week_data_csv in emp.get('weeks', []):
                kw_str_from_emp = week_data_csv.get('KW')
                shift_from_emp = week_data_csv.get('Schicht')
                if kw_str_from_emp and shift_from_emp:
                    emp_shift_map_by_kw_str[kw_str_from_emp] = shift_from_emp
                for day_data_csv in week_data_csv.get('days', []):
                    if isinstance(day_data_csv.get('date'), date):
                        date_obj_csv = day_data_csv['date']
                        emp_presence_map_by_date[date_obj_csv.isoformat()] = {
                            'ereignis': day_data_csv.get('ereignis', ''),
                            'holiday': day_data_csv.get('holiday', False)
                        }

            for header_item in web_headers:  # Iteriere nur über die Header des aktuellen Anzeigezeitraums
                if header_item["type"] == "kw":
                    shift_for_kw = emp_shift_map_by_kw_str.get(header_item["text"], '')
                    emp_web_data["presence"].append({"type": "kw", "shift": shift_for_kw})
                elif header_item["type"] == "day":
                    day_iso_str = header_item["date_iso"]
                    presence_info_for_day = emp_presence_map_by_date.get(day_iso_str,
                                                                         {"ereignis": "", "holiday": False})
                    emp_web_data["presence"].append({
                        "type": "day", "event": presence_info_for_day["ereignis"],
                        "is_holiday_from_data": presence_info_for_day["holiday"],
                    })
            group_data["employees"].append(emp_web_data)
        employee_groups_for_web.append(group_data)

    last_loaded_str = "N/A"
    if os.path.exists(EMPLOYEES_FILE_PATH):
        try:
            mtime = os.path.getmtime(EMPLOYEES_FILE_PATH)
            last_loaded_str = datetime.fromtimestamp(mtime).strftime("%d.%m.%Y %H:%M:%S")
        except:
            pass

    return {
        "headers": web_headers,
        "employee_groups": employee_groups_for_web,
        "last_loaded": last_loaded_str,
        "current_week_offset": current_week_offset,  # Wichtig für JS
        "num_weeks_displayed": num_weeks_to_display,  # Wichtig für JS
        "min_date_in_csv_iso": min_date_in_csv.isoformat() if min_date_in_csv else None,
        "max_date_in_csv_iso": max_date_in_csv.isoformat() if max_date_in_csv else None,
        "total_weeks_in_data": total_weeks_in_data
    }


def get_datetime_from_date_shift(date_obj: Optional[date], shift_str: str) -> Optional[datetime]:
    if not isinstance(date_obj, date):
        return None

    hour_val = TIME_MAPPING.get(shift_str.upper(), 0)  # Nimm 0 (AM) als Fallback

    try:
        # Hier ist der Trick: Erstelle ein naives datetime-Objekt
        return datetime(date_obj.year, date_obj.month, date_obj.day, hour_val)
    except Exception as e:
        print(f"Fehler in get_datetime_from_date_shift: {e}")
        return None


def split_datetime_to_date_shift(dt_obj):
    date_part = dt_obj.date()
    shift_part = 'AM' if dt_obj.hour < 12 else 'PM'
    return date_part, shift_part


def get_event_time_slots_for_api(event_data):
    slots = []
    if not event_data or 'start_datetime' not in event_data or 'end_datetime' not in event_data:
        return slots

    try:
        start_dt = datetime.fromisoformat(event_data['start_datetime'])
        end_dt = datetime.fromisoformat(event_data['end_datetime'])
    except (ValueError, TypeError):
        return slots

    current_dt = start_dt
    while current_dt < end_dt:
        date_part = current_dt.date()
        shift_part = 'AM' if current_dt.hour < 12 else 'PM'
        slots.append(f"{date_part.isoformat()}_{shift_part}")
        current_dt += timedelta(hours=12)
    return slots


def filter_sap_data(source_file_or_path, filter_rules: Optional[Dict] = None) -> Optional[pd.DataFrame]:
    try:
        import pandas as pd
    except ImportError:
        print("FEHLER: 'pandas' Paket wird benötigt. Bitte installieren: pip install pandas")
        return None

    try:
        df = pd.read_csv(source_file_or_path, sep=';', encoding='utf-8', dayfirst=True, low_memory=False)
    except Exception as e:
        print(f"FEHLER: SAP-CSV-Datei konnte nicht gelesen werden: {e}")
        return None

    required_columns = ["Project", "Engine Serial", "Customer", "Engine Type", "Workscope", "B3", "TS", "TE", "SA",
                        "Milestone type"]
    missing_cols = [col for col in required_columns if col not in df.columns]
    if missing_cols:
        print(f"FEHLER in filter_sap_data: Folgende Spalten fehlen in der SAP-Datei: {', '.join(missing_cols)}")
        return None

    # Grundfilterung (Datum, Milestone, etc.) - bleibt gleich
    df['TS_parsed'] = pd.to_datetime(df['TS'], format='%d.%m.%Y %H:%M:%S', errors='coerce')
    today = datetime.now()
    start_date, end_date = today - timedelta(weeks=4), today + timedelta(weeks=6)
    df = df.dropna(subset=['TS_parsed'])
    df = df.loc[(df['TS_parsed'] >= start_date) & (df['TS_parsed'] <= end_date)].copy()
    df = df[df['Milestone type'].astype(str) == "Plan (Planung, ZMMP)"].copy()

    # NEU: Verbesserte dynamische Filterungasdfsadfas dasdf sdfa
    if filter_rules:
        for column, patterns in filter_rules.items():
            if column in df.columns:
                try:
                    # Kombiniere alle Regex-Patterns mit einem ODER (|)
                    # case=False macht den Filter unempfindlich für Groß-/Kleinschreibung
                    combined_pattern = '|'.join(patterns)
                    df = df[df[column].astype(str).str.contains(combined_pattern, regex=True, na=False, case=False)]
                except Exception as e:
                    print(f"WARNUNG: Fehler beim Anwenden des SAP-Filters für Spalte '{column}': {e}")
                    # Im Fehlerfall wird diese Filterregel einfach übersprungen, anstatt die App abstürzen zu lassen.
                    pass

    if df.empty:
        print("INFO (SAP-Filter): Nach Anwendung aller Filter wurden keine passenden Projekte gefunden.")

    df.sort_values(by='TS_parsed', inplace=True)
    return df


def merge_into_timeline_projects(timeline_csv_path: str, filtered_df: pd.DataFrame,
                                 current_project_order: List[Tuple[str, str]]) -> bool:
    changes_made = False
    existing_projects = {}

    # 1. Bestehende Projekte laden
    if os.path.exists(timeline_csv_path):
        with open(timeline_csv_path, newline='', encoding='utf-8-sig') as f:
            reader = csv.reader(f)
            for row in reader:
                if not row: continue
                # Key ist (Project Code, Engine Serial)
                key = (row[2].strip(), row[3].strip())
                existing_projects[key] = row

    # 2. Prozess-Templates laden (für die dynamische Event-Generierung)
    process_templates = load_process_templates()

    # Hilfsfunktion für Formatierung
    def convert_to_original_format(value) -> str:
        if pd.isnull(value): return ""
        if isinstance(value, pd.Timestamp) or isinstance(value, datetime):
            return value.strftime("%d.%m.%Y %H:%M:%S")
        return str(value).strip()

    # 3. Durch die gefilterten SAP-Daten iterieren
    for _, row in filtered_df.iterrows():
        engine_serial = str(row["Engine Serial"]).strip()
        engine_type = str(row["Engine Type"]).strip()  # Wichtig für Template-Wahl
        project_code = str(row["Project"]).strip()
        customer = str(row["Customer"]).strip()
        project_name = f"{engine_type} - {project_code}"

        b3_str = convert_to_original_format(row['B3'])
        ts_str = convert_to_original_format(row['TS'])
        te_str = convert_to_original_format(row['TE'])
        sa_str = convert_to_original_format(row['SA'])

        if not ts_str: continue

        key = (project_code, engine_serial)

        # A) Projekt existiert bereits -> Prüfen auf Updates
        if key in existing_projects:
            existing_row = existing_projects[key]

            # Prüfen ob sich Metadaten geändert haben
            if (existing_row[5] != b3_str or existing_row[6] != ts_str or
                    existing_row[7] != te_str or existing_row[8] != sa_str or
                    existing_row[4] != customer or existing_row[0] != project_name):

                existing_row[0] = project_name
                existing_row[4] = customer
                existing_row[5] = b3_str
                existing_row[6] = ts_str
                existing_row[7] = te_str
                existing_row[8] = sa_str

                is_active = existing_row[9] == '1'

                # Nur wenn das Projekt INAKTIV ist, werden die Events neu generiert
                if not is_active:
                    try:
                        ts_datetime = datetime.strptime(ts_str, "%d.%m.%Y %H:%M:%S")
                    except ValueError:
                        continue

                        # Template laden
                    template = find_best_template_for_engine(engine_type, process_templates)
                    template_events = template.get("timeline_events", [])

                    dynamic_events = []
                    current_date = ts_datetime.date()
                    current_shift = 'AM' if ts_datetime.hour < 14 else 'PM'

                    # Events generieren
                    for event_def in template_events:
                        duration = event_def.get("duration_shifts", 1)
                        start_date_loop = current_date
                        start_shift_loop = current_shift

                        end_date_loop = start_date_loop
                        end_shift_loop = start_shift_loop

                        # KORREKTUR: Schleife läuft exakt 'duration' mal
                        for _ in range(duration):
                            if end_shift_loop == 'AM':
                                end_shift_loop = 'PM'
                            else:
                                end_shift_loop = 'AM'
                                end_date_loop += timedelta(days=1)

                        dynamic_events.append({
                            'title': event_def.get("name", "Unknown"),
                            'start': start_date_loop.strftime("%Y-%m-%d"),
                            'start_shift': start_shift_loop,
                            'end': end_date_loop.strftime("%Y-%m-%d"),
                            'end_shift': end_shift_loop,
                            'color': event_def.get("color", "grey")
                        })

                        # KORREKTUR: Nächstes Event startet direkt am Ende des aktuellen (keine Lücke)
                        current_date = end_date_loop
                        current_shift = end_shift_loop

                    # Zeile neu zusammenbauen: Metadaten + neue Events
                    updated_row = existing_row[:10]
                    for event in dynamic_events:
                        updated_row.extend([
                            event['title'], event['start'], event['start_shift'],
                            event['end'], event['end_shift'], event['color']
                        ])
                    existing_projects[key] = updated_row

                changes_made = True

        # B) Neues Projekt -> Anlegen
        else:
            new_row = [
                project_name, engine_type, project_code, engine_serial, customer,
                b3_str, ts_str, te_str, sa_str, '0'  # Standardmäßig inaktiv
            ]

            try:
                ts_datetime = datetime.strptime(ts_str, "%d.%m.%Y %H:%M:%S")
            except ValueError:
                continue

            # Template laden
            template = find_best_template_for_engine(engine_type, process_templates)
            template_events = template.get("timeline_events", [])

            dynamic_events = []
            current_date = ts_datetime.date()
            current_shift = 'AM' if ts_datetime.hour < 14 else 'PM'

            # Events generieren
            for event_def in template_events:
                duration = event_def.get("duration_shifts", 1)
                start_date_loop = current_date
                start_shift_loop = current_shift

                end_date_loop = start_date_loop
                end_shift_loop = start_shift_loop

                # KORREKTUR: Schleife läuft exakt 'duration' mal
                for _ in range(duration):
                    if end_shift_loop == 'AM':
                        end_shift_loop = 'PM'
                    else:
                        end_shift_loop = 'AM'
                        end_date_loop += timedelta(days=1)

                dynamic_events.append({
                    'title': event_def.get("name", "Unknown"),
                    'start': start_date_loop.strftime("%Y-%m-%d"),
                    'start_shift': start_shift_loop,
                    'end': end_date_loop.strftime("%Y-%m-%d"),
                    'end_shift': end_shift_loop,
                    'color': event_def.get("color", "grey")
                })

                # KORREKTUR: Nächstes Event startet direkt am Ende des aktuellen (keine Lücke)
                current_date = end_date_loop
                current_shift = end_shift_loop

            for event in dynamic_events:
                new_row.extend([
                    event['title'], event['start'], event['start_shift'],
                    event['end'], event['end_shift'], event['color']
                ])

            existing_projects[key] = new_row
            changes_made = True

    # 4. Sortierung beibehalten / neue Projekte einfügen
    final_rows_ordered = []
    seen_keys = set()

    # Zuerst die Projekte in der existierenden Reihenfolge
    for proj_key in current_project_order:
        if proj_key in existing_projects:
            final_rows_ordered.append(existing_projects[proj_key])
            seen_keys.add(proj_key)

    # Dann alle neuen Projekte anhängen
    for key, row_data in existing_projects.items():
        if key not in seen_keys:
            final_rows_ordered.append(row_data)

    # 5. Speichern
    try:
        with open(timeline_csv_path, 'w', newline='', encoding='utf-8-sig') as f:
            writer = csv.writer(f)
            writer.writerows(final_rows_ordered)
        return changes_made
    except Exception as e:
        print(f"FEHLER beim Schreiben der Timeline CSV: {e}")
        return False


def load_assigned_employees(file_path: str) -> List[Dict]:
    """Lädt manuelle Zuweisungen aus der angegebenen CSV-Datei."""
    if not os.path.exists(file_path):
        try:
            with open(file_path, 'w', newline='', encoding='utf-8-sig') as f:
                writer = csv.DictWriter(f, fieldnames=ASSIGNED_EMPLOYEES_FIELDNAMES)
                writer.writeheader()
            print(f"INFO: Leere Zuweisungsdatei '{file_path}' wurde erstellt.")
            return []
        except IOError as e:
            print(f"FEHLER beim Erstellen der Zuweisungsdatei '{file_path}': {e}")
            return []
    try:
        clean_assignments = []
        with open(file_path, mode='r', newline='', encoding='utf-8-sig') as file:
            reader = csv.DictReader(file)
            for row in reader:
                clean_row = {key: value for key, value in row.items() if key is not None}
                clean_assignments.append(clean_row)
        return clean_assignments
    except Exception as e:
        print(f"FEHLER beim Laden der manuellen Zuweisungen aus '{file_path}': {e}")
        return []


def save_assigned_employees(file_path: str, assignments_list: List[Dict]):
    """Speichert die Liste der manuellen Mitarbeiterzuweisungen in die angegebene CSV-Datei."""
    try:
        with open(file_path, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file, fieldnames=ASSIGNED_EMPLOYEES_FIELDNAMES)
            writer.writeheader()
            for assignment in assignments_list:
                row_to_write = {key: assignment.get(key, '') for key in ASSIGNED_EMPLOYEES_FIELDNAMES}
                writer.writerow(row_to_write)
        return True
    except Exception as e:
        print(f"FEHLER beim Speichern der manuellen Zuweisungen in '{file_path}': {e}")
        return False


def get_employee_qualification_status_for_task_py(emp_quals: Optional[Dict], project: Dict, event_title: str) -> \
        Optional[str]:
    """
    Python-Pendant zur JavaScript-Funktion getEmployeeQualificationStatusForTask.
    Ermittelt den besten Qualifikationsstatus ('X', 'TS', 'TT', 'TP') eines Mitarbeiters
    für eine bestimmte Aufgabe.
    """
    if not emp_quals or not project:
        return None

    engine_type_clean = project.get('engine_type', '').strip().lower()
    event_title_clean = event_title.strip().lower()

    best_status = None
    status_priority = {'X': 1, 'TS': 2, 'TT': 3, 'TP': 4}

    for qual in emp_quals.get('merkmale', []):
        qual_name_clean = qual.get('merkmal', '').strip().lower()
        qual_value = qual.get('wert', '').upper()
        if not qual_value or qual_value not in status_priority:
            continue

        qual_parts = qual_name_clean.split('/')
        if len(qual_parts) < 2:
            continue

        qual_engine, qual_role = qual_parts[0].strip(), qual_parts[1].strip()

        # Motor-Typ-Abgleich (wie im JS)
        is_engine_match = False
        project_engine_base = engine_type_clean.split('e')[0]
        if qual_engine == project_engine_base:
            is_engine_match = True
        elif qual_engine in ['pw800', 'pw300', 'pw500'] and engine_type_clean.startswith(qual_engine[:3]):
            is_engine_match = True

        if not is_engine_match:
            continue

        # Rollen-Abgleich (wie im JS)
        is_role_match = False
        if 'mechanic' in qual_role and event_title_clean in ['rig', 'de-rig', 'atw']:
            is_role_match = True
        elif 'operator' in qual_role and event_title_clean == 'test':
            is_role_match = True

        if is_role_match:
            if best_status is None or status_priority[qual_value] < status_priority[best_status]:
                best_status = qual_value

    return best_status


def is_employee_available_for_slot_py(emp_id: str, slot_date_iso: str, slot_shift: str,
                                      employee_availability_map: Dict, global_assignments_by_slot: Dict,
                                      manual_assignments_in_slot: Dict) -> bool:
    """
    Prüft, ob ein Mitarbeiter für einen Slot verfügbar ist.
    Entspricht der Logik in isEmployeeAvailableForSlot im JS.
    """
    slot_key_global = (slot_date_iso, slot_shift)

    # 1. Ist der Mitarbeiter bereits für einen anderen Task in diesem Slot verplant?
    if emp_id in global_assignments_by_slot.get(slot_key_global, set()):
        return False

    # 2. Ist der Mitarbeiter manuell für einen anderen Task in diesem Slot geblockt?
    if emp_id in manual_assignments_in_slot.get(slot_key_global, set()):
        return False

    # 3. Ist der Mitarbeiter laut Schichtplan verfügbar?
    emp_availability = employee_availability_map.get(emp_id, {})
    day_info = emp_availability.get(slot_date_iso)
    if not day_info or not day_info.get('available') or day_info.get('shift') != slot_shift:
        return False

    return True


def is_employee_qualified_by_rules(emp_quals, project, event_title, assignment_rules):
    """
    Prüft anhand der dynamischen Regeln, ob ein Mitarbeiter qualifiziert ist.
    GIBT JETZT True/False ZURÜCK. Verwendet die zentrale VALID_ASSIGNABLE_STATUSES Konstante.
    """
    if not emp_quals:
        return False

    proj_engine_type = project.get('engine_type', '').strip()
    task_event_title = event_title.strip().upper()

    applicable_rules = []
    for rule in assignment_rules:
        event_match = not rule.get('event_titles') or task_event_title in rule.get('event_titles', [])
        engine_match = False
        if not rule.get('engine_types'):
            engine_match = True
        else:
            for rule_engine_prefix in rule.get('engine_types', []):
                if proj_engine_type.startswith(rule_engine_prefix):
                    engine_match = True
                    break
        if event_match and engine_match:
            applicable_rules.append(rule)

    if not applicable_rules:
        return False

    employee_valid_quals = {
        m['merkmal'] for m in emp_quals.get('merkmale', [])
        if m.get('wert') in VALID_ASSIGNABLE_STATUSES  # <-- Nutzung der neuen Konstante
    }

    for rule in applicable_rules:
        required_skills_for_rule = set(rule.get('required_skills', []))
        if required_skills_for_rule.issubset(employee_valid_quals):
            return True  # Ein Treffer genügt

    return False


def load_all_comments():
    global ALL_COMMENTS
    if not os.path.exists(COMMENTS_FILE_PATH):
        # Erstelle die Datei mit Header, wenn sie nicht existiert
        with open(COMMENTS_FILE_PATH, 'w', newline='', encoding='utf-8-sig') as f:
            writer = csv.writer(f)
            writer.writerow(['comment_id', 'project_id', 'author', 'timestamp', 'comment'])
        ALL_COMMENTS = []
        return
    try:
        with open(COMMENTS_FILE_PATH, mode='r', newline='', encoding='utf-8-sig') as file:
            reader = csv.DictReader(file)
            ALL_COMMENTS = list(reader)
        print(f"INFO: {len(ALL_COMMENTS)} Kommentare aus {COMMENTS_FILE_PATH} geladen.")
    except Exception as e:
        print(f"FEHLER beim Laden der Kommentare: {e}")
        ALL_COMMENTS = []


def save_all_comments():
    global ALL_COMMENTS
    fieldnames = ['comment_id', 'project_id', 'author', 'timestamp', 'comment']
    try:
        with open(COMMENTS_FILE_PATH, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file, fieldnames=fieldnames, extrasaction='ignore')
            writer.writeheader()
            sorted_comments = sorted(ALL_COMMENTS, key=lambda x: x.get('timestamp', ''))
            writer.writerows(sorted_comments)
        return True
    except Exception as e:
        print(f"FEHLER beim Speichern der Kommentare: {e}")
        return False


def calculate_timeline_assignments(projects: List[Dict], employees: List[Dict], qualifications: List[Dict],
                                   manual_assignments: List[Dict], timeline_start_date: date, num_days: int,
                                   assignment_rules: List[Dict], assignment_groups: List[str] = None,
                                   selected_test_cell_ids: List[str] = None,
                                   timeline_id: str = None, previous_global_assignments: Dict = None) -> Dict:
    if assignment_groups is None: assignment_groups = ['FF']

    # --- 1. DATENAUFBEREITUNG ---
    all_locations_full = load_locations_and_events()
    all_locations_full = inject_dependency_blocks(all_locations_full)
    all_possible_cell_ids = [loc['ID'] for loc in all_locations_full]
    allowed_test_cell_ids = selected_test_cell_ids if selected_test_cell_ids is not None else all_possible_cell_ids

    employee_map_by_id = {e['id'].lower(): e for e in employees if e.get('id')}
    employee_qualifications_map = {q['id'].lower(): q for q in qualifications if q.get('id')}
    locations_map_with_events = {loc['ID']: loc for loc in all_locations_full}

    employee_availability_map = {}
    for emp in employees:
        if not emp.get('id'): continue
        availability = {}
        for week in emp.get('weeks', []):
            shift_from_csv = week.get('Schicht', '').upper()
            web_shift = 'AM' if shift_from_csv == 'F' else ('PM' if shift_from_csv == 'S' else None)
            if not web_shift: continue
            for day in week.get('days', []):
                if isinstance(day.get('date'), date):
                    availability[day['date'].isoformat()] = {'shift': web_shift,
                                                             'available': not day.get('ereignis', '').strip()}
        employee_availability_map[emp['id'].lower()] = availability

    manual_assignments_map_by_task, manual_assignments_in_slot = {}, {}
    for ma in manual_assignments:
        emp_id = ma.get('employee_id', '').lower();
        if not emp_id: continue
        task_key = (ma.get('project_code'), ma.get('engine_serial'), ma.get('event_title'), ma.get('date_str'),
                    ma.get('shift'))
        if task_key not in manual_assignments_map_by_task: manual_assignments_map_by_task[task_key] = []
        manual_assignments_map_by_task[task_key].append(emp_id)
        slot_key_global = (ma.get('date_str'), ma.get('shift'))
        if slot_key_global not in manual_assignments_in_slot: manual_assignments_in_slot[slot_key_global] = set()
        manual_assignments_in_slot[slot_key_global].add(emp_id)

    all_active_events = []
    for proj in projects:
        if proj.get('active'):
            for evt in proj.get('events', []):
                if 'start_datetime' in evt and 'end_datetime' in evt:
                    all_active_events.append({'project': proj, 'event': evt})
    all_active_events.sort(key=lambda x: datetime.fromisoformat(x['event']['start_datetime'].replace('Z', '')))

    # --- INIT GLOBALER STATE ---
    global_assignments_by_slot = {}
    if timeline_id:
        end_date_calc = timeline_start_date + timedelta(days=num_days)
        global_assignments_by_slot = global_scheduler.get_busy_employees_in_range(
            timeline_start_date, end_date_calc, ignore_timeline_id=timeline_id
        )

    # --- INIT SMART SCHEDULER ---
    try:
        history_data = global_scheduler.load_history()
        smart_scheduler = SmartScheduler(history_data, global_scheduler.assignments)
    except:
        smart_scheduler = None  # Fallback

    # --- 2. ZUWEISUNGS-LOOP ---
    cell_assignments_by_slot, assignments_by_event_slot = {}, {}

    # State für "Sticky" Zuweisung (Testzellen)
    sticky_cell_for_test_block = None

    for item_index, item in enumerate(all_active_events):
        project, event = item['project'], item['event']
        is_test_event = event.get('title', '').lower() == 'test'

        # Sticky Logik für Testzellen
        if item_index > 0:
            prev_event = all_active_events[item_index - 1]['event']
            prev_is_test_event = prev_event.get('title', '').lower() == 'test'
            if not is_test_event or not prev_is_test_event or all_active_events[item_index - 1]['project'] != project:
                sticky_cell_for_test_block = None
        elif not is_test_event:
            sticky_cell_for_test_block = None

        try:
            start_dt = datetime.fromisoformat(event['start_datetime'].replace('Z', ''))
            end_dt = datetime.fromisoformat(event['end_datetime'].replace('Z', ''))
        except:
            continue

        current_dt, slot_index = start_dt, 0
        while current_dt < end_dt:
            slot_date_iso, slot_shift = current_dt.date().isoformat(), 'AM' if current_dt.hour < 12 else 'PM'
            slot_key_global = (slot_date_iso, slot_shift)

            if slot_key_global not in global_assignments_by_slot: global_assignments_by_slot[slot_key_global] = set()

            task_key_manual = (project.get('project_code'), project.get('engine_serial'), event.get('title'),
                               slot_date_iso, slot_shift)
            manually_assigned_ids = manual_assignments_map_by_task.get(task_key_manual, [])
            event_slot_key = f"{event['id']}_{slot_index}"

            common_data = {
                "test_cell_id": None,
                "date": slot_date_iso,
                "shift": slot_shift,
                "task": event.get('title', 'Unknown'),
                "engine": project.get('engine_type', 'N/A'),
                "project_name": project.get('name', 'N/A')
            }

            # A) Init Slot
            if manually_assigned_ids:
                assignments_by_event_slot[event_slot_key] = {
                    "employees": list(manually_assigned_ids),  # Kopie!
                    "is_manual": True,
                    **common_data
                }
                for emp_id in manually_assigned_ids: global_assignments_by_slot[slot_key_global].add(emp_id)
            else:
                assignments_by_event_slot[event_slot_key] = {
                    "employees": [],
                    "is_manual": False,
                    **common_data
                }

            # B) Testzellen Zuweisung
            if is_test_event:
                assigned_cell = None
                if sticky_cell_for_test_block:
                    assigned_cell = sticky_cell_for_test_block
                elif not assignments_by_event_slot[event_slot_key].get("test_cell_id"):
                    for cell_id in allowed_test_cell_ids:
                        loc = locations_map_with_events.get(cell_id)
                        if not loc: continue

                        # Blockaden Check
                        blocked = any((e['start_date'] < slot_date_iso or (
                                    e['start_date'] == slot_date_iso and e['start_shift'] <= slot_shift)) and
                                      (e['end_date'] > slot_date_iso or (
                                                  e['end_date'] == slot_date_iso and e['end_shift'] >= slot_shift))
                                      for e in loc.get('events', []) if e.get('type') in ['Maintenance', 'Blocked'])
                        if blocked: continue

                        # Typ Check
                        proj_eng = project.get('engine_type', '')
                        compatible = not loc.get('TYPES') or any(proj_eng.startswith(t) for t in loc.get('TYPES', []))
                        if not compatible: continue

                        # Verfügbarkeit
                        if cell_id not in cell_assignments_by_slot.get(slot_key_global, set()):
                            assigned_cell = cell_id
                            sticky_cell_for_test_block = cell_id
                            break

                if assigned_cell:
                    assignments_by_event_slot[event_slot_key]["test_cell_id"] = assigned_cell
                    if slot_key_global not in cell_assignments_by_slot: cell_assignments_by_slot[
                        slot_key_global] = set()
                    cell_assignments_by_slot[slot_key_global].add(assigned_cell)

            # C) SMART ASSIGNMENT (Mitarbeiter)
            max_employees_per_slot = 2
            current_assignment = assignments_by_event_slot[event_slot_key]

            if not current_assignment["is_manual"]:
                # 1. Kandidaten sammeln
                candidates = []
                for emp in employees:
                    eid = emp.get('id')
                    if not eid: continue
                    if emp.get('quali') not in assignment_groups: continue

                    if not is_employee_available_for_slot_py(eid, slot_date_iso, slot_shift, employee_availability_map,
                                                             global_assignments_by_slot, manual_assignments_in_slot):
                        continue

                    emp_quals = employee_qualifications_map.get(eid.lower())
                    if not is_employee_qualified_by_rules(emp_quals, project, event.get('title'), assignment_rules):
                        continue

                    status = get_employee_qualification_status_by_rules(emp_quals, project, event.get('title'),
                                                                        assignment_rules)

                    candidates.append({
                        "id": eid,
                        "name": emp.get('name'),
                        "quali_status": status,
                        "obj": emp
                    })

                # 2. Iteratives Auffüllen
                current_assigned_objects = []

                # Wenn schon jemand drin ist (durch Teil-Manuell o.ä.), übernehmen
                for existing_id in current_assignment["employees"]:
                    emp_obj = employee_map_by_id.get(existing_id)
                    if emp_obj:
                        # Quali holen für Score-Logik
                        q = employee_qualifications_map.get(existing_id)
                        s = get_employee_qualification_status_by_rules(q, project, event.get('title'), assignment_rules)
                        current_assigned_objects.append(
                            {"id": existing_id, "name": emp_obj.get('name'), "quali_status": s})

                while len(current_assignment["employees"]) < max_employees_per_slot and candidates:

                    ctx = {
                        "date_obj": current_dt.date(),
                        "engine": project.get('engine_type'),
                        "project_code": project.get('project_code'),
                        "task": event.get('title')
                    }

                    scored_candidates = []
                    for cand in candidates:
                        ctx['quali_status'] = cand['quali_status']
                        score = 0
                        if smart_scheduler:
                            score = smart_scheduler.calculate_score(cand, ctx, current_assigned_objects)
                        else:
                            score = 100 if cand['quali_status'] == 'X' else 50

                        # === OPTION 1: STICKY MEMORY BONUS (Das "Gedächtnis") ===
                        if previous_global_assignments:
                            shift_data = previous_global_assignments.get(slot_date_iso, {}).get(slot_shift, {})
                            if cand['id'].lower() in shift_data:
                                prev_details = shift_data[cand['id'].lower()]
                                # Prüfen, ob er genau für DASSELBE Projekt/Task eingeteilt war
                                if prev_details.get('project') == project.get('name') and prev_details.get(
                                        'task') == event.get('title'):
                                    score += 10000  # Massiver Bonus, damit er dort bleibt!

                        scored_candidates.append((score, cand))

                    # === DETERMINISTISCHE SORTIERUNG (Der Tie-Breaker) ===
                    # Sortiert 1. nach Score (absteigend: -x[0])
                    # und 2. nach ID (aufsteigend: x[1]['id']) als Schiedsrichter bei Gleichstand
                    scored_candidates.sort(key=lambda x: (-x[0], x[1]['id'].lower()))

                    # Besten auswählen
                    best_score, best_cand = scored_candidates[0]

                    # Zuweisen
                    current_assignment["employees"].append(best_cand['id'])
                    global_assignments_by_slot[slot_key_global].add(best_cand['id'])

                    current_assigned_objects.append(best_cand)
                    candidates.remove(best_cand)

            current_dt += timedelta(hours=12)
            slot_index += 1

    # 3. Footer berechnen
    footer_availability = {}
    timeline_start_datetime = datetime.combine(timeline_start_date, time(0, 0))
    for i in range(num_days * 2):
        col_dt = timeline_start_datetime + timedelta(hours=i * 12)
        slot_date_iso, slot_shift = col_dt.date().isoformat(), 'AM' if col_dt.hour < 12 else 'PM'
        slot_key_global = (slot_date_iso, slot_shift)

        available_ids = [e.get('id') for e in employees if
                         e.get('quali') in assignment_groups and is_employee_available_for_slot_py(e.get('id'),
                                                                                                   slot_date_iso,
                                                                                                   slot_shift,
                                                                                                   employee_availability_map,
                                                                                                   global_assignments_by_slot,
                                                                                                   manual_assignments_in_slot)]

        manual_blockers_in_slot = [e_id for e_id in manual_assignments_in_slot.get(slot_key_global, set()) if
                                   e_id not in global_assignments_by_slot.get(slot_key_global, set())]

        footer_availability[i] = {
            "available": [employee_map_by_id.get(eid.lower(), {}).get('initials', '??') for eid in available_ids],
            "manual": [employee_map_by_id.get(eid.lower(), {}).get('initials', '??') for eid in manual_blockers_in_slot]
        }

    # Rückgabe vorbereiten: Set in Liste umwandeln für JSON
    blocked_map_json = {}
    for k, v in global_assignments_by_slot.items():
        blocked_map_json[f"{k[0]}_{k[1]}"] = list(v)

    return {
        "assignments": assignments_by_event_slot,
        "footer_availability": footer_availability,
        "blocked_employees": blocked_map_json
    }

def get_or_create_project_status_details(project_data: Dict) -> Optional[Dict]:
    if not project_data: return None

    project_code = project_data.get('project_code', 'UNKNOWN')
    engine_serial = project_data.get('engine_serial', 'UNKNOWN')
    engine_type = project_data.get('engine_type')

    raw_project_id = f"{project_code}_{engine_serial}"
    safe_project_id = re.sub(r'[^a-zA-Z0-9_-]', '', raw_project_id)
    status_file_path = os.path.join('db', 'projectstatus', f'project_status_{safe_project_id}.json')

    if os.path.exists(status_file_path):
        try:
            with open(status_file_path, 'r', encoding='utf-8') as f:
                loaded_data = json.load(f)
                loaded_data['project_code'] = project_code
                loaded_data['engine_serial'] = engine_serial
                return loaded_data
        except Exception as e:
            print(
                f"WARNUNG: Status-JSON für {safe_project_id} konnte nicht gelesen werden, wird neu generiert. Fehler: {e}")

    templates = load_process_templates()
    template = find_best_template_for_engine(engine_type, templates)

    project_tasks = project_data.get('events', [])
    due_date = None
    if project_tasks:
        try:
            project_tasks_with_end_dt = [t for t in project_tasks if 'end_datetime' in t]
            if project_tasks_with_end_dt:
                project_tasks_with_end_dt.sort(key=lambda t: datetime.fromisoformat(t['end_datetime']))
                last_task_end_dt = datetime.fromisoformat(project_tasks_with_end_dt[-1]['end_datetime'])
                due_date = last_task_end_dt.date()
        except Exception:
            pass
    due_date_display = due_date.strftime("%d. %b %Y") if due_date else "N/A"

    steps_with_status = []
    for step_config in template.get('steps', []):
        steps_with_status.append({
            "id_str": step_config.get('id'), "parent_id_str": step_config.get('parent_id'),
            "type": "main", "is_expanded": True, "name": step_config.get('name'), "status": "Upcoming",
            "status_detail": "", "notes": "", "last_updated_by": None, "last_updated_at": None, "history": []
        })

    current_step_for_notes_obj = None
    if steps_with_status:
        sorted_steps = sorted(steps_with_status, key=lambda s: [int(p) for p in s['id_str'].split('.')])
        first_step = sorted_steps[0]
        current_step_for_notes_obj = {"name": first_step['name'], "id_str": first_step['id_str']}

    return {
        "name": project_data.get("name"),
        "engine_type": engine_type,
        "engine_serial": project_data.get("engine_serial", "N/A"),
        "project_code": project_code,
        "customer": project_data.get("customer", "N/A"),  # <-- HIER IST DER FIX
        "due_date_display": due_date_display,
        "progress_percent": 0,
        "steps": steps_with_status,
        "current_step_for_notes": current_step_for_notes_obj
    }


def get_employee_dashboard_data():
    if not ALL_EMPLOYEES or not ALL_QUALIFICATIONS:
        return {
            "total_employees": 0, "total_groups": 0, "total_features": 0,
            "employees_list": [], "groups_list": [], "skills_list": [],
            "full_employee_data": [], "full_qualification_data": []
        }

    df_emp = pd.DataFrame(ALL_EMPLOYEES)

    # KPIs berechnen
    total_employees = len(df_emp)
    total_groups = len(df_emp['quali'].dropna().unique())
    all_merkmale = {m['merkmal'] for q in ALL_QUALIFICATIONS for m in q.get('merkmale', [])}
    total_features = len(all_merkmale)

    # Filter-Listen erstellen
    employees_list = sorted(
        [{'name': name, 'id': eid.lower()} for name, eid in zip(df_emp['name'], df_emp['id']) if eid],
        key=lambda x: x['name'])
    groups_list = sorted(list(df_emp['quali'].dropna().unique()))
    skills_list = sorted(list(set(
        merkmal_string.split('/')[1]
        for merkmal_string in all_merkmale
        if isinstance(merkmal_string, str) and '/' in merkmal_string
    )))

    import copy
    employees_for_json = copy.deepcopy(ALL_EMPLOYEES)

    # Wir durchlaufen die Kopie und konvertieren alle Datumsobjekte in ISO-Strings.
    for emp in employees_for_json:
        if 'weeks' in emp:
            for week in emp['weeks']:
                if 'days' in week:
                    for day in week['days']:
                        if isinstance(day.get('date'), date):
                            day['date'] = day['date'].isoformat()  # z.B. date(2023, 11, 9) -> "2023-11-09"

    return {
        "total_employees": total_employees,
        "total_groups": total_groups,
        "total_features": total_features,
        "employees_list": employees_list,
        "groups_list": groups_list,
        "skills_list": skills_list,
        "full_employee_data": employees_for_json,  # Diese Liste enthält jetzt Strings statt date-Objekte
        "full_qualification_data": ALL_QUALIFICATIONS
    }


def load_assignment_rules(file_path: str):
    """Lädt die Zuordnungsregeln aus der angegebenen CSV-Datei."""
    if not os.path.exists(file_path):
        # Erstelle eine leere Datei mit Header, wenn sie nicht existiert
        try:
            with open(file_path, 'w', newline='', encoding='utf-8-sig') as f:
                writer = csv.writer(f)
                writer.writerow(ASSIGNMENT_RULES_FIELDNAMES)
            print(f"INFO: Leere Regeldatei '{file_path}' wurde erstellt.")
            return []
        except IOError as e:
            print(f"FEHLER beim Erstellen der Regeldatei '{file_path}': {e}")
            return []

    try:
        rules = []
        with open(file_path, mode='r', newline='', encoding='utf-8-sig') as file:
            reader = csv.DictReader(file)
            for row in reader:
                row['event_titles'] = [t.strip() for t in row.get('event_titles', '').split(',') if t.strip()]
                row['engine_types'] = [t.strip() for t in row.get('engine_types', '').split(',') if t.strip()]
                row['required_skills'] = [s.strip() for s in row.get('required_skills', '').split(',') if s.strip()]
                rules.append(row)
        return rules
    except Exception as e:
        print(f"FEHLER beim Laden der Zuordnungsregeln aus '{file_path}': {e}")
        return []


def save_assignment_rules(rules: List[Dict], file_path: str):
    """Speichert die Zuordnungsregeln in die angegebene CSV-Datei."""
    try:
        with open(file_path, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file, fieldnames=ASSIGNMENT_RULES_FIELDNAMES)
            writer.writeheader()
            for rule in rules:
                rule_to_write = rule.copy()
                rule_to_write['event_titles'] = ','.join(rule.get('event_titles', []))
                rule_to_write['engine_types'] = ','.join(rule.get('engine_types', []))
                rule_to_write['required_skills'] = ','.join(rule.get('required_skills', []))
                writer.writerow(rule_to_write)
        return True
    except Exception as e:
        print(f"FEHLER beim Speichern der Zuordnungsregeln in '{file_path}': {e}")
        return False


def is_employee_qualified_by_rules(emp_quals, project, event_title, assignment_rules):
    """
    Prüft anhand der dynamischen Regeln, ob ein Mitarbeiter qualifiziert ist.
    GIBT JETZT True/False ZURÜCK. Verwendet die zentrale VALID_ASSIGNABLE_STATUSES Konstante.
    """
    if not emp_quals:
        return False

    proj_engine_type = project.get('engine_type', '').strip()
    task_event_title = event_title.strip().upper()

    applicable_rules = []
    for rule in assignment_rules:
        event_match = not rule.get('event_titles') or task_event_title in rule.get('event_titles', [])
        engine_match = False
        if not rule.get('engine_types'):
            engine_match = True
        else:
            for rule_engine_prefix in rule.get('engine_types', []):
                if proj_engine_type.startswith(rule_engine_prefix):
                    engine_match = True
                    break
        if event_match and engine_match:
            applicable_rules.append(rule)

    if not applicable_rules:
        return False
    employee_valid_quals = {
        m['merkmal'] for m in emp_quals.get('merkmale', [])
        if m.get('wert') in VALID_ASSIGNABLE_STATUSES  # <-- Nutzung der neuen Konstante
    }

    for rule in applicable_rules:
        required_skills_for_rule = set(rule.get('required_skills', []))
        if required_skills_for_rule.issubset(employee_valid_quals):
            return True  # Ein Treffer genügt

    return False


def get_employee_qualification_status_by_rules(emp_quals: Optional[Dict], project: Dict, event_title: str,
                                               assignment_rules: List[Dict]) -> Optional[str]:
    """
    (VOLLSTÄNDIG KORRIGIERTE VERSION)
    Ermittelt den besten Qualifikationsstatus ('X', 'TS', 'TT', 'TP'), aber nur, wenn der Mitarbeiter
    auch tatsächlich nach den Regeln als qualifiziert gilt.
    """
    if not emp_quals:
        return None

    proj_engine_type = project.get('engine_type', '').strip()
    task_event_title = event_title.strip().upper()

    # 1. Anwendbare Regeln finden (Logik unverändert)
    applicable_rules = []
    for rule in assignment_rules:
        event_match = not rule.get('event_titles') or task_event_title in rule.get('event_titles', [])
        engine_match = False
        if not rule.get('engine_types'):
            engine_match = True
        else:
            for rule_engine_prefix in rule.get('engine_types', []):
                if proj_engine_type.startswith(rule_engine_prefix):
                    engine_match = True
                    break
        if event_match and engine_match and rule.get('required_skills'):
            applicable_rules.append(rule)

    if not applicable_rules:
        return None

    # 2. Hole die Skills des Mitarbeiters
    #    a) Eine Map mit allen Skills und ihren Werten (auch 'TP' etc.)
    employee_skills_map = {m.get('merkmal'): m.get('wert') for m in emp_quals.get('merkmale', [])}
    #    b) Ein Set NUR der Skills, die einen gültigen Zuweisungsstatus haben
    valid_assignable_skills_set = {
        m['merkmal'] for m in emp_quals.get('merkmale', [])
        if m.get('wert') in VALID_ASSIGNABLE_STATUSES  # <-- Nutzung der neuen Konstante
    }

    # 3. Finde den besten Status über alle Regeln hinweg, die der Mitarbeiter ERFÜLLT
    best_overall_status = None
    status_priority = {'X': 1, 'TS': 2, 'TT': 3, 'TP': 4}

    for rule in applicable_rules:
        required_skills_set = set(rule.get('required_skills', []))

        if required_skills_set.issubset(valid_assignable_skills_set):

            current_rule_status = 'X'  # Startannahme
            highest_priority_num = 0

            for skill in required_skills_set:
                skill_status = employee_skills_map.get(skill)

                if skill_status in status_priority and status_priority[skill_status] > highest_priority_num:
                    highest_priority_num = status_priority[skill_status]
                    current_rule_status = skill_status

            if best_overall_status is None or status_priority[current_rule_status] < status_priority[
                best_overall_status]:
                best_overall_status = current_rule_status

    return best_overall_status


def load_locations_and_events(locations_path=LOCATIONS_FILE_PATH, events_path=LOCATIONS_EVENTS_CSV_PATH):
    """
    Lädt Testzellen UND ihre manuell hinterlegten Events (Wartung, etc.).
    Dies ist die Basis für weitere Anreicherungen.
    """
    test_cells = {}
    if not os.path.exists(locations_path):
        print(f"WARNUNG: Standort-Datei {locations_path} nicht gefunden.")
        return []

    try:
        # 1. Testzellen-Stammdaten laden
        with open(locations_path, mode='r', newline='', encoding='utf-8-sig') as file:
            reader = csv.DictReader(file)
            for row in reader:
                row['TYPES'] = [t.strip() for t in row.get('TYPES', '').split(',') if t.strip()]
                row['REQUIRED_UNITS'] = [u.strip() for u in row.get('REQUIRED_UNITS', '').split(',') if u.strip()]

                row['events'] = []
                test_cells[row['ID']] = row

        # 2. Manuelle Events laden und zuordnen
        if os.path.exists(events_path):
            with open(events_path, mode='r', newline='', encoding='utf-8-sig') as file:
                reader = csv.DictReader(file)
                for row in reader:
                    cell_id = row.get('cell_id')
                    if cell_id in test_cells:
                        # NEU: Sicherstellen, dass neue Felder existieren, falls alte CSV-Datei verwendet wird
                        row['created_by'] = row.get('created_by', 'System')
                        row['created_at'] = row.get('created_at', '')
                        row['last_modified_by'] = row.get('last_modified_by', '')
                        row['last_modified_at'] = row.get('last_modified_at', '')
                        test_cells[cell_id]['events'].append(row)

        return list(test_cells.values())
    except Exception as e:
        print(f"FEHLER beim Laden von Standorten und Events: {e}")
        return []


def aggregate_operational_events(locations_with_manual_events, projects):
    """
    Reichert die Testzellen-Daten mit 'Operational' Events aus der Projekt-Timeline an.
    """
    locations_map = {loc['ID']: loc for loc in locations_with_manual_events}

    for proj in projects:
        if not proj.get('active'):
            continue

        # Finde die 'Test' Events in diesem Projekt
        project_test_events = [evt for evt in proj.get('events', []) if evt.get('title', '').lower() == 'test']

        for event_data in project_test_events:
            test_cell_id = event_data.get('assigned_test_cell_id')  # Fiktives Feld, das wir in der API-Route füllen

            if test_cell_id and test_cell_id in locations_map:
                operational_event = {
                    "event_id": f"op_{event_data.get('id')}",
                    "cell_id": test_cell_id,
                    "title": proj.get('project_code', 'Unbekanntes Projekt'),
                    "type": "Operational",
                    "start_date": event_data.get('start'),
                    "start_shift": event_data.get('start_shift'),
                    "end_date": event_data.get('end'),
                    "end_shift": event_data.get('end_shift'),
                    "description": f"Testlauf für {proj.get('name')}"
                }
                locations_map[test_cell_id]['events'].append(operational_event)

    return list(locations_map.values())


def get_dynamic_status_for_locations(locations_with_all_events):
    """
    Berechnet den aktuellen 'STATUS' für jede Testzelle,
    damit die alte Planungslogik weiterhin funktioniert.
    """
    now = datetime.now()
    today_iso = now.date().isoformat()
    current_shift = 'AM' if now.hour < 12 else 'PM'

    for cell in locations_with_all_events:
        cell['STATUS'] = 'AVAILABLE'  # Standard-Annahme
        for event in cell.get('events', []):
            # Prüfen, ob das Event jetzt gerade aktiv ist
            start_ok = (event['start_date'] < today_iso) or (
                    event['start_date'] == today_iso and event['start_shift'] <= current_shift)
            end_ok = (event['end_date'] > today_iso) or (
                    event['end_date'] == today_iso and event['end_shift'] >= current_shift)

            if start_ok and end_ok:
                event_type = event.get('type')
                if event_type == 'Blocked':
                    cell['STATUS'] = 'BLOCKED'
                    break  # Blocked hat die höchste Priorität
                elif event_type == 'Maintenance':
                    cell['STATUS'] = 'MAINTENANCE'
                    # Weiter suchen, falls noch ein 'Blocked' Event kommt
    return locations_with_all_events


def save_locations_and_events(data, locations_path=LOCATIONS_FILE_PATH, events_path=LOCATIONS_EVENTS_CSV_PATH):
    """
    Speichert die Testzellen-Stammdaten und die manuellen Events in die jeweiligen CSVs.
    """
    cell_headers = ['ID', 'TESTCELL', 'TYPES', 'REQUIRED_UNITS']
    event_headers = ['event_id', 'cell_id', 'title', 'type', 'start_date', 'start_shift', 'end_date', 'end_shift',
                     'description', 'created_by', 'created_at', 'last_modified_by', 'last_modified_at']

    all_manual_events = []
    cells_to_write = []

    for cell in data:
        cell_copy = {
            'ID': cell.get('ID'),
            'TESTCELL': cell.get('TESTCELL'),
            'TYPES': ','.join(cell.get('TYPES', [])),
            # NEU: Abhängigkeiten speichern
            'REQUIRED_UNITS': ','.join(cell.get('REQUIRED_UNITS', []))
        }
        cells_to_write.append(cell_copy)

        for event in cell.get('events', []):
            # WICHTIG: Virtuelle Events NICHT speichern!
            if event.get('isVirtual'): continue

            # WICHTIG: Archivierte Events NICHT wieder in die Live-CSV speichern!
            if event.get('isArchived'): continue

            if event.get('type') in ['Maintenance', 'Blocked']:
                # ... (Der Rest bleibt wie er ist) ...
                event_to_save = {h: event.get(h, '') for h in event_headers}
                all_manual_events.append(event_to_save)

    try:
        with open(locations_path, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file, fieldnames=cell_headers)
            writer.writeheader()
            writer.writerows(cells_to_write)

        with open(events_path, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file, fieldnames=event_headers)
            writer.writeheader()
            writer.writerows(all_manual_events)
        return True
    except IOError as e:
        print(f"FEHLER beim Speichern der Testzellen-Daten: {e}")
        return False


def inject_dependency_blocks(locations_list):
    """
    Injiziert virtuelle 'Blocked'-Events in Testzellen, wenn verknüpfte
    Betriebseinheiten (Units) durch ein kritisches Issue gesperrt sind.
    """
    # Schutz vor None-Input
    if locations_list is None:
        return []

    # 1. Alle Orders laden
    m_data = load_maintenance_data()
    orders = m_data.get('orders', [])

    # 2. Map erstellen: UnitUUID -> Liste von Blocker-Issues
    unit_blocker_map = {}

    for o in orders:
        # Prüfen auf Unit Blocker (neue Logik + Legacy Fallback)
        is_unit_blocker = o.get('isUnitBlocker')
        if is_unit_blocker is None:
            is_blocker = o.get('isBlocker')
            has_blocked_assets = bool(o.get('blockedAssetIds'))
            is_unit_blocker = is_blocker and not has_blocked_assets

        # Nur relevante Issues betrachten
        if o.get('type') == 'I' and is_unit_blocker:
            unit_id = o.get('facilityUnitId')
            if unit_id:
                if unit_id not in unit_blocker_map: unit_blocker_map[unit_id] = []
                unit_blocker_map[unit_id].append(o)

    # 3. Events injizieren
    today_iso = date.today().isoformat()

    for loc in locations_list:
        required_units = loc.get('REQUIRED_UNITS', [])
        # Sicherstellen, dass required_units eine Liste ist (falls CSV Fehler hat)
        if isinstance(required_units, str):
            required_units = [required_units]

        for req_unit_id in required_units:
            if req_unit_id in unit_blocker_map:
                issues = unit_blocker_map[req_unit_id]

                for issue in issues:
                    # Startdatum & Schicht
                    start_date = issue.get('date', today_iso)
                    start_shift = issue.get('startShift', 'AM')

                    # Enddatum & Schicht
                    if issue.get('status') == 'Completed':
                        end_date = issue.get('completedDate', today_iso)
                        end_shift = issue.get('completedShift', 'PM')
                    else:
                        end_date = '2099-12-31'
                        end_shift = 'PM'

                    virtual_event = {
                        "event_id": f"virt_block_{issue.get('id')}_{loc['ID']}",
                        "cell_id": loc['ID'],
                        "title": f"⛔ Sperre: {issue.get('plantIndex') or 'Anlage'}",
                        "type": "Blocked",
                        "start_date": start_date,
                        "start_shift": start_shift,
                        "end_date": end_date,
                        "end_shift": end_shift,
                        "description": f"Automatische Sperre durch Issue {issue.get('id')}: {issue.get('task')}",
                        "isVirtual": True,
                        "sourceIssueId": issue.get('id')
                    }

                    if 'events' not in loc: loc['events'] = []
                    loc['events'].append(virtual_event)

    return locations_list

def docx_find_replace(doc, replacements):
    """
    Sucht und ersetzt Text in einem docx-Dokument (Absätze und Tabellen),
    auch wenn die Platzhalter über mehrere "Runs" aufgeteilt sind.
    """
    for p in doc.paragraphs:
        if any(key in p.text for key in replacements):
            full_text = "".join(run.text for run in p.runs)
            original_full_text = full_text
            for key, value in replacements.items():
                full_text = full_text.replace(key, value)

            if original_full_text != full_text:
                style = p.runs[0].style if p.runs else None
                original_font = p.runs[0].font if p.runs and p.runs[0].font else None
                p.clear()

                new_run = p.add_run(full_text)
                if style:
                    new_run.style = style
                if original_font:
                    new_run.font.name = original_font.name
                    new_run.font.size = original_font.size
                    new_run.font.bold = original_font.bold
                    new_run.font.italic = original_font.italic
                    new_run.font.underline = original_font.underline
                    if original_font.color and original_font.color.rgb:
                        new_run.font.color.rgb = original_font.color.rgb

    for table in doc.tables:
        for row in table.rows:
            for cell in row.cells:
                # Behandle die Absätze in jeder Zelle wie ein eigenes Mini-Dokument
                temp_doc_for_cell = type('obj', (object,), {'paragraphs': cell.paragraphs, 'tables': []})()
                docx_find_replace(temp_doc_for_cell, replacements)


def build_file_tree(root_path):
    """Durchsucht einen Ordner rekursiv und erstellt eine flache Liste für die Baumansicht."""
    file_tree = []
    root_level = root_path.count(os.sep)
    for dirpath, dirnames, filenames in os.walk(root_path):
        # Berechne die Tiefe für die Einrückung im Frontend
        level = dirpath.count(os.sep) - root_level
        # Füge das aktuelle Verzeichnis hinzu (außer dem Root-Verzeichnis selbst)
        if level >= 0:
            dir_name = os.path.basename(dirpath)
            if dir_name != root_path:  # Füge das Root-Verzeichnis nicht als eigenen Eintrag hinzu
                relative_dir_path = os.path.relpath(dirpath, root_path)
                file_tree.append({'name': dir_name, 'path': relative_dir_path, 'type': 'dir', 'depth': level})

            # Sortiere die Unterordner und Dateien für eine konsistente Anzeige
            dirnames.sort()
            filenames.sort()

            # Füge die Dateien im aktuellen Verzeichnis hinzu
            for f in filenames:
                relative_file_path = os.path.relpath(os.path.join(dirpath, f), root_path)
                file_tree.append({'name': f, 'path': relative_file_path, 'type': 'file', 'depth': level + 1})

    # Sortiere die gesamte Liste, um sicherzustellen, dass Ordner vor Dateien im selben Level kommen
    file_tree.sort(key=lambda x: (x['path'].split(os.sep), x['type'] == 'file'))
    return file_tree


def load_process_templates():
    """
    Lädt die Prozess-Templates aus der JSON-Datei.
    Erstellt die Datei mit einem Standard-Template, falls sie nicht existiert.
    """
    if not os.path.exists(PROCESS_TEMPLATES_FILE_PATH):
        print(f"WARNUNG: '{PROCESS_TEMPLATES_FILE_PATH}' nicht gefunden. Erstelle eine Standard-Konfigurationsdatei.")
        default_templates = {
            "default": {
                "steps": [
                    {"name": "Rigging", "document_template": None},
                    {"name": "Test", "document_template": None},
                    {"name": "Paperwork", "document_template": None},
                    {"name": "De-Rigging", "document_template": None},
                    {"name": "After Test Work", "document_template": None},
                    {"name": "BSI", "document_template": None}
                ]
            }
        }
        try:
            with open(PROCESS_TEMPLATES_FILE_PATH, 'w', encoding='utf-8') as f:
                json.dump(default_templates, f, indent=2, ensure_ascii=False)
            print(f"INFO: Standard-Template-Datei '{PROCESS_TEMPLATES_FILE_PATH}' wurde erstellt.")
            return default_templates
        except IOError as e:
            print(f"FEHLER beim Erstellen der Template-Datei: {e}")
            return default_templates  # Gibt im Fehlerfall immer noch den Default-Wert zurück

    try:
        with open(PROCESS_TEMPLATES_FILE_PATH, 'r', encoding='utf-8') as f:
            return json.load(f)
    except (IOError, json.JSONDecodeError) as e:
        print(f"FEHLER beim Laden von process_templates.json: {e}")
        # Wichtiger Fallback, damit die App nicht abstürzt
        return {"default": {"steps": []}}


def find_best_template_for_engine(engine_type, templates):
    """
    Findet das passendste Prozess-Template für einen gegebenen Triebwerkstyp.
    Priorität: 1. Exakte Übereinstimmung -> 2. Längste Präfix-Übereinstimmung -> 3. Default.
    """
    if not engine_type or not templates:
        return templates.get('default')

    # 1. Höchste Priorität: Exakte Übereinstimmung
    if engine_type in templates:
        return templates[engine_type]

    # 2. Zweite Priorität: Längste Präfix-Übereinstimmung
    matching_keys = [key for key in templates.keys() if engine_type.startswith(key) and key != 'default']
    if matching_keys:
        # Finde den längsten (spezifischsten) Schlüssel unter den Treffern
        best_match_key = max(matching_keys, key=len)
        return templates[best_match_key]

    # 3. Letzte Priorität: Fallback auf das Default-Template
    return templates.get('default')


def calculate_dynamic_status(status: str, start_date_str: str, end_date_str: str) -> str:
    """
    Prüft, ob ein Training mit Status 'TS' (Gestartet) basierend auf dem Startdatum
    älter als ein Jahr ist und noch kein Abschlussdatum hat. Gibt dann 'TT' (Überfällig) zurück.
    Ein Status 'X' (Abgeschlossen) wird immer respektiert und nie geändert.
    """
    # 1. Wenn der Status bereits 'Abgeschlossen' ist oder kein Startdatum hat, nichts tun.
    #    Auch alle anderen Status ('TP', 'TT' etc.) werden nicht angefasst.
    if status != 'TS' or not start_date_str:
        return status

    # 2. Wenn bereits ein Abschlussdatum gesetzt ist, gilt das Training als abgeschlossen,
    #    auch wenn der Status noch nicht 'X' ist. In diesem Fall nichts ändern.
    if end_date_str:
        return status

    # 3. Parsen des Startdatums. Bei Fehler Originalstatus zurückgeben.
    try:
        start_date = parse_date_from_string(start_date_str)
        if not start_date:
            return status
    except (ValueError, TypeError):
        return status

    # 4. Berechne das "Ablaufdatum" (1 Jahr nach Start)
    expiry_date = start_date + timedelta(days=365)
    today = date.today()

    # 5. Wenn das heutige Datum nach dem Ablaufdatum liegt, ist es überfällig.
    if today > expiry_date:
        return 'TT'
    else:
        return status


def get_certificate_metadata(employee_id: str) -> List[Dict]:
    """Liest die Metadaten-JSON für einen Mitarbeiter (z.B. mmuster.json)."""
    os.makedirs(CERTIFICATES_FOLDER_PATH, exist_ok=True)  # Sicherstellen, dass der Hauptordner existiert
    metadata_path = os.path.join(CERTIFICATES_FOLDER_PATH, f"{secure_filename(employee_id)}.json")
    if os.path.exists(metadata_path):
        try:
            with open(metadata_path, 'r', encoding='utf-8') as f:
                return json.load(f)
        except (IOError, json.JSONDecodeError):
            return []
    return []


def save_certificate_metadata(employee_id: str, metadata: List[Dict]):
    """Schreibt die Metadaten-JSON für einen Mitarbeiter."""
    os.makedirs(CERTIFICATES_FOLDER_PATH, exist_ok=True)
    metadata_path = os.path.join(CERTIFICATES_FOLDER_PATH, f"{secure_filename(employee_id)}.json")
    try:
        with open(metadata_path, 'w', encoding='utf-8') as f:
            json.dump(metadata, f, indent=2, ensure_ascii=False)
    except IOError as e:
        print(f"FEHLER beim Schreiben der Metadaten für {employee_id}: {e}")


def get_aggregated_test_cell_data():
    """
    Sammelt alle Testzellen-Daten (manuell & operativ) aus allen Quellen.
    Dies ist die "Single Source of Truth" für den globalen Testzellen-Status.
    """
    # 1. Lade Testzellen-Stammdaten und manuelle Events
    locations_with_manual_events = load_locations_and_events()

    # Dependency Injection (Blocker übertragen)
    locations_with_manual_events = inject_dependency_blocks(locations_with_manual_events)
    if locations_with_manual_events is None:
        locations_with_manual_events = []

    all_test_cell_ids = [loc.get('ID') for loc in locations_with_manual_events if loc.get('ID')]

    # 2. Lade ALLE Projekte aus ALLEN konfigurierten Quellen
    all_projects = project_manager.get_all_active_projects()

    # 3. Bereite Projekte für die Berechnung vor
    import copy
    projects_for_calc = [copy.deepcopy(p) for p in all_projects if p]

    # --- FIX: ISO-Zeitstempel für den Algorithmus generieren ---
    for project in projects_for_calc:
        project_id_prefix = f"{project.get('project_code')}_{project.get('engine_serial')}"
        events_with_dt = []
        for i, evt in enumerate(project.get('events', [])):
            if not isinstance(evt, dict): continue

            if 'id' not in evt or not evt['id']:
                evt['id'] = f"{project_id_prefix}_{evt.get('title', 'event')}_{i}"

            start_dt = get_datetime_from_date_shift(parse_date_from_string(evt.get('start')), evt.get('start_shift'))
            end_dt = get_datetime_from_date_shift(parse_date_from_string(evt.get('end')), evt.get('end_shift'))

            if start_dt and end_dt:
                evt['start_datetime'] = start_dt.isoformat()
                evt['end_datetime'] = end_dt.isoformat()
                events_with_dt.append(evt)
        project['events'] = events_with_dt
    # -----------------------------------------------------------

    # 4. Führe eine Berechnung durch, um die Testzellen-Zuweisungen zu erhalten
    today = date.today()
    # 4 Wochen Historie für das schnelle Live-Fenster reichen aus, Rest kommt aus dem Archiv
    timeline_start_date = (today - timedelta(days=today.weekday())) - timedelta(weeks=4)
    num_days = 40 * 7

    calculated_data = calculate_timeline_assignments(
        projects=projects_for_calc, employees=[], qualifications=[], manual_assignments=[],
        timeline_start_date=timeline_start_date, num_days=num_days, assignment_rules=[],
        selected_test_cell_ids=all_test_cell_ids
    )

    # 5. Füge die berechnete Zuweisung zu den Projekt-Events hinzu
    assignments = calculated_data.get('assignments', {})
    for proj in projects_for_calc:
        for evt in proj.get('events', []):
            if evt.get('title', '').lower() == 'test':
                for i in range(100):
                    event_slot_key = f"{evt.get('id', '')}_{i}"
                    if event_slot_key in assignments:
                        assignment_info = assignments[event_slot_key]
                        if assignment_info and assignment_info.get("test_cell_id"):
                            evt['assigned_test_cell_id'] = assignment_info.get("test_cell_id")
                            break
                    else:
                        break

    # 6. Reichere die Daten mit den "Operational" Events an
    return aggregate_operational_events(locations_with_manual_events, projects_for_calc)


def order_system_setup_data_files():
    """Stellt sicher, dass das Datenverzeichnis und die JSON-Dateien für das Bestellsystem existieren."""
    if not os.path.exists(ORDER_DATA_DIR):
        os.makedirs(ORDER_DATA_DIR)

    if not os.path.exists(ORDER_ORDERS_FILE):
        initial_orders = [
            {"id": 1, "name": "Druckerpapier (A4)", "quantity": "5 Pakete", "requester": "Alice", "status": "requested",
             "createdAt": "2024-07-29T10:00:00.000Z", "notes": "Bitte extra dickes Papier (100g/m²).",
             "isCritical": True, "stdOrderNr": "STD-A-123", "supplier": "Office-Discount",
             "link": "https://www.office-discount.de/papier", "storageLocation": "Lager A, Regal 3"},
            {"id": 2, "name": "Kaffeebohnen", "quantity": "2 kg", "requester": "Bob", "status": "requested",
             "createdAt": "2024-07-28T14:30:00.000Z", "notes": "", "isCritical": False, "stdOrderNr": "",
             "supplier": "", "link": "", "storageLocation": "Küche, Schrank 1"},
            {"id": 6, "name": "Zucker", "quantity": "1 Kiste", "requester": "Charlie", "status": "completed",
             "createdAt": "2024-07-20T10:00:00.000Z", "notes": "", "isCritical": False, "orderedAt": "2024-07-21",
             "eta": "2024-07-25", "orderedBy": "Debug User", "deliveredAt": "2024-07-24", "batchNumber": "CH-123",
             "expiryDate": "2026-12-31", "stdOrderNr": "", "supplier": "", "link": "",
             "storageLocation": "Küche, Schrank 2", "documentationChecked": True,
             "documentationFile": "lieferschein_zucker.pdf"}
        ]
        with open(ORDER_ORDERS_FILE, 'w', encoding='utf-8') as f:
            json.dump(initial_orders, f, indent=4, ensure_ascii=False)

    if not os.path.exists(ORDER_TEMPLATES_FILE):
        initial_templates = [
            {"name": "Druckerpapier (A4)", "quantity": "10 Pakete", "notes": "Standard 80g/m²", "isCritical": False,
             "stdOrderNr": "STD-A-123", "supplier": "Office-Discount", "link": "https://www.office-discount.de/papier",
             "storageLocation": "Lager A, Regal 3"},
            {"name": "Kaffeebohnen (Espresso)", "quantity": "5 kg", "notes": "", "isCritical": False, "stdOrderNr": "",
             "supplier": "Kaffee-Rösterei", "link": "", "storageLocation": "Küche, Schrank 1"}
        ]
        with open(ORDER_TEMPLATES_FILE, 'w', encoding='utf-8') as f:
            json.dump(initial_templates, f, indent=4, ensure_ascii=False)


def order_system_load_data():
    """Lädt die Daten für das Bestellsystem aus den JSON-Dateien."""
    # Ruft die Setup-Funktion auf, um sicherzustellen, dass die Dateien existieren
    order_system_setup_data_files()
    with open(ORDER_ORDERS_FILE, 'r', encoding='utf-8') as f:
        orders = json.load(f)
    with open(ORDER_TEMPLATES_FILE, 'r', encoding='utf-8') as f:
        templates = json.load(f)
    return orders, templates


def order_system_save_data(data):
    """Speichert die übergebenen Daten in die entsprechenden JSON-Dateien des Bestellsystems."""
    if 'orders' in data:
        with open(ORDER_ORDERS_FILE, 'w', encoding='utf-8') as f:
            json.dump(data['orders'], f, indent=4, ensure_ascii=False)
    if 'templates' in data:
        with open(ORDER_TEMPLATES_FILE, 'w', encoding='utf-8') as f:
            json.dump(data['templates'], f, indent=4, ensure_ascii=False)


def load_maintenance_data():
    """
    Lädt Wartungsdaten aus den drei getrennten Dateien.
    Führt eine Migration durch, falls die neuen Dateien fehlen, aber die alte existiert.
    """
    data = {
        "orders": [],
        "definitions": [],
        "companies": [],
        "structure": []
    }

    # Helper zum Laden einer einzelnen Datei
    def load_json_file(filepath, default_val):
        if os.path.exists(filepath):
            try:
                with open(filepath, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except Exception as e:
                print(f"Fehler beim Laden von {filepath}: {e}")
        return default_val

    # Prüfen, ob wir migrieren müssen (wenn ORDERS Datei fehlt, aber ALTE Datei da ist)
    if not os.path.exists(MAINTENANCE_ORDERS_FILE) and os.path.exists(MAINTENANCE_DATA_FILE_OLD):
        print("INFO: Starte Migration von alter single-file zu multi-file Struktur...")
        try:
            with open(MAINTENANCE_DATA_FILE_OLD, 'r', encoding='utf-8') as f:
                old_data = json.load(f)

            # Daten verteilen
            data["orders"] = old_data.get("orders", [])
            data["definitions"] = old_data.get("definitions", [])
            data["companies"] = old_data.get("companies", [])  # Falls schon da

            # Sofort in neue Struktur speichern
            save_maintenance_orders(data["orders"])
            save_maintenance_definitions(data["definitions"])
            save_maintenance_companies(data["companies"])
            print("INFO: Migration erfolgreich abgeschlossen.")
            return data
        except Exception as e:
            print(f"FEHLER bei der Migration: {e}")

    # Normales Laden der getrennten Dateien
    data["orders"] = load_json_file(MAINTENANCE_ORDERS_FILE, [])
    data["definitions"] = load_json_file(MAINTENANCE_DEFINITIONS_FILE, [])
    data["companies"] = load_json_file(MAINTENANCE_COMPANIES_FILE, [])

    return data


def save_json_file(filepath, data):
    """
    Speichert Daten atomar und Thread-sicher (Race-Condition-Schutz).
    """
    # 1. Einzigartiger Temp-Name für DIESEN Schreibvorgang (verhindert Kollision in der .tmp Datei)
    dir_name = os.path.dirname(filepath)
    unique_id = uuid.uuid4().hex
    temp_filepath = f"{filepath}.{unique_id}.tmp"

    # 2. Globaler Lock: Nur EINER darf den kritischen Bereich betreten
    with file_write_lock:
        try:
            os.makedirs(dir_name, exist_ok=True)

            # Schreiben in die exklusive Temp-Datei
            with open(temp_filepath, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
                f.flush()
                os.fsync(f.fileno())

            # Atomares Ersetzen (Das ist der einzige Moment, wo die echte Datei berührt wird)
            os.replace(temp_filepath, filepath)
            return True

        except Exception as e:
            print(f"!!! KRITISCHER FEHLER beim Speichern von {filepath}: {e}")
            traceback.print_exc()
            # Aufräumen bei Fehler
            if os.path.exists(temp_filepath):
                try:
                    os.remove(temp_filepath)
                except OSError:
                    pass
            return False


def save_maintenance_orders(orders_data):
    # Wrapper, um sicherzustellen, dass immer die Atomic-Funktion genutzt wird
    return save_json_file(MAINTENANCE_ORDERS_FILE, orders_data)


def save_maintenance_definitions(definitions_data):
    # Wrapper, um sicherzustellen, dass immer die Atomic-Funktion genutzt wird
    return save_json_file(MAINTENANCE_DEFINITIONS_FILE, definitions_data)


def save_maintenance_companies(companies_data):
    # Wrapper, um sicherzustellen, dass immer die Atomic-Funktion genutzt wird
    return save_json_file(MAINTENANCE_COMPANIES_FILE, companies_data)


def sync_maintenance_links_to_hardware(maintenance_orders):
    """
    Aktualisiert Hardware-Daten (Verknüpfungen zu Wartungen & Issues).
    Nutzt Definitionen für aktuelle Titel.
    Unterstützt ID- und String-Matches.
    """
    try:
        te_data = load_test_equipment_data()
        if te_data is None: return

        inventory = te_data.get('inventory', {})
        today_iso = date.today().isoformat()

        # --- 0. DEFINITIONEN LADEN (Für aktuelle Titel) ---
        definitions_map = {}
        try:
            if os.path.exists(MAINTENANCE_DEFINITIONS_FILE):
                with open(MAINTENANCE_DEFINITIONS_FILE, 'r', encoding='utf-8') as f:
                    defs = json.load(f)
                    for d in defs:
                        if d.get('id'):
                            definitions_map[d['id']] = d.get('task', 'Unbenannter Plan')
        except Exception as e:
            print(f"Warnung beim Laden der Definitionen für Sync: {e}")

        # --- 1. DATEN VORBEREITEN ---
        id_to_sn_map = {}
        sn_to_id_map = {}

        for pn, part_data in inventory.items():
            for serial_entry in part_data.get('serials', []):
                hw_id = serial_entry.get('id')
                sn = serial_entry.get('sn')
                if hw_id:
                    id_to_sn_map[hw_id] = {'sn': sn, 'pn': pn}
                if sn:
                    sn_to_id_map[sn] = hw_id

                    # --- 2. ORDERS ANALYSIEREN ---
        asset_active_plans = {}
        asset_last_checks = {}
        asset_issues = {}
        asset_next_due = {}

        for order in maintenance_orders:
            target_asset_ids = set()

            # Assets sammeln (JSON & String Support)
            raw_assets = []
            if order.get('maintenanceObjects'):
                raw_assets.extend(order.get('maintenanceObjects'))
            elif order.get('maintenanceObject'):
                raw_assets.append(order.get('maintenanceObject'))
            if order.get('affectedAssets'):
                raw_assets.extend(order.get('affectedAssets'))
            elif order.get('affectedAsset'):
                raw_assets.append(order.get('affectedAsset'))

            for raw in raw_assets:
                if not raw: continue
                if raw.strip().startswith('{'):
                    try:
                        obj = json.loads(raw)
                        if obj.get('id'): target_asset_ids.add(obj.get('id'))
                    except:
                        pass
                else:
                    sn_part = raw.split(' ')[0]
                    if sn_part in sn_to_id_map:
                        target_asset_ids.add(sn_to_id_map[sn_part])

            link_id = order.get('parentTaskId') or order.get('id')
            status = order.get('status')
            order_date = order.get('date', '')

            for asset_id in target_asset_ids:
                if not asset_id: continue

                # ISSUE LOGIK
                if order.get('type') == 'I' and status != 'Completed':
                    if asset_id not in asset_issues: asset_issues[asset_id] = {'count': 0, 'critical': False,
                                                                               'list': []}
                    asset_issues[asset_id]['count'] += 1
                    asset_issues[asset_id]['list'].append(
                        {'id': order.get('id'), 'title': order.get('task'), 'priority': order.get('priority')})
                    if order.get('priority') == 'Critical' or order.get('isBlocker'): asset_issues[asset_id][
                        'critical'] = True

                # WARTUNGS LOGIK
                if order.get('type') == 'M' or not order.get('type'):
                    if status == 'Completed' and order.get('completedDate'):
                        if asset_id not in asset_last_checks: asset_last_checks[asset_id] = {}
                        curr = asset_last_checks[asset_id].get(link_id)
                        if not curr or order.get('completedDate') > curr:
                            asset_last_checks[asset_id][link_id] = order.get('completedDate')

                    if status != 'Completed' and status != 'Review Ready':
                        if asset_id not in asset_active_plans: asset_active_plans[asset_id] = set()
                        asset_active_plans[asset_id].add(link_id)

                        if order_date:
                            if asset_id not in asset_next_due:
                                asset_next_due[asset_id] = order_date
                            elif order_date < asset_next_due[asset_id]:
                                asset_next_due[asset_id] = order_date

        # --- 3. INVENTAR UPDATEN ---
        changes_made = False
        for pn, part_data in inventory.items():
            for serial_entry in part_data.get('serials', []):
                hw_id = serial_entry.get('id') or sn_to_id_map.get(serial_entry.get('sn'))
                if not hw_id: continue

                new_links_list = []
                if hw_id in asset_active_plans:
                    for plan_id in asset_active_plans[hw_id]:
                        last_date = "N/A"
                        if hw_id in asset_last_checks and plan_id in asset_last_checks[hw_id]:
                            last_date = asset_last_checks[hw_id][plan_id]

                        # --- TITEL LOGIK: Definition > Auftrag ---
                        plan_title = "Unbekannter Plan"
                        if plan_id in definitions_map:
                            plan_title = definitions_map[plan_id]
                        else:
                            # Fallback: Suche in Orders
                            found = next((o for o in maintenance_orders if o.get('id') == plan_id), None)
                            if found: plan_title = found.get('task', 'Unbekannt')

                        new_links_list.append({"id": plan_id, "title": plan_title, "lastCheck": last_date})

                new_links_list.sort(key=lambda x: x['id'])

                traffic_light = "GREEN"
                next_due = asset_next_due.get(hw_id)
                issues = asset_issues.get(hw_id)

                if issues and issues['critical']:
                    traffic_light = "RED"
                elif next_due and next_due < today_iso:
                    traffic_light = "RED"
                elif issues and issues['count'] > 0:
                    traffic_light = "YELLOW"
                elif next_due:
                    try:
                        if datetime.strptime(next_due, "%Y-%m-%d").date() <= (
                                date.today() + timedelta(days=14)): traffic_light = "YELLOW"
                    except:
                        pass
                elif not new_links_list:
                    traffic_light = "NONE"

                new_status_summary = {
                    "light": traffic_light,
                    "nextDue": next_due or "N/A",
                    "openIssues": issues['count'] if issues else 0,
                    "issueList": issues['list'] if issues else []
                }

                current_links = serial_entry.get('maintenanceLinks', [])
                current_status = serial_entry.get('statusSummary', {})

                if (json.dumps(current_links, sort_keys=True) != json.dumps(new_links_list, sort_keys=True) or
                        json.dumps(current_status, sort_keys=True) != json.dumps(new_status_summary, sort_keys=True)):
                    serial_entry['maintenanceLinks'] = new_links_list
                    serial_entry['statusSummary'] = new_status_summary
                    changes_made = True

        if changes_made:
            save_test_equipment_data_to_file(te_data)
            print(f"INFO: Hardware-Daten (inkl. aktuellen Titeln) synchronisiert.")

    except Exception as e:
        print(f"FEHLER beim Synchronisieren: {e}")
        traceback.print_exc()



def get_flat_facility_units(nodes, prefix=""):
    """
    Rekursive Hilfsfunktion, die den Strukturbaum in eine flache Liste
    für Dropdowns umwandelt.
    Rückgabe: [{'id': 'uuid...', 'name': '1. Halle Nord', 'path': '0'}, ...]
    """
    result = []
    for i, node in enumerate(nodes):
        # Index für die Anzeige generieren (z.B. "1.1")
        current_index_str = f"{prefix}{i + 1}"
        display_name = f"{current_index_str} {node.get('name', 'Unbekannt')}"

        result.append({
            'id': node.get('id'),
            'name': display_name,
            'original_name': node.get('name'),
            'type': 'unit'  # Marker, falls wir später Gebäude unterscheiden wollen
        })

        if 'children' in node:
            result.extend(get_flat_facility_units(node['children'], current_index_str + "."))

    return result


def init_maintenance_structure():
    """Erstellt die Ordnerstruktur für Maintenance."""
    if not os.path.exists(MAINTENANCE_DATA_DIR):
        os.makedirs(MAINTENANCE_DATA_DIR)
        print(f"INFO: Ordner {MAINTENANCE_DATA_DIR} erstellt.")

    if not os.path.exists(MAINTENANCE_UPLOADS_DIR):
        os.makedirs(MAINTENANCE_UPLOADS_DIR)
        print(f"INFO: Ordner {MAINTENANCE_UPLOADS_DIR} erstellt.")


# --- NEU: ProjectManager Klasse ---
class ProjectManager:
    def __init__(self):
        self.projects_cache = []
        self.timeline_file_map = {}  # Map: timeline_id -> file_path
        self.reload_config()

    def reload_config(self):
        """Lädt die Konfiguration und baut die Maps auf."""
        config = load_app_config()
        self.timeline_file_map = {}
        all_files = set()

        for tl in config.get('timelines', []):
            # Editable Timelines haben EINE Datei
            if tl.get('type') == 'editable' and tl.get('data_file'):
                self.timeline_file_map[tl['id']] = [tl['data_file']]
                all_files.add(tl['data_file'])

            # Statische Timelines können MEHRERE Dateien haben
            if tl.get('type') == 'static' and tl.get('data_files'):
                self.timeline_file_map[tl['id']] = tl['data_files']
                for f in tl['data_files']:
                    all_files.add(f)

        # Lade alle Daten einmalig in den Cache
        self.load_all_files(list(all_files))

    def load_all_files(self, file_paths):
        """Lädt alle Projekte aus den Dateien in den Cache."""
        self.projects_cache = load_timeline_projects_for_flask(file_paths)
        print(f"ProjectManager: {len(self.projects_cache)} Projekte global geladen.")

    def get_all_active_projects(self):
        """Liefert alle aktiven Projekte systemweit."""
        return [p for p in self.projects_cache if p.get('active')]

    def get_projects_for_timeline(self, timeline_id):
        """Filtert Projekte für eine spezifische Timeline."""
        # 1. Welche Dateien gehören zu dieser Timeline?
        files = self.timeline_file_map.get(timeline_id, [])
        if not files: return []

        # ACHTUNG: load_timeline_projects_for_flask lädt aktuell vom Dateisystem.
        # Um den Cache zu nutzen, müssten wir wissen, aus welcher Datei ein Projekt kam.
        # Da unsere aktuelle load-Funktion das nicht speichert (sie merged alles),
        # müssen wir für diesen Schritt pragmatisch sein:
        # Wir laden für die Timeline-Ansicht (noch) direkt, um Konsistenz zu den Filtern zu wahren.

        # OPTIMIERUNG:
        # Langfristig sollte load_timeline_projects jedem Projekt ein Feld 'source_file' geben.
        # Dann könnten wir hier einfach filtern:
        # return [p for p in self.projects_cache if p['source_file'] in files]

        return load_timeline_projects_for_flask(files)

    def save_projects(self, projects, timeline_id):
        """Speichert Projekte in die Datei der Timeline (nur für Editable)."""
        files = self.timeline_file_map.get(timeline_id)
        if not files or len(files) > 1:
            print(f"Fehler: Speichern für Timeline {timeline_id} nicht möglich (Keine oder mehrere Dateien).")
            return False

        target_file = files[0]
        if save_projects_for_flask(target_file, projects):
            # Cache aktualisieren (einfachste Methode: Alles neu laden)
            self.reload_config()
            return True
        return False

# Globale Instanz
project_manager = ProjectManager()

class GlobalAssignmentManager:
    def __init__(self):
        self.assignments = {}
        self.load()

    def load(self):
        if os.path.exists(GLOBAL_ASSIGNMENTS_FILE):
            try:
                with open(GLOBAL_ASSIGNMENTS_FILE, 'r', encoding='utf-8') as f:
                    self.assignments = json.load(f)
            except:
                self.assignments = {}

    def save(self):
        with open(GLOBAL_ASSIGNMENTS_FILE, 'w', encoding='utf-8') as f:
            json.dump(self.assignments, f, indent=2)

    def set_assignment(self, date_iso, shift, user_id, details):
        print(f"DEBUG SET: {date_iso} {shift} {user_id}")
        print(f"DEBUG INSTANCE ID: {id(self)}")
        if date_iso not in self.assignments: self.assignments[date_iso] = {}
        if shift not in self.assignments[date_iso]: self.assignments[date_iso][shift] = {}

        # Conflict Check könnte hier passieren
        self.assignments[date_iso][shift][user_id] = details

    def clear_assignments_for_date(self, date_iso):
        if date_iso in self.assignments:
            del self.assignments[date_iso]

    def get_assignments_for_day(self, date_iso):
        return self.assignments.get(date_iso, {})

    def get_busy_employees_in_range(self, start_date, end_date, ignore_timeline_id=None):
        busy_map = {}  # Key: (date_iso, shift), Value: Set of user_ids

        current = start_date
        while current <= end_date:
            date_iso = current.isoformat()
            if date_iso in self.assignments:
                for shift, users in self.assignments[date_iso].items():
                    key = (date_iso, shift)
                    if key not in busy_map: busy_map[key] = set()

                    for uid, details in users.items():
                        # WICHTIG: Ignoriere Einträge der eigenen Timeline!
                        if ignore_timeline_id and details.get('timeline') == ignore_timeline_id:
                            continue
                        busy_map[key].add(uid)
            current += timedelta(days=1)
        return busy_map

    def load_history(self):
        if os.path.exists(ASSIGNMENT_HISTORY_FILE):
            try:
                with open(ASSIGNMENT_HISTORY_FILE, 'r', encoding='utf-8') as f:
                    return json.load(f)
            except:
                return {}
        return {}

    def save_history(self, history_data):
        with open(ASSIGNMENT_HISTORY_FILE, 'w', encoding='utf-8') as f:
            json.dump(history_data, f, indent=2)

    def archive_old_entries(self):
        """Verschiebt alles älter als 5 Tage ins Archiv."""
        today = date.today()
        cutoff_date = today - timedelta(days=5)

        history = self.load_history()
        keys_to_delete = []

        for date_iso, shifts in self.assignments.items():
            if date_iso < cutoff_date.isoformat():
                # Ins Archiv kopieren
                history[date_iso] = shifts
                keys_to_delete.append(date_iso)

        # Aus Live löschen
        for k in keys_to_delete:
            del self.assignments[k]

        if keys_to_delete:
            self.save_history(history)
            self.save()
            print(f"INFO: {len(keys_to_delete)} Tage ins Archiv verschoben.")

# Globale Instanz
global_scheduler = GlobalAssignmentManager()


def load_testcell_history():
    if os.path.exists(TESTCELL_HISTORY_FILE):
        try:
            with open(TESTCELL_HISTORY_FILE, 'r', encoding='utf-8') as f:
                return json.load(f)
        except:
            pass
    return {}


def save_testcell_history(data):
    return save_json_file(TESTCELL_HISTORY_FILE, data)


def archive_old_testcell_events():
    """
    Zieht einen Snapshot der berechneten Testzellen-Auslastung und
    archiviert alles, was älter als 5 Tage ist. Löscht alte manuelle Events aus der CSV.
    """
    print("--- Starte Testzellen Archivierung ---")
    cutoff_date = date.today() - timedelta(days=5)
    cutoff_iso = cutoff_date.isoformat()

    history = load_testcell_history()

    # 1. Snapshot der "Allwissenden Ansicht" holen
    all_data = get_aggregated_test_cell_data()

    archived_count = 0
    for cell in all_data:
        for event in cell.get('events', []):
            # Nur archivieren, wenn das Event beendet ist und das Ende vor dem Cutoff liegt
            if event.get('end_date', '') < cutoff_iso:
                event_id = event.get('event_id')
                if event_id:
                    # Wir nutzen ein Dictionary (Key=Event-ID), das verhindert Duplikate.
                    # Bereits archivierte Events werden ggf. mit ihrem finalen Status geupdatet.
                    history[event_id] = event
                    archived_count += 1

    save_testcell_history(history)
    print(f"INFO: {archived_count} Events im Archiv aktualisiert/gespeichert.")

    # 2. Aufräumen der manuellen CSV (Nur Zukunft & letzte 5 Tage behalten)
    if os.path.exists(LOCATIONS_EVENTS_CSV_PATH):
        try:
            manual_events = []
            with open(LOCATIONS_EVENTS_CSV_PATH, mode='r', newline='', encoding='utf-8-sig') as f:
                reader = csv.DictReader(f)
                for row in reader:
                    # Wir behalten nur Events, die NACH dem Cutoff enden
                    if row.get('end_date', '') >= cutoff_iso:
                        manual_events.append(row)

            # CSV überschreiben
            event_headers = ['event_id', 'cell_id', 'title', 'type', 'start_date', 'start_shift', 'end_date',
                             'end_shift', 'description', 'created_by', 'created_at', 'last_modified_by',
                             'last_modified_at']
            with open(LOCATIONS_EVENTS_CSV_PATH, mode='w', newline='', encoding='utf-8-sig') as f:
                writer = csv.DictWriter(f, fieldnames=event_headers)
                writer.writeheader()
                writer.writerows(manual_events)
            print(f"INFO: Testzellen CSV aufgeräumt. Es verbleiben {len(manual_events)} aktive manuelle Einträge.")
        except Exception as e:
            print(f"Fehler beim Aufräumen der Testzellen CSV: {e}")

def update_global_schedule():
    """Berechnet die globale Belegung neu (Zukunft) und archiviert Altes."""
    print("--- Start Global Schedule Update ---")
    global global_scheduler
    # 1. Laden & Archivieren (Cleanup)
    # Das verschiebt alles < (Heute - 5 Tage) in die History
    global_scheduler.load()
    global_scheduler.archive_old_entries()

    # 2. Reset NUR der Zukunft (ab heute)
    # Wir löschen alle Einträge ab heute, damit sie frisch berechnet werden können.
    # Die manuellen Korrekturen der letzten 5 Tage bleiben erhalten.
    today = date.today()
    today_iso = today.isoformat()

    # NEU: Wir speichern ein "Gedächtnis" der Zukunft, bevor wir sie löschen
    previous_future_assignments = {d: v for d, v in global_scheduler.assignments.items() if d >= today_iso}

    future_dates =[d for d in list(global_scheduler.assignments.keys()) if d >= today_iso]
    for d in future_dates:
        del global_scheduler.assignments[d]

    # 3. Globale Daten laden
    if not ALL_EMPLOYEES: load_employees_from_csv(EMPLOYEES_FILE_PATH)
    if not ALL_QUALIFICATIONS: load_and_prepare_qualifications_data()

    config = load_app_config()

    # Startdatum für die Berechnung ist HEUTE (da wir die Vergangenheit ja behalten haben)
    start_date = today
    num_days = 365 * 2  # 2 Jahre Vorschau

    # 4. Berechnung (Zukunft)
    for tl in config.get('timelines', []):
        if tl.get('type') != 'editable': continue

        tl_id = tl['id']
        projects = project_manager.get_projects_for_timeline(tl_id)

        # Datenaufbereitung (ISO-Dates)
        for project in projects:
            project_id_prefix = f"{project.get('project_code')}_{project.get('engine_serial')}"
            prepared_events = []
            for i, evt_data in enumerate(project.get('events', [])):
                if not isinstance(evt_data, dict): continue
                if 'id' not in evt_data or not evt_data['id']:
                    evt_data['id'] = f"{project_id_prefix}_{evt_data.get('title', 'event')}_{i}"

                start_dt = get_datetime_from_date_shift(parse_date_from_string(evt_data.get('start')),
                                                        evt_data.get('start_shift'))
                end_dt = get_datetime_from_date_shift(parse_date_from_string(evt_data.get('end')),
                                                      evt_data.get('end_shift'))

                if start_dt and end_dt:
                    evt_data['start_datetime'] = start_dt.isoformat()
                    evt_data['end_datetime'] = end_dt.isoformat()
                    prepared_events.append(evt_data)
            project['events'] = prepared_events

        groups = tl.get('assignment_groups', [])
        manual_file = tl.get('manual_assignments_file')
        manual_assignments = load_assigned_employees(manual_file) if manual_file else []
        rules_file = tl.get('assignment_rules_file')
        rules = load_assignment_rules(rules_file) if rules_file else []

        # Berechnung aufrufen
        result = calculate_timeline_assignments(
            projects, ALL_EMPLOYEES, ALL_QUALIFICATIONS, manual_assignments,
            start_date, num_days, rules, groups,
            previous_global_assignments=previous_future_assignments
        )

        assignments = result.get('assignments', {})
        print(f"DEBUG: Timeline {tl_id} hat {len(assignments)} Slots mit Daten (Zukunft).")

        # Ergebnisse speichern
        for event_slot_key, data in assignments.items():
            date_iso = data.get('date')
            shift = data.get('shift')
            employees = data.get('employees', [])

            # --- DEBUG BLOCK ---
            if employees:
                print(f"DEBUG CANDIDATE: {date_iso} {shift} -> {employees}")
            # -------------------

            if not date_iso or not shift: continue

            # WICHTIG: Nur speichern, wenn es >= Heute ist (Sicherheitsnetz)
            if date_iso < today_iso: continue

            print(f"DEBUG ADDING {date_iso}")
            for emp_id in employees:
                global_scheduler.set_assignment(date_iso, shift, emp_id, {
                    "timeline": tl_id,
                    "project": data.get('project_name'),
                    "engine": data.get('engine'),
                    "task": data.get('task'),
                    "type": "manual" if data.get('is_manual') else "auto"
                })


    # 5. Speichern
    print(f"DEBUG TOTAL: Scheduler hat jetzt {len(global_scheduler.assignments)} Tage im Speicher.")
    print(f"DEBUG TOTAL INSTANCE ID: {id(global_scheduler)}")
    global_scheduler.save()

    try:
        archive_old_testcell_events()
    except Exception as e:
        print(f"Fehler bei Testzellen-Archivierung: {e}")

def normalize_engine_family(engine_name):
    """Fasst spezifische Typen zu Familien zusammen."""
    if not engine_name: return "Unknown"
    u = engine_name.upper().strip()

    # Priorität: Längere Matches zuerst prüfen!
    if "CF34-10" in u: return "CF34-10"
    if "CF34-8" in u: return "CF34-8"
    if "CFM56-7B" in u: return "CFM56-7B"

    if "PW2" in u: return "PW200"
    if "PW3" in u: return "PW300"
    if "PW5" in u: return "PW500"
    if "PW8" in u: return "PW800"
    if "LEAP" in u: return "LEAP"

    if "LM25" in u: return "LM2500"
    if "LM6" in u: return "LM6000"

    # Fallback für alles andere
    return u


class SmartScheduler:
    def __init__(self, history_data, global_assignments):
        self.history = history_data
        self.live = global_assignments
        self.relations = {}
        self.load_relations()

    def load_relations(self):
        """Lädt Buddy- und Team-Daten."""
        if os.path.exists(USER_RELATIONS_FILE):
            try:
                with open(USER_RELATIONS_FILE, 'r', encoding='utf-8') as f:
                    self.relations = json.load(f)
            except:
                self.relations = {}

    def get_user_preferences(self, user_id):
        if not user_id: return {"buddy": None, "preferred_partners": []}
        user_rel = self.relations.get(user_id, {})
        buddy = user_rel.get('buddy')
        if not buddy: buddy = None
        groups = user_rel.get('groups', [])
        partners = set()
        for other_id, other_rel in self.relations.items():
            if other_id == user_id: continue
            other_groups = other_rel.get('groups', [])
            if any(g in other_groups for g in groups):
                partners.add(other_id)
        return {
            "buddy": buddy,
            "preferred_partners": list(partners)
        }

    def get_last_experience_date(self, user_id, engine_type, task_name, today_iso):
        """Sucht das letzte Datum für User + Engine + Task (NUR IN DER VERGANGENHEIT)."""
        last_date_str = "1900-01-01"

        def search_in_dict(data_dict):
            nonlocal last_date_str
            for date_iso, shifts in data_dict.items():
                # STRIKTE TRENNUNG: Zukunft zählt nicht als Erfahrung!
                if date_iso >= today_iso: continue

                if date_iso <= last_date_str: continue
                for shift, users in shifts.items():
                    if user_id in users:
                        details = users[user_id]
                        eng_match = details.get('engine') and engine_type and engine_type in details['engine']
                        task_match = False
                        if task_name and details.get('task'):
                            t1 = task_name.upper().strip()
                            t2 = details.get('task').upper().strip()
                            if t1 == t2 or t1 in t2:
                                task_match = True

                        if eng_match and task_match:
                            last_date_str = date_iso

        search_in_dict(self.history)
        search_in_dict(self.live)

        return datetime.strptime(last_date_str, "%Y-%m-%d").date()

    def worked_on_project_yesterday(self, user_id, context, today_iso):
        """Prüft Kontinuität (NUR IN DER VERGANGENHEIT)."""
        today = context['date_obj']
        yesterday = (today - timedelta(days=1)).isoformat()

        # STRIKTE TRENNUNG: Wenn "Gestern" eigentlich in der Zukunft liegt, zählt es nicht!
        if yesterday >= today_iso:
            return False

        data_source = self.live if yesterday in self.live else self.history
        if yesterday in data_source:
            for shift in ['AM', 'PM']:
                if user_id in data_source[yesterday].get(shift, {}):
                    details = data_source[yesterday][shift][user_id]
                    if details.get('project') and context['project_code'] in details['project']:
                        return True
        return False

    def calculate_score(self, candidate, context, current_team):
        score = 0
        reasons = []
        user_id = candidate['id'].lower()
        today_iso = date.today().isoformat()

        # --- A. INDIVIDUUM ---
        if context['quali_status'] == 'X':
            score += 100
        else:
            score += 50

        if context['engine'] and context['task']:
            last_date = self.get_last_experience_date(user_id, context['engine'], context['task'], today_iso)
            days_since = (context['date_obj'] - last_date).days
            if days_since > 0:
                boost = min(days_since, 180)
                score += boost

        if self.worked_on_project_yesterday(user_id, context, today_iso):
            score += 200
            reasons.append("Kontinuität")

        # --- B. TEAM-FIT ---
        if current_team:
            prefs = self.get_user_preferences(user_id)

            for teammate in current_team:
                t_prefs = self.get_user_preferences(teammate['id'])
                is_buddy = (prefs['buddy'] == teammate['id'] or t_prefs['buddy'] == user_id)

                if is_buddy:
                    cand_status = context['quali_status']
                    mate_status = teammate['quali_status']
                    is_mentoring_case = (cand_status == 'X' and mate_status != 'X') or (
                                cand_status != 'X' and mate_status == 'X')

                    if is_mentoring_case:
                        score += 500
                        reasons.append(f"Mentoring Buddy mit {teammate['name']} (+500)")
                    else:
                        score += 50
                        reasons.append(f"Buddy (Sozial) mit {teammate['name']} (+50)")

                elif teammate['id'] in prefs['preferred_partners'] or user_id in t_prefs['preferred_partners']:
                    score += 150
                    reasons.append(f"Team-Präferenz mit {teammate['name']} (+150)")

            team_has_expert = any(t['quali_status'] == 'X' for t in current_team)
            team_has_trainee = any(t['quali_status'] != 'X' for t in current_team)

            if context['quali_status'] == 'X':
                if team_has_trainee and not team_has_expert:
                    score += 300
                    reasons.append("Supervisor Bonus")
                elif team_has_expert:
                    score -= 50
            else:
                if team_has_expert:
                    score += 300
                    reasons.append("Learning Bonus")
                elif team_has_trainee:
                    score -= 100
                    reasons.append("Trainee-Cluster Malus")

        return score


def can_user_review(order, user_id, user_roles):
    user = next((u for u in ALL_USERS if u['id'] == user_id), None)
    user_units = user.get('units', []) if user else []

    # 1. Admin darf immer
    if Roles.ADMIN in user_roles: return True

    # 2. Designated Reviewer
    designated = order.get('designatedReviewer', '').strip().lower()
    if designated and designated == user_id: return True

    if user_units:
        target_unit = order.get('plantIndex', '')
        # Wenn Auftrag keinen Ort hat, aber User eingeschränkt ist -> Darf er das sehen?
        # Diskussion: Ein "Allgemeines" Issue sollte vielleicht jeder sehen?
        # Entscheidung: Ja, ohne Ort -> Zeigen.
        if target_unit:
            is_responsible = False
            for u in user_units:
                if u == target_unit or target_unit.startswith(u + "."):
                    is_responsible = True
                    break
            if not is_responsible:
                return False # Harter Abbruch: Falscher Ort
    # -----------------------

    # 3. Topic Berechtigung
    topic = (order.get('topic') or order.get('category') or '').lower()

    if 'elektrik' in topic or 'messtechnik' in topic:
        return Roles.TOPIC_ELEKTRIK in user_roles

    if 'mechanik' in topic or 'anlagen' in topic:
        return Roles.TOPIC_MECHANIK in user_roles

    if 'it' in topic or 'software' in topic:
        return Roles.TOPIC_IT in user_roles

    if 'facility' in topic:
        return Roles.TOPIC_FACILITY in user_roles

    if 'test' in topic:
        return Roles.TOPIC_TESTENG in user_roles

    # 4. Fallback Rollen
    if Roles.TESTCELLINSPECTOR in user_roles or Roles.MAINTENANCEMANAGER in user_roles:
        return True

    return False


def ensure_kit_ids_and_migration(data):
    """
    Migriert alte Kit-Strukturen (Key = Name) zu UUIDs.
    Aktualisiert Mappings und Inventar-Referenzen.
    """
    boms = data.get('boms', {})
    mappings = data.get('mappings', {})
    inventory = data.get('inventory', {})

    # Map AlterName -> NeueUUID für Referenz-Updates
    name_to_uuid_map = {}
    new_boms = {}
    changes_made = False

    # 1. BOMS durchgehen und UUIDs vergeben
    for key, kit_data in boms.items():
        # Prüfen, ob der Key schon eine UUID ist (einfacher Check auf Prefix oder Länge)
        # Wir nehmen an, alte Namen sind keine UUIDs. Wir generieren neue IDs mit Prefix 'kit_'

        if key.startswith('kit_') and 'id' in kit_data and kit_data['id'] == key:
            # Schon migriert
            new_boms[key] = kit_data
            # Mapping für Sicherheit auch hier pflegen (falls Namen in Mappings gemischt sind)
            name_to_uuid_map[kit_data.get('name')] = key
        else:
            # Das ist ein alter Eintrag (Key = Name)
            new_uuid = f"kit_{uuid.uuid4().hex[:8]}"

            # Neues Objekt bauen
            new_kit_obj = {
                "id": new_uuid,
                "name": key,  # Der alte Key war der Name
                "items": kit_data.get('items', []),
                "status": kit_data.get('status', 'missing')
            }
            new_boms[new_uuid] = new_kit_obj
            name_to_uuid_map[key] = new_uuid
            changes_made = True
            print(f"MIGRATION: Kit '{key}' zu UUID '{new_uuid}' konvertiert.")

    data['boms'] = new_boms

    # 2. Mappings aktualisieren (Engine -> [KitUUIDs])
    if changes_made:
        for engine, kit_list in mappings.items():
            new_kit_list = []
            for kit_ref in kit_list:
                # Wenn die Referenz ein alter Name ist, ersetze durch UUID
                if kit_ref in name_to_uuid_map:
                    new_kit_list.append(name_to_uuid_map[kit_ref])
                elif kit_ref in new_boms:  # Schon eine UUID
                    new_kit_list.append(kit_ref)
                else:
                    # Referenz existiert nicht mehr in BOMs -> entfernen oder behalten?
                    # Wir behalten sie vorerst nicht, um Datenmüll zu vermeiden
                    print(f"WARNUNG: Kit-Referenz '{kit_ref}' in Engine '{engine}' nicht gefunden.")

            mappings[engine] = new_kit_list

        # 3. Inventar aktualisieren (Serial -> kitId)
        for pn, part in inventory.items():
            for serial in part.get('serials', []):
                old_kit_ref = serial.get('kitId', 'N/A')
                if old_kit_ref != 'N/A' and old_kit_ref in name_to_uuid_map:
                    serial['kitId'] = name_to_uuid_map[old_kit_ref]

    return changes_made


def recalculate_kit_status(data):
    """
    Berechnet serverseitig, ob Kits vollständig sind.
    Setzt 'status' im Kit und 'kitId' im Inventar.
    """
    inventory = data.get('inventory', {})
    boms = data.get('boms', {})

    print("--- DEBUG: Starte Kit-Neuberechnung (Python) ---")

    # 1. Reset aller Zuweisungen im Inventar
    # Wir setzen alles auf 'N/A', es sei denn, der Status ist Maintenance/Decommissioned
    # (dann ist das Teil eh nicht verfügbar).
    pool = {}  # Map: PN -> Liste verfügbarer SNs (Objekte)

    for pn, part in inventory.items():
        pool[pn] = []
        for serial in part.get('serials', []):
            # Reset Kit-Zuweisung
            serial['kitId'] = 'N/A'

            # Nur 'Available' Teile kommen in den Pool
            if serial.get('status') == 'Available':
                pool[pn].append(serial)

    # 2. Kits durchgehen und Material "buchen"
    # Wir sortieren Kits vielleicht nach Name, damit die Zuweisung deterministisch ist
    sorted_kit_ids = sorted(boms.keys(), key=lambda k: boms[k].get('name', ''))

    for kit_id in sorted_kit_ids:
        kit = boms[kit_id]
        items_needed = kit.get('items', [])

        if not items_needed:
            kit['status'] = 'missing'
            continue

        is_fully_ready = True
        is_partial = False

        for item in items_needed:
            needed_pn = item.get('partNumber')
            needed_qty = int(item.get('quantity', 1))

            available_sns = pool.get(needed_pn, [])

            # Checken wie viele wir haben
            if len(available_sns) >= needed_qty:
                # Genug da -> Zuweisen
                # Wir nehmen die ersten X aus dem Pool und entfernen sie
                for _ in range(needed_qty):
                    sn_obj = available_sns.pop(0)
                    sn_obj['kitId'] = kit_id  # HIER wird die UUID zugewiesen
                    # sn_obj['status'] = 'Assigned' # OPTIONAL: Wenn du willst, dass der Status sich ändert

                is_partial = True  # Zumindest dieser Teil ist da

            elif len(available_sns) > 0:
                # Teilweise da -> Alles nehmen was da ist
                for _ in range(len(available_sns)):
                    sn_obj = available_sns.pop(0)
                    sn_obj['kitId'] = kit_id

                is_partial = True
                is_fully_ready = False
            else:
                # Gar nichts da
                is_fully_ready = False

        # Status setzen
        if is_fully_ready:
            kit['status'] = 'ready'
        elif is_partial:
            kit['status'] = 'partial'
        else:
            kit['status'] = 'missing'

    print("--- DEBUG: Kit-Neuberechnung abgeschlossen ---")
    return True  # Immer True, da wir Daten modifiziert haben

# 4. Flask Routen

@app.route('/api/analytics/test_cells', methods=['GET'])
@login_required
def api_analytics_test_cells():
    # 1. Live Daten holen (die letzten 4 Wochen + Zukunft)
    live_cells = get_aggregated_test_cell_data()

    # 2. History holen
    history_events = list(load_testcell_history().values())

    # 3. Mergen (Zusammenführen)
    cell_map = {c['ID']: c.copy() for c in live_cells}

    # Live-Event-IDs sammeln (um Duplikate im Überschneidungsbereich zu verhindern)
    live_event_ids = set()
    for c in live_cells:
        for e in c.get('events', []):
            if e.get('event_id'): live_event_ids.add(e.get('event_id'))

    # Archivierte Events anhängen, falls sie nicht im Live-Set sind
    for he in history_events:
        cell_id = he.get('cell_id')
        event_id = he.get('event_id')

        if cell_id in cell_map and event_id not in live_event_ids:
            he_copy = he.copy()
            he_copy['isArchived'] = True  # <--- WICHTIG: Flag setzen!
            cell_map[cell_id]['events'].append(he_copy)

    return jsonify(list(cell_map.values()))

@app.route('/api/archive/projects/<string:timeline_id>')
@login_required
def api_get_archived_projects(timeline_id):
    """
    Scant den Status-Ordner und gibt eine Liste der Projekte zurück,
    die explizit zu der angefragten Timeline gehören.
    """
    archived_projects = []
    if not os.path.exists(PROJECT_STATUS_DIR):
        return jsonify([])

    for filename in os.listdir(PROJECT_STATUS_DIR):
        if filename.endswith('.json'):
            filepath = os.path.join(PROJECT_STATUS_DIR, filename)
            try:
                with open(filepath, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    if data.get('origin_timeline') != timeline_id:
                        continue

                    mtime = os.path.getmtime(filepath)
                    last_updated = datetime.fromtimestamp(mtime).strftime("%d.%m.%Y %H:%M")

                    archived_projects.append({
                        "name": data.get("name", "Unbekannt"),
                        "project_code": data.get("project_code", ""),
                        "engine_serial": data.get("engine_serial", "N/A"),
                        "customer": data.get("customer", "N/A"),
                        "engine_type": data.get("engine_type", "N/A"),
                        "progress_percent": data.get("progress_percent", 0),
                        "last_updated": last_updated,
                        "timestamp": mtime  # Zum Sortieren
                    })
            except Exception as e:
                print(f"Fehler beim Lesen des Archivs {filename}: {e}")

    archived_projects.sort(key=lambda x: x['timestamp'], reverse=True)
    return jsonify(archived_projects)

@app.route('/api/change_password', methods=['POST'])
@login_required
def api_change_password():
    data = request.json
    user_id = session.get('user_id')

    current_pw = data.get('currentPassword')
    new_pw = data.get('newPassword')
    confirm_pw = data.get('confirmPassword')

    if not all([current_pw, new_pw, confirm_pw]):
        return jsonify({"error": "Bitte alle Felder ausfüllen."}), 400

    if new_pw != confirm_pw:
        return jsonify({"error": "Die neuen Passwörter stimmen nicht überein."}), 400

    # User in der globalen Liste finden
    user = next((u for u in ALL_USERS if u['id'] == user_id), None)

    if not user:
        return jsonify({"error": "Benutzer nicht gefunden."}), 404

    # Altes Passwort prüfen
    if user['password'] != current_pw:
        return jsonify({"error": "Das aktuelle Passwort ist falsch."}), 403

    # Neues Passwort setzen
    user['password'] = new_pw

    # CSV Speichern (wir nutzen die gleiche Logik wie beim User-Edit)
    try:
        users_to_save_dicts = []
        for u in ALL_USERS:
            # Admin-Flag Logik beibehalten für Kompatibilität
            is_admin = Roles.ADMIN in u.get('roles', [])
            users_to_save_dicts.append({
                'NACHNAME': u['nachname'],
                'VORNAME': u['vorname'],
                'ID': u['id'],
                'LEVEL': u.get('level', ''),
                'PASSWORD': u['password'],  # Hier ist das neue PW drin
                'ADMIN': 'x' if is_admin else '',
                'ROLES': ",".join(u.get('roles', [])),
                'UNITS': ",".join(u.get('units', []))
            })

        with open(USERS_FILE_PATH, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file,
                                    fieldnames=['NACHNAME', 'VORNAME', 'ID', 'LEVEL', 'PASSWORD', 'ADMIN', 'ROLES',
                                                'UNITS'])
            writer.writeheader()
            writer.writerows(users_to_save_dicts)

        return jsonify({"message": "Passwort erfolgreich geändert."}), 200

    except Exception as e:
        print(f"Fehler beim Speichern des Passworts: {e}")
        return jsonify({"error": "Interner Speicherfehler."}), 500

@app.route('/api/training/upload', methods=['POST'])
@login_required
def api_training_upload():
    if 'file' not in request.files: return jsonify({"error": "Keine Datei"}), 400
    file = request.files['file']
    if file.filename == '': return jsonify({"error": "Leer"}), 400

    try:
        filename = secure_filename(f"{int(datetime.now().timestamp())}_{file.filename}")
        file.save(os.path.join(TRAINING_UPLOADS_DIR, filename))
        return jsonify({"path": f"/training/files/{filename}"}), 200
    except Exception as e:
        print(e)
        return jsonify({"error": "Fehler"}), 500


@app.route('/training/files/<path:filename>')
@login_required
def serve_training_file(filename):
    return send_from_directory(TRAINING_UPLOADS_DIR, filename)

@app.route('/api/training/data', methods=['GET', 'POST'])
@login_required
def api_training_data():
    if request.method == 'POST':
        data = request.json
        if save_json_file(TRAINING_DATA_FILE, data):
            return jsonify({"status": "success"}), 200
        return jsonify({"error": "Fehler beim Speichern"}), 500

    if os.path.exists(TRAINING_DATA_FILE):
        try:
            with open(TRAINING_DATA_FILE, 'r', encoding='utf-8') as f:
                return jsonify(json.load(f))
        except:
            pass
    # Default Struktur
    return jsonify({"catalog": [], "sessions": []})


@app.route('/api/training/relations', methods=['GET', 'POST'])
@login_required
def api_user_relations():
    if request.method == 'POST':
        data = request.json
        if save_json_file(USER_RELATIONS_FILE, data):
            return jsonify({"status": "success"}), 200
        return jsonify({"error": "Fehler beim Speichern"}), 500

    if os.path.exists(USER_RELATIONS_FILE):
        try:
            with open(USER_RELATIONS_FILE, 'r', encoding='utf-8') as f:
                return jsonify(json.load(f))
        except:
            pass
    return jsonify({})  # { "user_id": { "buddy": "uid", "groups": ["grp1"] } }


@app.route('/training_center')
@login_required
def serve_training_page():
    # ... Standard Boilerplate für User & Navigation ...
    user_name = get_current_username_from_session()
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname: user_initials = (vorname[0] + nachname[0]).upper()

    # Wir brauchen die User-Liste für die Dropdowns
    if not ALL_USERS: load_all_users_on_startup()

    return render_template_string(
        TRAINING_PAGE_HTML,  # Das bauen wir gleich
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        active_page='training',
        users=ALL_USERS,
        url_for=url_for,
        session=session
    )

@app.route('/api/projektplan')
@login_required
def api_projektplan():
    """Returns all timelines with their projects, milestones, manual events, and optional progress %."""
    try:
        config = load_app_config()
        today = date.today()
        today_str = today.isoformat()
        week_start = today - timedelta(days=today.weekday())   # Monday
        week_end   = week_start + timedelta(days=6)            # Sunday
        next_week_end = week_end + timedelta(days=7)           # Sunday of next week
        week_start_str    = week_start.isoformat()
        week_end_str      = week_end.isoformat()
        next_week_end_str = next_week_end.isoformat()

        def fmt_milestone(raw):
            if not raw: return ''
            d = parse_date_from_string(raw)
            return d.strftime('%d.%m') if d else raw

        def milestone_obj(raw, key):
            if not raw: return None
            d = parse_date_from_string(raw)
            if not d: return None
            return {'key': key, 'label': key, 'value': d.strftime('%d.%m'), 'iso': d.isoformat()}

        def project_dates(p):
            """All relevant dates (milestones + event starts/ends) for a project."""
            dates = []
            for key in ['BE', 'TS', 'TE', 'SA']:
                d = parse_date_from_string(p.get(key, ''))
                if d:
                    dates.append(d)
            for evt in p.get('events', []):
                for field in ['start', 'end']:
                    d = parse_date_from_string(evt.get(field, ''))
                    if d:
                        dates.append(d)
            return dates

        def show_active(p):
            """Active project visible if anything falls within this week or next week
            (including events that span across the window boundary)."""
            for evt in p.get('events', []):
                s = parse_date_from_string(evt.get('start', ''))
                e = parse_date_from_string(evt.get('end', ''))
                if s and e and s <= next_week_end and e >= week_start:
                    return True
            for d in project_dates(p):
                if week_start <= d <= next_week_end:
                    return True
            return False

        result = []
        for tl in config.get('timelines', []):
            data_file = tl.get('data_file')
            data_files = [data_file] if data_file else tl.get('data_files', [])
            data_files = [f for f in data_files if f]
            projects = load_timeline_projects_for_flask(data_files) if data_files else []

            # Only active projects with something in this week or next week
            visible = [p for p in projects if p.get('active', True) and show_active(p)]
            visible.sort(key=lambda p: parse_date_from_string(p.get('TS') or p.get('BE') or '') or date.max)

            proj_list = []
            for p in visible:
                # Try to load progress % from project status file
                progress = None
                raw_id = f"{p.get('project_code', '')}_{p.get('engine_serial', '')}"
                safe_id = re.sub(r'[^a-zA-Z0-9_-]', '', raw_id)
                status_path = os.path.join(PROJECT_STATUS_DIR, f'project_status_{safe_id}.json')
                if os.path.exists(status_path):
                    try:
                        with open(status_path, 'r', encoding='utf-8') as f:
                            progress = json.load(f).get('progress_percent')
                    except Exception:
                        pass

                # Only show events that overlap with the two-week window
                events = []
                for evt in p.get('events', []):
                    if evt.get('end', '') >= week_start_str and evt.get('start', '') <= next_week_end_str:
                        # Compute realEnd: exclusive-end convention means AM end_shift → real last day is end - 1
                        end_dt = parse_date_from_string(evt.get('end', ''))
                        end_shift = evt.get('end_shift', 'PM')
                        if end_dt and end_shift == 'AM':
                            real_end_str = (end_dt - timedelta(days=1)).isoformat()
                        else:
                            real_end_str = evt.get('end', '')
                        events.append({
                            'title': evt.get('title', ''),
                            'start': evt.get('start', ''),
                            'start_shift': evt.get('start_shift', 'AM'),
                            'end': evt.get('end', ''),
                            'end_shift': end_shift,
                            'real_end': real_end_str,
                            'color': evt.get('color', 'DEFAULT'),
                            'is_current': evt.get('start', '') <= today_str <= real_end_str,
                        })
                events.sort(key=lambda e: e.get('start', ''))

                milestones = [m for m in [
                    milestone_obj(p.get('BE', ''), 'BE'),
                    milestone_obj(p.get('TS', ''), 'TS'),
                    milestone_obj(p.get('TE', ''), 'TE'),
                    milestone_obj(p.get('SA', ''), 'SA'),
                ] if m]

                project_id = f"{p.get('project_code', '')}_{p.get('engine_serial', '')}"
                proj_comments = sorted(
                    [c for c in ALL_COMMENTS if c.get('project_id') == project_id],
                    key=lambda c: c.get('timestamp', ''), reverse=True
                )[:2]
                comments_out = [{'author': c.get('author', ''), 'timestamp': c.get('timestamp', '')[:10], 'text': c.get('comment', '')} for c in proj_comments]

                proj_list.append({
                    'engine_type': p.get('engine_type', ''),
                    'project_code': p.get('project_code', ''),
                    'engine_serial': p.get('engine_serial', ''),
                    'customer': p.get('customer', ''),
                    'milestones': milestones,
                    'progress': progress,
                    'events': events,
                    'comments': comments_out,
                })

            result.append({'id': tl['id'], 'name': tl['name'], 'projects': proj_list})

        return jsonify(result)
    except Exception as e:
        traceback.print_exc()
        return jsonify({"error": str(e)}), 500


@app.route('/api/analytics/skill_matrix')
@login_required
def api_get_skill_matrix():
    global_scheduler.load()
    history = global_scheduler.load_history()
    live = global_scheduler.assignments
    all_data = {**history, **live}

    matrix = {}
    all_engines = set()
    today_iso = date.today().isoformat()

    for date_iso, shifts in all_data.items():
        # Erfahrung zählt nur bis heute. Morgen geplante Jobs sind noch keine Erfahrung.
        if date_iso > today_iso:
            continue

        for shift, users in shifts.items():
            for uid, details in users.items():
                raw_engine = details.get('engine')
                task_name = details.get('task', 'Unbekannt')

                if not raw_engine or raw_engine == 'N/A': continue

                engine = normalize_engine_family(raw_engine)
                all_engines.add(engine)

                if uid not in matrix: matrix[uid] = {}
                if engine not in matrix[uid]:
                    matrix[uid][engine] = {"last_total": "1900-01-01", "tasks": {}}

                # Update Gesamt-Datum
                if date_iso > matrix[uid][engine]["last_total"]:
                    matrix[uid][engine]["last_total"] = date_iso

                # Update Task-Datum
                curr_task_date = matrix[uid][engine]["tasks"].get(task_name, "1900-01-01")
                if date_iso > curr_task_date:
                    matrix[uid][engine]["tasks"][task_name] = date_iso

    if not ALL_USERS: load_all_users_on_startup()
    user_map = {u['id']: f"{u['nachname']}, {u['vorname']}" for u in ALL_USERS}

    result = []
    for uid, engines_data in matrix.items():
        name = user_map.get(uid, uid)
        result.append({
            "id": uid,
            "name": name,
            "skills": engines_data  # Das ganze Objekt
        })

    return jsonify({
        "matrix": result,
        "engines": sorted(list(all_engines))
    })

@app.route('/api/maintenance/logs', methods=['GET', 'POST'])
@login_required
def api_maintenance_logs():
    if request.method == 'POST':
        data = request.json
        if not data or 'logs' not in data:
            return jsonify({"error": "Keine Logs empfangen"}), 400

        if save_json_file(MAINTENANCE_LOGBOOK_FILE, data['logs']):
            return jsonify({"status": "success"}), 200
        return jsonify({"error": "Speichern fehlgeschlagen"}), 500

    # GET
    if os.path.exists(MAINTENANCE_LOGBOOK_FILE):
        try:
            with open(MAINTENANCE_LOGBOOK_FILE, 'r', encoding='utf-8') as f:
                return jsonify(json.load(f))
        except:
            return jsonify([])
    return jsonify([])

@app.route('/api/test_equipment/rename_storage', methods=['POST'])
@login_required
def api_rename_storage():
    data = request.json
    storage_type = data.get('type')  # 'trolleys' oder 'storage_units'
    old_name = data.get('oldName')
    new_name = data.get('newName')

    if not all([storage_type, old_name, new_name]):
        return jsonify({"error": "Fehlende Parameter"}), 400

    te_data = load_test_equipment_data()
    if te_data is None: return jsonify({"error": "DB Fehler"}), 500

    container_dict = te_data.get(storage_type, {})

    if new_name in container_dict:
        return jsonify({"error": f"Name '{new_name}' existiert bereits."}), 409
    if old_name not in container_dict:
        return jsonify({"error": "Ursprungsobjekt nicht gefunden."}), 404

    # 1. Key umbenennen (Daten kopieren, alten löschen)
    container_dict[new_name] = container_dict.pop(old_name)

    # 2. Hardware Locations updaten
    inventory = te_data.get('inventory', {})
    count_hw = 0
    for pn, part in inventory.items():
        for s in part.get('serials', []):
            if s.get('location') == old_name:
                s['location'] = new_name
                count_hw += 1

    # 3. Facility Structure updaten
    facility_count = 0
    try:
        if os.path.exists(FACILITY_STRUCTURE_FILE):
            with open(FACILITY_STRUCTURE_FILE, 'r', encoding='utf-8') as f:
                structure = json.load(f)

            def update_node_storage(nodes):
                c = 0
                for node in nodes:
                    if node.get('storageLink') == old_name:
                        node['storageLink'] = new_name
                        c += 1
                        print(f"INFO: Facility Node '{node.get('name')}' Storage Link aktualisiert.")
                    if 'children' in node:
                        c += update_node_storage(node['children'])
                return c

            facility_count += update_node_storage(structure.get('betriebseinheiten', []))
            facility_count += update_node_storage(structure.get('gebaeude', []))

            if facility_count > 0:
                save_json_file(FACILITY_STRUCTURE_FILE, structure)
    except Exception as e:
        print(f"Fehler beim Facility Sync: {e}")

    # 4. Alles speichern
    if save_test_equipment_data_to_file(te_data):
        return jsonify({
            "success": True,
            "message": f"Umbenannt. {count_hw} Hardware-Teile und {facility_count} Facility-Links aktualisiert."
        }), 200
    else:
        return jsonify({"error": "Speichern fehlgeschlagen"}), 500

@app.route('/api/hardware/upload', methods=['POST'])
@login_required
def api_hardware_upload():
    if 'file' not in request.files:
        return jsonify({"error": "Keine Datei."}), 400
    file = request.files['file']
    if file.filename == '':
        return jsonify({"error": "Leerer Dateiname."}), 400

    try:
        filename = secure_filename(file.filename)
        # Timestamp für Eindeutigkeit
        unique_filename = f"{int(datetime.now().timestamp())}_{filename}"

        # Speichern
        file.save(os.path.join(HARDWARE_UPLOADS_DIR, unique_filename))

        # Pfad für Frontend zurückgeben
        return jsonify({
            "filename": filename,  # Originalname für Anzeige
            "stored_name": unique_filename,
            "path": f"/hardware/files/{unique_filename}"
        }), 200
    except Exception as e:
        print(f"Hardware Upload Fehler: {e}")
        return jsonify({"error": "Speichern fehlgeschlagen"}), 500


@app.route('/hardware/files/<path:filename>')
@login_required
def serve_hardware_file(filename):
    return send_from_directory(HARDWARE_UPLOADS_DIR, filename)

@app.route('/facility/model/current')
@login_required
def serve_current_facility_model():
    """Liefert das aktuelle 3D-Modell aus dem geschützten db/facility Ordner."""
    if os.path.exists(FACILITY_MODEL_PATH):
        return send_from_directory(FACILITY_MODELS_DIR, FACILITY_MODEL_FILENAME)
    else:
        return "Modell nicht gefunden", 404

@app.route('/api/facility/storage_options', methods=['GET'])
@login_required
def api_get_facility_storage_options():
    """Liefert eine Liste aller Rüstwagen und Lagerorte für das Dropdown."""
    try:
        te_data = load_test_equipment_data()
        if te_data is None: return jsonify([])

        options = []
        # Rüstwagen
        for name in te_data.get('trolleys', {}):
            options.append({"id": name, "name": name, "type": "Trolley"})
        # Lagerorte
        for name in te_data.get('storage_units', {}):
            options.append({"id": name, "name": name, "type": "Storage"})

        options.sort(key=lambda x: x['name'])
        return jsonify(options)
    except Exception as e:
        print(f"Fehler beim Laden der Storage Options: {e}")
        return jsonify([])


@app.route('/api/facility/storage_content', methods=['GET'])
@login_required
def api_get_facility_storage_content():
    """Liefert den Inhalt eines Lagerorts."""
    storage_name = request.args.get('name')
    if not storage_name: return jsonify([])

    try:
        te_data = load_test_equipment_data()
        if te_data is None: return jsonify([])

        # Suche in Trolleys und Storage Units
        container = te_data.get('trolleys', {}).get(storage_name) or \
                    te_data.get('storage_units', {}).get(storage_name)

        if not container: return jsonify([])

        # Inventar laden (nur als Fallback nötig)
        inventory = te_data.get('inventory', {})

        items = []
        for item in container.get('items', []):
            # 1. Versuch: Name direkt aus dem Item (Freitext oder gesyncter Name)
            desc = item.get('name')

            # 2. Versuch: Fallback über Inventar-Lookup (falls alte Datenstruktur ohne Name)
            if not desc:
                part = inventory.get(item.get('pn'), {})
                desc = part.get('name', 'Unbekannt')

            items.append({
                "pn": item.get('pn', '-'),
                "sn": item.get('sn', '-'),
                "qty": item.get('qty', 1),
                "box": item.get('box', ''),
                "level": item.get('level', ''),
                "desc": desc
            })

        # Sortieren nach Ebene -> Box
        items.sort(key=lambda x: (str(x['level']), str(x['box'])))

        return jsonify(items)
    except Exception as e:
        print(f"Fehler beim Laden des Storage Contents: {e}")
        return jsonify([])

@app.route('/api/admin/save_sfm_text', methods=['POST'])
@admin_required
def api_save_sfm_text():
    data = request.json
    if not data or 'key' not in data:
        return jsonify({"error": "Ungültige Daten"}), 400

    key = data['key']  # z.B. "motd"

    current_texts = load_sfm_texts()

    # Update des spezifischen Text-Blocks
    current_texts[key] = {
        "text": data.get('text', ''),
        "type": data.get('type', 'info'),
        "visible": data.get('visible', False)
    }

    if save_sfm_texts_data(current_texts):
        return jsonify({"message": "Text gespeichert."}), 200
    else:
        return jsonify({"error": "Fehler beim Speichern."}), 500

@app.route('/api/facility/units', methods=['GET'])
@login_required
def api_get_facility_units():
    """Gibt eine flache Liste aller Betriebseinheiten für Dropdowns zurück."""
    try:
        data = {}
        if os.path.exists(FACILITY_STRUCTURE_FILE):
            with open(FACILITY_STRUCTURE_FILE, 'r', encoding='utf-8') as f:
                data = json.load(f)

        # Wir nehmen nur die 'betriebseinheiten', Gebäude sind meist nur Hüllen
        units = data.get('betriebseinheiten', [])
        flat_list = get_flat_facility_units(units)

        return jsonify(flat_list)
    except Exception as e:
        print(f"Fehler beim Laden der Facility Units: {e}")
        return jsonify([])


@app.route('/facility/viewer')
@login_required
def serve_facility_viewer():
    """Zeigt den 3D-Viewer mit Strukturbaum an."""
    structure_data = {}

    # Sicherstellen, dass Datei existiert (doppelte Sicherheit)
    init_facility_data()

    try:
        if os.path.exists(FACILITY_STRUCTURE_FILE):
            with open(FACILITY_STRUCTURE_FILE, 'r', encoding='utf-8') as f:
                structure_data = json.load(f)
        else:
            structure_data = FACILITY_EMPTY_DATA
    except Exception as e:
        print(f"Fehler beim Laden der Struktur: {e}")
        structure_data = FACILITY_EMPTY_DATA

    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()
    elif session.get('username_display'):
        # Fallback auf ersten Buchstaben des Display-Namens, falls Vorname/Nachname fehlen
        user_initials = session.get('username_display')[0].upper()

    return render_template_string(
        FACILITY_VIEWER_HTML,
        structure_data=structure_data,
        url_for=url_for,
        session=session,
        current_user_initials=user_initials
    )


@app.route('/facility/editor')
@login_required
# @admin_required  <-- Optional: Aktivieren, wenn nur Admins editieren sollen
def serve_facility_editor():
    """Zeigt den 3D-Editor zum Bearbeiten der Geometrie an."""
    return render_template_string(TEMPLATE_EDITOR, url_for=url_for)


@app.route('/api/facility/save_structure', methods=['POST'])
@login_required
def api_facility_save_structure():
    """Speichert die logische Struktur (JSON)."""
    data = request.get_json()
    if data is None:
        return jsonify({"status": "error", "message": "No data received"}), 400

    # Atomares Speichern nutzen (unsere Helper-Funktion)
    if save_json_file(FACILITY_STRUCTURE_FILE, data):
        sync_facility_names_to_maintenance(data)
        return jsonify({"status": "success"}), 200
    else:
        return jsonify({"status": "error", "message": "Save failed"}), 500


@app.route('/api/facility/room_status')
@login_required
def api_facility_room_status():
    """
    Berechnet den Status der Räume/Objekte.
    Priorität: UUID-Match > Namens-Match.
    """
    status_map = {}

    try:
        if os.path.exists(FACILITY_STRUCTURE_FILE):
            with open(FACILITY_STRUCTURE_FILE, 'r', encoding='utf-8') as f:
                structure = json.load(f)
        else:
            structure = FACILITY_EMPTY_DATA

        data = load_maintenance_data()
        orders = data.get('orders', [])
    except Exception as e:
        print(f"Fehler beim Laden der Daten für Status: {e}")
        return jsonify({})

    # 1. Maps aufbauen:
    id_to_mesh_map = {}
    name_to_mesh_map = {}

    def map_structure(nodes, prefix=""):
        for i, node in enumerate(nodes):
            current_index_str = f"{prefix}{i + 1}"

            # Der volle Name ("1.1 Name")
            full_name = f"{current_index_str} {node.get('name', '')}"

            meshes = node.get('meshNames', [])

            if meshes:
                # Mapping via ID (Sicher!)
                if node.get('id'):
                    id_to_mesh_map[node['id']] = meshes

                # Mapping via Namen (Fallback)
                name_to_mesh_map[full_name] = meshes
                if node.get('name'):
                    name_to_mesh_map[node.get('name')] = meshes

            if 'children' in node:
                map_structure(node['children'], current_index_str + ".")

    map_structure(structure.get('betriebseinheiten', []))
    map_structure(structure.get('gebaeude', []))

    # 2. Aufträge analysieren
    mesh_status_level = {}
    orders_map = {o['id']: o for o in orders}

    for order in orders:
        if order.get('status') == 'Completed': continue

        # Bestimme die Meshes für diesen Auftrag
        target_meshes = []

        # A) Versuch über UUID (Neu & Robust)
        f_id = order.get('facilityUnitId')

        # Fallback: ID vom Parent holen, falls Issue
        if not f_id and order.get('sourceTask'):
            parent = orders_map.get(order.get('sourceTask'))
            if parent: f_id = parent.get('facilityUnitId')

        if f_id and f_id in id_to_mesh_map:
            target_meshes = id_to_mesh_map[f_id]

        # B) Versuch über Name (Legacy Fallback)
        if not target_meshes:
            plant_index = order.get('plantIndex')
            # Fallback Parent Name
            if not plant_index and order.get('sourceTask'):
                parent = orders_map.get(order.get('sourceTask'))
                if parent: plant_index = parent.get('plantIndex')

            if plant_index and plant_index in name_to_mesh_map:
                target_meshes = name_to_mesh_map[plant_index]

        if not target_meshes: continue

        # 1. Unit Blocker Status ermitteln
        is_unit_blocker = order.get('isUnitBlocker')
        if is_unit_blocker is None:
            # Legacy Fallback: Wenn das Feld fehlt, raten wir anhand der Assets
            is_blocker_legacy = order.get('isBlocker')
            has_blocked_assets = bool(order.get('blockedAssetIds'))
            # Wenn 'isBlocker' true ist, aber KEINE Assets blockiert sind -> War wohl als Unit-Blocker gemeint
            is_unit_blocker = is_blocker_legacy and not has_blocked_assets

        # 2. Level berechnen
        new_level = 0

        if is_unit_blocker:
            # HÖCHSTE STUFE: Nur explizite Anlagen-Sperre macht Rot
            new_level = 3  # Rot ("heiss" / Blocked)

        elif order.get('priority') == 'Critical':
            # Critical, aber kein Anlagen-Blocker -> Orange/Gelb (Wartung/Warnung)
            # Das signalisiert: "Hier ist was Dringendes, aber der Raum ist nicht tot."
            new_level = 2

        elif order.get('priority') == 'High':
            new_level = 2  # Orange

        elif order.get('type') == 'I' or order.get('status') == 'In Progress':
            # Normale Issues oder laufende Arbeiten -> Blau
            new_level = 1

            # Anstehende Wartung (Bald fällig)
        elif order.get('type') == 'M' and order.get('status') == 'Pending':
            try:
                if datetime.strptime(order.get('date'), '%Y-%m-%d').date() <= (date.today() + timedelta(days=14)):
                    if new_level < 2: new_level = 2
            except:
                pass

        # Auf alle zugehörigen Meshes anwenden
        for mesh in target_meshes:
            current = mesh_status_level.get(mesh, 0)
            if new_level > current:
                mesh_status_level[mesh] = new_level

    # 3. Ergebnis bauen
    for mesh, level in mesh_status_level.items():
        if level == 3:
            status_map[mesh] = "heiss"
        elif level == 2:
            status_map[mesh] = "Wartung_geplant"
        elif level == 1:
            status_map[mesh] = "in_Betrieb"

    return jsonify(status_map)

@app.route('/api/facility/unit_orders', methods=['GET'])
@login_required
def api_facility_unit_orders():
    """
    Liefert gefilterte Wartungsaufträge für eine Unit.
    Filter:
    - Keine abgeschlossenen Aufträge. - Issues: Alle offenen. - Maintenance: Aktive, Überfällige und Zukunft (max 14 Tage). - Actions: Nur aktive (In Progress/Review), keine geplanten (Pending).
    """
    unit_name = request.args.get('unit')
    if not unit_name:
        return jsonify([])

    try:
        data = load_maintenance_data()
        orders = data.get('orders', [])
    except Exception as e:
        print(f"Fehler beim Laden der Orders: {e}")
        return jsonify([])

    relevant_orders = []

    # Datumsgrenzen berechnen
    today = date.today()
    future_limit = today + timedelta(days=14)

    # Lookup Map für Parent-Beziehungen
    orders_map = {o['id']: o for o in orders}

    for order in orders:
        # 1. Grundfilter: Nichts Abgeschlossenes
        if order.get('status') == 'Completed': continue

        # 2. Namens-Matching (Unit)
        plant_index = order.get('plantIndex', '')
        is_match = False

        if plant_index == unit_name:
            is_match = True
        elif not plant_index and order.get('sourceTask'):
            parent = orders_map.get(order.get('sourceTask'))
            if parent and parent.get('plantIndex') == unit_name:
                is_match = True

        if not is_match: continue

        # 3. Logische Filterung nach Typ und Zeit
        order_type = order.get('type')
        status = order.get('status')
        order_date_str = order.get('date')
        show_order = False

        if order_type == 'I':
            # Issues: Immer anzeigen, solange nicht Completed
            show_order = True

        elif order_type == 'A':
            # Actions: Nur anzeigen, wenn aktiv bearbeitet oder im Review
            if status in ['In Progress', 'Review Ready']:
                show_order = True
            else:
                # Pending Actions ausblenden
                show_order = False

        elif order_type == 'M' or not order_type:  # Maintenance (oder Legacy ohne Typ)
            if status in ['In Progress', 'Review Ready']:
                show_order = True
            elif status == 'Pending':
                # Datum prüfen
                try:
                    due_date = datetime.strptime(order_date_str, '%Y-%m-%d').date()
                    if due_date <= future_limit:
                        show_order = True  # Überfällig oder in den nächsten 14 Tagen
                    else:
                        show_order = False  # Zu weit in der Zukunft
                except:
                    # Falls Datum fehlt/falsch: Sicherheitshalber anzeigen
                    show_order = True

        if show_order:
            relevant_orders.append({
                "id": order.get('id'),
                "type": order_type,
                "title": order.get('task'),
                "priority": order.get('priority', 'Medium'),
                "status": status,
                "isBlocker": order.get('isBlocker', False),
                "date": order_date_str
            })

    # 4. Sortierung (Score-basiert für Dringlichkeit)
    def sort_key(o):
        score = 0
        # Höchste Prio: Blocker & Kritische Issues
        if o['type'] == 'I' and (o['priority'] == 'Critical' or o['isBlocker']):
            score += 100

        # Hohe Prio: Aktive Arbeit
        if o['status'] == 'In Progress':
            score += 80

        # Mittlere Prio: Überfällige Wartung
        if o['type'] == 'M' and o['status'] == 'Pending':
            try:
                if datetime.strptime(o['date'], '%Y-%m-%d').date() < today:
                    score += 60
            except:
                pass

        # Prio für High Issues
        if o['type'] == 'I' and o['priority'] == 'High':
            score += 50

        return score

    relevant_orders.sort(key=sort_key, reverse=True)

    return jsonify(relevant_orders)

@app.route('/api/facility/save_model', methods=['POST'])
@login_required
def api_facility_save_model():
    """Speichert die GLB-Datei (3D-Modell) und erstellt ein Backup."""
    if 'file' not in request.files:
        return jsonify({"status": "error", "message": "Keine Datei hochgeladen"}), 400

    file = request.files['file']

    try:
        # Backup erstellen, falls Datei existiert
        if os.path.exists(FACILITY_MODEL_PATH):
            timestamp = int(datetime.now().timestamp())
            backup_name = f"anlage_backup_{timestamp}.glb"
            shutil.copy(FACILITY_MODEL_PATH, os.path.join(FACILITY_BACKUP_DIR, backup_name))

        # Neue Datei speichern
        file.save(FACILITY_MODEL_PATH)
        return jsonify({"status": "success"})
    except Exception as e:
        print(f"Fehler beim Speichern des Modells: {e}")
        return jsonify({"status": "error", "message": str(e)}), 500


@app.route('/facility/audit_report')
@login_required
def serve_facility_audit_report():
    """Generiert den Audit-Bericht für Struktur UND Inventar."""

    # 1. Struktur laden
    structure_data = {}
    if os.path.exists(FACILITY_STRUCTURE_FILE):
        with open(FACILITY_STRUCTURE_FILE, 'r', encoding='utf-8') as f:
            structure_data = json.load(f)

    # 2. Hardware/Lager laden (NEU)
    te_data = load_test_equipment_data()
    trolleys = te_data.get('trolleys', {}) if te_data else {}
    storage = te_data.get('storage_units', {}) if te_data else {}

    # 3. Flow-Map aufbauen
    flow_map = {}

    def get_flow_entry(nid):
        if nid not in flow_map: flow_map[nid] = {'in': [], 'out': [], 'assoc': []}
        return flow_map[nid]

    for flow in structure_data.get('fluesse', []):
        if flow.get('associatedUnitId'):
            get_flow_entry(flow['associatedUnitId'])['assoc'].append(flow)
        for segment in flow.get('path', []):
            get_flow_entry(segment['from'])['out'].append(flow)
            get_flow_entry(segment['to'])['in'].append(flow)

    # 4. Helper zum Flachklopfen
    def flatten_tree(nodes, level=0, prefix=""):
        result = []
        for i, node in enumerate(nodes):
            current_index = f"{prefix}{i + 1}"
            f_data = flow_map.get(node.get('id'), {'in': [], 'out': [], 'assoc': []})
            resp = node.get('responsibility', [])
            if isinstance(resp, str): resp = [resp] if resp else []

            # Hardware-Liste bereinigen (JSON -> Label)
            raw_hardware = node.get('hardware', [])
            clean_hardware = []
            for hw in raw_hardware:
                try:
                    if hw.strip().startswith('{'):
                        hw_obj = json.loads(hw)
                        clean_hardware.append(hw_obj.get('label', hw))
                    else:
                        clean_hardware.append(hw)
                except:
                    clean_hardware.append(hw)

            result.append({
                'index_str': current_index,
                'level': level,
                'name': node.get('name', 'Unbenannt'),
                'responsibility': resp,
                'hardware': clean_hardware,
                'storageLink': node.get('storageLink'),
                'details': node.get('details', {}),
                'flows_in': f_data['in'],
                'flows_out': f_data['out'],
                'flows_assoc': f_data['assoc']
            })

            if 'children' in node:
                result.extend(flatten_tree(node['children'], level + 1, current_index + "."))
        return result

    flat_data_units = flatten_tree(structure_data.get('betriebseinheiten', []))
    flat_data_buildings = flatten_tree(structure_data.get('gebaeude', []))

    return render_template_string(
        FACILITY_REPORT_TEMPLATE,
        flat_data_units=flat_data_units,
        flat_data_buildings=flat_data_buildings,
        trolleys=trolleys,
        storage=storage,
        date=datetime.now().strftime("%d.%m.%Y %H:%M"),
        count_units=len(flat_data_units),
        count_buildings=len(flat_data_buildings)
    )

@app.route('/api/generate_events_from_template/<path:engine_type>/<string:ts_iso_date>')
@login_required
def api_generate_events_from_template(engine_type, ts_iso_date):
    try:
        decoded_date_str = unquote(ts_iso_date)
        ts_datetime = datetime.fromisoformat(decoded_date_str.replace('Z', '+00:00'))

    except ValueError:
        # Dieser Fehler wird ausgelöst, wenn das Datum nach dem Dekodieren immer noch ungültig ist
        return jsonify({"error": "Ungültiges TS-Datum nach Dekodierung."}), 400

    # Der restliche Code ist perfekt und kann so bleiben
    process_templates = load_process_templates()
    template = find_best_template_for_engine(engine_type, process_templates)

    # Sicherstellen, dass timeline_events existiert, um Fehler zu vermeiden
    template_events = template.get("timeline_events", [])
    if not template_events:
        print(f"WARNUNG: Kein 'timeline_events'-Array für Engine Type '{engine_type}' (oder im Default) gefunden.")

    dynamic_events = []
    current_date = ts_datetime.date()
    current_shift = 'AM' if ts_datetime.hour < 14 else 'PM'

    for event_def in template_events:
        duration = event_def.get("duration_shifts", 1)
        start_date_loop = current_date
        start_shift_loop = current_shift

        end_date_loop = start_date_loop
        end_shift_loop = start_shift_loop
        for _ in range(duration - 1):
            if end_shift_loop == 'AM':
                end_shift_loop = 'PM'
            else:
                end_shift_loop = 'AM'
                end_date_loop += timedelta(days=1)

        dynamic_events.append({
            'title': event_def.get("name", "Unknown"),
            'start': start_date_loop.strftime("%Y-%m-%d"),
            'start_shift': start_shift_loop,
            'end': end_date_loop.strftime("%Y-%m-%d"),
            'end_shift': end_shift_loop,
            'color': event_def.get("color", "grey")
        })

        current_date = end_date_loop
        current_shift = end_shift_loop
        if current_shift == 'AM':
            current_shift = 'PM'
        else:
            current_shift = 'AM'
            current_date += timedelta(days=1)

    return jsonify(dynamic_events)


@app.route('/api/hardware/set_status', methods=['POST'])
@login_required
def api_set_hardware_status():
    data = request.json
    if not data or 'status' not in data:
        return jsonify({"error": "Fehlende Daten: status ist erforderlich."}), 400

    # Wir unterstützen jetzt beides: IDs (neu) und SNs (alt)
    ids_to_update = set(data.get('ids', []))
    sns_to_update = set(data.get('serial_numbers', []))
    new_status = data.get('status')

    if not ids_to_update and not sns_to_update:
        return jsonify({"message": "Keine IDs oder Seriennummern zum Aktualisieren angegeben."}), 200

    # Lade die komplette Hardware-Datenbank
    hardware_data = load_test_equipment_data()
    if hardware_data is None:
        return jsonify({"error": "Hardware-Daten konnten nicht geladen werden."}), 500

    inventory = hardware_data.get('inventory', {})
    changes_made = False
    updated_count = 0

    # Iteriere durch das Inventar
    for pn, part in inventory.items():
        for serial_entry in part.get('serials', []):
            is_match = False

            # Check 1: ID Match (bevorzugt)
            if serial_entry.get('id') and serial_entry.get('id') in ids_to_update:
                is_match = True

            # Check 2: SN Match (Fallback)
            elif serial_entry.get('sn') and serial_entry.get('sn') in sns_to_update:
                is_match = True

            if is_match:
                if serial_entry.get('status') != new_status:
                    print(
                        f"INFO: Ändere Status für {serial_entry.get('sn')} ({serial_entry.get('id')}) zu '{new_status}'.")
                    serial_entry['status'] = new_status
                    changes_made = True
                    updated_count += 1

    if changes_made:
        # NEU: Bevor wir speichern, berechnen wir die Kits neu!
        # Denn wenn ein Teil 'Maintenance' wird, könnte ein Kit 'missing' werden.
        recalculate_kit_status(hardware_data)

        if save_test_equipment_data_to_file(hardware_data):
            return jsonify({"message": f"{updated_count} Hardware-Status aktualisiert."}), 200
        else:
            return jsonify({"error": "Fehler beim Speichern der Hardware-Daten."}), 500

    return jsonify({"message": "Keine Statusänderungen erforderlich (Status war bereits korrekt)."}), 200


@app.route('/api/maintenance/save_orders', methods=['POST'])
@login_required
def api_save_maintenance_orders():
    data = request.json
    if not data or 'orders' not in data:
        return jsonify({"error": "Keine Orders empfangen"}), 400

    if save_maintenance_orders(data['orders']):
        # Sync Logik für Hardware bleibt hier hängen, da sie an Orders hängt
        sync_maintenance_links_to_hardware(data['orders'])
        return jsonify({"status": "success"}), 200
    return jsonify({"error": "Speichern fehlgeschlagen"}), 500


@app.route('/api/maintenance/save_definitions', methods=['POST'])
@login_required
def api_save_maintenance_definitions():
    data = request.json
    if not data or 'definitions' not in data:
        return jsonify({"error": "Keine Definitionen empfangen"}), 400

    if save_maintenance_definitions(data['definitions']):
        try:
            m_data = load_maintenance_data()  # Lädt Orders, Defs, Companies
            orders = m_data.get('orders', [])
            sync_maintenance_links_to_hardware(orders)
            print("INFO: Hardware-Links nach Definitions-Update synchronisiert.")

        except Exception as e:
            print(f"WARNUNG: Sync nach Definitions-Speichern fehlgeschlagen: {e}")

        return jsonify({"status": "success"}), 200
    return jsonify({"error": "Speichern fehlgeschlagen"}), 500


@app.route('/api/maintenance/save_companies', methods=['POST'])
@login_required
def api_save_maintenance_companies():
    data = request.json
    if not data or 'companies' not in data:
        return jsonify({"error": "Keine Firmen empfangen"}), 400

    if save_maintenance_companies(data['companies']):
        return jsonify({"status": "success"}), 200
    return jsonify({"error": "Speichern fehlgeschlagen"}), 500

@app.route('/api/test_equipment/data', methods=['GET'])
@login_required
def api_get_test_equipment_data_json():
    """Gibt eine Liste aller physischen Geräte zurück (ID, Label, PN, SN, ProductType)."""
    data = load_test_equipment_data()
    if data is None: return jsonify([])

    inventory = data.get('inventory', {})
    hardware_list = []

    for pn, item in inventory.items():
        part_name = item.get('name', 'Unbekannt')
        # NEU: Product Type holen (kann Array oder String sein)
        p_type = item.get('productType', [])

        for serial_entry in item.get('serials', []):
            hw_id = serial_entry.get('id', 'temp_missing_id')
            sn = serial_entry.get('sn', 'N/A')
            display_string = f"{sn} ({pn} - {part_name})"

            hardware_list.append({
                "id": hw_id,
                "label": display_string,
                "sn": sn,
                "pn": pn,
                "productType": p_type
            })

    hardware_list.sort(key=lambda x: x['label'])
    return jsonify(hardware_list)


@app.route('/api/maintenance/upload', methods=['POST'])
@login_required
def api_maintenance_upload():
    if 'file' not in request.files:
        return jsonify({"error": "Keine Datei."}), 400
    file = request.files['file']
    if file.filename == '':
        return jsonify({"error": "Leerer Dateiname."}), 400

    try:
        os.makedirs(MAINTENANCE_UPLOADS_DIR, exist_ok=True)
        filename = secure_filename(file.filename)
        # Um Duplikate zu vermeiden, Timestamp voranstellen
        unique_filename = f"{int(datetime.now().timestamp())}_{filename}"
        file.save(os.path.join(MAINTENANCE_UPLOADS_DIR, unique_filename))

        # Wir geben eine Data-URL zurück für die Vorschau (in Produktion würde man den Pfad speichern)
        # Hier simulieren wir die Data-URL Rückgabe vereinfacht durch den Pfad,
        # da wir im HTML FileReader nutzen. Für echte Persistenz muss man den Pfad speichern.
        return jsonify({"filename": unique_filename, "path": f"/maintenance/files/{unique_filename}"}), 200
    except Exception as e:
        print(f"Upload Fehler: {e}")
        return jsonify({"error": "Speichern fehlgeschlagen"}), 500


@app.route('/maintenance/files/<path:filename>')
@login_required
def serve_maintenance_file(filename):
    return send_from_directory(MAINTENANCE_UPLOADS_DIR, filename)


@app.route('/api/maintenance/save', methods=['POST'])
@login_required
def api_save_maintenance():
    data = request.json

    # 1. Wartungsdaten speichern
    if save_maintenance_data_to_file(data):

        # 2. NEU: Verknüpfungen zur Hardware aktualisieren (im Hintergrund)
        # Wir übergeben die Liste der Orders an die Sync-Funktion
        sync_maintenance_links_to_hardware(data.get('orders', []))

        return jsonify({"status": "success"}), 200
    else:
        return jsonify({"status": "error"}), 500


@app.route('/maintenance')
@login_required
def serve_maintenance_page():
    user_name = get_current_username_from_session() or "Unbekannt"
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()
    user_id = session.get('user_id', '').lower()
    user_roles = session.get('roles', [])
    data = load_maintenance_data()
    orders = data.get('orders', [])
    permissions_map = {}
    for order in orders:
        if order.get('status') in ['Review Ready'] or (order.get('type') == 'I' and order.get('priority') == 'OPEN'):
            permissions_map[order.get('id')] = can_user_review(order, user_id, user_roles)
        else:
            permissions_map[order.get('id')] = False

    companies_data = data.get('companies', [])
    all_hardware_assets = api_get_test_equipment_data_json().get_json()

    return render_template_string(
        MAINTENANCE_PAGE_HTML,
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        active_page='maintenance',
        url_for=url_for,
        session=session,
        orders_json=json.dumps(data.get('orders', [])),
        definitions_json=json.dumps(data.get('definitions', [])),
        companies_json=json.dumps(companies_data),
        permissions_json=json.dumps(permissions_map),
        hardware_assets_json=json.dumps(all_hardware_assets)
    )

def ensure_hardware_ids(data):
    """
    Durchläuft das Inventar und gibt jedem Serial-Item eine UUID,
    falls es noch keine hat.
    """
    inventory = data.get('inventory', {})
    changes = False

    for pn, part in inventory.items():
        for serial in part.get('serials', []):
            if 'id' not in serial or not serial['id']:
                # Neue ID generieren
                serial['id'] = f"hw_{uuid.uuid4().hex[:12]}"  # Kurze UUID reicht oft
                changes = True
    return changes


@app.route('/api/test_equipment/save', methods=['POST'])
@login_required
def api_save_test_equipment():
    print("\n=== DEBUG: API /api/test_equipment/save aufgerufen ===")

    try:
        data = request.json
    except Exception as e:
        return jsonify({"status": "error", "message": "Invalid JSON format"}), 400

    if data is None:
        return jsonify({"status": "error", "message": "Keine Daten empfangen"}), 400

    # 1. IDs sicherstellen (Hardware)
    ensure_hardware_ids(data)

    # 2. NEU: Kits migrieren (Namen -> UUIDs)
    ensure_kit_ids_and_migration(data)

    # 3. NEU: Status serverseitig berechnen (Single Source of Truth)
    recalculate_kit_status(data)

    # 4. Die Hardware-Daten speichern
    if save_test_equipment_data_to_file(data):

        # --- SYNC 1: Wartungspläne ---
        sync_hardware_names_to_definitions(data)

        # --- SYNC 2: Pillen wiederherstellen ---
        try:
            maintenance_data = load_maintenance_data()
            orders = maintenance_data.get('orders', [])
            sync_maintenance_links_to_hardware(orders)
        except Exception as e:
            print(f"!!! FEHLER beim Wiederherstellen der Links: {e}")

        # --- SYNC 3 (NEU): Facility Struktur & Trolley ---
        sync_hardware_names_to_facility(data)
        sync_trolleys_and_storage(data)

        return jsonify({"status": "success", "message": "Hardware gespeichert, berechnet & synchronisiert."}), 200
    else:
        return jsonify({"status": "error", "message": "Fehler beim Schreiben der Hardware-Datei."}), 500

@app.route('/test_equipment')
@login_required
def serve_test_equipment_page():
    user_name = get_current_username_from_session() or "Unbekannt"

    # Initialen berechnen
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    te_data = load_test_equipment_data()

    if te_data is None:
        te_data = {"inventory": {}, "boms": {}, "mappings": {}, "trolleys": {}, "storage_units": {}}

    if ensure_hardware_ids(te_data):
        print("INFO: Hardware-IDs beim Seitenaufruf generiert und persistent gespeichert.")
        save_test_equipment_data_to_file(te_data)


    return render_template_string(
        TEST_EQUIPMENT_PAGE_HTML,
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        active_page='test_equipment',
        url_for=url_for,
        session=session,
        inventory_json=json.dumps(te_data.get('inventory', {})),
        boms_json=json.dumps(te_data.get('boms', {})),
        mappings_json=json.dumps(te_data.get('mappings', {})),
        trolleys_json=json.dumps(te_data.get('trolleys', {})),
        storage_units_json=json.dumps(te_data.get('storage_units', {})),
        employees_json=json.dumps(sorted([e.get('name', '') for e in ALL_EMPLOYEES if e.get('name')]))
    )


@app.route('/livestatus')
def serve_project_monitor():
    """Zeigt das eigenständige Projekt-Monitor Dashboard ohne Sidebar."""
    project_list = load_project_data_monitor()
    return render_template_string(PROJECT_MONITOR_HTML, projects=project_list, url_for=url_for)


@app.route('/ordersystem')
@login_required
def serve_consumable_order_page():
    """Rendert die Hauptseite für das Bestellsystem."""
    user_name = get_current_username_from_session() or "Unbekannt"
    orders_data, templates_data = order_system_load_data()
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    return render_template_string(
        CONSUMABLE_ORDER_PAGE_HTML,
        current_user_display_name=user_name,
        current_user_initials=user_initials,  # <-- HINZUGEFÜGT
        orders_json=json.dumps(orders_data),
        templates_json=json.dumps(templates_data),
        active_page='ordersystem'
    )


@app.route('/api/order_system/add_request', methods=['POST'])
@login_required
def api_add_order_request():
    # Jeder darf bestellen!
    new_order = request.json
    if not new_order or 'id' not in new_order or 'name' not in new_order:
        return jsonify({"error": "Ungültige Bestellung"}), 400

    # Daten laden
    orders_data, templates_data = order_system_load_data()

    # Sicherstellen, dass Status 'requested' ist (Security)
    new_order['status'] = 'requested'

    # Anhängen
    orders_data.append(new_order)

    # Speichern
    order_system_save_data({'orders': orders_data, 'templates': templates_data})

    return jsonify({"status": "success", "message": "Bestellung aufgegeben."}), 200

@app.route('/api/order_system/save_data', methods=['POST'])
@login_required
def order_system_save_data_endpoint():
    user_roles = session.get('roles', [])

    # Nur Admins und ConsumableManager dürfen die Daten vollständig überschreiben.
    if Roles.ADMIN not in user_roles and Roles.CONSUMABLEMANAGER not in user_roles:
        # Hier könnte man eine komplexere Logik einbauen, die prüft,
        # ob der User nur seine eigenen "requested" Orders hinzugefügt hat.
        # Für den Anfang ist eine strikte Sperre sicherer.
        return jsonify({"error": "Keine Berechtigung zum Speichern der Bestelldaten."}), 403

    data = request.get_json()
    order_system_save_data(data)
    return jsonify({"status": "success", "message": "Daten erfolgreich gespeichert."})


@app.route('/admin/download_db_folder/<path:folderpath>')
@admin_required
def download_db_folder(folderpath):
    """Packt einen Ordner aus dem 'db'-Verzeichnis in ein ZIP-Archiv und sendet es zum Download."""
    base_dir = os.path.abspath(DB_ROOT_PATH)

    # Der spezielle Wert 'ROOT' steht für das gesamte 'db'-Verzeichnis
    if folderpath == 'ROOT':
        full_path = base_dir
    else:
        full_path = os.path.abspath(os.path.join(base_dir, folderpath))

    # Security Check
    if not full_path.startswith(base_dir) or not os.path.isdir(full_path):
        abort(404, description="Ordner nicht gefunden oder Zugriff verweigert.")

    # Erstelle eine ZIP-Datei im Speicher
    memory_file = io.BytesIO()
    with zipfile.ZipFile(memory_file, 'w', zipfile.ZIP_DEFLATED) as zf:
        for root, dirs, files in os.walk(full_path):
            for file in files:
                file_path = os.path.join(root, file)
                # Erstelle den relativen Pfad für die Struktur innerhalb der ZIP-Datei
                arcname = os.path.relpath(file_path, full_path)
                zf.write(file_path, arcname)

    memory_file.seek(0)

    # Dateiname für den Download festlegen
    if folderpath == 'ROOT':
        download_filename = 'db_backup.zip'
    else:
        download_filename = f"{os.path.basename(folderpath)}.zip"

    return send_file(
        memory_file,
        mimetype='application/zip',
        as_attachment=True,
        download_name=download_filename
    )


@app.route('/api/upload_certificate', methods=['POST'])
@admin_required
def api_upload_certificate():
    if 'certificate_file' not in request.files:
        return jsonify({"error": "Keine Datei im Upload gefunden."}), 400

    file = request.files['certificate_file']
    employee_id = request.form.get('employee_id')
    merkmal_name = request.form.get('merkmal_name')  # NEU: Merkmal wird mitgesendet

    if file.filename == '' or not employee_id or not merkmal_name:
        return jsonify({"error": "Fehlende Informationen (Datei, Mitarbeiter-ID oder Merkmal)."}), 400

    try:
        filename = secure_filename(file.filename)
        # NEU: Speichere die Datei in einem Unterordner /uploads/ID/
        user_upload_folder = os.path.join(CERTIFICATES_UPLOADS_FOLDER_PATH, employee_id)
        os.makedirs(user_upload_folder, exist_ok=True)

        full_path = os.path.join(user_upload_folder, filename)
        file.save(full_path)

        # NEU: Metadaten in ID.json aktualisieren
        metadata = get_certificate_metadata(employee_id)
        metadata = [m for m in metadata if m.get('filename') != filename]  # Alte Einträge für dieselbe Datei entfernen
        metadata.append({
            "filename": filename,
            "merkmal": merkmal_name,  # NEU: Merkmal speichern
            "uploaded_at": datetime.now().isoformat(),
            "uploaded_by": get_current_username_from_session()
        })
        save_certificate_metadata(employee_id, metadata)

        return jsonify({"message": "Datei erfolgreich hochgeladen.", "filename": filename}), 200

    except Exception as e:
        print(f"FEHLER beim Zertifikat-Upload: {e}")
        return jsonify({"error": "Ein interner Fehler ist beim Speichern der Datei aufgetreten."}), 500


@app.route('/download_certificate/<string:employee_id>/<path:filename>')
@login_required
def download_certificate_route(employee_id, filename):
    # Security Check wurde verbessert
    safe_employee_id = secure_filename(employee_id)
    safe_filename = secure_filename(filename)

    base_dir = os.path.abspath(CERTIFICATES_UPLOADS_FOLDER_PATH)
    full_path = os.path.abspath(os.path.join(base_dir, safe_employee_id, safe_filename))

    if not full_path.startswith(base_dir):
        abort(403)

    if not os.path.isfile(full_path):
        abort(404)

    return send_file(full_path, as_attachment=True)


@app.route('/api/admin/create_timeline', methods=['POST'])
@admin_required
def api_create_timeline():
    data = request.json
    required_fields = ['name', 'id', 'type']
    if not data or not all(field in data for field in required_fields):
        return jsonify({"error": "Fehlende Daten: Name, ID und Typ sind erforderlich."}), 400

    raw_id = data.get('id')
    if not raw_id:
        return jsonify({"error": "Die ID darf nicht leer sein."}), 400
    new_id = raw_id.strip().lower().replace(' ', '_')
    if not new_id.isidentifier():  # Prüft, ob es ein gültiger Bezeichner ist
        return jsonify({
            "error": "Die ID darf nur Buchstaben, Zahlen und Unterstriche enthalten und nicht mit einer Zahl beginnen."}), 400

    config = load_app_config()
    timelines = config.get('timelines', [])

    # Prüfen, ob die ID bereits existiert
    if any(tl['id'] == new_id for tl in timelines):
        return jsonify({"error": f"Eine Timeline mit der ID '{new_id}' existiert bereits."}), 409

    # Basis-Struktur für die neue Timeline
    new_timeline_config = {
        "id": new_id,
        "name": data['name'].strip(),
        "type": data['type']
    }

    # Füge die Konfigurationsoptionen hinzu, die für BEIDE Typen gelten
    new_timeline_config['visible_test_cells'] = data.get('visible_test_cells', [])
    new_timeline_config['assignment_groups'] = data.get('assignment_groups', [])

    # Spezifische Konfiguration je nach Typ
    if data['type'] == 'editable':
        base_file_path = os.path.join('db', f"{new_id}_timeline")
        new_timeline_config['data_file'] = f"{base_file_path}_projects.csv"
        new_timeline_config['assignment_rules_file'] = f"{base_file_path}_rules.csv"
        new_timeline_config['manual_assignments_file'] = f"{base_file_path}_assignments.csv"
        new_timeline_config['sap_filter'] = data.get('sap_filter', {})
    else:  # Für 'static' Timelines
        # Statische Timelines haben keine eigene Projektdatei, sondern referenzieren andere.
        # Dies muss weiterhin manuell in der config.json geschehen, aber wir erstellen einen leeren Platzhalter.
        new_timeline_config['data_files'] = data.get('data_files', [])

    try:
        # 1. Konfiguration aktualisieren und speichern
        timelines.append(new_timeline_config)
        config['timelines'] = timelines
        with open(os.path.join(DB_ROOT_PATH, 'config.json'), 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=2, ensure_ascii=False)

        # 2. Leere CSV-Dateien erstellen, falls 'editable'
        if data['type'] == 'editable':
            # Projektdaten-Datei
            with open(new_timeline_config['data_file'], 'w', newline='', encoding='utf-8-sig') as f:
                csv.writer(f).writerow(
                    ['name', 'engine_type', 'project_code', 'engine_serial', 'customer', 'BE', 'TS', 'TE', 'SA',
                     'active'])

            # Regel-Datei
            with open(new_timeline_config['assignment_rules_file'], 'w', newline='', encoding='utf-8-sig') as f:
                csv.writer(f).writerow(ASSIGNMENT_RULES_FIELDNAMES)

            # Manuelle Zuweisungs-Datei
            with open(new_timeline_config['manual_assignments_file'], 'w', newline='', encoding='utf-8-sig') as f:
                csv.writer(f).writerow(ASSIGNED_EMPLOYEES_FIELDNAMES)

        return jsonify({"message": f"Timeline '{new_timeline_config['name']}' wurde erfolgreich erstellt."}), 201

    except Exception as e:
        # Hier wäre eine Rollback-Logik ideal, aber für den Anfang reicht eine Fehlermeldung
        print(f"FEHLER beim Erstellen der Timeline: {e}")
        traceback.print_exc()
        return jsonify(
            {"error": "Ein Fehler ist beim Erstellen der Timeline und der zugehörigen Dateien aufgetreten."}), 500


@app.route('/api/admin/delete_timeline/<string:timeline_id>', methods=['DELETE'])
@admin_required
def api_delete_timeline(timeline_id):
    """Löscht eine Timeline aus der Konfiguration und die zugehörigen Dateien."""
    config = load_app_config()
    timelines = config.get('timelines', [])

    timeline_to_delete = next((tl for tl in timelines if tl['id'] == timeline_id), None)

    if not timeline_to_delete:
        return jsonify({"error": "Timeline nicht gefunden."}), 404

    try:
        # 1. Entferne die Timeline aus der Konfigurationsliste
        timelines = [tl for tl in timelines if tl['id'] != timeline_id]
        config['timelines'] = timelines

        # Schreibe die aktualisierte config.json
        with open(os.path.join(DB_ROOT_PATH, 'config.json'), 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=2, ensure_ascii=False)

        # 2. Lösche die zugehörigen CSV-Dateien, falls es eine 'editable' Timeline war
        if timeline_to_delete.get('type') == 'editable':
            files_to_delete = [
                timeline_to_delete.get('data_file'),
                timeline_to_delete.get('assignment_rules_file'),
                timeline_to_delete.get('manual_assignments_file')
            ]
            for file_path in files_to_delete:
                if file_path and os.path.exists(file_path):
                    try:
                        os.remove(file_path)
                        print(f"INFO: Datei '{file_path}' wurde gelöscht.")
                    except OSError as e:
                        # Logge den Fehler, aber fahre fort, damit der Prozess nicht abbricht
                        print(f"WARNUNG: Datei '{file_path}' konnte nicht gelöscht werden: {e}")

        return jsonify({"message": f"Timeline '{timeline_to_delete.get('name')}' wurde erfolgreich gelöscht."}), 200

    except Exception as e:
        print(f"FEHLER beim Löschen der Timeline '{timeline_id}': {e}")
        traceback.print_exc()
        # Hier wäre ein Rollback der config.json-Änderung ideal, aber für den Moment ist eine Fehlermeldung ausreichend.
        return jsonify({"error": "Ein Fehler ist beim Löschvorgang aufgetreten."}), 500


@app.route('/api/timeline_config/<string:timeline_id>')
@admin_required
def get_timeline_config(timeline_id):
    """Gibt die Konfiguration für eine einzelne Timeline zurück."""
    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)
    if timeline_config:
        return jsonify(timeline_config)
    else:
        return jsonify({"error": "Timeline nicht gefunden."}), 404


@app.route('/api/admin/update_timeline/<string:timeline_id>', methods=['PUT'])
@admin_required
def api_update_timeline(timeline_id):
    """Aktualisiert die Konfiguration einer bestehenden Timeline."""
    data = request.json
    if not data:
        return jsonify({"error": "Keine Daten empfangen."}), 400

    config = load_app_config()
    timelines = config.get('timelines', [])

    timeline_to_update_index = -1
    for i, tl in enumerate(timelines):
        if tl['id'] == timeline_id:
            timeline_to_update_index = i
            break

    if timeline_to_update_index == -1:
        return jsonify({"error": "Timeline nicht gefunden."}), 404

    # Aktualisiere die Felder
    # Die ID wird nicht geändert!
    timelines[timeline_to_update_index]['name'] = data.get('name', timelines[timeline_to_update_index]['name'])
    timelines[timeline_to_update_index]['type'] = data.get('type', timelines[timeline_to_update_index]['type'])

    if timelines[timeline_to_update_index]['type'] == 'editable':
        timelines[timeline_to_update_index]['visible_test_cells'] = data.get('visible_test_cells', [])
        timelines[timeline_to_update_index]['assignment_groups'] = data.get('assignment_groups', [])
        timelines[timeline_to_update_index]['sap_filter'] = data.get('sap_filter', {})

    if timelines[timeline_to_update_index]['type'] == 'static':
        timelines[timeline_to_update_index]['data_files'] = data.get('data_files', [])
        # Entferne 'editable'-spezifische Schlüssel, falls sie noch existieren
        timelines[timeline_to_update_index].pop('assignment_rules_file', None)
        timelines[timeline_to_update_index].pop('manual_assignments_file', None)
        timelines[timeline_to_update_index].pop('visible_test_cells', None)
        timelines[timeline_to_update_index].pop('assignment_groups', None)
        timelines[timeline_to_update_index].pop('sap_filter', None)

    config['timelines'] = timelines

    try:
        with open(os.path.join(DB_ROOT_PATH, 'config.json'), 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=2, ensure_ascii=False)
        return jsonify({"message": f"Timeline '{data.get('name')}' wurde erfolgreich aktualisiert."}), 200
    except Exception as e:
        print(f"FEHLER beim Aktualisieren der config.json: {e}")
        return jsonify({"error": "Konfigurationsdatei konnte nicht gespeichert werden."}), 500

@app.route('/')
@login_required
def index():
    """Leitet den Benutzer direkt zum neuen Dashboard weiter."""
    return redirect(url_for('dashboard_page'))

@app.route('/dashboard')
@login_required
def dashboard_page():
    # 1. Basis-Daten laden
    config = load_app_config()
    timelines = config.get('timelines', [])
    timelines.sort(key=lambda x: x['type'], reverse=True)

    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname: user_initials = (vorname[0] + nachname[0]).upper()
    current_user_id = session.get('user_id', '').lower()

    # 2. Mitarbeiter laden
    if not ALL_EMPLOYEES:
        load_employees_from_csv(EMPLOYEES_FILE_PATH)
    # User-Map für schnellen Namens-Lookup (für Kollegen-Anzeige)
    user_map = {u['id']: u for u in ALL_USERS if u.get('id')}

    # 3. Anwesenheit Heute prüfen
    today_iso = date.today().isoformat()
    present_employees = []

    for emp in ALL_EMPLOYEES:
        is_present_today = False
        for week in emp.get('weeks', []):
            for day in week.get('days', []):
                if day.get('date') and day['date'].isoformat() == today_iso:
                    if not day.get('ereignis', '').strip():
                        is_present_today = True
                        break
            if is_present_today: break
        if is_present_today:
            present_employees.append(emp)

    present_employees_count = len(present_employees)

    # 4. Globale Zuweisungen laden
    global_scheduler.load()
    assignments_today = global_scheduler.get_assignments_for_day(today_iso)

    # 5. "Free Pool" & "Busy List" berechnen
    busy_employee_ids = set()
    for shift_data in assignments_today.values():
        busy_employee_ids.update(shift_data.keys())

    free_employees = []
    busy_employees_details = []

    for emp in present_employees:
        emp_id = emp.get('id', '').lower()
        if emp_id in busy_employee_ids:
            # Details holen (AM oder PM)
            assignment_am = assignments_today.get('AM', {}).get(emp_id)
            assignment_pm = assignments_today.get('PM', {}).get(emp_id)
            details = assignment_am or assignment_pm

            # Text bauen
            proj = details.get('project', 'Unbekannt')
            task = details.get('task', '')
            display_text = f"{proj} ({task})" if task else proj

            busy_employees_details.append({
                "name": emp.get('name'),
                "assignment": display_text
            })
        else:
            free_employees.append({"name": emp.get('name')})

    free_employees.sort(key=lambda x: x['name'])
    busy_employees_details.sort(key=lambda x: x['name'])

    # 6. "Mein Plan Heute" (Persönliche Agenda)
    my_missions = []  # Liste von Dictionaries

    if current_user_id:
        def get_mission_details(details, all_assignments_in_shift):
            if not details: return None

            proj = details.get('project', 'Projekt')
            eng = details.get('engine', '')
            task = details.get('task', '')

            # Redundanz-Check: Wenn Engine im Projektnamen steht, Engine ausblenden?
            # Oder einfach beides liefern, das Frontend entscheidet.

            # Kollegen finden
            colleagues = []
            for uid, other_details in all_assignments_in_shift.items():
                if uid != current_user_id and \
                        other_details.get('project') == proj and \
                        other_details.get('task') == task:
                    colleague_user = user_map.get(uid)
                    # Schön formatierter Name (Initial + Nachname) oder nur Nachname
                    name = colleague_user.get('nachname', uid).capitalize()
                    colleagues.append(name)

            return {
                "project": proj,
                "engine": eng,
                "task": task,
                "team": colleagues
            }

        mission_am = get_mission_details(assignments_today.get('AM', {}).get(current_user_id),
                                         assignments_today.get('AM', {}))
        if mission_am:
            mission_am['shift'] = 'Vormittag'
            my_missions.append(mission_am)

        mission_pm = get_mission_details(assignments_today.get('PM', {}).get(current_user_id),
                                         assignments_today.get('PM', {}))
        if mission_pm:
            # Check ob identisch zu AM (dann zusammenfassen oder nur einmal zeigen)
            # Wir zeigen es separat, wenn es unterschiedlich ist, sonst merken wir es an
            is_same = mission_am and mission_am['project'] == mission_pm['project'] and mission_am['task'] == \
                      mission_pm['task']

            if is_same:
                mission_am['shift'] = 'Ganztägig'  # Update AM Label
            else:
                mission_pm['shift'] = 'Nachmittag'
                my_missions.append(mission_pm)

    # 7. Testzellen Status (für Kachel)
    all_test_cells_with_data = load_locations_and_events()
    test_cell_statuses = [{'name': cell.get('ID', 'Unbekannt')} for cell in all_test_cells_with_data]
    test_cell_statuses.sort(key=lambda x: x['name'])

    sfm_texts = load_sfm_texts()

    # 8. Trainings laden
    upcoming_trainings = []
    if os.path.exists(TRAINING_DATA_FILE):
        try:
            with open(TRAINING_DATA_FILE, 'r', encoding='utf-8') as f:
                t_data = json.load(f)
                sessions = t_data.get('sessions', [])

                # Catalog Map robuster machen: Keys als String speichern
                catalog = {str(c['id']): c for c in t_data.get('catalog', [])}

                today_str = date.today().isoformat()

                # Filtern
                future_sessions = [s for s in sessions if s.get('date') >= today_str]
                future_sessions.sort(key=lambda x: x['date'])

                for sess in future_sessions[:3]:
                    # Lookup mit String-Konvertierung
                    cat_id = str(sess.get('catalogId'))
                    cat_item = catalog.get(cat_id)

                    if cat_item:
                        try:
                            date_obj = datetime.strptime(sess['date'], '%Y-%m-%d')
                            upcoming_trainings.append({
                                "title": cat_item['title'],
                                "category": cat_item.get('category', 'Training'),
                                "date_day": date_obj.day,
                                "date_month": date_obj.strftime('%b').upper(),
                                "time": sess.get('time', ''),
                                "image": cat_item.get('image'),
                                "location": sess.get('location', '')
                            })
                        except:
                            pass
        except Exception as e:
            print(f"Fehler beim Laden der Trainings für Dashboard: {e}")
    # --- NEU: ENTSCHEIDUNGEN (Triage & Review) ---
    my_decisions = []

    # Wir brauchen die Orders. Da wir sie hier nicht geladen haben, laden wir sie.
    m_data = load_maintenance_data()
    orders = m_data.get('orders', [])

    user_roles = session.get('roles', [])

    issues_with_actions = set()
    for o in orders:
        if o.get('type') == 'A' and o.get('linkedIssue'):
            issues_with_actions.add(o.get('linkedIssue'))

    for order in orders:
        if order.get('status') == 'Completed': continue

        # Fall 1: Triage
        if order.get('type') == 'I' and order.get('priority') == 'OPEN':
            if can_user_review(order, current_user_id, user_roles):
                my_decisions.append({
                    "type": "triage",
                    "title": order.get('task'),
                    "id": order.get('id')
                })

        # Fall 2: Review (Review Ready)
        if order.get('status') == 'Review Ready':
            if can_user_review(order, current_user_id, user_roles):
                my_decisions.append({
                    "type": "review",
                    "title": order.get('task'),
                    "id": order.get('id')
                })

        # Fall 3: Planung (Issue ohne Action)
        # Bedingung: Issue, nicht Completed, nicht OPEN (also schon bewertet), keine Actions
        if order.get('type') == 'I' and \
                order.get('status') != 'Completed' and \
                order.get('priority') != 'OPEN' and \
                order.get('id') not in issues_with_actions:

            # Berechtigung: Wer darf planen? (Analog zu Review)
            if can_user_review(order, current_user_id, user_roles):
                my_decisions.append({
                    "type": "planning",
                    "title": order.get('task'),
                    "id": order.get('id')
                })

    return render_template_string(
        DASHBOARD_HTML_CONTENT,
        timelines=timelines,
        current_user_display_name=get_current_username_from_session(),
        current_user_initials=user_initials,
        test_cell_statuses=test_cell_statuses,
        present_employees_count=present_employees_count,
        free_employees_count=len(free_employees),
        free_employees=free_employees,
        busy_employees=busy_employees_details,
        sfm_texts=sfm_texts,
        my_missions=my_missions,
        my_decisions=my_decisions,
        active_page='dashboard',
        upcoming_trainings=upcoming_trainings,
        session=session,
        url_for=url_for
    )


@app.route('/admin/delete_db_file/<path:filepath>', methods=['POST'])
@admin_required
def delete_db_file(filepath):
    """Löscht eine Datei aus dem 'db' Ordner."""
    base_dir = os.path.abspath(DB_ROOT_PATH)
    full_path = os.path.abspath(os.path.join(base_dir, filepath))

    if not full_path.startswith(base_dir):
        flash('Ungültiger Dateipfad, Löschen verweigert.', 'error')
        return redirect(url_for('file_manager_page'))

    try:
        if os.path.isfile(full_path):
            os.remove(full_path)
            flash(f"Datei '{os.path.basename(filepath)}' wurde erfolgreich gelöscht.", 'success')
        else:
            flash('Datei nicht gefunden.', 'error')
    except Exception as e:
        flash(f"Fehler beim Löschen der Datei: {e}", 'error')

    return redirect(url_for('file_manager_page'))


@app.route('/admin/upload_new_db_file/', defaults={'dirpath': '.'}, methods=['POST'])
@app.route('/admin/upload_new_db_file/<path:dirpath>', methods=['POST'])
@admin_required
def upload_new_db_file(dirpath):
    """Lädt eine neue Datei in ein bestimmtes Verzeichnis im 'db' Ordner hoch."""
    base_dir = os.path.abspath(DB_ROOT_PATH)

    # Der Punkt '.' repräsentiert das Root-Verzeichnis 'db'
    if dirpath == '.':
        target_dir = base_dir
    else:
        target_dir = os.path.abspath(os.path.join(base_dir, dirpath))

    if not target_dir.startswith(base_dir) or not os.path.isdir(target_dir):
        flash('Ungültiges Zielverzeichnis.', 'error')
        return redirect(url_for('file_manager_page'))

    if 'new_file' not in request.files:
        flash('Keine Datei im Upload gefunden.', 'error')
        return redirect(url_for('file_manager_page'))

    file = request.files['new_file']
    if file.filename == '':
        flash('Keine Datei ausgewählt.', 'error')
        return redirect(url_for('file_manager_page'))

    if file:
        filename = secure_filename(file.filename)
        destination = os.path.join(target_dir, filename)

        if os.path.exists(destination):
            flash(
                f"Fehler: Eine Datei mit dem Namen '{filename}' existiert bereits. Bitte verwenden Sie die 'Ersetzen'-Funktion.",
                'error')
            return redirect(url_for('file_manager_page'))

        try:
            file.save(destination)
            flash(f"Datei '{filename}' wurde erfolgreich hochgeladen.", 'success')
        except Exception as e:
            flash(f"Fehler beim Speichern der neuen Datei: {e}", 'error')

    return redirect(url_for('file_manager_page'))


@app.route('/admin/process_templates')
@login_required
def serve_process_templates_management_page():
    user_roles = session.get('roles', [])
    allowed_roles = [Roles.ADMIN, Roles.PLANNER, Roles.TESTENGINEER]

    if not any(role in user_roles for role in allowed_roles):
        return redirect(url_for('dashboard_page'))  # Oder abort(403)

    config = load_app_config()
    timelines = config.get('timelines', [])

    user_name = get_current_username_from_session() or "Unbekannt"
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    return render_template_string(
        PROCESS_TEMPLATES_MANAGEMENT_HTML,
        timelines=timelines,
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        active_page='process_templates',
        url_for=url_for,
        session=session
    )


@app.route('/api/process_templates', methods=['GET'])
@login_required
def api_get_process_templates():
    return jsonify(load_process_templates())


@app.route('/api/config')
@admin_required
def get_app_config():
    """Gibt die gesamte config.json zurück."""
    return jsonify(load_app_config())


@app.route('/api/process_templates', methods=['POST'])
@login_required  # War @admin_required
def api_save_process_templates():
    user_roles = session.get('roles', [])
    allowed_roles = [Roles.ADMIN, Roles.PLANNER, Roles.TESTENGINEER]

    if not any(role in user_roles for role in allowed_roles):
        return jsonify({"error": "Keine Berechtigung."}), 403

    full_updated_data = request.json

    # Wir prüfen nur noch, ob überhaupt Daten gesendet wurden
    if not isinstance(full_updated_data, dict):
        return jsonify({"error": "Ungültiges Datenformat. Ein JSON-Objekt wurde erwartet."}), 400

    try:
        # Wir müssen nichts mehr zusammenfügen. Wir überschreiben die Datei einfach
        # mit den neuen, vollständigen Daten.
        with open(PROCESS_TEMPLATES_FILE_PATH, 'w', encoding='utf-8') as f:
            json.dump(full_updated_data, f, indent=2, ensure_ascii=False)

        return jsonify({"message": "Prozess-Templates erfolgreich gespeichert."}), 200

    except IOError as e:
        print(f"FEHLER beim Speichern von process_templates.json: {e}")
        return jsonify({"error": "Fehler beim Schreiben der Datei."}), 500

@app.route('/api/available_documents')
@login_required
def api_get_available_documents():
    if not os.path.isdir(DOCUMENTS_FOLDER_PATH):
        # Erstelle den Ordner, wenn er nicht existiert, um Fehler zu vermeiden
        os.makedirs(DOCUMENTS_FOLDER_PATH, exist_ok=True)
        return jsonify([])
    try:
        # Liste nur Dateien auf, keine Ordner, und filtere versteckte Dateien (z.B. .DS_Store auf Mac)
        files = [f for f in os.listdir(DOCUMENTS_FOLDER_PATH)
                 if os.path.isfile(os.path.join(DOCUMENTS_FOLDER_PATH, f)) and not f.startswith('.')]
        return jsonify(sorted(files))
    except Exception as e:
        print(f"FEHLER beim Lesen des Dokumentenordners: {e}")
        return jsonify({"error": "Fehler beim Zugriff auf den Dokumentenordner."}), 500


@app.route('/admin/file_manager')
@admin_required
def file_manager_page():
    if not os.path.exists(DB_ROOT_PATH):
        flash(f"Fehler: Das Verzeichnis '{DB_ROOT_PATH}' existiert nicht!", 'error')
        tree = []
    else:
        tree = build_file_tree(DB_ROOT_PATH)

    # NEU: Initialen und Benutzername ermitteln
    user_name = get_current_username_from_session() or "Unbekannt"
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    return render_template_string(
        FILE_MANAGER_HTML_CONTENT,
        file_tree=tree,
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        active_page='file_manager',  # Eindeutiger Name für diese Seite
        url_for=url_for,
        get_flashed_messages=get_flashed_messages,  # Bleibt wichtig für diese Seite
        session=session
    )


@app.route('/admin/download_db_file/<path:filepath>')
@admin_required
def download_db_file(filepath):
    """Stellt eine Datei aus dem 'db' Ordner zum Download bereit."""
    base_dir = os.path.abspath(DB_ROOT_PATH)
    full_path = os.path.abspath(os.path.join(base_dir, filepath))

    if not full_path.startswith(base_dir):
        abort(403)  # Forbidden

    if not os.path.isfile(full_path):
        abort(404)  # Not Found

    return send_file(full_path, as_attachment=True)


@app.route('/admin/upload_db_file/<path:filepath>', methods=['POST'])
@admin_required
def upload_db_file(filepath):
    """Ersetzt eine bestehende Datei im 'db' Ordner."""
    base_dir = os.path.abspath(DB_ROOT_PATH)
    full_path = os.path.abspath(os.path.join(base_dir, filepath))

    if not full_path.startswith(base_dir):
        flash('Ungültiger Dateipfad.', 'error')
        return redirect(url_for('file_manager_page'))

    if 'file' not in request.files:
        flash('Keine Datei im Upload gefunden.', 'error')
        return redirect(url_for('file_manager_page'))

    file = request.files['file']
    if file.filename == '':
        flash('Keine Datei ausgewählt.', 'error')
        return redirect(url_for('file_manager_page'))

    if file:
        try:
            file.save(full_path)
            flash(
                f"Datei '{os.path.basename(filepath)}' wurde erfolgreich ersetzt. Die Anwendung muss möglicherweise neu gestartet werden, damit die Änderungen wirksam werden.",
                'success')
        except Exception as e:
            flash(f"Fehler beim Speichern der Datei: {e}", 'error')

    return redirect(url_for('file_manager_page'))


@app.route('/download_document/<path:project_id>/<step_name>')
@login_required
def download_document_route(project_id, step_name):
    if '_' not in project_id:
        return "Ungültige Projekt-ID-Formatierung.", 400
    try:
        project_code, engine_serial = project_id.split('_', 1)
    except ValueError:
        return f"Ungültige Projekt-ID: '{project_id}'", 400

    try:
        # 1. Lade die Konfiguration, um alle möglichen Projektdateien zu kennen
        config = load_app_config()
        all_possible_data_files = set()
        for tl in config.get('timelines', []):
            if tl.get('data_file'):
                all_possible_data_files.add(tl.get('data_file'))
            if tl.get('data_files'):
                all_possible_data_files.update(tl.get('data_files'))

        # 2. Lade alle Projekte aus allen konfigurierten Dateien
        all_known_projects = load_timeline_projects_for_flask(list(all_possible_data_files))

        # 3. Suche das spezifische Projekt in der frisch geladenen Gesamtliste
        project_data = next((p for p in all_known_projects if
                             p.get('project_code') == project_code and p.get('engine_serial') == engine_serial), None)

        if not project_data:
            return f"Projekt mit ID {project_id} nicht gefunden.", 404

        # 4. Der Rest der Logik bleibt identisch
        templates = load_process_templates()
        engine_type = project_data.get('engine_type')
        template = find_best_template_for_engine(engine_type, templates)

        step_config = next((s for s in template.get('steps', []) if s['name'] == step_name), None)

        if not step_config or not step_config.get('document_template'):
            return f"Kein Dokument-Template für Schritt '{step_name}' beim Typ '{engine_type}' definiert.", 404

        template_name = step_config['document_template']
        template_path = os.path.join(DOCUMENTS_FOLDER_PATH, template_name)
        if not os.path.exists(template_path):
            return f"Template-Datei '{template_name}' nicht gefunden.", 404

        context = {
            'WORK_ORDER_PLACEHOLDER': project_data.get('project_code', ''),
            'ENGINE_SERIAL_PLACEHOLDER': project_data.get('engine_serial', '')
        }

        document = Document(template_path)
        docx_find_replace(document, context)

        file_stream = io.BytesIO()
        document.save(file_stream)
        file_stream.seek(0)

        safe_project_code = project_data.get('project_code', '').replace('.', '')
        download_filename = f"{step_name}_{safe_project_code}_{project_data.get('engine_serial', '')}.docx"

        return send_file(
            file_stream,
            as_attachment=True,
            download_name=download_filename,
            mimetype='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
        )
    except Exception as e:
        print(f"!!! KRITISCHER FEHLER bei der Dokumentenerstellung für {project_id}:")
        traceback.print_exc()
        return "Fehler bei der Dokumentenerstellung. Bitte Konsole prüfen.", 500


@app.route('/api/manual_sap_update', methods=['POST'])
@admin_required
def api_manual_sap_update():
    if 'sap_file' not in request.files:
        return jsonify({"error": "Keine Datei im Request gefunden."}), 400

    file = request.files['sap_file']

    if file.filename == '':
        return jsonify({"error": "Keine Datei ausgewählt."}), 400

    if not file.filename.lower().endswith('.csv'):
        return jsonify({"error": "Ungültiger Dateityp. Bitte eine CSV-Datei hochladen."}), 400

    try:
        file_stream = io.BytesIO(file.read())

        global ALL_PROJECTS
        current_project_order = []
        for proj in ALL_PROJECTS:
            key = (proj.get('project_code', ''), proj.get('engine_serial', ''))
            if key[0] and key[1]:
                current_project_order.append(key)

        # `filter_sap_data` kann jetzt direkt den Stream verarbeiten!
        filtered_df = filter_sap_data(file_stream)

        if filtered_df is None or filtered_df.empty:
            return jsonify(
                {"message": "Keine neuen oder relevanten Daten in der hochgeladenen SAP-Datei gefunden."}), 200

        changes_were_made = merge_into_timeline_projects(CURRENT_PROJECT_FILE_PATH, filtered_df, current_project_order)

        # Wichtig: Die ALL_PROJECTS im Speicher neu laden, da die CSV-Datei überschrieben wurde
        load_timeline_projects_for_flask(CURRENT_PROJECT_FILE_PATH)

        if changes_were_made:
            return jsonify({"message": "Timeline erfolgreich mit manuell hochgeladenen SAP-Daten aktualisiert."}), 200
        else:
            return jsonify({
                "message": "Projekte sind bereits auf dem neuesten Stand (keine Änderungen durch manuelle SAP-Daten)."}), 200

    except Exception as e:
        traceback.print_exc()
        return jsonify({"error": f"Ein Fehler ist beim manuellen SAP-Update aufgetreten: {str(e)}"}), 500


@app.route('/api/upload_employee_presence', methods=['POST'])
@admin_required  # Wichtig: Nur Admins dürfen diese Aktion ausführen!
def api_upload_employee_presence():
    if 'presence_file' not in request.files:
        return jsonify({"error": "Keine Datei im Request gefunden."}), 400

    file = request.files['presence_file']

    if file.filename == '':
        return jsonify({"error": "Keine Datei ausgewählt."}), 400

    if not file.filename.lower().endswith('.csv'):
        return jsonify({"error": "Ungültiger Dateityp. Bitte eine CSV-Datei hochladen."}), 400

    try:
        uploaded_content = file.read().decode('utf-8-sig')
        with open(EMPLOYEES_FILE_PATH, 'w', newline='', encoding='utf-8-sig') as target_file:
            target_file.write(uploaded_content)

        global ALL_EMPLOYEES, ALL_QUALIFICATIONS
        ALL_EMPLOYEES = []  # Leeren, um Neuladen zu erzwingen
        ALL_QUALIFICATIONS = []  # Ebenfalls leeren, da der Sync davon abhängt

        load_and_prepare_qualifications_data()

        return jsonify({
            "message": f"Anwesenheitsdaten aus '{file.filename}' erfolgreich importiert. Die Seite wird neu geladen."}), 200

    except Exception as e:
        print(f"FEHLER beim Verarbeiten der hochgeladenen Anwesenheitsdatei: {e}")
        traceback.print_exc()
        return jsonify({"error": "Ein interner Fehler ist beim Speichern der Datei aufgetreten."}), 500


@app.route('/api/create_manual_project/<string:timeline_id>', methods=['POST'])
@admin_required
def api_create_manual_project(timeline_id):
    # 1. Konfiguration laden
    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)
    if not timeline_config or 'data_file' not in timeline_config:
        return jsonify({"error": "Timeline-Konfiguration zum Projekt erstellen nicht gefunden."}), 404

    target_file = timeline_config['data_file']

    # 2. Daten validieren
    data = request.json
    required_fields = ['name', 'engine_type', 'project_code', 'engine_serial', 'events_start_date']
    if not data or not all(field in data for field in required_fields):
        return jsonify({"error": "Fehlende Projektdaten."}), 400

    # 3. Projekt-Existenz prüfen
    current_projects_for_target_file = load_timeline_projects_for_flask([target_file])
    project_key = (data['project_code'], data['engine_serial'])
    if any(p.get('project_code') == project_key[0] and p.get('engine_serial') == project_key[1] for p in
           current_projects_for_target_file):
        return jsonify({
            "error": "Ein Projekt mit diesem Projekt-Code und Engine Serial existiert bereits in dieser Timeline."}), 409

    # 4. Startdatum verarbeiten
    try:
        start_date = datetime.strptime(data['events_start_date'], '%Y-%m-%d').date()
        ts_datetime = datetime.combine(start_date, time(8, 0))  # Annahme: Start um 8 Uhr
    except ValueError:
        return jsonify({"error": "Ungültiges Datumsformat. Bitte YYYY-MM-DD verwenden."}), 400

    # 5. Events dynamisch aus Template generieren
    engine_type = data['engine_type']
    process_templates = load_process_templates()
    template = find_best_template_for_engine(engine_type, process_templates)
    template_events = template.get("timeline_events", [])

    dynamic_events = []
    current_date = ts_datetime.date()
    current_shift = 'AM'

    for event_def in template_events:
        duration = event_def.get("duration_shifts", 1)
        start_date_loop = current_date
        start_shift_loop = current_shift

        end_date_loop = start_date_loop
        end_shift_loop = start_shift_loop

        # KORREKTUR: Schleife läuft exakt 'duration' mal (keine -1 mehr)
        for _ in range(duration):
            if end_shift_loop == 'AM':
                end_shift_loop = 'PM'
            else:
                end_shift_loop = 'AM'
                end_date_loop += timedelta(days=1)

        dynamic_events.append({
            'title': event_def.get("name", "Unknown"),
            'start': start_date_loop.strftime("%Y-%m-%d"),
            'start_shift': start_shift_loop,
            'end': end_date_loop.strftime("%Y-%m-%d"),
            'end_shift': end_shift_loop,
            'color': event_def.get("color", "grey")
        })

        # KORREKTUR: Der Start des nächsten Events ist exakt das Ende des aktuellen
        # (da die Schleife oben bereits einen Schritt weiter gegangen ist)
        current_date = end_date_loop
        current_shift = end_shift_loop

    # 6. Neues Projekt-Objekt erstellen
    new_project = {
        'name': data['name'],
        'engine_type': data['engine_type'],
        'project_code': data['project_code'],
        'engine_serial': data['engine_serial'],
        'customer': data.get('customer', '').strip() or 'Manuell erstellt',
        'BE': '',
        'TS': ts_datetime.strftime("%d.%m.%Y %H:%M:%S"),
        'TE': '',
        'SA': '',
        'active': True,
        'events': dynamic_events
    }

    # 7. Speichern
    current_projects_for_target_file.insert(0, new_project)

    if save_projects_for_flask(target_file, current_projects_for_target_file):
        return jsonify({"message": f"Projekt '{new_project['name']}' wurde erfolgreich erstellt und gespeichert."}), 201
    else:
        return jsonify({"error": "Projekt konnte erstellt, aber nicht in der CSV-Datei gespeichert werden."}), 500

@app.route('/api/assignment_rules/<string:timeline_id>', methods=['GET'])
@admin_required
def get_assignment_rules(timeline_id):
    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)

    if not timeline_config or 'assignment_rules_file' not in timeline_config:
        return jsonify({"error": f"Keine 'assignment_rules_file' für Timeline '{timeline_id}' konfiguriert."}), 404

    rules_file_path = timeline_config['assignment_rules_file']
    rules = load_assignment_rules(rules_file_path)

    # Die Optionen (mögliche Skills etc.) können global bleiben, da sie aus allen Qualifikationen stammen
    all_merkmale_for_skills = {m.get('merkmal', '').strip() for q in ALL_QUALIFICATIONS for m in q.get('merkmale', [])
                               if m.get('merkmal', '').strip()}
    all_event_titles = sorted(list(set(evt.get('title', '').upper() for proj in ALL_PROJECTS for evt in
                                       proj.get('events', [])))) if ALL_PROJECTS else []
    all_engine_types = sorted(list(set(merkmal.split('/')[0] for merkmal in all_merkmale_for_skills if '/' in merkmal)))

    return jsonify({
        "rules": rules,
        "options": {
            "event_titles": all_event_titles,
            "engine_types": all_engine_types,
            "skills": sorted(list(all_merkmale_for_skills))
        }
    })


@app.route('/api/process_template_options')
@login_required
def api_get_process_template_options():
    """
    Gibt eine JSON-Struktur zurück, die Triebwerkstypen auf die Namen ihrer
    Timeline-Events abbildet. Nützlich für dynamische Dropdowns im Frontend.
    Beispiel: {"PW1900G": ["Rigging", "Test"], "default": ["AUF", "Test", "AB"]}
    """
    try:
        templates = load_process_templates()
        options = {}
        for engine_type, config in templates.items():
            # Stelle sicher, dass 'timeline_events' existiert und eine Liste ist
            timeline_events = config.get("timeline_events", [])
            if isinstance(timeline_events, list):
                # Extrahiere nur die Namen der Events
                event_names = [event.get("name") for event in timeline_events if event.get("name")]
                options[engine_type] = event_names

        return jsonify(options)
    except Exception as e:
        print(f"FEHLER in api_get_process_template_options: {e}")
        return jsonify({"error": "Fehler beim Laden der Template-Optionen."}), 500

@app.route('/api/assignment_rules/<string:timeline_id>', methods=['POST'])
@admin_required
def update_assignment_rules(timeline_id):
    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)

    if not timeline_config or 'assignment_rules_file' not in timeline_config:
        return jsonify({"error": f"Keine 'assignment_rules_file' für Timeline '{timeline_id}' konfiguriert."}), 404

    data = request.json
    if not data or 'rules' not in data:
        return jsonify({"error": "Ungültige Daten"}), 400

    new_rules = data['rules']
    import uuid
    for rule in new_rules:
        if not rule.get('rule_id'):
            rule['rule_id'] = f"rule_{uuid.uuid4().hex[:8]}"

    rules_file_path = timeline_config['assignment_rules_file']
    if save_assignment_rules(new_rules, rules_file_path):
        return jsonify({"message": "Zuordnungsregeln erfolgreich gespeichert."}), 200
    else:
        return jsonify({"error": "Fehler beim Speichern der Regeln."}), 500


@app.route('/employee_dashboard')
@login_required
@admin_required
def serve_employee_dashboard():
    config = load_app_config()
    timelines = config.get('timelines', [])
    dashboard_data = get_employee_dashboard_data()
    user_name = get_current_username_from_session() or "Unbekannt"
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    m_data = load_maintenance_data()
    orders = m_data.get('orders', [])

    sfm_details = {
        "critical_prio": [],  # Nur Prio Critical
        "blocked_units": [],  # Nur Unit Blocker
        "blocked_hw": [],  # Nur Hardware Blocker
        "other_open": []
    }

    for o in orders:
        if o.get('type') == 'I' and o.get('status') != 'Completed':
            display_text = f"{o.get('id')}: {o.get('task')}"

            is_crit = o.get('priority') == 'Critical'
            is_unit = o.get('isUnitBlocker') in [True, "true"]
            is_hw = len(o.get('blockedAssetIds', [])) > 0

            if is_crit: sfm_details["critical_prio"].append(display_text)
            if is_unit: sfm_details["blocked_units"].append(display_text)
            if is_hw: sfm_details["blocked_hw"].append(display_text)

            if not is_crit and not is_unit and not is_hw:
                sfm_details["other_open"].append(display_text)

    # Statistiken für die Kacheln berechnen
    sfm_stats = {
        # Safety Kachel: Alles was Critical ODER Unit/HW Blocker ist (Eindeutige IDs)
        "critical_total": len(
            set(sfm_details["critical_prio"] + sfm_details["blocked_units"] + sfm_details["blocked_hw"])),
        # Quality Kachel: Alle offenen Issues
        "open_issues": len(orders) - len([o for o in orders if o.get('status') == 'Completed' or o.get('type') != 'I']),
        "details": sfm_details
    }

    sfm_texts = load_sfm_texts()
    sfm_calendar_data = {}
    if os.path.exists(SFM_CALENDAR_FILE):
        try:
            with open(SFM_CALENDAR_FILE, 'r', encoding='utf-8') as f:
                sfm_calendar_data = json.load(f)
        except: sfm_calendar_data = {}

    return render_template_string(
        EMPLOYEE_DASHBOARD_HTML_CONTENT,
        data=dashboard_data,
        current_user_display_name=user_name,
        timelines=timelines,
        current_user_initials=user_initials,
        active_page='employee_dashboard',
        url_for=url_for,
        session=session,
        sfm_stats=sfm_stats,
        sfm_calendar_data=sfm_calendar_data,
        sfm_texts=load_sfm_texts()  # Falls noch für MOTD gebraucht
    )

@app.route('/api/save_sfm_calendar', methods=['POST'])
@login_required
def api_save_sfm_calendar():
    data = request.json
    if save_json_file(SFM_CALENDAR_FILE, data):
        return jsonify({"status": "success"}), 200
    return jsonify({"error": "Speichern fehlgeschlagen"}), 500

@app.route('/api/v2/recalculate_assignments/<string:timeline_id>', methods=['POST'])
@login_required
def api_recalculate_assignments(timeline_id):
    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)

    if not timeline_config:
        return jsonify({"error": f"Keine Konfiguration für Timeline '{timeline_id}' gefunden."}), 404

    data = request.json
    if not data or 'projects' not in data:
        return jsonify({"error": "Ungültige Daten. Projektliste fehlt."}), 400

    projects_from_client = data['projects']
    assignment_groups = data.get('assignment_group', [])
    selected_test_cell_ids = data.get('selected_test_cells', [])

    rules_file_path = timeline_config.get('assignment_rules_file')
    assignment_rules = load_assignment_rules(rules_file_path) if rules_file_path else []

    for project in projects_from_client:
        project_id_prefix = f"{project.get('project_code')}_{project.get('engine_serial')}"
        prepared_events = []
        for i, event in enumerate(project.get('events', [])):
            if not isinstance(event, dict): continue
            if 'id' not in event or not event['id']:
                event['id'] = f"{project_id_prefix}_{event.get('title', 'event')}_{i}"
            start_dt = get_datetime_from_date_shift(parse_date_from_string(event.get('start')),
                                                    event.get('start_shift'))
            end_dt = get_datetime_from_date_shift(parse_date_from_string(event.get('end')), event.get('end_shift'))
            if start_dt and end_dt:
                event['start_datetime'] = start_dt.isoformat()
                event['end_datetime'] = end_dt.isoformat()
                prepared_events.append(event)
        project['events'] = prepared_events

    if not ALL_EMPLOYEES: load_employees_from_csv(EMPLOYEES_FILE_PATH)
    if not ALL_QUALIFICATIONS: load_and_prepare_qualifications_data()
    manual_assignments_file = timeline_config.get('manual_assignments_file')
    manual_assignments = load_assigned_employees(manual_assignments_file) if manual_assignments_file else []

    today = date.today()
    timeline_start_date = (today - timedelta(days=today.weekday())) - timedelta(weeks=3)
    num_days = 10 * 7

    calculated_data = calculate_timeline_assignments(
        projects_from_client, ALL_EMPLOYEES, ALL_QUALIFICATIONS,
        manual_assignments, timeline_start_date, num_days,
        assignment_rules=assignment_rules,
        assignment_groups=assignment_groups,
        selected_test_cell_ids=selected_test_cell_ids,
        timeline_id=timeline_id,
        previous_global_assignments=global_scheduler.assignments
    )

    return jsonify({
        "calculated_data": calculated_data,
        "manual_assignments": manual_assignments
    })


@app.route('/api/timeline_data', methods=['GET'])
@login_required
def get_timeline_data_api():
    """
    Diese Route ist jetzt der dedizierte Endpunkt für die Read-Only-Ansicht.
    Sie ruft die zentrale API-Logik auf, aber schneidet die Daten auf den
    gewünschten Zeitraum (1 oder 2 Wochen) zu.
    """
    try:
        # 1. Parameter aus der Anfrage lesen
        weeks_to_display = request.args.get('weeks', default=2, type=int)
        if weeks_to_display not in [1, 2]:
            weeks_to_display = 2

        # 2. Rufe die neue, zentrale Datenquelle auf, um alle Daten zu erhalten
        unified_response = api_get_unified_timeline_data()
        unified_data = unified_response.get_json()

        if unified_response.status_code != 200:
            return jsonify(unified_data), unified_response.status_code

    except Exception as e:
        print(f"Schwerwiegender Fehler beim Aufruf von api_get_unified_timeline_data: {e}")
        traceback.print_exc()
        return jsonify({"error": "Interne Datenbeschaffung fehlgeschlagen."}), 500

    try:
        # 3. Zeitraum für die statische Ansicht definieren
        today = date.today()
        # Montag der aktuellen Woche ist der Startpunkt
        start_of_period = today - timedelta(days=today.weekday())
        num_display_days = 7 * weeks_to_display

        # Mitarbeiter-Map für schnellen Zugriff erstellen
        all_employees_data = unified_data.get('employees', [])
        employee_map_by_id = {emp.get('id', '').lower(): emp for emp in all_employees_data if emp.get('id')}

        # 4. Übersetze die Datenstruktur und FILTERE sie auf den gewünschten Zeitraum
        projects_for_readonly = []
        for proj in unified_data.get('projects', []):
            if not proj.get('active'):
                continue

            tasks_within_period = []
            events_in_project = proj.get('events', [])
            event_starts = {e['start_datetime'] for e in events_in_project if 'start_datetime' in e}
            event_ends = {e['end_datetime'] for e in events_in_project if 'end_datetime' in e}

            for event in events_in_project:
                if 'start_datetime' not in event or 'end_datetime' not in event:
                    continue

                try:
                    # ALT: start_dt = datetime.fromisoformat(event['start_datetime'])
                    # NEU: Wir parsen explizit ohne Zeitzone
                    start_dt = datetime.fromisoformat(event['start_datetime'].replace('Z', ''))
                    end_dt = datetime.fromisoformat(event['end_datetime'].replace('Z', ''))
                except (ValueError, TypeError):
                    continue

                is_connected_to_prev = event['start_datetime'] in event_ends
                is_connected_to_next = event['end_datetime'] in event_starts

                current_dt = start_dt
                slot_index = 0
                while current_dt < end_dt:
                    # Hier wird gefiltert!
                    day_offset = (current_dt.date() - start_of_period).days
                    if 0 <= day_offset < num_display_days:
                        # Die Spaltennummer wird jetzt relativ zum Start der Anzeigeperiode berechnet
                        start_column = (day_offset * 2) + (0 if current_dt.hour < 12 else 1) + 1

                        event_slot_key = f"{event.get('id', '')}_{slot_index}"
                        assignment_info = unified_data.get('calculated_data', {}).get('assignments', {}).get(
                            event_slot_key, {})
                        employee_ids = assignment_info.get("employees", [])
                        employee_initials = [employee_map_by_id.get(eid.lower(), {}).get('initials', '??') for eid in
                                             employee_ids if eid]

                        tasks_within_period.append({
                            "task_name": event.get('title'),
                            "start_datetime_iso": current_dt.isoformat(),
                            "people_initials": employee_initials,
                            "start_column_on_timeline": start_column,
                            "is_connected_to_prev": is_connected_to_prev and slot_index == 0,
                            "is_connected_to_next": is_connected_to_next and slot_index == int(
                                (end_dt - start_dt).total_seconds() / 43200) - 1,
                            "project_name": proj.get('name'),
                            "project_typ": "ENGINE",
                            "engine_serial": proj.get('engine_serial'),
                            "original_shift_name": 'AM' if current_dt.hour < 12 else 'PM'
                        })

                    current_dt += timedelta(hours=12)
                    slot_index += 1

            if tasks_within_period:
                projects_for_readonly.append({
                    "project_typ": "ENGINE",
                    "project_name": proj.get('name'),
                    "engine_type": proj.get('engine_type'),
                    "tasks": tasks_within_period
                })

        todays_tasks_summary = []
        today_iso = date.today().isoformat()
        for proj in projects_for_readonly:
            for task in proj.get('tasks', []):
                task_date = datetime.fromisoformat(task['start_datetime_iso']).date().isoformat()
                if task_date == today_iso:
                    todays_tasks_summary.append({
                        "task_name": task.get('task_name'),
                        "people_initials": task.get('people_initials', []),
                        "original_shift_name": task.get('original_shift_name'),
                        "project_name": proj.get('project_name'),
                        "project_typ": proj.get('project_typ', "ENGINE")
                    })
        todays_tasks_summary.sort(key=lambda x: (x['original_shift_name'] == 'PM', x['project_name']))

        return jsonify({
            "projects": projects_for_readonly,
            "todays_summary": todays_tasks_summary,
            "timeline_start_date": start_of_period.isoformat(),  # Wichtig: Das korrekte Startdatum senden!
            "num_days_to_display": num_display_days,
            "num_columns_to_display": num_display_days * 2
        })

    except Exception as e:
        print(f"FEHLER bei der Übersetzung der Daten für die Read-Only-Ansicht: {e}")
        traceback.print_exc()
        return jsonify({"error": "Fehler bei der Datenaufbereitung für die Anzeige."}), 500


@app.route('/api/v2/timeline_data/<string:timeline_id>')
@login_required
def api_get_unified_timeline_data(timeline_id):
    # NEU: Lese den view_mode aus der URL
    view_mode = request.args.get('view_mode', 'editable')  # Standard ist 'editable', wenn nichts übergeben wird

    print("\n--- DEBUG: api_get_unified_timeline_data AUFGERUFEN ---")
    print(f"--- DEBUG: Angefragte timeline_id: {timeline_id}, view_mode: {view_mode} ---")

    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)
    if not timeline_config:
        return jsonify({"error": "Timeline-Konfiguration nicht gefunden."}), 404

    file_paths = []
    if 'data_files' in timeline_config:
        file_paths = timeline_config['data_files']
    elif 'data_file' in timeline_config:
        file_paths = [timeline_config['data_file']]

    if not file_paths:
        return jsonify(
            {"error": f"Keine 'data_file' oder 'data_files' für Timeline '{timeline_id}' konfiguriert."}), 500

    timeline_projects = load_timeline_projects_for_flask(file_paths)
    print(f"--- DEBUG: {len(timeline_projects)} Projekte initial aus CSV-Dateien geladen. ---")

    # --- ANGEPASSTE FILTERLOGIK ---

    # Filtere, wenn der Aufruf explizit als 'static' markiert ist
    if view_mode == 'static':
        print("--- DEBUG: view_mode ist 'static', starte Filterung. ---")
        today = date.today()
        start_of_current_week = today - timedelta(days=today.weekday())
        end_of_next_week = start_of_current_week + timedelta(days=13)

        print(
            f"--- DEBUG: Relevanter Zeitraum: {start_of_current_week.isoformat()} bis {end_of_next_week.isoformat()} ---")

        relevant_projects = []
        for project in timeline_projects:
            if not project.get('active'):
                continue

            has_relevant_event = False
            for event in project.get('events', []):
                try:
                    event_start_date = parse_date_from_string(event.get('start'))
                    event_end_date = parse_date_from_string(event.get('end'))

                    if event_start_date and event_end_date:
                        if not (event_end_date < start_of_current_week or event_start_date > end_of_next_week):
                            has_relevant_event = True
                            break
                except:
                    continue

            if has_relevant_event:
                relevant_projects.append(project)

        print(
            f"--- DEBUG: Vor Filterung: {len(timeline_projects)} Projekte. Nach Filterung: {len(relevant_projects)} Projekte. ---")
        timeline_projects = relevant_projects

    else:
        print("--- DEBUG: view_mode ist nicht 'static', keine Filterung der Projektliste. ---")

    # --- ENDE DER FILTERLOGIK ---

    # Der Rest der Funktion bleibt unverändert
    if not ALL_EMPLOYEES: load_employees_from_csv(EMPLOYEES_FILE_PATH)
    if not ALL_QUALIFICATIONS: load_and_prepare_qualifications_data()
    if not ALL_COMMENTS: load_all_comments()

    all_locations_full = load_locations_and_events()
    visible_cell_ids = timeline_config.get('visible_test_cells', [])
    visible_locations = [loc for loc in all_locations_full if loc.get('ID') in visible_cell_ids]

    manual_assignments_file = timeline_config.get('manual_assignments_file')
    manual_assignments = load_assigned_employees(manual_assignments_file) if manual_assignments_file else []

    comment_counts = Counter(c.get('project_id') for c in ALL_COMMENTS)

    projects_copy = [p.copy() for p in timeline_projects if p]
    for project in projects_copy:
        project_id = f"{project.get('project_code')}_{project.get('engine_serial')}"
        project['comment_count'] = comment_counts.get(project_id, 0)
        project_events = []
        for i, evt_data in enumerate(project.get('events', [])):
            if not isinstance(evt_data, dict): continue
            if 'id' not in evt_data or not evt_data['id']:
                evt_data['id'] = f"{project_id}_{evt_data.get('title', 'event')}_{i}"
            start_dt = get_datetime_from_date_shift(parse_date_from_string(evt_data.get('start')),
                                                    evt_data.get('start_shift'))
            end_dt = get_datetime_from_date_shift(parse_date_from_string(evt_data.get('end')),
                                                  evt_data.get('end_shift'))
            if start_dt and end_dt:
                evt_data['start_datetime'] = start_dt.isoformat()
                evt_data['end_datetime'] = end_dt.isoformat()
                project_events.append(evt_data)
        project['events'] = project_events

    employee_qualifications_map = {q['id'].lower(): q for q in ALL_QUALIFICATIONS if q.get('id')}
    rules_file_path = timeline_config.get('assignment_rules_file')
    assignment_rules = load_assignment_rules(rules_file_path) if rules_file_path else []

    qualification_statuses_for_tasks = {}
    for project in projects_copy:
        for event in project.get('events', []):
            event_id = event.get('id')
            if not event_id: continue
            qualification_statuses_for_tasks[event_id] = {}
            for employee in ALL_EMPLOYEES:
                emp_id = employee.get('id')
                if not emp_id: continue
                emp_quals = employee_qualifications_map.get(emp_id.lower())
                status = get_employee_qualification_status_by_rules(emp_quals, project, event.get('title'),
                                                                    assignment_rules)
                if status:
                    qualification_statuses_for_tasks[event_id][emp_id.lower()] = status

    today = date.today()
    timeline_start_date = (today - timedelta(days=today.weekday())) - timedelta(weeks=3)
    num_days = 10 * 7

    assignment_groups_for_timeline = timeline_config.get('assignment_groups', ['FF'])

    calculated_data = calculate_timeline_assignments(
        projects_copy, ALL_EMPLOYEES, ALL_QUALIFICATIONS,
        manual_assignments, timeline_start_date, num_days,
        assignment_rules=assignment_rules,
        assignment_groups=assignment_groups_for_timeline,
        selected_test_cell_ids=visible_cell_ids,
        timeline_id=timeline_id,
        previous_global_assignments=global_scheduler.assignments
    )

    return jsonify({
        "projects": projects_copy,
        "employees": ALL_EMPLOYEES,
        "qualifications": ALL_QUALIFICATIONS,
        "locations": visible_locations,
        "timeline_config": {
            "start_date": timeline_start_date.isoformat(),
            "num_days": num_days,
            "columns_per_day": 2,
            "assignment_groups": assignment_groups_for_timeline
        },
        "calculated_data": calculated_data,
        "manual_assignments": manual_assignments,
        "qualification_statuses_for_tasks": qualification_statuses_for_tasks
    })


@app.route('/test_cell_management')
@login_required
def serve_test_cell_management_page():
    config = load_app_config()
    timelines = config.get('timelines', [])
    user_name = get_current_username_from_session() or "Unbekannt"
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    return render_template_string(
        TEST_CELL_MANAGEMENT_HTML_CONTENT,
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        timelines=timelines,
        active_page='test_cells',
        url_for=url_for,
        session=session
    )


@app.route('/api/test_cells', methods=['GET'])
@login_required
def api_get_test_cells():
    try:
        locations_with_manual_events = load_locations_and_events()
        locations_with_manual_events = inject_dependency_blocks(locations_with_manual_events)

        all_test_cell_ids = [loc.get('ID') for loc in locations_with_manual_events if loc.get('ID')]

        # 2. Lade ALLE Projekte aus ALLEN konfigurierten Quellen
        all_projects = project_manager.get_all_active_projects()

        # 3. Lade alle globalen Daten, die für die Berechnung benötigt werden
        if not ALL_EMPLOYEES: load_employees_from_csv(EMPLOYEES_FILE_PATH)
        if not ALL_QUALIFICATIONS: load_and_prepare_qualifications_data()

        assignment_rules_to_use = []  # Fürs Cockpit brauchen wir keine spezifischen Regeln

        # 4. Bereite Projekte für die Berechnungsfunktion vor
        projects_for_calc = [p.copy() for p in all_projects if p]
        for project in projects_for_calc:
            project_id_prefix = f"{project.get('project_code')}_{project.get('engine_serial')}"
            events_with_dt = []
            for i, evt in enumerate(project.get('events', [])):
                if not isinstance(evt, dict): continue
                if 'id' not in evt or not evt['id']:
                    evt['id'] = f"{project_id_prefix}_{evt.get('title', 'event')}_{i}"
                start_dt = get_datetime_from_date_shift(parse_date_from_string(evt.get('start')),
                                                        evt.get('start_shift'))
                end_dt = get_datetime_from_date_shift(parse_date_from_string(evt.get('end')), evt.get('end_shift'))
                if start_dt and end_dt:
                    evt['start_datetime'] = start_dt.isoformat()
                    evt['end_datetime'] = end_dt.isoformat()
                    events_with_dt.append(evt)
            project['events'] = events_with_dt

        # 5. Führe eine vollständige, globale Berechnung durch (wie im Originalcode)
        today = date.today()
        timeline_start_date = (today - timedelta(days=today.weekday())) - timedelta(weeks=10)  # Großer Zeitraum
        num_days = 20 * 7

        calculated_data = calculate_timeline_assignments(
            projects=projects_for_calc,
            employees=ALL_EMPLOYEES,
            qualifications=ALL_QUALIFICATIONS,
            manual_assignments=[],  # Manuelle Zuweisungen sind hier nicht relevant für die Testzellen-Planung
            timeline_start_date=timeline_start_date,
            num_days=num_days,
            assignment_rules=assignment_rules_to_use,  # Leere Liste ist ok
            assignment_groups=['FF', 'IG', 'IT'],  # Nimm alle bekannten Gruppen
            selected_test_cell_ids=all_test_cell_ids  # WICHTIG: Gib alle Zellen zur Planung frei
        )

        # 6. Füge die berechnete Zuweisung zu den Projekt-Events hinzu (wie im Originalcode)
        assignments = calculated_data.get('assignments', {})
        for proj in projects_for_calc:
            for evt in proj.get('events', []):
                if evt.get('title', '').lower() == 'test':
                    # Finde die erste Zuweisung für dieses Event
                    for i in range(100):  # Sicherheitslimit
                        event_slot_key = f"{evt.get('id')}_{i}"
                        if event_slot_key in assignments:
                            assignment_info = assignments[event_slot_key]
                            if assignment_info and assignment_info.get("test_cell_id"):
                                evt['assigned_test_cell_id'] = assignment_info.get("test_cell_id")
                                break
                        else:
                            break

        # 7. Reichere die Daten mit den "Operational" Events an (wie im Originalcode)
        all_aggregated_locations = aggregate_operational_events(locations_with_manual_events, projects_for_calc)

        return jsonify(all_aggregated_locations)

    except Exception as e:
        print(f"!!! KRITISCHER FEHLER in api_get_test_cells (Original-Pattern Version): {e}")
        traceback.print_exc()
        return jsonify({"error": "Interner Serverfehler beim Laden der Testzellen-Daten."}), 500


@app.route('/api/test_cells', methods=['POST'])
@login_required
def api_save_test_cells():
    user_roles = session.get('roles', [])
    data = request.get_json()
    if data is None:
        return jsonify({"status": "error", "message": "Invalid JSON"}), 400

    # Lade die Originaldaten, um zu prüfen, welche Art von Änderung vorliegt
    original_data = load_locations_and_events()
    original_cell_ids = {cell['ID'] for cell in original_data}
    new_cell_ids = {cell['ID'] for cell in data}

    # Prüfen, ob Stammdaten (Zellen selbst) geändert/hinzugefügt/gelöscht wurden
    is_master_data_changed = (original_cell_ids != new_cell_ids) or \
                             any(c1['TESTCELL'] != c2.get('TESTCELL', '') or set(c1.get('TYPES', [])) != set(
                                 c2.get('TYPES', []))
                                 for c1 in original_data for c2 in data if c1['ID'] == c2.get('ID'))

    # Führe Berechtigungsprüfungen durch
    if is_master_data_changed:
        if Roles.ADMIN not in user_roles and Roles.PLANNER not in user_roles:
            return jsonify(
                {"status": "error", "message": "Keine Berechtigung zum Verwalten von Testzellen-Stammdaten."}), 403

    # Für Event-Änderungen (die immer passieren können)
    can_manage_events = any(role in user_roles for role in
                            [Roles.ADMIN, Roles.PLANNER, Roles.TESTENGINEER, Roles.MAINTENANCEMANAGER,
                             Roles.TESTCELLINSPECTOR])
    if not can_manage_events and not is_master_data_changed:
        # Wenn nur Events geändert wurden, aber keine Berechtigung dafür da ist
        return jsonify({"status": "error", "message": "Keine Berechtigung zum Verwalten von Events."}), 403

    if save_locations_and_events(data):
        return jsonify({"status": "success", "message": "Daten erfolgreich gespeichert."}), 200
    else:
        return jsonify({"status": "error", "message": "Fehler beim Speichern der CSV-Dateien."}), 500


@app.route('/api/comments/<project_id>', methods=['POST'])
@login_required
def add_comment(project_id):
    data = request.json
    if not data or 'comment' not in data or not data['comment'].strip():
        return jsonify({"error": "Kommentar darf nicht leer sein."}), 400

    import uuid
    new_comment = {
        "comment_id": f"comm_{uuid.uuid4().hex[:8]}",
        "project_id": project_id,
        "author": get_current_username_from_session(),
        "timestamp": datetime.now().isoformat(),
        "comment": data['comment'].strip()
    }

    ALL_COMMENTS.append(new_comment)
    if save_all_comments():
        return jsonify(new_comment), 201
    else:
        ALL_COMMENTS.pop()
        return jsonify({"error": "Fehler beim Speichern des Kommentars."}), 500


@app.route('/api/comments/entry/<comment_id>', methods=['PUT'])
@login_required
def update_comment(comment_id):
    data = request.json
    if not data or 'comment' not in data or not data['comment'].strip():
        return jsonify({"error": "Kommentar darf nicht leer sein."}), 400

    comment_to_update = next((c for c in ALL_COMMENTS if c.get('comment_id') == comment_id), None)

    if not comment_to_update:
        return jsonify({"error": "Kommentar nicht gefunden."}), 404

    if comment_to_update.get('author') != get_current_username_from_session():
        return jsonify({"error": "Keine Berechtigung zum Bearbeiten dieses Kommentars."}), 403

    comment_to_update['comment'] = data['comment'].strip()
    comment_to_update['timestamp'] = datetime.now().isoformat()

    if save_all_comments():
        return jsonify(comment_to_update), 200
    else:
        return jsonify({"error": "Fehler beim Speichern der Änderungen."}), 500


@app.route('/api/comments/entry/<comment_id>', methods=['DELETE'])
@login_required
def delete_comment(comment_id):
    global ALL_COMMENTS
    comment_to_delete = next((c for c in ALL_COMMENTS if c.get('comment_id') == comment_id), None)

    if not comment_to_delete:
        return jsonify({"error": "Kommentar nicht gefunden."}), 404

    if comment_to_delete.get('author') != get_current_username_from_session():
        return jsonify({"error": "Keine Berechtigung zum Löschen dieses Kommentars."}), 403

    ALL_COMMENTS = [c for c in ALL_COMMENTS if c.get('comment_id') != comment_id]

    if save_all_comments():
        return jsonify({"message": "Kommentar erfolgreich gelöscht."}), 200
    else:
        ALL_COMMENTS.append(comment_to_delete)
        return jsonify({"error": "Fehler beim Speichern nach dem Löschen."}), 500


@app.route('/api/comments/<project_id>', methods=['GET'])
@login_required
def get_comments(project_id):
    project_comments = [c for c in ALL_COMMENTS if c.get('project_id') == project_id]
    project_comments.sort(key=lambda x: x.get('timestamp', ''), reverse=True)
    return jsonify(project_comments)


@app.route('/api/manual_assignment/<string:timeline_id>', methods=['POST'])
@login_required
def api_manual_assignment(timeline_id):
    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)

    if not timeline_config or 'manual_assignments_file' not in timeline_config:
        return jsonify({"error": f"Keine 'manual_assignments_file' für Timeline '{timeline_id}' konfiguriert."}), 404

    assigned_employees_file = timeline_config['manual_assignments_file']

    data = request.json
    if not data or 'slot_info' not in data or 'assignments' not in data:
        return jsonify({"error": "Ungültige Daten"}), 400

    all_manual_assignments = load_assigned_employees(assigned_employees_file)

    new_assignments_for_slot = data['assignments']
    slot_info = data.get('slot_info')

    if slot_info:
        all_manual_assignments = [
            a for a in all_manual_assignments
            if not (
                    a.get('project_code') == slot_info.get('project_code') and
                    a.get('engine_serial') == slot_info.get('engine_serial') and
                    a.get('event_title') == slot_info.get('event_title') and
                    a.get('date_str') == slot_info.get('date_str') and
                    a.get('shift') == slot_info.get('shift')
            )
        ]

    all_manual_assignments.extend(new_assignments_for_slot)

    if save_assigned_employees(assigned_employees_file, all_manual_assignments):
        update_global_schedule()
        message = "Manuelle Zuweisung erfolgreich gespeichert."
        if not new_assignments_for_slot:
            message = "Manuelle Zuweisung erfolgreich gelöscht."
        return jsonify({"message": message}), 200
    else:
        return jsonify({"error": "Fehler beim Speichern der Zuweisungsdatei."}), 500


@app.route('/api/update_from_sap/<string:timeline_id>', methods=['POST'])
@login_required
def api_update_from_sap(timeline_id):
    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)
    if not timeline_config:
        return jsonify({"error": "Timeline-Konfiguration nicht gefunden."}), 404

    # NEU: Direkter Zugriff auf das Filterobjekt
    sap_filter_rules = timeline_config.get('sap_filter')
    target_file = timeline_config.get('data_file')

    # Die Prüfung `if not sap_filter_rules` ist weiterhin sinnvoll
    if not sap_filter_rules or not target_file:
        return jsonify(
            {"error": f"Kein SAP-Filter oder keine Zieldatei für Timeline '{timeline_id}' konfiguriert."}), 400

    sap_csv_path = r"L:\lgroup\@IT_Data\PPS\ZLSGS_Dataexport.csv"
    if not os.path.exists(sap_csv_path):
        sap_csv_path = "ZLSGS_Dataexport.csv"
        if not os.path.exists(sap_csv_path):
            return jsonify({"error": "SAP-Datei nicht gefunden."}), 404

    try:
        current_projects = load_timeline_projects_for_flask([target_file])
        current_project_order = [(p.get('project_code', ''), p.get('engine_serial', '')) for p in current_projects]

        filtered_df = filter_sap_data(sap_csv_path, filter_rules=sap_filter_rules)

        if filtered_df is None or filtered_df.empty:
            return jsonify(
                {"message": "Keine neuen oder relevanten Daten im SAP-Export für diesen Filter gefunden."}), 200

        changes_made = merge_into_timeline_projects(target_file, filtered_df, current_project_order)

        if changes_made:
            return jsonify(
                {"message": f"Timeline '{timeline_config['name']}' erfolgreich mit SAP-Daten aktualisiert."}), 200
        else:
            return jsonify({"message": "Projekte sind bereits auf dem neuesten Stand."}), 200

    except Exception as e:
        traceback.print_exc()
        return jsonify({"error": f"Fehler beim SAP-Update: {str(e)}"}), 500


@app.route('/api/save_editable_timeline_data/<string:timeline_id>', methods=['POST'])
@login_required
def api_save_editable_timeline_data(timeline_id):
    config = load_app_config()
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)
    if not timeline_config or 'data_file' not in timeline_config:
        return jsonify({"error": "Timeline-Konfiguration zum Speichern nicht gefunden."}), 404

    data_from_client = request.json
    if not data_from_client or 'projects' not in data_from_client:
        return jsonify({"error": "Ungültige Daten erhalten."}), 400

    projects_to_save = data_from_client['projects']
    target_file = timeline_config['data_file']

    # --- GARBAGE COLLECTION & ARCHIVIERUNG ---
    today = date.today()
    # Stichtag: Alles was älter als 4 Wochen (28 Tage) ist, wird aussortiert
    cutoff_date = today - timedelta(weeks=4)

    cleaned_projects = []
    archived_count = 0
    deleted_count = 0

    # Sicherstellen, dass der Ordner für JSONs existiert
    os.makedirs(PROJECT_STATUS_DIR, exist_ok=True)

    for p in projects_to_save:
        # 1. Finde das späteste Enddatum aller Events in diesem Projekt
        latest_date = None
        for evt in p.get('events', []):
            end_date_obj = parse_date_from_string(evt.get('end', ''))
            if end_date_obj:
                if not latest_date or end_date_obj > latest_date:
                    latest_date = end_date_obj

        # Fallback: Wenn keine Events da sind, schau auf TS (Startdatum)
        if not latest_date:
            ts_str = p.get('TS', '')
            try:
                if ts_str:
                    # Parse z.B. "25.02.2026 08:00:00"
                    latest_date = datetime.strptime(ts_str, "%d.%m.%Y %H:%M:%S").date()
            except:
                pass

        # 2. Prüfen: Ist das Projekt alt genug zum Aufräumen?
        if latest_date and latest_date < cutoff_date:

            if p.get('active'):
                # FALL A: Aktiv -> ARCHIVIEREN
                project_code = p.get('project_code', 'UNKNOWN')
                engine_serial = p.get('engine_serial', 'UNKNOWN')
                safe_project_id = re.sub(r'[^a-zA-Z0-9_-]', '', f"{project_code}_{engine_serial}")
                status_file_path = os.path.join(PROJECT_STATUS_DIR, f'project_status_{safe_project_id}.json')

                archive_data = None
                if os.path.exists(status_file_path):
                    try:
                        with open(status_file_path, 'r', encoding='utf-8') as f:
                            archive_data = json.load(f)
                    except:
                        pass

                # Falls im Maintenance Center noch keine JSON erstellt wurde, generieren wir sie jetzt
                if not archive_data:
                    archive_data = get_or_create_project_status_details(p)

                if archive_data:
                    # Planungs-Events in die JSON pressen (Digitaler Zwilling!)
                    archive_data['archived_planning_events'] = p.get('events', [])
                    archive_data['origin_timeline'] = timeline_id
                    archive_data['system_archived_at'] = today.isoformat()

                    save_json_file(status_file_path, archive_data)
                    archived_count += 1

                # WICHTIG: Das Projekt wird NICHT an cleaned_projects angehängt (fliegt also aus der CSV)

            else:
                # FALL B: Inaktiv -> EINFACH LÖSCHEN
                deleted_count += 1
                # WICHTIG: Fliegt ebenfalls aus der CSV, ohne JSON-Update

        else:
            # FALL C: Projekt ist noch aktuell (oder hat kein Datum) -> Behalten
            cleaned_projects.append(p)
    # ----------------------------------------

    # 3. Speichern der bereinigten Liste in die CSV
    if project_manager.save_projects(cleaned_projects, timeline_id):
        update_global_schedule()

        # Dynamische Erfolgsmeldung für den User bauen
        msg = "Änderungen erfolgreich gespeichert."
        if archived_count > 0 or deleted_count > 0:
            msg += f" Aufräumaktion: {archived_count} Projekt(e) archiviert, {deleted_count} inaktive gelöscht."

        return jsonify({"message": msg}), 200
    else:
        return jsonify({"error": "Änderungen nicht erfolgreich gespeichert."}), 500


@app.route('/api/get_editable_timeline_data')
@login_required
def api_get_editable_timeline_data():
    return api_get_unified_timeline_data()


@app.route('/timeline/edit/<string:timeline_id>')
@login_required
def serve_editable_timeline_page(timeline_id):
    config = load_app_config()
    timelines = config.get('timelines', [])
    timeline_config = next(
        (tl for tl in config.get('timelines', []) if tl['id'] == timeline_id and tl['type'] == 'editable'), None)
    if not timeline_config:
        abort(404)

    timeline_name_for_display = timeline_config.get('name', 'Timeline Bearbeitung')
    user_initials = "N/A"  # NEU
    vorname = session.get('user_vorname', '')  # NEU
    nachname = session.get('user_nachname', '')  # NEU
    if vorname and nachname: user_initials = (vorname[0] + nachname[0]).upper()  # NEU

    return render_template_string(
        EDITABLE_TIMELINE_HTML_CONTENT,
        current_user_display_name=get_current_username_from_session(),
        timelines=timelines,
        timeline_id=timeline_id,
        timeline_name=timeline_name_for_display,
        current_user_initials=user_initials,
        active_page='timelines',
    )


@app.route('/employee_presence')
@login_required
def serve_employee_presence_page():
    config = load_app_config()
    timelines = config.get('timelines', [])
    user_name = get_current_username_from_session() or "Unbekannt"
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    return render_template_string(
        EMPLOYEE_PRESENCE_HTML_CONTENT,
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        timelines=timelines,
        active_page='presence',  # Eindeutiger Name für diese Seite
        presence_data={"headers": [], "employee_groups": [], "last_loaded": "Lädt..."},
        url_for=url_for,
        session=session
    )


@app.route('/api/employee_presence_data')
@login_required
def api_get_employee_presence_data():
    week_offset = request.args.get('offset', default=0, type=int)
    num_weeks = request.args.get('num_weeks', default=2, type=int)

    global ALL_EMPLOYEES, MERKMALE_IN_ORDER
    if not ALL_EMPLOYEES:
        if not MERKMALE_IN_ORDER and os.path.exists(QUALIFICATIONS_FILE_PATH):
            _, loaded_merkmale = load_qualifications_from_csv(QUALIFICATIONS_FILE_PATH)
            if loaded_merkmale: MERKMALE_IN_ORDER = loaded_merkmale
        elif not MERKMALE_IN_ORDER:
            MERKMALE_IN_ORDER = ["CFM56-7B", "PW1500G", "LEAP-1A", "Training Allg."];
            print(f"WARNUNG: MERKMALE_IN_ORDER leer, verwende Default: {MERKMALE_IN_ORDER}")
        ALL_EMPLOYEES = load_employees_from_csv(EMPLOYEES_FILE_PATH, MERKMALE_IN_ORDER)

    presence_data_for_web = get_employee_presence_data_for_web(ALL_EMPLOYEES, current_week_offset=week_offset,
                                                               num_weeks_to_display=num_weeks)
    return jsonify(presence_data_for_web)


@app.route('/login', methods=['GET', 'POST'])
def login_page():
    error = None
    if request.method == 'POST':
        user_id_input = request.form.get('username', '').strip().lower()
        password_input = request.form.get('password')
        user_data = next((user for user in ALL_USERS if user['id'] == user_id_input), None)
        if user_data and user_data['password'] == password_input:
            session['user_id'] = user_data['id']

            session['roles'] = user_data.get('roles', [Roles.OBSERVER])

            # 2. Alte Session-Variablen für Abwärtskompatibilität setzen
            session['is_admin'] = Roles.ADMIN in session['roles']
            # WICHTIG: Definiere hier, welche neuen Rollen die alte 'can_change_entries' Berechtigung abbilden.
            # Für den Anfang nehmen wir an, dass 'PLANNER' und 'TESTENGINEER' dies tun.
            session['can_change_entries'] = any(role in session['roles'] for role in
                                                [Roles.PLANNER, Roles.TESTENGINEER, Roles.MAINTENANCEMANAGER,
                                                 Roles.HARDWAREMANAGER])

            vorname = user_data.get('vorname', '')
            nachname = user_data.get('nachname', '')
            if vorname and nachname:
                session['username_display'] = f"{vorname[0]}. {nachname}"
            elif nachname:
                session['username_display'] = nachname
            elif vorname:
                session['username_display'] = vorname
            else:
                session['username_display'] = user_data['id'].upper()

            session['username_actual'] = user_data['id']
            session['user_nachname'] = user_data.get('nachname', '')
            session['user_vorname'] = user_data.get('vorname', '')

            return redirect(request.args.get('next') or url_for('index'))
        error = 'Ungültige Benutzer-ID oder Passwort.'
    return render_template_string(LOGIN_PAGE_HTML, error=error, request=request, url_for=url_for)


@app.route('/logout')
def logout_page():
    session.pop('user_id', None);
    session.pop('username_display', None)
    session.pop('is_admin', None);
    session.pop('can_change_entries', None)
    session.pop('username_actual', None)
    return redirect(url_for('login_page'))


@app.route('/api/project_details/<path:project_name_url>')
@login_required
def api_get_project_details(project_name_url):
    project_name = unquote(project_name_url)
    global LOGGED_IN_USER_DISPLAY_NAME_FOR_PYTHON
    LOGGED_IN_USER_DISPLAY_NAME_FOR_PYTHON = get_current_username_from_session()

    try:
        # 1. Lade die Konfiguration, um alle möglichen Projektdateien zu kennen
        config = load_app_config()
        all_possible_data_files = set()
        for tl in config.get('timelines', []):
            if 'data_file' in tl:
                all_possible_data_files.add(tl['data_file'])
            if 'data_files' in tl:
                for f in tl['data_files']:
                    all_possible_data_files.add(f)

        # 2. Lade alle Projekte aus allen konfigurierten Dateien
        all_known_projects = load_timeline_projects_for_flask(list(all_possible_data_files))

        # 3. Suche das spezifische Projekt in der frisch geladenen Gesamtliste (Live-CSV)
        project_to_detail = next((p for p in all_known_projects if p.get('name') == project_name), None)

        if not project_to_detail:
            # --- NEU: FALLBACK INS ARCHIV (JSON) ---
            print(f"INFO: Projekt '{project_name}' nicht in Live-CSV gefunden. Suche im Archiv...")
            if os.path.exists(PROJECT_STATUS_DIR):
                for filename in os.listdir(PROJECT_STATUS_DIR):
                    if filename.endswith('.json'):
                        filepath = os.path.join(PROJECT_STATUS_DIR, filename)
                        try:
                            with open(filepath, 'r', encoding='utf-8') as f:
                                archive_data = json.load(f)
                                if archive_data.get('name') == project_name:
                                    # Treffer im Archiv!
                                    # Wir fügen noch die 'has_document' Flags hinzu, wie beim Live-Projekt
                                    templates = load_process_templates()
                                    engine_type = archive_data.get('engine_type')
                                    template = find_best_template_for_engine(engine_type, templates)
                                    doc_map = {step['name']: step['document_template'] for step in
                                               template.get('steps', [])}

                                    for step in archive_data.get('steps', []):
                                        step['has_document'] = bool(doc_map.get(step['name']))

                                    # Spezielles Flag, damit das Frontend weiß: "Das ist alt!"
                                    archive_data['is_archived'] = True
                                    return jsonify(archive_data)
                        except Exception as e:
                            print(f"Fehler beim Lesen der Archivdatei {filename}: {e}")

            # Wenn auch im Archiv nichts ist:
            return jsonify({"error": f"Projekt '{project_name}' nicht gefunden."}), 404

        # 4. Generiere die Statusdetails (der Rest der Funktion bleibt gleich)
        # Hier wird die Event-Struktur mit 'start_datetime' etc. angereichert
        for i, evt_data in enumerate(project_to_detail.get('events', [])):
            if not isinstance(evt_data, dict): continue
            start_dt = get_datetime_from_date_shift(parse_date_from_string(evt_data.get('start')),
                                                    evt_data.get('start_shift'))
            end_dt = get_datetime_from_date_shift(parse_date_from_string(evt_data.get('end')),
                                                  evt_data.get('end_shift'))
            if start_dt and end_dt:
                evt_data['start_datetime'] = start_dt.isoformat()
                evt_data['end_datetime'] = end_dt.isoformat()

        project_details = get_or_create_project_status_details(project_to_detail)

        if not project_details:
            return jsonify({"error": f"Details für Projekt '{project_name}' konnten nicht generiert werden."}), 500

        # Füge 'has_document' Flag hinzu
        templates = load_process_templates()
        engine_type = project_to_detail.get('engine_type')
        template = find_best_template_for_engine(engine_type, templates)
        doc_map = {step['name']: step['document_template'] for step in template.get('steps', [])}

        for step in project_details.get('steps', []):
            step['has_document'] = bool(doc_map.get(step['name']))

        return jsonify(project_details)

    except Exception as e:
        print(f"Fehler in api_get_project_details für '{project_name}': {e}")
        traceback.print_exc()
        return jsonify({"error": "Interner Serverfehler beim Abrufen der Projektdetails."}), 500


@app.route('/api/project_status/<path:project_id>', methods=['POST'])
@login_required
def api_save_project_status(project_id):
    user_roles = session.get('roles', [])

    # Definiere alle Rollen, die speichern dürfen
    allowed_roles_to_save = {
        Roles.ADMIN,
        Roles.PLANNER,
        Roles.TESTMECHANIC,
        Roles.TESTCELLINSPECTOR,
        Roles.TESTENGINEER,
        Roles.MAINTENANCEMANAGER,
        Roles.HARDWAREMANAGER
    }

    # Prüfe, ob der User mindestens eine der erlaubten Rollen hat
    if not any(role in user_roles for role in allowed_roles_to_save):
        return jsonify({"error": "Keine Berechtigung zum Speichern des Projektstatus."}), 403

    data_to_save = request.json
    if not data_to_save or 'steps' not in data_to_save:
        return jsonify({"error": "Ungültige Daten empfangen."}), 400

    # Sanitize project_id, um Directory Traversal zu verhindern
    safe_project_id = re.sub(r'[^a-zA-Z0-9_-]', '', project_id)

    # Definiere den Zielordner
    status_dir = os.path.join('db', 'projectstatus')

    # Erstelle den Ordner, falls er nicht existiert
    os.makedirs(status_dir, exist_ok=True)

    # Baue den vollständigen Dateipfad zusammen
    status_file_path = os.path.join(status_dir, f'project_status_{safe_project_id}.json')

    if save_json_file(status_file_path, data_to_save):
        return jsonify({"message": "Status erfolgreich gespeichert."}), 200
    else:
        return jsonify({"error": "Interner Fehler beim atomaren Speichern der Datei."}), 500


@app.route('/timeline/view/<string:timeline_id>')
@login_required
def serve_timeline_page(timeline_id):
    config = load_app_config()
    timelines = config.get('timelines', [])
    timeline_config = next((tl for tl in config.get('timelines', []) if tl['id'] == timeline_id), None)
    if not timeline_config:
        abort(404)

    timeline_name_for_display = timeline_config.get('name', 'Timeline Ansicht')
    user_initials = "N/A"  # NEU
    vorname = session.get('user_vorname', '')  # NEU
    nachname = session.get('user_nachname', '')  # NEU
    if vorname and nachname: user_initials = (vorname[0] + nachname[0]).upper()  # NEU

    return render_template_string(
        TIMELINE_HTML_CONTENT,
        current_user_display_name=get_current_username_from_session(),
        url_for=url_for,
        session=session,
        timeline_id=timeline_id,
        timelines=timelines,
        current_user_initials=user_initials,
        active_page='timelines',
        timeline_name=timeline_name_for_display
    )


@app.route('/my_qualifications')
@login_required
def serve_my_qualifications_page():
    config = load_app_config()
    timelines = config.get('timelines', [])
    display_username_for_header = get_current_username_from_session()
    user_full_name_for_page_body = get_current_user_full_name_from_session()

    user_level_from_csv = "Unbekannt"
    current_qualifications_data = []
    ongoing_trainings_data = []
    error_message_personal_qualis = None

    user_id_for_data = session.get('user_id')

    if user_id_for_data:
        user_object = next((u for u in ALL_USERS if u['id'] == user_id_for_data), None)
        if user_object:
            user_level_from_csv = user_object.get('level', 'Standardbenutzer')

        user_specific_qual_entry = next((q for q in ALL_QUALIFICATIONS if q.get('id') == user_id_for_data), None)

        if user_specific_qual_entry:
            for merkmal_info in user_specific_qual_entry.get('merkmale', []):
                qual_name = merkmal_info.get('merkmal', 'Unbekannt')
                qual_wert = merkmal_info.get('wert', '').upper()

                start_date = merkmal_info.get('start_date', 'N/A')
                end_date = merkmal_info.get('end_date', 'N/A')
                cert_path = merkmal_info.get('cert_path', '')

                if qual_wert == 'X':
                    current_qualifications_data.append(
                        {"name": qual_name, "end_date": end_date, "cert_path": cert_path})
                elif qual_wert in ['TS', 'TP', 'TT']:
                    progress_percent = 0
                    if qual_wert == 'TS':
                        progress_percent = 50
                    elif qual_wert == 'TP':
                        progress_percent = 10
                    elif qual_wert == 'TT':
                        progress_percent = 75
                    ongoing_trainings_data.append(
                        {"name": qual_name, "start_date": start_date, "progress": progress_percent,
                         "status": qual_wert})
        else:
            error_message_personal_qualis = f"Kein Qualifikationseintrag für '{user_full_name_for_page_body}' (ID: {user_id_for_data}) gefunden."
    else:
        error_message_personal_qualis = "Benutzer nicht eingeloggt."

    current_qualifications_data.sort(key=lambda x: x['name'])
    ongoing_trainings_data.sort(key=lambda x: x['name'])

    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname: user_initials = (vorname[0] + nachname[0]).upper()

    return render_template_string(
        PERSONAL_QUALIFICATIONS_HTML_CONTENT,
        user_full_name=user_full_name_for_page_body,
        current_user_display_name=display_username_for_header,
        user_level=user_level_from_csv,
        current_qualifications=current_qualifications_data,
        ongoing_trainings=ongoing_trainings_data,
        error_message=error_message_personal_qualis,
        url_for=url_for,
        current_user_initials=user_initials,
        active_page='my_qualifications',
        session=session,
        user_id=user_id_for_data
    )


@app.route('/qualifications_table')
@login_required
def serve_qualifications_table_page():
    config = load_app_config()
    timelines = config.get('timelines', [])
    grouped_qualifications_data, merkmale_for_table = load_and_prepare_qualifications_data()
    user_name = get_current_username_from_session() or "Unbekannt"
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    return render_template_string(
        QUALIFICATIONS_TABLE_HTML_CONTENT,
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        timelines=timelines,
        active_page='qualifications_table',  # Eindeutiger Name für diese Seite
        grouped_qualifications=grouped_qualifications_data,
        merkmale_in_order=merkmale_for_table,
        url_for=url_for,
        session=session
    )


@app.route('/admin/user_management')
@admin_required
def user_management_page():
    user_name = get_current_username_from_session() or "Unbekannt"
    user_initials = "N/A"
    vorname = session.get('user_vorname', '')
    nachname = session.get('user_nachname', '')
    if vorname and nachname:
        user_initials = (vorname[0] + nachname[0]).upper()

    # Sortiere die Benutzerliste nach Nachname für eine bessere Übersicht
    sorted_users = sorted(ALL_USERS, key=lambda u: u.get('nachname', '').lower())

    return render_template_string(
        USER_MANAGEMENT_HTML_CONTENT,
        users=sorted_users,  # Übergib die sortierte Liste
        available_roles=AVAILABLE_ROLES, # NEU: Übergib die Liste aller möglichen Rollen
        current_user_display_name=user_name,
        current_user_initials=user_initials,
        active_page='user_management',
        url_for=url_for,
        session=session
    )


@app.route('/api/admin/add_user', methods=['POST'])
@admin_required
def api_add_user():
    data = request.json
    if not all(k in data for k in ['nachname', 'vorname', 'id', 'level', 'roles']):
        return jsonify({"error": "Fehlende Benutzerdaten."}), 400

    new_user_id = data['id'].strip().lower()
    if any(u['id'] == new_user_id for u in ALL_USERS):
        return jsonify({"error": f"Benutzer-ID '{new_user_id.upper()}' existiert bereits."}), 409
    units = data.get('units', [])

    new_user_obj = {
        'nachname': data['nachname'].strip().upper(),
        'vorname': data['vorname'].strip().capitalize(),
        'id': new_user_id,
        'normalized_name': f"{data['nachname'].strip().lower()}, {data['vorname'].strip().lower()}",
        'level': data['level'].strip(),
        'password': 'MBET',  # Standardpasswort
        'roles': data.get('roles', []),
        'units': data.get('units', [])
    }
    ALL_USERS.append(new_user_obj)

    try:
        users_to_save_dicts = []
        for u in ALL_USERS:
            # ADMIN Spalte für Abwärtskompatibilität vorerst beibehalten
            is_admin = Roles.ADMIN in u.get('roles', [])
            users_to_save_dicts.append({
                'NACHNAME': u['nachname'], 'VORNAME': u['vorname'], 'ID': u['id'],
                'LEVEL': u.get('level', ''),
                'PASSWORD': u['password'],
                'ADMIN': 'x' if is_admin else '',
                'ROLES': ",".join(u.get('roles', [])),
                'UNITS': ",".join(u.get('units', []))
            })
            print(f"DEBUG WRITING USER {u['id']}: Units='{u.get('units')}' -> CSV='{users_to_save_dicts[-1]['UNITS']}'")
        with open(USERS_FILE_PATH, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file,
                                    fieldnames=['NACHNAME', 'VORNAME', 'ID', 'LEVEL', 'PASSWORD', 'ADMIN', 'ROLES',
                                                'UNITS'])
            writer.writeheader()
            writer.writerows(users_to_save_dicts)
        return jsonify({"message": "Benutzer erfolgreich hinzugefügt."}), 201
    except Exception as e:
        ALL_USERS.pop()
        print(f"Fehler Speichern Benutzer-CSV (Hinzufügen): {e}")
        traceback.print_exc()
        return jsonify({"error": "Fehler beim Speichern."}), 500

@app.route('/api/admin/users/<user_id>/edit', methods=['PUT'])
@admin_required
def api_edit_user(user_id):
    data = request.json
    print(f"DEBUG EDIT RECEIVED: {data}")
    if not data or not all(k in data for k in ['nachname', 'vorname', 'level', 'roles']):
        return jsonify({"error": "Fehlende oder ungültige Benutzerdaten."}), 400

    user_to_edit = next((user for user in ALL_USERS if user['id'] == user_id.lower()), None)
    if not user_to_edit:
        return jsonify({"error": "Benutzer nicht gefunden."}), 404

    units = data.get('units', [])
    original_values = user_to_edit.copy()

    user_to_edit['nachname'] = data['nachname'].strip().upper()
    user_to_edit['vorname'] = data['vorname'].strip().capitalize()
    user_to_edit['level'] = data['level'].strip()
    user_to_edit['roles'] = data.get('roles', [])
    user_to_edit['units'] = data.get('units', [])
    user_to_edit['normalized_name'] = f"{data['nachname'].strip().lower()}, {data['vorname'].strip().lower()}"

    try:
        users_to_save_dicts = []
        for u in ALL_USERS:
            is_admin = Roles.ADMIN in u.get('roles', [])
            users_to_save_dicts.append({
                'NACHNAME': u['nachname'], 'VORNAME': u['vorname'], 'ID': u['id'],
                'LEVEL': u.get('level', ''),
                'PASSWORD': u['password'],
                'ADMIN': 'x' if is_admin else '',
                'ROLES': ",".join(u.get('roles', [])),
                'UNITS': ",".join(u.get('units', []))
            })
            print(f"DEBUG WRITING USER {u['id']}: Units='{u.get('units')}' -> CSV='{users_to_save_dicts[-1]['UNITS']}'")
        with open(USERS_FILE_PATH, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file,
                                    fieldnames=['NACHNAME', 'VORNAME', 'ID', 'LEVEL', 'PASSWORD', 'ADMIN', 'ROLES',
                                                'UNITS'])
            writer.writeheader()
            writer.writerows(users_to_save_dicts)
        return jsonify({"message": "Benutzer erfolgreich aktualisiert."}), 200
    except Exception as e:
        user_index = ALL_USERS.index(user_to_edit)
        ALL_USERS[user_index] = original_values
        print(f"Fehler beim Speichern der Benutzer-CSV nach Bearbeitung: {e}")
        traceback.print_exc()
        return jsonify({"error": "Fehler beim Speichern der Änderungen."}), 500


@app.route('/api/admin/users/<user_id>', methods=['DELETE'])
@admin_required
def api_delete_user(user_id):
    user_to_delete_idx = -1
    original_user_data = None
    for idx, user in enumerate(ALL_USERS):
        if user['id'] == user_id.lower():
            user_to_delete_idx = idx
            original_user_data = user.copy()
            break
    if user_to_delete_idx == -1:
        return jsonify({"error": "Benutzer nicht gefunden."}), 404

    del ALL_USERS[user_to_delete_idx]

    try:
        users_to_save_dicts = []
        for u in ALL_USERS:
            is_admin = Roles.ADMIN in u.get('roles', [])
            users_to_save_dicts.append({
                'NACHNAME': u['nachname'], 'VORNAME': u['vorname'], 'ID': u['id'],
                'LEVEL': u.get('level', ''),
                'PASSWORD': u['password'],
                'ADMIN': 'x' if is_admin else '',
                'ROLES': ",".join(u.get('roles', [])),
                'UNITS': ",".join(u.get('units', []))
            })
        with open(USERS_FILE_PATH, mode='w', newline='', encoding='utf-8-sig') as file:
            writer = csv.DictWriter(file,
                                    fieldnames=['NACHNAME', 'VORNAME', 'ID', 'LEVEL', 'PASSWORD', 'ADMIN', 'ROLES',
                                                'UNITS'])
            writer.writeheader()
            writer.writerows(users_to_save_dicts)
        return jsonify({"message": "Benutzer erfolgreich gelöscht."}), 200
    except Exception as e:
        if original_user_data:
            ALL_USERS.insert(user_to_delete_idx, original_user_data)
        print(f"Fehler beim Speichern nach dem Löschen: {e}")
        return jsonify({"error": "Fehler beim Speichern nach dem Löschen."}), 500

@app.route('/api/update_qualification_status', methods=['POST'])
@login_required
def api_update_qualification_status():
    data = request.json
    if not data or not all(k in data for k in ['id', 'merkmal', 'new_value', 'start_date', 'end_date', 'cert_path']):
        return jsonify({"error": "Fehlende Daten (id, merkmal, status, daten...)."}), 400

    user_id = data['id'].strip().lower()
    merkmal_name_to_update = data['merkmal'].strip()
    new_status_value = data['new_value'].strip()
    new_start_date = data['start_date'].strip()
    new_end_date = data['end_date'].strip()
    new_cert_path = data['cert_path'].strip()

    global ALL_QUALIFICATIONS, MERKMALE_IN_ORDER

    found_employee = False
    updated_in_memory = False

    for qual_entry in ALL_QUALIFICATIONS:
        if qual_entry.get('id', '').strip().lower() == user_id:
            found_employee = True
            merkmal_found_for_employee = False
            if 'merkmale' not in qual_entry or not isinstance(qual_entry['merkmale'], list):
                qual_entry['merkmale'] = []

            for merkmal_data in qual_entry['merkmale']:
                if merkmal_data.get('merkmal', '').strip() == merkmal_name_to_update:
                    merkmal_data['wert'] = new_status_value
                    merkmal_data['start_date'] = new_start_date
                    merkmal_data['end_date'] = new_end_date
                    merkmal_data['cert_path'] = new_cert_path
                    merkmal_found_for_employee = True
                    updated_in_memory = True
                    break

            if not merkmal_found_for_employee and merkmal_name_to_update in MERKMALE_IN_ORDER:
                qual_entry.get('merkmale', []).append({
                    'merkmal': merkmal_name_to_update,
                    'wert': new_status_value,
                    'start_date': new_start_date,
                    'end_date': new_end_date,
                    'cert_path': new_cert_path
                })
                updated_in_memory = True
            break

    if not found_employee:
        return jsonify({"error": f"Mitarbeiter mit ID '{user_id}' nicht gefunden."}), 404

    if not updated_in_memory:
        return jsonify(
            {"error": f"Merkmal '{merkmal_name_to_update}' konnte für den Mitarbeiter nicht aktualisiert werden."}), 400

    if save_qualifications_to_csv(QUALIFICATIONS_FILE_PATH, ALL_QUALIFICATIONS, MERKMALE_IN_ORDER, silent=True):
        return jsonify({"message": "Qualifikationsdetails erfolgreich aktualisiert."}), 200
    else:
        return jsonify({"error": "Fehler beim Speichern der Qualifikationsdatei."}), 500


@app.route('/api/get_merkmale', methods=['GET'])
@login_required
def api_get_merkmale():
    global MERKMALE_IN_ORDER
    if not MERKMALE_IN_ORDER and os.path.exists(QUALIFICATIONS_FILE_PATH):
        _, loaded_merkmale = load_qualifications_from_csv(QUALIFICATIONS_FILE_PATH)
        if loaded_merkmale: MERKMALE_IN_ORDER = loaded_merkmale

    return jsonify(MERKMALE_IN_ORDER)


@app.route('/api/update_merkmale', methods=['POST'])
@admin_required
def api_update_merkmale():
    data = request.json
    if not data or 'merkmale' not in data or not isinstance(data['merkmale'], list):
        return jsonify({"error": "Ungültige Daten für Merkmale."}), 400

    new_merkmale_order_from_client = [str(m).strip() for m in data['merkmale'] if str(m).strip()]

    global ALL_QUALIFICATIONS, MERKMALE_IN_ORDER

    # Stelle sicher, dass die Daten geladen sind
    if not ALL_QUALIFICATIONS or not MERKMALE_IN_ORDER:
        load_and_prepare_qualifications_data()

    # Speichere den alten Zustand für ein mögliches Rollback
    original_merkmale_in_order = list(MERKMALE_IN_ORDER)
    # Eine tiefe Kopie der Qualifikationsdaten für ein Rollback bei Fehler
    import copy
    original_qualifications = copy.deepcopy(ALL_QUALIFICATIONS)

    # Finde heraus, welche Merkmale hinzugefügt oder entfernt wurden
    set_original_merkmale = set(original_merkmale_in_order)
    set_new_merkmale = set(new_merkmale_order_from_client)

    added_merkmale = list(set_new_merkmale - set_original_merkmale)
    removed_merkmale = list(set_original_merkmale - set_new_merkmale)

    # Aktualisiere die In-Memory-Daten für jeden Mitarbeiter
    for qual_entry in ALL_QUALIFICATIONS:
        # 1. Entferne Daten für gelöschte Merkmale
        if removed_merkmale:
            qual_entry['merkmale'] = [m for m in qual_entry.get('merkmale', []) if
                                      m.get('merkmal') not in removed_merkmale]

        # 2. Füge leere Datensätze für neue Merkmale hinzu
        for added_m_name in added_merkmale:
            # Stelle sicher, dass das Merkmal nicht schon (durch einen Fehler) existiert
            if not any(m.get('merkmal') == added_m_name for m in qual_entry.get('merkmale', [])):
                qual_entry.get('merkmale', []).append({
                    'merkmal': added_m_name,
                    'wert': '',
                    'start_date': '',
                    'end_date': '',
                    'cert_path': ''
                })

    MERKMALE_IN_ORDER = new_merkmale_order_from_client

    # Versuche, die Änderungen in die CSV zu schreiben
    if save_qualifications_to_csv(QUALIFICATIONS_FILE_PATH, ALL_QUALIFICATIONS, MERKMALE_IN_ORDER, silent=False):
        return jsonify({"message": "Merkmale erfolgreich aktualisiert und CSV gespeichert."}), 200
    else:
        MERKMALE_IN_ORDER = original_merkmale_in_order
        ALL_QUALIFICATIONS = original_qualifications
        print(
            "FEHLER: Änderungen an Merkmalen konnten nicht gespeichert werden. Rollback der In-Memory-Daten wurde durchgeführt.")
        return jsonify({"error": "Fehler beim Speichern der aktualisierten Qualifikations-CSV."}), 500


@app.route('/api/admin/global_sap_update', methods=['POST'])
@admin_required
def api_global_sap_update():
    if 'sap_file' not in request.files:
        return jsonify({"error": "Keine Datei hochgeladen."}), 400

    file = request.files['sap_file']
    if not file.filename.lower().endswith('.csv'):
        return jsonify({"error": "Ungültiges Dateiformat. Bitte CSV verwenden."}), 400

    try:
        # 1. Datei EINMALIG in den Speicher laden und vorverarbeiten (Performance!)
        import pandas as pd

        file_stream = io.BytesIO(file.read())
        try:
            # Grundeinstellungen für SAP Export lesen
            df_master = pd.read_csv(file_stream, sep=';', encoding='utf-8', dayfirst=True, low_memory=False)
        except Exception as e:
            return jsonify({"error": f"Konnte CSV nicht lesen: {str(e)}"}), 400

        # Prüfen ob notwendige Spalten da sind
        required_cols = ["Project", "Engine Serial", "Customer", "Engine Type", "Milestone type", "TS"]
        if not all(col in df_master.columns for col in required_cols):
            return jsonify({"error": "CSV hat nicht das erwartete SAP-Format (Spalten fehlen)."}), 400

        # 2. Globale Vorfilterung (Datum & Meilenstein-Typ) - gilt für ALLE Boards
        df_master['TS_parsed'] = pd.to_datetime(df_master['TS'], format='%d.%m.%Y %H:%M:%S', errors='coerce')
        today = datetime.now()
        # Zeitraum: 6 Wochen zurück bis 6 Wochen in Zukunft (wie in der Original-Funktion)
        start_date, end_date = today - timedelta(weeks=4), today + timedelta(weeks=6)

        # Filtern auf Zeitraum und "Plan"-Events
        df_master = df_master.dropna(subset=['TS_parsed'])
        df_master = df_master.loc[(df_master['TS_parsed'] >= start_date) & (df_master['TS_parsed'] <= end_date)]
        df_master = df_master[df_master['Milestone type'].astype(str) == "Plan (Planung, ZMMP)"]

        if df_master.empty:
            return jsonify({"message": "Keine relevanten 'Plan'-Meilensteine im Zeitraum +/- 6 Wochen gefunden."}), 200

        # 3. Iteration durch alle Timelines
        config = load_app_config()
        timelines = config.get('timelines', [])
        results = []

        for tl in timelines:
            if tl.get('type') != 'editable' or not tl.get('sap_filter') or not tl.get('data_file'):
                continue

            target_file = tl['data_file']
            sap_filters = tl['sap_filter']  # z.B. {"Engine Type": ["CFM56"]}

            # 4. Spezifische Filterung für dieses Board
            df_timeline = df_master.copy()

            filter_match = True
            for col, patterns in sap_filters.items():
                if col in df_timeline.columns and patterns:
                    try:
                        combined_pattern = '|'.join(patterns)
                        df_timeline = df_timeline[
                            df_timeline[col].astype(str).str.contains(combined_pattern, regex=True, na=False,
                                                                      case=False)]
                    except:
                        filter_match = False
                        break

            if not filter_match or df_timeline.empty:
                results.append(f"Skipped: {tl['name']} (Keine Daten nach Filter)")
                continue

            # 5. Merge durchführen
            # Wir müssen die aktuelle Projekt-Reihenfolge laden, um sie nicht zu zerstören
            current_projects = load_timeline_projects_for_flask([target_file])
            current_order = [(p.get('project_code', ''), p.get('engine_serial', '')) for p in current_projects]

            changes = merge_into_timeline_projects(target_file, df_timeline, current_order)

            status = "Aktualisiert" if changes else "Keine Änderungen"
            results.append(f"<strong>{tl['name']}</strong>: {status}")

        return jsonify({
            "success": True,
            "message": "Globales Update abgeschlossen.",
            "details": "<br>".join(results)
        }), 200

    except Exception as e:
        traceback.print_exc()
        return jsonify({"error": f"Systemfehler beim globalen Update: {str(e)}"}), 500


if __name__ == '__main__':
    # 1. Benutzer laden (ist Voraussetzung für alles andere)
    if not os.path.exists(USERS_FILE_PATH):
        print(f"WARNUNG: Benutzerdatei '{USERS_FILE_PATH}' nicht gefunden. Erstelle Beispieldatei.")
        try:
            with open(USERS_FILE_PATH, 'w', newline='', encoding='utf-8-sig') as f_user:
                writer_user = csv.writer(f_user)
                writer_user.writerow(USER_CSV_FIELDNAMES)
                writer_user.writerow(['Admin', 'Local', 'admin', 'Teamleiter', 'MBET', 'x', 'x', 'x'])
                writer_user.writerow(['User', 'Normal', 'user', 'Mechaniker', 'MBET', '', 'x', ''])
            print(f"INFO: Beispieldatei '{USERS_FILE_PATH}' wurde erstellt.")
        except Exception as e_create_user:
            print(f"FEHLER beim Erstellen der Benutzer-Beispieldatei: {e_create_user}")
    load_all_users_on_startup()

    if not os.path.exists(HARDWARE_UPLOADS_DIR):
        os.makedirs(HARDWARE_UPLOADS_DIR)

    # 2. Testzellen und deren manuelle Events laden (NEUE LOGIK)
    # 2a. Stammdaten der Testzellen prüfen
    init_facility_data()
    init_maintenance_structure()
    if not os.path.exists(LOCATIONS_FILE_PATH):
        print(f"WARNUNG: Testzellen-Stammdatendatei '{LOCATIONS_FILE_PATH}' nicht gefunden. Erstelle Beispieldatei.")
        try:
            with open(LOCATIONS_FILE_PATH, 'w', newline='', encoding='utf-8-sig') as f_loc:
                writer = csv.writer(f_loc)
                writer.writerow(['ID', 'TESTCELL', 'TYPES'])
                writer.writerow(['1', 'Prüfstand 4.1', 'CFM56,LEAP-1A'])
                writer.writerow(['2', 'Prüfstand 4.2', 'PW1500,PW1900'])
            print(f"INFO: Beispieldatei '{LOCATIONS_FILE_PATH}' wurde erstellt.")
        except Exception as e:
            print(f"FEHLER beim Erstellen der Testzellen-Beispieldatei: {e}")

    # 2b. Manuelle Events der Testzellen prüfen
    if not os.path.exists(LOCATIONS_EVENTS_CSV_PATH):
        print(f"WARNUNG: Testzellen-Eventdatei '{LOCATIONS_EVENTS_CSV_PATH}' nicht gefunden. Erstelle leere Datei.")
        try:
            with open(LOCATIONS_EVENTS_CSV_PATH, 'w', newline='', encoding='utf-8-sig') as f_evt:
                writer = csv.writer(f_evt)
                writer.writerow(
                    ['event_id', 'cell_id', 'title', 'type', 'start_date', 'start_shift', 'end_date', 'end_shift',
                     'description'])
            print(f"INFO: Leere Eventdatei '{LOCATIONS_EVENTS_CSV_PATH}' wurde erstellt.")
        except Exception as e:
            print(f"FEHLER beim Erstellen der Testzellen-Eventdatei: {e}")

    # 2c. Daten laden (ersetzt den alten load_locations() Aufruf)
    load_locations_and_events()

    # 3. Mitarbeiter-Anwesenheiten laden (wird für Sync benötigt)
    if not os.path.exists(EMPLOYEES_FILE_PATH):
        print(f"WARNUNG: Mitarbeiterdatei '{EMPLOYEES_FILE_PATH}' nicht gefunden. Erstelle Beispieldatei.")
        try:
            with open(EMPLOYEES_FILE_PATH, 'w', newline='', encoding='utf-8-sig') as f_emp:
                writer_emp = csv.writer(f_emp)
                emp_header = [" ", "Name", "ID"]
                kw_today = date.today().isocalendar()[1]
                year_short_today = str(date.today().year)[-2:]
                emp_header.append(f"KW{kw_today}_{year_short_today}")
                for i in range(7):
                    day_header_emp = date.today() - timedelta(days=date.today().weekday()) + timedelta(days=i)
                    emp_header.append(
                        f"{['Mo', 'Di', 'Mi', 'Do', 'Fr', 'Sa', 'So'][day_header_emp.weekday()]}\\n{day_header_emp.strftime('%d.%m.')}")
                writer_emp.writerow(emp_header)
                writer_emp.writerow(['FF', 'Mustermann, Max', 'mmuster'] + ['F'] + [''] * 7)
            print(f"INFO: Beispieldatei '{EMPLOYEES_FILE_PATH}' wurde erstellt.")
        except Exception as e_create_e:
            print(f"FEHLER beim Erstellen der Mitarbeiter-Beispieldatei: {e_create_e}")

    # 4. Qualifikationen laden und mit Mitarbeitern synchronisieren
    if not os.path.exists(QUALIFICATIONS_FILE_PATH):
        print(
            f"FEHLER: Die Qualifikationsdatei '{QUALIFICATIONS_FILE_PATH}' wurde nicht gefunden. Die Anwendung kann ohne diese Datei nicht korrekt funktionieren.")
    load_and_prepare_qualifications_data()

    # 5. Projekte laden
    if not os.path.exists(CURRENT_PROJECT_FILE_PATH):
        print(f"WARNUNG: Projektdatei '{CURRENT_PROJECT_FILE_PATH}' nicht gefunden. Erstelle leere Datei.")
        try:
            with open(CURRENT_PROJECT_FILE_PATH, 'w', newline='', encoding='utf-8-sig') as f_proj:
                writer = csv.writer(f_proj)
                # Header für eine leere Projektdatei
                writer.writerow(
                    ['name', 'engine_type', 'project_code', 'engine_serial', 'customer', 'BE', 'TS', 'TE', 'SA',
                     'active'])
            print(f"INFO: Leere Projektdatei '{CURRENT_PROJECT_FILE_PATH}' mit Header wurde erstellt.")
        except Exception as e_create_proj:
            print(f"FEHLER beim Erstellen der Projekt-Datei: {e_create_proj}")

    # 6. Kommentare laden (erstellt Datei, falls nicht vorhanden)
    load_all_comments()

    # 7. App starten
    try:
        app.run(debug=True, host='0.0.0.0', port=5001, use_reloader=False)
    except Exception as e:
        print(f"Kritischer Fehler beim App-Start: {e}")
        traceback.print_exc()