Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cf45a5bc31 | |||
| 9826331545 | |||
| 62bf4ebd92 | |||
| 6e465e3010 | |||
| 8b08969c69 | |||
| 34d4dc8660 | |||
| 1510ef03f1 | |||
| ce8c95921f | |||
| 095a6ae879 | |||
| 296143016a |
@@ -1,47 +0,0 @@
|
|||||||
APP_ENV=production
|
|
||||||
APP_DEBUG=true
|
|
||||||
APP_KEY=base64:C+sutHm6xP5sE4QXhoZFhYjArlVN11s2mDU1F8beUkM=
|
|
||||||
APP_URL=http://vanguard.test
|
|
||||||
|
|
||||||
LOG_CHANNEL=stack
|
|
||||||
|
|
||||||
DB_CONNECTION=mysql
|
|
||||||
DB_HOST="localhost"
|
|
||||||
DB_DATABASE="trfcertest"
|
|
||||||
DB_USERNAME="solocla"
|
|
||||||
DB_PASSWORD="!Massarosa2"
|
|
||||||
DB_PREFIX="auth_"
|
|
||||||
|
|
||||||
BROADCAST_DRIVER=log
|
|
||||||
CACHE_DRIVER=file
|
|
||||||
QUEUE_DRIVER=sync
|
|
||||||
SESSION_DRIVER=database
|
|
||||||
SESSION_LIFETIME=120
|
|
||||||
|
|
||||||
REDIS_HOST=127.0.0.1
|
|
||||||
REDIS_PASSWORD=null
|
|
||||||
REDIS_PORT=6379
|
|
||||||
|
|
||||||
MAIL_MAILER=mail
|
|
||||||
MAIL_FROM_NAME=Vanguard
|
|
||||||
MAIL_FROM_ADDRESS=vanguard@test.dev
|
|
||||||
MAIL_HOST=smtp.mailtrap.io
|
|
||||||
MAIL_PORT=2525
|
|
||||||
MAIL_USERNAME=null
|
|
||||||
MAIL_PASSWORD=null
|
|
||||||
MAIL_ENCRYPTION=null
|
|
||||||
|
|
||||||
PUSHER_APP_ID=
|
|
||||||
PUSHER_APP_KEY=
|
|
||||||
PUSHER_APP_SECRET=
|
|
||||||
PUSHER_APP_CLUSTER=mt1
|
|
||||||
|
|
||||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
|
||||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
|
||||||
|
|
||||||
# Credenziali API VisualLims
|
|
||||||
API_BASE_URL=https://93.43.5.102/limsapi
|
|
||||||
API_USERNAME=WebApiUser
|
|
||||||
API_PASSWORD=webapiuser01
|
|
||||||
|
|
||||||
BASE_URL=http://localhost:8000/userarea/
|
|
||||||
+12
@@ -51,4 +51,16 @@ public/userarea/class/curl_request_debug.log
|
|||||||
# Ignora cartella photostrf in public/userarea
|
# Ignora cartella photostrf in public/userarea
|
||||||
/public/userarea/photostrf/
|
/public/userarea/photostrf/
|
||||||
public/userarea/customfield_values_response.json
|
public/userarea/customfield_values_response.json
|
||||||
|
/public/userarea/logaspi/
|
||||||
|
|
||||||
|
public/userarea/logsapi/campione_762_1.json
|
||||||
|
public/userarea/logsapi/campione_763_1.json
|
||||||
|
public/userarea/logsapi/campione_762_2.json
|
||||||
|
public/userarea/logsapi/campione_763_2.json
|
||||||
|
public/userarea/logsapi/commessaweb_create_762.json
|
||||||
|
public/userarea/logsapi/commessaweb_create_763.json
|
||||||
|
public/userarea/logsapi/commessaweb_customfields_762.json
|
||||||
|
public/userarea/logsapi/commessaweb_customfields_763.json
|
||||||
|
public/userarea/logsapi/commessaweb_invia_762.json
|
||||||
|
public/userarea/logsapi/commessaweb_invia_763.json
|
||||||
|
public/userarea/logsapi/last_auth_url.txt
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once dirname(__DIR__, 3) . '/vendor/autoload.php'; // Torna al livello di public
|
require_once dirname(__DIR__, 3) . '/vendor/autoload.php';
|
||||||
|
|
||||||
use Dotenv\Dotenv;
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ class VisualLimsApiClient
|
|||||||
|
|
||||||
private function __construct()
|
private function __construct()
|
||||||
{
|
{
|
||||||
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3)); // Torna al livello di public
|
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3)); // Corretto per C:\xampp8-2-12\htdocs\trf_certest\.env
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
|
|
||||||
$this->baseUrl = $_ENV['API_BASE_URL'];
|
$this->baseUrl = $_ENV['API_BASE_URL'];
|
||||||
@@ -87,6 +87,7 @@ class VisualLimsApiClient
|
|||||||
$token = $this->getToken();
|
$token = $this->getToken();
|
||||||
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||||
|
|
||||||
|
|
||||||
$ch = curl_init($url);
|
$ch = curl_init($url);
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
@@ -120,4 +121,88 @@ class VisualLimsApiClient
|
|||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function post($endpoint, $payload)
|
||||||
|
{
|
||||||
|
$token = $this->getToken();
|
||||||
|
$url = "{$this->baseUrl}/api/odata/{$endpoint}"; // Corretto per /api/odata/CommessaWeb
|
||||||
|
file_put_contents(__DIR__ . '/url_debug.log', date('Y-m-d H:i:s') . " - POST URL: {$url}\n", FILE_APPEND);
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POST, true);
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"Accept: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
||||||
|
$log = fopen(__DIR__ . '/curl_request_debug.log', 'w') ?: fopen('php://stderr', 'w');
|
||||||
|
curl_setopt($ch, CURLOPT_STDERR, $log);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curl_error = curl_error($ch);
|
||||||
|
fclose($log);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new Exception("Errore nella richiesta POST: {$curl_error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code >= 400) {
|
||||||
|
throw new Exception("Errore nella richiesta POST: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($response, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
throw new Exception("Risposta non JSON valida: " . substr($response, 0, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patch($endpoint, $payload)
|
||||||
|
{
|
||||||
|
$token = $this->getToken();
|
||||||
|
$url = "{$this->baseUrl}/api/odata/{$endpoint}"; // Corretto per /api/odata/CommessaWeb({key})
|
||||||
|
|
||||||
|
$ch = curl_init($url);
|
||||||
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||||
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'PATCH');
|
||||||
|
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||||
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||||
|
"Authorization: Bearer {$token}",
|
||||||
|
"Content-Type: application/json",
|
||||||
|
"Accept: application/json"
|
||||||
|
]);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||||
|
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||||
|
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
||||||
|
$log = fopen(__DIR__ . '/curl_request_debug.log', 'w') ?: fopen('php://stderr', 'w');
|
||||||
|
curl_setopt($ch, CURLOPT_STDERR, $log);
|
||||||
|
|
||||||
|
$response = curl_exec($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
$curl_error = curl_error($ch);
|
||||||
|
fclose($log);
|
||||||
|
curl_close($ch);
|
||||||
|
|
||||||
|
if ($response === false) {
|
||||||
|
throw new Exception("Errore nella richiesta PATCH: {$curl_error}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($http_code >= 400) {
|
||||||
|
throw new Exception("Errore nella richiesta PATCH: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = json_decode($response, true);
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||||
|
throw new Exception("Risposta non JSON valida: " . substr($response, 0, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
* Trying 93.43.5.102:443...
|
|
||||||
* Connected to 93.43.5.102 (93.43.5.102) port 443
|
|
||||||
* ALPN: curl offers h2,http/1.1
|
|
||||||
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
|
|
||||||
* ALPN: server accepted h2
|
|
||||||
* Server certificate:
|
|
||||||
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
|
|
||||||
* start date: Feb 17 00:00:00 2025 GMT
|
|
||||||
* expire date: Feb 17 23:59:59 2026 GMT
|
|
||||||
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
|
|
||||||
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
|
|
||||||
* using HTTP/2
|
|
||||||
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
|
|
||||||
* [HTTP/2] [1] [:method: POST]
|
|
||||||
* [HTTP/2] [1] [:scheme: https]
|
|
||||||
* [HTTP/2] [1] [:authority: 93.43.5.102]
|
|
||||||
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
|
|
||||||
* [HTTP/2] [1] [content-type: application/json]
|
|
||||||
* [HTTP/2] [1] [accept: application/json]
|
|
||||||
* [HTTP/2] [1] [content-length: 51]
|
|
||||||
> POST /limsapi/api/authentication/authenticate HTTP/2
|
|
||||||
Host: 93.43.5.102
|
|
||||||
Content-Type: application/json
|
|
||||||
Accept: application/json
|
|
||||||
Content-Length: 51
|
|
||||||
|
|
||||||
< HTTP/2 200
|
|
||||||
< cache-control: max-age=0
|
|
||||||
< content-type: application/json; charset=utf-8
|
|
||||||
< server: Microsoft-IIS/10.0
|
|
||||||
< strict-transport-security: max-age=2592000
|
|
||||||
< x-powered-by: ASP.NET
|
|
||||||
< x-content-type-options: nosniff
|
|
||||||
< date: Mon, 08 Sep 2025 11:59:10 GMT
|
|
||||||
<
|
|
||||||
* Connection #0 to host 93.43.5.102 left intact
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
* Trying 93.43.5.102:443...
|
|
||||||
* Connected to 93.43.5.102 (93.43.5.102) port 443
|
|
||||||
* ALPN: curl offers h2,http/1.1
|
|
||||||
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
|
|
||||||
* ALPN: server accepted h2
|
|
||||||
* Server certificate:
|
|
||||||
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
|
|
||||||
* start date: Feb 17 00:00:00 2025 GMT
|
|
||||||
* expire date: Feb 17 23:59:59 2026 GMT
|
|
||||||
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
|
|
||||||
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
|
|
||||||
* using HTTP/2
|
|
||||||
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues
|
|
||||||
* [HTTP/2] [1] [:method: GET]
|
|
||||||
* [HTTP/2] [1] [:scheme: https]
|
|
||||||
* [HTTP/2] [1] [:authority: 93.43.5.102]
|
|
||||||
* [HTTP/2] [1] [:path: /limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues]
|
|
||||||
* [HTTP/2] [1] [authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NzMzOTk1MSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.JywaxXVFGeXH44RCOZUPfDGuEr5dt0cHAWN_vmIiFaw]
|
|
||||||
* [HTTP/2] [1] [accept: application/json]
|
|
||||||
> GET /limsapi/api/odata/CustomField(1083)?$expand=CustomFieldsValues HTTP/2
|
|
||||||
Host: 93.43.5.102
|
|
||||||
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc1NzMzOTk1MSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.JywaxXVFGeXH44RCOZUPfDGuEr5dt0cHAWN_vmIiFaw
|
|
||||||
Accept: application/json
|
|
||||||
|
|
||||||
< HTTP/2 200
|
|
||||||
< cache-control: max-age=0
|
|
||||||
< content-type: application/json; odata.metadata=minimal; odata.streaming=true; charset=utf-8
|
|
||||||
< server: Microsoft-IIS/10.0
|
|
||||||
< strict-transport-security: max-age=2592000
|
|
||||||
< odata-version: 4.0
|
|
||||||
< x-powered-by: ASP.NET
|
|
||||||
< x-content-type-options: nosniff
|
|
||||||
< date: Mon, 08 Sep 2025 11:59:14 GMT
|
|
||||||
<
|
|
||||||
* Connection #0 to host 93.43.5.102 left intact
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,239 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
console.log("export_to_lims.js loaded");
|
||||||
|
|
||||||
|
// Debug: verifica che i pulsanti siano trovati
|
||||||
|
const exportButtons = document.querySelectorAll(".export-lims-btn");
|
||||||
|
console.log(`Found ${exportButtons.length} export-lims-btn buttons`);
|
||||||
|
|
||||||
|
if (exportButtons.length === 0) {
|
||||||
|
console.warn("No .export-lims-btn buttons found in the DOM");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exportButtons.forEach((btn) => {
|
||||||
|
btn.addEventListener("click", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const rowIndex = btn.dataset.row;
|
||||||
|
const iddatadb = btn.dataset.iddatadb;
|
||||||
|
console.log(
|
||||||
|
`Export to LIMS clicked for row ${rowIndex}, iddatadb: ${iddatadb}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mostra il modale di conferma
|
||||||
|
const confirmModalElement =
|
||||||
|
document.getElementById("exportConfirmModal");
|
||||||
|
if (!confirmModalElement) {
|
||||||
|
console.error("exportConfirmModal not found in the DOM");
|
||||||
|
alert("Errore: Modale di conferma non trovato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmModal = new bootstrap.Modal(confirmModalElement, {
|
||||||
|
keyboard: false,
|
||||||
|
});
|
||||||
|
document.getElementById("exportIddatadb").textContent = iddatadb;
|
||||||
|
confirmModal.show();
|
||||||
|
|
||||||
|
// Gestisci il click su "Conferma"
|
||||||
|
const confirmBtn = document.getElementById("exportConfirmBtn");
|
||||||
|
if (!confirmBtn) {
|
||||||
|
console.error("exportConfirmBtn not found in the DOM");
|
||||||
|
confirmModal.hide();
|
||||||
|
alert("Errore: Pulsante di conferma non trovato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmHandler = async () => {
|
||||||
|
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
||||||
|
confirmModal.hide();
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("iddatadb", iddatadb);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch("export_to_lims.php", {
|
||||||
|
method: "POST",
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
if (!response.ok)
|
||||||
|
throw new Error(
|
||||||
|
`HTTP error! status: ${response.status}`,
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
console.log("Export response:", data);
|
||||||
|
|
||||||
|
// Mostra il modale di risposta
|
||||||
|
const responseModalElement = document.getElementById(
|
||||||
|
"exportResponseModal",
|
||||||
|
);
|
||||||
|
if (!responseModalElement) {
|
||||||
|
console.error(
|
||||||
|
"exportResponseModal not found in the DOM",
|
||||||
|
);
|
||||||
|
alert("Errore: Modale di risposta non trovato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseModal = new bootstrap.Modal(
|
||||||
|
responseModalElement,
|
||||||
|
{
|
||||||
|
keyboard: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const responseMessage = document.getElementById(
|
||||||
|
"exportResponseMessage",
|
||||||
|
);
|
||||||
|
if (data.success) {
|
||||||
|
responseMessage.innerHTML = `${data.message.replace(/\n/g, "<br>")}<br>ID CommessaWeb: ${data.idcommessaweb}`;
|
||||||
|
document.getElementById(
|
||||||
|
"exportResponseModalLabel",
|
||||||
|
).textContent = "Esportazione Completata";
|
||||||
|
responseModal.show();
|
||||||
|
|
||||||
|
// Aggiorna la UI per riflettere lo stato 'To LIMS'
|
||||||
|
const statusCell = btn
|
||||||
|
.closest(".grid-row")
|
||||||
|
.querySelector(
|
||||||
|
'.grid-cell[data-col="status"] .status-badge',
|
||||||
|
);
|
||||||
|
if (statusCell) {
|
||||||
|
statusCell.classList.remove("status-i", "status-P");
|
||||||
|
statusCell.classList.add("status-l");
|
||||||
|
statusCell.textContent = "To LIMS";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestisci la chiusura del modale di risposta
|
||||||
|
responseModalElement.addEventListener(
|
||||||
|
"hidden.bs.modal",
|
||||||
|
() => {
|
||||||
|
console.log(
|
||||||
|
"exportResponseModal closed, cleaning up",
|
||||||
|
);
|
||||||
|
// Rimuovi tutti i backdrop residui
|
||||||
|
document
|
||||||
|
.querySelectorAll(".modal-backdrop")
|
||||||
|
.forEach((backdrop) => {
|
||||||
|
console.log(
|
||||||
|
"Removing backdrop:",
|
||||||
|
backdrop,
|
||||||
|
);
|
||||||
|
backdrop.remove();
|
||||||
|
});
|
||||||
|
// Ripristina il body
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
document.body.style.paddingRight = "";
|
||||||
|
// Nascondi l'overlay
|
||||||
|
const overlay = document.querySelector(
|
||||||
|
".overlay.toggle-icon",
|
||||||
|
);
|
||||||
|
if (overlay) {
|
||||||
|
overlay.style.display = "none";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
responseMessage.textContent = `Errore durante la generazione dei payload: ${data.message}`;
|
||||||
|
document.getElementById(
|
||||||
|
"exportResponseModalLabel",
|
||||||
|
).textContent = "Errore Esportazione";
|
||||||
|
responseModal.show();
|
||||||
|
|
||||||
|
// Gestisci la chiusura del modale di risposta anche in caso di errore
|
||||||
|
responseModalElement.addEventListener(
|
||||||
|
"hidden.bs.modal",
|
||||||
|
() => {
|
||||||
|
console.log(
|
||||||
|
"exportResponseModal closed, cleaning up",
|
||||||
|
);
|
||||||
|
// Rimuovi tutti i backdrop residui
|
||||||
|
document
|
||||||
|
.querySelectorAll(".modal-backdrop")
|
||||||
|
.forEach((backdrop) => {
|
||||||
|
console.log(
|
||||||
|
"Removing backdrop:",
|
||||||
|
backdrop,
|
||||||
|
);
|
||||||
|
backdrop.remove();
|
||||||
|
});
|
||||||
|
// Ripristina il body
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
document.body.style.paddingRight = "";
|
||||||
|
// Nascondi l'overlay
|
||||||
|
const overlay = document.querySelector(
|
||||||
|
".overlay.toggle-icon",
|
||||||
|
);
|
||||||
|
if (overlay) {
|
||||||
|
overlay.style.display = "none";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Export error:", error);
|
||||||
|
const responseModalElement = document.getElementById(
|
||||||
|
"exportResponseModal",
|
||||||
|
);
|
||||||
|
if (!responseModalElement) {
|
||||||
|
console.error(
|
||||||
|
"exportResponseModal not found in the DOM",
|
||||||
|
);
|
||||||
|
alert("Errore: Modale di risposta non trovato");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const responseModal = new bootstrap.Modal(
|
||||||
|
responseModalElement,
|
||||||
|
{
|
||||||
|
keyboard: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
document.getElementById(
|
||||||
|
"exportResponseMessage",
|
||||||
|
).textContent =
|
||||||
|
`Errore durante la generazione dei payload: ${error.message}`;
|
||||||
|
document.getElementById(
|
||||||
|
"exportResponseModalLabel",
|
||||||
|
).textContent = "Errore Esportazione";
|
||||||
|
responseModal.show();
|
||||||
|
|
||||||
|
// Gestisci la chiusura del modale di risposta in caso di errore
|
||||||
|
responseModalElement.addEventListener(
|
||||||
|
"hidden.bs.modal",
|
||||||
|
() => {
|
||||||
|
console.log(
|
||||||
|
"exportResponseModal closed, cleaning up",
|
||||||
|
);
|
||||||
|
// Rimuovi tutti i backdrop residui
|
||||||
|
document
|
||||||
|
.querySelectorAll(".modal-backdrop")
|
||||||
|
.forEach((backdrop) => {
|
||||||
|
console.log("Removing backdrop:", backdrop);
|
||||||
|
backdrop.remove();
|
||||||
|
});
|
||||||
|
// Ripristina il body
|
||||||
|
document.body.classList.remove("modal-open");
|
||||||
|
document.body.style.paddingRight = "";
|
||||||
|
// Nascondi l'overlay
|
||||||
|
const overlay = document.querySelector(
|
||||||
|
".overlay.toggle-icon",
|
||||||
|
);
|
||||||
|
if (overlay) {
|
||||||
|
overlay.style.display = "none";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ once: true },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rimuovi il listener dopo l'esecuzione
|
||||||
|
confirmBtn.removeEventListener("click", confirmHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Rimuovi eventuali listener precedenti
|
||||||
|
confirmBtn.removeEventListener("click", confirmHandler);
|
||||||
|
confirmBtn.addEventListener("click", confirmHandler);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,290 @@
|
|||||||
|
<?php
|
||||||
|
// File: export_to_lims.php
|
||||||
|
ini_set('display_errors', '1');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
ini_set('error_log', __DIR__ . '/logsapi/export_lims_error.log');
|
||||||
|
|
||||||
|
// Includi il file con la connessione al database e Dotenv
|
||||||
|
require_once __DIR__ . '/include/headscript.php';
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
use Dotenv\Dotenv;
|
||||||
|
|
||||||
|
// Carica il file .env
|
||||||
|
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2)); // Torna al livello di public
|
||||||
|
$dotenv->load();
|
||||||
|
|
||||||
|
// Leggi la variabile SIMULATE_EXPORT_LIMS
|
||||||
|
$simulate = filter_var($_ENV['SIMULATE_EXPORT_LIMS'] ?? true, FILTER_VALIDATE_BOOLEAN);
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Verifica che la richiesta sia POST e contenga iddatadb
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['iddatadb'])) {
|
||||||
|
throw new Exception('Richiesta non valida: iddatadb mancante');
|
||||||
|
}
|
||||||
|
|
||||||
|
$iddatadb = (int)$_POST['iddatadb'];
|
||||||
|
|
||||||
|
// Crea la cartella logsapi se non esiste
|
||||||
|
$logDir = __DIR__ . '/logsapi';
|
||||||
|
if (!is_dir($logDir)) {
|
||||||
|
mkdir($logDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ottieni connessione al database
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// Step 1: Creazione payload per CommessaWeb
|
||||||
|
$queryCommessa = "
|
||||||
|
SELECT
|
||||||
|
d.iddatadb,
|
||||||
|
e.idclient AS Cliente,
|
||||||
|
e.idschema AS SchemaCustomField
|
||||||
|
FROM datadb d
|
||||||
|
LEFT JOIN excel_templates e ON d.templateid = e.id
|
||||||
|
WHERE d.iddatadb = :iddatadb
|
||||||
|
";
|
||||||
|
|
||||||
|
$stmtCommessa = $pdo->prepare($queryCommessa);
|
||||||
|
$stmtCommessa->execute(['iddatadb' => $iddatadb]);
|
||||||
|
$recordCommessa = $stmtCommessa->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$recordCommessa) {
|
||||||
|
throw new Exception("Nessun record trovato per iddatadb: {$iddatadb}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validazione payload
|
||||||
|
if (empty($recordCommessa['Cliente']) || empty($recordCommessa['SchemaCustomField'])) {
|
||||||
|
throw new Exception("Dati mancanti per CommessaWeb: Cliente o SchemaCustomField non validi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Payload per creazione CommessaWeb
|
||||||
|
$payloadCommessa = [
|
||||||
|
'Cliente' => (int)$recordCommessa['Cliente'],
|
||||||
|
'SchemaCustomField' => (int)$recordCommessa['SchemaCustomField'],
|
||||||
|
'Richiedente' => 'Richiedente test',
|
||||||
|
'Descrizione' => 'esempio Claudio'
|
||||||
|
];
|
||||||
|
|
||||||
|
// Step 2: Creazione payload per campi custom (CommesseCustomFields)
|
||||||
|
$queryCustomFields = "
|
||||||
|
SELECT
|
||||||
|
tm.field_id AS IdCommesseCustomFields,
|
||||||
|
idd.field_value AS Valore
|
||||||
|
FROM import_data_details idd
|
||||||
|
JOIN template_mapping tm ON idd.mapping_id = tm.id
|
||||||
|
WHERE idd.id = :iddatadb
|
||||||
|
";
|
||||||
|
|
||||||
|
$stmtCustomFields = $pdo->prepare($queryCustomFields);
|
||||||
|
$stmtCustomFields->execute(['iddatadb' => $iddatadb]);
|
||||||
|
$customFields = $stmtCustomFields->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$commesseCustomFields = [];
|
||||||
|
foreach ($customFields as $field) {
|
||||||
|
$commesseCustomFields[] = [
|
||||||
|
'IdCommesseCustomFields' => (int)$field['IdCommesseCustomFields'],
|
||||||
|
'Valore' => $field['Valore'] ?? ''
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$payloadCustomFields = [
|
||||||
|
'CommesseCustomFields' => $commesseCustomFields
|
||||||
|
];
|
||||||
|
|
||||||
|
// Step 3: Creazione payload per Campioni (da identification_parts)
|
||||||
|
$queryCampioni = "
|
||||||
|
SELECT
|
||||||
|
part_number,
|
||||||
|
idmatrice AS Matrice,
|
||||||
|
part_description AS NoteWeb
|
||||||
|
FROM identification_parts
|
||||||
|
WHERE iddatadb = :iddatadb
|
||||||
|
";
|
||||||
|
|
||||||
|
$stmtCampioni = $pdo->prepare($queryCampioni);
|
||||||
|
$stmtCampioni->execute(['iddatadb' => $iddatadb]);
|
||||||
|
$campioni = $stmtCampioni->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
$payloadsCampioni = [];
|
||||||
|
foreach ($campioni as $campione) {
|
||||||
|
if (empty($campione['Matrice'])) {
|
||||||
|
throw new Exception("Matrice non valida per campione: {$campione['part_number']}");
|
||||||
|
}
|
||||||
|
$payloadCampione = [
|
||||||
|
'Commessa' => null, // Sarà impostato dopo
|
||||||
|
'Matrice' => (int)$campione['Matrice'],
|
||||||
|
'SottoMatrice' => null,
|
||||||
|
'SchemaCustomField' => 1,
|
||||||
|
'NoteWeb' => $campione['NoteWeb'] ?? ''
|
||||||
|
];
|
||||||
|
$payloadsCampioni[] = $payloadCampione;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Creazione payload per InviaCommessa
|
||||||
|
$payloadInviaCommessa = [];
|
||||||
|
|
||||||
|
// Variabile per idcommessaweb
|
||||||
|
$idcommessaweb = null;
|
||||||
|
$commessaweb = '';
|
||||||
|
|
||||||
|
if ($simulate) {
|
||||||
|
// Flusso simulato
|
||||||
|
$idcommessaweb = 10176; // Fittizio per il test
|
||||||
|
|
||||||
|
// Salva idcommessaweb in datadb
|
||||||
|
$updateStmt = $pdo->prepare("UPDATE datadb SET idcommessaweb = :idcommessaweb WHERE iddatadb = :iddatadb");
|
||||||
|
$updateStmt->execute(['idcommessaweb' => $idcommessaweb, 'iddatadb' => $iddatadb]);
|
||||||
|
|
||||||
|
// Salva i payload in file JSON
|
||||||
|
$outputFileCommessa = $logDir . "/commessaweb_create_{$iddatadb}.json";
|
||||||
|
file_put_contents($outputFileCommessa, json_encode($payloadCommessa, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
$outputFileCustomFields = $logDir . "/commessaweb_customfields_{$iddatadb}.json";
|
||||||
|
file_put_contents($outputFileCustomFields, json_encode($payloadCustomFields, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
foreach ($payloadsCampioni as $index => $payloadCampione) {
|
||||||
|
$payloadCampione['Commessa'] = $idcommessaweb;
|
||||||
|
$outputFileCampione = $logDir . "/campione_{$iddatadb}_{$campioni[$index]['part_number']}.json";
|
||||||
|
file_put_contents($outputFileCampione, json_encode($payloadCampione, JSON_PRETTY_PRINT));
|
||||||
|
}
|
||||||
|
|
||||||
|
$outputFileInviaCommessa = $logDir . "/commessaweb_invia_{$iddatadb}.json";
|
||||||
|
file_put_contents($outputFileInviaCommessa, json_encode($payloadInviaCommessa, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
// Aggiorna lo status a 'l' (To LIMS)
|
||||||
|
$updateStmt = $pdo->prepare("UPDATE datadb SET status = 'l' WHERE iddatadb = :iddatadb");
|
||||||
|
$updateStmt->execute(['iddatadb' => $iddatadb]);
|
||||||
|
|
||||||
|
// Risposta di successo (simulazione)
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'mode' => 'simulated',
|
||||||
|
'message' => "Payload generati e salvati in {$outputFileCommessa}, {$outputFileCustomFields}, file campioni e {$outputFileInviaCommessa}",
|
||||||
|
'idcommessaweb' => $idcommessaweb,
|
||||||
|
'commessaweb' => $commessaweb,
|
||||||
|
'payload_commessa' => $payloadCommessa,
|
||||||
|
'payload_customfields' => $payloadCustomFields,
|
||||||
|
'payload_campioni' => $payloadsCampioni,
|
||||||
|
'payload_invia_commessa' => $payloadInviaCommessa
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
// Flusso reale
|
||||||
|
$apiClient = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Step 1: Crea CommessaWeb
|
||||||
|
try {
|
||||||
|
$response = $apiClient->post('CommessaWeb', $payloadCommessa);
|
||||||
|
if (!isset($response['IdCommessa']) || !isset($response['CodiceCommessa'])) {
|
||||||
|
throw new Exception("Risposta API non valida: IdCommessa o CodiceCommessa mancanti: " . json_encode($response));
|
||||||
|
}
|
||||||
|
$idcommessaweb = (int)$response['IdCommessa'];
|
||||||
|
$commessaweb = $response['CodiceCommessa'];
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - CommessaWeb creata: idcommessaweb {$idcommessaweb}, codice {$commessaweb} per iddatadb {$iddatadb}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
// Salva payload CommessaWeb
|
||||||
|
$outputFileCommessa = $logDir . "/commessaweb_create_{$iddatadb}_{$idcommessaweb}.json";
|
||||||
|
file_put_contents($outputFileCommessa, json_encode($payloadCommessa, JSON_PRETTY_PRINT));
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Payload CommessaWeb salvato in {$outputFileCommessa}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Errore nella creazione della CommessaWeb: " . $e->getMessage() . "\n", 3, $logDir . '/export_lims_error.log');
|
||||||
|
throw new Exception("Errore nella creazione della CommessaWeb: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Salva idcommessaweb e commessaweb in datadb
|
||||||
|
$updateStmt = $pdo->prepare("UPDATE datadb SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb WHERE iddatadb = :iddatadb");
|
||||||
|
$updateStmt->execute([
|
||||||
|
'idcommessaweb' => $idcommessaweb,
|
||||||
|
'commessaweb' => $commessaweb,
|
||||||
|
'iddatadb' => $iddatadb
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Step 2: Crea Campioni
|
||||||
|
try {
|
||||||
|
foreach ($payloadsCampioni as $index => $payloadCampione) {
|
||||||
|
$payloadCampione['Commessa'] = $idcommessaweb;
|
||||||
|
// Salva payload Campione
|
||||||
|
$outputFileCampione = $logDir . "/campione_{$iddatadb}_{$idcommessaweb}_{$campioni[$index]['part_number']}.json";
|
||||||
|
file_put_contents($outputFileCampione, json_encode($payloadCampione, JSON_PRETTY_PRINT));
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Payload Campione salvato in {$outputFileCampione}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
$apiClient->post('Campione', $payloadCampione);
|
||||||
|
$payloadsCampioni[$index] = $payloadCampione; // Aggiorna il payload con Commessa
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Campione creato: part_number {$campioni[$index]['part_number']} per idcommessaweb {$idcommessaweb}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Errore nella creazione dei Campioni: " . $e->getMessage() . "\n", 3, $logDir . '/export_lims_error.log');
|
||||||
|
throw new Exception("Errore nella creazione dei Campioni: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Aggiorna CommesseCustomFields
|
||||||
|
try {
|
||||||
|
// Salva payload CustomFields
|
||||||
|
$outputFileCustomFields = $logDir . "/commessaweb_customfields_{$iddatadb}_{$idcommessaweb}.json";
|
||||||
|
file_put_contents($outputFileCustomFields, json_encode($payloadCustomFields, JSON_PRETTY_PRINT));
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - PayloadCustomFields per idcommessaweb {$idcommessaweb}: " . json_encode($payloadCustomFields, JSON_PRETTY_PRINT) . "\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Payload CustomFields salvato in {$outputFileCustomFields}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
$apiClient->patch("CommessaWeb({$idcommessaweb})", $payloadCustomFields);
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - CommesseCustomFields aggiornati per idcommessaweb {$idcommessaweb}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Errore nell'aggiornamento CommesseCustomFields: " . $e->getMessage() . "\n", 3, $logDir . '/export_lims_error.log');
|
||||||
|
throw new Exception("Errore nell'aggiornamento CommesseCustomFields: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4: Invia Commessa
|
||||||
|
try {
|
||||||
|
// Salva payload InviaCommessa
|
||||||
|
$outputFileInviaCommessa = $logDir . "/commessaweb_invia_{$iddatadb}_{$idcommessaweb}.json";
|
||||||
|
file_put_contents($outputFileInviaCommessa, json_encode($payloadInviaCommessa, JSON_PRETTY_PRINT));
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Payload InviaCommessa salvato in {$outputFileInviaCommessa}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
$apiClient->post("CommessaWeb({$idcommessaweb})/InviaCommessa", $payloadInviaCommessa);
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Commessa inviata: idcommessaweb {$idcommessaweb}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Errore nell'invio della Commessa: " . $e->getMessage() . "\n", 3, $logDir . '/export_lims_error.log');
|
||||||
|
throw new Exception("Errore nell'invio della Commessa: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Recupera il numero commessaweb
|
||||||
|
try {
|
||||||
|
$commessaData = $apiClient->get("CommessaWeb({$idcommessaweb})");
|
||||||
|
$commessaweb = $commessaData['CodiceCommessaWeb'] ?? $commessaweb; // Usa CodiceCommessaWeb o fallback
|
||||||
|
if ($commessaweb) {
|
||||||
|
$updateStmt = $pdo->prepare("UPDATE datadb SET commessaweb = :commessaweb WHERE iddatadb = :iddatadb");
|
||||||
|
$updateStmt->execute(['commessaweb' => $commessaweb, 'iddatadb' => $iddatadb]);
|
||||||
|
}
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - CommessaWeb recuperata: codice {$commessaweb} per idcommessaweb {$idcommessaweb}\n", 3, $logDir . '/export_lims_success.log');
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log(date('Y-m-d H:i:s') . " - Errore nel recupero della CommessaWeb: " . $e->getMessage() . "\n", 3, $logDir . '/export_lims_error.log');
|
||||||
|
throw new Exception("Errore nel recupero della CommessaWeb: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aggiorna lo status a 'l' (To LIMS)
|
||||||
|
$updateStmt = $pdo->prepare("UPDATE datadb SET status = 'l' WHERE iddatadb = :iddatadb");
|
||||||
|
$updateStmt->execute(['iddatadb' => $iddatadb]);
|
||||||
|
|
||||||
|
// Risposta di successo (flusso reale)
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'mode' => 'real',
|
||||||
|
'message' => "Dati inviati al LIMS con successo",
|
||||||
|
'idcommessaweb' => $idcommessaweb,
|
||||||
|
'commessaweb' => $commessaweb,
|
||||||
|
'payload_commessa' => $payloadCommessa,
|
||||||
|
'payload_customfields' => $payloadCustomFields,
|
||||||
|
'payload_campioni' => $payloadsCampioni,
|
||||||
|
'payload_invia_commessa' => $payloadInviaCommessa
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Log dell'errore
|
||||||
|
file_put_contents($logDir . '/export_lims_error.log', date('Y-m-d H:i:s') . ' - Flusso ' . ($simulate ? 'simulato' : 'reale') . ' fallito: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'mode' => $simulate ? 'simulated' : 'real',
|
||||||
|
'message' => 'Errore: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Endpoint per recuperare le Matrici
|
||||||
|
$endpoint = 'Matrice';
|
||||||
|
|
||||||
|
// (Opzionale) aggiungi parametri, ad esempio $top per limitare i risultati
|
||||||
|
$options = []; // oppure ad esempio: ['$top' => 100]
|
||||||
|
|
||||||
|
// Debug: salva URL usato
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
|
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Chiamata API
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Salva il JSON in locale
|
||||||
|
file_put_contents(__DIR__ . '/matrici_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
echo json_encode($data);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode(['error' => $e->getMessage()]);
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
@@ -405,6 +405,16 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
.save-all-btn:hover {
|
.save-all-btn:hover {
|
||||||
background-color: #218838;
|
background-color: #218838;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#exportConfirmModal,
|
||||||
|
#exportResponseModal {
|
||||||
|
z-index: 1300 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#exportConfirmModal .modal-backdrop,
|
||||||
|
#exportResponseModal .modal-backdrop {
|
||||||
|
z-index: 1299 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
<title>Edit Imported Data - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
</head>
|
</head>
|
||||||
@@ -708,6 +718,7 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
<script src="photos.js"></script>
|
<script src="photos.js"></script>
|
||||||
<script src="parts.js"></script>
|
<script src="parts.js"></script>
|
||||||
<script src="tracking.js"></script>
|
<script src="tracking.js"></script>
|
||||||
|
<script src="export_to_lims.js"></script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
console.log("Page loaded, initializing event listeners");
|
console.log("Page loaded, initializing event listeners");
|
||||||
@@ -900,6 +911,8 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
window.addEventListener("beforeunload", function(e) {
|
window.addEventListener("beforeunload", function(e) {
|
||||||
if (hasChanges) {
|
if (hasChanges) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -907,6 +920,26 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Gestisci la chiusura dei modali per rimuovere i backdrop
|
||||||
|
document.querySelectorAll('#exportConfirmModal, #exportResponseModal').forEach(modal => {
|
||||||
|
modal.addEventListener('hidden.bs.modal', () => {
|
||||||
|
console.log(`Modal ${modal.id} closed, removing backdrops`);
|
||||||
|
// Rimuovi tutti i backdrop residui
|
||||||
|
document.querySelectorAll('.modal-backdrop').forEach(backdrop => {
|
||||||
|
console.log('Removing backdrop:', backdrop);
|
||||||
|
backdrop.remove();
|
||||||
|
});
|
||||||
|
// Ripristina il body
|
||||||
|
document.body.classList.remove('modal-open');
|
||||||
|
document.body.style.paddingRight = '';
|
||||||
|
// Assicurati che l'overlay sia nascosto
|
||||||
|
const overlay = document.querySelector('.overlay.toggle-icon');
|
||||||
|
if (overlay) {
|
||||||
|
overlay.style.display = 'none';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
@@ -1066,6 +1099,43 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
|||||||
populateDropdowns();
|
populateDropdowns();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<!-- Modale di conferma per l'esportazione -->
|
||||||
|
<div class="modal fade" id="exportConfirmModal" tabindex="-1" aria-labelledby="exportConfirmModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exportConfirmModalLabel">Conferma Esportazione</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p id="exportConfirmMessage">Confermi l'esportazione al LIMS per iddatadb <span id="exportIddatadb"></span>?</p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" id="exportConfirmBtn">Conferma</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modale di risposta per l'esportazione -->
|
||||||
|
<div class="modal fade" id="exportResponseModal" tabindex="-1" aria-labelledby="exportResponseModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exportResponseModalLabel">Risultato Esportazione</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p id="exportResponseMessage"></p>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-primary btn-sm" data-bs-dismiss="modal">Chiudi</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -21,7 +21,7 @@ $schemajson = $template['schemajson'] ? json_decode($template['schemajson'], tru
|
|||||||
$isSchemajsonEmpty = empty(trim($template['schemajson']));
|
$isSchemajsonEmpty = empty(trim($template['schemajson']));
|
||||||
|
|
||||||
// Recupera i campi dalla tabella template_mapping
|
// Recupera i campi dalla tabella template_mapping
|
||||||
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label, main_field FROM template_mapping WHERE template_id = ?");
|
$stmt = $pdo->prepare("SELECT id, field_id, excel_column, is_manual, manual_default, data_type, is_required, default_value, has_list, length, decimals, min_value, max_value, default_curr_date, tablename, field_label, main_field, is_visible_import FROM template_mapping WHERE template_id = ?");
|
||||||
$stmt->execute([$id]);
|
$stmt->execute([$id]);
|
||||||
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
@@ -83,8 +83,10 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
}
|
}
|
||||||
|
|
||||||
#schemaFieldsTable th:first-child,
|
#schemaFieldsTable th:first-child,
|
||||||
#schemaFieldsTable td:first-child {
|
#schemaFieldsTable td:first-child,
|
||||||
width: 50px;
|
#schemaFieldsTable th:nth-child(2),
|
||||||
|
#schemaFieldsTable td:nth-child(2) {
|
||||||
|
width: 100px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -137,8 +139,8 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Main</th>
|
<th>Main</th>
|
||||||
|
<th>Visible on Import</th>
|
||||||
<th>Title</th>
|
<th>Title</th>
|
||||||
<th>ID</th>
|
|
||||||
<th>Type</th>
|
<th>Type</th>
|
||||||
<th>Mapping</th>
|
<th>Mapping</th>
|
||||||
<th>Default Value</th>
|
<th>Default Value</th>
|
||||||
@@ -150,8 +152,15 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
<td>
|
<td>
|
||||||
<input type="checkbox" class="main-field-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['main_field'] == 1 ? 'checked' : ''; ?>>
|
<input type="checkbox" class="main-field-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo $mapping['main_field'] == 1 ? 'checked' : ''; ?>>
|
||||||
</td>
|
</td>
|
||||||
<td><?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?></td>
|
<td>
|
||||||
<td><?php echo htmlspecialchars($mapping['field_id'] ?? 'N/A'); ?></td>
|
<input type="checkbox" class="visible-import-checkbox" data-mapping-id="<?php echo $mapping['id']; ?>" <?php echo (isset($mapping['is_visible_import']) ? $mapping['is_visible_import'] : 1) == 1 ? 'checked' : ''; ?>>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
|
||||||
|
<?php if ($mapping['is_required'] == 1): ?>
|
||||||
|
<span class="badge bg-danger ms-2">Required</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
||||||
<td>
|
<td>
|
||||||
<?php
|
<?php
|
||||||
@@ -428,7 +437,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
|
|
||||||
async function updateSchemaDetails() {
|
async function updateSchemaDetails() {
|
||||||
if (!schemaId) {
|
if (!schemaId) {
|
||||||
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-warning">No schema associated.</td></tr>'; // Aggiornato colspan a 6
|
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-warning">No schema associated.</td></tr>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,7 +454,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
await saveSchemaJson(templateId, JSON.stringify(data));
|
await saveSchemaJson(templateId, JSON.stringify(data));
|
||||||
alert('Schema updated successfully. Refresh the page to see changes.');
|
alert('Schema updated successfully. Refresh the page to see changes.');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-danger">Error: ' + error.message + '</td></tr>'; // Aggiornato colspan a 6
|
document.getElementById('schemaFieldsBody').innerHTML = '<tr><td colspan="6" class="text-danger">Error: ' + error.message + '</td></tr>';
|
||||||
} finally {
|
} finally {
|
||||||
updateSchemaButton.disabled = false;
|
updateSchemaButton.disabled = false;
|
||||||
updateSchemaButton.textContent = 'Update Schema Details';
|
updateSchemaButton.textContent = 'Update Schema Details';
|
||||||
@@ -524,16 +533,11 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
.then(data => {
|
.then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
console.error("❌ Error updating main_field:", data.message);
|
console.error("❌ Error updating main_field:", data.message);
|
||||||
// Revert checkbox state on error
|
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
// Riselezione visiva degli altri se errore
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
||||||
if (value === 1) {
|
cb.checked = cb.dataset.originalChecked === 'true';
|
||||||
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
});
|
||||||
cb.checked = cb.dataset.originalChecked === 'true';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Aggiorna lo stato originale dopo successo
|
|
||||||
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
||||||
cb.dataset.originalChecked = cb.checked;
|
cb.dataset.originalChecked = cb.checked;
|
||||||
});
|
});
|
||||||
@@ -542,12 +546,36 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error("❌ Fetch error:", error);
|
console.error("❌ Fetch error:", error);
|
||||||
checkbox.checked = !checkbox.checked;
|
checkbox.checked = !checkbox.checked;
|
||||||
// Riselezione visiva degli altri se errore
|
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
||||||
if (value === 1) {
|
cb.checked = cb.dataset.originalChecked === 'true';
|
||||||
document.querySelectorAll('.main-field-checkbox').forEach(cb => {
|
});
|
||||||
cb.checked = cb.dataset.originalChecked === 'true';
|
});
|
||||||
});
|
} else if (event.target.classList.contains('visible-import-checkbox')) {
|
||||||
|
const checkbox = event.target;
|
||||||
|
const mappingId = checkbox.dataset.mappingId;
|
||||||
|
const value = checkbox.checked ? 1 : 0;
|
||||||
|
|
||||||
|
fetch('update_visible_import.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
template_id: <?php echo $id; ?>,
|
||||||
|
mapping_id: mappingId,
|
||||||
|
value: value
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (!data.success) {
|
||||||
|
console.error("❌ Error updating is_visible_import:", data.message);
|
||||||
|
checkbox.checked = !checkbox.checked;
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("❌ Fetch error:", error);
|
||||||
|
checkbox.checked = !checkbox.checked;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -569,7 +597,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
mappedColumn = document.createElement('span');
|
mappedColumn = document.createElement('span');
|
||||||
mappedColumn.className = 'mapped-column';
|
mappedColumn.className = 'mapped-column';
|
||||||
mappedColumn.style.marginLeft = '5px';
|
mappedColumn.style.marginLeft = '5px';
|
||||||
tr.querySelector('td:nth-child(5)').appendChild(mappedColumn); // Aggiornato a nth-child(5) per la nuova colonna
|
tr.querySelector('td:nth-child(5)').appendChild(mappedColumn);
|
||||||
}
|
}
|
||||||
if (!removeBtn) {
|
if (!removeBtn) {
|
||||||
removeBtn = document.createElement('button');
|
removeBtn = document.createElement('button');
|
||||||
@@ -577,7 +605,7 @@ $xlsHeaders = $template['xls_headers'] ? json_decode($template['xls_headers'], t
|
|||||||
removeBtn.textContent = 'X';
|
removeBtn.textContent = 'X';
|
||||||
removeBtn.style.marginLeft = '5px';
|
removeBtn.style.marginLeft = '5px';
|
||||||
removeBtn.setAttribute('data-id', mappingId);
|
removeBtn.setAttribute('data-id', mappingId);
|
||||||
tr.querySelector('td:nth-child(5)').appendChild(removeBtn); // Aggiornato a nth-child(5)
|
tr.querySelector('td:nth-child(5)').appendChild(removeBtn);
|
||||||
|
|
||||||
removeBtn.addEventListener('click', function(e) {
|
removeBtn.addEventListener('click', function(e) {
|
||||||
let tr = e.target.closest('tr');
|
let tr = e.target.closest('tr');
|
||||||
|
|||||||
@@ -190,4 +190,38 @@
|
|||||||
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
|
box-shadow: 0 0 0 0 rgba(220, 53, 69, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stile per il selettore personalizzato dei colori */
|
||||||
|
.color-picker-container {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-picker {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 25px;
|
||||||
|
left: 0;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 5px;
|
||||||
|
z-index: 1002;
|
||||||
|
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
margin: 3px;
|
||||||
|
border: 1px solid #000;
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.color-option:hover {
|
||||||
|
border: 2px solid #000;
|
||||||
|
margin: 2px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
+58
-14
@@ -457,14 +457,6 @@ $(document).ready(function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on("change", ".part-color", function () {
|
|
||||||
const partNumber = $(this).closest("li").data("part-number");
|
|
||||||
const partColor = $(this).val();
|
|
||||||
partColors[partNumber] = partColor;
|
|
||||||
updateMarkers();
|
|
||||||
markUnsaved();
|
|
||||||
});
|
|
||||||
|
|
||||||
function loadExistingParts(iddatadb) {
|
function loadExistingParts(iddatadb) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: "load_parts.php",
|
url: "load_parts.php",
|
||||||
@@ -517,6 +509,19 @@ $(document).ready(function () {
|
|||||||
function updatePartsList() {
|
function updatePartsList() {
|
||||||
const showMixParts = $("#showMixParts").is(":checked");
|
const showMixParts = $("#showMixParts").is(":checked");
|
||||||
$("#partsList").empty();
|
$("#partsList").empty();
|
||||||
|
|
||||||
|
// Definizione di 8 colori predefiniti
|
||||||
|
const predefinedColors = [
|
||||||
|
"#ff0000", // Rosso
|
||||||
|
"#0000ff", // Blu
|
||||||
|
"#00ff00", // Verde
|
||||||
|
"#01832cff", // Giallo
|
||||||
|
"#ff00ff", // Magenta
|
||||||
|
"#00ffff", // Ciano
|
||||||
|
"#800080", // Viola
|
||||||
|
"#ffa500", // Arancione
|
||||||
|
];
|
||||||
|
|
||||||
$("#partsTableBody tr").each(function () {
|
$("#partsTableBody tr").each(function () {
|
||||||
const partNumber = $(this).find(".part-number").val();
|
const partNumber = $(this).find(".part-number").val();
|
||||||
const partDescription = $(this).find(".part-description").val();
|
const partDescription = $(this).find(".part-description").val();
|
||||||
@@ -528,17 +533,56 @@ $(document).ready(function () {
|
|||||||
partDescription &&
|
partDescription &&
|
||||||
(showMixParts || !partDescription.startsWith("Mix"))
|
(showMixParts || !partDescription.startsWith("Mix"))
|
||||||
) {
|
) {
|
||||||
|
const colorOptions = predefinedColors
|
||||||
|
.map(
|
||||||
|
(color) =>
|
||||||
|
`<div class="color-option" style="background-color: ${color};" data-color="${color}"></div>`,
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
|
||||||
const listItem = `
|
const listItem = `
|
||||||
<li class="list-group-item" data-part-number="${partNumber}">
|
<li class="list-group-item" data-part-number="${partNumber}">
|
||||||
${partNumber} - ${partDescription}
|
${partNumber} - ${partDescription}
|
||||||
<div style="display: flex; align-items: center;">
|
<div style="display: flex; align-items: center;">
|
||||||
<button type="button" class="btn btn-success btn-sm add-to-mix-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
|
<button type="button" class="btn btn-success btn-sm add-to-mix-btn" style="padding: 0.1rem 0.3rem; font-size: 0.8rem;"><i class="fas fa-plus fa-xs"></i></button>
|
||||||
<input type="color" class="part-color" value="${partColor}" style="margin-left: 5px;">
|
<div class="color-picker-container">
|
||||||
|
<div class="color-option selected-color" style="background-color: ${partColor}; margin-left: 5px;"></div>
|
||||||
|
<div class="color-picker">${colorOptions}</div>
|
||||||
</div>
|
</div>
|
||||||
</li>`;
|
</div>
|
||||||
|
</li>`;
|
||||||
$("#partsList").append(listItem);
|
$("#partsList").append(listItem);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Gestione del selettore colori personalizzato
|
||||||
|
$(".selected-color").on("click", function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const $picker = $(this).siblings(".color-picker");
|
||||||
|
$(".color-picker").not($picker).hide(); // Chiude altri selettori aperti
|
||||||
|
$picker.toggle();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".color-option").on("click", function (e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
const $this = $(this);
|
||||||
|
const color = $this.data("color");
|
||||||
|
const $listItem = $this.closest("li");
|
||||||
|
const partNumber = $listItem.data("part-number");
|
||||||
|
partColors[partNumber] = color;
|
||||||
|
$listItem.find(".selected-color").css("background-color", color);
|
||||||
|
$this.closest(".color-picker").hide(); // Chiude il selettore dopo la scelta
|
||||||
|
updateMarkers();
|
||||||
|
markUnsaved();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chiude il selettore se si clicca fuori
|
||||||
|
$(document).on("click", function (e) {
|
||||||
|
if (!$(e.target).closest(".color-picker-container").length) {
|
||||||
|
$(".color-picker").hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
updateMarkers();
|
updateMarkers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+108
-234
@@ -7,7 +7,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
console.log("Caricamento contenuto per iddatadb:", iddatadb);
|
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
`photos_popup.php?iddatadb=${iddatadb}`,
|
`photos_popup.php?iddatadb=${iddatadb}`,
|
||||||
);
|
);
|
||||||
@@ -56,22 +55,18 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Funzione per avviare la webcam con un deviceId specifico
|
|
||||||
async function startWebcam(deviceId = null) {
|
async function startWebcam(deviceId = null) {
|
||||||
try {
|
try {
|
||||||
// Ferma il flusso video esistente, se presente
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream.getTracks().forEach((track) => track.stop());
|
stream.getTracks().forEach((track) => track.stop());
|
||||||
stream = null;
|
stream = null;
|
||||||
webcamVideo.srcObject = null;
|
webcamVideo.srcObject = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configura i vincoli per getUserMedia
|
|
||||||
const constraints = {
|
const constraints = {
|
||||||
video: deviceId ? { deviceId: { exact: deviceId } } : true,
|
video: deviceId ? { deviceId: { exact: deviceId } } : true,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Avvia il flusso video
|
|
||||||
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
webcamVideo.srcObject = stream;
|
webcamVideo.srcObject = stream;
|
||||||
webcamArea.style.display = "block";
|
webcamArea.style.display = "block";
|
||||||
@@ -86,21 +81,17 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Funzione per popolare il dropdown delle webcam
|
|
||||||
async function populateWebcamSelect() {
|
async function populateWebcamSelect() {
|
||||||
try {
|
try {
|
||||||
// Richiedi i permessi per accedere ai dispositivi
|
|
||||||
await navigator.mediaDevices.getUserMedia({ video: true });
|
await navigator.mediaDevices.getUserMedia({ video: true });
|
||||||
const devices = await navigator.mediaDevices.enumerateDevices();
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
||||||
const videoDevices = devices.filter(
|
const videoDevices = devices.filter(
|
||||||
(device) => device.kind === "videoinput",
|
(device) => device.kind === "videoinput",
|
||||||
);
|
);
|
||||||
|
|
||||||
// Svuota il dropdown
|
|
||||||
webcamSelect.innerHTML =
|
webcamSelect.innerHTML =
|
||||||
'<option value="">Select a webcam</option>';
|
'<option value="">Select a webcam</option>';
|
||||||
|
|
||||||
// Popola il dropdown con le webcam disponibili
|
|
||||||
videoDevices.forEach((device) => {
|
videoDevices.forEach((device) => {
|
||||||
const option = document.createElement("option");
|
const option = document.createElement("option");
|
||||||
option.value = device.deviceId;
|
option.value = device.deviceId;
|
||||||
@@ -109,11 +100,9 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
webcamSelect.appendChild(option);
|
webcamSelect.appendChild(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Mostra il dropdown solo se ci sono più webcam
|
|
||||||
webcamSelect.style.display =
|
webcamSelect.style.display =
|
||||||
videoDevices.length > 1 ? "block" : "none";
|
videoDevices.length > 1 ? "block" : "none";
|
||||||
|
|
||||||
// Avvia la webcam predefinita se ce n'è almeno una
|
|
||||||
if (videoDevices.length > 0) {
|
if (videoDevices.length > 0) {
|
||||||
await startWebcam(videoDevices[0].deviceId);
|
await startWebcam(videoDevices[0].deviceId);
|
||||||
} else {
|
} else {
|
||||||
@@ -129,12 +118,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apri la webcam e popola il dropdown
|
|
||||||
openWebcamBtn.addEventListener("click", async () => {
|
openWebcamBtn.addEventListener("click", async () => {
|
||||||
await populateWebcamSelect();
|
await populateWebcamSelect();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Gestisci il cambio della webcam selezionata
|
|
||||||
webcamSelect.addEventListener("change", async (e) => {
|
webcamSelect.addEventListener("change", async (e) => {
|
||||||
const deviceId = e.target.value;
|
const deviceId = e.target.value;
|
||||||
if (deviceId) {
|
if (deviceId) {
|
||||||
@@ -142,7 +129,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Chiudi la webcam
|
|
||||||
closeWebcamBtn.addEventListener("click", () => {
|
closeWebcamBtn.addEventListener("click", () => {
|
||||||
if (stream) {
|
if (stream) {
|
||||||
stream.getTracks().forEach((track) => track.stop());
|
stream.getTracks().forEach((track) => track.stop());
|
||||||
@@ -154,7 +140,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
dropArea.style.display = "block";
|
dropArea.style.display = "block";
|
||||||
});
|
});
|
||||||
|
|
||||||
// Cattura la foto
|
|
||||||
captureBtn.addEventListener("click", () => {
|
captureBtn.addEventListener("click", () => {
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
canvas.width = webcamVideo.videoWidth;
|
canvas.width = webcamVideo.videoWidth;
|
||||||
@@ -171,7 +156,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
);
|
);
|
||||||
const loader = document.getElementById("loader");
|
const loader = document.getElementById("loader");
|
||||||
if (loader) {
|
if (loader) {
|
||||||
console.log("Mostro loader per upload webcam");
|
|
||||||
loader.style.display = "flex";
|
loader.style.display = "flex";
|
||||||
}
|
}
|
||||||
await handleFiles([file], iddatadb);
|
await handleFiles([file], iddatadb);
|
||||||
@@ -187,7 +171,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Funzione per gestire il caricamento dei file
|
|
||||||
async function handleFiles(files, iddatadb) {
|
async function handleFiles(files, iddatadb) {
|
||||||
const loader = document.getElementById("loader");
|
const loader = document.getElementById("loader");
|
||||||
if (!loader) {
|
if (!loader) {
|
||||||
@@ -206,7 +189,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Inizio upload del file:", file.name);
|
|
||||||
loader.style.display = "flex";
|
loader.style.display = "flex";
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
@@ -219,9 +201,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log(
|
|
||||||
"Upload completato con successo, ricarico popup",
|
|
||||||
);
|
|
||||||
loadPopupContent(iddatadb);
|
loadPopupContent(iddatadb);
|
||||||
} else {
|
} else {
|
||||||
alert("Errore durante il caricamento: " + result.message);
|
alert("Errore durante il caricamento: " + result.message);
|
||||||
@@ -229,13 +208,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
alert("Errore durante il caricamento: " + error.message);
|
alert("Errore durante il caricamento: " + error.message);
|
||||||
} finally {
|
} finally {
|
||||||
console.log("Nascondo loader dopo upload");
|
|
||||||
loader.style.display = "none";
|
loader.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Funzione per attaccare gli event listener al contenuto del popup
|
|
||||||
function attachPhotoEventListeners(iddatadb) {
|
function attachPhotoEventListeners(iddatadb) {
|
||||||
const dropArea = document.getElementById("dropArea");
|
const dropArea = document.getElementById("dropArea");
|
||||||
const photoInput = document.getElementById("photoInput");
|
const photoInput = document.getElementById("photoInput");
|
||||||
@@ -250,9 +227,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Event listener associati per iddatadb:", iddatadb);
|
|
||||||
|
|
||||||
// Gestione drag-and-drop
|
|
||||||
const preventDefaults = (e) => {
|
const preventDefaults = (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -281,7 +255,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
dropArea.addEventListener(
|
dropArea.addEventListener(
|
||||||
"drop",
|
"drop",
|
||||||
(e) => {
|
(e) => {
|
||||||
console.log("Evento drop attivato");
|
|
||||||
const files = e.dataTransfer.files;
|
const files = e.dataTransfer.files;
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
handleFiles(files, iddatadb);
|
handleFiles(files, iddatadb);
|
||||||
@@ -293,7 +266,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
dropArea.addEventListener(
|
dropArea.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
() => {
|
() => {
|
||||||
console.log("Click su dropArea, apro input file");
|
|
||||||
photoInput.click();
|
photoInput.click();
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
@@ -302,18 +274,15 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
photoInput.addEventListener(
|
photoInput.addEventListener(
|
||||||
"change",
|
"change",
|
||||||
(e) => {
|
(e) => {
|
||||||
console.log("Evento change su photoInput");
|
|
||||||
const files = e.target.files;
|
const files = e.target.files;
|
||||||
if (files.length > 0) {
|
if (files.length > 0) {
|
||||||
handleFiles(files, iddatadb);
|
handleFiles(files, iddatadb);
|
||||||
}
|
}
|
||||||
// Resetta l'input per consentire il caricamento dello stesso file
|
|
||||||
e.target.value = "";
|
e.target.value = "";
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Gestione rimozione foto
|
|
||||||
document.querySelectorAll(".delete-photo-btn").forEach((button) => {
|
document.querySelectorAll(".delete-photo-btn").forEach((button) => {
|
||||||
button.addEventListener("click", async function () {
|
button.addEventListener("click", async function () {
|
||||||
const photoId = this.getAttribute("data-photo-id");
|
const photoId = this.getAttribute("data-photo-id");
|
||||||
@@ -329,9 +298,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
console.log(
|
|
||||||
"Foto eliminata con successo, ricarico popup",
|
|
||||||
);
|
|
||||||
loadPopupContent(iddatadb);
|
loadPopupContent(iddatadb);
|
||||||
} else {
|
} else {
|
||||||
alert(
|
alert(
|
||||||
@@ -348,21 +314,17 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Gestione ingrandimento immagini
|
|
||||||
document.querySelectorAll(".thumbnail").forEach((img) => {
|
document.querySelectorAll(".thumbnail").forEach((img) => {
|
||||||
img.addEventListener("click", function () {
|
img.addEventListener("click", function () {
|
||||||
console.log("Click su thumbnail, apro modale immagine");
|
|
||||||
const enlargedImage = document.getElementById("enlargedImage");
|
const enlargedImage = document.getElementById("enlargedImage");
|
||||||
enlargedImage.src = this.src;
|
enlargedImage.src = this.src;
|
||||||
document.getElementById("imageModal").style.display = "block";
|
document.getElementById("imageModal").style.display = "block";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Gestione chiusura modale immagine
|
|
||||||
const imageCloseBtn = document.querySelector(".image-modal-close");
|
const imageCloseBtn = document.querySelector(".image-modal-close");
|
||||||
if (imageCloseBtn) {
|
if (imageCloseBtn) {
|
||||||
imageCloseBtn.addEventListener("click", () => {
|
imageCloseBtn.addEventListener("click", () => {
|
||||||
console.log("Chiusura modale immagine");
|
|
||||||
document.getElementById("imageModal").style.display = "none";
|
document.getElementById("imageModal").style.display = "none";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -370,57 +332,42 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
.getElementById("imageModal")
|
.getElementById("imageModal")
|
||||||
.addEventListener("click", function (event) {
|
.addEventListener("click", function (event) {
|
||||||
if (event.target === this) {
|
if (event.target === this) {
|
||||||
console.log(
|
|
||||||
"Chiusura modale immagine cliccando sullo sfondo",
|
|
||||||
);
|
|
||||||
this.style.display = "none";
|
this.style.display = "none";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Inizializza la gestione della webcam
|
|
||||||
setupWebcam(iddatadb);
|
setupWebcam(iddatadb);
|
||||||
|
|
||||||
// Gestione bottone Crea Collage
|
|
||||||
const createCollageBtn = document.getElementById("createCollageBtn");
|
const createCollageBtn = document.getElementById("createCollageBtn");
|
||||||
if (createCollageBtn) {
|
if (createCollageBtn) {
|
||||||
createCollageBtn.addEventListener("click", () => {
|
createCollageBtn.addEventListener("click", () => {
|
||||||
console.log("Apertura modale collage");
|
|
||||||
document.getElementById("collageModal").style.display = "block";
|
document.getElementById("collageModal").style.display = "block";
|
||||||
initCollageCanvas();
|
initCollageCanvas();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chiusura modale collage
|
|
||||||
const closeCollageBtn = document.querySelector(".close-collage");
|
const closeCollageBtn = document.querySelector(".close-collage");
|
||||||
if (closeCollageBtn) {
|
if (closeCollageBtn) {
|
||||||
closeCollageBtn.addEventListener("click", () => {
|
closeCollageBtn.addEventListener("click", () => {
|
||||||
console.log("Chiusura modale collage");
|
|
||||||
document.getElementById("collageModal").style.display = "none";
|
document.getElementById("collageModal").style.display = "none";
|
||||||
if (isCropping) {
|
if (isCropping) {
|
||||||
console.log(
|
|
||||||
"Chiusura modale durante ritaglio, esco dalla modalità ritaglio",
|
|
||||||
);
|
|
||||||
exitCropMode();
|
exitCropMode();
|
||||||
}
|
}
|
||||||
if (isRemovingBackground) {
|
if (isRemovingBackground) {
|
||||||
console.log(
|
|
||||||
"Chiusura modale durante rimozione sfondo, esco dalla modalità",
|
|
||||||
);
|
|
||||||
exitBackgroundRemovalMode();
|
exitBackgroundRemovalMode();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inizializza canvas con Fabric.js
|
|
||||||
let canvas;
|
let canvas;
|
||||||
let cropRect = null;
|
let cropRect = null;
|
||||||
let isCropping = false;
|
let isCropping = false;
|
||||||
let croppedImage = null; // Memorizza l'immagine da ritagliare
|
let croppedImage = null;
|
||||||
let isApplyingCrop = false; // Flag per prevenire duplicazioni
|
let isApplyingCrop = false;
|
||||||
let isRemovingBackground = false; // Flag per modalità rimozione sfondo
|
let isRemovingBackground = false;
|
||||||
let backgroundRemovalImage = null; // Immagine in modalità rimozione sfondo
|
let backgroundRemovalImage = null;
|
||||||
let history = []; // Pila per salvare gli stati del canvas
|
let history = [];
|
||||||
const maxHistory = 20; // Limite massimo di stati nella pila
|
const maxHistory = 20;
|
||||||
|
|
||||||
function initCollageCanvas() {
|
function initCollageCanvas() {
|
||||||
if (typeof fabric === "undefined") {
|
if (typeof fabric === "undefined") {
|
||||||
@@ -434,7 +381,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
backgroundColor: "#fff",
|
backgroundColor: "#fff",
|
||||||
selection: true,
|
selection: true,
|
||||||
});
|
});
|
||||||
// Imposta stile globale per i controlli
|
|
||||||
fabric.Object.prototype.set({
|
fabric.Object.prototype.set({
|
||||||
cornerColor: "black",
|
cornerColor: "black",
|
||||||
cornerStrokeColor: "black",
|
cornerStrokeColor: "black",
|
||||||
@@ -442,63 +388,103 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
borderColor: "black",
|
borderColor: "black",
|
||||||
transparentCorners: false,
|
transparentCorners: false,
|
||||||
});
|
});
|
||||||
// Abilita ridimensionamento e trascinamento
|
|
||||||
canvas.on("object:modified", () => {
|
canvas.on("object:modified", () => {
|
||||||
console.log("Oggetto modificato nel canvas");
|
|
||||||
saveCanvasState();
|
saveCanvasState();
|
||||||
|
updateLayersPanel();
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
});
|
});
|
||||||
// Gestisci selezione per abilitare/disabilitare pulsanti
|
canvas.on("object:added", () => {
|
||||||
|
updateLayersPanel();
|
||||||
|
});
|
||||||
|
canvas.on("object:removed", () => {
|
||||||
|
updateLayersPanel();
|
||||||
|
});
|
||||||
canvas.on("selection:created", () => {
|
canvas.on("selection:created", () => {
|
||||||
console.log("Evento selection:created triggerato");
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
});
|
});
|
||||||
canvas.on("selection:updated", () => {
|
canvas.on("selection:updated", () => {
|
||||||
console.log("Evento selection:updated triggerato");
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
});
|
});
|
||||||
canvas.on("selection:cleared", () => {
|
canvas.on("selection:cleared", () => {
|
||||||
console.log("Evento selection:cleared triggerato");
|
|
||||||
if (!isCropping && !isRemovingBackground) {
|
if (!isCropping && !isRemovingBackground) {
|
||||||
updateButtons();
|
updateButtons();
|
||||||
} else if (isCropping && cropRect) {
|
} else if (isCropping && cropRect) {
|
||||||
console.log(
|
canvas.setActiveObject(cropRect);
|
||||||
"Ignoro selection:cleared perché in modalità ritaglio",
|
|
||||||
);
|
|
||||||
canvas.setActiveObject(cropRect); // Ripristina selezione del rettangolo
|
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
} else if (isRemovingBackground) {
|
} else if (isRemovingBackground) {
|
||||||
console.log(
|
canvas.setActiveObject(backgroundRemovalImage);
|
||||||
"Ignoro selection:cleared perché in modalità rimozione sfondo",
|
|
||||||
);
|
|
||||||
canvas.setActiveObject(backgroundRemovalImage); // Ripristina selezione dell'immagine
|
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Gestisci click sul canvas per la rimozione dello sfondo
|
|
||||||
canvas.on("mouse:down", (event) => {
|
canvas.on("mouse:down", (event) => {
|
||||||
if (isRemovingBackground && backgroundRemovalImage) {
|
if (isRemovingBackground && backgroundRemovalImage) {
|
||||||
console.log(
|
|
||||||
"Click sul canvas in modalità rimozione sfondo",
|
|
||||||
);
|
|
||||||
handleBackgroundColorSelection(event);
|
handleBackgroundColorSelection(event);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Salva lo stato iniziale del canvas
|
|
||||||
saveCanvasState();
|
saveCanvasState();
|
||||||
// Forza un aggiornamento iniziale dei pulsanti
|
updateLayersPanel();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Salva lo stato del canvas nella pila
|
function updateLayersPanel() {
|
||||||
function saveCanvasState() {
|
const layersList = document.getElementById("layersList");
|
||||||
if (isCropping || isRemovingBackground) {
|
if (!layersList) {
|
||||||
console.log(
|
console.error("Elemento layersList non trovato");
|
||||||
"Non salvo lo stato perché in modalità ritaglio o rimozione sfondo",
|
return;
|
||||||
);
|
}
|
||||||
|
layersList.innerHTML = "";
|
||||||
|
|
||||||
|
const images = canvas.getObjects("image");
|
||||||
|
images.forEach((img, index) => {
|
||||||
|
let thumbSrc;
|
||||||
|
try {
|
||||||
|
const thumbCanvas = document.createElement("canvas");
|
||||||
|
thumbCanvas.width = 50;
|
||||||
|
thumbCanvas.height = 50;
|
||||||
|
const thumbFabric = new fabric.Canvas(thumbCanvas);
|
||||||
|
|
||||||
|
const clonedImg = fabric.util.object.clone(img);
|
||||||
|
const scaleFactor = Math.min(
|
||||||
|
50 / img.width,
|
||||||
|
50 / img.height,
|
||||||
|
);
|
||||||
|
clonedImg.scaleX = scaleFactor;
|
||||||
|
clonedImg.scaleY = scaleFactor;
|
||||||
|
clonedImg.left = (50 - img.width * scaleFactor) / 2;
|
||||||
|
clonedImg.top = (50 - img.height * scaleFactor) / 2;
|
||||||
|
clonedImg.setCoords();
|
||||||
|
thumbFabric.add(clonedImg);
|
||||||
|
thumbFabric.renderAll();
|
||||||
|
|
||||||
|
thumbSrc = thumbCanvas.toDataURL("image/png");
|
||||||
|
thumbFabric.dispose();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
"Errore nella generazione della thumbnail per immagine",
|
||||||
|
index + 1,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
thumbSrc = img.getSrc(); // Fallback all'URL originale
|
||||||
|
}
|
||||||
|
|
||||||
|
const layerItem = document.createElement("li");
|
||||||
|
const thumbImg = document.createElement("img");
|
||||||
|
thumbImg.src = thumbSrc;
|
||||||
|
thumbImg.title = `Layer ${index + 1}`;
|
||||||
|
thumbImg.addEventListener("click", () => {
|
||||||
|
canvas.setActiveObject(img);
|
||||||
|
canvas.renderAll();
|
||||||
|
});
|
||||||
|
|
||||||
|
layerItem.appendChild(thumbImg);
|
||||||
|
layersList.appendChild(layerItem);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveCanvasState() {
|
||||||
|
if (isCropping || isRemovingBackground) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("Salvataggio stato del canvas");
|
|
||||||
const state = JSON.stringify(
|
const state = JSON.stringify(
|
||||||
canvas.toJSON([
|
canvas.toJSON([
|
||||||
"cornerColor",
|
"cornerColor",
|
||||||
@@ -510,26 +496,22 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
);
|
);
|
||||||
history.push(state);
|
history.push(state);
|
||||||
if (history.length > maxHistory) {
|
if (history.length > maxHistory) {
|
||||||
history.shift(); // Rimuovi lo stato più vecchio se superato il limite
|
history.shift();
|
||||||
}
|
}
|
||||||
console.log("Stato salvato, lunghezza pila:", history.length);
|
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ripristina l'ultimo stato del canvas
|
|
||||||
function undo() {
|
function undo() {
|
||||||
if (history.length <= 1) {
|
if (history.length <= 1) {
|
||||||
console.log("Nessuno stato da annullare");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("Annullamento ultima azione");
|
history.pop();
|
||||||
history.pop(); // Rimuovi lo stato corrente
|
|
||||||
const previousState = history[history.length - 1];
|
const previousState = history[history.length - 1];
|
||||||
if (previousState) {
|
if (previousState) {
|
||||||
canvas.clear();
|
canvas.clear();
|
||||||
canvas.loadFromJSON(previousState, () => {
|
canvas.loadFromJSON(previousState, () => {
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
console.log("Stato ripristinato");
|
updateLayersPanel();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@@ -537,11 +519,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
canvas.clear();
|
canvas.clear();
|
||||||
canvas.setBackgroundColor("#fff");
|
canvas.setBackgroundColor("#fff");
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
|
updateLayersPanel();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggiorna stato dei pulsanti
|
|
||||||
function updateButtons() {
|
function updateButtons() {
|
||||||
const cropBtn = document.getElementById("cropImageBtn");
|
const cropBtn = document.getElementById("cropImageBtn");
|
||||||
const applyCropBtn = document.getElementById("applyCropBtn");
|
const applyCropBtn = document.getElementById("applyCropBtn");
|
||||||
@@ -555,20 +537,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
"backgroundRemovalInstruction",
|
"backgroundRemovalInstruction",
|
||||||
);
|
);
|
||||||
const activeObject = canvas.getActiveObject();
|
const activeObject = canvas.getActiveObject();
|
||||||
console.log(
|
|
||||||
"updateButtons: activeObject =",
|
|
||||||
activeObject ? activeObject.type : null,
|
|
||||||
"isCropping =",
|
|
||||||
isCropping,
|
|
||||||
"isRemovingBackground =",
|
|
||||||
isRemovingBackground,
|
|
||||||
"history.length =",
|
|
||||||
history.length,
|
|
||||||
);
|
|
||||||
if (isCropping && cropRect) {
|
if (isCropping && cropRect) {
|
||||||
console.log(
|
|
||||||
"Modo ritaglio attivo, applyCropBtn e cancelCropBtn abilitati",
|
|
||||||
);
|
|
||||||
cropBtn.disabled = true;
|
cropBtn.disabled = true;
|
||||||
applyCropBtn.disabled = false;
|
applyCropBtn.disabled = false;
|
||||||
cancelCropBtn.disabled = false;
|
cancelCropBtn.disabled = false;
|
||||||
@@ -577,9 +546,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
undoBtn.disabled = true;
|
undoBtn.disabled = true;
|
||||||
instruction.style.display = "none";
|
instruction.style.display = "none";
|
||||||
} else if (isRemovingBackground && backgroundRemovalImage) {
|
} else if (isRemovingBackground && backgroundRemovalImage) {
|
||||||
console.log(
|
|
||||||
"Modo rimozione sfondo attivo, removeBackgroundBtn disabilitato",
|
|
||||||
);
|
|
||||||
cropBtn.disabled = true;
|
cropBtn.disabled = true;
|
||||||
applyCropBtn.disabled = true;
|
applyCropBtn.disabled = true;
|
||||||
cancelCropBtn.disabled = true;
|
cancelCropBtn.disabled = true;
|
||||||
@@ -593,9 +559,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
!isCropping &&
|
!isCropping &&
|
||||||
!isRemovingBackground
|
!isRemovingBackground
|
||||||
) {
|
) {
|
||||||
console.log(
|
|
||||||
"Abilitazione cropImageBtn, removeBackgroundBtn e removeImageBtn",
|
|
||||||
);
|
|
||||||
cropBtn.disabled = false;
|
cropBtn.disabled = false;
|
||||||
applyCropBtn.disabled = true;
|
applyCropBtn.disabled = true;
|
||||||
cancelCropBtn.disabled = true;
|
cancelCropBtn.disabled = true;
|
||||||
@@ -604,7 +567,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
undoBtn.disabled = history.length <= 1;
|
undoBtn.disabled = history.length <= 1;
|
||||||
instruction.style.display = "none";
|
instruction.style.display = "none";
|
||||||
} else {
|
} else {
|
||||||
console.log("Disabilitazione tutti i pulsanti");
|
|
||||||
cropBtn.disabled = true;
|
cropBtn.disabled = true;
|
||||||
applyCropBtn.disabled = true;
|
applyCropBtn.disabled = true;
|
||||||
cancelCropBtn.disabled = true;
|
cancelCropBtn.disabled = true;
|
||||||
@@ -615,7 +577,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entra in modalità ritaglio
|
|
||||||
function enterCropMode() {
|
function enterCropMode() {
|
||||||
const activeObject = canvas.getActiveObject();
|
const activeObject = canvas.getActiveObject();
|
||||||
if (!activeObject || activeObject.type !== "image") {
|
if (!activeObject || activeObject.type !== "image") {
|
||||||
@@ -623,14 +584,9 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
alert("Seleziona un'immagine prima di attivare il ritaglio!");
|
alert("Seleziona un'immagine prima di attivare il ritaglio!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(
|
|
||||||
"Entrata in modalità ritaglio per immagine:",
|
|
||||||
activeObject,
|
|
||||||
);
|
|
||||||
isCropping = true;
|
isCropping = true;
|
||||||
croppedImage = activeObject; // Memorizza l'immagine da ritagliare
|
croppedImage = activeObject;
|
||||||
canvas.discardActiveObject(); // Deseleziona l'immagine
|
canvas.discardActiveObject();
|
||||||
// Crea un rettangolo di ritaglio
|
|
||||||
cropRect = new fabric.Rect({
|
cropRect = new fabric.Rect({
|
||||||
left: activeObject.left,
|
left: activeObject.left,
|
||||||
top: activeObject.top,
|
top: activeObject.top,
|
||||||
@@ -653,13 +609,10 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
canvas.setActiveObject(cropRect);
|
canvas.setActiveObject(cropRect);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
console.log("Rettangolo di ritaglio creato e applicato");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Esci dalla modalità ritaglio
|
|
||||||
function exitCropMode() {
|
function exitCropMode() {
|
||||||
if (cropRect) {
|
if (cropRect) {
|
||||||
console.log("Rimozione rettangolo di ritaglio");
|
|
||||||
canvas.remove(cropRect);
|
canvas.remove(cropRect);
|
||||||
cropRect = null;
|
cropRect = null;
|
||||||
}
|
}
|
||||||
@@ -669,20 +622,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
canvas.discardActiveObject();
|
canvas.discardActiveObject();
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
console.log("Uscita dalla modalità ritaglio");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Applica il ritaglio
|
|
||||||
function applyCrop() {
|
function applyCrop() {
|
||||||
if (isApplyingCrop) {
|
if (isApplyingCrop) {
|
||||||
console.log("applyCrop già in esecuzione, ignoro chiamata");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("applyCrop chiamato, stato:", {
|
|
||||||
isCropping,
|
|
||||||
cropRect: !!cropRect,
|
|
||||||
croppedImage: !!croppedImage,
|
|
||||||
});
|
|
||||||
if (!isCropping || !cropRect || !croppedImage) {
|
if (!isCropping || !cropRect || !croppedImage) {
|
||||||
console.warn("Condizioni per il ritaglio non soddisfatte", {
|
console.warn("Condizioni per il ritaglio non soddisfatte", {
|
||||||
isCropping,
|
isCropping,
|
||||||
@@ -696,20 +641,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
isApplyingCrop = true;
|
isApplyingCrop = true;
|
||||||
console.log("Applicazione ritaglio all'immagine:", croppedImage);
|
|
||||||
const img = croppedImage;
|
const img = croppedImage;
|
||||||
const cropX = (cropRect.left - img.left) / img.scaleX;
|
const cropX = (cropRect.left - img.left) / img.scaleX;
|
||||||
const cropY = (cropRect.top - img.top) / img.scaleY;
|
const cropY = (cropRect.top - img.top) / img.scaleY;
|
||||||
const cropWidth = (cropRect.width * cropRect.scaleX) / img.scaleX;
|
const cropWidth = (cropRect.width * cropRect.scaleX) / img.scaleX;
|
||||||
const cropHeight = (cropRect.height * cropRect.scaleY) / img.scaleY;
|
const cropHeight = (cropRect.height * cropRect.scaleY) / img.scaleY;
|
||||||
console.log("Parametri di ritaglio:", {
|
|
||||||
cropX,
|
|
||||||
cropY,
|
|
||||||
cropWidth,
|
|
||||||
cropHeight,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Crea un'immagine ritagliata
|
|
||||||
fabric.Image.fromURL(
|
fabric.Image.fromURL(
|
||||||
img.getSrc(),
|
img.getSrc(),
|
||||||
(newImg) => {
|
(newImg) => {
|
||||||
@@ -730,20 +667,19 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
borderColor: "black",
|
borderColor: "black",
|
||||||
transparentCorners: false,
|
transparentCorners: false,
|
||||||
});
|
});
|
||||||
canvas.remove(img); // Rimuovi l'immagine originale
|
canvas.remove(img);
|
||||||
canvas.remove(cropRect); // Rimuovi il rettangolo di ritaglio
|
canvas.remove(cropRect);
|
||||||
canvas.add(newImg); // Aggiungi l'immagine ritagliata
|
canvas.add(newImg);
|
||||||
canvas.setActiveObject(newImg);
|
canvas.setActiveObject(newImg);
|
||||||
exitCropMode();
|
exitCropMode();
|
||||||
saveCanvasState(); // Salva lo stato dopo il ritaglio
|
saveCanvasState();
|
||||||
|
updateLayersPanel();
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
console.log("Ritaglio applicato con successo");
|
|
||||||
},
|
},
|
||||||
{ crossOrigin: "anonymous" },
|
{ crossOrigin: "anonymous" },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entra in modalità rimozione sfondo
|
|
||||||
function enterBackgroundRemovalMode() {
|
function enterBackgroundRemovalMode() {
|
||||||
const activeObject = canvas.getActiveObject();
|
const activeObject = canvas.getActiveObject();
|
||||||
if (!activeObject || activeObject.type !== "image") {
|
if (!activeObject || activeObject.type !== "image") {
|
||||||
@@ -755,29 +691,19 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log(
|
|
||||||
"Entrata in modalità rimozione sfondo per immagine:",
|
|
||||||
activeObject,
|
|
||||||
);
|
|
||||||
isRemovingBackground = true;
|
isRemovingBackground = true;
|
||||||
backgroundRemovalImage = activeObject;
|
backgroundRemovalImage = activeObject;
|
||||||
updateButtons();
|
updateButtons();
|
||||||
console.log(
|
|
||||||
"Modalità rimozione sfondo attivata, clicca sull'immagine per selezionare il colore",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Esci dalla modalità rimozione sfondo
|
|
||||||
function exitBackgroundRemovalMode() {
|
function exitBackgroundRemovalMode() {
|
||||||
isRemovingBackground = false;
|
isRemovingBackground = false;
|
||||||
backgroundRemovalImage = null;
|
backgroundRemovalImage = null;
|
||||||
canvas.discardActiveObject();
|
canvas.discardActiveObject();
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
updateButtons();
|
updateButtons();
|
||||||
console.log("Uscita dalla modalità rimozione sfondo");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestisci la selezione del colore dello sfondo
|
|
||||||
function handleBackgroundColorSelection(event) {
|
function handleBackgroundColorSelection(event) {
|
||||||
if (!isRemovingBackground || !backgroundRemovalImage) {
|
if (!isRemovingBackground || !backgroundRemovalImage) {
|
||||||
console.warn(
|
console.warn(
|
||||||
@@ -788,14 +714,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const pointer = canvas.getPointer(event.e);
|
const pointer = canvas.getPointer(event.e);
|
||||||
const img = backgroundRemovalImage;
|
const img = backgroundRemovalImage;
|
||||||
|
|
||||||
// Crea una canvas temporanea per ottenere i dati dei pixel
|
|
||||||
const tempCanvas = document.createElement("canvas");
|
const tempCanvas = document.createElement("canvas");
|
||||||
tempCanvas.width = img.width;
|
tempCanvas.width = img.width;
|
||||||
tempCanvas.height = img.height;
|
tempCanvas.height = img.height;
|
||||||
const ctx = tempCanvas.getContext("2d");
|
const ctx = tempCanvas.getContext("2d");
|
||||||
ctx.drawImage(img.getElement(), 0, 0, img.width, img.height);
|
ctx.drawImage(img.getElement(), 0, 0, img.width, img.height);
|
||||||
|
|
||||||
// Calcola la posizione relativa del click rispetto all'immagine
|
|
||||||
const imgLeft = img.left;
|
const imgLeft = img.left;
|
||||||
const imgTop = img.top;
|
const imgTop = img.top;
|
||||||
const scaleX = img.scaleX;
|
const scaleX = img.scaleX;
|
||||||
@@ -803,22 +727,17 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const x = (pointer.x - imgLeft) / scaleX;
|
const x = (pointer.x - imgLeft) / scaleX;
|
||||||
const y = (pointer.y - imgTop) / scaleY;
|
const y = (pointer.y - imgTop) / scaleY;
|
||||||
|
|
||||||
// Ottieni il colore del pixel cliccato
|
|
||||||
const pixelData = ctx.getImageData(x, y, 1, 1).data;
|
const pixelData = ctx.getImageData(x, y, 1, 1).data;
|
||||||
const targetColor = {
|
const targetColor = {
|
||||||
r: pixelData[0],
|
r: pixelData[0],
|
||||||
g: pixelData[1],
|
g: pixelData[1],
|
||||||
b: pixelData[2],
|
b: pixelData[2],
|
||||||
};
|
};
|
||||||
console.log("Colore dello sfondo selezionato:", targetColor);
|
|
||||||
|
|
||||||
// Rimuovi il colore dello sfondo
|
|
||||||
removeBackground(img, targetColor);
|
removeBackground(img, targetColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rimuovi il colore dello sfondo
|
|
||||||
function removeBackground(img, targetColor) {
|
function removeBackground(img, targetColor) {
|
||||||
console.log("Inizio rimozione sfondo con colore:", targetColor);
|
|
||||||
const tempCanvas = document.createElement("canvas");
|
const tempCanvas = document.createElement("canvas");
|
||||||
tempCanvas.width = img.width;
|
tempCanvas.width = img.width;
|
||||||
tempCanvas.height = img.height;
|
tempCanvas.height = img.height;
|
||||||
@@ -832,27 +751,24 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
tempCanvas.height,
|
tempCanvas.height,
|
||||||
);
|
);
|
||||||
const data = imageData.data;
|
const data = imageData.data;
|
||||||
const tolerance = 50; // Tolleranza per colori simili
|
const tolerance = 50;
|
||||||
|
|
||||||
// Scansiona i pixel e rendi trasparenti quelli che corrispondono al colore target
|
|
||||||
for (let i = 0; i < data.length; i += 4) {
|
for (let i = 0; i < data.length; i += 4) {
|
||||||
const r = data[i];
|
const r = data[i];
|
||||||
const g = data[i + 1];
|
const g = data[i + 1];
|
||||||
const b = data[i + 2];
|
const b = data[i + 2];
|
||||||
// Verifica se il colore è simile al targetColor
|
|
||||||
if (
|
if (
|
||||||
Math.abs(r - targetColor.r) <= tolerance &&
|
Math.abs(r - targetColor.r) <= tolerance &&
|
||||||
Math.abs(g - targetColor.g) <= tolerance &&
|
Math.abs(g - targetColor.g) <= tolerance &&
|
||||||
Math.abs(b - targetColor.b) <= tolerance
|
Math.abs(b - targetColor.b) <= tolerance
|
||||||
) {
|
) {
|
||||||
data[i + 3] = 0; // Imposta l'alpha a 0 (trasparente)
|
data[i + 3] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.putImageData(imageData, 0, 0);
|
ctx.putImageData(imageData, 0, 0);
|
||||||
const newImageUrl = tempCanvas.toDataURL("image/png");
|
const newImageUrl = tempCanvas.toDataURL("image/png");
|
||||||
|
|
||||||
// Crea una nuova immagine con lo sfondo rimosso
|
|
||||||
fabric.Image.fromURL(
|
fabric.Image.fromURL(
|
||||||
newImageUrl,
|
newImageUrl,
|
||||||
(newImg) => {
|
(newImg) => {
|
||||||
@@ -869,19 +785,18 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
borderColor: "black",
|
borderColor: "black",
|
||||||
transparentCorners: false,
|
transparentCorners: false,
|
||||||
});
|
});
|
||||||
canvas.remove(img); // Rimuovi l'immagine originale
|
canvas.remove(img);
|
||||||
canvas.add(newImg); // Aggiungi l'immagine con sfondo rimosso
|
canvas.add(newImg);
|
||||||
canvas.setActiveObject(newImg);
|
canvas.setActiveObject(newImg);
|
||||||
exitBackgroundRemovalMode();
|
exitBackgroundRemovalMode();
|
||||||
saveCanvasState(); // Salva lo stato dopo la rimozione dello sfondo
|
saveCanvasState();
|
||||||
|
updateLayersPanel();
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
console.log("Sfondo rimosso con successo");
|
|
||||||
},
|
},
|
||||||
{ crossOrigin: "anonymous" },
|
{ crossOrigin: "anonymous" },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rimuovi l'immagine selezionata
|
|
||||||
function removeImage() {
|
function removeImage() {
|
||||||
const activeObject = canvas.getActiveObject();
|
const activeObject = canvas.getActiveObject();
|
||||||
if (!activeObject || activeObject.type !== "image") {
|
if (!activeObject || activeObject.type !== "image") {
|
||||||
@@ -889,15 +804,13 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
alert("Seleziona un'immagine da rimuovere!");
|
alert("Seleziona un'immagine da rimuovere!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
console.log("Rimozione immagine selezionata:", activeObject);
|
|
||||||
canvas.remove(activeObject);
|
canvas.remove(activeObject);
|
||||||
canvas.discardActiveObject();
|
canvas.discardActiveObject();
|
||||||
saveCanvasState(); // Salva lo stato dopo la rimozione
|
saveCanvasState();
|
||||||
|
updateLayersPanel();
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
console.log("Immagine rimossa con successo");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aggiungi foto selezionate al canvas
|
|
||||||
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
|
const addToCanvasBtn = document.getElementById("addToCanvasBtn");
|
||||||
if (addToCanvasBtn) {
|
if (addToCanvasBtn) {
|
||||||
addToCanvasBtn.addEventListener("click", () => {
|
addToCanvasBtn.addEventListener("click", () => {
|
||||||
@@ -914,11 +827,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
imgPath,
|
imgPath,
|
||||||
(img) => {
|
(img) => {
|
||||||
img.set({
|
img.set({
|
||||||
left: Math.random() * 600, // Posizione random iniziale
|
left: Math.random() * 600,
|
||||||
top: Math.random() * 400,
|
top: Math.random() * 400,
|
||||||
scaleX: 0.5, // Scala iniziale
|
scaleX: 0.5,
|
||||||
scaleY: 0.5,
|
scaleY: 0.5,
|
||||||
hasControls: true, // Abilita resize/rotate
|
hasControls: true,
|
||||||
hasBorders: true,
|
hasBorders: true,
|
||||||
cornerColor: "black",
|
cornerColor: "black",
|
||||||
cornerStrokeColor: "black",
|
cornerStrokeColor: "black",
|
||||||
@@ -928,25 +841,22 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
canvas.add(img);
|
canvas.add(img);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
console.log(
|
|
||||||
"Immagine aggiunta al canvas:",
|
|
||||||
imgPath,
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
{ crossOrigin: "anonymous" },
|
{ crossOrigin: "anonymous" },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
// Deseleziona checkbox dopo aggiunta
|
|
||||||
checkboxes.forEach((cb) => (cb.checked = false));
|
checkboxes.forEach((cb) => (cb.checked = false));
|
||||||
saveCanvasState(); // Salva lo stato dopo l'aggiunta
|
saveCanvasState();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Salva collage
|
|
||||||
const saveCollageBtn = document.getElementById("saveCollageBtn");
|
const saveCollageBtn = document.getElementById("saveCollageBtn");
|
||||||
if (saveCollageBtn) {
|
if (saveCollageBtn) {
|
||||||
saveCollageBtn.addEventListener("click", async () => {
|
saveCollageBtn.addEventListener("click", async () => {
|
||||||
if (canvas.getObjects().length === 0) {
|
if (
|
||||||
|
canvas.getObjects().length === 0 &&
|
||||||
|
!canvas.backgroundImage
|
||||||
|
) {
|
||||||
alert("Il canvas è vuoto! Aggiungi almeno una foto.");
|
alert("Il canvas è vuoto! Aggiungi almeno una foto.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -959,29 +869,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
type: "image/jpeg",
|
type: "image/jpeg",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Upload come nuova foto
|
|
||||||
await handleFiles([file], iddatadb);
|
await handleFiles([file], iddatadb);
|
||||||
// Chiudi modale e ricarica popup
|
|
||||||
document.getElementById("collageModal").style.display = "none";
|
document.getElementById("collageModal").style.display = "none";
|
||||||
loadPopupContent(iddatadb);
|
loadPopupContent(iddatadb);
|
||||||
console.log("Collage salvato e modale chiuso");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pulisci canvas
|
|
||||||
const clearCanvasBtn = document.getElementById("clearCanvasBtn");
|
|
||||||
if (clearCanvasBtn) {
|
|
||||||
clearCanvasBtn.addEventListener("click", () => {
|
|
||||||
canvas.clear();
|
|
||||||
canvas.setBackgroundColor("#fff");
|
|
||||||
history = []; // Resetta la pila di stati
|
|
||||||
saveCanvasState(); // Salva lo stato vuoto
|
|
||||||
canvas.renderAll();
|
|
||||||
console.log("Canvas pulito");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gestione livelli delle immagini
|
|
||||||
const bringToFrontBtn = document.getElementById("bringToFrontBtn");
|
const bringToFrontBtn = document.getElementById("bringToFrontBtn");
|
||||||
if (bringToFrontBtn) {
|
if (bringToFrontBtn) {
|
||||||
bringToFrontBtn.addEventListener("click", () => {
|
bringToFrontBtn.addEventListener("click", () => {
|
||||||
@@ -989,8 +882,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (activeObject) {
|
if (activeObject) {
|
||||||
canvas.bringToFront(activeObject);
|
canvas.bringToFront(activeObject);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
saveCanvasState(); // Salva lo stato dopo il cambio di livello
|
saveCanvasState();
|
||||||
console.log("Oggetto portato in primo piano");
|
updateLayersPanel();
|
||||||
} else {
|
} else {
|
||||||
alert("Seleziona un'immagine sul canvas!");
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
}
|
}
|
||||||
@@ -1004,8 +897,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (activeObject) {
|
if (activeObject) {
|
||||||
canvas.sendToBack(activeObject);
|
canvas.sendToBack(activeObject);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
saveCanvasState(); // Salva lo stato dopo il cambio di livello
|
saveCanvasState();
|
||||||
console.log("Oggetto mandato in fondo");
|
updateLayersPanel();
|
||||||
} else {
|
} else {
|
||||||
alert("Seleziona un'immagine sul canvas!");
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
}
|
}
|
||||||
@@ -1019,8 +912,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (activeObject) {
|
if (activeObject) {
|
||||||
canvas.bringForward(activeObject);
|
canvas.bringForward(activeObject);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
saveCanvasState(); // Salva lo stato dopo il cambio di livello
|
saveCanvasState();
|
||||||
console.log("Oggetto spostato avanti di un livello");
|
updateLayersPanel();
|
||||||
} else {
|
} else {
|
||||||
alert("Seleziona un'immagine sul canvas!");
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
}
|
}
|
||||||
@@ -1034,19 +927,17 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (activeObject) {
|
if (activeObject) {
|
||||||
canvas.sendBackwards(activeObject);
|
canvas.sendBackwards(activeObject);
|
||||||
canvas.renderAll();
|
canvas.renderAll();
|
||||||
saveCanvasState(); // Salva lo stato dopo il cambio di livello
|
saveCanvasState();
|
||||||
console.log("Oggetto spostato indietro di un livello");
|
updateLayersPanel();
|
||||||
} else {
|
} else {
|
||||||
alert("Seleziona un'immagine sul canvas!");
|
alert("Seleziona un'immagine sul canvas!");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestione ritaglio immagini
|
|
||||||
const cropImageBtn = document.getElementById("cropImageBtn");
|
const cropImageBtn = document.getElementById("cropImageBtn");
|
||||||
if (cropImageBtn) {
|
if (cropImageBtn) {
|
||||||
cropImageBtn.addEventListener("click", () => {
|
cropImageBtn.addEventListener("click", () => {
|
||||||
console.log("Pulsante Ritaglia cliccato");
|
|
||||||
enterCropMode();
|
enterCropMode();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1054,7 +945,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const applyCropBtn = document.getElementById("applyCropBtn");
|
const applyCropBtn = document.getElementById("applyCropBtn");
|
||||||
if (applyCropBtn) {
|
if (applyCropBtn) {
|
||||||
applyCropBtn.addEventListener("click", () => {
|
applyCropBtn.addEventListener("click", () => {
|
||||||
console.log("Pulsante Applica Ritaglio cliccato");
|
|
||||||
applyCrop();
|
applyCrop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1062,49 +952,39 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
const cancelCropBtn = document.getElementById("cancelCropBtn");
|
const cancelCropBtn = document.getElementById("cancelCropBtn");
|
||||||
if (cancelCropBtn) {
|
if (cancelCropBtn) {
|
||||||
cancelCropBtn.addEventListener("click", () => {
|
cancelCropBtn.addEventListener("click", () => {
|
||||||
console.log("Pulsante Annulla Ritaglio cliccato");
|
|
||||||
exitCropMode();
|
exitCropMode();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestione rimozione sfondo
|
|
||||||
const removeBackgroundBtn = document.getElementById(
|
const removeBackgroundBtn = document.getElementById(
|
||||||
"removeBackgroundBtn",
|
"removeBackgroundBtn",
|
||||||
);
|
);
|
||||||
if (removeBackgroundBtn) {
|
if (removeBackgroundBtn) {
|
||||||
removeBackgroundBtn.addEventListener("click", () => {
|
removeBackgroundBtn.addEventListener("click", () => {
|
||||||
console.log("Pulsante Rimuovi Sfondo cliccato");
|
|
||||||
enterBackgroundRemovalMode();
|
enterBackgroundRemovalMode();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestione rimozione immagine
|
|
||||||
const removeImageBtn = document.getElementById("removeImageBtn");
|
const removeImageBtn = document.getElementById("removeImageBtn");
|
||||||
if (removeImageBtn) {
|
if (removeImageBtn) {
|
||||||
removeImageBtn.addEventListener("click", () => {
|
removeImageBtn.addEventListener("click", () => {
|
||||||
console.log("Pulsante Rimuovi Immagine cliccato");
|
|
||||||
removeImage();
|
removeImage();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestione undo
|
|
||||||
const undoBtn = document.getElementById("undoBtn");
|
const undoBtn = document.getElementById("undoBtn");
|
||||||
if (undoBtn) {
|
if (undoBtn) {
|
||||||
undoBtn.addEventListener("click", () => {
|
undoBtn.addEventListener("click", () => {
|
||||||
console.log("Pulsante Annulla cliccato");
|
|
||||||
undo();
|
undo();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assicurati che il loader sia nascosto all'apertura del popup
|
|
||||||
const loader = document.getElementById("loader");
|
const loader = document.getElementById("loader");
|
||||||
if (loader) {
|
if (loader) {
|
||||||
console.log("Nascondo loader all'apertura del popup");
|
|
||||||
loader.style.display = "none";
|
loader.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gestione del pulsante Photos
|
|
||||||
const photosButtons = document.querySelectorAll(".photos-btn");
|
const photosButtons = document.querySelectorAll(".photos-btn");
|
||||||
const photosModal = document.getElementById("photosModal");
|
const photosModal = document.getElementById("photosModal");
|
||||||
const closeBtn = document.querySelector(".close-btn");
|
const closeBtn = document.querySelector(".close-btn");
|
||||||
@@ -1112,10 +992,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
if (photosButtons.length && photosModal && closeBtn) {
|
if (photosButtons.length && photosModal && closeBtn) {
|
||||||
photosButtons.forEach((button) => {
|
photosButtons.forEach((button) => {
|
||||||
button.addEventListener("click", function () {
|
button.addEventListener("click", function () {
|
||||||
console.log(
|
|
||||||
"Pulsante Photos cliccato per iddatadb:",
|
|
||||||
this.getAttribute("data-iddatadb"),
|
|
||||||
);
|
|
||||||
const iddatadb = this.getAttribute("data-iddatadb");
|
const iddatadb = this.getAttribute("data-iddatadb");
|
||||||
loadPopupContent(iddatadb);
|
loadPopupContent(iddatadb);
|
||||||
photosModal.style.display = "block";
|
photosModal.style.display = "block";
|
||||||
@@ -1124,7 +1000,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
closeBtn.addEventListener("click", function () {
|
closeBtn.addEventListener("click", function () {
|
||||||
console.log("Chiusura modale photos");
|
|
||||||
photosModal.style.display = "none";
|
photosModal.style.display = "none";
|
||||||
document.querySelector(".overlay").style.display = "none";
|
document.querySelector(".overlay").style.display = "none";
|
||||||
document.body.style.pointerEvents = "auto";
|
document.body.style.pointerEvents = "auto";
|
||||||
@@ -1132,7 +1007,6 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
|
|
||||||
window.addEventListener("click", function (event) {
|
window.addEventListener("click", function (event) {
|
||||||
if (event.target === photosModal) {
|
if (event.target === photosModal) {
|
||||||
console.log("Chiusura modale photos cliccando sullo sfondo");
|
|
||||||
photosModal.style.display = "none";
|
photosModal.style.display = "none";
|
||||||
document.querySelector(".overlay").style.display = "none";
|
document.querySelector(".overlay").style.display = "none";
|
||||||
document.body.style.pointerEvents = "auto";
|
document.body.style.pointerEvents = "auto";
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ use Endroid\QrCode\Writer\PngWriter;
|
|||||||
try {
|
try {
|
||||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../../');
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__ . '/../../');
|
||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
error_log("File .env caricato correttamente da " . __DIR__ . '/../../.env');
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log("Errore nel caricamento del file .env: " . $e->getMessage());
|
error_log("Errore nel caricamento del file .env: " . $e->getMessage());
|
||||||
echo json_encode(['error' => 'Errore nel caricamento del file di configurazione']);
|
echo json_encode(['error' => 'Errore nel caricamento del file di configurazione']);
|
||||||
@@ -176,7 +175,7 @@ $result->saveToFile($qrCodeFile);
|
|||||||
<!-- Modale per collage -->
|
<!-- Modale per collage -->
|
||||||
<div id="collageModal" class="modal" style="display: none; position: fixed; z-index: 1002; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.8);">
|
<div id="collageModal" class="modal" style="display: none; position: fixed; z-index: 1002; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgba(0,0,0,0.8);">
|
||||||
<div class="modal-content" style="background: white; margin: 5% auto; padding: 20px; width: 80%; max-width: 1200px; position: relative;">
|
<div class="modal-content" style="background: white; margin: 5% auto; padding: 20px; width: 80%; max-width: 1200px; position: relative;">
|
||||||
<span class="close-collage" style="position: absolute; top: 10px; right: 20px; font-size: 30px; cursor: pointer;">×</span>
|
<span class="close-collage" style="position: absolute; top: 10px; right: 20px; font-size: 30px; font-weight: bold; color: #333; cursor: pointer; z-index: 1003; background: #fff; padding: 5px 10px; border-radius: 50%;">×</span>
|
||||||
<h3>Crea Collage</h3>
|
<h3>Crea Collage</h3>
|
||||||
|
|
||||||
<!-- Lista foto selezionabili -->
|
<!-- Lista foto selezionabili -->
|
||||||
@@ -195,10 +194,15 @@ $result->saveToFile($qrCodeFile);
|
|||||||
<!-- Canvas per editing -->
|
<!-- Canvas per editing -->
|
||||||
<canvas id="collageCanvas" width="800" height="600" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
<canvas id="collageCanvas" width="800" height="600" style="border: 1px solid #ccc; margin-top: 20px;"></canvas>
|
||||||
|
|
||||||
|
<!-- Pannello dei livelli -->
|
||||||
|
<div id="layersPanel" style="width: 120px; max-height: 600px; overflow-y: auto; background: #f8f9fa; padding: 10px; position: absolute; right: 0; top: 60px;">
|
||||||
|
<h4 style="margin: 0 0 10px 0; font-size: 16px;">Livelli</h4>
|
||||||
|
<ul id="layersList" style="list-style: none; padding: 0;"></ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Bottoni azioni -->
|
<!-- Bottoni azioni -->
|
||||||
<div style="margin-top: 20px; display: flex; flex-wrap: wrap; gap: 5px;">
|
<div style="margin-top: 20px; display: flex; flex-wrap: wrap; gap: 5px;">
|
||||||
<button id="saveCollageBtn" title="Salva il collage"><i class="fas fa-save"></i></button>
|
<button id="saveCollageBtn" title="Salva il collage"><i class="fas fa-save"></i></button>
|
||||||
<!-- <button id="clearCanvasBtn" title="Pulisci il canvas"><i class="fas fa-trash"></i></button> -->
|
|
||||||
<button id="bringToFrontBtn" title="Porta in primo piano"><i class="fas fa-arrow-up"></i></button>
|
<button id="bringToFrontBtn" title="Porta in primo piano"><i class="fas fa-arrow-up"></i></button>
|
||||||
<button id="sendToBackBtn" title="Manda in fondo"><i class="fas fa-arrow-down"></i></button>
|
<button id="sendToBackBtn" title="Manda in fondo"><i class="fas fa-arrow-down"></i></button>
|
||||||
<button id="bringForwardBtn" title="Sposta avanti di un livello"><i class="fas fa-arrow-circle-up"></i></button>
|
<button id="bringForwardBtn" title="Sposta avanti di un livello"><i class="fas fa-arrow-circle-up"></i></button>
|
||||||
@@ -311,11 +315,6 @@ $result->saveToFile($qrCodeFile);
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
#clearCanvasBtn {
|
|
||||||
background: #dc3545;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
#bringToFrontBtn,
|
#bringToFrontBtn,
|
||||||
#sendToBackBtn,
|
#sendToBackBtn,
|
||||||
#bringForwardBtn,
|
#bringForwardBtn,
|
||||||
@@ -323,7 +322,6 @@ $result->saveToFile($qrCodeFile);
|
|||||||
background: #007bff;
|
background: #007bff;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
/* Pulsanti solo icona più piccoli */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#cropImageBtn,
|
#cropImageBtn,
|
||||||
@@ -344,4 +342,25 @@ $result->saveToFile($qrCodeFile);
|
|||||||
#collageModal button i {
|
#collageModal button i {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stile per il pannello dei livelli */
|
||||||
|
#layersPanel {
|
||||||
|
z-index: 1002;
|
||||||
|
}
|
||||||
|
|
||||||
|
#layersPanel li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#layersPanel img {
|
||||||
|
width: 50px;
|
||||||
|
height: 50px;
|
||||||
|
border: 2px solid #ccc;
|
||||||
|
cursor: pointer;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
#layersPanel img:hover {
|
||||||
|
border-color: #007bff;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -0,0 +1,381 @@
|
|||||||
|
<?php
|
||||||
|
// Abilita errori per debug
|
||||||
|
ini_set('display_errors', 1);
|
||||||
|
ini_set('display_startup_errors', 1);
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
ini_set('error_log', __DIR__ . '/quotations_debug.log');
|
||||||
|
if (!file_exists(__DIR__ . '/quotations_debug.log')) {
|
||||||
|
file_put_contents(__DIR__ . '/quotations_debug.log', "Inizio operazioni alle " . date('Y-m-d H:i:s') . "\n", FILE_APPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log iniziale
|
||||||
|
error_log("Inizio operazioni alle " . date('Y-m-d H:i:s'));
|
||||||
|
|
||||||
|
include('include/headscript.php');
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
// Recupera l'ID dell'utente loggato
|
||||||
|
$user_id = $iduserlogin ?? 1;
|
||||||
|
|
||||||
|
// Gestione creazione nuova quotation (crea record vuoto su conferma)
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'create') {
|
||||||
|
$description = '';
|
||||||
|
$customer = '';
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO quotations (description, customer, iduser) VALUES (?, ?, ?)");
|
||||||
|
$stmt->execute([$description, $customer, $user_id]);
|
||||||
|
$newId = $pdo->lastInsertId();
|
||||||
|
|
||||||
|
// Log creazione
|
||||||
|
error_log("Creata nuova quotation ID: $newId");
|
||||||
|
|
||||||
|
// Reindirizza alla modifica della nuova quotation
|
||||||
|
header("Location: quotations.php?edit_id=" . $newId . "&status=success&message=" . urlencode("Quotation creata con successo"));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestione modifica quotation
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'update' && isset($_POST['id'])) {
|
||||||
|
$id = intval($_POST['id']);
|
||||||
|
$description = $_POST['description'] ?? '';
|
||||||
|
$customer = $_POST['customer'] ?? '';
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("UPDATE quotations SET description = ?, customer = ? WHERE id = ? AND iduser = ?");
|
||||||
|
$stmt->execute([$description, $customer, $id, $user_id]);
|
||||||
|
|
||||||
|
// Log modifica
|
||||||
|
error_log("Modificata quotation ID: $id");
|
||||||
|
|
||||||
|
// Reindirizza alla lista delle quotations
|
||||||
|
header("Location: quotations.php?status=success&message=" . urlencode("Quotation modificata con successo"));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestione cancellazione quotation
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'delete' && isset($_POST['id'])) {
|
||||||
|
$id = intval($_POST['id']);
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("DELETE FROM quotations WHERE id = ? AND iduser = ?");
|
||||||
|
$stmt->execute([$id, $user_id]);
|
||||||
|
|
||||||
|
// Log cancellazione
|
||||||
|
error_log("Cancellata quotation ID: $id");
|
||||||
|
|
||||||
|
header("Location: quotations.php?status=success&message=" . urlencode("Quotation cancellata con successo"));
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recupera tutte le quotations per l'utente
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM quotations WHERE iduser = ? ORDER BY creation_date DESC");
|
||||||
|
$stmt->execute([$user_id]);
|
||||||
|
$quotations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
// Verifica se è richiesta la modifica di una quotation
|
||||||
|
$editQuotation = null;
|
||||||
|
if (isset($_GET['edit_id'])) {
|
||||||
|
$editId = intval($_GET['edit_id']);
|
||||||
|
$stmt = $pdo->prepare("SELECT * FROM quotations WHERE id = ? AND iduser = ?");
|
||||||
|
$stmt->execute([$editId, $user_id]);
|
||||||
|
$editQuotation = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||||
|
<?php include('cssinclude.php'); ?>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2Lw==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css">
|
||||||
|
<style>
|
||||||
|
/* Stili simili alla pagina fornita, adattati */
|
||||||
|
.cell-changed {
|
||||||
|
background-color: #fff3b0 !important;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.manual-input,
|
||||||
|
select.manual-input {
|
||||||
|
background-color: #fff3cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.required-input,
|
||||||
|
select.required-input {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
}
|
||||||
|
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: 1px solid #ced4da;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
padding: 6px 8px;
|
||||||
|
margin-right: 5px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 35px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.save-btn {
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
background-color: #dc3545;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos-btn {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parts-btn {
|
||||||
|
background-color: #ffc107;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group textarea {
|
||||||
|
height: 100px;
|
||||||
|
resize: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flash-success {
|
||||||
|
background-color: #d4edda !important;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quotation-actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<title>Gestione Quotations - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrapper">
|
||||||
|
<?php include('include/navbar.php'); ?>
|
||||||
|
<?php include('include/topbar.php'); ?>
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="card radius-10">
|
||||||
|
<div class="card-header">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-0">Gestione Quotations</h6>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<?php if ($editQuotation): ?>
|
||||||
|
<!-- Modifica Quotation -->
|
||||||
|
<h6 class="mb-3">Modifica Quotation ID: <?= $editQuotation['id'] ?></h6>
|
||||||
|
<form id="editForm" method="post">
|
||||||
|
<input type="hidden" name="action" value="update">
|
||||||
|
<input type="hidden" name="id" value="<?= $editQuotation['id'] ?>">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="description">Descrizione</label>
|
||||||
|
<textarea id="description" name="description" class="manual-input required-input" required><?= htmlspecialchars($editQuotation['description']) ?></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="customer">Cliente</label>
|
||||||
|
<input type="text" id="customer" name="customer" class="manual-input required-input" value="<?= htmlspecialchars($editQuotation['customer']) ?>" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Salva Modifiche</button>
|
||||||
|
<a href="quotations.php" class="btn btn-secondary">Torna alla Lista</a>
|
||||||
|
</form>
|
||||||
|
<div class="quotation-actions">
|
||||||
|
<h6 class="mb-3">Azioni</h6>
|
||||||
|
<button type="button" class="photos-btn action-btn" data-idquotations="<?= $editQuotation['id'] ?>" title="Photos"><i class="fas fa-camera"></i></button>
|
||||||
|
<button type="button" class="parts-btn action-btn" data-idquotations="<?= $editQuotation['id'] ?>" title="Parts"><i class="fas fa-puzzle-piece"></i></button>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<!-- Lista Quotations -->
|
||||||
|
<div class="mb-3">
|
||||||
|
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createModal">Crea Nuova Quotation</button>
|
||||||
|
</div>
|
||||||
|
<h6 class="mb-3">Quotations Esistenti</h6>
|
||||||
|
<table id="quotationsTable" class="table table-striped table-bordered">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>Data Creazione</th>
|
||||||
|
<th>Descrizione</th>
|
||||||
|
<th>Cliente</th>
|
||||||
|
<th>Azioni</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($quotations as $row): ?>
|
||||||
|
<tr data-id="<?= $row['id'] ?>">
|
||||||
|
<td><?= htmlspecialchars($row['id']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($row['creation_date']) ?></td>
|
||||||
|
<td>
|
||||||
|
<textarea name="description" class="cell-input manual-input form-control"><?= htmlspecialchars($row['description']) ?></textarea>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="text" name="customer" class="cell-input manual-input form-control" value="<?= htmlspecialchars($row['customer']) ?>">
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<button type="button" class="save-btn action-btn edit-btn" data-id="<?= $row['id'] ?>" title="Salva Modifiche"><i class="fas fa-save"></i></button>
|
||||||
|
<button type="button" class="delete-btn action-btn" data-id="<?= $row['id'] ?>" title="Cancella" data-bs-toggle="modal" data-bs-target="#deleteModal"><i class="fas fa-trash"></i></button>
|
||||||
|
<button type="button" class="photos-btn action-btn" data-idquotations="<?= $row['id'] ?>" title="Photos"><i class="fas fa-camera"></i></button>
|
||||||
|
<button type="button" class="parts-btn action-btn" data-idquotations="<?= $row['id'] ?>" title="Parts"><i class="fas fa-puzzle-piece"></i></button>
|
||||||
|
<a href="quotations.php?edit_id=<?= $row['id'] ?>" class="btn btn-secondary action-btn" title="Modifica Dettagliata"><i class="fas fa-edit"></i></a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Modal per conferma creazione nuova quotation -->
|
||||||
|
<div class="modal fade" id="createModal" tabindex="-1" aria-labelledby="createModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="createModalLabel">Conferma Creazione Nuova Quotation</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Vuoi creare una nuova quotation?</p>
|
||||||
|
<form id="createModalForm" method="post">
|
||||||
|
<input type="hidden" name="action" value="create">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="confirmCreate">Conferma</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- Modal per conferma cancellazione -->
|
||||||
|
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="deleteModalLabel">Conferma Cancellazione</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p>Sicuro di voler cancellare questa quotation?</p>
|
||||||
|
<form id="deleteForm" method="post">
|
||||||
|
<input type="hidden" name="action" value="delete">
|
||||||
|
<input type="hidden" name="id" id="deleteQuotationId">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Annulla</button>
|
||||||
|
<button type="button" class="btn btn-danger" id="confirmDelete">Conferma</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="overlay toggle-icon"></div>
|
||||||
|
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||||
|
<?php include('include/footer.php'); ?>
|
||||||
|
</div>
|
||||||
|
<?php include('modal_parts.php'); ?>
|
||||||
|
<?php include('photos_functions.php'); ?>
|
||||||
|
<?php include('jsinclude.php'); ?>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script src="photos.js"></script>
|
||||||
|
<script src="parts.js"></script>
|
||||||
|
<script>
|
||||||
|
document.addEventListener("DOMContentLoaded", function() {
|
||||||
|
// Inizializza DataTables se non siamo in modalità modifica
|
||||||
|
if (!document.querySelector('#editForm')) {
|
||||||
|
$('#quotationsTable').DataTable({
|
||||||
|
"paging": true,
|
||||||
|
"searching": true,
|
||||||
|
"ordering": true,
|
||||||
|
"info": true,
|
||||||
|
"autoWidth": false,
|
||||||
|
"responsive": true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gestione conferma creazione nel modal
|
||||||
|
document.getElementById('confirmCreate').addEventListener('click', function() {
|
||||||
|
document.getElementById('createModalForm').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gestione modifica inline e save nella lista
|
||||||
|
document.querySelectorAll('.edit-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
const row = this.closest('tr');
|
||||||
|
const id = row.dataset.id;
|
||||||
|
const description = row.querySelector('textarea[name="description"]').value;
|
||||||
|
const customer = row.querySelector('input[name="customer"]').value;
|
||||||
|
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('action', 'update');
|
||||||
|
formData.append('id', id);
|
||||||
|
formData.append('description', description);
|
||||||
|
formData.append('customer', customer);
|
||||||
|
|
||||||
|
fetch('quotations.php', {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData
|
||||||
|
}).then(response => {
|
||||||
|
if (response.ok) {
|
||||||
|
row.classList.add('flash-success');
|
||||||
|
setTimeout(() => row.classList.remove('flash-success'), 500);
|
||||||
|
alert('Quotation modificata con successo!');
|
||||||
|
} else {
|
||||||
|
alert('Errore durante la modifica.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gestione apertura modal di cancellazione
|
||||||
|
document.querySelectorAll('.delete-btn').forEach(btn => {
|
||||||
|
btn.addEventListener('click', function() {
|
||||||
|
const id = this.dataset.id;
|
||||||
|
document.getElementById('deleteQuotationId').value = id;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Gestione conferma cancellazione nel modal
|
||||||
|
document.getElementById('confirmDelete').addEventListener('click', function() {
|
||||||
|
document.getElementById('deleteForm').submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
// I bottoni photos e parts usano gli script esistenti (photos.js, parts.js), passando data-idquotations
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
// File: test_auth.php
|
||||||
|
ini_set('display_errors', '0');
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
|
||||||
|
// Includi le dipendenze
|
||||||
|
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Crea la cartella logsapi se non esiste
|
||||||
|
$logDir = __DIR__ . '/logsapi';
|
||||||
|
if (!is_dir($logDir)) {
|
||||||
|
mkdir($logDir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Istanzia il client API (autenticazione automatica tramite .env)
|
||||||
|
$api = VisualLimsApiClient::getInstance();
|
||||||
|
|
||||||
|
// Esegui una chiamata di test per verificare l'autenticazione
|
||||||
|
$endpoint = 'SchemaCustomField';
|
||||||
|
$options = []; // Nessun filtro per il test
|
||||||
|
$data = $api->get($endpoint, $options);
|
||||||
|
|
||||||
|
// Debug: salva URL usato
|
||||||
|
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||||
|
$query = http_build_query($options);
|
||||||
|
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||||
|
file_put_contents($logDir . '/last_auth_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||||
|
|
||||||
|
// Nota: getToken() è privato, quindi non possiamo accedervi direttamente
|
||||||
|
// Supponiamo che l'autenticazione sia avvenuta correttamente se la GET ha successo
|
||||||
|
$token = null; // Non possiamo accedere al token direttamente
|
||||||
|
$auth_success = true; // La GET ha successo, quindi l'autenticazione funziona
|
||||||
|
|
||||||
|
// Salva un file di conferma dell'autenticazione
|
||||||
|
$outputFile = $logDir . '/auth_token.json';
|
||||||
|
file_put_contents($outputFile, json_encode(['auth_success' => true, 'token' => 'Not directly accessible (private method)'], JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
// Risposta di successo
|
||||||
|
echo json_encode([
|
||||||
|
'success' => true,
|
||||||
|
'message' => "Autenticazione completata con successo, dettagli salvati in {$outputFile}",
|
||||||
|
'auth_success' => $auth_success,
|
||||||
|
'schema_data' => $data // Dati di esempio dalla GET
|
||||||
|
]);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Log dell'errore
|
||||||
|
file_put_contents($logDir . '/auth_error.log', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||||
|
http_response_code(500);
|
||||||
|
echo json_encode([
|
||||||
|
'success' => false,
|
||||||
|
'message' => 'Errore durante l\'autenticazione: ' . $e->getMessage()
|
||||||
|
]);
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'include/headscript.php';
|
||||||
|
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
$data = json_decode(file_get_contents('php://input'), true);
|
||||||
|
$template_id = $data['template_id'] ?? null;
|
||||||
|
$mapping_id = $data['mapping_id'] ?? null;
|
||||||
|
$value = $data['value'] ?? null;
|
||||||
|
|
||||||
|
if (!$template_id || !$mapping_id || !isset($value)) {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Invalid input']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$db = DBHandlerSelect::getInstance();
|
||||||
|
$pdo = $db->getConnection();
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("UPDATE template_mapping SET is_visible_import = ? WHERE id = ? AND template_id = ?");
|
||||||
|
$result = $stmt->execute([$value, $mapping_id, $template_id]);
|
||||||
|
|
||||||
|
if ($result) {
|
||||||
|
echo json_encode(['success' => true]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['success' => false, 'message' => 'Failed to update is_visible_import']);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user