$(document).ready(function () {
// ===================
// GLOBAL STATE
// ===================
let partMatrice = {};
let unsavedChanges = false;
let matrici = [];
let macroMatrici = [];
let quotations = [];
let partsExtraField = null; // {field_id, field_label} oppure null
let extraFieldOptions = []; // [{id,label}]
// --- ROW ID helpers: niente più cache impazzita di jQuery .data() ---
function getPartId($row) {
// Legge in ordine: cache jQuery, nostra cache, attributo
const d = $row.data("part-id");
const ours = $row.data("__pid");
const attr = $row.attr("data-part-id");
// Scegli il primo definito
const id =
d !== undefined
? d
: ours !== undefined
? ours
: attr !== undefined
? attr
: null;
// Normalizza: "new" o "" NON sono ID validi
return id === "new" || id === "" || id === null ? null : id;
}
function loadPartsExtraField(iddatadb, done) {
partsExtraField = null;
if (!iddatadb) return done();
$.ajax({
url: "get_parts_extra_field.php",
method: "GET",
dataType: "json",
data: { iddatadb },
success: function (res) {
partsExtraField =
res && res.success && res.field ? res.field : null;
if (
partsExtraField &&
(partsExtraField.data_type || "").toLowerCase() ===
"sceltamultipla"
) {
loadExtraFieldOptions(
String(partsExtraField.field_id),
function () {
applyExtraFieldColumn();
done();
},
);
} else {
applyExtraFieldColumn();
done();
}
},
error: function () {
partsExtraField = null;
applyExtraFieldColumn();
done();
},
});
}
function loadExtraFieldOptions(fieldId, done) {
extraFieldOptions = [];
if (!fieldId) return done();
$.ajax({
url: "get_customfield_values.php",
method: "GET",
dataType: "json",
data: { field_ids: fieldId },
success: function (res) {
const arr = res && res[fieldId] ? res[fieldId] : [];
extraFieldOptions = (arr || []).map((x) => ({
id: x.IdCustomFieldsValue,
label: x.Valore,
}));
done();
},
error: function () {
extraFieldOptions = [];
done();
},
});
}
function buildExtraFieldCellHtml($row = null) {
if (!partsExtraField) return "";
const selectedValueId = $row ? $row.data("extra-value-id") || "" : "";
// SceltaMultipla -> select + hidden value id
if (
(partsExtraField.data_type || "").toLowerCase() === "sceltamultipla"
) {
const opts = [``]
.concat(
extraFieldOptions.map(
(o) => ``,
),
)
.join("");
return `
`;
}
// Testo -> input + hidden field_id
return `
`;
}
$(document).on("change", ".part-extra-select", function () {
const valId = $(this).val() || "";
const $row = $(this).closest("tr");
// salva nello stato della riga (così sopravvive ai re-render)
$row.data("extra-value-id", valId);
$(this).closest("td").find(".part-extra-value-id").val(valId);
saveRow($row);
});
function applyExtraFieldColumn() {
const $theadRow = $("#partsTable thead tr");
// 1) rimuovi header extra
$theadRow.find("th.extra-field-th").remove();
// 2) rimuovi celle extra esistenti
$("#partsTableBody tr").each(function () {
$(this).find("td.extra-field-td").remove();
});
// 3) se non c'è campo extra -> fine
if (!partsExtraField) return;
// 4) inserisci header prima di "Azioni" (ultima colonna)
$theadRow
.find("th:last")
.before(
`
${partsExtraField.field_label}
`,
);
// 5) aggiungi cella a ogni riga già presente (una sola volta)
$("#partsTableBody tr").each(function () {
const $row = $(this);
$row.find("td:last").before(buildExtraFieldCellHtml($row));
// ripristina selezione se già presente nello stato riga
const selected = ($row.data("extra-value-id") || "").toString();
if (selected) {
$row.find(".part-extra-value-id").val(selected);
$row.find(".part-extra-select").val(selected);
}
});
}
function setPartId($row, id) {
if (!id) return;
// Sincronizza TUTTO: attributo + cache jQuery + nostra cache
$row.attr("data-part-id", id);
$row.data("part-id", id); // <<< fondamentale per invalidare la cache di jQuery
$row.data("__pid", id);
}
// ===================
// 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, callback = null) {
if (iddatadb) {
if (matrici.length === 0) {
$.ajax({
url: "get_matrici_db.php",
method: "GET",
dataType: "json",
success: function (data) {
matrici = data.value || [];
loadMacroMatrici();
initializeGlobalSelect2();
loadPartsExtraField(iddatadb, function () {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
});
},
error: function (xhr, status, error) {
matrici = [];
loadMacroMatrici();
initializeGlobalSelect2();
loadPartsExtraField(iddatadb, function () {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
});
const errorMsg = $(
'
Errore nel caricamento delle matrici: ' +
error +
" (" +
xhr.status +
")
",
);
$("#partsModal .modal-body").prepend(errorMsg);
setTimeout(function () {
errorMsg.fadeOut(500, function () {
$(this).remove();
});
}, 5000);
},
});
} else {
loadMacroMatrici();
initializeGlobalSelect2();
loadPartsExtraField(iddatadb, function () {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
});
}
} else {
loadPartsExtraField(iddatadb, function () {
loadPhoto(iddatadb, idquotations);
loadExistingParts(iddatadb, idquotations, callback);
});
}
}
// 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 = $(
'