added assorbimento corso

This commit is contained in:
2026-07-24 08:32:50 +02:00
parent 6d60f89be2
commit c11adf19e8
6 changed files with 691 additions and 7 deletions
+104
View File
@@ -162,6 +162,8 @@ if ($employee) {
$trainings = [];
$trainingTopicsAll = [];
$missingMandatoryTopics = [];
$coveredTrainings = [];
$coveredKeys = [];
if ($employee) {
$stmt = $pdo->prepare("
SELECT et.*,
@@ -207,6 +209,34 @@ if ($employee) {
");
$missingStmt->execute(['eid' => $employeeId]);
$missingMandatoryTopics = $missingStmt->fetchAll(PDO::FETCH_ASSOC);
/* ==========================================
COPERTURE — corsi assorbiti da altri corsi
========================================== */
require_once __DIR__ . '/include/training_coverage.php';
$allTopicsById = [];
foreach ($pdo->query("SELECT id, name, default_frequency_months, default_reminder_days, is_active FROM training_topics")->fetchAll(PDO::FETCH_ASSOC) as $tt) {
if ((int)$tt['is_active'] !== 1) continue;
$allTopicsById[(int)$tt['id']] = $tt;
}
// Solo i record più recenti per topic generano copertura
$latestOnly = array_values(array_filter($trainings, function ($t) {
return !empty($t['_is_latest']);
}));
foreach ($latestOnly as &$lo) {
$lo['employee_id'] = $employeeId;
}
unset($lo);
$coveredTrainings = tc_buildVirtualRows($pdo, $latestOnly, $allTopicsById);
$coveredKeys = tc_coveredKeys($coveredTrainings);
// I corsi obbligatori coperti non sono più "non presenti"
$missingMandatoryTopics = array_values(array_filter($missingMandatoryTopics, function ($mt) use ($coveredKeys, $employeeId) {
return !isset($coveredKeys[$employeeId . ':' . (int)$mt['id']]);
}));
}
/* ==========================================
@@ -590,6 +620,29 @@ function fmtFileSize(?int $bytes): string
border: 1px solid #cbd5e1;
}
.pill-covered {
background: #ede9fe;
color: #5b21b6;
border: 1px solid #ddd6fe;
font-size: .78rem;
padding: 3px 9px;
white-space: nowrap;
}
tr.row-covered {
background-color: #faf5ff !important;
}
tr.row-covered td {
background-color: transparent !important;
}
.cov-note {
font-size: .8rem;
color: #7c3aed;
margin-top: 2px;
}
.pill-status-success {
background: #d1fae5;
color: #065f46;
@@ -1641,12 +1694,57 @@ function fmtFileSize(?int $bytes): string
</td>
</tr>
<?php endforeach; ?>
<?php foreach ($coveredTrainings as $cv): ?>
<?php
$s = trainingStatus(
$cv['next_due_date'] ?: null,
null,
$cv['topic_default_rem'] !== null ? (int)$cv['topic_default_rem'] : null
);
?>
<tr class="row-covered">
<td class="fw-semibold">
<?= htmlspecialchars($cv['topic_name']) ?>
<div class="cov-note">⛓️ Assorbito da <?= htmlspecialchars($cv['_covered_by_name']) ?></div>
</td>
<td><span class="pill pill-covered">Coperto</span></td>
<td><?= fmtDate($cv['completed_date']) ?></td>
<td><?= fmtDate($cv['next_due_date']) ?></td>
<td><span class="pill pill-status-<?= $s['class'] ?>"><?= $s['label'] ?></span></td>
<td>—</td>
<td class="text-end"><span class="text-muted small">—</span></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- MOBILE CARDS -->
<div class="d-block d-md-none">
<?php foreach ($coveredTrainings as $cv): ?>
<?php
$s = trainingStatus(
$cv['next_due_date'] ?: null,
null,
$cv['topic_default_rem'] !== null ? (int)$cv['topic_default_rem'] : null
);
?>
<div class="doc-card" style="background:#faf5ff;">
<div class="d-flex justify-content-between align-items-start gap-2 mb-2">
<span class="doc-card-title">📖 <?= htmlspecialchars($cv['topic_name']) ?></span>
<span class="pill pill-status-<?= $s['class'] ?>"><?= $s['label'] ?></span>
</div>
<div class="mb-2"><span class="pill pill-covered">⛓️ Assorbito da <?= htmlspecialchars($cv['_covered_by_name']) ?></span></div>
<div class="doc-card-meta">
<span><b>Completato:</b> <?= fmtDate($cv['completed_date']) ?></span>
<?php if ($cv['next_due_date']): ?>
<span><b>Prossimo:</b> <?= fmtDate($cv['next_due_date']) ?></span>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
<?php foreach ($trainings as $t): ?>
<?php
$tid = (int)$t['id'];
@@ -2305,6 +2403,12 @@ function fmtFileSize(?int $bytes): string
order: [
[2, 'desc']
],
createdRow: function(row, data, index) {
// preserva l'evidenziazione delle righe coperte dopo il redraw
if ($(row).find('.pill-covered').length) {
$(row).addClass('row-covered');
}
},
language: {
search: "Cerca:",
lengthMenu: "Mostra _MENU_ righe",