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");
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: */*"
+136 -83
View File
@@ -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, "<br>");
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, "<br>");
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 = '<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) => {
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.<br>` +
`<strong>${invalidCount}</strong> 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,
});
})();
}
});