added assorbimento corso
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user