655 lines
23 KiB
PHP
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 ({
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": '''
|
|
})[match];
|
|
});
|
|
}
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|