producstion pause

This commit is contained in:
2026-09-15 15:46:32 +02:00
parent 4241d8e62b
commit cc24202937
6 changed files with 1125 additions and 82 deletions
+133 -6
View File
@@ -286,6 +286,7 @@ if (!empty($_GET['ajax'])) {
}
.kpi-card {
position: relative;
background: #fff;
border-radius: 1rem;
padding: 1.1rem 1.2rem;
@@ -339,6 +340,7 @@ if (!empty($_GET['ajax'])) {
}
.chart-card {
position: relative;
background: #fff;
border-radius: 1rem;
padding: 1.1rem 1.2rem;
@@ -354,6 +356,7 @@ if (!empty($_GET['ajax'])) {
color: #1e293b;
margin: 0 0 .8rem;
font-size: .95rem;
padding-right: 1.8rem;
}
.chart-wrap {
@@ -362,6 +365,7 @@ if (!empty($_GET['ajax'])) {
}
.table-card {
position: relative;
background: #fff;
border-radius: 1rem;
padding: 1.1rem 1.2rem;
@@ -374,6 +378,7 @@ if (!empty($_GET['ajax'])) {
font-weight: 700;
color: #1e293b;
margin: 0 0 .8rem;
padding-right: 1.8rem;
}
table.stats-table {
@@ -442,6 +447,69 @@ if (!empty($_GET['ajax'])) {
padding: 3rem;
color: #94a3b8;
}
/* --- PULSANTINO "?" INFO --- */
.info-btn {
position: absolute;
top: .6rem;
right: .6rem;
width: 20px;
height: 20px;
border-radius: 50%;
border: none;
cursor: pointer;
background: #e2e8f0;
color: #475569;
font-size: .72rem;
font-weight: 800;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 0;
transition: .15s;
z-index: 5;
}
.info-btn:hover {
background: #dc2626;
color: #fff;
}
.info-pop {
position: absolute;
top: 2.1rem;
right: .6rem;
width: 250px;
max-width: 80vw;
background: #0f172a;
color: #f1f5f9;
border-radius: .6rem;
padding: .7rem .8rem;
font-size: .8rem;
line-height: 1.4;
font-weight: 400;
text-transform: none;
letter-spacing: normal;
box-shadow: 0 8px 24px rgba(0, 0, 0, .25);
z-index: 50;
display: none;
}
.info-pop.show {
display: block;
}
.info-pop strong {
color: #fff;
}
.info-pop .info-pop-title {
display: block;
font-weight: 800;
margin-bottom: .25rem;
color: #fff;
}
</style>
</head>
@@ -496,28 +564,34 @@ if (!empty($_GET['ajax'])) {
<div class="charts-grid" id="chartsGrid" style="display:none;">
<div class="chart-card wide">
<button type="button" class="info-btn" data-title="Pareto delle causali" data-info="Il grafico più utile. Le barre sono le ore perse per ogni causa, ordinate dalla peggiore. La linea sale e mostra la somma progressiva in percentuale. Regola pratica: poche cause fanno la maggior parte del tempo perso. Concentrati sulle prime 2-3 barre a sinistra.">?</button>
<h6>Pareto causali — tempo perso e cumulata</h6>
<div class="chart-wrap"><canvas id="chartPareto"></canvas></div>
</div>
<div class="chart-card">
<button type="button" class="info-btn" data-title="Pianificato vs non pianificato" data-info="La fetta di tempo perso previsto (arancione, es. cambio turno, pulizia) contro quello imprevisto (rosso, es. guasti). Una torta sbilanciata verso il rosso significa molte perdite evitabili.">?</button>
<h6>Ripartizione tempo: pianificato vs non pianificato</h6>
<div class="chart-wrap"><canvas id="chartCat"></canvas></div>
</div>
<div class="chart-card">
<button type="button" class="info-btn" data-title="Tempo perso per linea" data-info="Quale linea perde più ore di produzione a causa dei fermi. Individua la linea più problematica su cui concentrare le attenzioni.">?</button>
<h6>Tempo perso per linea (ore)</h6>
<div class="chart-wrap"><canvas id="chartLinea"></canvas></div>
</div>
<div class="chart-card">
<button type="button" class="info-btn" data-title="Numero fermi per causale" data-info="Quante volte si verifica ogni causa (la frequenza, non la durata). Una causa che compare tante volte anche se breve indica un problema ricorrente da eliminare alla radice.">?</button>
<h6>Numero fermi per causale</h6>
<div class="chart-wrap"><canvas id="chartCount"></canvas></div>
</div>
<div class="chart-card">
<button type="button" class="info-btn" data-title="Andamento giornaliero" data-info="Le ore perse giorno per giorno. Individua le giornate nere e cerca cosa è successo (un guasto grave, una fornitura mancata).">?</button>
<h6>Andamento fermi giornaliero (ore)</h6>
<div class="chart-wrap"><canvas id="chartGiorno"></canvas></div>
</div>
</div>
<div class="table-card" id="tableCard" style="display:none;">
<button type="button" class="info-btn" data-title="Dettaglio eventi" data-info="Ogni riga è un singolo fermo: inizio, fine, durata, motivo, tipo (Problema o Normale), categoria (Pianificato o Non pianificato), linea, matrice e la nota dell'operatore. Il massimo dettaglio per ricostruire cosa è successo in un momento preciso.">?</button>
<h6>Dettaglio eventi di pausa</h6>
<table class="stats-table">
<thead>
@@ -579,15 +653,49 @@ if (!empty($_GET['ajax'])) {
}
}
// Testi spiegazione per i KPI
const KPI_INFO = {
n_pause: {
t: "Fermi totali",
d: "Quante volte le linee si sono fermate in pausa nel periodo. Sotto, quante lavorazioni sono state coinvolte."
},
tot: {
t: "Tempo perso",
d: "Il totale di tutte le pause sommate: il costo in tempo complessivo dei fermi. Confrontalo tra periodi per vedere se migliora."
},
media: {
t: "Durata media",
d: "Quanto dura in media un singolo fermo (accanto, il fermo più lungo). Se il massimo è molto più alto della media, c'è stato un episodio eccezionale da approfondire."
},
problemi: {
t: "Problemi tecnici",
d: "Quanti fermi erano dovuti a un guasto o anomalia vera, e la loro percentuale sul totale. È la spia della salute degli impianti."
},
planned: {
t: "Pianificato",
d: "Il tempo dei fermi previsti (cambio turno, pulizia, manutenzione programmata...). È tempo normale, in parte inevitabile."
},
unplanned: {
t: "Non pianificato",
d: "Il tempo dei fermi imprevisti (guasti, mancanza materiale, qualità...). È QUESTO il tempo da ridurre: sono le perdite evitabili."
},
};
function infoBtn(key) {
const i = KPI_INFO[key];
if (!i) return '';
return `<button type="button" class="info-btn" data-title="${i.t}" data-info="${i.d.replace(/"/g,'&quot;')}">?</button>`;
}
function renderKpi(k) {
const pctProblemi = k.n_pause > 0 ? Math.round(k.n_problemi / k.n_pause * 100) : 0;
$('#kpiGrid').html(`
<div class="kpi-card"><div class="kpi-label">Fermi totali</div><div class="kpi-value">${k.n_pause}</div><div class="kpi-sub">${k.n_prod_con_pause} produzioni coinvolte</div></div>
<div class="kpi-card"><div class="kpi-label">Tempo perso</div><div class="kpi-value" style="font-size:1.25rem;">${secToHms(k.tot_s)}</div><div class="kpi-sub">totale fermi</div></div>
<div class="kpi-card slate"><div class="kpi-label">Durata media</div><div class="kpi-value" style="font-size:1.25rem;">${secToHms(k.media_s)}</div><div class="kpi-sub">max ${secToHms(k.max_s)}</div></div>
<div class="kpi-card red"><div class="kpi-label">Problemi tecnici</div><div class="kpi-value">${k.n_problemi}</div><div class="kpi-sub">${pctProblemi}% dei fermi</div></div>
<div class="kpi-card green"><div class="kpi-label">Pianificato</div><div class="kpi-value" style="font-size:1.25rem;">${secToHms(k.planned_s)}</div><div class="kpi-sub">fermi previsti</div></div>
<div class="kpi-card amber"><div class="kpi-label">Non pianificato</div><div class="kpi-value" style="font-size:1.25rem;">${secToHms(k.unplanned_s)}</div><div class="kpi-sub">da ridurre</div></div>
<div class="kpi-card">${infoBtn('n_pause')}<div class="kpi-label">Fermi totali</div><div class="kpi-value">${k.n_pause}</div><div class="kpi-sub">${k.n_prod_con_pause} produzioni coinvolte</div></div>
<div class="kpi-card">${infoBtn('tot')}<div class="kpi-label">Tempo perso</div><div class="kpi-value" style="font-size:1.25rem;">${secToHms(k.tot_s)}</div><div class="kpi-sub">totale fermi</div></div>
<div class="kpi-card slate">${infoBtn('media')}<div class="kpi-label">Durata media</div><div class="kpi-value" style="font-size:1.25rem;">${secToHms(k.media_s)}</div><div class="kpi-sub">max ${secToHms(k.max_s)}</div></div>
<div class="kpi-card red">${infoBtn('problemi')}<div class="kpi-label">Problemi tecnici</div><div class="kpi-value">${k.n_problemi}</div><div class="kpi-sub">${pctProblemi}% dei fermi</div></div>
<div class="kpi-card green">${infoBtn('planned')}<div class="kpi-label">Pianificato</div><div class="kpi-value" style="font-size:1.25rem;">${secToHms(k.planned_s)}</div><div class="kpi-sub">fermi previsti</div></div>
<div class="kpi-card amber">${infoBtn('unplanned')}<div class="kpi-label">Non pianificato</div><div class="kpi-value" style="font-size:1.25rem;">${secToHms(k.unplanned_s)}</div><div class="kpi-sub">da ridurre</div></div>
`);
}
@@ -802,6 +910,7 @@ if (!empty($_GET['ajax'])) {
$('#kpiGrid').html('<div class="empty">Errore di comunicazione con il server.</div>');
});
}
$(function() {
const today = new Date(),
past = new Date();
@@ -823,6 +932,24 @@ if (!empty($_GET['ajax'])) {
$('#fDateFrom').val(past.toISOString().slice(0, 10));
loadStats();
});
// --- POPOVER INFO (delegato: funziona anche sui box creati via JS) ---
$(document).on('click', '.info-btn', function(e) {
e.stopPropagation();
const $btn = $(this);
const alreadyOpen = $btn.next('.info-pop').length && $btn.next('.info-pop').hasClass('show');
$('.info-pop').remove();
if (alreadyOpen) return;
const title = $btn.data('title') || '';
const text = $btn.data('info') || '';
const $pop = $('<div class="info-pop show"></div>');
$pop.html('<span class="info-pop-title">' + title + '</span>' + text);
$btn.after($pop);
});
$(document).on('click', function() {
$('.info-pop').remove();
});
loadStats();
});
</script>