diff --git a/public/userarea/matrici.php b/public/userarea/matrici.php
index ad6d9db4..5d1cdc44 100644
--- a/public/userarea/matrici.php
+++ b/public/userarea/matrici.php
@@ -64,6 +64,7 @@ function revisionLabel($rev)
+
@@ -125,6 +126,57 @@ function revisionLabel($rev)
text-align: center;
}
+ /* paginazione tabella: pulsanti distanziati e cliccabili */
+ .dataTables_wrapper .dataTables_paginate {
+ margin-top: 12px;
+ }
+
+ .dataTables_wrapper .dataTables_paginate .pagination {
+ flex-wrap: wrap;
+ gap: 4px;
+ justify-content: flex-end;
+ }
+
+ .dataTables_wrapper .dataTables_paginate .page-link,
+ .dataTables_wrapper .dataTables_paginate a.paginate_button {
+ display: inline-block;
+ min-width: 40px;
+ padding: 8px 12px;
+ margin: 0 2px;
+ border-radius: 10px !important;
+ border: 1px solid #dee2e6;
+ text-align: center;
+ cursor: pointer;
+ user-select: none;
+ }
+
+ .dataTables_wrapper .dataTables_paginate .page-item.active .page-link,
+ .dataTables_wrapper .dataTables_paginate a.paginate_button.current {
+ background: #198754;
+ border-color: #198754;
+ color: #fff;
+ }
+
+ .dataTables_wrapper .dataTables_paginate .page-item.disabled .page-link,
+ .dataTables_wrapper .dataTables_paginate a.paginate_button.disabled {
+ opacity: .5;
+ cursor: default;
+ }
+
+ @keyframes rowFlash {
+ from {
+ background-color: #fde68a;
+ }
+
+ to {
+ background-color: transparent;
+ }
+ }
+
+ #tabellaMatrici tbody tr.row-flash>td {
+ animation: rowFlash 1.8s ease-out;
+ }
+
.action-btn {
border: none;
background: none;
@@ -1371,10 +1423,109 @@ function revisionLabel($rev)
$("#filePreviewContainer").html(html);
- const modal = new bootstrap.Modal(document.getElementById('filePreviewModal'));
+ const modal = bootstrap.Modal.getOrCreateInstance(document.getElementById('filePreviewModal'));
modal.show();
}
+ /* ---------- aggiornamento tabella senza ricaricare la pagina ----------
+ * Dopo un salvataggio rilegge la pagina in background e aggiorna solo le righe cambiate:
+ * ricerca, pagina e ordinamento restano dove l'utente li aveva lasciati. */
+ function rowId(tr) {
+ const b = tr && tr.querySelector('.action-btn.edit[data-id]');
+ return b ? String(b.getAttribute('data-id')) : null;
+ }
+
+ function hideTooltips() {
+ document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => {
+ const t = bootstrap.Tooltip.getInstance(el);
+ if (t) t.hide();
+ });
+ document.querySelectorAll('.tooltip').forEach(el => el.remove());
+ }
+
+ function toastOk(msg) {
+ Swal.fire({
+ toast: true,
+ position: 'top-end',
+ icon: 'success',
+ title: msg || 'Salvato',
+ showConfirmButton: false,
+ timer: 1600
+ });
+ }
+
+ function flashRow(tr) {
+ if (!tr) return;
+ tr.classList.remove('row-flash');
+ void tr.offsetWidth;
+ tr.classList.add('row-flash');
+ }
+
+ // focusId: riga da evidenziare; se è nuova e non visibile con il filtro attuale, cerco il suo nome
+ function refreshTable(focusId) {
+ const dt = $('#tabellaMatrici').DataTable();
+ return fetch(location.href, {
+ cache: 'no-store',
+ credentials: 'same-origin'
+ })
+ .then(r => r.text())
+ .then(html => {
+ const doc = new DOMParser().parseFromString(html, 'text/html');
+ const fresh = {};
+ doc.querySelectorAll('#tabellaMatrici tbody tr').forEach(tr => {
+ const id = rowId(tr);
+ if (id) fresh[id] = tr;
+ });
+ if (!Object.keys(fresh).length) return location.reload(); // risposta inattesa: ricarico come prima
+
+ hideTooltips();
+ const seen = {};
+ const added = [];
+ const gone = [];
+ dt.rows().every(function() {
+ const tr = this.node();
+ const id = rowId(tr);
+ if (!id || !fresh[id]) { // eliminata (o riga "Nessuna matrice presente")
+ gone.push(tr);
+ return;
+ }
+ seen[id] = true;
+ const nw = fresh[id];
+ if (tr.innerHTML !== nw.innerHTML) {
+ Array.from(nw.cells).forEach((td, i) => {
+ const old = tr.cells[i];
+ if (!old) return;
+ old.innerHTML = td.innerHTML;
+ if (td.hasAttribute('title')) old.setAttribute('title', td.getAttribute('title'));
+ });
+ this.invalidate('dom');
+ flashRow(tr);
+ }
+ });
+ gone.forEach(tr => dt.row(tr).remove());
+ Object.keys(fresh).forEach(id => {
+ if (!seen[id]) {
+ const node = document.importNode(fresh[id], true);
+ dt.row.add(node);
+ added.push(node);
+ }
+ });
+ dt.draw(false);
+ added.forEach(flashRow);
+
+ // matrice nuova: la mostro (filtrando per il suo nome se non è in pagina)
+ if (focusId && fresh[focusId]) {
+ const node = dt.rows().nodes().toArray().find(tr => rowId(tr) === String(focusId));
+ if (node && !document.body.contains(node)) {
+ const nome = (fresh[focusId].cells[1] && fresh[focusId].cells[1].textContent.trim()) || '';
+ if (nome) dt.search(nome).draw();
+ }
+ flashRow(node);
+ }
+ })
+ .catch(() => location.reload());
+ }
+
$(document).ready(function() {
$('#tabellaMatrici').DataTable({
@@ -1477,12 +1628,24 @@ function revisionLabel($rev)
})
.then(r => r.json())
.then(data => {
- Swal.fire({
- icon: data.success ? "success" : "error",
- title: data.success ? "Salvato!" : "Errore",
- text: data.message || ""
- }).then(() => location.reload());
- });
+ if (!data.success) {
+ Swal.fire({
+ icon: "error",
+ title: "Errore",
+ text: data.message || ""
+ });
+ return;
+ }
+ bootstrap.Modal.getOrCreateInstance(document.getElementById('addMatriceModal')).hide();
+ toastOk(data.message || "Matrice salvata");
+ // id della matrice nuova (se il salvataggio lo restituisce), altrimenti quella modificata
+ refreshTable(data.id || data.idmatrice || id || null);
+ })
+ .catch(() => Swal.fire({
+ icon: "error",
+ title: "Errore",
+ text: "Errore di comunicazione con il server"
+ }));
});
$(document).on("click", ".delete", function() {
@@ -1500,17 +1663,26 @@ function revisionLabel($rev)
fetch("delete_matrice.php?id=" + encodeURIComponent(id))
.then(r => r.json())
.then(data => {
- Swal.fire({
- icon: data.success ? "success" : "error",
- title: data.success ? "Eliminata!" : "Errore",
- text: data.message || ""
- }).then(() => location.reload());
+ if (!data.success) {
+ Swal.fire({
+ icon: "error",
+ title: "Errore",
+ text: data.message || ""
+ });
+ return;
+ }
+ toastOk(data.message || "Matrice eliminata");
+ refreshTable();
});
}
});
});
- document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(el => new bootstrap.Tooltip(el));
+ new bootstrap.Tooltip(document.body, {
+ selector: '[data-bs-toggle="tooltip"]',
+ trigger: 'hover'
+ });
+ document.addEventListener('show.bs.modal', hideTooltips);
$('#ml_linee').select2({
theme: 'bootstrap-5',
@@ -1547,7 +1719,7 @@ function revisionLabel($rev)
});
$sel.val((data.selected_ids || []).map(String)).trigger("change");
- new bootstrap.Modal(document.getElementById('matriceLineeModal')).show();
+ bootstrap.Modal.getOrCreateInstance(document.getElementById('matriceLineeModal')).show();
});
});
@@ -1565,16 +1737,17 @@ function revisionLabel($rev)
})
.then(r => r.json())
.then(data => {
- Swal.fire({
- icon: data.success ? "success" : "error",
- title: data.success ? "Salvato!" : "Errore",
- text: data.message || ""
- }).then(() => {
- if (data.success) {
- bootstrap.Modal.getInstance(document.getElementById('matriceLineeModal')).hide();
- location.reload();
- }
- });
+ if (!data.success) {
+ Swal.fire({
+ icon: "error",
+ title: "Errore",
+ text: data.message || ""
+ });
+ return;
+ }
+ bootstrap.Modal.getOrCreateInstance(document.getElementById('matriceLineeModal')).hide();
+ toastOk(data.message || "Linee salvate");
+ refreshTable(idmatrice);
});
});
@@ -1599,9 +1772,7 @@ function revisionLabel($rev)
const loadingOption = new Option("Caricamento mescole...", "", false, false);
$sel.append(loadingOption).trigger("change");
- const modalEl = document.getElementById('matriceMescoleModal');
- const modal = new bootstrap.Modal(modalEl);
- modal.show();
+ bootstrap.Modal.getOrCreateInstance(document.getElementById('matriceMescoleModal')).show();
fetch("get_matrice_mescole.php?id=" + encodeURIComponent(idmatrice))
.then(r => {
@@ -1652,16 +1823,17 @@ function revisionLabel($rev)
})
.then(r => r.json())
.then(data => {
- Swal.fire({
- icon: data.success ? "success" : "error",
- title: data.success ? "Salvato!" : "Errore",
- text: data.message || ""
- }).then(() => {
- if (data.success) {
- bootstrap.Modal.getInstance(document.getElementById('matriceMescoleModal')).hide();
- location.reload();
- }
- });
+ if (!data.success) {
+ Swal.fire({
+ icon: "error",
+ title: "Errore",
+ text: data.message || ""
+ });
+ return;
+ }
+ bootstrap.Modal.getOrCreateInstance(document.getElementById('matriceMescoleModal')).hide();
+ toastOk(data.message || "Mescole salvate");
+ refreshTable(idmatrice);
});
});
@@ -1678,7 +1850,7 @@ function revisionLabel($rev)
$("#mlm_list").html(items.map(x => `${escapeHtml(x)}`).join(''));
}
- new bootstrap.Modal(document.getElementById('mescoleListModal')).show();
+ bootstrap.Modal.getOrCreateInstance(document.getElementById('mescoleListModal')).show();
});
$(document).on("click", ".files", function() {
@@ -1692,7 +1864,7 @@ function revisionLabel($rev)
renderSelectedAttachments();
loadExistingAttachments(idmatrice);
- new bootstrap.Modal(document.getElementById('matriceFilesModal')).show();
+ bootstrap.Modal.getOrCreateInstance(document.getElementById('matriceFilesModal')).show();
});
$("#mf_pick_files_btn, #attachmentsDropzone").on("click", function() {