$(document).ready(function () { // =================== // GLOBAL STATE // =================== let partMatrice = {}; let unsavedChanges = false; let matrici = []; let macroMatrici = []; // =================== // VOICE RECOGNITION SETUP // =================== const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; let recognition = null; let isVoiceActive = false; const magicWord = "salva"; if (SpeechRecognition) { recognition = new SpeechRecognition(); recognition.lang = "it-IT"; recognition.continuous = true; recognition.interimResults = false; recognition.onresult = function (event) { const transcript = event.results[ event.results.length - 1 ][0].transcript .trim() .toLowerCase(); const $currentRow = $("#partsTableBody tr:last"); const $descriptionInput = $currentRow.find(".part-description"); if (transcript.includes(magicWord)) { const cleanedTranscript = transcript .replace(magicWord, "") .trim(); if (cleanedTranscript) { $descriptionInput.val( ( $descriptionInput.val() + " " + cleanedTranscript ).trim(), ); $descriptionInput.trigger("blur"); } const maxPartNumber = Math.max( ...$("#partsTableBody tr") .map(function () { return ( parseInt($(this).find(".part-number").val()) || 0 ); }) .get(), ); addNewRow(maxPartNumber + 1, false); // Aggiunge riga normale per riconoscimento vocale const $newRow = $("#partsTableBody tr:last"); $newRow.find(".part-description").focus(); } else { $descriptionInput.val( ($descriptionInput.val() + " " + transcript).trim(), ); $descriptionInput.trigger("blur"); } }; recognition.onerror = function (event) { if (event.error === "no-speech" || event.error === "aborted") { if (isVoiceActive) recognition.start(); } else { alert("Errore nel riconoscimento vocale: " + event.error); toggleVoiceRecognition(); } }; recognition.onend = function () { if (isVoiceActive) recognition.start(); }; } else { $("#toggleVoiceBtn").hide(); } function toggleVoiceRecognition() { if (!recognition) { console.log("Voce non supportata dal browser"); return; } isVoiceActive = !isVoiceActive; const $btn = $("#toggleVoiceBtn"); if (isVoiceActive) { $btn.addClass("btn-danger").html( ' Stop Voce', ); recognition.start(); $("#partsTableBody tr:last").find(".part-description").focus(); } else { $btn.removeClass("btn-danger") .addClass("btn-secondary") .html(' Voce'); recognition.stop(); } } $(document).on("click", "#toggleVoiceBtn", function (e) { console.log("Clicked Voce - Evento triggerato"); e.preventDefault(); toggleVoiceRecognition(); }); // =================== // MODAL HANDLING // =================== function loadParts(iddatadb, idquotations) { if (iddatadb) { if (matrici.length === 0) { $.ajax({ url: "get_matrici_db.php", method: "GET", dataType: "json", success: function (data) { matrici = data.value || []; loadMacroMatrici(); initializeGlobalSelect2(); loadPhoto(iddatadb, idquotations); loadExistingParts(iddatadb, idquotations); }, error: function (xhr, status, error) { matrici = []; loadMacroMatrici(); initializeGlobalSelect2(); loadPhoto(iddatadb, idquotations); loadExistingParts(iddatadb, idquotations); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } else { loadMacroMatrici(); initializeGlobalSelect2(); loadPhoto(iddatadb, idquotations); loadExistingParts(iddatadb, idquotations); } } else { loadPhoto(iddatadb, idquotations); loadExistingParts(iddatadb, idquotations); } } // EVENTO PER APRIRE IL SECONDO MODALE $(document).on("click", "#openAnnotationsBtn", function () { console.log("Clic su Apri Annotazioni..."); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const trfHeader = $("#trfHeader").text(); console.log("Dati recuperati da partsModal:", { iddatadb, idquotations, trfHeader, }); const partsModal = bootstrap.Modal.getInstance( document.getElementById("partsModal"), ); if (partsModal) { partsModal.hide(); } const annotationsModalElement = document.getElementById("annotationsModal"); if (annotationsModalElement) { console.log("Elemento #annotationsModal trovato nel DOM."); openAnnotationsModal(iddatadb, idquotations, trfHeader); return; } console.log("Caricamento dinamico di modal_annotations.php..."); $.ajax({ url: "modal_annotations.php", method: "GET", cache: false, beforeSend: function () { console.log("Inizio richiesta AJAX per modal_annotations.php"); }, success: function (response) { console.log( "modal_annotations.php caricato con successo:", response.substring(0, 100) + "...", ); $("#annotationsModalContainer").html(response); const annotationsModalElementAfterLoad = document.getElementById("annotationsModal"); if (!annotationsModalElementAfterLoad) { console.error( "Errore: #annotationsModal non trovato nel DOM dopo il caricamento.", ); alert( "Errore: Il modale delle annotazioni non è stato caricato correttamente. Controlla il contenuto di modal_annotations.php.", ); return; } openAnnotationsModal(iddatadb, idquotations, trfHeader); }, error: function (xhr, status, error) { console.error( "Errore nel caricamento di modal_annotations.php:", { status: status, statusCode: xhr.status, error: error, responseText: xhr.responseText, }, ); alert( "Errore nel caricamento del modale delle annotazioni: " + error + " (Codice: " + xhr.status + ")", ); }, }); }); function openAnnotationsModal(iddatadb, idquotations, trfHeader) { console.log("Tentativo di aprire annotationsModal con:", { iddatadb, idquotations, trfHeader, }); const annotationsModalElement = document.getElementById("annotationsModal"); if (!annotationsModalElement) { console.error( "Elemento #annotationsModal non trovato nel DOM durante openAnnotationsModal.", ); alert( "Errore: Modale annotazioni non trovato dopo il caricamento.", ); return; } if (typeof window.initAnnotationsModal === "function") { console.log("Chiamata a window.initAnnotationsModal..."); window.initAnnotationsModal(iddatadb, idquotations, trfHeader); } else { console.error( "initAnnotationsModal non definito. Verifica che annotationsModal.js sia caricato correttamente.", ); alert( "Errore: Funzione initAnnotationsModal non trovata. Controlla che annotationsModal.js sia incluso correttamente.", ); } } $("#partsModal").on("hide.bs.modal", function (e) { if ( unsavedChanges && !confirm("Hai modifiche non salvate. Vuoi davvero uscire?") ) { e.preventDefault(); } }); $("#partsModal").on("hidden.bs.modal", function () { partMatrice = {}; unsavedChanges = false; matrici = []; macroMatrici = []; $("#photoSelectorContainer").empty().hide(); $("#samplePhoto").attr("src", ""); $("#partsTableBody").empty(); $("#global-matrice").empty(); $("#macro-matrice-filter").empty(); $(".temp-alert").remove(); $(".modal-backdrop").remove(); $("body").removeClass("modal-open").css("padding-right", ""); $(":focus").blur(); }); // =================== // PHOTO LOADERS // =================== function loadPhoto(iddatadb, idquotations) { const currentPhoto = $("#samplePhoto").attr("src"); const endpoint = idquotations ? "load_photo_quotation.php" : "load_photo.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; $.ajax({ url: endpoint, method: "GET", data: data, success: function (response) { if (response.success) { if (response.photos && response.photos.length > 1) { showPhotoSelector(response.photos, currentPhoto); } else if ( response.photos && response.photos.length === 1 ) { loadSinglePhoto(response.photos[0]); } else { $("#samplePhoto").attr("src", ""); const errorMsg = $( '', ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } } else { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } function showPhotoSelector(photos, selected = null) { const selectorContainer = $("#photoSelectorContainer"); selectorContainer.empty().show(); const selector = $( '', ); photos.forEach((photo, index) => { const photoName = photo.split("/").pop(); const option = $("") .val(photo) .text(`Photo ${index + 1} - ${photoName}`); selector.append(option); }); selector.on("change", function () { loadSinglePhoto($(this).val()); }); selectorContainer.append(selector); const photoToSelect = selected && photos.includes(selected) ? selected : photos[0]; if (photoToSelect) { selector.val(photoToSelect); loadSinglePhoto(photoToSelect); } } function loadSinglePhoto(photoPath) { const img = $("#samplePhoto"); img.off("load").attr("src", photoPath); } // =================== // DOWNLOAD PHOTO // =================== $(document).on("click", "#downloadPhotoBtn", function (e) { console.log("Clicked Download Photo - Evento triggerato"); e.preventDefault(); const photoSrc = $("#samplePhoto").attr("src"); if (!photoSrc) { console.log("Nessuna foto caricata"); const errorMsg = $( '', ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); return; } const photoName = photoSrc.split("/").pop(); const link = document.createElement("a"); link.href = photoSrc; link.download = photoName; document.body.appendChild(link); link.click(); document.body.removeChild(link); console.log("Download avviato per:", photoSrc); }); // =================== // PARTS TABLE // =================== $(document).on("click", ".add-row-global", function (e) { e.preventDefault(); const maxPartNumber = Math.max( ...$("#partsTableBody tr") .map(function () { return parseInt($(this).find(".part-number").val()) || 0; }) .get(), ); addNewRow(maxPartNumber + 1, false); // Riga normale }); $(document).on("click", ".add-mix-global", function (e) { e.preventDefault(); const maxPartNumber = Math.max( ...$("#partsTableBody tr") .map(function () { return parseInt($(this).find(".part-number").val()) || 0; }) .get(), ); addNewRow(maxPartNumber + 1, true); // Riga Mix }); $(document).on("click", ".add-mix-row", function (e) { e.preventDefault(); const $row = $(this).closest("tr"); const partDescription = $row.find(".part-description").val().trim(); if (!partDescription) { const errorMsg = $( '', ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); return; } const maxPartNumber = Math.max( ...$("#partsTableBody tr") .map(function () { return parseInt($(this).find(".part-number").val()) || 0; }) .get(), ); let mixDescription = `Mix ${partDescription}`; const $mixRow = $("#partsTableBody tr") .filter(function () { return $(this) .find(".part-description") .val() .trim() .startsWith("Mix"); }) .last(); if ($mixRow.length > 0) { let currentMix = $mixRow.find(".part-description").val().trim(); if (currentMix === "Mix") { mixDescription = currentMix + " " + partDescription; } else if (!currentMix.includes(partDescription)) { mixDescription = currentMix + " + " + partDescription; } $mixRow .find(".part-description") .val(mixDescription) .trigger("blur"); } else { addNewRow(maxPartNumber + 1, true); // Crea nuova riga Mix const $newMixRow = $("#partsTableBody tr:last"); $newMixRow .find(".part-description") .val(mixDescription) .trigger("blur"); } }); function addNewRow(nextPartNumber, isMix = false) { const description = isMix ? "Mix" : ""; const newRow = `
`; $("#partsTableBody").append(newRow); const $select = $("#partsTableBody tr:last .part-matrice"); const selectedMacro = $("#macro-matrice-filter").val() || ""; initializeSelect2($select, nextPartNumber, "", null, selectedMacro); updateRowButtons(); markUnsaved(); } // =================== // NOTE HANDLING // =================== $(document).on("click", ".note-btn", function () { const $row = $(this).closest("tr"); const partId = $row.data("part-id"); const note = $row.data("note") || ""; const $noteModal = $("#noteModal"); $noteModal.find(".part-note").val(note); $noteModal.data("part-id", partId); $noteModal.data("row", $row); const modalInstance = new bootstrap.Modal($noteModal[0], { backdrop: "static", keyboard: false, }); modalInstance.show(); }); $(document).on("click", ".save-note-btn", function () { const $noteModal = $("#noteModal"); const $row = $noteModal.data("row"); const partId = $noteModal.data("part-id"); const note = $noteModal.find(".part-note").val().trim(); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const endpoint = idquotations ? "save_parts_quotation.php" : "save_parts.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; // Raccogli tutti i dati della riga per evitare sovrascritture const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); const mix = partDescription.startsWith("Mix") ? "Y" : "N"; const idmatrice = $row.find(".part-matrice").val() || null; const dateexpiry = $row.find(".part-dateexpiry").val() || null; if (partId && partId !== "new") { const $saveStatus = $row.find(".save-status"); const $saveLoading = $row.find(".save-loading"); $saveLoading.show(); $saveStatus.hide(); $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ ...data, parts: [ { id: partId, part_number: partNumber, part_description: partDescription, mix: mix, idmatrice: idmatrice, note: note || null, dateexpiry: dateexpiry, }, ], }), contentType: "application/json", success: function (response) { $saveLoading.hide(); if (response.success) { $row.data("note", note); $row.find(".note-btn").toggleClass("has-note", !!note); $saveStatus.show(); setTimeout(() => $saveStatus.hide(), 2000); bootstrap.Modal.getInstance( document.getElementById("noteModal"), ).hide(); } else { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { $saveLoading.hide(); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } else { $row.data("note", note); $row.find(".note-btn").toggleClass("has-note", !!note); bootstrap.Modal.getInstance( document.getElementById("noteModal"), ).hide(); markUnsaved(); } }); // =================== // DATEEXPIRY HANDLING // =================== $(document).on("change", ".part-dateexpiry", function () { const $input = $(this); const $row = $input.closest("tr"); const partId = $row.data("part-id"); const dateexpiry = $input.val(); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const endpoint = idquotations ? "save_parts_quotation.php" : "save_parts.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; // Raccogli tutti i dati della riga per evitare sovrascritture const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); const mix = partDescription.startsWith("Mix") ? "Y" : "N"; const idmatrice = $row.find(".part-matrice").val() || null; const note = $row.data("note") || null; if (partId && partId !== "new") { const $saveStatus = $row.find(".save-status"); const $saveLoading = $row.find(".save-loading"); $saveLoading.show(); $saveStatus.hide(); $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ ...data, parts: [ { id: partId, part_number: partNumber, part_description: partDescription, mix: mix, idmatrice: idmatrice, note: note, dateexpiry: dateexpiry || null, }, ], }), contentType: "application/json", success: function (response) { $saveLoading.hide(); if (response.success) { $saveStatus.show(); setTimeout(() => $saveStatus.hide(), 2000); } else { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { $saveLoading.hide(); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } else { markUnsaved(); } }); // =================== // OTHER TABLE HANDLERS // =================== function updateRowButtons() { const rowCount = $("#partsTableBody tr").length; $("#partsTableBody tr").each(function () { const $row = $(this); $row.find(".remove-row").css( "display", rowCount > 1 ? "inline-block" : "none", ); }); } $(document).on("click", ".remove-row", function (e) { e.preventDefault(); console.log("Clic su .remove-row rilevato"); // Debug: verifica clic const $row = $(this).closest("tr"); const partId = $row.data("part-id"); const partNumber = $row.find(".part-number").val(); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const endpoint = idquotations ? "delete_part_quotation.php" : "delete_part.php"; // Verifica se il modale esiste const $modal = $("#confirmDeleteModal"); if ($modal.length) { try { // Inizializza il modale Bootstrap in modo esplicito const modalInstance = new bootstrap.Modal($modal[0], { backdrop: "static", keyboard: false, }); // Associa l'handler al pulsante Elimina $("#confirmDeleteBtn") .off("click") .on("click", function () { if (partId && partId !== "new") { $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ part_id: partId }), contentType: "application/json", success: function (response) { if (response.success) { $row.remove(); delete partMatrice[partNumber]; updateRowButtons(); markUnsaved(); } else { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend( errorMsg, ); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } modalInstance.hide(); }, error: function (xhr, status, error) { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend( errorMsg, ); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); modalInstance.hide(); }, }); } else { $row.remove(); delete partMatrice[partNumber]; updateRowButtons(); markUnsaved(); modalInstance.hide(); } }); // Mostra il modale modalInstance.show(); } catch (error) { console.error("Errore nell'apertura del modale:", error); alert( "Errore: Impossibile aprire il modale di conferma. Dettagli: " + error.message, ); // Fallback con confirm nativo if (confirm("Sei sicuro di voler eliminare questa parte?")) { if (partId && partId !== "new") { $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ part_id: partId }), contentType: "application/json", success: function (response) { if (response.success) { $row.remove(); delete partMatrice[partNumber]; updateRowButtons(); markUnsaved(); } else { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend( errorMsg, ); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } else { $row.remove(); delete partMatrice[partNumber]; updateRowButtons(); markUnsaved(); } } } } else { console.error("Modale #confirmDeleteModal non trovato nel DOM"); alert("Errore: Modale di conferma non trovato. Verifica l'HTML."); // Fallback con confirm nativo if (confirm("Sei sicuro di voler eliminare questa parte?")) { if (partId && partId !== "new") { $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ part_id: partId }), contentType: "application/json", success: function (response) { if (response.success) { $row.remove(); delete partMatrice[partNumber]; updateRowButtons(); markUnsaved(); } else { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } else { $row.remove(); delete partMatrice[partNumber]; updateRowButtons(); markUnsaved(); } } } }); $(document).on("blur", ".part-description, .part-number", function () { const $input = $(this); const $row = $input.closest("tr"); const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); const dateexpiry = $row.find(".part-dateexpiry").val(); const note = $row.data("note") || null; const $saveStatus = $row.find(".save-status"); const $saveLoading = $row.find(".save-loading"); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const isMix = partDescription.startsWith("Mix") ? "Y" : "N"; const partId = $row.data("part-id") || null; const endpoint = idquotations ? "save_parts_quotation.php" : "save_parts.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; if (partDescription && (iddatadb || idquotations)) { $saveLoading.show(); $saveStatus.hide(); $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ ...data, parts: [ { id: partId, part_number: partNumber, part_description: partDescription, mix: isMix, dateexpiry: dateexpiry || null, note: note, }, ], }), contentType: "application/json", success: function (response) { $saveLoading.hide(); if (response.success) { $saveStatus.show(); if (response.part_id) { $row.attr("data-part-id", response.part_id).data( "part-id", response.part_id, ); } setTimeout(() => $saveStatus.hide(), 2000); } else { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { $saveLoading.hide(); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } }); function loadExistingParts(iddatadb, idquotations) { const endpoint = idquotations ? "load_parts_quotation.php" : "load_parts.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; $.ajax({ url: endpoint, method: "GET", data: data, success: function (response) { $("#partsTableBody").empty(); if ( response.success && response.parts && response.parts.length > 0 ) { response.parts.forEach((part) => { console.log( "Caricamento parte:", part.id, "Numero:", part.part_number, "Descrizione:", part.part_description, "idmatrice:", part.idmatrice || "non definito", "note:", part.note || "non definito", "dateexpiry:", part.dateexpiry || "non definito", ); // Escape HTML per evitare problemi con caratteri speciali const escapedDescription = $("
") .text(part.part_description || "") .html(); const escapedNote = part.note ? $("
").text(part.note).html() : ""; const newRow = `
`; $("#partsTableBody").append(newRow); const $select = $("#partsTableBody").find( `tr[data-part-id="${part.id}"] .part-matrice`, ); const selectedMacro = $("#macro-matrice-filter").val() || ""; initializeSelect2( $select, part.part_number, part.id, part.idmatrice || null, selectedMacro, ); if (part.idmatrice) { partMatrice[part.part_number] = part.idmatrice; } }); } else { addNewRow(1, false); // Riga iniziale normale } updateRowButtons(); }, error: function (xhr, status, error) { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); addNewRow(1, false); // Riga iniziale normale }, }); } function loadMacroMatrici() { $.ajax({ url: "get_macro_matrici.php", method: "GET", dataType: "json", success: function (data) { macroMatrici = data.value || []; initializeMacroMatriceFilter(); }, error: function (xhr, status, error) { console.error( "Errore nel caricamento delle MacroMatrici:", error, ); macroMatrici = []; initializeMacroMatriceFilter(); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } function initializeMacroMatriceFilter() { const $select = $("#macro-matrice-filter"); if ($select.length === 0) return; // Ignora se il filtro non è presente nell'HTML if (typeof $.fn.select2 === "undefined") { $select.replaceWith( '', ); return; } // Popola il dropdown MacroMatrice const options = [ { id: "", text: "Tutte le MacroMatrici" }, ...macroMatrici.map(function (macro) { return { id: macro, text: macro }; }), ]; // Distrugge Select2 esistente, se presente if ($select.hasClass("select2-hidden-accessible")) { $select.select2("destroy"); } $select.empty().select2({ placeholder: "Seleziona MacroMatrice", allowClear: true, data: options, dropdownParent: $("#partsModal"), }); $select.on("change", function () { const selectedMacro = $(this).val() || ""; console.log("Filtro MacroMatrice cambiato:", selectedMacro); // Aggiorna global-matrice initializeGlobalSelect2(selectedMacro); // Aggiorna tutti i part-matrice $("#partsTableBody .part-matrice").each(function () { const $partSelect = $(this); const partNumber = $partSelect .closest("tr") .find(".part-number") .val(); const partId = $partSelect.closest("tr").data("part-id"); const currentValue = $partSelect.val() || partMatrice[partNumber] || null; initializeSelect2( $partSelect, partNumber, partId, currentValue, selectedMacro, ); }); }); } function initializeGlobalSelect2(selectedMacro = null) { const $select = $("#global-matrice"); if (typeof $.fn.select2 === "undefined") { $select.replaceWith( '', ); return; } // Filtra le matrici in base alla MacroMatrice selezionata const filteredMatrici = selectedMacro ? matrici.filter( (matrice) => matrice.MacroMatrice === selectedMacro, ) : matrici; // Crea opzioni per Select2 const options = filteredMatrici.map(function (matrice) { return { id: matrice.IdMatrice, text: matrice.NomeMatrice }; }); // Memorizza il valore corrente const currentValue = $select.val(); // Distrugge Select2 esistente, se presente if ($select.hasClass("select2-hidden-accessible")) { $select.select2("destroy"); } // Inizializza Select2 $select.empty().select2({ placeholder: filteredMatrici.length ? "Seleziona matrice globale" : "Nessuna matrice disponibile", allowClear: true, data: options, dropdownParent: $("#partsModal"), minimumResultsForSearch: 0, // Abilita ricerca senza limite minimo di caratteri matcher: function (params, data) { if (!params.term) return data; // Mostra tutte le opzioni se non c'è termine di ricerca const term = params.term.toUpperCase(); if (data.text.toUpperCase().indexOf(term) >= 0) return data; return null; }, }); // Ripristina il valore corrente se valido if ( currentValue && filteredMatrici.some((m) => m.IdMatrice == currentValue) ) { $select.val(currentValue).trigger("change"); } else if (filteredMatrici.length === 0) { const errorMsg = $( '', ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } } function initializeSelect2( $select, partNumber, partId, idmatrice, selectedMacro = null, ) { if (typeof $.fn.select2 === "undefined") { $select.replaceWith( '', ); return; } // Filtra le matrici in base alla MacroMatrice selezionata const filteredMatrici = selectedMacro ? matrici.filter((m) => m.MacroMatrice === selectedMacro) : matrici; // Crea opzioni per Select2, includendo il valore pre-selezionato se non è nel filtro let options = filteredMatrici.map(function (matrice) { return { id: matrice.IdMatrice, text: matrice.NomeMatrice }; }); // Se idmatrice esiste ed è fuori dal filtro, aggiungilo come opzione if ( idmatrice && !filteredMatrici.some((m) => m.IdMatrice == idmatrice) ) { const selectedMatrice = matrici.find( (m) => m.IdMatrice == idmatrice, ); if (selectedMatrice) { options.push({ id: selectedMatrice.IdMatrice, text: selectedMatrice.NomeMatrice, }); } } // Memorizza il valore corrente const currentValue = idmatrice || $select.val(); // Distrugge Select2 esistente, se presente if ($select.hasClass("select2-hidden-accessible")) { $select.select2("destroy"); } // Inizializza Select2 $select.empty().select2({ placeholder: filteredMatrici.length ? "Seleziona matrice" : "Nessuna matrice disponibile", allowClear: true, data: options, dropdownParent: $("#partsModal"), minimumResultsForSearch: 0, // Abilita ricerca senza limite minimo di caratteri matcher: function (params, data) { if (!params.term) return data; // Mostra tutte le opzioni se non c'è termine di ricerca const term = params.term.toUpperCase(); if (data.text.toUpperCase().indexOf(term) >= 0) return data; return null; }, }); // Ripristina il valore se valido if (partId && partId !== "new" && currentValue) { const matrice = matrici.find((m) => m.IdMatrice == currentValue); if (matrice) { const option = new Option( matrice.NomeMatrice, matrice.IdMatrice, true, true, ); $select.append(option).trigger("change"); partMatrice[partNumber] = matrice.IdMatrice; } else { // Aggiusta valore non valido $select.val(null).trigger("change"); partMatrice[partNumber] = null; } } $select.on("change", function () { const idmatrice = $(this).val(); const $row = $(this).closest("tr"); const partId = $row.data("part-id"); const partNumber = $row.find(".part-number").val(); const $saveStatus = $row.find(".save-status"); const $saveLoading = $row.find(".save-loading"); partMatrice[partNumber] = idmatrice || null; if (partId && partId !== "new") { $saveLoading.show(); $saveStatus.hide(); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const endpoint = idquotations ? "save_matrice_quotation.php" : "save_matrice.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ ...data, parts: [{ id: partId, idmatrice: idmatrice || null }], }), contentType: "application/json", success: function (response) { if (response.success) { $saveLoading.hide(); $saveStatus.show(); setTimeout(() => $saveStatus.hide(), 2000); } else { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); $saveLoading.hide(); } }, error: function (xhr, status, error) { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); $saveLoading.hide(); }, }); } }); // Mostra messaggio se non ci sono matrici if (filteredMatrici.length === 0 && selectedMacro) { const errorMsg = $( '', ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } // Debug per verificare inizializzazione console.log( "Inizializzo Select2 per partId:", partId, "con idmatrice:", idmatrice, "Macro:", selectedMacro, ); } $(document).on("click", ".propagate-matrice-btn", function () { const $row = $(this).closest("tr"); const globalVal = $("#global-matrice").val(); if (globalVal) { $row.find(".part-matrice").val(globalVal).trigger("change"); } else { const errorMsg = $( '', ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }); $(document).on("click", ".propagate-all-btn", function () { const globalVal = $("#global-matrice").val(); if (globalVal) { $("#partsTableBody .part-matrice").val(globalVal).trigger("change"); } else { const errorMsg = $( '', ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }); function renumberParts() { console.log( "Inizio rinumera parts, numero righe:", $("#partsTableBody tr").length, ); const $rows = $("#partsTableBody tr"); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const endpoint = idquotations ? "renumber_parts_quotation.php" : "renumber_parts.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; let newPartMatrice = {}; let partsData = $rows .map(function (index) { const $row = $(this); return { partNumber: $row.find(".part-number").val(), partDescription: $row .find(".part-description") .val() .trim(), partId: $row.data("part-id") === "new" || $row.data("part-id") === "" ? null : $row.data("part-id"), note: $row.data("note") || null, dateexpiry: $row.find(".part-dateexpiry").val() || null, }; }) .get(); console.log("Parts to save prima di rinumerazione:", partsData); partsData.forEach((part, index) => { const newNumber = index + 1; newPartMatrice[newNumber] = partMatrice[part.partNumber] || null; part.partNumber = newNumber; }); $rows.each(function (index) { $(this) .find(".part-number") .val(index + 1); }); partMatrice = newPartMatrice; const partsToSave = partsData.map((part, index) => ({ id: part.partId, part_number: index + 1, part_description: part.partDescription, mix: part.partDescription.startsWith("Mix") ? "Y" : "N", idmatrice: partMatrice[index + 1] || null, note: part.note, dateexpiry: part.dateexpiry, })); console.log("Parts to save inviati al server:", partsToSave); $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ ...data, parts: partsToSave }), contentType: "application/json", success: function (response) { console.log("Risposta server:", response); if (response.success && response.part_ids) { $rows.each(function (index) { const $row = $(this); const newPartId = response.part_ids[index] || $row.data("part-id"); if (newPartId) { $row.attr("data-part-id", newPartId).data( "part-id", newPartId, ); } const $saveStatus = $row.find(".save-status"); const $saveLoading = $row.find(".save-loading"); $saveLoading.hide(); $saveStatus.show(); setTimeout(() => $saveStatus.hide(), 2000); }); $rows .find(".part-number, .part-description") .trigger("blur"); unsavedChanges = false; } else { console.error("Errore risposta server:", response.message); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { console.error( "Errore AJAX rinumerazione:", error, xhr.responseText, ); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } $(document).on("click", "#renumberPartsBtn", function (e) { console.log("Clicked Rinumera Parti - Evento triggerato"); e.preventDefault(); renumberParts(); }); function markUnsaved() { if (!unsavedChanges) { unsavedChanges = true; } } $(document).on( "input change", "#partsTableBody input, #partsTableBody select", markUnsaved, ); $(document).on( "click", ".add-row-global, .add-mix-global, .add-mix-row, .remove-row, .propagate-matrice-btn, .propagate-all-btn, .note-btn", markUnsaved, ); // Esporta la funzione loadParts per essere usata da import_Edit2.php window.loadParts = loadParts; }); $(document).on("change", ".propagate-date-input", function () { const dateexpiry = $(this).val(); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const endpoint = idquotations ? "save_parts_quotation.php" : "save_parts.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; const partsToSave = []; $("#partsTableBody tr").each(function () { const $row = $(this); const partId = $row.data("part-id"); const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); const mix = partDescription.startsWith("Mix") ? "Y" : "N"; const idmatrice = $row.find(".part-matrice").val() || null; const note = $row.data("note") || null; partsToSave.push({ id: partId && partId !== "new" ? partId : null, part_number: partNumber, part_description: partDescription, mix: mix, idmatrice: idmatrice, note: note, dateexpiry: dateexpiry || null, }); if (partId && partId !== "new") { $row.find(".save-loading").show(); $row.find(".save-status").hide(); } }); if (partsToSave.length > 0) { $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ ...data, parts: partsToSave, }), contentType: "application/json", success: function (response) { $("#partsTableBody tr").each(function () { const $row = $(this); const partId = $row.data("part-id"); if (partId && partId !== "new") { $row.find(".save-loading").hide(); $row.find(".save-status").show(); setTimeout( () => $row.find(".save-status").hide(), 2000, ); } $row.find(".part-dateexpiry").val(dateexpiry); }); if (!response.success) { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { $("#partsTableBody tr").each(function () { const $row = $(this); $row.find(".save-loading").hide(); }); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } else { $("#partsTableBody tr").find(".part-dateexpiry").val(dateexpiry); markUnsaved(); } }); $(document).on("click", ".propagate-note-btn", function () { const $commonNoteModal = $("#commonNoteModal"); $commonNoteModal.find(".part-note").val(""); const modalInstance = new bootstrap.Modal($commonNoteModal[0], { backdrop: "static", keyboard: false, }); modalInstance.show(); }); $(document).on("click", ".save-common-note-btn", function () { const $commonNoteModal = $("#commonNoteModal"); const note = $commonNoteModal.find(".part-note").val().trim(); const iddatadb = $("#partsModal").data("iddatadb"); const idquotations = $("#partsModal").data("idquotations"); const endpoint = idquotations ? "save_parts_quotation.php" : "save_parts.php"; const data = idquotations ? { idquotations: idquotations } : { iddatadb: iddatadb }; const partsToSave = []; $("#partsTableBody tr").each(function () { const $row = $(this); const partId = $row.data("part-id"); const partNumber = $row.find(".part-number").val(); const partDescription = $row.find(".part-description").val().trim(); const mix = partDescription.startsWith("Mix") ? "Y" : "N"; const idmatrice = $row.find(".part-matrice").val() || null; const dateexpiry = $row.find(".part-dateexpiry").val() || null; partsToSave.push({ id: partId && partId !== "new" ? partId : null, part_number: partNumber, part_description: partDescription, mix: mix, idmatrice: idmatrice, note: note || null, dateexpiry: dateexpiry, }); if (partId && partId !== "new") { $row.find(".save-loading").show(); $row.find(".save-status").hide(); } }); if (partsToSave.length > 0) { $.ajax({ url: endpoint, method: "POST", data: JSON.stringify({ ...data, parts: partsToSave, }), contentType: "application/json", success: function (response) { $("#partsTableBody tr").each(function () { const $row = $(this); const partId = $row.data("part-id"); if (partId && partId !== "new") { $row.find(".save-loading").hide(); $row.find(".save-status").show(); setTimeout( () => $row.find(".save-status").hide(), 2000, ); } $row.data("note", note); $row.find(".note-btn").toggleClass("has-note", !!note); }); bootstrap.Modal.getInstance( document.getElementById("commonNoteModal"), ).hide(); if (!response.success) { const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); } }, error: function (xhr, status, error) { $("#partsTableBody tr").each(function () { const $row = $(this); $row.find(".save-loading").hide(); }); const errorMsg = $( '", ); $("#partsModal .modal-body").prepend(errorMsg); setTimeout(function () { errorMsg.fadeOut(500, function () { $(this).remove(); }); }, 5000); }, }); } else { $("#partsTableBody tr").each(function () { const $row = $(this); $row.data("note", note); $row.find(".note-btn").toggleClass("has-note", !!note); }); bootstrap.Modal.getInstance( document.getElementById("commonNoteModal"), ).hide(); markUnsaved(); } });