diff --git a/public/userarea/class/VisualLimsApiClient.class.php b/public/userarea/class/VisualLimsApiClient.class.php
index 369d5857..a040e851 100644
--- a/public/userarea/class/VisualLimsApiClient.class.php
+++ b/public/userarea/class/VisualLimsApiClient.class.php
@@ -43,6 +43,7 @@ class VisualLimsApiClient
{
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
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_POSTFIELDS, json_encode([
'Username' => $this->username,
@@ -105,6 +106,7 @@ class VisualLimsApiClient
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_USERAGENT, 'VisualLims-Client/1.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
"Accept: application/json"
@@ -144,6 +146,7 @@ class VisualLimsApiClient
$ch = curl_init($url);
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_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
@@ -177,6 +180,7 @@ class VisualLimsApiClient
$ch = curl_init($url);
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_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
@@ -225,6 +229,7 @@ class VisualLimsApiClient
$ch = curl_init($url);
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_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
@@ -268,6 +273,7 @@ class VisualLimsApiClient
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_USERAGENT, 'VisualLims-Client/1.0');
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
"Accept: */*"
diff --git a/public/userarea/export_to_lims.js b/public/userarea/export_to_lims.js
index cf28fad0..0f043bb3 100644
--- a/public/userarea/export_to_lims.js
+++ b/public/userarea/export_to_lims.js
@@ -43,17 +43,23 @@ document.addEventListener("DOMContentLoaded", () => {
* Clear all validation-error highlights from the grid.
*/
function clearValidationErrors() {
- document.querySelectorAll(".grid-cell.validation-error").forEach((cell) => {
- cell.classList.remove("validation-error");
- cell.querySelectorAll(".input-validation-error").forEach((el) => {
- el.classList.remove("input-validation-error");
+ document
+ .querySelectorAll(".grid-cell.validation-error")
+ .forEach((cell) => {
+ 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
clearAllRowErrors();
}
@@ -90,16 +96,24 @@ document.addEventListener("DOMContentLoaded", () => {
}
});
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) {
- cell = row.querySelector(`.grid-cell[data-row="${rowIndex}"][data-index="${targetIndex}"]`);
+ cell = row.querySelector(
+ `.grid-cell[data-row="${rowIndex}"][data-index="${targetIndex}"]`,
+ );
}
}
} else {
// 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) {
- 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.
*/
function showExportResult(data) {
- const responseModalElement =
- document.getElementById("exportResponseModal");
+ const responseModalElement = document.getElementById(
+ "exportResponseModal",
+ );
if (!responseModalElement) {
alert("Errore: Modale di risposta non trovato");
return;
@@ -276,20 +291,28 @@ document.addEventListener("DOMContentLoaded", () => {
errorEl.className = "batch-error-msg";
errorEl.textContent = "⚠ Errore — clicca per dettagli";
errorEl.addEventListener("click", () => {
- document.getElementById("exportResponseMessage").innerHTML = message.replace(/\n/g, "
");
- document.getElementById("exportResponseModalLabel").textContent = "Errore Validazione (id: " + iddatadb + ")";
- new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
+ document.getElementById("exportResponseMessage").innerHTML =
+ message.replace(/\n/g, "
");
+ document.getElementById(
+ "exportResponseModalLabel",
+ ).textContent = "Errore Validazione (id: " + iddatadb + ")";
+ new bootstrap.Modal(
+ document.getElementById("exportResponseModal"),
+ { keyboard: false },
+ ).show();
});
btnCell.appendChild(errorEl);
}
}
function clearAllRowErrors() {
- document.querySelectorAll(".grid-row.batch-row-error").forEach((row) => {
- row.classList.remove("batch-row-error");
- const msg = row.querySelector(".batch-error-msg");
- if (msg) msg.remove();
- });
+ document
+ .querySelectorAll(".grid-row.batch-row-error")
+ .forEach((row) => {
+ row.classList.remove("batch-row-error");
+ const msg = row.querySelector(".batch-error-msg");
+ if (msg) msg.remove();
+ });
}
function disableAllRowButtons() {
@@ -333,15 +356,23 @@ document.addEventListener("DOMContentLoaded", () => {
// Show validating state on the row
setRowExporting(gridRow, true);
const spinner = gridRow.querySelector(".batch-row-spinner");
- if (spinner) spinner.innerHTML = ' Validating...';
+ if (spinner)
+ spinner.innerHTML =
+ ' Validating...';
- validateRows([{ iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) }])
+ validateRows([
+ { iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) },
+ ])
.then((validationData) => {
setRowExporting(gridRow, false);
gridRow.classList.remove("batch-disabled");
if (!validationData.success) {
- showExportResult({ success: false, message: validationData.message || "Errore di validazione" });
+ showExportResult({
+ success: false,
+ message:
+ validationData.message || "Errore di validazione",
+ });
return;
}
@@ -360,7 +391,10 @@ document.addEventListener("DOMContentLoaded", () => {
gridRow.classList.remove("batch-disabled");
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");
if (!data.success) {
- showRowError(gridRow, iddatadb, data.message || "Errore sconosciuto");
+ showRowError(
+ gridRow,
+ iddatadb,
+ data.message || "Errore sconosciuto",
+ );
}
showExportResult(data);
} catch (error) {
@@ -577,8 +615,9 @@ document.addEventListener("DOMContentLoaded", () => {
document.getElementById("exportResponseMessage").innerHTML =
`Nessuna riga valida per l'esportazione.
` +
`${invalidCount} righe con errori di validazione.`;
- document.getElementById("exportResponseModalLabel").textContent =
- "Validazione Fallita";
+ document.getElementById(
+ "exportResponseModalLabel",
+ ).textContent = "Validazione Fallita";
new bootstrap.Modal(
document.getElementById("exportResponseModal"),
{ keyboard: false },
@@ -595,27 +634,38 @@ document.addEventListener("DOMContentLoaded", () => {
if (invalidCount > 0) {
countText += ` (${invalidCount} escluse per errori di validazione)`;
}
- document.getElementById("exportBatchCount").textContent = countText;
+ document.getElementById("exportBatchCount").textContent =
+ countText;
confirmModal.show();
- const confirmBtn = document.getElementById("exportBatchConfirmBtn");
+ const confirmBtn = document.getElementById(
+ "exportBatchConfirmBtn",
+ );
if (pendingBatchConfirmHandler) {
- confirmBtn.removeEventListener("click", pendingBatchConfirmHandler);
+ confirmBtn.removeEventListener(
+ "click",
+ pendingBatchConfirmHandler,
+ );
}
pendingBatchConfirmHandler = () => {
pendingBatchConfirmHandler = null;
confirmModal.hide();
startBatchExport(validRows);
};
- confirmBtn.addEventListener("click", pendingBatchConfirmHandler, { once: true });
+ confirmBtn.addEventListener(
+ "click",
+ pendingBatchConfirmHandler,
+ { once: true },
+ );
})
.catch((error) => {
showValidationSpinner(false);
console.error("Batch validation error:", error);
document.getElementById("exportResponseMessage").textContent =
"Errore di validazione: " + error.message;
- document.getElementById("exportResponseModalLabel").textContent =
- "Errore Validazione";
+ document.getElementById(
+ "exportResponseModalLabel",
+ ).textContent = "Errore Validazione";
new bootstrap.Modal(
document.getElementById("exportResponseModal"),
{ keyboard: false },
@@ -635,54 +685,51 @@ document.addEventListener("DOMContentLoaded", () => {
);
unsavedModal.show();
- document
- .getElementById("batchSaveAndExportBtn")
- .addEventListener(
- "click",
- () => {
- unsavedModal.hide();
- // Trigger Save All, then proceed
- const saveAllEl =
- document.querySelector(".save-all-btn");
- if (!saveAllEl) return;
+ document.getElementById("batchSaveAndExportBtn").addEventListener(
+ "click",
+ () => {
+ unsavedModal.hide();
+ // Trigger Save All, then proceed
+ const saveAllEl = document.querySelector(".save-all-btn");
+ if (!saveAllEl) return;
- // Watch for all .cell-changed to disappear
- const observer = new MutationObserver(() => {
- if (!hasUnsavedChanges()) {
- observer.disconnect();
- const eligibleRows = collectEligibleRows();
- if (eligibleRows.length === 0) {
+ // Watch for all .cell-changed to disappear
+ const observer = new MutationObserver(() => {
+ if (!hasUnsavedChanges()) {
+ observer.disconnect();
+ const eligibleRows = collectEligibleRows();
+ 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(
- "exportResponseMessage",
- ).textContent =
- "Tutte le righe sono già state esportate al LIMS.";
- document.getElementById(
- "exportResponseModalLabel",
- ).textContent = "Export All";
- new bootstrap.Modal(
- document.getElementById(
- "exportResponseModal",
- ),
- { keyboard: false },
- ).show();
- return;
- }
- showBatchConfirm(eligibleRows);
+ "exportResponseModal",
+ ),
+ { keyboard: false },
+ ).show();
+ return;
}
- });
- observer.observe(
- document.querySelector(".grid-container"),
- {
- subtree: true,
- attributes: true,
- attributeFilter: ["class"],
- },
- );
+ showBatchConfirm(eligibleRows);
+ }
+ });
+ observer.observe(
+ document.querySelector(".grid-container"),
+ {
+ subtree: true,
+ attributes: true,
+ attributeFilter: ["class"],
+ },
+ );
- saveAllEl.click();
- },
- { once: true },
- );
+ saveAllEl.click();
+ },
+ { once: true },
+ );
return;
}
@@ -754,7 +801,11 @@ document.addEventListener("DOMContentLoaded", () => {
succeeded++;
} else {
failed++;
- showRowError(row, iddatadb, data.message || "Errore sconosciuto");
+ showRowError(
+ row,
+ iddatadb,
+ data.message || "Errore sconosciuto",
+ );
}
} catch (error) {
processed++;
@@ -794,7 +845,9 @@ document.addEventListener("DOMContentLoaded", () => {
const modalEl = document.getElementById("exportResponseModal");
const modal = new bootstrap.Modal(modalEl, { keyboard: false });
modal.show();
- modalEl.addEventListener("hidden.bs.modal", cleanupBackdrop, { once: true });
+ modalEl.addEventListener("hidden.bs.modal", cleanupBackdrop, {
+ once: true,
+ });
})();
}
});