751 lines
33 KiB
PHP
751 lines
33 KiB
PHP
<?php
|
|
include('include/headscript.php');
|
|
|
|
// Controlla se è stato passato un ID valido
|
|
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
|
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
|
|
exit;
|
|
}
|
|
|
|
$id = intval($_GET['id']); // Sanifica l'ID
|
|
|
|
// Recupera il template dal database
|
|
$db = DBHandlerSelect::getInstance();
|
|
$pdo = $db->getConnection();
|
|
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
|
$stmt->execute([$id]);
|
|
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$template) {
|
|
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
|
exit;
|
|
}
|
|
|
|
// Verifica i mapping
|
|
$stmt = $pdo->prepare("SELECT id FROM template_mapping WHERE template_id = ?");
|
|
$stmt->execute([$id]);
|
|
$hasMappings = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
// Debug del template
|
|
error_log("Loaded template: " . print_r($template, true));
|
|
?>
|
|
|
|
<!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 href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<style>
|
|
.top-scrollbar {
|
|
overflow-x: auto;
|
|
overflow-y: hidden;
|
|
width: 100%;
|
|
height: 18px;
|
|
margin-bottom: 8px;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.25rem;
|
|
background: #f8f9fa;
|
|
display: none;
|
|
position: sticky;
|
|
top: 0;
|
|
z-index: 1020;
|
|
}
|
|
|
|
.top-scrollbar-inner {
|
|
height: 1px;
|
|
}
|
|
|
|
.table-container {
|
|
overflow-x: auto;
|
|
max-width: 100%;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.table {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
}
|
|
|
|
.table th,
|
|
.table td {
|
|
padding: 10px;
|
|
text-align: left;
|
|
border: 1px solid #dee2e6;
|
|
min-width: 100px;
|
|
max-width: 200px;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
.table th:first-child,
|
|
.table td:first-child {
|
|
min-width: 50px;
|
|
max-width: 50px;
|
|
}
|
|
|
|
.table th {
|
|
background-color: #f8f9fa;
|
|
position: relative;
|
|
cursor: col-resize;
|
|
user-select: none;
|
|
}
|
|
|
|
.table th .resize-handle {
|
|
position: absolute;
|
|
top: 0;
|
|
right: 0;
|
|
width: 5px;
|
|
height: 100%;
|
|
cursor: col-resize;
|
|
background: transparent;
|
|
}
|
|
|
|
.table th .resize-handle:hover {
|
|
background: #007bff;
|
|
}
|
|
|
|
.search-container {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.search-container input {
|
|
width: 300px;
|
|
padding: 8px;
|
|
border: 1px solid #ced4da;
|
|
border-radius: 5px;
|
|
}
|
|
|
|
.loader {
|
|
display: none;
|
|
border: 4px solid #f3f3f3;
|
|
border-top: 4px solid #3498db;
|
|
border-radius: 50%;
|
|
width: 30px;
|
|
height: 30px;
|
|
animation: spin 1s linear infinite;
|
|
margin: 10px auto;
|
|
}
|
|
|
|
@keyframes spin {
|
|
0% {
|
|
transform: rotate(0deg);
|
|
}
|
|
|
|
100% {
|
|
transform: rotate(360deg);
|
|
}
|
|
}
|
|
|
|
.column-filters th {
|
|
background: #ffffff;
|
|
cursor: default;
|
|
}
|
|
|
|
.column-filters input {
|
|
width: 100%;
|
|
min-width: 80px;
|
|
}
|
|
</style>
|
|
<title><?= htmlspecialchars($template['name']) ?> - <?= 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">
|
|
<?php include('top_stat_widget.php'); ?>
|
|
<div class="mb-3 text">
|
|
<a href="imported.php?id=<?= $id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
|
<a href="tolims.php?id=<?= $id ?>" class="btn btn-success">To LIMS (l)</a>
|
|
</div>
|
|
<div class="card radius-10">
|
|
<div class="card-header">
|
|
<div class="d-flex align-items-center">
|
|
<div>
|
|
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?></h6>
|
|
<small>
|
|
Template ID: <?= $id ?>,
|
|
Sheet Number: <?= (int)($template['xls_sheet_index'] ?? 0) ?>,
|
|
Start Row: <?= $template['header_row'] ?>,
|
|
Start Column: <?= $template['start_column'] ?>
|
|
</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<?php if (!$hasMappings): ?>
|
|
<div class="alert alert-warning" role="alert">
|
|
Nessun mapping trovato per questo template. Configura i mapping prima di procedere.
|
|
</div>
|
|
<?php endif; ?>
|
|
<form id="uploadForm" enctype="multipart/form-data" class="mb-4">
|
|
<div class="mb-3">
|
|
<label for="excel_file" class="form-label">Upload XLS File</label>
|
|
<input type="file" class="form-control" id="excel_file" name="excel_file" accept=".xls,.xlsx" required>
|
|
</div>
|
|
<button type="submit" class="btn btn-primary" <?= !$hasMappings ? 'disabled' : '' ?>>Upload</button>
|
|
<div class="loader" id="loader"></div>
|
|
</form>
|
|
|
|
<div id="errorContainer" class="alert alert-danger mt-3" style="display: none;"></div>
|
|
|
|
<div id="tableContainer"></div>
|
|
|
|
<div class="modal fade" id="routineConfirmModal" tabindex="-1" aria-labelledby="routineConfirmModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="routineConfirmModalLabel">Conferma Applicazione Routine</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p><strong>Routine:</strong> <span id="routineName"></span></p>
|
|
<p><strong>Descrizione:</strong> <span id="routineDescription"></span></p>
|
|
<p>Vuoi applicare questa routine al file caricato?</p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancelRoutineBtn">Annulla</button>
|
|
<button type="button" class="btn btn-primary" id="confirmRoutineBtn">Applica</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</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>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
|
|
<?php include('jsinclude.php'); ?>
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function() {
|
|
const form = document.getElementById('uploadForm');
|
|
const loader = document.getElementById('loader');
|
|
const errorContainer = document.getElementById('errorContainer');
|
|
const tableContainer = document.getElementById('tableContainer');
|
|
const routineModal = new bootstrap.Modal(document.getElementById('routineConfirmModal'));
|
|
const confirmRoutineBtn = document.getElementById('confirmRoutineBtn');
|
|
const cancelRoutineBtn = document.getElementById('cancelRoutineBtn');
|
|
let routineData = null;
|
|
let excelData = null;
|
|
let responseData = null;
|
|
|
|
form.addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
loader.style.display = 'block';
|
|
errorContainer.style.display = 'none';
|
|
tableContainer.innerHTML = '';
|
|
|
|
const formData = new FormData(this);
|
|
const templateId = <?= $id ?>;
|
|
console.log('Template ID passed to formData:', templateId);
|
|
formData.append('template_id', templateId);
|
|
formData.append('header_row', <?= (int)$template['header_row'] ?>);
|
|
formData.append('start_column', <?= json_encode($template['start_column']) ?>);
|
|
formData.append('xls_sheet_index', <?= (int)($template['xls_sheet_index'] ?? 0) ?>);
|
|
|
|
fetch('process_import_xls2.php', {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => {
|
|
console.log('Stato risposta:', response.status);
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log('Risposta JSON:', data);
|
|
loader.style.display = 'none';
|
|
if (data.error) {
|
|
errorContainer.textContent = data.error;
|
|
errorContainer.style.display = 'block';
|
|
} else if (data.apply_routine) {
|
|
console.log('Routine rilevata:', data.routine_data);
|
|
routineData = data.routine_data;
|
|
excelData = data.excel_data;
|
|
responseData = data;
|
|
document.getElementById('routineName').textContent = routineData.name || 'Sconosciuta';
|
|
document.getElementById('routineDescription').textContent = routineData.instruction || 'Nessuna descrizione';
|
|
routineModal.show();
|
|
} else {
|
|
console.log('Nessuna routine, procedo con tabella');
|
|
showTable(data);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.log('Errore fetch:', error);
|
|
loader.style.display = 'none';
|
|
errorContainer.textContent = 'Errore durante il caricamento del file: ' + error.message;
|
|
errorContainer.style.display = 'block';
|
|
});
|
|
});
|
|
|
|
confirmRoutineBtn.addEventListener('click', function() {
|
|
console.log('Conferma routine:', routineData);
|
|
routineModal.hide();
|
|
|
|
fetch('apply_routine.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
template_id: <?= $id ?>,
|
|
filename: routineData.filename,
|
|
headerrow: routineData.headerrow,
|
|
excel_data: excelData,
|
|
routine_data: routineData
|
|
})
|
|
})
|
|
.then(response => {
|
|
console.log('Stato apply_routine:', response.status);
|
|
return response.json();
|
|
})
|
|
.then(data => {
|
|
console.log('Risposta apply_routine:', data);
|
|
if (data.error) {
|
|
errorContainer.textContent = data.error;
|
|
errorContainer.style.display = 'block';
|
|
} else {
|
|
showTable(data);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.log('Errore apply_routine:', error);
|
|
errorContainer.textContent = 'Errore durante l\'applicazione della routine: ' + error.message;
|
|
errorContainer.style.display = 'block';
|
|
});
|
|
});
|
|
|
|
cancelRoutineBtn.addEventListener('click', function() {
|
|
console.log('Routine annullata, procedo con tabella');
|
|
routineModal.hide();
|
|
showTable(responseData);
|
|
});
|
|
|
|
function showTable(data) {
|
|
console.log('Mostro tabella con dati:', data);
|
|
let html = `
|
|
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
|
<input type="hidden" name="template_id" value="${data.template_id}">
|
|
<input type="hidden" name="columns" value="${encodeURIComponent(JSON.stringify(data.columns))}">
|
|
<input type="hidden" name="rows" id="selectedRowsData" value="">
|
|
<input type="hidden" name="excelrows" id="selectedExcelRowsData" value="">
|
|
<input type="hidden" name="filename" value="${data.filename}">
|
|
|
|
<!-- TOP BUTTON -->
|
|
<div class="d-flex justify-content-end align-items-center mb-3 gap-2">
|
|
<span class="badge bg-secondary" id="selectedCountTop">0 selezionati</span>
|
|
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
|
</div>
|
|
|
|
<div class="top-scrollbar" id="topTableScrollbar">
|
|
<div class="top-scrollbar-inner" id="topTableScrollbarInner"></div>
|
|
</div>
|
|
|
|
<div class="table-container" id="mainTableContainer">
|
|
<table class="table table-striped table-bordered" id="importPreviewTable">
|
|
<thead>
|
|
<tr>
|
|
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
|
${data.columns.map(col => {
|
|
const label = !col ? 'Colonna senza nome' : (col.match(/^__empty_\d+__$/) ? 'Colonna senza nome' : col);
|
|
return `<th>${label}<div class="resize-handle"></div></th>`;
|
|
}).join('')}
|
|
</tr>
|
|
<tr class="column-filters">
|
|
<th></th>
|
|
${data.columns.map((col, i) => `
|
|
<th>
|
|
<input type="text"
|
|
class="form-control form-control-sm column-filter"
|
|
data-col-index="${i}"
|
|
placeholder="Filter...">
|
|
</th>
|
|
`).join('')}
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${data.excel_data.map((row, index) => `
|
|
<tr>
|
|
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${row.excelrow}"></td>
|
|
${row.data.map(cell => `<td>${cell}</td>`).join('')}
|
|
</tr>
|
|
`).join('')}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- BOTTOM BUTTON -->
|
|
<div class="d-flex align-items-center mt-3 gap-2">
|
|
<span class="badge bg-secondary" id="selectedCountBottom">0 selezionati</span>
|
|
<button type="submit" class="btn btn-primary" id="proceedButtonBottom" disabled>Prosegui</button>
|
|
</div>
|
|
</form>
|
|
`;
|
|
tableContainer.innerHTML = html;
|
|
|
|
const selectRowsForm = document.getElementById('selectRowsForm');
|
|
|
|
// Clic sulla cella (oltre che sulla checkbox) per selezionare/deselezionare la riga
|
|
document.querySelectorAll('#importPreviewTable tbody tr').forEach(tr => {
|
|
tr.style.cursor = 'pointer';
|
|
tr.addEventListener('click', function(e) {
|
|
// Se ho cliccato proprio sulla checkbox, lascio fare a lei (evita doppio toggle)
|
|
if (e.target.classList.contains('row-checkbox')) return;
|
|
|
|
const cb = this.querySelector('.row-checkbox');
|
|
if (!cb) return;
|
|
cb.checked = !cb.checked;
|
|
cb.dispatchEvent(new Event('change', {
|
|
bubbles: true
|
|
}));
|
|
});
|
|
});
|
|
|
|
selectRowsForm.addEventListener('submit', async function(e) {
|
|
e.preventDefault(); // gestiamo tutto via AJAX a batch
|
|
|
|
const checkedBoxes = Array.from(document.querySelectorAll('.row-checkbox:checked'));
|
|
|
|
if (checkedBoxes.length === 0) {
|
|
alert('Seleziona almeno una riga.');
|
|
return;
|
|
}
|
|
|
|
// Raccoglie le righe selezionate (riga mappata + excelrow parallelo)
|
|
const selectedRows = [];
|
|
const selectedExcelRows = [];
|
|
|
|
checkedBoxes.forEach(cb => {
|
|
const originalIndex = parseInt(cb.value, 10);
|
|
if (data.rows && data.rows[originalIndex]) {
|
|
selectedRows.push(data.rows[originalIndex]);
|
|
selectedExcelRows.push(
|
|
(data.excel_data && data.excel_data[originalIndex]) ?
|
|
data.excel_data[originalIndex].excelrow :
|
|
null
|
|
);
|
|
}
|
|
});
|
|
|
|
const total = selectedRows.length;
|
|
if (total === 0) {
|
|
alert('Nessuna riga valida da importare.');
|
|
return;
|
|
}
|
|
|
|
// importreferencecode generato UNA volta, condiviso da tutti i batch
|
|
const importReferenceCode =
|
|
new Date().toISOString().replace(/[-:.TZ]/g, '').slice(0, 14) +
|
|
'-' + Math.random().toString(36).slice(2, 10);
|
|
|
|
const BATCH_SIZE = 20;
|
|
|
|
// Disabilita i pulsanti e mostra la barra di progresso
|
|
const btnTop = document.getElementById('proceedButtonTop');
|
|
const btnBottom = document.getElementById('proceedButtonBottom');
|
|
if (btnTop) btnTop.disabled = true;
|
|
if (btnBottom) btnBottom.disabled = true;
|
|
|
|
const progressHtml = `
|
|
<div id="importProgressWrap" style="
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0,0,0,0.5);
|
|
z-index: 20000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;">
|
|
<div style="
|
|
background: #fff;
|
|
border-radius: 10px;
|
|
padding: 25px 30px;
|
|
width: 90%;
|
|
max-width: 500px;
|
|
box-shadow: 0 10px 40px rgba(0,0,0,0.3);">
|
|
<div class="mb-2"><strong>Importazione in corso...</strong>
|
|
<span id="importProgressText" class="float-end">0 / ${total}</span>
|
|
</div>
|
|
<div class="progress" style="height: 26px;">
|
|
<div id="importProgressBar" class="progress-bar progress-bar-striped progress-bar-animated"
|
|
role="progressbar" style="width: 0%;">0%</div>
|
|
</div>
|
|
<div id="importProgressError" class="alert alert-danger mt-3 mb-0" style="display:none;"></div>
|
|
</div>
|
|
</div>`;
|
|
document.body.insertAdjacentHTML('beforeend', progressHtml);
|
|
|
|
const progressBar = document.getElementById('importProgressBar');
|
|
const progressText = document.getElementById('importProgressText');
|
|
const progressErr = document.getElementById('importProgressError');
|
|
|
|
let insertedTotal = 0;
|
|
|
|
// Invia un batch e ritorna la risposta JSON
|
|
async function sendBatch(startIndex) {
|
|
const batchRows = selectedRows.slice(startIndex, startIndex + BATCH_SIZE);
|
|
const batchExcelrows = selectedExcelRows.slice(startIndex, startIndex + BATCH_SIZE);
|
|
|
|
const resp = await fetch('import_insert_batch.php', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify({
|
|
template_id: data.template_id,
|
|
importreferencecode: importReferenceCode,
|
|
filename: data.filename,
|
|
columns: data.columns,
|
|
batch_rows: batchRows,
|
|
batch_excelrows: batchExcelrows
|
|
})
|
|
});
|
|
return resp.json();
|
|
}
|
|
|
|
// Cicla i batch in sequenza
|
|
for (let start = 0; start < total; start += BATCH_SIZE) {
|
|
let result;
|
|
try {
|
|
result = await sendBatch(start);
|
|
} catch (err) {
|
|
result = {
|
|
ok: false,
|
|
error: err.message
|
|
};
|
|
}
|
|
|
|
if (!result.ok) {
|
|
// Batch fallito: chiediamo all'utente se continuare
|
|
progressErr.textContent =
|
|
'Errore su un blocco (righe ' + (start + 1) + '-' +
|
|
Math.min(start + BATCH_SIZE, total) + '): ' + (result.error || 'errore sconosciuto');
|
|
progressErr.style.display = 'block';
|
|
|
|
const continua = confirm(
|
|
'Un blocco di righe non è stato importato.\n\n' +
|
|
'Righe inserite finora: ' + insertedTotal + ' di ' + total + '.\n\n' +
|
|
'Vuoi continuare con i blocchi successivi? (Annulla = interrompi)'
|
|
);
|
|
|
|
if (!continua) {
|
|
progressBar.classList.remove('progress-bar-animated');
|
|
progressBar.classList.add('bg-warning');
|
|
return;
|
|
}
|
|
// se continua, prosegue col prossimo blocco
|
|
continue;
|
|
}
|
|
|
|
insertedTotal += (result.inserted || 0);
|
|
const done = Math.min(start + BATCH_SIZE, total);
|
|
const pct = Math.round((done / total) * 100);
|
|
progressBar.style.width = pct + '%';
|
|
progressBar.textContent = pct + '%';
|
|
progressText.textContent = insertedTotal + ' / ' + total;
|
|
}
|
|
|
|
// Fine: barra verde e redirect a imported.php
|
|
progressBar.classList.remove('progress-bar-animated', 'progress-bar-striped');
|
|
progressBar.classList.add('bg-success');
|
|
progressText.textContent = insertedTotal + ' / ' + total + ' completate';
|
|
|
|
// PAUSA temporanea di 2 secondi per vedere la barra completa (da rimuovere dopo il test)
|
|
await new Promise(r => setTimeout(r, 2000));
|
|
|
|
window.location.href = 'imported.php?id=' +
|
|
encodeURIComponent(data.template_id) +
|
|
'&importref=' + encodeURIComponent(importReferenceCode);
|
|
});
|
|
|
|
const topTableScrollbar = document.getElementById('topTableScrollbar');
|
|
const topTableScrollbarInner = document.getElementById('topTableScrollbarInner');
|
|
const mainTableContainer = document.getElementById('mainTableContainer');
|
|
const importPreviewTable = document.getElementById('importPreviewTable');
|
|
|
|
function updateTopTableScrollbar() {
|
|
if (!topTableScrollbar || !topTableScrollbarInner || !mainTableContainer || !importPreviewTable) return;
|
|
|
|
topTableScrollbarInner.style.width = importPreviewTable.scrollWidth + 'px';
|
|
|
|
if (mainTableContainer.scrollWidth > mainTableContainer.clientWidth) {
|
|
topTableScrollbar.style.display = 'block';
|
|
} else {
|
|
topTableScrollbar.style.display = 'none';
|
|
}
|
|
}
|
|
|
|
let syncingTop = false;
|
|
let syncingBottom = false;
|
|
|
|
if (topTableScrollbar && mainTableContainer) {
|
|
topTableScrollbar.addEventListener('scroll', function() {
|
|
if (syncingBottom) return;
|
|
syncingTop = true;
|
|
mainTableContainer.scrollLeft = topTableScrollbar.scrollLeft;
|
|
syncingTop = false;
|
|
});
|
|
|
|
mainTableContainer.addEventListener('scroll', function() {
|
|
if (syncingTop) return;
|
|
syncingBottom = true;
|
|
topTableScrollbar.scrollLeft = mainTableContainer.scrollLeft;
|
|
syncingBottom = false;
|
|
});
|
|
}
|
|
|
|
updateTopTableScrollbar();
|
|
setTimeout(updateTopTableScrollbar, 100);
|
|
setTimeout(updateTopTableScrollbar, 300);
|
|
window.addEventListener('resize', updateTopTableScrollbar);
|
|
|
|
const proceedButtonTop = document.getElementById('proceedButtonTop');
|
|
const proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
|
const selectAllCheckbox = document.getElementById('selectAll');
|
|
const checkboxes = document.querySelectorAll('.row-checkbox');
|
|
|
|
function updateProceedButton() {
|
|
const checkedCount = Array.from(checkboxes).filter(cb => cb.checked).length;
|
|
const enabled = checkedCount > 0;
|
|
|
|
if (proceedButtonTop) proceedButtonTop.disabled = !enabled;
|
|
if (proceedButtonBottom) proceedButtonBottom.disabled = !enabled;
|
|
|
|
const label = checkedCount + ' selezionati';
|
|
const countTop = document.getElementById('selectedCountTop');
|
|
const countBottom = document.getElementById('selectedCountBottom');
|
|
if (countTop) countTop.textContent = label;
|
|
if (countBottom) countBottom.textContent = label;
|
|
}
|
|
|
|
selectAllCheckbox.addEventListener('change', function() {
|
|
const visibleRows = Array.from(document.querySelectorAll('.table tbody tr'))
|
|
.filter(row => row.style.display !== 'none');
|
|
|
|
visibleRows.forEach(row => {
|
|
const checkbox = row.querySelector('.row-checkbox');
|
|
if (checkbox) {
|
|
checkbox.checked = this.checked;
|
|
}
|
|
});
|
|
|
|
updateProceedButton();
|
|
});
|
|
|
|
checkboxes.forEach(checkbox => {
|
|
checkbox.addEventListener('change', function() {
|
|
console.log('Checkbox changed, checked:', this.checked, 'excelrow:', this.dataset.excelrow);
|
|
|
|
const visibleCheckboxes = Array.from(document.querySelectorAll('.table tbody tr'))
|
|
.filter(row => row.style.display !== 'none')
|
|
.map(row => row.querySelector('.row-checkbox'))
|
|
.filter(cb => cb !== null);
|
|
|
|
selectAllCheckbox.checked =
|
|
visibleCheckboxes.length > 0 &&
|
|
visibleCheckboxes.every(cb => cb.checked);
|
|
|
|
updateProceedButton();
|
|
});
|
|
});
|
|
|
|
const thElements = document.querySelectorAll('.table th');
|
|
thElements.forEach((th, index) => {
|
|
if (index === 0) return;
|
|
const resizeHandle = th.querySelector('.resize-handle');
|
|
if (resizeHandle) {
|
|
resizeHandle.addEventListener('mousedown', (e) => {
|
|
e.preventDefault();
|
|
const startX = e.clientX;
|
|
const startWidth = th.offsetWidth;
|
|
|
|
const onMouseMove = (e) => {
|
|
const newWidth = Math.max(50, startWidth + (e.clientX - startX));
|
|
th.style.width = `${newWidth}px`;
|
|
th.style.minWidth = `${newWidth}px`;
|
|
th.style.maxWidth = `${newWidth}px`;
|
|
|
|
const cells = document.querySelectorAll(`.table td:nth-child(${index + 1})`);
|
|
cells.forEach(cell => {
|
|
cell.style.width = `${newWidth}px`;
|
|
cell.style.minWidth = `${newWidth}px`;
|
|
cell.style.maxWidth = `${newWidth}px`;
|
|
});
|
|
};
|
|
|
|
const onMouseUp = () => {
|
|
document.removeEventListener('mousemove', onMouseMove);
|
|
document.removeEventListener('mouseup', onMouseUp);
|
|
};
|
|
|
|
document.addEventListener('mousemove', onMouseMove);
|
|
document.addEventListener('mouseup', onMouseUp);
|
|
});
|
|
}
|
|
});
|
|
|
|
const rows = document.querySelectorAll('.table tbody tr');
|
|
const filterInputs = document.querySelectorAll('.column-filter');
|
|
|
|
// Stato filtri: key = colIndex, value = testo
|
|
const activeFilters = {};
|
|
|
|
function applyColumnFilters() {
|
|
rows.forEach(row => {
|
|
let visible = true;
|
|
|
|
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
|
const colIndex = parseInt(colIndexStr, 10);
|
|
const cell = row.cells[colIndex + 1];
|
|
|
|
const cellText = (cell?.textContent || '').toLowerCase();
|
|
const searchText = (filterValue || '').toLowerCase().trim();
|
|
|
|
if (searchText && !cellText.includes(searchText)) {
|
|
visible = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
row.style.display = visible ? '' : 'none';
|
|
});
|
|
|
|
const visibleCheckboxes = Array.from(document.querySelectorAll('.table tbody tr'))
|
|
.filter(row => row.style.display !== 'none')
|
|
.map(row => row.querySelector('.row-checkbox'))
|
|
.filter(cb => cb !== null);
|
|
|
|
selectAllCheckbox.checked =
|
|
visibleCheckboxes.length > 0 &&
|
|
visibleCheckboxes.every(cb => cb.checked);
|
|
|
|
updateProceedButton();
|
|
}
|
|
|
|
filterInputs.forEach(input => {
|
|
input.addEventListener('input', function() {
|
|
const colIndex = this.dataset.colIndex; // string
|
|
activeFilters[colIndex] = this.value;
|
|
applyColumnFilters();
|
|
});
|
|
});
|
|
|
|
updateProceedButton();
|
|
}
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|