519 lines
19 KiB
PHP
519 lines
19 KiB
PHP
<?php
|
|
include('include/headscript.php');
|
|
|
|
/*
|
|
* NOTE PERMESSI:
|
|
* Il login è già gestito da headscript.php (Auth::check + redirect).
|
|
* Se vuoi limitare questa pagina a determinati ruoli, aggiungi qui il tuo
|
|
* controllo, esattamente come nelle altre pagine. Esempio:
|
|
*
|
|
* if (!$user->hasRole('Admin') && !$user->hasRole('SuperUser')) {
|
|
* header('Location: import_dashboard.php');
|
|
* exit;
|
|
* }
|
|
*/
|
|
?>
|
|
<!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>Aggiornamento dati - <?= htmlspecialchars($titlewebsite ?? 'SmartTRF', ENT_QUOTES, 'UTF-8'); ?></title>
|
|
|
|
<style>
|
|
.warm-hero {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 18px;
|
|
}
|
|
|
|
.warm-hero .warm-icon {
|
|
width: 64px;
|
|
height: 64px;
|
|
border-radius: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 30px;
|
|
color: #fff;
|
|
background: linear-gradient(135deg, #0f172a, #1d4ed8);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.warm-progress-wrap {
|
|
display: none;
|
|
}
|
|
|
|
.warm-progress-wrap.is-visible {
|
|
display: block;
|
|
}
|
|
|
|
.progress.warm-progress {
|
|
height: 22px;
|
|
border-radius: 12px;
|
|
background: #eef2f7;
|
|
}
|
|
|
|
.progress.warm-progress .progress-bar {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.warm-steps {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 18px 0 0 0;
|
|
}
|
|
|
|
.warm-steps li {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
padding: 10px 12px;
|
|
border-radius: 10px;
|
|
margin-bottom: 6px;
|
|
background: #f8fafc;
|
|
border: 1px solid #eef2f7;
|
|
transition: background .2s, border-color .2s;
|
|
}
|
|
|
|
.warm-steps li .step-icon {
|
|
width: 26px;
|
|
height: 26px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
flex-shrink: 0;
|
|
background: #e5e7eb;
|
|
color: #94a3b8;
|
|
}
|
|
|
|
.warm-steps li.is-active {
|
|
background: #eff6ff;
|
|
border-color: #bfdbfe;
|
|
}
|
|
|
|
.warm-steps li.is-active .step-icon {
|
|
background: #2563eb;
|
|
color: #fff;
|
|
}
|
|
|
|
.warm-steps li.is-done {
|
|
background: #f0fdf4;
|
|
border-color: #bbf7d0;
|
|
}
|
|
|
|
.warm-steps li.is-done .step-icon {
|
|
background: #16a34a;
|
|
color: #fff;
|
|
}
|
|
|
|
.warm-steps li .step-label {
|
|
font-weight: 600;
|
|
color: #1f2937;
|
|
}
|
|
|
|
.warm-steps li .step-detail {
|
|
font-size: 12px;
|
|
color: #64748b;
|
|
margin-left: auto;
|
|
text-align: right;
|
|
}
|
|
|
|
.warm-log {
|
|
display: none;
|
|
margin-top: 18px;
|
|
}
|
|
|
|
.warm-log.is-visible {
|
|
display: block;
|
|
}
|
|
|
|
.warm-log pre {
|
|
max-height: 320px;
|
|
overflow: auto;
|
|
background: #0f172a;
|
|
color: #cbd5e1;
|
|
padding: 16px;
|
|
border-radius: 12px;
|
|
font-size: 12px;
|
|
line-height: 1.6;
|
|
margin: 0;
|
|
}
|
|
|
|
.warm-result {
|
|
display: none;
|
|
margin-top: 18px;
|
|
}
|
|
|
|
.warm-result.is-visible {
|
|
display: block;
|
|
}
|
|
</style>
|
|
</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-body p-4">
|
|
|
|
<div class="warm-hero">
|
|
<div class="warm-icon"><i class="bx bx-refresh"></i></div>
|
|
<div>
|
|
<h5 class="mb-1">Aggiornamento dati dal gestionale</h5>
|
|
<p class="mb-0 text-muted">
|
|
Ricarica clienti, matrici, analisi e le altre liste dal software di laboratorio.
|
|
Da usare quando sono state fatte modifiche sul gestionale e vuoi vederle subito
|
|
senza aspettare l'aggiornamento automatico.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<hr class="my-4">
|
|
|
|
<div class="form-check form-switch mb-3" id="analisiSwitchWrap">
|
|
<input class="form-check-input" type="checkbox" role="switch" id="includeAnalisi" checked>
|
|
<label class="form-check-label" for="includeAnalisi">
|
|
Aggiorna anche le <strong>analisi</strong>
|
|
<span class="text-muted small">(più lento — togli la spunta se hai modificato solo clienti o altre liste)</span>
|
|
</label>
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-primary btn-lg px-4" id="btnStart">
|
|
<i class="bx bx-play-circle me-1"></i> Avvia aggiornamento
|
|
</button>
|
|
<div class="small text-muted mt-2" id="idleHint">
|
|
L'operazione può richiedere da qualche secondo a un paio di minuti.
|
|
</div>
|
|
|
|
<!-- Progress -->
|
|
<div class="warm-progress-wrap mt-4" id="progressWrap">
|
|
<div class="d-flex justify-content-between align-items-center mb-2">
|
|
<span class="fw-semibold" id="progressTitle">Aggiornamento in corso…</span>
|
|
<span class="text-muted small" id="progressElapsed">0s</span>
|
|
</div>
|
|
|
|
<div class="progress warm-progress">
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated bg-primary"
|
|
id="progressBar" role="progressbar" style="width: 0%;">0%</div>
|
|
</div>
|
|
|
|
<ul class="warm-steps" id="stepsList">
|
|
<!-- steps injected by JS -->
|
|
</ul>
|
|
|
|
<div class="mt-3">
|
|
<a href="javascript:;" class="small text-decoration-none" id="toggleLog">
|
|
<i class="bx bx-code-alt"></i> Mostra dettaglio tecnico
|
|
</a>
|
|
</div>
|
|
|
|
<div class="warm-log" id="logBox">
|
|
<pre id="logPre"></pre>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Result -->
|
|
<div class="warm-result" id="resultBox">
|
|
<div class="alert d-flex align-items-center" id="resultAlert" role="alert">
|
|
<i class="bx me-2 fs-4" id="resultIcon"></i>
|
|
<div id="resultText"></div>
|
|
</div>
|
|
<button type="button" class="btn btn-outline-primary" id="btnAgain">
|
|
<i class="bx bx-refresh me-1"></i> Esegui di nuovo
|
|
</button>
|
|
</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>
|
|
|
|
<?php include('jsinclude.php'); ?>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
|
/*
|
|
* Se sposti warm_cache.php in un'altra cartella, cambia SOLO questa riga.
|
|
*/
|
|
const WARM_ENDPOINT = 'warm_cache.php';
|
|
|
|
const btnStart = document.getElementById('btnStart');
|
|
const btnAgain = document.getElementById('btnAgain');
|
|
const idleHint = document.getElementById('idleHint');
|
|
const includeAnalisi = document.getElementById('includeAnalisi');
|
|
const progressWrap = document.getElementById('progressWrap');
|
|
const progressBar = document.getElementById('progressBar');
|
|
const progressTitle = document.getElementById('progressTitle');
|
|
const progressElapsed = document.getElementById('progressElapsed');
|
|
const stepsList = document.getElementById('stepsList');
|
|
const toggleLog = document.getElementById('toggleLog');
|
|
const logBox = document.getElementById('logBox');
|
|
const logPre = document.getElementById('logPre');
|
|
const resultBox = document.getElementById('resultBox');
|
|
const resultAlert = document.getElementById('resultAlert');
|
|
const resultIcon = document.getElementById('resultIcon');
|
|
const resultText = document.getElementById('resultText');
|
|
|
|
/*
|
|
* I "passi" mostrati al cliente in modo leggibile.
|
|
* key = frammento che compare nel log tecnico (dentro le parentesi quadre)
|
|
* label = nome umano
|
|
* Il progresso avanza man mano che questi tag compaiono nel log.
|
|
*/
|
|
const STEPS = [{
|
|
key: 'clients',
|
|
label: 'Clienti'
|
|
},
|
|
{
|
|
key: 'MoltiplicatorePrezzo',
|
|
label: 'Listini prezzi'
|
|
},
|
|
{
|
|
key: 'AnagraficaCertest',
|
|
label: 'Anagrafiche Certest'
|
|
},
|
|
{
|
|
key: 'ClienteResponsabile',
|
|
label: 'Responsabili cliente'
|
|
},
|
|
{
|
|
key: 'CustomField',
|
|
label: 'Campi personalizzati'
|
|
},
|
|
{
|
|
key: 'matrici',
|
|
label: 'Matrici'
|
|
},
|
|
{
|
|
key: 'analisi',
|
|
label: 'Analisi'
|
|
}
|
|
];
|
|
|
|
let stepEls = {};
|
|
let currentStepIndex = -1;
|
|
let timerId = null;
|
|
let startedAt = 0;
|
|
let activeSteps = []; // i passi effettivamente in gioco per questa esecuzione
|
|
|
|
function buildSteps() {
|
|
stepsList.innerHTML = '';
|
|
stepEls = {};
|
|
// Se le analisi sono disattivate, escludo quel passo dalla lista.
|
|
const withAnalisi = includeAnalisi.checked;
|
|
activeSteps = STEPS.filter(function(s) {
|
|
return withAnalisi || s.key !== 'analisi';
|
|
});
|
|
activeSteps.forEach(function(step, i) {
|
|
const li = document.createElement('li');
|
|
li.dataset.index = i;
|
|
li.innerHTML =
|
|
'<span class="step-icon"><i class="bx bx-time"></i></span>' +
|
|
'<span class="step-label">' + step.label + '</span>' +
|
|
'<span class="step-detail"></span>';
|
|
stepsList.appendChild(li);
|
|
stepEls[i] = li;
|
|
});
|
|
}
|
|
|
|
function markStep(index, state, detail) {
|
|
const li = stepEls[index];
|
|
if (!li) return;
|
|
li.classList.remove('is-active', 'is-done');
|
|
const icon = li.querySelector('.step-icon i');
|
|
if (state === 'active') {
|
|
li.classList.add('is-active');
|
|
icon.className = 'bx bx-loader-alt bx-spin';
|
|
} else if (state === 'done') {
|
|
li.classList.add('is-done');
|
|
icon.className = 'bx bx-check';
|
|
}
|
|
if (detail !== undefined) {
|
|
li.querySelector('.step-detail').textContent = detail;
|
|
}
|
|
}
|
|
|
|
function advanceTo(index, detail) {
|
|
if (index <= currentStepIndex) {
|
|
// stesso passo, aggiorna solo il dettaglio (es. conteggio)
|
|
if (detail !== undefined) markStep(index, 'active', detail);
|
|
return;
|
|
}
|
|
// chiudi i passi precedenti
|
|
for (let i = 0; i <= currentStepIndex; i++) markStep(i, 'done');
|
|
currentStepIndex = index;
|
|
markStep(index, 'active', detail);
|
|
const pct = Math.round(((index) / activeSteps.length) * 100);
|
|
setProgress(Math.max(pct, 3));
|
|
}
|
|
|
|
function setProgress(pct) {
|
|
pct = Math.max(0, Math.min(100, pct));
|
|
progressBar.style.width = pct + '%';
|
|
progressBar.textContent = pct + '%';
|
|
}
|
|
|
|
function matchStep(line) {
|
|
// cerca [clients], [matrici], [analisi] ecc. nella riga di log
|
|
for (let i = 0; i < activeSteps.length; i++) {
|
|
if (line.indexOf('[' + activeSteps[i].key) !== -1 ||
|
|
line.indexOf(activeSteps[i].key + ']') !== -1) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
function extractDetail(line) {
|
|
// prova a estrarre un conteggio tipo "Cached 128 clients" o "34 analisi"
|
|
const m = line.match(/(\d+)\s+(clients|items|values|responsabili|analisi)/i);
|
|
if (m) return m[1] + ' elementi';
|
|
return undefined;
|
|
}
|
|
|
|
function startTimer() {
|
|
startedAt = Date.now();
|
|
timerId = setInterval(function() {
|
|
const s = Math.round((Date.now() - startedAt) / 1000);
|
|
progressElapsed.textContent = s + 's';
|
|
}, 1000);
|
|
}
|
|
|
|
function stopTimer() {
|
|
if (timerId) clearInterval(timerId);
|
|
timerId = null;
|
|
}
|
|
|
|
function resetUI() {
|
|
buildSteps();
|
|
currentStepIndex = -1;
|
|
setProgress(0);
|
|
logPre.textContent = '';
|
|
progressTitle.textContent = 'Aggiornamento in corso…';
|
|
progressElapsed.textContent = '0s';
|
|
resultBox.classList.remove('is-visible');
|
|
progressWrap.classList.add('is-visible');
|
|
}
|
|
|
|
function showResult(type, message) {
|
|
stopTimer();
|
|
progressBar.classList.remove('progress-bar-animated', 'progress-bar-striped');
|
|
|
|
resultAlert.className = 'alert d-flex align-items-center';
|
|
if (type === 'success') {
|
|
resultAlert.classList.add('alert-success');
|
|
resultIcon.className = 'bx bx-check-circle me-2 fs-4';
|
|
progressBar.classList.add('bg-success');
|
|
} else if (type === 'warning') {
|
|
resultAlert.classList.add('alert-warning');
|
|
resultIcon.className = 'bx bx-error me-2 fs-4';
|
|
progressBar.classList.add('bg-warning');
|
|
} else {
|
|
resultAlert.classList.add('alert-danger');
|
|
resultIcon.className = 'bx bx-x-circle me-2 fs-4';
|
|
progressBar.classList.add('bg-danger');
|
|
}
|
|
resultText.textContent = message;
|
|
resultBox.classList.add('is-visible');
|
|
btnStart.disabled = false;
|
|
includeAnalisi.disabled = false;
|
|
}
|
|
|
|
function run() {
|
|
btnStart.disabled = true;
|
|
includeAnalisi.disabled = true;
|
|
idleHint.style.display = 'none';
|
|
resetUI();
|
|
startTimer();
|
|
|
|
const analisiParam = includeAnalisi.checked ? '1' : '0';
|
|
const es = new EventSource(WARM_ENDPOINT + '?stream=1&analisi=' + analisiParam);
|
|
|
|
es.onmessage = function(e) {
|
|
const line = e.data;
|
|
logPre.textContent += line + '\n';
|
|
logPre.scrollTop = logPre.scrollHeight;
|
|
|
|
const idx = matchStep(line);
|
|
if (idx !== -1) {
|
|
advanceTo(idx, extractDetail(line));
|
|
}
|
|
};
|
|
|
|
es.addEventListener('busy', function(e) {
|
|
es.close();
|
|
showResult('warning', e.data ||
|
|
'Un aggiornamento è già in corso. Attendi qualche istante e riprova.');
|
|
});
|
|
|
|
es.addEventListener('done', function(e) {
|
|
es.close();
|
|
// completa tutti i passi
|
|
for (let i = 0; i < activeSteps.length; i++) markStep(i, 'done');
|
|
setProgress(100);
|
|
let elapsed = '';
|
|
try {
|
|
elapsed = JSON.parse(e.data).elapsed;
|
|
} catch (err) {}
|
|
|
|
// se nel log è comparsa la parola ERRORE, segnala come warning
|
|
if (logPre.textContent.indexOf('ERRORE') !== -1) {
|
|
showResult('warning',
|
|
'Aggiornamento terminato, ma alcuni dati potrebbero non essere stati caricati. ' +
|
|
'Controlla il dettaglio tecnico.');
|
|
} else {
|
|
progressTitle.textContent = 'Completato';
|
|
showResult('success',
|
|
'Dati aggiornati correttamente' +
|
|
(elapsed ? ' in ' + elapsed + ' secondi.' : '.') +
|
|
' Le nuove informazioni sono ora disponibili.');
|
|
}
|
|
});
|
|
|
|
es.onerror = function() {
|
|
// EventSource tenta il reconnect da solo: lo blocchiamo.
|
|
es.close();
|
|
stopTimer();
|
|
// Se avevamo già finito, il done è già scattato: non sovrascrivere.
|
|
if (!resultBox.classList.contains('is-visible')) {
|
|
showResult('error',
|
|
'Connessione interrotta durante l\'aggiornamento. ' +
|
|
'Verifica la rete e riprova.');
|
|
}
|
|
};
|
|
}
|
|
|
|
btnStart.addEventListener('click', run);
|
|
btnAgain.addEventListener('click', run);
|
|
|
|
toggleLog.addEventListener('click', function() {
|
|
logBox.classList.toggle('is-visible');
|
|
toggleLog.innerHTML = logBox.classList.contains('is-visible') ?
|
|
'<i class="bx bx-hide"></i> Nascondi dettaglio tecnico' :
|
|
'<i class="bx bx-code-alt"></i> Mostra dettaglio tecnico';
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
|
|
</html>
|