Please help me!

Hello, I have a problem with my js-code. Thats the error: Uncaught SyntaxError: expected expression, got ')'main.js:1032:2 And thats the code: // static/js/main.js // Stand: Korrigiert, aufgeräumt, korrektes Feedback im Schichtplan document.addEventListener('DOMContentLoaded', function() { console.log("########## DOMContentLoaded START ##########"); // === Globales Objekt für Daten aus Templates === // Wird einmalig beim Laden der Seite gefüllt window.schichtplanerData = {}; try { const empDataElem = document.getElementById('employeeDataJson'); const shiftDictElem = document.getElementById('shiftTypesDataJson'); // Dict mit Objekten const allShiftsElem = document.getElementById('allShiftTypesJson'); // Dict {id: obj} für Modal const allGroupsElem = document.getElementById('allMachineGroupsJson'); // Dict {id: obj} für Modal const qualiLevelsElem = document.getElementById('qualificationLevelsJson'); // Parse JSON data, provide empty objects as fallbacks window.schichtplanerData.employeeDict = empDataElem ? JSON.parse(empDataElem.textContent || '{}') : {}; window.schichtplanerData.shiftTypesDict = shiftDictElem ? JSON.parse(shiftDictElem.textContent || '{}') : {}; window.schichtplanerData.allShiftTypes = allShiftsElem ? JSON.parse(allShiftsElem.textContent || '{}') : {}; window.schichtplanerData.machineGroupsDict = allGroupsElem ? JSON.parse(allGroupsElem.textContent || '{}') : {}; window.schichtplanerData.qualificationLevelsData = qualiLevelsElem ? JSON.parse(qualiLevelsElem.textContent || '{}') : {}; console.log("Globale Daten geladen:", window.schichtplanerData); } catch (e) { console.error("Fehler beim Parsen der globalen JSON-Daten!", e); // Setze leere Fallbacks window.schichtplanerData = { employeeDict:{}, shiftTypesDict:{}, allShiftTypes:{}, machineGroupsDict:{}, qualificationLevelsData:{} }; } // === ENDE Globale Daten === // PASTE START 1: Ersetzt den ursprünglichen Modal-Steuerungsblock // === Modul: Modal Steuerung (ANGEPASST für generisches Öffnen) === const commonModalElement = document.getElementById("commonModal"); const commonModalTitle = commonModalElement ? commonModalElement.querySelector("#modalTitle") : null; const commonModalBody = commonModalElement ? commonModalElement.querySelector("#modalBody") : null; // const commonCloseModalBtn = commonModalElement ? commonModalElement.querySelector(".close-modal-btn") : null; // Nicht mehr global gebraucht // Das spezielle Einstellungs-Modal für Schichtarten holen const settingsModalElement = document.getElementById("shiftTypeSettingsModal"); const settingsModalTitle = settingsModalElement ? settingsModalElement.querySelector("#shiftTypeSettingsModalTitle") : null; const settingsModalBody = settingsModalElement ? settingsModalElement.querySelector("#shiftTypeSettingsModalBody") : null; // const settingsCloseModalBtn = settingsModalElement ? settingsModalElement.querySelector(".close-modal-btn") : null; // Nicht mehr global gebraucht // GENERISCHE Funktion zum Öffnen eines Modals // targetModalId: ID des Modals, das geöffnet werden soll ('commonModal' oder 'shiftTypeSettingsModal') // title: Titel für das Modal // contentSourceId: ID des Containers im HTML, dessen Inhalt kopiert werden soll // contentType: 'form' (nur erstes Formular kopieren) oder 'full' (gesamten Inhalt kopieren) function openModalGeneric(targetModalId, title, contentSourceId, contentType = 'form') { const modalToOpen = document.getElementById(targetModalId); // IDs für Titel/Body aus dem spezifischen Modal holen const modalTitleElement = modalToOpen ? modalToOpen.querySelector('h2') : null; // Nimmt die erste H2 im Modal const modalBodyElement = modalToOpen ? modalToOpen.querySelector('.modal-content > div:last-of-type') : null; // Nimmt das letzte Div im .modal-content const contentSourceContainer = document.getElementById(contentSourceId); if (modalToOpen && modalTitleElement && modalBodyElement && contentSourceContainer) { modalTitleElement.textContent = title || 'Dialog'; // Titel setzen let contentToInsert = null; if (contentType === 'full') { // Kopiere den gesamten *Inhalt* des Quellcontainers contentToInsert = contentSourceContainer.innerHTML; console.log(`Modal Content Type 'full' for ${contentSourceId}`); } else { // Default: 'form' // Kopiere nur das *erste Formular* aus dem Quellcontainer const formTemplate = contentSourceContainer.querySelector('form'); if (formTemplate) { contentToInsert = formTemplate.cloneNode(true); // Klonen, damit Original bleibt console.log(`Modal Content Type 'form' for ${contentSourceId}`); } else { console.error(`Kein Element im Container #${contentSourceId} gefunden für contentType 'form'.`); modalBodyElement.innerHTML = 'Fehler: Formular konnte nich

Apr 18, 2025 - 20:36
 0
Please help me!

Hello, I have a problem with my js-code.
Thats the error: Uncaught SyntaxError: expected expression, got ')'main.js:1032:2

And thats the code:
// static/js/main.js
// Stand: Korrigiert, aufgeräumt, korrektes Feedback im Schichtplan

