image cad area size
This commit is contained in:
@@ -0,0 +1,655 @@
|
||||
<?php include('include/headscript.php'); ?>
|
||||
<?php
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$iduser = $iduserlogin ?? null;
|
||||
|
||||
if ($iduser === null) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM cad_area_jobs
|
||||
ORDER BY id DESC
|
||||
");
|
||||
$stmt->execute();
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT *
|
||||
FROM cad_area_jobs
|
||||
WHERE iduser = :iduser
|
||||
ORDER BY id DESC
|
||||
");
|
||||
$stmt->execute([
|
||||
':iduser' => $iduser
|
||||
]);
|
||||
}
|
||||
$jobs = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="it">
|
||||
|
||||
<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'); ?>
|
||||
<title>Calcolo Area CAD - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||
|
||||
<style>
|
||||
body {
|
||||
font-size: 1.05rem;
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.back-dashboard {
|
||||
background-color: #cfe3ff !important;
|
||||
color: #1f2d3d !important;
|
||||
border: 1px solid #bcd4f4 !important;
|
||||
border-radius: 10px;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
padding: 10px 18px;
|
||||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.back-dashboard:hover {
|
||||
background-color: #b9d3ff !important;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.btn-add {
|
||||
background-color: #0d6efd;
|
||||
color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.btn-add:hover {
|
||||
background-color: #0b5ed7;
|
||||
color: #fff;
|
||||
transform: scale(1.02);
|
||||
}
|
||||
|
||||
.table thead {
|
||||
background-color: #cfe3ff;
|
||||
color: #1f2d3d;
|
||||
}
|
||||
|
||||
.drop-zone {
|
||||
border: 2px dashed #9fc5f8;
|
||||
background: #f4f8ff;
|
||||
border-radius: 16px;
|
||||
padding: 38px 20px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.drop-zone:hover,
|
||||
.drop-zone.dragover {
|
||||
background: #e8f2ff;
|
||||
border-color: #0d6efd;
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.drop-zone-icon {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.file-preview {
|
||||
border: 1px solid #dbeafe;
|
||||
background: #ffffff;
|
||||
border-radius: 12px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-preview small {
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
font-size: 0.85rem;
|
||||
padding: 6px 10px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
#cadAreaTable {
|
||||
table-layout: fixed;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
#cadAreaTable th,
|
||||
#cadAreaTable td {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(1),
|
||||
#cadAreaTable td:nth-child(1) {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(2),
|
||||
#cadAreaTable td:nth-child(2) {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(3),
|
||||
#cadAreaTable td:nth-child(3) {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(4),
|
||||
#cadAreaTable td:nth-child(4),
|
||||
#cadAreaTable th:nth-child(5),
|
||||
#cadAreaTable td:nth-child(5),
|
||||
#cadAreaTable th:nth-child(6),
|
||||
#cadAreaTable td:nth-child(6) {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(7),
|
||||
#cadAreaTable td:nth-child(7) {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#cadAreaTable th:nth-child(8),
|
||||
#cadAreaTable td:nth-child(8) {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.processing-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(15, 23, 42, 0.55);
|
||||
z-index: 9999;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.processing-box {
|
||||
background: white;
|
||||
border-radius: 18px;
|
||||
padding: 30px;
|
||||
min-width: 320px;
|
||||
text-align: center;
|
||||
box-shadow: 0 12px 35px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper" id="appWrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<div class="card p-3 mb-4">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h5 class="mb-0">Calcolo Area da PDF CAD</h5>
|
||||
<small class="text-muted">Upload PDF vettoriali e calcolo automatico della superficie del profilo</small>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn back-dashboard" onclick="location.href='production_dashboard.php'">
|
||||
↩️ Torna alla Dashboard
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div id="dropZone" class="drop-zone">
|
||||
<div class="drop-zone-icon">📐</div>
|
||||
<h5 class="mb-1">Trascina qui uno o più PDF CAD</h5>
|
||||
<p class="text-muted mb-2">Oppure clicca per selezionare i file</p>
|
||||
<small class="text-muted">Formati accettati: PDF - massimo 25 MB per file</small>
|
||||
<input type="file" id="pdfFiles" accept="application/pdf,.pdf" multiple hidden>
|
||||
</div>
|
||||
|
||||
<div id="selectedFiles" class="mt-3"></div>
|
||||
|
||||
<div class="text-end mt-3">
|
||||
<button id="uploadBtn" class="btn btn-add" disabled>
|
||||
⬆️ Carica PDF
|
||||
</button>
|
||||
|
||||
<button id="processUploadedBtn" class="btn btn-outline-primary ms-2" disabled>
|
||||
⚙️ Procedi al calcolo
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card p-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Risultati Elaborazione</h5>
|
||||
<button id="processSelectedBtn" class="btn btn-add">
|
||||
⚙️ Calcola selezionati
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="cadAreaTable" class="table table-striped align-middle text-center" style="width:100%;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<input type="checkbox" id="selectAll">
|
||||
</th>
|
||||
<th>Nome PDF</th>
|
||||
<th>Stato</th>
|
||||
<th>Area mm²</th>
|
||||
<th>Area cm²</th>
|
||||
<th>Scala</th>
|
||||
<th>Confidenza</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
<?php foreach ($jobs as $job): ?>
|
||||
<?php
|
||||
$status = $job['status'];
|
||||
|
||||
if ($status === 'completed') {
|
||||
$badge = "<span class='badge bg-success status-badge'>Completato</span>";
|
||||
} elseif ($status === 'processing') {
|
||||
$badge = "<span class='badge bg-warning text-dark status-badge'>In lavorazione</span>";
|
||||
} elseif ($status === 'error') {
|
||||
$badge = "<span class='badge bg-danger status-badge'>Errore</span>";
|
||||
} else {
|
||||
$badge = "<span class='badge bg-secondary status-badge'>Caricato</span>";
|
||||
}
|
||||
|
||||
$areaMm2 = $job['area_mm2'] !== null ? number_format((float)$job['area_mm2'], 3, ',', '.') : '-';
|
||||
$areaCm2 = $job['area_cm2'] !== null ? number_format((float)$job['area_cm2'], 4, ',', '.') : '-';
|
||||
$scale = $job['scale_detected'] ?: '-';
|
||||
$confidence = $job['confidence'] ?: '-';
|
||||
$fileUrl = htmlspecialchars($job['file_url'] ?? '', ENT_QUOTES, 'UTF-8');
|
||||
?>
|
||||
<tr data-id="<?= (int)$job['id']; ?>">
|
||||
<td>
|
||||
<?php if (in_array($job['status'], ['uploaded', 'error'], true)): ?>
|
||||
<input type="checkbox" class="row-check" value="<?= (int)$job['id']; ?>">
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
|
||||
<td title="<?= htmlspecialchars($job['original_filename'], ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<?= htmlspecialchars($job['original_filename'], ENT_QUOTES, 'UTF-8'); ?>
|
||||
</td>
|
||||
|
||||
<td><?= $badge; ?></td>
|
||||
<td class="fw-semibold"><?= $areaMm2; ?></td>
|
||||
<td class="fw-semibold"><?= $areaCm2; ?></td>
|
||||
<td><?= htmlspecialchars($scale, ENT_QUOTES, 'UTF-8'); ?></td>
|
||||
<td><?= htmlspecialchars($confidence, ENT_QUOTES, 'UTF-8'); ?></td>
|
||||
|
||||
<td>
|
||||
<?php if (!empty($job['file_url'])): ?>
|
||||
<a href="<?= $fileUrl; ?>" target="_blank" class="btn btn-sm btn-outline-dark">
|
||||
📄 Apri PDF
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (in_array($job['status'], ['uploaded', 'error'], true)): ?>
|
||||
<button class="btn btn-sm btn-outline-primary process-one" data-id="<?= (int)$job['id']; ?>">
|
||||
⚙️ Calcola
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
|
||||
<button class="btn btn-sm btn-outline-danger delete-one" data-id="<?= (int)$job['id']; ?>">
|
||||
🗑️
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
|
||||
<div class="processing-overlay" id="processingOverlay">
|
||||
<div class="processing-box">
|
||||
<div class="spinner-border text-primary mb-3" role="status"></div>
|
||||
<h5 class="mb-1">Elaborazione in corso</h5>
|
||||
<p class="text-muted mb-0">Invio dei PDF al servizio di calcolo area...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
|
||||
<script>
|
||||
let selectedFiles = [];
|
||||
let uploadedIds = [];
|
||||
|
||||
const dropZone = document.getElementById('dropZone');
|
||||
const fileInput = document.getElementById('pdfFiles');
|
||||
const selectedFilesBox = document.getElementById('selectedFiles');
|
||||
const uploadBtn = document.getElementById('uploadBtn');
|
||||
const processUploadedBtn = document.getElementById('processUploadedBtn');
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#cadAreaTable').DataTable({
|
||||
order: [
|
||||
[1, 'asc']
|
||||
],
|
||||
pageLength: 25,
|
||||
language: {
|
||||
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
dropZone.addEventListener('click', function() {
|
||||
fileInput.click();
|
||||
});
|
||||
|
||||
dropZone.addEventListener('dragover', function(e) {
|
||||
e.preventDefault();
|
||||
dropZone.classList.add('dragover');
|
||||
});
|
||||
|
||||
dropZone.addEventListener('dragleave', function() {
|
||||
dropZone.classList.remove('dragover');
|
||||
});
|
||||
|
||||
dropZone.addEventListener('drop', function(e) {
|
||||
e.preventDefault();
|
||||
dropZone.classList.remove('dragover');
|
||||
handleFiles(e.dataTransfer.files);
|
||||
});
|
||||
|
||||
fileInput.addEventListener('change', function(e) {
|
||||
handleFiles(e.target.files);
|
||||
});
|
||||
|
||||
function handleFiles(files) {
|
||||
const incomingFiles = Array.from(files);
|
||||
|
||||
incomingFiles.forEach(file => {
|
||||
const isPdf = file.type === 'application/pdf' || file.name.toLowerCase().endsWith('.pdf');
|
||||
|
||||
if (!isPdf) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'File non valido',
|
||||
text: `${file.name} non è un PDF.`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.size > 25 * 1024 * 1024) {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'File troppo grande',
|
||||
text: `${file.name} supera il limite di 25 MB.`
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
selectedFiles.push(file);
|
||||
});
|
||||
|
||||
renderSelectedFiles();
|
||||
}
|
||||
|
||||
function renderSelectedFiles() {
|
||||
selectedFilesBox.innerHTML = '';
|
||||
|
||||
selectedFiles.forEach((file, index) => {
|
||||
const sizeMb = (file.size / 1024 / 1024).toFixed(2);
|
||||
|
||||
const item = document.createElement('div');
|
||||
item.className = 'file-preview';
|
||||
item.innerHTML = `
|
||||
<div>
|
||||
<strong>📄 ${escapeHtml(file.name)}</strong><br>
|
||||
<small>${sizeMb} MB</small>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="removeSelectedFile(${index})">
|
||||
Rimuovi
|
||||
</button>
|
||||
`;
|
||||
selectedFilesBox.appendChild(item);
|
||||
});
|
||||
|
||||
uploadBtn.disabled = selectedFiles.length === 0;
|
||||
}
|
||||
|
||||
function removeSelectedFile(index) {
|
||||
selectedFiles.splice(index, 1);
|
||||
renderSelectedFiles();
|
||||
}
|
||||
|
||||
uploadBtn.addEventListener('click', function() {
|
||||
if (selectedFiles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
selectedFiles.forEach(file => {
|
||||
formData.append('pdf_files[]', file);
|
||||
});
|
||||
|
||||
showOverlay();
|
||||
|
||||
fetch('cad_area_upload.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
hideOverlay();
|
||||
|
||||
if (!data.success) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore upload',
|
||||
text: data.message || 'Upload non riuscito'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
uploadedIds = data.ids || [];
|
||||
selectedFiles = [];
|
||||
renderSelectedFiles();
|
||||
|
||||
processUploadedBtn.disabled = uploadedIds.length === 0;
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'PDF caricati',
|
||||
text: 'Ora puoi procedere al calcolo area.',
|
||||
confirmButtonText: 'OK'
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
hideOverlay();
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore',
|
||||
text: error.message || 'Errore durante upload'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
processUploadedBtn.addEventListener('click', function() {
|
||||
if (uploadedIds.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
processJobs(uploadedIds);
|
||||
});
|
||||
|
||||
$('#selectAll').on('change', function() {
|
||||
$('.row-check').prop('checked', this.checked);
|
||||
});
|
||||
|
||||
$('#processSelectedBtn').on('click', function() {
|
||||
const ids = $('.row-check:checked').map(function() {
|
||||
return parseInt($(this).val(), 10);
|
||||
}).get();
|
||||
|
||||
if (ids.length === 0) {
|
||||
Swal.fire({
|
||||
icon: 'info',
|
||||
title: 'Nessun PDF selezionato',
|
||||
text: 'Seleziona almeno un PDF da elaborare.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
processJobs(ids);
|
||||
});
|
||||
|
||||
$(document).on('click', '.process-one', function() {
|
||||
const id = parseInt($(this).data('id'), 10);
|
||||
processJobs([id]);
|
||||
});
|
||||
|
||||
$(document).on('click', '.delete-one', function() {
|
||||
const id = parseInt($(this).data('id'), 10);
|
||||
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'Eliminare il PDF?',
|
||||
text: 'Il file e il relativo risultato saranno rimossi.',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Sì, elimina',
|
||||
cancelButtonText: 'Annulla'
|
||||
}).then(result => {
|
||||
if (!result.isConfirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('cad_area_delete.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: id
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
location.reload();
|
||||
} else {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore',
|
||||
text: data.message || 'Eliminazione non riuscita'
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function processJobs(ids) {
|
||||
showOverlay();
|
||||
|
||||
fetch('cad_area_process.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
ids: ids
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
hideOverlay();
|
||||
|
||||
if (!data.success) {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore elaborazione',
|
||||
text: data.message || 'Elaborazione non riuscita'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Elaborazione completata',
|
||||
text: 'I risultati sono stati aggiornati.',
|
||||
confirmButtonText: 'OK'
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
})
|
||||
.catch(error => {
|
||||
hideOverlay();
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Errore',
|
||||
text: error.message || 'Errore durante elaborazione'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showOverlay() {
|
||||
$('#processingOverlay').css('display', 'flex');
|
||||
}
|
||||
|
||||
function hideOverlay() {
|
||||
$('#processingOverlay').hide();
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
return text.replace(/[&<>"']/g, function(match) {
|
||||
return ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": '''
|
||||
})[match];
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user