Files
zibo-dashboard/public/userarea/cad-area.php
T
2026-06-11 09:02:22 +02:00

655 lines
23 KiB
PHP

<?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 ({
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
})[match];
});
}
</script>
</body>
</html>