added assorbimento corso
This commit is contained in:
@@ -7,6 +7,8 @@ $pdo = $db->getConnection();
|
||||
/* ==========================================
|
||||
PAGE DATA
|
||||
========================================== */
|
||||
require_once __DIR__ . '/include/training_coverage.php';
|
||||
|
||||
$sql = "
|
||||
SELECT tt.*,
|
||||
(SELECT COUNT(*) FROM employee_trainings et WHERE et.training_topic_id = tt.id) AS trainings_count
|
||||
@@ -14,6 +16,30 @@ $sql = "
|
||||
ORDER BY tt.sort_order ASC, tt.name ASC
|
||||
";
|
||||
$topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
/* Coperture: [topic_id => [ ['id'=>..,'name'=>..], ... ]] */
|
||||
$coverageRows = $pdo->query("
|
||||
SELECT c.topic_id, c.covered_topic_id, tt.name AS covered_name
|
||||
FROM training_topic_coverage c
|
||||
JOIN training_topics tt ON tt.id = c.covered_topic_id
|
||||
ORDER BY tt.sort_order, tt.name
|
||||
")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$coverageByTopic = [];
|
||||
foreach ($coverageRows as $cr) {
|
||||
$coverageByTopic[(int)$cr['topic_id']][] = [
|
||||
'id' => (int)$cr['covered_topic_id'],
|
||||
'name' => $cr['covered_name'],
|
||||
];
|
||||
}
|
||||
|
||||
/* Mappa inversa: chi copre questo corso */
|
||||
$coveredByTopic = [];
|
||||
foreach ($coverageRows as $cr) {
|
||||
$coveredByTopic[(int)$cr['covered_topic_id']][] = (int)$cr['topic_id'];
|
||||
}
|
||||
$topicNames = [];
|
||||
foreach ($topics as $t) $topicNames[(int)$t['id']] = $t['name'];
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="it">
|
||||
@@ -274,6 +300,42 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
color: #94a3b8;
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
.cov-chip {
|
||||
display: inline-block;
|
||||
padding: 2px 9px;
|
||||
border-radius: 999px;
|
||||
background: #ede9fe;
|
||||
color: #5b21b6;
|
||||
border: 1px solid #ddd6fe;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
margin: 2px 3px 2px 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.cov-chip-parent {
|
||||
background: #fef3c7;
|
||||
color: #92400e;
|
||||
border-color: #fde68a;
|
||||
}
|
||||
|
||||
.cov-cell {
|
||||
max-width: 240px;
|
||||
text-align: left;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.cov-help {
|
||||
font-size: .85rem;
|
||||
color: #64748b;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
#addCovered,
|
||||
#editCovered {
|
||||
min-height: 140px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@@ -328,6 +390,7 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
<th>Ordine</th>
|
||||
<th>Stato</th>
|
||||
<th>Formazioni</th>
|
||||
<th>Copre</th>
|
||||
<th>Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -368,6 +431,25 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
<td><?= $sortOrder ?></td>
|
||||
<td><span class="badge-status <?= $statusClass ?>"><?= $statusLabel ?></span></td>
|
||||
<td><?= $cnt ?></td>
|
||||
<td class="cov-cell">
|
||||
<?php $covList = $coverageByTopic[$id] ?? []; ?>
|
||||
<?php if ($covList): ?>
|
||||
<?php foreach ($covList as $cv): ?>
|
||||
<span class="cov-chip">⛓️ <?= htmlspecialchars($cv['name']) ?></span>
|
||||
<?php endforeach; ?>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">—</span>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($coveredByTopic[$id])): ?>
|
||||
<div class="mt-1">
|
||||
<?php foreach ($coveredByTopic[$id] as $parentId): ?>
|
||||
<span class="cov-chip cov-chip-parent" title="Questo corso è assorbito da">
|
||||
↑ <?= htmlspecialchars($topicNames[$parentId] ?? ('#' . $parentId)) ?>
|
||||
</span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-sm btn-outline-secondary edit-topic"
|
||||
data-id="<?= $id ?>"
|
||||
@@ -377,7 +459,8 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
data-rem="<?= $rem ?>"
|
||||
data-sort_order="<?= $sortOrder ?>"
|
||||
data-is_active="<?= $isActive ?>"
|
||||
data-is_mandatory="<?= $isMandatory ?>">
|
||||
data-is_mandatory="<?= $isMandatory ?>"
|
||||
data-covered="<?= htmlspecialchars(json_encode(array_column($coverageByTopic[$id] ?? [], 'id')), ENT_QUOTES) ?>">
|
||||
✏️ Modifica
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger delete-topic"
|
||||
@@ -430,6 +513,17 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
<span><b>Formazioni:</b> <?= $cnt ?></span>
|
||||
<span><b>Ordine:</b> <?= $sortOrder ?></span>
|
||||
</div>
|
||||
<?php $covList = $coverageByTopic[$id] ?? []; ?>
|
||||
<?php if ($covList || !empty($coveredByTopic[$id])): ?>
|
||||
<div class="mb-2">
|
||||
<?php foreach ($covList as $cv): ?>
|
||||
<span class="cov-chip">⛓️ <?= htmlspecialchars($cv['name']) ?></span>
|
||||
<?php endforeach; ?>
|
||||
<?php foreach (($coveredByTopic[$id] ?? []) as $parentId): ?>
|
||||
<span class="cov-chip cov-chip-parent">↑ <?= htmlspecialchars($topicNames[$parentId] ?? ('#' . $parentId)) ?></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="tt-card-actions">
|
||||
<button class="btn btn-sm btn-outline-secondary edit-topic"
|
||||
data-id="<?= $id ?>"
|
||||
@@ -439,7 +533,8 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
data-rem="<?= $rem ?>"
|
||||
data-sort_order="<?= $sortOrder ?>"
|
||||
data-is_active="<?= $isActive ?>"
|
||||
data-is_mandatory="<?= $isMandatory ?>">
|
||||
data-is_mandatory="<?= $isMandatory ?>"
|
||||
data-covered="<?= htmlspecialchars(json_encode(array_column($coverageByTopic[$id] ?? [], 'id')), ENT_QUOTES) ?>">
|
||||
✏️ Modifica
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-danger delete-topic"
|
||||
@@ -520,6 +615,19 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
Se attivo, i dipendenti senza registrazione di questo corso compaiono come "Non presente" nello storico.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">⛓️ Corsi assorbiti da questo</label>
|
||||
<select class="form-select" id="addCovered" multiple size="6">
|
||||
<?php foreach ($topics as $t): ?>
|
||||
<option value="<?= (int)$t['id'] ?>"><?= htmlspecialchars($t['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="cov-help">
|
||||
Chi completa questo corso risulta automaticamente in regola anche per quelli selezionati.
|
||||
La scadenza del corso assorbito viene ricalcolata con la sua frequenza propria.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-add">💾 Salva</button>
|
||||
</div>
|
||||
@@ -590,6 +698,18 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
Se attivo, i dipendenti senza registrazione di questo corso compaiono come "Non presente" nello storico.
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold">⛓️ Corsi assorbiti da questo</label>
|
||||
<select class="form-select" id="editCovered" multiple size="6">
|
||||
<?php foreach ($topics as $t): ?>
|
||||
<option value="<?= (int)$t['id'] ?>"><?= htmlspecialchars($t['name']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<div class="cov-help">
|
||||
Chi completa questo corso risulta automaticamente in regola anche per quelli selezionati.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="text-center">
|
||||
<button type="submit" class="btn btn-add">💾 Salva Modifiche</button>
|
||||
</div>
|
||||
@@ -604,6 +724,10 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#tabellaTopics').DataTable({
|
||||
columnDefs: [{
|
||||
targets: [8, 9],
|
||||
orderable: false
|
||||
}],
|
||||
order: [
|
||||
[5, 'asc'],
|
||||
[1, 'asc']
|
||||
@@ -660,6 +784,9 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
p.append('sort_order', $("#addSortOrder").val());
|
||||
p.append('is_active', $("#addIsActive").val());
|
||||
p.append('is_mandatory', $("#addIsMandatory").is(':checked') ? '1' : '0');
|
||||
($("#addCovered").val() || []).forEach(function(v) {
|
||||
p.append('covered_ids[]', v);
|
||||
});
|
||||
ajaxPost("ajax/training_topics/save.php", p, "Salvato!", "Impossibile salvare il corso.");
|
||||
});
|
||||
|
||||
@@ -678,6 +805,24 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
$("#editSortOrder").val(b.data("sort_order"));
|
||||
$("#editIsActive").val(String(b.data("is_active")));
|
||||
$("#editIsMandatory").prop('checked', String(b.data("is_mandatory")) === '1');
|
||||
|
||||
// Coperture: nascondi il corso stesso e preseleziona le esistenti
|
||||
var selfId = String(b.data("id"));
|
||||
var covered = b.data("covered") || [];
|
||||
if (typeof covered === 'string') {
|
||||
try {
|
||||
covered = JSON.parse(covered);
|
||||
} catch (e) {
|
||||
covered = [];
|
||||
}
|
||||
}
|
||||
covered = covered.map(String);
|
||||
$("#editCovered option").each(function() {
|
||||
$(this).prop('hidden', this.value === selfId)
|
||||
.prop('disabled', this.value === selfId);
|
||||
});
|
||||
$("#editCovered").val(covered);
|
||||
|
||||
$("#editTopicModal").modal("show");
|
||||
});
|
||||
|
||||
@@ -692,6 +837,9 @@ $topics = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||||
p.append('sort_order', $("#editSortOrder").val());
|
||||
p.append('is_active', $("#editIsActive").val());
|
||||
p.append('is_mandatory', $("#editIsMandatory").is(':checked') ? '1' : '0');
|
||||
($("#editCovered").val() || []).forEach(function(v) {
|
||||
p.append('covered_ids[]', v);
|
||||
});
|
||||
ajaxPost("ajax/training_topics/save.php", p, "Aggiornato!", "Impossibile aggiornare il corso.");
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user