429 lines
22 KiB
PHP
429 lines
22 KiB
PHP
<?php
|
||
include('include/headscript.php');
|
||
|
||
$db = DBHandlerSelect::getInstance();
|
||
$pdo = $db->getConnection();
|
||
|
||
/* ==========================================
|
||
PAGE DATA
|
||
========================================== */
|
||
$sql = "
|
||
SELECT jr.*,
|
||
(SELECT COUNT(*) FROM employees e WHERE e.job_role_id = jr.id) AS employees_count
|
||
FROM job_roles jr
|
||
ORDER BY jr.sort_order ASC, jr.name ASC
|
||
";
|
||
$jobRoles = $pdo->query($sql)->fetchAll(PDO::FETCH_ASSOC);
|
||
?>
|
||
<!doctype html>
|
||
<html lang="it">
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||
<?php include('cssinclude.php'); ?>
|
||
<title>Gestione Mansioni - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||
|
||
<script src="https://code.jquery.com/jquery-3.6.0.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: 1.05rem; background: #f8fafc; }
|
||
.card { border-radius: 16px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); }
|
||
.back-dashboard {
|
||
background-color: #cfe3ff !important; color: #1f2d3d !important;
|
||
border: 1px solid #bcd4f4 !important; border-radius: 10px;
|
||
font-weight: 600; padding: 10px 18px;
|
||
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.1);
|
||
transition: all 0.2s ease-in-out;
|
||
}
|
||
.back-dashboard:hover { background-color: #b9d3ff !important; transform: translateY(-2px); }
|
||
.btn-add { background-color: #0d6efd; color: #fff; border-radius: 8px; padding: 10px 20px; font-weight: 500; }
|
||
.btn-add:hover { background-color: #0b5ed7; transform: scale(1.02); }
|
||
.table thead { background-color: #cfe3ff; color: #1f2d3d; }
|
||
.modal-content { border-radius: 16px; }
|
||
#tabellaJobRoles thead th { text-align: center; vertical-align: middle; }
|
||
.badge-status { padding: 0.25rem 0.6rem; border-radius: 999px; font-size: 0.8rem; font-weight: 600; }
|
||
.badge-status.active { background-color: #d1fae5; color: #065f46; }
|
||
.badge-status.inactive { background-color: #e5e7eb; color: #374151; }
|
||
.description-cell {
|
||
max-width: 320px; white-space: nowrap; overflow: hidden;
|
||
text-overflow: ellipsis; text-align: left;
|
||
}
|
||
@media (max-width: 767.98px) {
|
||
.card-header { flex-direction: column; align-items: flex-start !important; gap: .5rem; }
|
||
.back-dashboard { width: 100%; }
|
||
.btn-add { width: 100%; }
|
||
}
|
||
|
||
.jr-card {
|
||
border: 1px solid #e2e8f0;
|
||
border-radius: 14px;
|
||
padding: 14px 16px;
|
||
margin-bottom: 12px;
|
||
background: #fff;
|
||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04);
|
||
}
|
||
.jr-card-title {
|
||
font-size: 1.1rem;
|
||
font-weight: 600;
|
||
color: #1f2937;
|
||
margin: 0 0 4px 0;
|
||
word-break: break-word;
|
||
}
|
||
.jr-card-desc {
|
||
color: #475569;
|
||
font-size: 0.95rem;
|
||
margin: 0 0 10px 0;
|
||
word-break: break-word;
|
||
}
|
||
.jr-card-meta {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px 14px;
|
||
font-size: 0.85rem;
|
||
color: #64748b;
|
||
margin-bottom: 12px;
|
||
}
|
||
.jr-card-meta b { color: #1f2937; font-weight: 600; }
|
||
.jr-card-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
}
|
||
.jr-card-actions .btn {
|
||
flex: 1;
|
||
}
|
||
.jr-empty {
|
||
text-align: center;
|
||
color: #94a3b8;
|
||
padding: 24px 0;
|
||
}
|
||
</style>
|
||
</head>
|
||
|
||
<body>
|
||
<div class="wrapper" id="appWrapper">
|
||
<?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 flex-wrap gap-2">
|
||
<h5 class="mb-0">Gestione Mansioni</h5>
|
||
<button type="button" class="btn back-dashboard" onclick="location.href='production_dashboard.php'">
|
||
↩️ Torna alla Dashboard
|
||
</button>
|
||
</div>
|
||
|
||
<div class="card-body">
|
||
<div class="d-flex justify-content-between align-items-center mb-3 flex-wrap gap-2">
|
||
<h6 class="fw-semibold mb-0">Elenco Mansioni / Job Roles</h6>
|
||
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addJobRoleModal">
|
||
➕ Aggiungi Mansione
|
||
</button>
|
||
</div>
|
||
|
||
<!-- DESKTOP / TABLET ≥768px: TABLE -->
|
||
<div class="table-responsive d-none d-md-block"><!-- hide on <md -->
|
||
<table id="tabellaJobRoles" class="table table-striped align-middle text-center" style="width:100%;">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th>
|
||
<th>Nome</th>
|
||
<th>Descrizione</th>
|
||
<th>Ordine</th>
|
||
<th>Stato</th>
|
||
<th>Dipendenti</th>
|
||
<th>Creato</th>
|
||
<th>Azioni</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<?php foreach ($jobRoles as $row): ?>
|
||
<?php
|
||
$id = (int)$row['id'];
|
||
$name = $row['name'] ?? '';
|
||
$description = $row['description'] ?? '';
|
||
$sortOrder = (int)($row['sort_order'] ?? 999);
|
||
$isActive = (int)($row['is_active'] ?? 1);
|
||
$cnt = (int)($row['employees_count'] ?? 0);
|
||
$statusClass = $isActive === 1 ? 'active' : 'inactive';
|
||
$statusLabel = $isActive === 1 ? 'Attivo' : 'Inattivo';
|
||
$createdAt = !empty($row['created_at'])
|
||
? date('d/m/Y H:i', strtotime($row['created_at']))
|
||
: '-';
|
||
?>
|
||
<tr>
|
||
<td><?= $id ?></td>
|
||
<td class="fw-semibold text-start"><?= htmlspecialchars($name) ?></td>
|
||
<td class="description-cell" title="<?= htmlspecialchars($description, ENT_QUOTES) ?>">
|
||
<?= $description !== '' ? htmlspecialchars($description) : '-' ?>
|
||
</td>
|
||
<td><?= $sortOrder ?></td>
|
||
<td>
|
||
<span class="badge-status <?= $statusClass ?>"><?= $statusLabel ?></span>
|
||
</td>
|
||
<td><?= $cnt ?></td>
|
||
<td><?= $createdAt ?></td>
|
||
<td>
|
||
<button class="btn btn-sm btn-outline-secondary edit-job-role"
|
||
data-id="<?= $id ?>"
|
||
data-name="<?= htmlspecialchars($name, ENT_QUOTES) ?>"
|
||
data-description="<?= htmlspecialchars($description, ENT_QUOTES) ?>"
|
||
data-sort_order="<?= $sortOrder ?>"
|
||
data-is_active="<?= $isActive ?>">
|
||
✏️ Modifica
|
||
</button>
|
||
<button class="btn btn-sm btn-outline-danger delete-job-role"
|
||
data-id="<?= $id ?>"
|
||
data-name="<?= htmlspecialchars($name, ENT_QUOTES) ?>"
|
||
data-count="<?= $cnt ?>">
|
||
🗑️ Cancella
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
<?php endforeach; ?>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- MOBILE <768px: CARDS -->
|
||
<div class="d-block d-md-none">
|
||
<?php if (empty($jobRoles)): ?>
|
||
<div class="jr-empty">Nessuna mansione presente</div>
|
||
<?php endif; ?>
|
||
<?php foreach ($jobRoles as $row): ?>
|
||
<?php
|
||
$id = (int)$row['id'];
|
||
$name = $row['name'] ?? '';
|
||
$description = $row['description'] ?? '';
|
||
$sortOrder = (int)($row['sort_order'] ?? 999);
|
||
$isActive = (int)($row['is_active'] ?? 1);
|
||
$cnt = (int)($row['employees_count'] ?? 0);
|
||
$statusClass = $isActive === 1 ? 'active' : 'inactive';
|
||
$statusLabel = $isActive === 1 ? 'Attivo' : 'Inattivo';
|
||
?>
|
||
<div class="jr-card">
|
||
<h6 class="jr-card-title"><?= htmlspecialchars($name) ?></h6>
|
||
<?php if ($description !== ''): ?>
|
||
<p class="jr-card-desc"><?= htmlspecialchars($description) ?></p>
|
||
<?php endif; ?>
|
||
<div class="jr-card-meta">
|
||
<span><span class="badge-status <?= $statusClass ?>"><?= $statusLabel ?></span></span>
|
||
<span><b>Dipendenti:</b> <?= $cnt ?></span>
|
||
<span><b>Ordine:</b> <?= $sortOrder ?></span>
|
||
</div>
|
||
<div class="jr-card-actions">
|
||
<button class="btn btn-sm btn-outline-secondary edit-job-role"
|
||
data-id="<?= $id ?>"
|
||
data-name="<?= htmlspecialchars($name, ENT_QUOTES) ?>"
|
||
data-description="<?= htmlspecialchars($description, ENT_QUOTES) ?>"
|
||
data-sort_order="<?= $sortOrder ?>"
|
||
data-is_active="<?= $isActive ?>">
|
||
✏️ Modifica
|
||
</button>
|
||
<button class="btn btn-sm btn-outline-danger delete-job-role"
|
||
data-id="<?= $id ?>"
|
||
data-name="<?= htmlspecialchars($name, ENT_QUOTES) ?>"
|
||
data-count="<?= $cnt ?>">
|
||
🗑️ Cancella
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php include('include/footer.php'); ?>
|
||
</div>
|
||
|
||
<!-- ADD MODAL -->
|
||
<div class="modal fade" id="addJobRoleModal" tabindex="-1">
|
||
<div class="modal-dialog modal-dialog-centered modal-fullscreen-sm-down">
|
||
<div class="modal-content">
|
||
<div class="modal-header" style="background-color:#cfe3ff;">
|
||
<h5 class="modal-title">Aggiungi Mansione</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="addJobRoleForm">
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Nome</label>
|
||
<input type="text" class="form-control" id="addName" name="name" placeholder="es. Saldatore" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Descrizione</label>
|
||
<textarea class="form-control" id="addDescription" name="description" rows="3" placeholder="Opzionale"></textarea>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-12 col-md-6 mb-3">
|
||
<label class="form-label fw-semibold">Ordine</label>
|
||
<input type="number" class="form-control" id="addSortOrder" name="sort_order" value="999" min="0">
|
||
</div>
|
||
<div class="col-12 col-md-6 mb-3">
|
||
<label class="form-label fw-semibold">Stato</label>
|
||
<select class="form-select" id="addIsActive" name="is_active">
|
||
<option value="1" selected>Attivo</option>
|
||
<option value="0">Inattivo</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="text-center">
|
||
<button type="submit" class="btn btn-add">💾 Salva</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- EDIT MODAL -->
|
||
<div class="modal fade" id="editJobRoleModal" tabindex="-1">
|
||
<div class="modal-dialog modal-dialog-centered modal-fullscreen-sm-down">
|
||
<div class="modal-content">
|
||
<div class="modal-header" style="background-color:#cfe3ff;">
|
||
<h5 class="modal-title">Modifica Mansione</h5>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="editJobRoleForm">
|
||
<input type="hidden" id="editJobRoleId">
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Nome</label>
|
||
<input type="text" class="form-control" id="editName" name="name" required>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="form-label fw-semibold">Descrizione</label>
|
||
<textarea class="form-control" id="editDescription" name="description" rows="3"></textarea>
|
||
</div>
|
||
<div class="row">
|
||
<div class="col-12 col-md-6 mb-3">
|
||
<label class="form-label fw-semibold">Ordine</label>
|
||
<input type="number" class="form-control" id="editSortOrder" name="sort_order" value="999" min="0">
|
||
</div>
|
||
<div class="col-12 col-md-6 mb-3">
|
||
<label class="form-label fw-semibold">Stato</label>
|
||
<select class="form-select" id="editIsActive" name="is_active">
|
||
<option value="1">Attivo</option>
|
||
<option value="0">Inattivo</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="text-center">
|
||
<button type="submit" class="btn btn-add">💾 Salva Modifiche</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<?php include('jsinclude.php'); ?>
|
||
|
||
<script>
|
||
$(document).ready(function() {
|
||
$('#tabellaJobRoles').DataTable({
|
||
order: [[3, 'asc'], [1, 'asc']],
|
||
pageLength: 25,
|
||
language: {
|
||
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json',
|
||
emptyTable: 'Nessuna mansione presente'
|
||
}
|
||
});
|
||
|
||
function ajaxPost(url, payload, successTitle, errorFallback) {
|
||
return fetch(url, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||
body: payload.toString()
|
||
})
|
||
.then(r => r.json())
|
||
.then(data => {
|
||
if (data.success) {
|
||
Swal.fire({ icon: "success", title: successTitle, confirmButtonColor: "#3085d6" })
|
||
.then(() => location.reload());
|
||
} else {
|
||
Swal.fire({ icon: "error", title: "Errore", text: data.message || errorFallback });
|
||
}
|
||
})
|
||
.catch(err => {
|
||
Swal.fire({ icon: "error", title: "Errore", text: "Errore di comunicazione." });
|
||
console.error(err);
|
||
});
|
||
}
|
||
|
||
$("#addJobRoleForm").on("submit", function(e) {
|
||
e.preventDefault();
|
||
const p = new URLSearchParams();
|
||
p.append('name', $("#addName").val().trim());
|
||
p.append('description', $("#addDescription").val().trim());
|
||
p.append('sort_order', $("#addSortOrder").val());
|
||
p.append('is_active', $("#addIsActive").val());
|
||
ajaxPost("ajax/job_roles/save.php", p, "Salvato!", "Impossibile salvare la mansione.");
|
||
});
|
||
|
||
$(document).on("click", ".edit-job-role", function() {
|
||
const b = $(this);
|
||
$("#editJobRoleId").val(b.data("id"));
|
||
$("#editName").val(b.data("name"));
|
||
$("#editDescription").val(b.data("description"));
|
||
$("#editSortOrder").val(b.data("sort_order"));
|
||
$("#editIsActive").val(String(b.data("is_active")));
|
||
$("#editJobRoleModal").modal("show");
|
||
});
|
||
|
||
$("#editJobRoleForm").on("submit", function(e) {
|
||
e.preventDefault();
|
||
const p = new URLSearchParams();
|
||
p.append('id', $("#editJobRoleId").val());
|
||
p.append('name', $("#editName").val().trim());
|
||
p.append('description', $("#editDescription").val().trim());
|
||
p.append('sort_order', $("#editSortOrder").val());
|
||
p.append('is_active', $("#editIsActive").val());
|
||
ajaxPost("ajax/job_roles/save.php", p, "Aggiornato!", "Impossibile aggiornare la mansione.");
|
||
});
|
||
|
||
$(document).on("click", ".delete-job-role", function() {
|
||
const id = $(this).data("id");
|
||
const name = $(this).data("name");
|
||
const cnt = parseInt($(this).data("count")) || 0;
|
||
|
||
if (cnt > 0) {
|
||
Swal.fire({
|
||
icon: "warning",
|
||
title: "Impossibile cancellare",
|
||
text: "La mansione \"" + name + "\" è assegnata a " + cnt + " dipendente/i. Rimuovi prima l'associazione."
|
||
});
|
||
return;
|
||
}
|
||
|
||
Swal.fire({
|
||
title: "Confermi la cancellazione?",
|
||
text: name ? ("Mansione: " + name) : "La mansione verrà cancellata.",
|
||
icon: "warning",
|
||
showCancelButton: true,
|
||
confirmButtonColor: "#d33",
|
||
cancelButtonColor: "#6c757d",
|
||
confirmButtonText: "Sì, cancella",
|
||
cancelButtonText: "Annulla"
|
||
}).then((result) => {
|
||
if (!result.isConfirmed) return;
|
||
const p = new URLSearchParams();
|
||
p.append('id', id);
|
||
ajaxPost("ajax/job_roles/delete.php", p, "Cancellato!", "Impossibile cancellare la mansione.");
|
||
});
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|