Compare commits

..

7 Commits

Author SHA1 Message Date
solocla 3e4a627ca7 fixed quotations 2025-09-20 22:00:16 +02:00
solocla 25d4519684 fixed quotations 2025-09-13 21:28:11 +02:00
solocla 34d4dc8660 quotations page 2025-09-13 12:31:31 +02:00
solocla 1510ef03f1 added level to collahge 2025-09-12 18:09:01 +02:00
solocla ce8c95921f remove .env from repository 2025-09-12 10:17:22 +02:00
solocla 095a6ae879 remove debug/log/json files from tracking 2025-09-12 10:03:50 +02:00
solocla 296143016a template mapping addes is visible as checkbox and is required as badge 2025-09-12 10:01:19 +02:00
23 changed files with 1539 additions and 23889 deletions
-47
View File
@@ -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/
+1
View File
@@ -32,3 +32,4 @@ $langdatatables = [
"paginate_next" => "Next", "paginate_next" => "Next",
"paginate_previous" => "Previous" "paginate_previous" => "Previous"
]; ];
$quotationstitle = "Quotations";
-36
View File
@@ -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
+28
View File
@@ -0,0 +1,28 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$partId = $data['part_id'] ?? null;
if (!$partId) {
echo json_encode(['success' => false, 'message' => 'ID parte mancante']);
exit;
}
try {
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE id = :part_id");
$stmt->execute([':part_id' => $partId]);
$rowCount = $stmt->rowCount();
if ($rowCount > 0) {
echo json_encode(['success' => true, 'message' => 'Parte eliminata con successo']);
} else {
echo json_encode(['success' => false, 'message' => 'Nessuna parte trovata con ID ' . $partId]);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nell\'eliminazione: ' . $e->getMessage()]);
}
File diff suppressed because one or more lines are too long
+15 -1
View File
@@ -41,7 +41,21 @@
</li> </li>
</ul> </ul>
</li>--> </li>
<li>
<a href="javascript:;" class="has-arrow">
<div class="parent-icon"><i class="bx bx-category"></i>
</div>
<div class="menu-title">Other Functions</div>
</a>
<ul>
<li> <a href="quotations.php"><i class='bx bx-radio-circle'></i><?php echo $quotationstitle; ?></a>
</li>
</ul>
</li>
+23
View File
@@ -0,0 +1,23 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$idquotations = $_GET['idquotations'] ?? null;
if (!$idquotations) {
echo json_encode(['success' => false, 'message' => 'ID quotations mancante']);
exit;
}
try {
$stmt = $pdo->prepare("SELECT id, idquotations, part_number, part_description FROM identification_parts WHERE idquotations = :idquotations ORDER BY part_number ASC");
$stmt->execute([':idquotations' => $idquotations]);
$parts = $stmt->fetchAll();
echo json_encode(['success' => true, 'parts' => $parts]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
}
+33
View File
@@ -0,0 +1,33 @@
<?php
// load_photo_quotation.php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$idquotations = isset($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
if (!$idquotations) {
echo json_encode(['success' => false, 'message' => 'ID quotation mancante']);
exit;
}
try {
// Seleziona le foto per il dato idquotations dalla tabella datadb_photos
$stmt = $pdo->prepare("SELECT id, file_path FROM datadb_photos WHERE idquotations = ?");
$stmt->execute([$idquotations]);
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
if ($photos && count($photos) > 0) {
$photoPaths = array_map(function ($photo) {
return '../photostrf/' . $photo['file_path'];
}, $photos);
echo json_encode(['success' => true, 'photos' => $photoPaths]);
} else {
echo json_encode(['success' => false, 'message' => 'Nessuna foto trovata']);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
}
@@ -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
if (value === 1) {
document.querySelectorAll('.main-field-checkbox').forEach(cb => { document.querySelectorAll('.main-field-checkbox').forEach(cb => {
cb.checked = cb.dataset.originalChecked === 'true'; 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
if (value === 1) {
document.querySelectorAll('.main-field-checkbox').forEach(cb => { document.querySelectorAll('.main-field-checkbox').forEach(cb => {
cb.checked = cb.dataset.originalChecked === 'true'; 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');
+5
View File
@@ -97,6 +97,11 @@
font-size: 0.6rem !important; font-size: 0.6rem !important;
} }
#partsModal {
z-index: 1060 !important;
/* Aumentato per superare il modale foto (z-index: 1050) */
}
#partsModal .modal-content { #partsModal .modal-content {
width: 100% !important; width: 100% !important;
max-width: 100% !important; max-width: 100% !important;
+216 -298
View File
File diff suppressed because it is too large Load Diff
+139 -252
View File
@@ -1,20 +1,21 @@
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
// Funzione per caricare il contenuto del popup // Funzione per caricare il contenuto del popup
async function loadPopupContent(iddatadb) { async function loadPopupContent(iddatadb, idquotations) {
const popupContent = document.getElementById("popupContent"); const popupContent = document.getElementById("popupContent");
if (!popupContent) { if (!popupContent) {
console.error("Elemento popupContent non trovato"); console.error("Elemento popupContent non trovato");
return; return;
} }
try { try {
console.log("Caricamento contenuto per iddatadb:", iddatadb); const endpoint = idquotations
const response = await fetch( ? `photos_popup.php?idquotations=${idquotations}`
`photos_popup.php?iddatadb=${iddatadb}`, : `photos_popup.php?iddatadb=${iddatadb}`;
); console.log("Caricamento popup da:", endpoint);
const response = await fetch(endpoint);
if (!response.ok) if (!response.ok)
throw new Error("Errore nella risposta del server"); throw new Error("Errore nella risposta del server");
popupContent.innerHTML = await response.text(); popupContent.innerHTML = await response.text();
attachPhotoEventListeners(iddatadb); attachPhotoEventListeners(iddatadb, idquotations);
} catch (error) { } catch (error) {
popupContent.innerHTML = `<p>Errore durante il caricamento: ${error.message}</p>`; popupContent.innerHTML = `<p>Errore durante il caricamento: ${error.message}</p>`;
console.error("Errore in loadPopupContent:", error); console.error("Errore in loadPopupContent:", error);
@@ -22,7 +23,7 @@ document.addEventListener("DOMContentLoaded", function () {
} }
// Funzione per gestire la webcam // Funzione per gestire la webcam
function setupWebcam(iddatadb) { function setupWebcam(iddatadb, idquotations) {
const openWebcamBtn = document.getElementById("openWebcamBtn"); const openWebcamBtn = document.getElementById("openWebcamBtn");
const webcamArea = document.getElementById("webcamArea"); const webcamArea = document.getElementById("webcamArea");
const webcamVideo = document.getElementById("webcamVideo"); const webcamVideo = document.getElementById("webcamVideo");
@@ -56,22 +57,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 +83,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 +102,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 +120,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 +131,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 +142,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,10 +158,9 @@ 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, idquotations);
if (stream) { if (stream) {
stream.getTracks().forEach((track) => track.stop()); stream.getTracks().forEach((track) => track.stop());
stream = null; stream = null;
@@ -187,8 +173,7 @@ document.addEventListener("DOMContentLoaded", function () {
}); });
} }
// Funzione per gestire il caricamento dei file async function handleFiles(files, iddatadb, idquotations) {
async function handleFiles(files, iddatadb) {
const loader = document.getElementById("loader"); const loader = document.getElementById("loader");
if (!loader) { if (!loader) {
console.error("Elemento loader non trovato"); console.error("Elemento loader non trovato");
@@ -206,12 +191,16 @@ 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();
formData.append("photo", file); formData.append("photo", file);
if (idquotations) {
formData.append("idquotations", idquotations);
} else {
formData.append("iddatadb", iddatadb); formData.append("iddatadb", iddatadb);
}
try { try {
const response = await fetch("upload_photo.php", { const response = await fetch("upload_photo.php", {
method: "POST", method: "POST",
@@ -219,24 +208,19 @@ document.addEventListener("DOMContentLoaded", function () {
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
console.log( loadPopupContent(iddatadb, idquotations);
"Upload completato con successo, ricarico popup",
);
loadPopupContent(iddatadb);
} else { } else {
alert("Errore durante il caricamento: " + result.message); alert("Errore durante il caricamento: " + result.message);
} }
} 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, idquotations) {
function attachPhotoEventListeners(iddatadb) {
const dropArea = document.getElementById("dropArea"); const dropArea = document.getElementById("dropArea");
const photoInput = document.getElementById("photoInput"); const photoInput = document.getElementById("photoInput");
const photosModal = document.getElementById("photosModal"); const photosModal = document.getElementById("photosModal");
@@ -250,9 +234,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,10 +262,9 @@ 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, idquotations);
} }
}, },
false, false,
@@ -293,7 +273,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 +281,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, idquotations);
} }
// 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,10 +305,7 @@ document.addEventListener("DOMContentLoaded", function () {
}); });
const result = await response.json(); const result = await response.json();
if (result.success) { if (result.success) {
console.log( loadPopupContent(iddatadb, idquotations);
"Foto eliminata con successo, ricarico popup",
);
loadPopupContent(iddatadb);
} else { } else {
alert( alert(
"Errore durante l'eliminazione: " + "Errore durante l'eliminazione: " +
@@ -348,21 +321,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 +339,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, idquotations);
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 +388,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 +395,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 +503,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 +526,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 +544,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 +553,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 +566,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 +574,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 +584,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 +591,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 +616,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 +629,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 +648,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 +674,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 +698,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 +721,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 +734,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 +758,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 +792,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 +811,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 +834,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 +848,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 +876,12 @@ document.addEventListener("DOMContentLoaded", function () {
type: "image/jpeg", type: "image/jpeg",
}); });
// Upload come nuova foto await handleFiles([file], iddatadb, idquotations);
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, idquotations);
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 +889,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 +904,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 +919,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 +934,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 +952,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 +959,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,19 +999,20 @@ 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( const iddatadb = this.getAttribute("data-iddatadb") || null;
"Pulsante Photos cliccato per iddatadb:", const idquotations =
this.getAttribute("data-iddatadb"), this.getAttribute("data-idquotations") || null;
); console.log("Apertura modale foto con:", {
const iddatadb = this.getAttribute("data-iddatadb"); iddatadb,
loadPopupContent(iddatadb); idquotations,
});
loadPopupContent(iddatadb, idquotations);
photosModal.style.display = "block"; photosModal.style.display = "block";
document.querySelector(".overlay").style.display = "none"; document.querySelector(".overlay").style.display = "none";
}); });
}); });
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 +1020,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";
+118 -37
View File
@@ -17,70 +17,131 @@ use Endroid\QrCode\QrCode;
use Endroid\QrCode\RoundBlockSizeMode; use Endroid\QrCode\RoundBlockSizeMode;
use Endroid\QrCode\Writer\PngWriter; use Endroid\QrCode\Writer\PngWriter;
// Abilita logging 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__ . '/photos_popup_debug.log');
// Log iniziale
error_log("Richiesta a photos_popup.php: " . print_r($_GET, true));
// Carica le variabili d'ambiente // Carica le variabili d'ambiente
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']); ?>
<div class="popup-content">
<p>Errore: Impossibile caricare il file di configurazione.</p>
</div>
<?php
exit; exit;
} }
// Verifica che BASE_URL sia definito // Verifica che BASE_URL sia definito
if (!isset($_ENV['BASE_URL'])) { if (!isset($_ENV['BASE_URL'])) {
error_log("Errore: la variabile BASE_URL non è definita nel file .env"); error_log("Errore: la variabile BASE_URL non è definita nel file .env");
echo json_encode(['error' => 'Variabile BASE_URL non definita']); ?>
<div class="popup-content">
<p>Errore: Variabile BASE_URL non definita.</p>
</div>
<?php
exit; exit;
} }
$db = DBHandlerSelect::getInstance(); $db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection(); $pdo = $db->getConnection();
// Verifica che l'iddatadb sia stato passato // Verifica che almeno uno degli ID sia passato
if (!isset($_GET['iddatadb']) || empty($_GET['iddatadb'])) { $iddatadb = isset($_GET['iddatadb']) && !empty($_GET['iddatadb']) ? intval($_GET['iddatadb']) : null;
echo json_encode(['error' => 'ID riga non fornito']); $idquotations = isset($_GET['idquotations']) && !empty($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
if (!$iddatadb && !$idquotations) {
error_log("Errore: ID riga o ID quotations non fornito");
?>
<div class="popup-content">
<p>Errore: ID riga o ID quotations non fornito.</p>
</div>
<?php
exit; exit;
} }
$iddatadb = intval($_GET['iddatadb']); if ($iddatadb && $idquotations) {
error_log("Errore: Non è possibile specificare sia iddatadb che idquotations");
// Recupera i dettagli della riga (idriga e sample_code) ?>
$stmt = $pdo->prepare("SELECT iddatadb, sample_code FROM datadb WHERE iddatadb = ?"); <div class="popup-content">
$stmt->execute([$iddatadb]); <p>Errore: Non è possibile specificare sia iddatadb che idquotations.</p>
$row = $stmt->fetch(PDO::FETCH_ASSOC); </div>
<?php
if (!$row) {
echo json_encode(['error' => 'Riga non trovata']);
exit; exit;
} }
$idriga = $row['iddatadb']; // Determina quale ID e tabella usare
$sampleCode = $row['sample_code'] ?? 'Non disponibile'; $paramName = $iddatadb ? 'iddatadb' : 'id'; // Usa 'id' per quotations
$paramValue = $iddatadb ?: $idquotations;
$table = $iddatadb ? 'datadb' : 'quotations';
$field = $iddatadb ? 'sample_code' : 'description'; // Usa 'description' per quotations
$photoTable = 'datadb_photos'; // Usa sempre datadb_photos
$photoParamName = $iddatadb ? 'iddatadb' : 'idquotations'; // Usa 'idquotations' per datadb_photos
// Recupera i dettagli della riga
try {
$stmt = $pdo->prepare("SELECT {$paramName}, {$field} FROM {$table} WHERE {$paramName} = ?");
$stmt->execute([$paramValue]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
error_log("Errore: Riga non trovata per {$paramName} = {$paramValue}");
?>
<div class="popup-content">
<p>Errore: Riga non trovata.</p>
</div>
<?php
exit;
}
} catch (Exception $e) {
error_log("Errore query dettagli riga: " . $e->getMessage());
?>
<div class="popup-content">
<p>Errore: Impossibile recuperare i dettagli della riga.</p>
</div>
<?php
exit;
}
$id = $row[$paramName];
$code = $row[$field] ?? 'Non disponibile';
// Recupera le foto associate alla riga // Recupera le foto associate alla riga
$stmt = $pdo->prepare("SELECT id, file_path, file_name, description, uploaded_at FROM datadb_photos WHERE iddatadb = ? ORDER BY uploaded_at DESC"); try {
$stmt->execute([$iddatadb]); $stmt = $pdo->prepare("SELECT id, file_path, file_name, description, uploaded_at FROM {$photoTable} WHERE {$photoParamName} = ? ORDER BY uploaded_at DESC");
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC); $stmt->execute([$paramValue]);
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) {
error_log("Errore query foto: " . $e->getMessage());
$photos = []; // Imposta array vuoto in caso di errore
}
// Definisci il percorso base per le foto // Definisci il percorso base per le foto
$photoBasePath = '../photostrf/'; $photoBasePath = '../photostrf/';
// Usa la variabile d'ambiente BASE_URL // Usa la variabile d'ambiente BASE_URL
$baseUrl = rtrim($_ENV['BASE_URL'], '/'); // Rimuove eventuali slash finali $baseUrl = rtrim($_ENV['BASE_URL'], '/');
$uploadUrl = $baseUrl . "/upload_photos_mobile.php?iddatadb=" . $iddatadb; $uploadUrl = $iddatadb
? $baseUrl . "/upload_photos_mobile.php?iddatadb=" . $iddatadb
: $baseUrl . "/upload_photos_mobile.php?idquotations=" . $idquotations;
// Genera il QR code con endroid/qr-code 6.0.6 // Genera il QR code con endroid/qr-code 6.0.6
$qrCodeDir = '../photostrf/qrcodes/'; $qrCodeDir = '../photostrf/qrcodes/';
if (!is_dir($qrCodeDir)) { if (!is_dir($qrCodeDir)) {
mkdir($qrCodeDir, 0755, true); mkdir($qrCodeDir, 0755, true);
} }
$qrCodeFile = $qrCodeDir . "qrcode_{$iddatadb}.png"; $qrCodeFile = $qrCodeDir . "qrcode_{$id}.png";
$writer = new PngWriter(); $writer = new PngWriter();
// Crea il QR code usando il costruttore
$qrCode = new QrCode( $qrCode = new QrCode(
data: $uploadUrl, data: $uploadUrl,
encoding: new Encoding('UTF-8'), encoding: new Encoding('UTF-8'),
@@ -104,13 +165,13 @@ $result->saveToFile($qrCodeFile);
</div> </div>
</div> </div>
<h3>Manage Photos</h3> <h3>Manage Photos</h3>
<p><strong>ID Row:</strong> <?= htmlspecialchars($idriga) ?></p> <p><strong>ID:</strong> <?= htmlspecialchars($id) ?></p>
<p><strong>Sample Code:</strong> <?= htmlspecialchars($sampleCode) ?></p> <p><strong>Code:</strong> <?= htmlspecialchars($code) ?></p>
<!-- QR Code per il caricamento da mobile --> <!-- QR Code per il caricamento da mobile -->
<div style="text-align: center; margin-bottom: 20px;"> <div style="text-align: center; margin-bottom: 20px;">
<p>Scan the QR Code with the mobile to take photo with camera:</p> <p>Scan the QR Code with the mobile to take photo with camera:</p>
<img src="../photostrf/qrcodes/qrcode_<?= $iddatadb ?>.png" alt="QR Code" style="max-width: 150px;"> <img src="../photostrf/qrcodes/qrcode_<?= htmlspecialchars($id) ?>.png" alt="QR Code" style="max-width: 150px;">
<p style="margin-top: 10px;"> <p style="margin-top: 10px;">
<a href="<?= htmlspecialchars($uploadUrl) ?>" target="_blank"><?= htmlspecialchars($uploadUrl) ?></a> <a href="<?= htmlspecialchars($uploadUrl) ?>" target="_blank"><?= htmlspecialchars($uploadUrl) ?></a>
</p> </p>
@@ -137,7 +198,7 @@ $result->saveToFile($qrCodeFile);
<!-- Elenco delle foto --> <!-- Elenco delle foto -->
<div id="photosList"> <div id="photosList">
<?php if (empty($photos)): ?> <?php if (empty($photos)): ?>
<p>No Photos present.</p> <p>Nessuna foto presente.</p>
<?php else: ?> <?php else: ?>
<?php foreach ($photos as $photo): ?> <?php foreach ($photos as $photo): ?>
<?php <?php
@@ -176,7 +237,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;">&times;</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%;">&times;</span>
<h3>Crea Collage</h3> <h3>Crea Collage</h3>
<!-- Lista foto selezionabili --> <!-- Lista foto selezionabili -->
@@ -195,10 +256,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 +377,6 @@ $result->saveToFile($qrCodeFile);
color: white; color: white;
} }
#clearCanvasBtn {
background: #dc3545;
color: white;
}
#bringToFrontBtn, #bringToFrontBtn,
#sendToBackBtn, #sendToBackBtn,
#bringForwardBtn, #bringForwardBtn,
@@ -323,7 +384,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 +404,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>
+429
View File
@@ -0,0 +1,429 @@
<?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">
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.1/fabric.min.js"></script>
<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;
}
.modal {
display: none;
position: fixed;
z-index: 1050;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0, 0, 0, 0.5);
}
.modal-content {
background-color: #fefefe;
margin: 15% auto;
padding: 20px;
border: 1px solid #888;
width: 80%;
max-width: 600px;
border-radius: 8px;
position: relative;
}
.close-btn {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
cursor: pointer;
}
.close-btn:hover,
.close-btn:focus {
color: #000;
text-decoration: none;
}
</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-row="<?= $index ?>" data-idquotations="<?= $editQuotation['id'] ?>" style="background: #007bff; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-camera"></i></button>
<button class="parts-btn" data-iddatadb="" data-idquotations="456" data-row="0">Parti</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>
<!-- In quotations.php, nella tabella delle quotations -->
<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-entity-type="quotation" data-idquotations="<?= $row['id'] ?>" title="Photos"><i class="fas fa-camera"></i></button>
<button type="button" class="parts-btn action-btn" data-entity-type="quotation" 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>
<!-- Modale per le foto in quotations.php -->
<div class="modal" id="photosModal">
<div class="modal-content">
<span class="close-btn">&times;</span>
<div class="popup-content"></div>
</div>
</div>
</body>
</html>
@@ -0,0 +1,63 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$idquotations = $data['idquotations'] ?? null;
$parts = $data['parts'] ?? [];
if (!$idquotations || empty($parts)) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit;
}
try {
$pdo->beginTransaction();
// Elimina tutte le parti esistenti per idquotations
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE idquotations = :idquotations");
$stmt->execute([':idquotations' => $idquotations]);
// Prepara l'inserimento delle nuove parti
$stmt = $pdo->prepare("
INSERT INTO identification_parts
(idquotations, part_number, part_description, mix, created_at, updated_at)
VALUES (:idquotations, :part_number, :part_description, :mix, NOW(), NOW())
");
$part_ids = [];
foreach ($parts as $part) {
$partNumber = $part['part_number'] ?? null;
$partDescription = $part['part_description'] ?? '';
$mix = $part['mix'] ?? 'N';
if (!$partNumber || !$partDescription) {
throw new PDOException("Numero parte o descrizione mancante per parte: " . json_encode($part));
}
$stmt->execute([
':idquotations' => $idquotations,
':part_number' => $partNumber,
':part_description' => $partDescription,
':mix' => $mix
]);
$part_ids[] = $pdo->lastInsertId();
}
$pdo->commit();
echo json_encode([
'success' => true,
'part_ids' => $part_ids,
'message' => 'Parti rinumerate con successo'
]);
} catch (PDOException $e) {
$pdo->rollBack();
echo json_encode([
'success' => false,
'message' => 'Errore nel salvataggio: ' . $e->getMessage()
]);
}
@@ -0,0 +1,59 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
error_reporting(E_ALL);
ini_set('display_errors', 1);
$dataURL = $_POST['dataURL'] ?? null;
$filename = $_POST['filename'] ?? null;
$idquotations = $_POST['idquotations'] ?? null;
if (!$dataURL || !$filename || !$idquotations) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit;
}
try {
// Verifica che idquotations esista nella tabella quotations
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$stmt = $pdo->prepare("SELECT idquotations FROM quotations WHERE idquotations = :idquotations");
$stmt->execute([':idquotations' => $idquotations]);
if (!$stmt->fetch()) {
echo json_encode(['success' => false, 'message' => 'idquotations non valido']);
exit;
}
// Salva l'immagine
$data = explode(',', $dataURL)[1];
$decodedData = base64_decode($data);
$dirPath = '../photostrf/annotated';
if (!file_exists($dirPath)) {
mkdir($dirPath, 0777, true);
}
$filePath = $dirPath . '/' . $filename;
file_put_contents($filePath, $decodedData);
// Registra nel database
$stmt = $pdo->prepare("
INSERT INTO datadb_photos (idquotations, file_path, file_name, uploaded_at, uploaded_by)
VALUES (:idquotations, :file_path, :file_name, NOW(), :uploaded_by)
");
$stmt->execute([
':idquotations' => $idquotations,
':file_path' => $filePath,
':file_name' => $filename,
':uploaded_by' => $iduserlogin
]);
echo json_encode([
'success' => true,
'file_path' => $filePath,
'message' => 'Foto salvata con successo e registrata nel DB'
]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => 'Errore: ' . $e->getMessage()]);
}
+60
View File
@@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$data = json_decode(file_get_contents('php://input'), true);
$idquotations = $data['idquotations'] ?? null;
$parts = $data['parts'] ?? [];
if (!$idquotations || empty($parts)) {
echo json_encode(['success' => false, 'message' => 'Dati mancanti']);
exit;
}
$part = $parts[0];
$partId = $part['id'] ?? null;
$partNumber = $part['part_number'] ?? null;
$partDescription = $part['part_description'] ?? '';
$mix = $part['mix'] ?? 'N';
if ($partDescription) {
try {
if ($partId) {
// UPDATE se esiste già la parte
$stmt = $pdo->prepare("UPDATE identification_parts
SET part_number = :part_number,
part_description = :part_description,
mix = :mix,
updated_at = NOW()
WHERE id = :id");
$stmt->execute([
':id' => $partId,
':part_number' => $partNumber,
':part_description' => $partDescription,
':mix' => $mix
]);
echo json_encode(['success' => true, 'part_id' => $partId, 'part_number' => $partNumber, 'message' => 'Parte aggiornata con successo']);
} else {
// INSERT se è nuova
$stmt = $pdo->prepare("INSERT INTO identification_parts
(idquotations, part_number, part_description, mix, created_at, updated_at)
VALUES (:idquotations, :part_number, :part_description, :mix, NOW(), NOW())");
$stmt->execute([
':idquotations' => $idquotations,
':part_number' => $partNumber,
':part_description' => $partDescription,
':mix' => $mix
]);
$newId = $pdo->lastInsertId();
echo json_encode(['success' => true, 'part_id' => $newId, 'part_number' => $partNumber, 'message' => 'Parte salvata con successo']);
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nel salvataggio: ' . $e->getMessage()]);
}
} else {
echo json_encode(['success' => false, 'message' => 'Descrizione mancante']);
}
+26
View File
@@ -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']);
}
+48 -9
View File
@@ -4,13 +4,23 @@ include('include/headscript.php');
header('Content-Type: application/json'); header('Content-Type: application/json');
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['photo']) || !isset($_POST['iddatadb'])) { if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_FILES['photo']) || (!isset($_POST['iddatadb']) && !isset($_POST['idquotations']))) {
echo json_encode(['success' => false, 'message' => 'Richiesta non valida']); echo json_encode(['success' => false, 'message' => 'Richiesta non valida']);
exit; exit;
} }
$iddatadb = intval($_POST['iddatadb']); $iddatadb = isset($_POST['iddatadb']) ? intval($_POST['iddatadb']) : null;
$photo = $_FILES['photo']; $idquotations = isset($_POST['idquotations']) ? intval($_POST['idquotations']) : null;
if ($iddatadb && $idquotations) {
echo json_encode(['success' => false, 'message' => 'Non è possibile specificare sia iddatadb che idquotations']);
exit;
}
if (!$iddatadb && !$idquotations) {
echo json_encode(['success' => false, 'message' => 'ID TRF o ID quotations mancante']);
exit;
}
// Verifica che l'utente loggato esista in auth_users // Verifica che l'utente loggato esista in auth_users
$db = DBHandlerSelect::getInstance(); $db = DBHandlerSelect::getInstance();
@@ -25,6 +35,28 @@ if (!$userExists) {
exit; exit;
} }
// Verifica l'esistenza dell'ID nella tabella corrispondente
try {
if ($iddatadb) {
$stmt = $pdo->prepare("SELECT iddatadb FROM datadb WHERE iddatadb = ?");
$stmt->execute([$iddatadb]);
if (!$stmt->fetch()) {
echo json_encode(['success' => false, 'message' => 'iddatadb non valido']);
exit;
}
} else {
$stmt = $pdo->prepare("SELECT id FROM quotations WHERE id = ?");
$stmt->execute([$idquotations]);
if (!$stmt->fetch()) {
echo json_encode(['success' => false, 'message' => 'idquotations non valido']);
exit;
}
}
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore nella validazione: ' . $e->getMessage()]);
exit;
}
// Usa un percorso assoluto per la cartella photostrf // Usa un percorso assoluto per la cartella photostrf
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/'; $uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
if (!is_dir($uploadDir)) { if (!is_dir($uploadDir)) {
@@ -41,6 +73,7 @@ if (!is_writable($uploadDir)) {
} }
// Verifica che il file sia un'immagine (inclusi HEIC/HEIF) // Verifica che il file sia un'immagine (inclusi HEIC/HEIF)
$photo = $_FILES['photo'];
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/heic', 'image/heif']; $allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/heic', 'image/heif'];
if (!in_array($photo['type'], $allowedTypes)) { if (!in_array($photo['type'], $allowedTypes)) {
echo json_encode(['success' => false, 'message' => 'Il file deve essere un\'immagine (JPEG, PNG, GIF, HEIC)']); echo json_encode(['success' => false, 'message' => 'Il file deve essere un\'immagine (JPEG, PNG, GIF, HEIC)']);
@@ -53,10 +86,11 @@ if (!file_exists($photo['tmp_name']) || !is_uploaded_file($photo['tmp_name'])) {
exit; exit;
} }
// Rinomina il file: idriga-timestamp-nomeoriginale.estensione // Rinomina il file: id-timestamp-nomeoriginale.estensione
$timestamp = date('YmdHis'); $timestamp = date('YmdHis');
$originalName = pathinfo($photo['name'], PATHINFO_FILENAME); $originalName = pathinfo($photo['name'], PATHINFO_FILENAME);
$extension = strtolower(pathinfo($photo['name'], PATHINFO_EXTENSION)); $extension = strtolower(pathinfo($photo['name'], PATHINFO_EXTENSION));
$id = $iddatadb ?: $idquotations;
// Se il file è HEIC/HEIF, convertilo in JPEG // Se il file è HEIC/HEIF, convertilo in JPEG
if (in_array($photo['type'], ['image/heic', 'image/heif'])) { if (in_array($photo['type'], ['image/heic', 'image/heif'])) {
@@ -74,11 +108,11 @@ if (in_array($photo['type'], ['image/heic', 'image/heif'])) {
} }
// Crea un nuovo nome per il file JPEG // Crea un nuovo nome per il file JPEG
$newFileName = "{$iddatadb}-{$timestamp}-{$originalName}.jpg"; $newFileName = "{$id}-{$timestamp}-{$originalName}.jpg";
$destination = $uploadDir . $newFileName; $destination = $uploadDir . $newFileName;
// Salva l'immagine come JPEG // Salva l'immagine come JPEG
if (!imagejpeg($image, $destination, 90)) { // 90 è la qualità JPEG if (!imagejpeg($image, $destination, 90)) {
imagedestroy($image); imagedestroy($image);
echo json_encode(['success' => false, 'message' => 'Errore durante la conversione del file HEIC in JPEG']); echo json_encode(['success' => false, 'message' => 'Errore durante la conversione del file HEIC in JPEG']);
exit; exit;
@@ -88,7 +122,7 @@ if (in_array($photo['type'], ['image/heic', 'image/heif'])) {
imagedestroy($image); imagedestroy($image);
} else { } else {
// Per i formati non HEIC, usa il nome e l'estensione originali // Per i formati non HEIC, usa il nome e l'estensione originali
$newFileName = "{$iddatadb}-{$timestamp}-{$originalName}.{$extension}"; $newFileName = "{$id}-{$timestamp}-{$originalName}.{$extension}";
$destination = $uploadDir . $newFileName; $destination = $uploadDir . $newFileName;
// Salva il file // Salva il file
@@ -105,7 +139,12 @@ error_log("Destination: $destination");
error_log("Temp file: " . $photo['tmp_name']); error_log("Temp file: " . $photo['tmp_name']);
// Salva il riferimento nel database // Salva il riferimento nel database
$stmt = $pdo->prepare("INSERT INTO datadb_photos (iddatadb, file_path, file_name, uploaded_by) VALUES (?, ?, ?, ?)"); try {
$stmt->execute([$iddatadb, $newFileName, $newFileName, $iduserlogin]); $stmt = $pdo->prepare("INSERT INTO datadb_photos (iddatadb, idquotations, file_path, file_name, uploaded_by) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$iddatadb, $idquotations, $newFileName, $newFileName, $iduserlogin]);
} catch (PDOException $e) {
echo json_encode(['success' => false, 'message' => 'Errore durante il salvataggio nel database: ' . $e->getMessage()]);
exit;
}
echo json_encode(['success' => true, 'message' => 'Foto caricata con successo']); echo json_encode(['success' => true, 'message' => 'Foto caricata con successo']);
+183 -42
View File
@@ -5,24 +5,41 @@ include('include/headscript.php');
$db = DBHandlerSelect::getInstance(); $db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection(); $pdo = $db->getConnection();
// Verifica che l'iddatadb sia stato passato // Verifica che almeno uno degli ID sia passato
if (!isset($_GET['iddatadb']) || empty($_GET['iddatadb'])) { $iddatadb = isset($_GET['iddatadb']) && !empty($_GET['iddatadb']) ? intval($_GET['iddatadb']) : null;
die('ID riga non fornito'); $idquotations = isset($_GET['idquotations']) && !empty($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
if (!$iddatadb && !$idquotations) {
die('ID riga o ID quotations non fornito');
} }
$iddatadb = intval($_GET['iddatadb']); if ($iddatadb && $idquotations) {
die('Non è possibile specificare sia iddatadb che idquotations');
}
// Recupera i dettagli della riga (idriga e sample_code) // Verifica che l'utente loggato esista
$stmt = $pdo->prepare("SELECT iddatadb, sample_code FROM datadb WHERE iddatadb = ?"); $stmt = $pdo->prepare("SELECT id FROM auth_users WHERE id = ?");
$stmt->execute([$iddatadb]); $stmt->execute([$iduserlogin]);
if (!$stmt->fetch(PDO::FETCH_ASSOC)) {
die('Utente non valido');
}
// Determina quale ID usare e verifica l'esistenza
$paramName = $iddatadb ? 'iddatadb' : 'idquotations';
$paramValue = $iddatadb ?: $idquotations;
$table = $iddatadb ? 'datadb' : 'quotations';
$field = $iddatadb ? 'sample_code' : 'quotation_code';
$stmt = $pdo->prepare("SELECT {$paramName}, {$field} FROM {$table} WHERE {$paramName} = ?");
$stmt->execute([$paramValue]);
$row = $stmt->fetch(PDO::FETCH_ASSOC); $row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) { if (!$row) {
die('Riga non trovata'); die('Riga non trovata');
} }
$idriga = $row['iddatadb']; $id = $row[$paramName];
$sampleCode = $row['sample_code'] ?? 'Non disponibile'; $code = $row[$field] ?? 'Non disponibile';
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
@@ -32,17 +49,23 @@ $sampleCode = $row['sample_code'] ?? 'Non disponibile';
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Carica Foto da Mobile</title> <title>Carica Foto da Mobile</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style> <style>
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
padding: 20px; padding: 20px;
text-align: center; text-align: center;
background: #f4f4f4;
} }
.upload-area { .upload-area {
border: 2px dashed #ccc; border: 2px dashed #ccc;
padding: 20px; padding: 20px;
margin: 20px 0; margin: 20px auto;
max-width: 500px;
background: white;
border-radius: 8px;
cursor: pointer;
} }
.upload-area.highlight { .upload-area.highlight {
@@ -56,57 +79,121 @@ $sampleCode = $row['sample_code'] ?? 'Non disponibile';
margin-bottom: 10px; margin-bottom: 10px;
border-bottom: 1px solid #eee; border-bottom: 1px solid #eee;
padding-bottom: 10px; padding-bottom: 10px;
max-width: 500px;
margin-left: auto;
margin-right: auto;
} }
.photo-item img { .photo-item img {
max-width: 100px; max-width: 100px;
max-height: 100px; max-height: 100px;
margin-right: 10px; margin-right: 10px;
border-radius: 4px;
}
.loader {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.loader i {
font-size: 40px;
margin-bottom: 10px;
}
.error-message {
color: red;
margin: 10px 0;
display: none;
}
button.delete-photo-btn {
background: none;
border: none;
color: #dc3545;
cursor: pointer;
font-size: 18px;
} }
</style> </style>
</head> </head>
<body> <body>
<h2>Carica Foto per ID Riga: <?= htmlspecialchars($idriga) ?></h2> <h2>Carica Foto per ID: <?= htmlspecialchars($id) ?></h2>
<p><strong>Sample Code:</strong> <?= htmlspecialchars($sampleCode) ?></p> <p><strong>Codice:</strong> <?= htmlspecialchars($code) ?></p>
<div class="loader" id="loader">
<div>
<i class="fas fa-spinner fa-spin"></i>
<p>Caricamento in corso...</p>
</div>
</div>
<div class="error-message" id="errorMessage"></div>
<div class="upload-area" id="uploadArea"> <div class="upload-area" id="uploadArea">
<p>Scatta una foto o seleziona un'immagine</p> <p>Scatta una foto o seleziona immagini</p>
<input type="file" id="photoInput" accept="image/*" capture="camera"> <input type="file" id="photoInput" accept="image/*" capture="camera" multiple style="display: none;">
</div>
<div id="photosList">
<!-- Le foto verranno caricate dinamicamente -->
</div> </div>
<div id="photosList"></div>
<script> <script>
const uploadArea = document.getElementById('uploadArea'); const uploadArea = document.getElementById('uploadArea');
const photoInput = document.getElementById('photoInput'); const photoInput = document.getElementById('photoInput');
const photosList = document.getElementById('photosList'); const photosList = document.getElementById('photosList');
const loader = document.getElementById('loader');
const errorMessage = document.getElementById('errorMessage');
const iddatadb = '<?= $iddatadb ?>';
const idquotations = '<?= $idquotations ?>';
const endpoint = idquotations ? 'load_photo_quotation.php' : 'load_photo.php';
const dataParam = idquotations ? {
idquotations: idquotations
} : {
iddatadb: iddatadb
};
// Carica le foto esistenti all'avvio // Carica le foto esistenti all'avvio
loadPhotos(); loadPhotos();
// Gestione del click sull'area di upload // Gestione drag-and-drop
uploadArea.addEventListener('click', () => { uploadArea.addEventListener('dragover', (e) => {
photoInput.click(); e.preventDefault();
uploadArea.classList.add('highlight');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('highlight');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('highlight');
handleFiles(e.dataTransfer.files);
}); });
// Gestione del caricamento delle foto // Gestione click sull'area di upload
photoInput.addEventListener('change', () => { uploadArea.addEventListener('click', () => photoInput.click());
handleFiles(photoInput.files);
}); // Gestione caricamento foto
photoInput.addEventListener('change', () => handleFiles(photoInput.files));
async function handleFiles(files) { async function handleFiles(files) {
loader.style.display = 'flex';
errorMessage.style.display = 'none';
for (const file of files) { for (const file of files) {
if (!file.type.startsWith('image/')) { if (!file.type.startsWith('image/')) {
alert('Per favore, carica solo immagini!'); showError('Per favore, carica solo immagini!');
continue; continue;
} }
const formData = new FormData(); const formData = new FormData();
formData.append('photo', file); formData.append('photo', file);
formData.append('iddatadb', '<?= $iddatadb ?>'); if (iddatadb) formData.append('iddatadb', iddatadb);
if (idquotations) formData.append('idquotations', idquotations);
try { try {
const response = await fetch('upload_photo.php', { const response = await fetch('upload_photo.php', {
@@ -114,31 +201,85 @@ $sampleCode = $row['sample_code'] ?? 'Non disponibile';
body: formData body: formData
}); });
const result = await response.json(); const result = await response.json();
if (!result.success) {
if (result.success) { showError('Errore durante il caricamento: ' + result.message);
loadPhotos(); // Ricarica le foto
} else {
alert('Errore durante il caricamento: ' + result.message);
} }
} catch (error) { } catch (error) {
alert('Errore durante il caricamento: ' + error.message); showError('Errore di rete: ' + error.message);
} }
} }
loadPhotos();
loader.style.display = 'none';
} }
// Funzione per caricare le foto esistenti
async function loadPhotos() { async function loadPhotos() {
loader.style.display = 'flex';
errorMessage.style.display = 'none';
try { try {
const response = await fetch(`photos_popup.php?iddatadb=<?= $iddatadb ?>`); const response = await fetch(`${endpoint}?${new URLSearchParams(dataParam)}`);
const html = await response.text(); const result = await response.json();
const parser = new DOMParser(); photosList.innerHTML = '';
const doc = parser.parseFromString(html, 'text/html'); if (result.success && result.photos && result.photos.length > 0) {
const photosListContent = doc.querySelector('#photosList').innerHTML; for (const photo of result.photos) {
photosList.innerHTML = photosListContent; const photoName = photo.split('/').pop();
const photoItem = document.createElement('div');
photoItem.className = 'photo-item';
photoItem.innerHTML = `
<img src="${photo}" alt="${photoName}">
<div style="flex: 1; text-align: left;">
<strong>Nome:</strong> ${photoName}<br>
<strong>Caricata il:</strong> Non disponibile
</div>
<button class="delete-photo-btn" data-photo-path="${photo}">
<i class="fas fa-trash"></i>
</button>`;
photosList.appendChild(photoItem);
}
} else {
photosList.innerHTML = '<p>Nessuna foto presente.</p>';
}
} catch (error) { } catch (error) {
photosList.innerHTML = `<p>Errore durante il caricamento delle foto: ${error.message}</p>`; showError('Errore durante il caricamento delle foto: ' + error.message);
}
loader.style.display = 'none';
}
function showError(message) {
errorMessage.textContent = message;
errorMessage.style.display = 'block';
setTimeout(() => errorMessage.style.display = 'none', 5000);
}
// Gestione eliminazione foto
photosList.addEventListener('click', async (e) => {
if (e.target.closest('.delete-photo-btn')) {
const button = e.target.closest('.delete-photo-btn');
const photoPath = button.dataset.photoPath;
if (confirm('Sei sicuro di voler eliminare questa foto?')) {
loader.style.display = 'flex';
try {
const response = await fetch('delete_photo.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
file_path: photoPath
})
});
const result = await response.json();
if (result.success) {
loadPhotos();
} else {
showError('Errore durante l\'eliminazione: ' + result.message);
}
} catch (error) {
showError('Errore durante l\'eliminazione: ' + error.message);
}
loader.style.display = 'none';
} }
} }
});
</script> </script>
</body> </body>