fixed api

This commit is contained in:
2026-10-15 20:55:05 +02:00
parent e6c8549cea
commit 65f02b9212
2 changed files with 142 additions and 83 deletions
@@ -43,6 +43,7 @@ class VisualLimsApiClient
{ {
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate"); $ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'VisualLims-Client/1.0');
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([ curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'Username' => $this->username, 'Username' => $this->username,
@@ -105,6 +106,7 @@ class VisualLimsApiClient
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'VisualLims-Client/1.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, [ curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}", "Authorization: Bearer {$token}",
"Accept: application/json" "Accept: application/json"
@@ -144,6 +146,7 @@ class VisualLimsApiClient
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'VisualLims-Client/1.0');
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [ curl_setopt($ch, CURLOPT_HTTPHEADER, [
@@ -177,6 +180,7 @@ class VisualLimsApiClient
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'VisualLims-Client/1.0');
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH"); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload)); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [ curl_setopt($ch, CURLOPT_HTTPHEADER, [
@@ -225,6 +229,7 @@ class VisualLimsApiClient
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'VisualLims-Client/1.0');
curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [ curl_setopt($ch, CURLOPT_HTTPHEADER, [
@@ -268,6 +273,7 @@ class VisualLimsApiClient
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT, 'VisualLims-Client/1.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, [ curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}", "Authorization: Bearer {$token}",
"Accept: */*" "Accept: */*"
+136 -83
View File
@@ -43,17 +43,23 @@ document.addEventListener("DOMContentLoaded", () => {
* Clear all validation-error highlights from the grid. * Clear all validation-error highlights from the grid.
*/ */
function clearValidationErrors() { function clearValidationErrors() {
document.querySelectorAll(".grid-cell.validation-error").forEach((cell) => { document
cell.classList.remove("validation-error"); .querySelectorAll(".grid-cell.validation-error")
cell.querySelectorAll(".input-validation-error").forEach((el) => { .forEach((cell) => {
el.classList.remove("input-validation-error"); cell.classList.remove("validation-error");
cell.querySelectorAll(".input-validation-error").forEach(
(el) => {
el.classList.remove("input-validation-error");
},
);
const tooltip = cell.querySelector(".validation-tooltip");
if (tooltip) tooltip.remove();
});
document
.querySelectorAll(".grid-row.validation-row-error")
.forEach((row) => {
row.classList.remove("validation-row-error");
}); });
const tooltip = cell.querySelector(".validation-tooltip");
if (tooltip) tooltip.remove();
});
document.querySelectorAll(".grid-row.validation-row-error").forEach((row) => {
row.classList.remove("validation-row-error");
});
// Also clear batch-row-error that came from validation // Also clear batch-row-error that came from validation
clearAllRowErrors(); clearAllRowErrors();
} }
@@ -90,16 +96,24 @@ document.addEventListener("DOMContentLoaded", () => {
} }
}); });
if (targetIndex) { if (targetIndex) {
const rowIndex = row.querySelector(".grid-cell[data-row]")?.getAttribute("data-row"); const rowIndex = row
.querySelector(".grid-cell[data-row]")
?.getAttribute("data-row");
if (rowIndex !== null) { if (rowIndex !== null) {
cell = row.querySelector(`.grid-cell[data-row="${rowIndex}"][data-index="${targetIndex}"]`); cell = row.querySelector(
`.grid-cell[data-row="${rowIndex}"][data-index="${targetIndex}"]`,
);
} }
} }
} else { } else {
// Direct data-col match // Direct data-col match
const rowIndex = row.querySelector(".grid-cell[data-row]")?.getAttribute("data-row"); const rowIndex = row
.querySelector(".grid-cell[data-row]")
?.getAttribute("data-row");
if (rowIndex !== null) { if (rowIndex !== null) {
cell = row.querySelector(`.grid-cell[data-col="${err.field}"][data-row="${rowIndex}"]`); cell = row.querySelector(
`.grid-cell[data-col="${err.field}"][data-row="${rowIndex}"]`,
);
} }
} }
@@ -194,8 +208,9 @@ document.addEventListener("DOMContentLoaded", () => {
* Show the result of a single export in the response modal. * Show the result of a single export in the response modal.
*/ */
function showExportResult(data) { function showExportResult(data) {
const responseModalElement = const responseModalElement = document.getElementById(
document.getElementById("exportResponseModal"); "exportResponseModal",
);
if (!responseModalElement) { if (!responseModalElement) {
alert("Errore: Modale di risposta non trovato"); alert("Errore: Modale di risposta non trovato");
return; return;
@@ -276,20 +291,28 @@ document.addEventListener("DOMContentLoaded", () => {
errorEl.className = "batch-error-msg"; errorEl.className = "batch-error-msg";
errorEl.textContent = "⚠ Errore — clicca per dettagli"; errorEl.textContent = "⚠ Errore — clicca per dettagli";
errorEl.addEventListener("click", () => { errorEl.addEventListener("click", () => {
document.getElementById("exportResponseMessage").innerHTML = message.replace(/\n/g, "<br>"); document.getElementById("exportResponseMessage").innerHTML =
document.getElementById("exportResponseModalLabel").textContent = "Errore Validazione (id: " + iddatadb + ")"; message.replace(/\n/g, "<br>");
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show(); document.getElementById(
"exportResponseModalLabel",
).textContent = "Errore Validazione (id: " + iddatadb + ")";
new bootstrap.Modal(
document.getElementById("exportResponseModal"),
{ keyboard: false },
).show();
}); });
btnCell.appendChild(errorEl); btnCell.appendChild(errorEl);
} }
} }
function clearAllRowErrors() { function clearAllRowErrors() {
document.querySelectorAll(".grid-row.batch-row-error").forEach((row) => { document
row.classList.remove("batch-row-error"); .querySelectorAll(".grid-row.batch-row-error")
const msg = row.querySelector(".batch-error-msg"); .forEach((row) => {
if (msg) msg.remove(); row.classList.remove("batch-row-error");
}); const msg = row.querySelector(".batch-error-msg");
if (msg) msg.remove();
});
} }
function disableAllRowButtons() { function disableAllRowButtons() {
@@ -333,15 +356,23 @@ document.addEventListener("DOMContentLoaded", () => {
// Show validating state on the row // Show validating state on the row
setRowExporting(gridRow, true); setRowExporting(gridRow, true);
const spinner = gridRow.querySelector(".batch-row-spinner"); const spinner = gridRow.querySelector(".batch-row-spinner");
if (spinner) spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Validating...'; if (spinner)
spinner.innerHTML =
'<i class="fas fa-spinner fa-spin"></i> Validating...';
validateRows([{ iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) }]) validateRows([
{ iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) },
])
.then((validationData) => { .then((validationData) => {
setRowExporting(gridRow, false); setRowExporting(gridRow, false);
gridRow.classList.remove("batch-disabled"); gridRow.classList.remove("batch-disabled");
if (!validationData.success) { if (!validationData.success) {
showExportResult({ success: false, message: validationData.message || "Errore di validazione" }); showExportResult({
success: false,
message:
validationData.message || "Errore di validazione",
});
return; return;
} }
@@ -360,7 +391,10 @@ document.addEventListener("DOMContentLoaded", () => {
gridRow.classList.remove("batch-disabled"); gridRow.classList.remove("batch-disabled");
console.error("Validation error:", error); console.error("Validation error:", error);
showExportResult({ success: false, message: "Errore di validazione: " + error.message }); showExportResult({
success: false,
message: "Errore di validazione: " + error.message,
});
}); });
} }
@@ -402,7 +436,11 @@ document.addEventListener("DOMContentLoaded", () => {
gridRow.classList.remove("batch-disabled"); gridRow.classList.remove("batch-disabled");
if (!data.success) { if (!data.success) {
showRowError(gridRow, iddatadb, data.message || "Errore sconosciuto"); showRowError(
gridRow,
iddatadb,
data.message || "Errore sconosciuto",
);
} }
showExportResult(data); showExportResult(data);
} catch (error) { } catch (error) {
@@ -577,8 +615,9 @@ document.addEventListener("DOMContentLoaded", () => {
document.getElementById("exportResponseMessage").innerHTML = document.getElementById("exportResponseMessage").innerHTML =
`Nessuna riga valida per l'esportazione.<br>` + `Nessuna riga valida per l'esportazione.<br>` +
`<strong>${invalidCount}</strong> righe con errori di validazione.`; `<strong>${invalidCount}</strong> righe con errori di validazione.`;
document.getElementById("exportResponseModalLabel").textContent = document.getElementById(
"Validazione Fallita"; "exportResponseModalLabel",
).textContent = "Validazione Fallita";
new bootstrap.Modal( new bootstrap.Modal(
document.getElementById("exportResponseModal"), document.getElementById("exportResponseModal"),
{ keyboard: false }, { keyboard: false },
@@ -595,27 +634,38 @@ document.addEventListener("DOMContentLoaded", () => {
if (invalidCount > 0) { if (invalidCount > 0) {
countText += ` (${invalidCount} escluse per errori di validazione)`; countText += ` (${invalidCount} escluse per errori di validazione)`;
} }
document.getElementById("exportBatchCount").textContent = countText; document.getElementById("exportBatchCount").textContent =
countText;
confirmModal.show(); confirmModal.show();
const confirmBtn = document.getElementById("exportBatchConfirmBtn"); const confirmBtn = document.getElementById(
"exportBatchConfirmBtn",
);
if (pendingBatchConfirmHandler) { if (pendingBatchConfirmHandler) {
confirmBtn.removeEventListener("click", pendingBatchConfirmHandler); confirmBtn.removeEventListener(
"click",
pendingBatchConfirmHandler,
);
} }
pendingBatchConfirmHandler = () => { pendingBatchConfirmHandler = () => {
pendingBatchConfirmHandler = null; pendingBatchConfirmHandler = null;
confirmModal.hide(); confirmModal.hide();
startBatchExport(validRows); startBatchExport(validRows);
}; };
confirmBtn.addEventListener("click", pendingBatchConfirmHandler, { once: true }); confirmBtn.addEventListener(
"click",
pendingBatchConfirmHandler,
{ once: true },
);
}) })
.catch((error) => { .catch((error) => {
showValidationSpinner(false); showValidationSpinner(false);
console.error("Batch validation error:", error); console.error("Batch validation error:", error);
document.getElementById("exportResponseMessage").textContent = document.getElementById("exportResponseMessage").textContent =
"Errore di validazione: " + error.message; "Errore di validazione: " + error.message;
document.getElementById("exportResponseModalLabel").textContent = document.getElementById(
"Errore Validazione"; "exportResponseModalLabel",
).textContent = "Errore Validazione";
new bootstrap.Modal( new bootstrap.Modal(
document.getElementById("exportResponseModal"), document.getElementById("exportResponseModal"),
{ keyboard: false }, { keyboard: false },
@@ -635,54 +685,51 @@ document.addEventListener("DOMContentLoaded", () => {
); );
unsavedModal.show(); unsavedModal.show();
document document.getElementById("batchSaveAndExportBtn").addEventListener(
.getElementById("batchSaveAndExportBtn") "click",
.addEventListener( () => {
"click", unsavedModal.hide();
() => { // Trigger Save All, then proceed
unsavedModal.hide(); const saveAllEl = document.querySelector(".save-all-btn");
// Trigger Save All, then proceed if (!saveAllEl) return;
const saveAllEl =
document.querySelector(".save-all-btn");
if (!saveAllEl) return;
// Watch for all .cell-changed to disappear // Watch for all .cell-changed to disappear
const observer = new MutationObserver(() => { const observer = new MutationObserver(() => {
if (!hasUnsavedChanges()) { if (!hasUnsavedChanges()) {
observer.disconnect(); observer.disconnect();
const eligibleRows = collectEligibleRows(); const eligibleRows = collectEligibleRows();
if (eligibleRows.length === 0) { if (eligibleRows.length === 0) {
document.getElementById(
"exportResponseMessage",
).textContent =
"Tutte le righe sono già state esportate al LIMS.";
document.getElementById(
"exportResponseModalLabel",
).textContent = "Export All";
new bootstrap.Modal(
document.getElementById( document.getElementById(
"exportResponseMessage", "exportResponseModal",
).textContent = ),
"Tutte le righe sono già state esportate al LIMS."; { keyboard: false },
document.getElementById( ).show();
"exportResponseModalLabel", return;
).textContent = "Export All";
new bootstrap.Modal(
document.getElementById(
"exportResponseModal",
),
{ keyboard: false },
).show();
return;
}
showBatchConfirm(eligibleRows);
} }
}); showBatchConfirm(eligibleRows);
observer.observe( }
document.querySelector(".grid-container"), });
{ observer.observe(
subtree: true, document.querySelector(".grid-container"),
attributes: true, {
attributeFilter: ["class"], subtree: true,
}, attributes: true,
); attributeFilter: ["class"],
},
);
saveAllEl.click(); saveAllEl.click();
}, },
{ once: true }, { once: true },
); );
return; return;
} }
@@ -754,7 +801,11 @@ document.addEventListener("DOMContentLoaded", () => {
succeeded++; succeeded++;
} else { } else {
failed++; failed++;
showRowError(row, iddatadb, data.message || "Errore sconosciuto"); showRowError(
row,
iddatadb,
data.message || "Errore sconosciuto",
);
} }
} catch (error) { } catch (error) {
processed++; processed++;
@@ -794,7 +845,9 @@ document.addEventListener("DOMContentLoaded", () => {
const modalEl = document.getElementById("exportResponseModal"); const modalEl = document.getElementById("exportResponseModal");
const modal = new bootstrap.Modal(modalEl, { keyboard: false }); const modal = new bootstrap.Modal(modalEl, { keyboard: false });
modal.show(); modal.show();
modalEl.addEventListener("hidden.bs.modal", cleanupBackdrop, { once: true }); modalEl.addEventListener("hidden.bs.modal", cleanupBackdrop, {
once: true,
});
})(); })();
} }
}); });