zibo-dashboard/public/userarea/skill_matrix.php

396 lines
15 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
ini_set('display_errors', 1);
error_reporting(E_ALL);
include('include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// AJAX salvataggio singolo
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax']) && $_POST['ajax'] == '1') {
header('Content-Type: application/json');
if ($_POST['action'] === 'save_single_skill') {
$employee_id = (int)($_POST['employee_id'] ?? 0);
$skill_id = (int)($_POST['skill_id'] ?? 0);
$level = trim($_POST['level'] ?? '');
try {
$pdo->prepare("DELETE FROM employee_skills WHERE employee_id = ? AND skill_id = ?")
->execute([$employee_id, $skill_id]);
if ($level !== '' && $level !== '') {
$pdo->prepare("INSERT INTO employee_skills (employee_id, skill_id, level) VALUES (?, ?, ?)")
->execute([$employee_id, $skill_id, $level]);
}
echo json_encode(['success' => true]);
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
exit;
}
echo json_encode(['success' => false, 'message' => 'Azione non valida']);
exit;
}
// DATI
$employees = $pdo->query("
SELECT id, CONCAT(first_name, ' ', last_name) AS nome
FROM employees
ORDER BY id
")->fetchAll(PDO::FETCH_ASSOC);
$skills = $pdo->query("
SELECT
s.id,
s.name,
COALESCE(s.abbreviato, SUBSTRING(s.name, 1, 12)) AS acronimo,
pl.line_number,
pl.name AS linea
FROM skills s
LEFT JOIN production_lines pl ON s.production_line_id = pl.id
ORDER BY s.ordinamento ASC, COALESCE(pl.line_number, 999), s.id
")->fetchAll(PDO::FETCH_ASSOC);
$livelli = [];
$stmt = $pdo->query("SELECT employee_id, skill_id, level FROM employee_skills");
while ($r = $stmt->fetch(PDO::FETCH_ASSOC)) {
$livelli[$r['employee_id']][$r['skill_id']] = $r['level'];
}
?>
<!doctype html>
<html lang="it">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<?php include('cssinclude.php'); ?>
<title>Matrice Skills</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<style>
body {
font-size: 0.95rem;
background: #f8fafc;
}
.card {
border-radius: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
}
.back-btn {
background: #cfe3ff !important;
color: #1f2d3d !important;
border: 1px solid #bcd4f4 !important;
border-radius: 10px;
font-weight: 600;
padding: 8px 16px;
}
.table th,
.table td {
text-align: center;
vertical-align: middle;
padding: 6px 8px;
}
.table thead th {
background: #cfe3ff;
color: #1f2d3d;
white-space: nowrap;
}
.table thead th.group {
background: #a5d8ff;
font-weight: bold;
font-size: 1.05rem;
}
.fixed-name {
min-width: 220px !important;
max-width: 260px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 600;
background: #e9f3ff !important;
position: sticky;
left: 0;
z-index: 1;
}
.skill-col {
min-width: 60px;
}
.skill-header {
font-size: 0.72rem !important;
line-height: 1.0;
padding: 4px 6px !important;
}
.table select,
.table input[type="text"] {
width: 100%;
font-size: 0.85rem;
padding: 4px 6px;
text-align: center;
border-radius: 4px;
border: 1px solid #ccc;
}
.table input[type="text"] {
background: #fffacd;
}
/* COLORI LIVELLI */
.level-Q {
background: #d4edda;
color: #155724;
font-weight: 600;
}
.level-CQ {
background: #fff3cd;
color: #856404;
}
.level-C {
background: #ffeeba;
color: #856404;
}
.level-DF {
background: #f8d7da;
color: #721c24;
}
.level-si {
background: #28a745;
color: white;
font-weight: bold;
}
.level-no {
background: #dc3545;
color: white;
}
.level-NON-RICH {
background: #e9ecef;
color: #6c757d;
}
/* Cella vuota / trattino */
td:empty,
td select option[value=""]:checked~select,
td input[value=""] {
background: #f8f9fa !important;
color: #6c757d;
}
.table-responsive {
overflow-x: auto;
}
</style>
</head>
<body>
<div class="wrapper toggled">
<?php include('include/navbar.php'); ?>
<?php include('include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<div class="card p-3">
<div class="card-header d-flex justify-content-between align-items-center">
<h5 class="mb-0">Matrice Skills</h5>
<button class="btn back-btn" onclick="location.href='employees.php'">↩️ Dipendenti</button>
</div>
<div class="card-body p-0">
<div class="table-responsive">
<table id="matrix" class="table table-bordered table-sm">
<thead>
<tr>
<th rowspan="2" class="fixed-name" style="background:#cfe3ff;">Dipendente</th>
<th colspan="2" class="group text-center" style="background:#fff;"></th>
<th colspan="2" class="group text-center">UHF</th>
<th colspan="2" class="group text-center">BSF</th>
<th colspan="2" class="group text-center">Gerlach</th>
</tr>
<tr>
<?php foreach ($skills as $s): ?>
<th class="skill-col skill-header"
title="<?= htmlspecialchars($s['name']) ?>">
<?= htmlspecialchars($s['acronimo']) ?>
</th>
<?php endforeach; ?>
</tr>
</thead>
<tbody>
<?php foreach ($employees as $emp): ?>
<tr>
<td class="fixed-name"><?= htmlspecialchars($emp['nome']) ?></td>
<?php foreach ($skills as $s):
// Default: trattino se non c'è valore
$val = $livelli[$emp['id']][$s['id']] ?? '';
// Classe solo se valore reale (non trattino)
$cls = ($val !== '' && $val !== '') ? str_replace(['.', ' '], '-', $val) : '';
?>
<td>
<?php if ($s['id'] == 29): ?>
<!-- Campo testo: mostra vuoto se trattino -->
<input type="text"
class="form-control"
value="<?= $val === '' ? '' : htmlspecialchars($val) ?>"
onblur="salvaTesto(<?= $emp['id'] ?>, <?= $s['id'] ?>, this.value)">
<?php else: ?>
<!-- Tendina: include opzione "" -->
<select class="form-select <?= $cls ? 'level-' . $cls : '' ?>"
onchange="salva(<?= $emp['id'] ?>, <?= $s['id'] ?>, this.value, this)">
<?php if (strpos($s['name'], 'TURNO NOTTURNO') !== false): ?>
<option value="si" <?= $val === 'si' ? 'selected' : '' ?>>si</option>
<option value="no" <?= $val === 'no' ? 'selected' : '' ?>>no</option>
<option value="" <?= $val === '' ? 'selected' : '' ?>></option>
<?php else: ?>
<option value="DF" <?= $val === 'DF' ? 'selected' : '' ?>>DF</option>
<option value="C" <?= $val === 'C' ? 'selected' : '' ?>>C</option>
<option value="CQ" <?= $val === 'CQ' ? 'selected' : '' ?>>CQ</option>
<option value="Q" <?= $val === 'Q' ? 'selected' : '' ?>>Q</option>
<option value="NON RICH." <?= $val === 'NON RICH.' ? 'selected' : '' ?>>NON RICH.</option>
<option value="" <?= $val === '' ? 'selected' : '' ?>></option>
<?php endif; ?>
</select>
<?php endif; ?>
</td>
<?php endforeach; ?>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<script>
// Salvataggio per tendina
function salva(empId, skillId, val, selectElement) {
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `ajax=1&action=save_single_skill&employee_id=${empId}&skill_id=${skillId}&level=${encodeURIComponent(val)}`
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'OK',
timer: 700,
showConfirmButton: false
});
if (selectElement && selectElement.tagName === 'SELECT') {
selectElement.className = selectElement.className.replace(/\blevel-[^\s]+\b/g, '').trim();
if (val && val !== '') {
const newCls = 'level-' + val.replace(/[\.\s]/g, '-');
selectElement.classList.add(newCls);
}
selectElement.classList.add('form-select');
}
} else {
Swal.fire({
icon: 'error',
title: 'Errore server',
text: data.message || '?'
});
}
})
.catch(err => {
console.error('Fetch error:', err);
Swal.fire({
icon: 'error',
title: 'Errore rete',
text: err.message || 'Controlla console'
});
});
}
// Salvataggio per testo
function salvaTesto(empId, skillId, val) {
val = val.trim();
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `ajax=1&action=save_single_skill&employee_id=${empId}&skill_id=${skillId}&level=${encodeURIComponent(val)}`
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire({
icon: 'success',
title: 'Salvato',
timer: 700,
showConfirmButton: false
});
} else {
Swal.fire({
icon: 'error',
title: 'Errore',
text: data.message || '?'
});
}
})
.catch(() => Swal.fire({
icon: 'error',
title: 'Errore rete'
}));
}
$(document).ready(function() {
$('#matrix').DataTable({
scrollX: true,
scrollCollapse: true,
paging: false,
searching: false,
info: false,
ordering: false,
fixedColumns: {
left: 1
},
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
},
columnDefs: [{
targets: 0,
className: 'fixed-name'
}]
});
});
</script>
</body>
</html>