document.addEventListener('DOMContentLoaded', function() {
console.log("########## DOMContentLoaded START ##########");

// === Globales Objekt für Daten aus Templates ===
// Wird einmalig beim Laden der Seite gefüllt
window.schichtplanerData = {};
try {
    const empDataElem = document.getElementById('employeeDataJson');
    const shiftDictElem = document.getElementById('shiftTypesDataJson'); // Dict mit Objekten
    const allShiftsElem = document.getElementById('allShiftTypesJson');   // Dict {id: obj} für Modal
    const allGroupsElem = document.getElementById('allMachineGroupsJson'); // Dict {id: obj} für Modal
    const qualiLevelsElem = document.getElementById('qualificationLevelsJson');

    // Parse JSON data, provide empty objects as fallbacks
    window.schichtplanerData.employeeDict = empDataElem ? JSON.parse(empDataElem.textContent || '{}') : {};
    window.schichtplanerData.shiftTypesDict = shiftDictElem ? JSON.parse(shiftDictElem.textContent || '{}') : {};
    window.schichtplanerData.allShiftTypes = allShiftsElem ? JSON.parse(allShiftsElem.textContent || '{}') : {};
    window.schichtplanerData.machineGroupsDict = allGroupsElem ? JSON.parse(allGroupsElem.textContent || '{}') : {};
    window.schichtplanerData.qualificationLevelsData = qualiLevelsElem ? JSON.parse(qualiLevelsElem.textContent || '{}') : {};

    console.log("Globale Daten geladen:", window.schichtplanerData);
} catch (e) {
    console.error("Fehler beim Parsen der globalen JSON-Daten!", e);
    // Setze leere Fallbacks
    window.schichtplanerData = { employeeDict:{}, shiftTypesDict:{}, allShiftTypes:{}, machineGroupsDict:{}, qualificationLevelsData:{} };
}
// === ENDE Globale Daten ===

// PASTE START 1: Ersetzt den ursprünglichen Modal-Steuerungsblock

// === Modul: Modal Steuerung (ANGEPASST für generisches Öffnen) ===
const commonModalElement = document.getElementById("commonModal");
const commonModalTitle = commonModalElement ? commonModalElement.querySelector("#modalTitle") : null;
const commonModalBody = commonModalElement ? commonModalElement.querySelector("#modalBody") : null;
// const commonCloseModalBtn = commonModalElement ? commonModalElement.querySelector(".close-modal-btn") : null; // Nicht mehr global gebraucht

// Das spezielle Einstellungs-Modal für Schichtarten holen
const settingsModalElement = document.getElementById("shiftTypeSettingsModal");
const settingsModalTitle = settingsModalElement ? settingsModalElement.querySelector("#shiftTypeSettingsModalTitle") : null;
const settingsModalBody = settingsModalElement ? settingsModalElement.querySelector("#shiftTypeSettingsModalBody") : null;
// const settingsCloseModalBtn = settingsModalElement ? settingsModalElement.querySelector(".close-modal-btn") : null; // Nicht mehr global gebraucht


// GENERISCHE Funktion zum Öffnen eines Modals
// targetModalId: ID des Modals, das geöffnet werden soll ('commonModal' oder 'shiftTypeSettingsModal')
// title: Titel für das Modal
// contentSourceId: ID des Containers im HTML, dessen Inhalt kopiert werden soll
// contentType: 'form' (nur erstes Formular kopieren) oder 'full' (gesamten Inhalt kopieren)
function openModalGeneric(targetModalId, title, contentSourceId, contentType = 'form') {
    const modalToOpen = document.getElementById(targetModalId);
    // IDs für Titel/Body aus dem spezifischen Modal holen
    const modalTitleElement = modalToOpen ? modalToOpen.querySelector('h2') : null; // Nimmt die erste H2 im Modal
    const modalBodyElement = modalToOpen ? modalToOpen.querySelector('.modal-content > div:last-of-type') : null; // Nimmt das letzte Div im .modal-content
    const contentSourceContainer = document.getElementById(contentSourceId);

    if (modalToOpen && modalTitleElement && modalBodyElement && contentSourceContainer) {
        modalTitleElement.textContent = title || 'Dialog'; // Titel setzen

        let contentToInsert = null;
        if (contentType === 'full') {
            // Kopiere den gesamten *Inhalt* des Quellcontainers
             contentToInsert = contentSourceContainer.innerHTML;
             console.log(`Modal Content Type 'full' for ${contentSourceId}`);

        } else { // Default: 'form'
            // Kopiere nur das *erste Formular* aus dem Quellcontainer
            const formTemplate = contentSourceContainer.querySelector('form');
            if (formTemplate) {
                contentToInsert = formTemplate.cloneNode(true); // Klonen, damit Original bleibt
                console.log(`Modal Content Type 'form' for ${contentSourceId}`);
            } else {
                console.error(`Kein 
Element im Container #${contentSourceId} gefunden für contentType 'form'.`); modalBodyElement.innerHTML = '

Fehler: Formular konnte nicht geladen werden.'; modalToOpen.style.display = "block"; // Zeige Modal trotzdem an, aber mit Fehlermeldung return; } } // Inhalt einfügen if (contentType === 'full') { modalBodyElement.innerHTML = contentToInsert; // Gesamten HTML-String einfügen } else if (contentToInsert) { modalBodyElement.innerHTML = ''; // Alten Inhalt leeren modalBodyElement.appendChild(contentToInsert); // Geklontes Formular einfügen } // Event-Listener für interne Schließen-Buttons im neu eingefügten Inhalt hinzufügen modalBodyElement.querySelectorAll('.close-modal-btn-internal').forEach(btn => { btn.addEventListener('click', () => { modalToOpen.style.display = "none"; }); }); modalToOpen.style.display = "block"; // Modal anzeigen console.log(`Modal #${targetModalId} geöffnet für: ${title} mit Inhalt von #${contentSourceId}`); } else { console.error("Modal-Öffnen fehlgeschlagen. Fehlende Elemente:", { modalToOpen: !!modalToOpen, modalTitleElement: !!modalTitleElement, modalBodyElement: !!modalBodyElement, contentSourceContainer: !!contentSourceContainer, targetModalId: targetModalId, contentSourceId: contentSourceId }); } } // Ende openModalGeneric // Globale Event Listener für Modal-Schließen (für beide Modals über das X oder Klick daneben) [commonModalElement, settingsModalElement].forEach(modalElem => { if (modalElem) { const closeBtn = modalElem.querySelector(".close-modal-btn"); // Das X oben rechts if (closeBtn) { closeBtn.onclick = function() { modalElem.style.display = "none"; } } // Schließen bei Klick außerhalb des Inhalts modalElem.addEventListener('click', function(event) { if (event.target === modalElem) { // Nur wenn direkt auf den Hintergrund geklickt wird modalElem.style.display = "none"; } }); } }); // Den alten globalen window.onclick entfernen, falls vorhanden, da er jetzt pro Modal gehandhabt wird. // --- Event Listener für Buttons, die Modals öffnen --- // Wir nutzen Event Delegation auf dem document, um alle Buttons zu fangen document.addEventListener('click', function(event) { const button = event.target.closest('button[data-modal-target]'); // Finde Button mit Attribut if (button) { event.preventDefault(); // Verhindere Standardverhalten, falls es ein Submit-Button in einem Formular ist const contentSourceId = button.dataset.modalTarget; // Standardmäßig das 'commonModal' verwenden, außer ein anderes ist spezifiziert const targetModalId = button.dataset.modalId || 'commonModal'; const title = button.dataset.modalTitle || 'Dialog'; // Prüfen, ob es der spezielle Settings-Button ist, der den gesamten Inhalt braucht const contentType = (targetModalId === 'shiftTypeSettingsModal' && contentSourceId === 'shiftTypeSettingsContainer') ? 'full' : 'form'; console.log(`Button Click -> Opening Modal: targetModalId=${targetModalId}, title=${title}, contentSourceId=${contentSourceId}, contentType=${contentType}`); openModalGeneric(targetModalId, title, contentSourceId, contentType); } }); // === ENDE Modul: Modal Steuerung === // PASTE END 1 // --- Modul: Schichtplan Interaktion (Kompakte Zelle + Modal) --- const scheduleGroupTableBody = document.getElementById('schedule-group-body'); if (scheduleGroupTableBody) { console.log("Schichtplaner JS: Initialisiere Schichtplan Logik (Gruppenansicht - Modal Edit)."); // Listener für Klicks auf die Zuweisungszellen -> öffnet Modal scheduleGroupTableBody.addEventListener('click', function(event) { const cell = event.target.closest('.schedule-assignment-cell'); if (cell) { event.preventDefault(); const date = cell.dataset.date; const shiftId = cell.dataset.shiftId; const groupId = cell.dataset.groupId; const currentEmployeeIdsStr = cell.dataset.currentEmployees || ''; const currentEmployeeIds = currentEmployeeIdsStr ? currentEmployeeIdsStr.split(',').map(id => id.trim()) : []; console.log(`Klick auf Zelle: Datum=${date}, Schicht=${shiftId}, Gruppe=${groupId}, Zugewiesen=[${currentEmployeeIds.join(',')}]`); openAssignmentEditModal(date, shiftId, groupId, currentEmployeeIds); // Spezielle Modal-Funktion } }); // Funktion zum Öffnen und Befüllen des Zuweisungs-Modals function openAssignmentEditModal(date, shiftId, groupId, currentEmployeeIds) { console.log("Öffne Zuweisungs-Edit-Modal für:", {date, shiftId, groupId, currentEmployeeIds}); if (!modal || !modalTitle || !modalBody) { console.error("Modal Elemente nicht initialisiert!"); return; } // Titel setzen const shiftInfo = window.schichtplanerData.shiftTypesDict[shiftId] || { abbreviation: '?' }; const groupInfo = window.schichtplanerData.machineGroupsDict[groupId] || { name: 'Keine', abbreviation: '--' }; modalTitle.textContent = `Zuweisung ändern (${date} / ${shiftInfo.abbreviation} / ${groupInfo.name})`; // Modal-Inhalt aus Vorlage holen und befüllen const formTemplateContainer = document.getElementById('assignmentEditModalTemplate'); if (!formTemplateContainer) { console.error("Modal-Vorlage #assignmentEditModalTemplate nicht gefunden!"); return; } const formTemplate = formTemplateContainer.querySelector('form'); if (!formTemplate) { console.error("Kein in #assignmentEditModalTemplate gefunden!"); return; } const formClone = formTemplate.cloneNode(true); // Formular klonen // Mitarbeiter-Select füllen const employeeSelect = formClone.querySelector('#modal_employee_select'); if (employeeSelect && window.schichtplanerData.employeeDict) { employeeSelect.innerHTML = ''; // Alte Optionen leeren for (const empId in window.schichtplanerData.employeeDict) { const employee = window.schichtplanerData.employeeDict[empId]; const isSelected = currentEmployeeIds.includes(String(empId)); // Als String vergleichen // TODO: Quali-Check und Option markieren const option = document.createElement('option'); option.value = empId; option.textContent = employee.name; if (isSelected) { option.selected = true; } employeeSelect.appendChild(option); } } else if (!employeeSelect) { console.error("#modal_employee_select nicht im Template gefunden"); } else { console.error("Mitarbeiterdaten (employeeDict) für Modal nicht verfügbar"); } // Modal-Body leeren und befülltes Formular einfügen modalBody.innerHTML = ''; modalBody.appendChild(formClone); modal.style.display = 'block'; // Modal anzeigen // Event Listener für das Formular im Modal hinzufügen const visibleForm = modalBody.querySelector('#assignmentModalForm'); const deleteBtn = modalBody.querySelector('#deleteAssignmentBtn'); if (visibleForm) { // Daten im Formular speichern für Submit/Delete Handler visibleForm.dataset.date = date; visibleForm.dataset.shiftId = shiftId; visibleForm.dataset.groupId = groupId; // Listener neu anhängen visibleForm.replaceWith(visibleForm.cloneNode(true)); // Alten Listener entfernen modalBody.querySelector('#assignmentModalForm').addEventListener('submit', handleAssignmentModalSubmit); // Listener für Löschen-Button auch neu anhängen const newDeleteBtn = modalBody.querySelector('#deleteAssignmentBtn'); if(newDeleteBtn) { newDeleteBtn.replaceWith(newDeleteBtn.cloneNode(true)); modalBody.querySelector('#deleteAssignmentBtn').addEventListener('click', handleDeleteAssignment); } } // Interne Schließen-Buttons modalBody.querySelectorAll('.close-modal-btn-internal').forEach(btn => { btn.addEventListener('click', () => { modal.style.display = "none"; }); }); } // Ende openAssignmentEditModal // Funktion zum Verarbeiten des Modal-Submits (sendet PUT mit Liste) function handleAssignmentModalSubmit(event) { event.preventDefault(); const form = event.target; const date = form.dataset.date; const shiftId = form.dataset.shiftId; const groupId = form.dataset.groupId; const selectedEmployees = Array.from(form.querySelector('select[name="selected_employees"]').selectedOptions).map(option => option.value); console.log("Sende Modal Daten (PUT):", {date, shiftId, groupId, employee_ids: selectedEmployees}); const apiUrl = '/api/assignment'; const dataToSend = { date, shift_id: shiftId, group_id: groupId, employee_ids: selectedEmployees }; const submitButton = form.querySelector('button[type="submit"]'); if(submitButton) submitButton.disabled = true; fetch(apiUrl, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(dataToSend) }) .then(response => response.json().then(data => ({ ok: response.ok, data: data }))) .then(result => { if (result.ok && result.data && result.data.status === 'success') { console.log("Update erfolgreich:", result.data.message); if(modal) modal.style.display = 'none'; updateScheduleCell(date, shiftId, groupId, result.data.assigned_employee_ids || []); } else { throw new Error(result.data.message || 'Fehler beim Speichern'); } }) .catch(error => { console.error("Fehler Modal Submit:", error); alert(`Fehler: ${error.message}`); }) .finally(() => { if(submitButton) submitButton.disabled = false; }); } // Ende handleAssignmentModalSubmit // Funktion zum Löschen der Zuweisung(en) für diese Zelle (sendet DELETE) function handleDeleteAssignment(event) { const form = event.target.closest('form'); const date = form.dataset.date; const shiftId = form.dataset.shiftId; const groupId = form.dataset.groupId; if (confirm(`Alle Zuweisungen für Schicht ${shiftId} / Gruppe ${groupId || 'Keine'} am ${date} löschen?`)) { console.log("Lösche Zuweisungen für:", {date, shiftId, groupId}); const apiUrl = '/api/assignment'; const dataToSend = { date: date, shift_id: shiftId, group_id: groupId }; fetch(apiUrl, { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(dataToSend) }) .then(response => response.json().then(data => ({ ok: response.ok, data: data }))) .then(result => { if (result.ok && result.data && result.data.status === 'success') { console.log("Löschen erfolgreich:", result.data.message); if(modal) modal.style.display = 'none'; updateScheduleCell(date, shiftId, groupId, []); } else { throw new Error(result.data.message || 'Fehler beim Löschen'); } }) .catch(error => { console.error("Fehler beim Löschen:", error); alert(`Fehler: ${error.message}`); }); } } // Ende handleDeleteAssignment // Funktion zum Aktualisieren der Zelle im Schichtplan function updateScheduleCell(date, shiftId, groupId, assignedEmployeeIds) { const cellSelector = `td[data-date="${date}"][data-shift-id="${shiftId}"][data-group-id="${groupId}"]`; const cell = scheduleGroupTableBody.querySelector(cellSelector); if (cell) { console.log("Aktualisiere Zelle (Kürzel-Ansicht):", cellSelector, "Neue IDs:", assignedEmployeeIds); cell.innerHTML = ''; // Leere Zelle zuerst const shiftInfo = window.schichtplanerData.shiftTypesDict[shiftId] || {}; const groupInfo = window.schichtplanerData.machineGroupsDict[groupId] || {}; // Nutze machineGroupsDict für Abk. const bgColor = shiftInfo?.color || '#ffffff'; const shiftTextColor = shiftInfo?.effective_text_color || '#000000'; const isAbsence = shiftInfo?.is_absence || false; cell.style.backgroundColor = bgColor; const contentWrapper = document.createElement('div'); contentWrapper.className = 'schedule-cell-content-simple'; // Verwende die Klasse für das einfache Layout // Schicht-Kürzel if (assignedEmployeeIds.length > 0 || isAbsence) { // Zeige Kürzel wenn MA da sind ODER es Abwesenheit ist const shiftDiv = document.createElement('div'); shiftDiv.className = 'schedule-cell-shift-abbr'; shiftDiv.style.color = shiftTextColor; shiftDiv.textContent = shiftInfo.abbreviation || '-'; contentWrapper.appendChild(shiftDiv); } // Gruppen-Kürzel if (!isAbsence && groupId && groupInfo.abbreviation && assignedEmployeeIds.length > 0) { // Zeige Gruppe nur wenn MA da und keine Abwesenheit const groupDiv = document.createElement('div'); groupDiv.className = 'schedule-cell-group-abbr-simple'; groupDiv.style.color = shiftTextColor; groupDiv.textContent = groupInfo.abbreviation; contentWrapper.appendChild(groupDiv); } // Plus-Zeichen if (assignedEmployeeIds.length === 0 && !isAbsence) { const span = document.createElement('span'); span.className = 'text-muted no-assignment-plus'; span.textContent = '+'; contentWrapper.appendChild(span); } cell.appendChild(contentWrapper); // Wichtig: Dataset aktualisieren, damit der nächste Klick die korrekten IDs hat! cell.dataset.currentEmployees = assignedEmployeeIds.join(','); } else { console.error("Konnte Zelle zum Aktualisieren nicht finden:", cellSelector); } } // Ende updateScheduleCell (Kürzel-Version) // --- Modul 2: Qualifikationsmatrix Interaktion --- const qualiMatrixBody = document.getElementById('quali-matrix-body'); if (qualiMatrixBody) { console.log("Schichtplaner JS: Initialisiere Quali-Matrix Logik."); let qualificationLevelsData = {}; try { const qualiLevelsElement = document.getElementById('qualificationLevelsJson'); if (qualiLevelsElement) { qualificationLevelsData = JSON.parse(qualiLevelsElement.textContent); console.log("Quali Levels Data loaded:", qualificationLevelsData); } else { throw new Error("JSON script element for levels not found."); } } catch(e) { console.error("Konnte qualification_levels nicht laden! Verwende Fallback.", e); qualificationLevelsData = { 0: { abbr: '-', color: '#f8f9fa', effective_text_color: '#6c757d', desc: 'Error' }}; } // Klick auf Zelle -> Edit-Modus qualiMatrixBody.addEventListener('click', function(event) { const cell = event.target.closest('td.qual-cell'); if (cell && event.target.tagName !== 'SELECT') { const isEditing = cell.classList.contains('editing'); qualiMatrixBody.querySelectorAll('td.qual-cell.editing').forEach(otherCell => { if (otherCell !== cell) { otherCell.classList.remove('editing'); otherCell.querySelector('.qual-level-display').style.display = 'block'; otherCell.querySelector('.qual-level-select').style.display = 'none'; } }); if (!isEditing) { cell.classList.add('editing'); cell.querySelector('.qual-level-display').style.display = 'none'; const select = cell.querySelector('.qual-level-select'); select.style.display = 'block'; select.value = cell.dataset.currentLevel; select.focus(); } } }); // Änderung im Select -> Speichern via API qualiMatrixBody.addEventListener('change', function(event) { const select = event.target; if (select.classList.contains('qual-level-select')) { const cell = select.closest('td.qual-cell'); const employeeId = cell.dataset.employeeId; const qualificationId = cell.dataset.qualificationId; const newLevel = select.value; const displayDiv = cell.querySelector('.qual-level-display'); console.log(`Speichere Quali: emp=${employeeId}, qual=${qualificationId}, level=${newLevel}`); cell.style.opacity = '0.7'; fetch('/api/qualification_level', { method: 'POST', headers: {'Content-Type': 'application/json', 'Accept': 'application/json'}, body: JSON.stringify({employee_id: employeeId, qualification_id: qualificationId, level: newLevel}) }) .then(response => response.json().then(data => ({ ok: response.ok, status: response.status, data: data }))) .then(result => { console.log("Quali API Antwort:", result); cell.style.opacity = '1'; if (result.ok && result.data && result.data.status === 'success') { const info = qualificationLevelsData[newLevel] || qualificationLevelsData[0]; displayDiv.textContent = info.abbr; if(info.effective_text_color) displayDiv.style.color = info.effective_text_color; cell.style.backgroundColor = info.color; cell.title = `${cell.title.split(' - ')[0]} - ${info.desc}`; cell.dataset.currentLevel = newLevel; select.style.display = 'none'; displayDiv.style.display = 'block'; cell.classList.remove('editing'); cell.style.outline = '2px solid green'; setTimeout(() => cell.style.outline = 'none', 500); } else { throw new Error(result.data.message || `API Fehler (Status ${result.status})`); } }) .catch(error => { console.error('Fehler beim Speichern des Quali-Levels:', error); select.style.display = 'none'; displayDiv.style.display = 'block'; cell.classList.remove('editing'); select.value = cell.dataset.currentLevel; cell.style.outline = '2px solid red'; setTimeout(() => cell.style.outline = 'none', 1500); }); } }); // Klick außerhalb schließt das aktive Select document.addEventListener('click', function(event) { if (!event.target.closest('td.qual-cell.editing')) { qualiMatrixBody.querySelectorAll('td.qual-cell.editing').forEach(cell => { cell.classList.remove('editing'); cell.querySelector('.qual-level-display').style.display = 'block'; cell.querySelector('.qual-level-select').style.display = 'none'; }); } }, true); } else { console.warn("Schichtplaner JS: Quali-Matrix tbody nicht gefunden."); } // --- ENDE Modul 2: Quali-Matrix --- // --- Modul 4: Mitarbeiter Bearbeiten/Löschen --- const employeeTableBody = document.getElementById('employee-table-body'); const editEmployeeFormTemplate = document.getElementById('editEmployeeFormTemplate'); if (employeeTableBody && editEmployeeFormTemplate) { console.log("Schichtplaner JS: Initialisiere Mitarbeiter Edit/Delete Logik."); // Event Listener für Klicks auf Bearbeiten- oder Löschen-Buttons employeeTableBody.addEventListener('click', function(event) { const targetButton = event.target.closest('button'); // Finde den geklickten Button if (!targetButton) return; // Kein Button geklickt // --- BEARBEITEN --- if (targetButton.classList.contains('edit-employee-btn')) { const employeeId = targetButton.dataset.employeeId; const modalTitle = targetButton.dataset.modalTitle; console.log(`Bearbeiten geklickt für Mitarbeiter ID: ${employeeId}`); // 1. Hole Mitarbeiterdaten von der API fetch(`/api/employee/${employeeId}`) .then(response => { if (!response.ok) { return response.json().then(err => { throw new Error(err.message || `HTTP Fehler ${response.status}`)}); } return response.json(); }) .then(result => { if (result.status === 'success' && result.employee) { const employeeData = result.employee; console.log("Mitarbeiterdaten geladen:", employeeData); // 2. Fülle das Bearbeiten-Formular im Modal // Klone die Vorlage const formContainerClone = editEmployeeFormTemplate.cloneNode(true); const editForm = formContainerClone.querySelector('.edit-employee-form'); // Setze die Action-URL dynamisch editForm.action = `/api/employee/${employeeId}`; // PUT Request wird per JS gemacht // Fülle die Standardfelder editForm.querySelector('#emp_name_edit').value = employeeData.name || ''; editForm.querySelector('#emp_type_edit').value = employeeData.employee_type || ''; editForm.querySelector('#emp_entry_date_edit').value = employeeData.entry_date || ''; editForm.querySelector('#emp_birthday_edit').value = employeeData.birthday || ''; // 3. Füge Rollen-Checkboxes hinzu const rolesContainer = editForm.querySelector('#editEmployeeRolesContainer'); rolesContainer.innerHTML = ''; // Überschrift wiederherstellen // Hole ALLE verfügbaren Rollen (müssen wir annehmen oder per API laden - einfacher: aus HTML lesen) const availableRoles = []; document.querySelectorAll('.assign-roles-form .form-check-input[name="role_ids"]').forEach(checkbox => { // Verhindere Duplikate if (!availableRoles.some(role => role.id == checkbox.value)) { availableRoles.push({ id: checkbox.value, name: checkbox.nextElementSibling.textContent.trim() // Hole Namen aus Label }); } }); if (availableRoles.length > 0) { availableRoles.forEach(role => { const isChecked = employeeData.assigned_role_ids.includes(parseInt(role.id)); const div = document.createElement('div'); div.className = 'form-check form-check-inline'; div.innerHTML = ` `; rolesContainer.appendChild(div); }); } else { rolesContainer.innerHTML += '

Keine Rollen definiert.'; } // 4. Öffne das Modal mit dem befüllten Formular if (modal && modalTitle && modalBody) { modalTitle.textContent = modalTitle || `Mitarbeiter ${employeeId} bearbeiten`; modalBody.innerHTML = ''; modalBody.appendChild(editForm); // Füge das befüllte Formular ein modal.style.display = 'block'; // Listener für interne Abbrechen-Buttons hinzufügen modalBody.querySelectorAll('.close-modal-btn-internal').forEach(btn => { btn.addEventListener('click', () => { modal.style.display = "none"; }); }); // 5. Event Listener für das Absenden des Bearbeiten-Formulars editForm.addEventListener('submit', function(submitEvent) { submitEvent.preventDefault(); // Verhindere Standard-Formular-Submit handleEditEmployeeSubmit(employeeId, editForm); }); } else { console.error("Modal-Elemente nicht gefunden!"); } } else { throw new Error(result.message || "Fehler beim Laden der Mitarbeiterdaten"); } }) .catch(error => { console.error("Fehler beim Abrufen/Vorbereiten der Mitarbeiterdaten:", error); alert(`Fehler: ${error.message}`); }); } // --- LÖSCHEN --- else if (targetButton.classList.contains('delete-employee-btn')) { const employeeId = targetButton.dataset.employeeId; const employeeName = targetButton.dataset.employeeName || 'diesen Mitarbeiter'; // Fallback-Name // Sicherheitsabfrage if (confirm(`Möchtest du ${employeeName} wirklich unwiderruflich löschen?`)) { console.log(`Löschen bestätigt für Mitarbeiter ID: ${employeeId}`); // API-Aufruf zum Löschen fetch(`/api/employee/${employeeId}`, { method: 'DELETE', headers: { 'Accept': 'application/json' } }) .then(response => response.json().then(data => ({ ok: response.ok, status: response.status, data: data }))) .then(result => { console.log("Lösch-API Antwort:", result); if (result.ok && result.data && result.data.status === 'success') { console.log("Löschen erfolgreich:", result.data.message); // Entferne die Zeile aus der Tabelle const rowToRemove = document.getElementById(`employee-row-${employeeId}`); if (rowToRemove) { rowToRemove.style.transition = 'opacity 0.5s ease-out'; rowToRemove.style.opacity = '0'; setTimeout(() => { rowToRemove.remove(); }, 500); // Nach Fade-Out entfernen } // Zeige Erfolgsmeldung (optional, da Zeile verschwindet) // alert(result.data.message); // Optional: Flash-Nachricht dynamisch hinzufügen (komplexer) } else { // Zeige Fehlermeldung von der API throw new Error(result.data.message || `API Fehler ${result.status}`); } }) .catch(error => { console.error("Fehler beim Löschen des Mitarbeiters:", error); alert(`Fehler beim Löschen: ${error.message}`); }); } else { console.log("Löschen abgebrochen."); } } }); // Ende Klick-Listener // Funktion zum Verarbeiten des Bearbeiten-Formulars function handleEditEmployeeSubmit(employeeId, formElement) { const formData = new FormData(formElement); const dataToSend = { name: formData.get('name'), employee_type: formData.get('employee_type'), entry_date: formData.get('entry_date'), birthday: formData.get('birthday'), assigned_role_ids: formData.getAll('assigned_role_ids') // Holt alle ausgewählten Rollen-IDs }; console.log("Sende Update für Mitarbeiter:", employeeId, dataToSend); // Visuelles Feedback im Modal (optional) const submitButton = formElement.querySelector('button[type="submit"]'); if(submitButton) submitButton.disabled = true; // Button deaktivieren fetch(`/api/employee/${employeeId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(dataToSend) }) .then(response => response.json().then(data => ({ ok: response.ok, status: response.status, data: data }))) .then(result => { console.log("Update API Antwort:", result); if (result.ok && result.data && result.data.status === 'success') { console.log("Update erfolgreich:", result.data.message); if(modal) modal.style.display = "none"; // Modal schließen // Seite neu laden, um Änderungen in der Tabelle sicher anzuzeigen // Alternativ: Zeile dynamisch updaten (komplexer) window.location.reload(); // Zeige Flash-Nachricht (wird nach Reload angezeigt) } else { throw new Error(result.data.message || `API Fehler ${result.status}`); } }) .catch(error => { console.error("Fehler beim Mitarbeiter-Update:", error); alert(`Fehler beim Speichern: ${error.message}`); if(submitButton) submitButton.disabled = false; // Button wieder aktivieren }); } } else { console.warn("Schichtplaner JS: Mitarbeiter Tabelle Body (tbody#employee-table-body) oder Edit-Vorlage nicht gefunden."); } // --- ENDE Modul 4: Mitarbeiter Edit/Delete --- // ... (Modal Steuerung Modul bleibt unverändert) ... const qualificationTableBody = document.getElementById('qualification-table-body'); const editQualificationFormTemplate = document.getElementById('editQualificationFormTemplate'); if (qualificationTableBody && editQualificationFormTemplate) { console.log("Schichtplaner JS: Initialisiere Qualifikationen Edit/Delete Logik."); qualificationTableBody.addEventListener('click', function(event) { const targetButton = event.target.closest('button'); if (!targetButton) return; // --- BEARBEITEN --- if (targetButton.classList.contains('edit-qualification-btn')) { const qualificationId = targetButton.dataset.qualificationId; const modalTitle = targetButton.dataset.modalTitle; const targetTemplateId = targetButton.dataset.modalTarget; // Hole auch die Template ID console.log(`Bearbeiten geklickt für Quali ID: ${qualificationId}`); fetch(`/api/qualification/${qualificationId}`) .then(response => response.ok ? response.json() : response.json().then(err => Promise.reject(err))) .then(result => { if (result.status === 'success' && result.qualification) { const qData = result.qualification; console.log("Quali Daten geladen:", qData); // Öffne das allgemeine Modal mit der Quali-Edit-Vorlage // Wichtig: openModalGeneric verwenden! openModalGeneric('commonModal', modalTitle || `Qualifikation bearbeiten`, targetTemplateId, 'form'); // Finde das Formular im JETZT SICHTBAREN #commonModal Body const visibleEditForm = commonModalBody.querySelector('.edit-qualification-form'); if (visibleEditForm) { // Stelle sicher, dass das Formular gefunden wurde // Standardfelder füllen const nameInput = visibleEditForm.querySelector('#qual_name_edit'); const categoryInput = visibleEditForm.querySelector('#qual_category_edit'); const descriptionInput = visibleEditForm.querySelector('#qual_description_edit'); const profileCheckbox = visibleEditForm.querySelector('#qual_has_profile_edit'); // Checkbox finden if(nameInput) nameInput.value = qData.name || ''; if(categoryInput) categoryInput.value = qData.category || ''; if(descriptionInput) descriptionInput.value = qData.description || ''; // === Checkbox HIER setzen === if (profileCheckbox) { profileCheckbox.checked = qData.has_profile; // Setze Checkbox-Status basierend auf API-Daten } else { console.warn("Checkbox 'has_profile' nicht im Quali-Edit-Formular gefunden."); } // ============================ // Submit Listener neu binden (wichtig!) const newForm = visibleEditForm.cloneNode(true); visibleEditForm.parentNode.replaceChild(newForm, visibleEditForm); newForm.addEventListener('submit', function(submitEvent) { submitEvent.preventDefault(); handleEditQualificationSubmit(qualificationId, newForm); }); } else { console.error("Konnte .edit-qualification-form im Modal nicht finden nach dem Öffnen."); } } else { throw new Error(result.message || "Fehler beim Laden der Quali-Daten"); } }) .catch(error => { console.error("Fehler:", error); alert(`Fehler: ${error.message}`); }); } // --- LÖSCHEN --- (Rest bleibt gleich) else if (targetButton.classList.contains('delete-qualification-btn')) { // ... (Lösch-Code unverändert) ... } // --- LÖSCHEN --- else if (targetButton.classList.contains('delete-qualification-btn')) { const qualificationId = targetButton.dataset.qualificationId; const qualificationName = targetButton.dataset.qualificationName || 'diese Qualifikation'; if (confirm(`Möchtest du die Qualifikation '${qualificationName}' wirklich löschen? Sie darf nicht mehr verwendet werden!`)) { console.log(`Löschen bestätigt für Quali ID: ${qualificationId}`); fetch(`/api/qualification/${qualificationId}`, { method: 'DELETE', headers: { 'Accept': 'application/json' } }) .then(response => response.json().then(data => ({ ok: response.ok, status: response.status, data: data }))) .then(result => { console.log("Lösch-API Antwort:", result); if (result.ok && result.data && result.data.status === 'success') { console.log("Löschen erfolgreich:", result.data.message); const rowToRemove = document.getElementById(`qualification-row-${qualificationId}`); if (rowToRemove) { rowToRemove.style.transition = 'opacity 0.5s ease-out'; rowToRemove.style.opacity = '0'; setTimeout(() => { rowToRemove.remove(); }, 500); } } else { throw new Error(result.data.message || `API Fehler ${result.status}`); } }) .catch(error => { console.error("Fehler:", error); alert(`Fehler beim Löschen: ${error.message}`); }); } else { console.log("Löschen abgebrochen."); } } }); // Ende Klick-Listener // Funktion zum Verarbeiten des Bearbeiten-Formulars function handleEditQualificationSubmit(qualificationId, formElement) { const formData = new FormData(formElement); const dataToSend = { // JSON erstellen name: formData.get('name'), category: formData.get('category'), description: formData.get('description'), // === NEU: Checkbox-Wert holen === has_profile: formData.has('has_profile') // true wenn angehakt, false wenn nicht // ============================= }; console.log("Sende Update für Quali:", qualificationId, dataToSend); const submitButton = formElement.querySelector('button[type="submit"]'); if(submitButton) submitButton.disabled = true; fetch(`/api/qualification/${qualificationId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify(dataToSend) }) .then(response => response.json().then(data => ({ ok: response.ok, status: response.status, data: data }))) .then(result => { console.log("Update API Antwort:", result); if (result.ok && result.data && result.data.status === 'success') { console.log("Update erfolgreich:", result.data.message); // Schließe das #commonModal if(commonModalElement) commonModalElement.style.display = "none"; // Lade die Seite neu, um Änderungen in der Tabelle zu sehen window.location.reload(); } else { throw new Error(result.data.message || `API Fehler ${result.status}`); } }) .catch(error => { console.error("Fehler beim Quali-Update:", error); alert(`Fehler beim Speichern: ${error.message}`); if(submitButton) submitButton.disabled = false; }); } // Ende handleEditQualificationSubmit

// --- Modul 8: Maschinen & Gruppen Bearbeiten/Löschen ---
const mgmtContainer = document.getElementById('groups-and-machines-list');
const editGroupFormTemplate = document.getElementById('editGroupFormTemplate');
const editMachineFormTemplate = document.getElementById('editMachineFormTemplate');

// Stelle sicher, dass alle Elemente auf der SEITE vorhanden sind UND das Modal existiert
if (mgmtContainer && editGroupFormTemplate && editMachineFormTemplate && modal) {
    console.log("Schichtplaner JS: Initialisiere Maschinen/Gruppen Edit/Delete Logik.");

    // === Event Listener für Klicks im Verwaltungsbereich ===
    mgmtContainer.addEventListener('click', function(event) {
         const targetButton = event.target.closest('button'); // Finde den geklickten Button
         if (!targetButton) return; // Ignoriere Klicks, die nicht auf Buttons sind

         // --- GRUPPE BEARBEITEN ---
         if (targetButton.classList.contains('edit-group-btn')) {
             event.preventDefault(); // Verhindere Standardverhalten
             const groupId = targetButton.dataset.groupId;
             const modalTitle = targetButton.dataset.modalTitle;
             console.log(`Gruppe Bearbeiten geklickt für ID: ${groupId}`);

             // API-Call zum Holen der Gruppendaten
             fetch(`/api/group/${groupId}`)
                .then(response => response.ok ? response.json() : response.json().then(err => Promise.reject(err)))
                .then(result => {
                    if (result.status === 'success' && result.group) {
                        const groupData = result.group;
                        console.log("Gruppendaten geladen:", groupData);

                        // Öffne das Modal mit der GRUPPEN-Bearbeiten-Vorlage
                        openModalGeneric('commonModal', modalTitle || `Gruppe bearbeiten`, 'editGroupFormTemplate', 'form');

                        // Finde das Formular im JETZT SICHTBAREN Modal Body
                        const visibleEditForm = modalBody.querySelector('.edit-group-form');
                        if (visibleEditForm) {
                            // Fülle die Formularfelder mit den Daten
                            const nameInput = visibleEditForm.querySelector('input[name="name"]');
                            const abbrInput = visibleEditForm.querySelector('input[name="abbreviation"]');
                            const colorInput = visibleEditForm.querySelector('input[name="color"]');
                            if(nameInput) nameInput.value = groupData.name || '';
                            if(abbrInput) abbrInput.value = groupData.abbreviation || ''; // Abkürzung füllen
                            if(colorInput) colorInput.value = groupData.color || '#D0D0D0';   // Farbe füllen

                            // WICHTIG: Alten Submit-Listener entfernen (durch Klonen) und neuen hinzufügen
                            const newForm = visibleEditForm.cloneNode(true);
                            visibleEditForm.parentNode.replaceChild(newForm, visibleEditForm);
                            newForm.addEventListener('submit', (e) => {
                                e.preventDefault();
                                handleEditGroupSubmit(groupId, newForm); // Ruft den Submit Handler auf
                            });
                        } else { console.error("Konnte .edit-group-form im Modal nicht finden."); }
                    } else { throw new Error(result.message || "Fehler beim Laden der Gruppendaten"); }
                })
                .catch(error => { console.error("Fehler:", error); alert(`Fehler: ${error.message}`); });
         }

         // --- GRUPPE LÖSCHEN ---
         else if (targetButton.classList.contains('delete-group-btn')) {
             event.preventDefault();
             const groupId = targetButton.dataset.groupId;
             const groupName = targetButton.dataset.groupName || 'diese Gruppe';
             if (confirm(`Gruppe '${groupName}' wirklich löschen? Zugeordnete Maschinen, Personalbedarf und Quali-Anforderungen gehen verloren!`)) {
                 console.log(`Löschen bestätigt für Gruppe ID: ${groupId}`);
                 fetch(`/api/group/${groupId}`, { method: 'DELETE', headers: { 'Accept': 'application/json' } })
                    .then(response => response.json().then(data => ({ ok: response.ok, data: data })))
                    .then(result => {
                        if (result.ok && result.data.status === 'success') {
                            console.log("Gruppen-Löschen erfolgreich:", result.data.message);
                            const cardToRemove = document.getElementById(`group-card-${groupId}`);
                            if (cardToRemove) { cardToRemove.remove(); } // Entferne ganze Karte
                            // Optional: Flash-Nachricht hinzufügen
                        } else { throw new Error(result.data.message || 'Fehler beim Löschen der Gruppe'); }
                    })
                    .catch(error => { console.error("Fehler beim Gruppen-Löschen:", error); alert(`Fehler: ${error.message}`); });
             } else { console.log("Löschen der Gruppe abgebrochen."); }
         }

         // --- MASCHINE BEARBEITEN ---
         else if (targetButton.classList.contains('edit-machine-btn')) {
             event.preventDefault();
             const machineId = targetButton.dataset.machineId;
             const modalTitle = targetButton.dataset.modalTitle;
             console.log(`Maschine Bearbeiten geklickt für ID: ${machineId}`);
             fetch(`/api/machine/${machineId}`) // API holt Maschine UND Gruppenliste
                .then(response => response.ok ? response.json() : response.json().then(err => Promise.reject(err)))
                .then(result => {
                    if (result.status === 'success' && result.machine && result.groups !== undefined) {
                        const machineData = result.machine;
                        const groupOptions = result.groups;
                        // Öffne das Modal mit der MASCHINEN-Bearbeiten-Vorlage
                        openModalGeneric('commonModal', modalTitle || 'Maschine bearbeiten', 'editMachineFormTemplate', 'form');
                        // Finde das Formular im JETZT SICHTBAREN Modal Body
                        const visibleEditForm = modalBody.querySelector('.edit-machine-form');
                        if (visibleEditForm) {
                            const nameInput = visibleEditForm.querySelector('input[name="name"]');
                            const groupSelect = visibleEditForm.querySelector('select[name="group_id"]');
                            // Dynamische IDs setzen (optional aber besser für Labels)
                            if(nameInput) nameInput.id = `machine_name_edit_${machineId}`;
                            if(groupSelect) groupSelect.id = `machine_group_edit_${machineId}`;
                            const nameLabel = nameInput ? nameInput.previousElementSibling : null;
                            const groupLabel = groupSelect ? groupSelect.previousElementSibling : null;
                            if (nameLabel && nameInput) nameLabel.setAttribute('for', nameInput.id);
                            if (groupLabel && groupSelect) groupLabel.setAttribute('for', groupSelect.id);

                            // Felder füllen
                            if (nameInput) nameInput.value = machineData.name || '';
                            if (groupSelect) {
                                // Alte Optionen löschen, neue hinzufügen
                                while (groupSelect.options.length > 1) { groupSelect.remove(1); }
                                groupOptions.forEach(group => { const option = document.createElement('option'); option.value = group.id; option.textContent = group.name; groupSelect.appendChild(option); });
                                groupSelect.value = machineData.group_id || '0'; // Wert setzen
                            }
                            // Alten Listener entfernen, neuen hinzufügen
                            const newForm = visibleEditForm.cloneNode(true);
                            visibleEditForm.parentNode.replaceChild(newForm, visibleEditForm);
                            newForm.addEventListener('submit', (e) => { e.preventDefault(); handleEditMachineSubmit(machineId, newForm); });
                        } else { console.error("Konnte .edit-machine-form im Modal nicht finden."); }
                    } else { throw new Error(result.message || "Fehler Laden Maschinendaten/Gruppen"); }
                })
                .catch(error => { console.error("Fehler:", error); alert(`Fehler: ${error.message}`); });
        }

         // --- MASCHINE LÖSCHEN ---
         else if (targetButton.classList.contains('delete-machine-btn')) {
             event.preventDefault();
             const machineId = targetButton.dataset.machineId;
             const machineName = targetButton.dataset.machineName || 'diese Maschine';
             if (confirm(`Maschine '${machineName}' wirklich löschen?`)) {
                  console.log(`Löschen bestätigt für Maschine ID: ${machineId}`);
                  fetch(`/api/machine/${machineId}`, { method: 'DELETE', headers: { 'Accept': 'application/json' } })
                     .then(response => response.json().then(data => ({ ok: response.ok, data: data })))
                     .then(result => {
                         if (result.ok && result.data.status === 'success') {
                             console.log("Maschinen-Löschen erfolgreich:", result.data.message);
                             const rowToRemove = document.getElementById(`machine-row-${machineId}`);
                             if (rowToRemove) {
                                  const list = rowToRemove.closest('ul');
                                  rowToRemove.remove();
                                  // Optional: Platzhalter wieder einfügen, wenn Liste leer ist
                                  if (list && !list.querySelector('li')) { const placeholder = document.createElement('li'); placeholder.className = 'list-group-item text-muted small ps-0 py-1 no-machines-placeholder'; placeholder.textContent = 'Keine Maschinen (mehr) in dieser Gruppe.'; list.appendChild(placeholder); }
                              }
                         } else { throw new Error(result.data.message || 'Fehler beim Löschen der Maschine'); }
                     })
                     .catch(error => { console.error("Fehler beim Maschinen-Löschen:", error); alert(`Fehler: ${error.message}`); });
             } else { console.log("Löschen der Maschine abgebrochen."); }
         }
    }); // Ende Klick-Listener für mgmtContainer

    // --- Submit-Handler für Gruppen-Bearbeitung ---
    function handleEditGroupSubmit(groupId, formElement) {
        const formData = new FormData(formElement);
        const dataToSend = {
            name: formData.get('name'),
            abbreviation: formData.get('abbreviation'), // Holt Abkürzung
            color: formData.get('color')                // Holt Farbe
        };
        console.log("Sende Update für Gruppe:", groupId, dataToSend);
        const submitButton = formElement.querySelector('button[type="submit"]');
        if(submitButton) submitButton.disabled = true;
        fetch(`/api/group/${groupId}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(dataToSend) })
        .then(response => response.ok ? response.json() : response.json().then(err => Promise.reject(err)))
        .then(result => { if(result.status === 'success') { if(modal) modal.style.display = 'none'; window.location.reload(); } else { throw new Error(result.message); } })
        .catch(error => { alert(`Fehler: ${error.message}`); if(submitButton) submitButton.disabled = false; });
    } // Ende handleEditGroupSubmit

    // --- Submit-Handler für Maschinen-Bearbeitung ---
    function handleEditMachineSubmit(machineId, formElement) {
        const formData = new FormData(formElement);
        const groupIdValue = formData.get('group_id');
        const dataToSend = { name: formData.get('name'), group_id: (groupIdValue && groupIdValue !== '0') ? groupIdValue : null };
        console.log("Sende Update für Maschine:", machineId, dataToSend);
        const submitButton = formElement.querySelector('button[type="submit"]');
        if(submitButton) submitButton.disabled = true;
        fetch(`/api/machine/${machineId}`, { method: 'PUT', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(dataToSend) })
        .then(response => response.ok ? response.json() : response.json().then(err => Promise.reject(err)))
        .then(result => { if(result.status === 'success') { if(modal) modal.style.display = 'none'; window.location.reload(); } else { throw new Error(result.message); } })
        .catch(error => { alert(`Fehler: ${error.message}`); if(submitButton) submitButton.disabled = false; });
    } // Ende handleEditMachineSubmit

} else {
    // Logge, welche Elemente fehlen könnten
    console.warn("Schichtplaner JS: Container für Maschinen/Gruppen oder Edit-Vorlagen nicht gefunden.", {mgmtContainer, editGroupFormTemplate, editMachineFormTemplate});
}
// --- ENDE Modul 8: Maschinen & Gruppen ---

// --- Neuer Event-Listener für Interaktionen im Schichtarten-Einstellungs-Modal ---

if (settingsModalElement) { // Prüfe ob das Einstellungs-Modal existiert
console.log("Schichtplaner JS: Initialisiere Edit/Delete Listener für Schichtarten im Einstellungen-Modal.");

// Nutze Event Delegation auf dem Modal-Element selbst
settingsModalElement.addEventListener('click', function(event) {
    const targetButton = event.target.closest('button'); // Finde den geklickten Button
    if (!targetButton) return; // Ignoriere Klicks, die nicht auf Buttons sind

    // --- Schichtart Bearbeiten ---
    if (targetButton.classList.contains('edit-shift-type-btn')) {
        event.preventDefault(); // Wichtig, falls Button in Formular ist
        const shiftTypeId = targetButton.dataset.shiftTypeId;
        const modalTitleText = targetButton.dataset.modalTitle; // Titel für das *nächste* (common) Modal
        const targetTemplateId = targetButton.dataset.modalTarget; // ID der Edit-Vorlage im HTML

        console.log(`Bearbeiten geklickt für Schichtart ID: ${shiftTypeId}`);
        // API Call zum Holen der Daten
        fetch(`/api/shift_type/${shiftTypeId}`)
            .then(response => response.ok ? response.json() : response.json().then(err => Promise.reject(err)))
            .then(result => {
                if (result.status === 'success' && result.shift_type) {
                    const stData = result.shift_type;
                    // Öffne das ALLGEMEINE Modal (#commonModal) mit der Edit-Vorlage für Schichtarten
                    // Wichtig: targetTemplateId muss die ID der *versteckten* Vorlage sein
                    openModalGeneric('commonModal', modalTitleText || 'Schichtart bearbeiten', targetTemplateId, 'form');

                    // Finde das Formular im JETZT geöffneten #commonModal
                    const visibleEditForm = commonModalBody.querySelector('.edit-shift-type-form');
                    if(visibleEditForm) {
                        // Formularfelder füllen (verwende die IDs aus der Vorlage #editShiftTypeFormTemplateModal)
                        const nameInput = visibleEditForm.querySelector('#st_name_edit_modal');
                        const abbrInput = visibleEditForm.querySelector('#st_abbr_edit_modal');
                        const colorInput = visibleEditForm.querySelector('#st_color_edit_modal');
                        const textColorInput = visibleEditForm.querySelector('#st_textcolor_edit_modal');
                        const isAbsenceInput = visibleEditForm.querySelector('#st_is_absence_edit_modal');
                        const hidePrintInput = visibleEditForm.querySelector('#st_hide_on_print_edit_modal');

                        if(nameInput) nameInput.value = stData.name || '';
                        if(abbrInput) abbrInput.value = stData.abbreviation || '';
                        if(colorInput) colorInput.value = stData.color || '#FFFFFF';
                        if(textColorInput) textColorInput.value = stData.text_color || '#000000';
                        if(isAbsenceInput) isAbsenceInput.checked = stData.is_absence;
                        if(hidePrintInput) hidePrintInput.checked = stData.hide_on_print;

                        // WICHTIG: Submit Listener neu binden (verhindert Mehrfachbindung)
                        const newForm = visibleEditForm.cloneNode(true);
                        visibleEditForm.parentNode.replaceChild(newForm, visibleEditForm);
                        newForm.addEventListener('submit', (e) => {
                            e.preventDefault();
                            // Rufe den Submit Handler auf (siehe unten)
                            handleEditShiftTypeSubmit(shiftTypeId, newForm);
                        });
                    } else { console.error("Konnte .edit-shift-type-form im #commonModal nicht finden nach dem Öffnen."); }
                } else { throw new Error(result.message || "Fehler Laden Schichtartdaten"); }
            })
            .catch(error => { console.error("Fehler:", error); alert(`Fehler: ${error.message}`); });
    }
    // --- Schichtart Löschen ---
    else if (targetButton.classList.contains('delete-shift-type-btn')) {
        event.preventDefault();
        const shiftTypeId = targetButton.dataset.shiftTypeId;
        const shiftTypeName = targetButton.dataset.shiftTypeName || 'diese Schichtart';
         if (confirm(`Schichtart '${shiftTypeName}' wirklich löschen?`)) {
            console.log(`Löschen bestätigt für Schichtart ID: ${shiftTypeId}`);
            fetch(`/api/shift_type/${shiftTypeId}`, { method: 'DELETE', headers: { 'Accept': 'application/json' } })
                .then(response => response.json().then(data => ({ ok: response.ok, status: response.status, data: data })))
                .then(result => {
                    if (result.ok && result.data && result.data.status === 'success') {
                        // Entferne Zeile aus der Tabelle im Einstellungs-Modal
                        const rowToRemove = settingsModalElement.querySelector(`#shift-type-row-modal-${shiftTypeId}`);
                        if (rowToRemove) {
                            rowToRemove.remove();
                            console.log(`Zeile shift-type-row-modal-${shiftTypeId} entfernt.`);
                         } else {
                             console.warn(`Zeile shift-type-row-modal-${shiftTypeId} nicht im Modal gefunden.`);
                         }
                    } else { throw new Error(result.data.message || `API Fehler ${result.status}`); }
                })
                .catch(error => { console.error("Fehler:", error); alert(`Fehler beim Löschen: ${error.message}`); });
        }
    }
     // --- HIER KEINE ROLLE MEHR ---

}); // Ende Klick-Listener für Einstellungs-Modal

} else {
console.warn("Schichtplaner JS: Einstellungs-Modal (#shiftTypeSettingsModal) nicht gefunden, kann keine Listener binden.");
}

// --- Submit Handler MÜSSEN außerhalb des if-Blocks definiert sein, ---
// damit sie auch gefunden werden, wenn das Modal geschlossen ist.

// Funktion zum Verarbeiten des Schichtart-Bearbeiten-Formulars
function handleEditShiftTypeSubmit(shiftTypeId, formElement) {
const formData = new FormData(formElement);
const dataToSend = {
name: formData.get('name'),
abbreviation: formData.get('abbreviation'),
color: formData.get('color'),
text_color: formData.get('text_color'),
is_absence: formData.has('is_absence'), // .has prüft ob Checkbox gesendet wurde
hide_on_print: formData.has('hide_on_print')
};
console.log("Sende Update für Schichtart:", shiftTypeId, dataToSend);
const submitButton = formElement.querySelector('button[type="submit"]');
if(submitButton) submitButton.disabled = true;

fetch(`/api/shift_type/${shiftTypeId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(dataToSend) })
.then(response => response.json().then(data => ({ ok: response.ok, data: data })))
.then(result => {
     if (result.ok && result.data.status === 'success') {
         // Schließe das Bearbeiten-Modal (#commonModal)
         if(commonModalElement) commonModalElement.style.display = "none";
         // Gib Hinweis statt Reload
         alert("Schichtart erfolgreich gespeichert.\n\nDie Änderungen werden im Einstellungs-Modal erst nach dem erneuten Öffnen sichtbar.");
         // Alternativ: Versuche Tabelle im Einstellungs-Modal dynamisch zu aktualisieren (komplexer)
     } else { throw new Error(result.data.message || `API Fehler`); }
})
.catch(error => { alert(`Fehler beim Speichern: ${error.message}`); if(submitButton) submitButton.disabled = false; });

}

}); // Ende DOMContentLoaded

Please help me!