Files
zibo-dashboard/public/userarea/employees.php
T

2374 lines
106 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();
function normalizeNullableInt($value): ?int
{
return (isset($value) && $value !== '') ? (int)$value : null;
}
function normalizeIntArray($values): array
{
if (!is_array($values)) {
$values = $values !== null && $values !== '' ? [$values] : [];
}
return array_values(array_unique(array_filter(array_map('intval', $values))));
}
function validateJobSubRoles(PDO $pdo, array $jobSubRoleIds): array
{
$jobSubRoleIds = normalizeIntArray($jobSubRoleIds);
if (!$jobSubRoleIds) {
return [];
}
$placeholders = implode(',', array_fill(0, count($jobSubRoleIds), '?'));
$stmt = $pdo->prepare("
SELECT jsr.id
FROM job_sub_roles jsr
INNER JOIN job_roles jr ON jr.id = jsr.job_role_id
WHERE jsr.id IN ($placeholders)
AND jsr.is_active = 1
AND jr.is_active = 1
");
$stmt->execute($jobSubRoleIds);
$validIds = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN));
sort($validIds);
$requestedIds = $jobSubRoleIds;
sort($requestedIds);
if ($validIds !== $requestedIds) {
throw new Exception('Una o più sottomansioni selezionate non sono valide o non sono attive.');
}
return $jobSubRoleIds;
}
function getPrimaryJobRoleAndSubRole(PDO $pdo, array $jobSubRoleIds): array
{
$jobSubRoleIds = normalizeIntArray($jobSubRoleIds);
if (!$jobSubRoleIds) {
return [null, null];
}
$stmt = $pdo->prepare("
SELECT job_role_id
FROM job_sub_roles
WHERE id = ?
LIMIT 1
");
$stmt->execute([$jobSubRoleIds[0]]);
$jobRoleId = $stmt->fetchColumn();
return [$jobRoleId !== false ? (int)$jobRoleId : null, (int)$jobSubRoleIds[0]];
}
function saveEmployeeSubRoles(PDO $pdo, int $employeeId, array $jobSubRoleIds): void
{
$jobSubRoleIds = validateJobSubRoles($pdo, $jobSubRoleIds);
$stmtDelete = $pdo->prepare("DELETE FROM employee_job_sub_roles WHERE employee_id = ?");
$stmtDelete->execute([$employeeId]);
if (!$jobSubRoleIds) {
return;
}
$stmtInsert = $pdo->prepare("
INSERT INTO employee_job_sub_roles
(employee_id, job_sub_role_id, is_primary, created_at)
VALUES
(?, ?, ?, NOW())
");
foreach ($jobSubRoleIds as $index => $jobSubRoleId) {
$stmtInsert->execute([$employeeId, $jobSubRoleId, $index === 0 ? 1 : 0]);
}
}
function getEmployeeSubRoleIds(PDO $pdo, int $employeeId): array
{
$stmt = $pdo->prepare("
SELECT job_sub_role_id
FROM employee_job_sub_roles
WHERE employee_id = ?
ORDER BY is_primary DESC, id ASC
");
$stmt->execute([$employeeId]);
return array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN));
}
function jsonResponse(array $data): void
{
header('Content-Type: application/json; charset=utf-8');
echo json_encode($data);
exit;
}
function calculateExpiryDate(?int $validityMonths): ?string
{
if ($validityMonths === null || $validityMonths <= 0) {
return null;
}
return date('Y-m-d', strtotime('+' . $validityMonths . ' months'));
}
function getCurrentAuthUserId(): ?int
{
$candidateVariables = ['iduserlogin', 'idUserLogin', 'authUserId', 'userid', 'user_id'];
foreach ($candidateVariables as $variableName) {
if (isset($GLOBALS[$variableName]) && (int)$GLOBALS[$variableName] > 0) {
return (int)$GLOBALS[$variableName];
}
}
if (isset($_SESSION['auth_user_id']) && (int)$_SESSION['auth_user_id'] > 0) {
return (int)$_SESSION['auth_user_id'];
}
if (isset($_SESSION['user_id']) && (int)$_SESSION['user_id'] > 0) {
return (int)$_SESSION['user_id'];
}
return null;
}
function assignPpeToEmployee(
PDO $pdo,
int $employeeId,
array $ppeIds,
string $notes = '',
?string $assignedDate = null,
?string $deliveredBy = null,
?int $createdBy = null
): int {
$ppeIds = array_values(array_unique(array_filter(array_map('intval', $ppeIds))));
if (!$ppeIds) {
return 0;
}
$assignedDate = $assignedDate ?: date('Y-m-d');
$deliveredBy = trim((string)$deliveredBy) !== '' ? trim((string)$deliveredBy) : null;
$createdBy = $createdBy ?: getCurrentAuthUserId();
$stmtPpe = $pdo->prepare("SELECT id, validity_months FROM ppe_items WHERE id = ? AND is_active = 1 LIMIT 1");
$stmtExists = $pdo->prepare("
SELECT COUNT(*)
FROM employee_ppe_items
WHERE employee_id = ?
AND ppe_item_id = ?
AND status = 'assigned'
AND (expiry_date IS NULL OR expiry_date >= CURDATE())
");
$stmtInsert = $pdo->prepare("
INSERT INTO employee_ppe_items
(employee_id, ppe_item_id, assigned_date, expiry_date, delivered_by, quantity, status, notes, created_by, created_at, updated_at)
VALUES
(:employee_id, :ppe_item_id, :assigned_date, :expiry_date, :delivered_by, 1, 'assigned', :notes, :created_by, NOW(), NOW())
");
$inserted = 0;
foreach ($ppeIds as $ppeId) {
if ($ppeId <= 0) {
continue;
}
$stmtPpe->execute([$ppeId]);
$ppe = $stmtPpe->fetch(PDO::FETCH_ASSOC);
if (!$ppe) {
continue;
}
$stmtExists->execute([$employeeId, $ppeId]);
if ((int)$stmtExists->fetchColumn() > 0) {
continue;
}
$expiryDate = calculateExpiryDate(isset($ppe['validity_months']) ? (int)$ppe['validity_months'] : null);
$stmtInsert->execute([
'employee_id' => $employeeId,
'ppe_item_id' => $ppeId,
'assigned_date' => $assignedDate,
'expiry_date' => $expiryDate,
'delivered_by' => $deliveredBy,
'notes' => $notes !== '' ? $notes : null,
'created_by' => $createdBy,
]);
$inserted++;
}
return $inserted;
}
/* ==========================================
AJAX HANDLERS
========================================== */
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['ajax']) && $_POST['ajax'] == '1') {
$action = $_POST['action'] ?? '';
try {
if ($action === 'add') {
$employee_code = trim($_POST['employee_code'] ?? '');
$first_name = trim($_POST['first_name'] ?? '');
$last_name = trim($_POST['last_name'] ?? '');
$address = trim($_POST['address'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$email = trim($_POST['email'] ?? '');
$department_id = normalizeNullableInt($_POST['department_id'] ?? '');
$job_sub_role_ids = validateJobSubRoles($pdo, $_POST['job_sub_role_ids'] ?? []);
[$job_role_id, $job_sub_role_id] = getPrimaryJobRoleAndSubRole($pdo, $job_sub_role_ids);
$hire_date = trim($_POST['hire_date'] ?? '');
$status = trim($_POST['status'] ?? 'active');
$auth_user_id = normalizeNullableInt($_POST['auth_user_id'] ?? '');
$role_id = normalizeNullableInt($_POST['role_id'] ?? '');
if ($first_name === '' || $last_name === '') {
jsonResponse(['success' => false, 'message' => 'First name and Last name are required.']);
}
if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
jsonResponse(['success' => false, 'message' => 'Email non valida.']);
}
if (!in_array($status, ['active', 'inactive', 'suspended'], true)) {
$status = 'active';
}
$sql = "INSERT INTO employees
(auth_user_id, employee_code, first_name, last_name, address, phone, email,
department_id, job_role_id, job_sub_role_id, hire_date, status, created_at, updated_at)
VALUES
(:auth_user_id, :employee_code, :first_name, :last_name, :address, :phone, :email,
:department_id, :job_role_id, :job_sub_role_id, :hire_date, :status, NOW(), NOW())";
$stmt = $pdo->prepare($sql);
$stmt->execute([
'auth_user_id' => $auth_user_id,
'employee_code' => $employee_code !== '' ? $employee_code : null,
'first_name' => $first_name,
'last_name' => $last_name,
'address' => $address !== '' ? $address : null,
'phone' => $phone !== '' ? $phone : null,
'email' => $email !== '' ? $email : null,
'department_id' => $department_id,
'job_role_id' => $job_role_id,
'job_sub_role_id' => $job_sub_role_id,
'hire_date' => $hire_date !== '' ? $hire_date : null,
'status' => $status,
]);
$employeeId = (int)$pdo->lastInsertId();
saveEmployeeSubRoles($pdo, $employeeId, $job_sub_role_ids);
if ($auth_user_id !== null && $role_id !== null) {
$checkRole = $pdo->prepare("SELECT COUNT(*) FROM auth_roles WHERE id = ?");
$checkRole->execute([$role_id]);
if ((int)$checkRole->fetchColumn() > 0) {
$stmtRole = $pdo->prepare("UPDATE auth_users SET role_id = :role_id, updated_at = NOW() WHERE id = :auth_user_id");
$stmtRole->execute([
'role_id' => $role_id,
'auth_user_id' => $auth_user_id,
]);
}
}
jsonResponse(['success' => true]);
}
if ($action === 'edit') {
$id = (int)($_POST['id'] ?? 0);
$employee_code = trim($_POST['employee_code'] ?? '');
$first_name = trim($_POST['first_name'] ?? '');
$last_name = trim($_POST['last_name'] ?? '');
$address = trim($_POST['address'] ?? '');
$phone = trim($_POST['phone'] ?? '');
$email = trim($_POST['email'] ?? '');
$department_id = normalizeNullableInt($_POST['department_id'] ?? '');
$job_sub_role_ids = validateJobSubRoles($pdo, $_POST['job_sub_role_ids'] ?? []);
[$job_role_id, $job_sub_role_id] = getPrimaryJobRoleAndSubRole($pdo, $job_sub_role_ids);
$hire_date = trim($_POST['hire_date'] ?? '');
$status = trim($_POST['status'] ?? 'active');
$auth_user_id = normalizeNullableInt($_POST['auth_user_id'] ?? '');
$role_id = normalizeNullableInt($_POST['role_id'] ?? '');
if ($id <= 0) {
jsonResponse(['success' => false, 'message' => 'Invalid employee ID.']);
}
if ($first_name === '' || $last_name === '') {
jsonResponse(['success' => false, 'message' => 'First name and Last name are required.']);
}
if ($email !== '' && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
jsonResponse(['success' => false, 'message' => 'Email non valida.']);
}
if (!in_array($status, ['active', 'inactive', 'suspended'], true)) {
$status = 'active';
}
$sql = "UPDATE employees
SET auth_user_id = :auth_user_id,
employee_code = :employee_code,
first_name = :first_name,
last_name = :last_name,
address = :address,
phone = :phone,
email = :email,
department_id = :department_id,
job_role_id = :job_role_id,
job_sub_role_id = :job_sub_role_id,
hire_date = :hire_date,
status = :status,
updated_at = NOW()
WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute([
'auth_user_id' => $auth_user_id,
'employee_code' => $employee_code !== '' ? $employee_code : null,
'first_name' => $first_name,
'last_name' => $last_name,
'address' => $address !== '' ? $address : null,
'phone' => $phone !== '' ? $phone : null,
'email' => $email !== '' ? $email : null,
'department_id' => $department_id,
'job_role_id' => $job_role_id,
'job_sub_role_id' => $job_sub_role_id,
'hire_date' => $hire_date !== '' ? $hire_date : null,
'status' => $status,
'id' => $id,
]);
saveEmployeeSubRoles($pdo, $id, $job_sub_role_ids);
if ($auth_user_id !== null && $role_id !== null) {
$checkRole = $pdo->prepare("SELECT COUNT(*) FROM auth_roles WHERE id = ?");
$checkRole->execute([$role_id]);
if ((int)$checkRole->fetchColumn() > 0) {
$stmtRole = $pdo->prepare("UPDATE auth_users SET role_id = :role_id, updated_at = NOW() WHERE id = :auth_user_id");
$stmtRole->execute([
'role_id' => $role_id,
'auth_user_id' => $auth_user_id,
]);
}
}
jsonResponse(['success' => true]);
}
if ($action === 'delete') {
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
jsonResponse(['success' => false, 'message' => 'Invalid employee ID.']);
}
$stmt = $pdo->prepare("DELETE FROM employees WHERE id = :id");
$stmt->execute(['id' => $id]);
jsonResponse(['success' => true]);
}
if ($action === 'get_employee_skills') {
$id = (int)$_POST['id'];
if ($id <= 0) {
jsonResponse(['success' => false, 'message' => 'Invalid ID']);
}
$stmt = $pdo->prepare("SELECT skill_id, level FROM employee_skills WHERE employee_id = ?");
$stmt->execute([$id]);
$skills = [];
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$skills[$row['skill_id']] = $row['level'];
}
jsonResponse(['success' => true, 'skills' => $skills]);
}
if ($action === 'save_employee_skills') {
$id = (int)$_POST['id'];
$skills_json = $_POST['skills'] ?? '';
$skills = json_decode($skills_json, true);
if ($id <= 0 || !is_array($skills)) {
jsonResponse(['success' => false, 'message' => 'Invalid data']);
}
$pdo->beginTransaction();
$stmtDelete = $pdo->prepare("DELETE FROM employee_skills WHERE employee_id = ?");
$stmtDelete->execute([$id]);
$stmtInsert = $pdo->prepare("INSERT INTO employee_skills (employee_id, skill_id, level) VALUES (?, ?, ?)");
foreach ($skills as $skill_id => $level) {
$stmtInsert->execute([$id, (int)$skill_id, $level]);
}
$pdo->commit();
jsonResponse(['success' => true]);
}
if ($action === 'get_employee_ppe') {
$id = (int)($_POST['id'] ?? 0);
if ($id <= 0) {
jsonResponse(['success' => false, 'message' => 'Invalid employee ID.']);
}
$stmtEmp = $pdo->prepare("
SELECT
e.id,
e.first_name,
e.last_name,
COALESCE(ejm.job_sub_role_ids, IF(e.job_sub_role_id IS NULL, '', CAST(e.job_sub_role_id AS CHAR))) AS job_sub_role_ids,
COALESCE(ejm.job_sub_role_names, jsr.name) AS job_sub_role_name
FROM employees e
LEFT JOIN (
SELECT
ejs.employee_id,
GROUP_CONCAT(DISTINCT jsr2.id ORDER BY jr2.name, jsr2.name SEPARATOR ',') AS job_sub_role_ids,
GROUP_CONCAT(DISTINCT jsr2.name ORDER BY jr2.name, jsr2.name SEPARATOR ', ') AS job_sub_role_names
FROM employee_job_sub_roles ejs
INNER JOIN job_sub_roles jsr2 ON jsr2.id = ejs.job_sub_role_id
INNER JOIN job_roles jr2 ON jr2.id = jsr2.job_role_id
GROUP BY ejs.employee_id
) ejm ON ejm.employee_id = e.id
LEFT JOIN job_sub_roles jsr ON jsr.id = e.job_sub_role_id
WHERE e.id = ?
LIMIT 1
");
$stmtEmp->execute([$id]);
$employee = $stmtEmp->fetch(PDO::FETCH_ASSOC);
if (!$employee) {
jsonResponse(['success' => false, 'message' => 'Dipendente non trovato.']);
}
$jobSubRoleIds = [];
if (!empty($employee['job_sub_role_ids'])) {
$jobSubRoleIds = array_values(array_unique(array_filter(array_map('intval', explode(',', $employee['job_sub_role_ids'])))));
}
$requiredPpe = [];
if ($jobSubRoleIds) {
$placeholders = implode(',', array_fill(0, count($jobSubRoleIds), '?'));
$stmtReq = $pdo->prepare("
SELECT
p.id AS ppe_item_id,
p.name AS ppe_name,
p.category,
p.photo,
p.standard_reference,
p.validity_months,
GROUP_CONCAT(DISTINCT jsr.name ORDER BY jsr.name SEPARATOR ', ') AS required_by_subroles
FROM job_sub_role_ppe_items jsp
INNER JOIN job_sub_roles jsr ON jsr.id = jsp.job_sub_role_id
INNER JOIN ppe_items p ON p.id = jsp.ppe_item_id
WHERE jsp.job_sub_role_id IN ($placeholders)
AND jsp.is_active = 1
AND p.is_active = 1
GROUP BY p.id, p.name, p.category, p.photo, p.standard_reference, p.validity_months
ORDER BY p.category ASC, p.name ASC
");
$stmtReq->execute($jobSubRoleIds);
$requiredPpe = $stmtReq->fetchAll(PDO::FETCH_ASSOC);
}
$stmtAssigned = $pdo->prepare("
SELECT
epi.id AS assignment_id,
epi.ppe_item_id,
epi.assigned_date,
epi.expiry_date,
epi.delivered_by,
epi.quantity,
epi.status,
epi.notes,
p.name AS ppe_name,
p.category,
p.photo,
p.standard_reference
FROM employee_ppe_items epi
INNER JOIN ppe_items p ON p.id = epi.ppe_item_id
WHERE epi.employee_id = ?
ORDER BY FIELD(epi.status, 'assigned', 'expired', 'damaged', 'lost', 'returned'), p.name ASC
");
$stmtAssigned->execute([$id]);
$assignedPpe = $stmtAssigned->fetchAll(PDO::FETCH_ASSOC);
$assignedActiveMap = [];
foreach ($assignedPpe as $a) {
$isActiveAssignment = ($a['status'] === 'assigned') && (empty($a['expiry_date']) || $a['expiry_date'] >= date('Y-m-d'));
if ($isActiveAssignment) {
$assignedActiveMap[(int)$a['ppe_item_id']] = $a;
}
}
$requiredStatus = [];
foreach ($requiredPpe as $r) {
$ppeId = (int)$r['ppe_item_id'];
$assignedRow = $assignedActiveMap[$ppeId] ?? null;
$requiredStatus[] = [
'ppe_item_id' => $ppeId,
'ppe_name' => $r['ppe_name'],
'category' => $r['category'],
'standard_reference' => $r['standard_reference'],
'validity_months' => $r['validity_months'],
'required_by_subroles' => $r['required_by_subroles'] ?? '',
'is_assigned' => $assignedRow !== null,
'assignment' => $assignedRow,
];
}
jsonResponse([
'success' => true,
'employee' => $employee,
'required_ppe' => $requiredStatus,
'assigned_ppe' => $assignedPpe,
]);
}
if ($action === 'assign_missing_employee_ppe') {
$id = (int)($_POST['id'] ?? 0);
$selectedPpeIds = $_POST['ppe_ids'] ?? [];
if ($id <= 0) {
jsonResponse(['success' => false, 'message' => 'Invalid employee ID.']);
}
$stmtEmp = $pdo->prepare("SELECT id FROM employees WHERE id = ? LIMIT 1");
$stmtEmp->execute([$id]);
$employee = $stmtEmp->fetch(PDO::FETCH_ASSOC);
if (!$employee) {
jsonResponse(['success' => false, 'message' => 'Dipendente non trovato.']);
}
$jobSubRoleIds = getEmployeeSubRoleIds($pdo, $id);
if (!$jobSubRoleIds) {
$stmtLegacy = $pdo->prepare("SELECT job_sub_role_id FROM employees WHERE id = ? AND job_sub_role_id IS NOT NULL LIMIT 1");
$stmtLegacy->execute([$id]);
$legacySubRoleId = $stmtLegacy->fetchColumn();
if ($legacySubRoleId) {
$jobSubRoleIds = [(int)$legacySubRoleId];
}
}
if (!$jobSubRoleIds) {
jsonResponse(['success' => false, 'message' => 'Il dipendente non ha sottomansioni collegate.']);
}
$placeholders = implode(',', array_fill(0, count($jobSubRoleIds), '?'));
$stmtReq = $pdo->prepare("
SELECT DISTINCT p.id
FROM job_sub_role_ppe_items jsp
INNER JOIN ppe_items p ON p.id = jsp.ppe_item_id
WHERE jsp.job_sub_role_id IN ($placeholders)
AND jsp.is_active = 1
AND p.is_active = 1
");
$stmtReq->execute($jobSubRoleIds);
$requiredIds = array_map('intval', $stmtReq->fetchAll(PDO::FETCH_COLUMN));
if (!$selectedPpeIds) {
$selectedPpeIds = $requiredIds;
} else {
$selectedPpeIds = array_values(array_intersect(array_map('intval', $selectedPpeIds), $requiredIds));
}
$assignedDate = trim($_POST['assigned_date'] ?? '') ?: date('Y-m-d');
$deliveredBy = trim($_POST['delivered_by'] ?? '');
$notes = trim($_POST['notes'] ?? '');
$inserted = assignPpeToEmployee(
$pdo,
$id,
$selectedPpeIds,
$notes !== '' ? $notes : 'Assegnato automaticamente dalle sottomansioni.',
$assignedDate,
$deliveredBy
);
jsonResponse(['success' => true, 'message' => 'DPI obbligatori assegnati: ' . $inserted]);
}
if ($action === 'assign_employee_ppe') {
$id = (int)($_POST['id'] ?? 0);
$ppeIds = $_POST['ppe_ids'] ?? [];
$assignedDate = trim($_POST['assigned_date'] ?? '') ?: date('Y-m-d');
$deliveredBy = trim($_POST['delivered_by'] ?? '');
$notes = trim($_POST['notes'] ?? '');
if ($id <= 0) {
jsonResponse(['success' => false, 'message' => 'Invalid employee ID.']);
}
$stmtEmp = $pdo->prepare("SELECT id FROM employees WHERE id = ? LIMIT 1");
$stmtEmp->execute([$id]);
if (!$stmtEmp->fetchColumn()) {
jsonResponse(['success' => false, 'message' => 'Dipendente non trovato.']);
}
$inserted = assignPpeToEmployee(
$pdo,
$id,
$ppeIds,
$notes !== '' ? $notes : 'Assegnato manualmente.',
$assignedDate,
$deliveredBy
);
jsonResponse(['success' => true, 'message' => 'DPI assegnati: ' . $inserted]);
}
if ($action === 'remove_employee_ppe') {
$assignmentId = (int)($_POST['assignment_id'] ?? 0);
$employeeId = (int)($_POST['employee_id'] ?? 0);
if ($assignmentId <= 0 || $employeeId <= 0) {
jsonResponse(['success' => false, 'message' => 'Dati DPI non validi.']);
}
$stmt = $pdo->prepare("
UPDATE employee_ppe_items
SET status = 'returned',
updated_at = NOW()
WHERE id = ?
AND employee_id = ?
AND status = 'assigned'
");
$stmt->execute([$assignmentId, $employeeId]);
jsonResponse(['success' => true, 'message' => 'DPI rimosso dal dipendente.']);
}
if ($action === 'assign_bulk_employee_ppe') {
$employeeIds = $_POST['employee_ids'] ?? [];
$ppeIds = $_POST['ppe_ids'] ?? [];
$assignedDate = trim($_POST['assigned_date'] ?? '') ?: date('Y-m-d');
$deliveredBy = trim($_POST['delivered_by'] ?? '');
$notes = trim($_POST['notes'] ?? '');
$employeeIds = array_values(array_unique(array_filter(array_map('intval', $employeeIds))));
if (!$employeeIds || !$ppeIds) {
jsonResponse(['success' => false, 'message' => 'Seleziona almeno un dipendente e almeno un DPI.']);
}
$totalInserted = 0;
foreach ($employeeIds as $employeeId) {
$totalInserted += assignPpeToEmployee(
$pdo,
$employeeId,
$ppeIds,
$notes !== '' ? $notes : 'Assegnazione DPI multipla.',
$assignedDate,
$deliveredBy
);
}
jsonResponse(['success' => true, 'message' => 'Assegnazioni DPI create: ' . $totalInserted]);
}
jsonResponse(['success' => false, 'message' => 'Unknown action.']);
} catch (Exception $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
jsonResponse(['success' => false, 'message' => $e->getMessage()]);
}
}
/* ==========================================
PAGE DATA
========================================== */
$sql = "
SELECT e.*,
d.name AS department_name,
d.color AS department_color,
COALESCE(ejm.job_role_names, jr.name) AS job_role_name,
COALESCE(ejm.job_sub_role_names, jsr.name) AS job_sub_role_name,
COALESCE(ejm.job_sub_role_ids, IF(e.job_sub_role_id IS NULL, '', CAST(e.job_sub_role_id AS CHAR))) AS job_sub_role_ids,
au.email AS user_email,
au.role_id AS user_role_id,
ar.display_name AS role_display_name,
ar.name AS role_name,
CONCAT(COALESCE(au.first_name, ''), ' ', COALESCE(au.last_name, '')) AS user_fullname
FROM employees e
LEFT JOIN departments d ON e.department_id = d.id
LEFT JOIN (
SELECT
ejs.employee_id,
GROUP_CONCAT(DISTINCT jr2.name ORDER BY jr2.name SEPARATOR ', ') AS job_role_names,
GROUP_CONCAT(DISTINCT jsr2.name ORDER BY jr2.name, jsr2.name SEPARATOR ', ') AS job_sub_role_names,
GROUP_CONCAT(DISTINCT jsr2.id ORDER BY jr2.name, jsr2.name SEPARATOR ',') AS job_sub_role_ids
FROM employee_job_sub_roles ejs
INNER JOIN job_sub_roles jsr2 ON jsr2.id = ejs.job_sub_role_id
INNER JOIN job_roles jr2 ON jr2.id = jsr2.job_role_id
GROUP BY ejs.employee_id
) ejm ON ejm.employee_id = e.id
LEFT JOIN job_roles jr ON jr.id = e.job_role_id
LEFT JOIN job_sub_roles jsr ON jsr.id = e.job_sub_role_id
LEFT JOIN auth_users au ON e.auth_user_id = au.id
LEFT JOIN auth_roles ar ON ar.id = au.role_id
ORDER BY e.id DESC
";
$stmtEmployees = $pdo->query($sql);
$employees = $stmtEmployees->fetchAll(PDO::FETCH_ASSOC);
$employeesForSelect = $pdo->query("
SELECT id, first_name, last_name, employee_code, department_id
FROM employees
ORDER BY last_name, first_name
")->fetchAll(PDO::FETCH_ASSOC);
$jobRoles = $pdo->query("
SELECT id, name FROM job_roles WHERE is_active = 1 ORDER BY sort_order, name
")->fetchAll(PDO::FETCH_ASSOC);
$jobSubRoles = $pdo->query("
SELECT jsr.id, jsr.job_role_id, jsr.name, jr.name AS job_role_name
FROM job_sub_roles jsr
INNER JOIN job_roles jr ON jr.id = jsr.job_role_id
WHERE jsr.is_active = 1
AND jr.is_active = 1
ORDER BY jr.sort_order ASC, jr.name ASC, jsr.sort_order ASC, jsr.name ASC
")->fetchAll(PDO::FETCH_ASSOC);
$jobSubRolesByRole = [];
foreach ($jobSubRoles as $subRole) {
$jobSubRolesByRole[(int)$subRole['job_role_id']][] = [
'id' => (int)$subRole['id'],
'name' => $subRole['name'],
'job_role_name' => $subRole['job_role_name'],
];
}
$ppeItems = $pdo->query("
SELECT id, name, category, standard_reference
FROM ppe_items
WHERE is_active = 1
ORDER BY category ASC, name ASC
")->fetchAll(PDO::FETCH_ASSOC);
$sqlUsers = "
SELECT id,
role_id,
CONCAT(
COALESCE(first_name, ''),
' ',
COALESCE(last_name, ''),
' (',
email,
')'
) AS label
FROM auth_users
ORDER BY first_name, last_name, email
";
$users = $pdo->query($sqlUsers)->fetchAll(PDO::FETCH_ASSOC);
$sqlRoles = "
SELECT id, name, display_name
FROM auth_roles
ORDER BY display_name, name
";
$authRoles = $pdo->query($sqlRoles)->fetchAll(PDO::FETCH_ASSOC);
$sqlDepartments = "
SELECT id, name, code, color
FROM departments
WHERE is_active = 1
ORDER BY sort_order ASC, name ASC
";
$departments = $pdo->query($sqlDepartments)->fetchAll(PDO::FETCH_ASSOC);
$sqlSkills = "
SELECT s.id, s.name, pl.name as line_name, pl.line_number
FROM skills s
LEFT JOIN production_lines pl ON s.production_line_id = pl.id
ORDER BY IFNULL(pl.line_number, 999), s.name
";
$allSkills = $pdo->query($sqlSkills)->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 Dipendenti - <?= 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>
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<link href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.full.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;
font-size: 1rem;
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;
transition: all 0.2s ease-in-out;
}
.btn-add:hover {
background-color: #0b5ed7;
transform: scale(1.02);
}
.table thead {
background-color: #cfe3ff;
color: #1f2d3d;
}
.modal-content {
border-radius: 16px;
}
#tabellaDipendenti 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;
}
.badge-status.suspended {
background-color: #fee2e2;
color: #b91c1c;
}
.btn-matrix {
background-color: #198754;
color: #fff;
border-radius: 8px;
padding: 10px 20px;
font-weight: 500;
transition: all 0.2s ease-in-out;
margin-right: 10px;
}
.btn-matrix:hover {
background-color: #157347;
transform: scale(1.02);
}
.department-badge {
display: inline-block;
color: #fff;
padding: 0.25rem 0.6rem;
border-radius: 999px;
font-size: 0.8rem;
font-weight: 600;
}
.job-subrole-text {
color: #64748b;
font-size: 0.86rem;
}
.ppe-required-card {
border: 1px solid #e2e8f0;
border-radius: 12px;
padding: 12px;
margin-bottom: 10px;
background: #ffffff;
}
.ppe-required-card.missing {
border-color: #fecaca;
background: #fff7f7;
}
.ppe-required-card.assigned {
border-color: #bbf7d0;
background: #f0fdf4;
}
.ppe-status-pill {
border-radius: 999px;
padding: 4px 10px;
font-size: 0.78rem;
font-weight: 700;
}
.ppe-status-pill.missing {
background: #fee2e2;
color: #991b1b;
}
.ppe-status-pill.assigned {
background: #dcfce7;
color: #166534;
}
</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">
<h5 class="mb-0">Gestione Dipendenti</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">
<h6 class="fw-semibold mb-0">Elenco Completo</h6>
<div>
<button class="btn btn-matrix" onclick="location.href='skill_matrix.php'">
📊 Matrice Skills
</button>
<button type="button" class="btn btn-matrix" id="btnBulkPpe" data-bs-toggle="modal" data-bs-target="#bulkPpeModal">
🦺 Assegna DPI
</button>
<button class="btn btn-add" data-bs-toggle="modal" data-bs-target="#addEmployeeModal">
Aggiungi Dipendente
</button>
</div>
</div>
<div class="table-responsive">
<table id="tabellaDipendenti" class="table table-striped align-middle text-center" style="width:100%;">
<thead>
<tr>
<th>Codice</th>
<th>Nome</th>
<th>Contatti</th>
<th>Reparto</th>
<th>Mansioni / Sottomansioni</th>
<th>Data Assunzione</th>
<th>Stato</th>
<th>Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($employees as $row): ?>
<?php
$fullName = trim(($row['first_name'] ?? '') . ' ' . ($row['last_name'] ?? ''));
$hireDate = $row['hire_date'] && $row['hire_date'] !== '0000-00-00'
? date('d/m/Y', strtotime($row['hire_date']))
: '-';
$status = $row['status'] ?? 'active';
$statusClass = $status;
$statusLabel = ucfirst($status);
$linkedUser = '-';
if (!empty($row['user_email'])) {
$labelName = trim($row['user_fullname']);
if ($labelName !== '') {
$linkedUser = htmlspecialchars($labelName . ' (' . $row['user_email'] . ')');
} else {
$linkedUser = htmlspecialchars($row['user_email']);
}
}
?>
<tr>
<td><?= htmlspecialchars($row['employee_code'] ?? '') ?></td>
<td>
<a href="employee-profile.php?id=<?= (int)$row['id'] ?>" class="fw-semibold text-decoration-none">
<?= htmlspecialchars($fullName) ?>
</a>
</td>
<td class="text-start">
<?php if (!empty($row['email'])): ?>
<a href="mailto:<?= htmlspecialchars($row['email'], ENT_QUOTES) ?>" class="text-decoration-none small">
✉️ <?= htmlspecialchars($row['email']) ?>
</a><br>
<?php endif; ?>
<?php if (!empty($row['phone'])): ?>
<a href="tel:<?= htmlspecialchars($row['phone'], ENT_QUOTES) ?>" class="text-decoration-none small">
📞 <?= htmlspecialchars($row['phone']) ?>
</a>
<?php endif; ?>
<?php if (empty($row['email']) && empty($row['phone'])): ?>-<?php endif; ?>
</td>
<td>
<?php if (!empty($row['department_name'])): ?>
<span class="department-badge" style="background-color: <?= htmlspecialchars($row['department_color'] ?? '#6c757d', ENT_QUOTES) ?>;">
<?= htmlspecialchars($row['department_name']) ?>
</span>
<?php else: ?>
-
<?php endif; ?>
</td>
<td>
<div><?= !empty($row['job_role_name']) ? htmlspecialchars($row['job_role_name']) : '-' ?></div>
<div class="job-subrole-text">
<?= !empty($row['job_sub_role_name']) ? htmlspecialchars($row['job_sub_role_name']) : 'Nessuna sottomansione' ?>
</div>
</td>
<td><?= $hireDate ?></td>
<td>
<span class="badge-status <?= htmlspecialchars($statusClass, ENT_QUOTES) ?>">
<?= htmlspecialchars($statusLabel) ?>
</span>
</td>
<td>
<button
class="btn btn-sm btn-outline-secondary edit-employee mb-1"
data-id="<?= (int)$row['id'] ?>"
data-code="<?= htmlspecialchars($row['employee_code'] ?? '', ENT_QUOTES) ?>"
data-first_name="<?= htmlspecialchars($row['first_name'] ?? '', ENT_QUOTES) ?>"
data-last_name="<?= htmlspecialchars($row['last_name'] ?? '', ENT_QUOTES) ?>"
data-department_id="<?= $row['department_id'] !== null ? (int)$row['department_id'] : '' ?>"
data-job_sub_role_ids="<?= htmlspecialchars($row['job_sub_role_ids'] ?? '', ENT_QUOTES) ?>"
data-address="<?= htmlspecialchars($row['address'] ?? '', ENT_QUOTES) ?>"
data-phone="<?= htmlspecialchars($row['phone'] ?? '', ENT_QUOTES) ?>"
data-email="<?= htmlspecialchars($row['email'] ?? '', ENT_QUOTES) ?>"
data-hire_date="<?= htmlspecialchars($row['hire_date'] ?? '', ENT_QUOTES) ?>"
data-status="<?= htmlspecialchars($status, ENT_QUOTES) ?>"
data-auth_user_id="<?= $row['auth_user_id'] !== null ? (int)$row['auth_user_id'] : '' ?>"
data-role_id="<?= $row['user_role_id'] !== null ? (int)$row['user_role_id'] : '' ?>">
✏️ Modifica
</button>
<button
class="btn btn-sm btn-outline-primary manage-employee-ppe mb-1"
data-id="<?= (int)$row['id'] ?>"
data-name="<?= htmlspecialchars($fullName, ENT_QUOTES) ?>">
🦺 DPI
</button>
<button
class="btn btn-sm btn-outline-info manage-skills mb-1"
data-id="<?= (int)$row['id'] ?>"
data-name="<?= htmlspecialchars($fullName, ENT_QUOTES) ?>">
🛠️ Skills
</button>
<button
class="btn btn-sm btn-outline-danger delete-employee mb-1"
data-id="<?= (int)$row['id'] ?>"
data-name="<?= htmlspecialchars($fullName, ENT_QUOTES) ?>">
🗑️ Cancella
</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<?php include('include/footer.php'); ?>
</div>
<!-- MODALE AGGIUNTA DIPENDENTE -->
<div class="modal fade" id="addEmployeeModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header" style="background-color:#cfe3ff;">
<h5 class="modal-title">Aggiungi Nuovo Dipendente</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="addEmployeeForm">
<div class="mb-3">
<label class="form-label fw-semibold">Codice Dipendente</label>
<input type="text" class="form-control" id="addEmployeeCode" name="employee_code" placeholder="Opzionale">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Nome</label>
<input type="text" class="form-control" id="addFirstName" name="first_name" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Cognome</label>
<input type="text" class="form-control" id="addLastName" name="last_name" required>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Indirizzo</label>
<input type="text" class="form-control" id="addAddress" name="address" placeholder="Via, città, CAP">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Telefono</label>
<input type="tel" class="form-control" id="addPhone" name="phone">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Email</label>
<input type="email" class="form-control" id="addEmail" name="email">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Reparto</label>
<select class="form-select" id="addDepartmentId" name="department_id" style="width:100%;">
<option value="">-- Nessuno --</option>
<?php foreach ($departments as $d): ?>
<option value="<?= (int)$d['id'] ?>">
<?= htmlspecialchars($d['name']) ?><?= !empty($d['code']) ? ' (' . htmlspecialchars($d['code']) . ')' : '' ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Data Assunzione</label>
<input type="date" class="form-control" id="addHireDate" name="hire_date">
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Sottomansioni</label>
<select class="form-select" id="addJobSubRoleIds" name="job_sub_role_ids[]" multiple style="width:100%;">
<?php
$currentGroup = null;
foreach ($jobSubRoles as $subRole):
if ($currentGroup !== $subRole['job_role_name']):
if ($currentGroup !== null): ?>
</optgroup>
<?php endif;
$currentGroup = $subRole['job_role_name']; ?>
<optgroup label="<?= htmlspecialchars($currentGroup, ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<option value="<?= (int)$subRole['id'] ?>">
<?= htmlspecialchars($subRole['name'], ENT_QUOTES, 'UTF-8') ?>
</option>
<?php endforeach;
if ($currentGroup !== null): ?>
</optgroup>
<?php endif; ?>
</select>
<small class="text-muted">Puoi selezionare più sottomansioni anche appartenenti a mansioni diverse.</small>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Stato</label>
<select class="form-select" id="addStatus" name="status">
<option value="active" selected>Attivo</option>
<option value="inactive">Cessato</option>
<option value="suspended">Sospeso</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Utente collegato (account login)</label>
<select class="form-select" id="addAuthUserId" name="auth_user_id" style="width:100%;">
<option value="">-- Nessuno --</option>
<?php foreach ($users as $u): ?>
<option value="<?= (int)$u['id'] ?>" data-role_id="<?= (int)$u['role_id'] ?>">
<?= htmlspecialchars($u['label']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3 d-none" id="addRoleWrapper">
<label class="form-label fw-semibold">Ruolo di accesso</label>
<select class="form-select" id="addRoleId" name="role_id" style="width:100%;">
<option value="">-- Seleziona ruolo --</option>
<?php foreach ($authRoles as $r): ?>
<option value="<?= (int)$r['id'] ?>">
<?= htmlspecialchars($r['display_name'] ?: $r['name']) ?>
</option>
<?php endforeach; ?>
</select>
<small class="text-muted">Visibile solo quando è collegato un utente di sistema.</small>
</div>
<div class="text-center">
<button type="submit" class="btn btn-add">💾 Save</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- MODALE EDIT DIPENDENTE -->
<div class="modal fade" id="editEmployeeModal" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header" style="background-color:#cfe3ff;">
<h5 class="modal-title">Modifica Dipendente</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="editEmployeeForm">
<input type="hidden" id="editEmployeeId">
<div class="mb-3">
<label class="form-label fw-semibold">Codice Dipendente</label>
<input type="text" class="form-control" id="editEmployeeCode" name="employee_code" placeholder="Opzionale">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Nome</label>
<input type="text" class="form-control" id="editFirstName" name="first_name" required>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Cognome</label>
<input type="text" class="form-control" id="editLastName" name="last_name" required>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Indirizzo</label>
<input type="text" class="form-control" id="editAddress" name="address">
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Telefono</label>
<input type="tel" class="form-control" id="editPhone" name="phone">
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Email</label>
<input type="email" class="form-control" id="editEmail" name="email">
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Reparto</label>
<select class="form-select" id="editDepartmentId" name="department_id" style="width:100%;">
<option value="">-- Nessuno --</option>
<?php foreach ($departments as $d): ?>
<option value="<?= (int)$d['id'] ?>">
<?= htmlspecialchars($d['name']) ?><?= !empty($d['code']) ? ' (' . htmlspecialchars($d['code']) . ')' : '' ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Data Assunzione</label>
<input type="date" class="form-control" id="editHireDate" name="hire_date">
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Sottomansioni</label>
<select class="form-select" id="editJobSubRoleIds" name="job_sub_role_ids[]" multiple style="width:100%;">
<?php
$currentGroup = null;
foreach ($jobSubRoles as $subRole):
if ($currentGroup !== $subRole['job_role_name']):
if ($currentGroup !== null): ?>
</optgroup>
<?php endif;
$currentGroup = $subRole['job_role_name']; ?>
<optgroup label="<?= htmlspecialchars($currentGroup, ENT_QUOTES, 'UTF-8') ?>">
<?php endif; ?>
<option value="<?= (int)$subRole['id'] ?>">
<?= htmlspecialchars($subRole['name'], ENT_QUOTES, 'UTF-8') ?>
</option>
<?php endforeach;
if ($currentGroup !== null): ?>
</optgroup>
<?php endif; ?>
</select>
<small class="text-muted">Puoi selezionare più sottomansioni anche appartenenti a mansioni diverse.</small>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label class="form-label fw-semibold">Stato</label>
<select class="form-select" id="editStatus" name="status">
<option value="active">Attivo</option>
<option value="inactive">Cessato</option>
<option value="suspended">Sospeso</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Utente collegato (account login)</label>
<select class="form-select" id="editAuthUserId" name="auth_user_id" style="width:100%;">
<option value="">-- Nessuno --</option>
<?php foreach ($users as $u): ?>
<option value="<?= (int)$u['id'] ?>" data-role_id="<?= (int)$u['role_id'] ?>">
<?= htmlspecialchars($u['label']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3 d-none" id="editRoleWrapper">
<label class="form-label fw-semibold">Ruolo di accesso</label>
<select class="form-select" id="editRoleId" name="role_id" style="width:100%;">
<option value="">-- Seleziona ruolo --</option>
<?php foreach ($authRoles as $r): ?>
<option value="<?= (int)$r['id'] ?>">
<?= htmlspecialchars($r['display_name'] ?: $r['name']) ?>
</option>
<?php endforeach; ?>
</select>
<small class="text-muted">Visibile solo quando è collegato un utente di sistema.</small>
</div>
<div class="text-center">
<button type="submit" class="btn btn-add">💾 Save Changes</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- MODALE GESTIONE SKILLS -->
<div class="modal fade" id="manageSkillsModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header" style="background-color:#cfe3ff;">
<h5 class="modal-title">Gestione Skills per <span id="skillsEmployeeName"></span></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<form id="manageSkillsForm">
<input type="hidden" id="skillsEmployeeId">
<div id="skillsContainer"></div>
<div class="text-center">
<button type="submit" class="btn btn-add">💾 Salva Skills</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- EMPLOYEE PPE MODAL -->
<div class="modal fade" id="employeePpeModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header" style="background-color:#cfe3ff;">
<div>
<h5 class="modal-title">🦺 DPI dipendente - <span id="employeePpeName"></span></h5>
<div class="text-muted small" id="employeePpeSubRoleBox"></div>
</div>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="employeePpeEmployeeId">
<div class="row g-3 mb-3">
<div class="col-12 col-md-4">
<label class="form-label fw-semibold">Data consegna</label>
<input type="date" id="employeePpeAssignedDate" class="form-control" value="<?= date('Y-m-d') ?>">
</div>
<div class="col-12 col-md-4">
<label class="form-label fw-semibold">Consegnato da</label>
<input type="text" id="employeePpeDeliveredBy" class="form-control" maxlength="255" placeholder="Nome o azienda">
</div>
<div class="col-12 col-md-4">
<label class="form-label fw-semibold">Note consegna</label>
<input type="text" id="manualPpeNotes" class="form-control" placeholder="Opzionale">
</div>
</div>
<div class="row g-4">
<div class="col-12 col-lg-7">
<h6 class="fw-bold">DPI obbligatori da sottomansione</h6>
<div class="text-muted small mb-2">Questi sono suggeriti automaticamente in base alla sottomansione del dipendente.</div>
<div id="employeeRequiredPpeBox"></div>
<button type="button" class="btn btn-primary mt-2" id="assignMissingPpeBtn">
Assegna DPI obbligatori mancanti
</button>
</div>
<div class="col-12 col-lg-5">
<h6 class="fw-bold">Assegna altri DPI</h6>
<div class="text-muted small mb-2">Puoi assegnare anche DPI non previsti dalla sottomansione.</div>
<select id="manualPpeSelect" class="form-select" multiple style="width:100%;">
<?php foreach ($ppeItems as $ppe): ?>
<option value="<?= (int)$ppe['id'] ?>">
<?= htmlspecialchars($ppe['name'], ENT_QUOTES, 'UTF-8') ?>
<?= !empty($ppe['category']) ? ' - ' . htmlspecialchars($ppe['category'], ENT_QUOTES, 'UTF-8') : '' ?>
<?= !empty($ppe['standard_reference']) ? ' - ' . htmlspecialchars($ppe['standard_reference'], ENT_QUOTES, 'UTF-8') : '' ?>
</option>
<?php endforeach; ?>
</select>
<button type="button" class="btn btn-success mt-2" id="assignManualPpeBtn">
Assegna DPI selezionati
</button>
</div>
</div>
<hr>
<h6 class="fw-bold">Storico DPI assegnati</h6>
<div class="table-responsive">
<table class="table table-sm table-striped align-middle">
<thead>
<tr>
<th>DPI</th>
<th>Categoria</th>
<th>Assegnato</th>
<th>Scadenza</th>
<th>Stato</th>
<th>Consegnato da</th>
<th>Note</th>
<th>Azioni</th>
</tr>
</thead>
<tbody id="employeeAssignedPpeHistory"></tbody>
</table>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Chiudi</button>
</div>
</div>
</div>
</div>
<!-- BULK DPI MODAL originale -->
<div class="modal fade" id="bulkPpeModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">🦺 Assegna DPI a più dipendenti</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
</div>
<form id="bulkPpeForm">
<div class="modal-body" style="max-height:65vh; overflow-y:auto;">
<p class="text-muted small">Registra la consegna dello stesso DPI, con gli stessi dati, per più dipendenti contemporaneamente.</p>
<div class="row g-3">
<div class="col-12 col-md-6">
<label class="form-label fw-semibold">DPI <span class="text-danger">*</span></label>
<select id="bulkPpeItems" class="form-select" multiple required style="width:100%;">
<?php foreach ($ppeItems as $ppe): ?>
<option value="<?= (int)$ppe['id'] ?>">
<?= htmlspecialchars($ppe['name'], ENT_QUOTES, 'UTF-8') ?>
<?= !empty($ppe['category']) ? ' - ' . htmlspecialchars($ppe['category'], ENT_QUOTES, 'UTF-8') : '' ?>
<?= !empty($ppe['standard_reference']) ? ' - ' . htmlspecialchars($ppe['standard_reference'], ENT_QUOTES, 'UTF-8') : '' ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-6 col-md-3">
<label class="form-label fw-semibold">Data consegna</label>
<input type="date" id="bulkPpeDeliveryDate" class="form-control" value="<?= date('Y-m-d') ?>">
</div>
<div class="col-6 col-md-3">
<label class="form-label fw-semibold">Consegnato da</label>
<input type="text" id="bulkPpeDeliveredBy" class="form-control" maxlength="255" placeholder="Nome o azienda">
</div>
<div class="col-12">
<label class="form-label fw-semibold">Note</label>
<textarea id="bulkPpeNotes" class="form-control" rows="2" placeholder="Opzionale"></textarea>
</div>
<div class="col-12">
<hr class="my-1">
</div>
<div class="col-12">
<label class="form-label fw-semibold">Dipendenti <span class="text-danger">*</span></label>
<div class="d-flex flex-wrap gap-2 mb-2 align-items-end">
<div>
<select id="bulkPpeDept" class="form-select form-select-sm" style="min-width:180px">
<option value="">— Reparto —</option>
<?php foreach ($departments as $d): ?>
<option value="<?= (int)$d['id'] ?>"><?= htmlspecialchars($d['name'], ENT_QUOTES, 'UTF-8') ?></option>
<?php endforeach; ?>
</select>
</div>
<button type="button" class="btn btn-sm btn-outline-primary" id="bulkPpeAddDept">+ Aggiungi reparto</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="bulkPpeSelectAll">Tutti</button>
<button type="button" class="btn btn-sm btn-outline-secondary" id="bulkPpeClear">Pulisci</button>
</div>
<select id="bulkPpeEmployees" class="form-select" multiple required>
<?php foreach ($employeesForSelect as $e): ?>
<option value="<?= (int)$e['id'] ?>" data-dept="<?= (int)($e['department_id'] ?? 0) ?>">
<?= htmlspecialchars(trim($e['last_name'] . ' ' . $e['first_name']), ENT_QUOTES, 'UTF-8') ?><?php if (!empty($e['employee_code'])): ?> (<?= htmlspecialchars($e['employee_code'], ENT_QUOTES, 'UTF-8') ?>)<?php endif; ?>
</option>
<?php endforeach; ?>
</select>
<div class="form-text"><span id="bulkPpeCount">0</span> dipendenti selezionati</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light border" data-bs-dismiss="modal">Annulla</button>
<button type="submit" class="btn btn-primary" id="bulkPpeSaveBtn">Assegna DPI</button>
</div>
</form>
</div>
</div>
</div>
<?php include('jsinclude.php'); ?>
<script>
const allSkills = <?= json_encode($allSkills, JSON_UNESCAPED_UNICODE) ?>;
const jobSubRolesByRole = <?= json_encode($jobSubRolesByRole, JSON_UNESCAPED_UNICODE) ?>;
function escapeHtml(value) {
return $('<div>').text(value || '').html();
}
function initSelect2() {
$('#addAuthUserId, #editAuthUserId, #addDepartmentId, #editDepartmentId, #addRoleId, #editRoleId, #addJobSubRoleIds, #editJobSubRoleIds').select2({
theme: 'bootstrap-5',
width: '100%',
closeOnSelect: false,
placeholder: 'Seleziona...'
});
$('#manualPpeSelect').select2({
theme: 'bootstrap-5',
dropdownParent: $('#employeePpeModal'),
placeholder: 'Cerca DPI per nome, categoria o standard...',
closeOnSelect: false,
allowClear: true,
width: '100%'
});
$('#bulkPpeItems').select2({
theme: 'bootstrap-5',
placeholder: 'Seleziona uno o più DPI...',
dropdownParent: $('#bulkPpeModal'),
closeOnSelect: false,
width: '100%'
});
}
function populateJobSubRoles($select, jobRoleId, selectedValue) {
$select.empty();
if (!jobRoleId || !jobSubRolesByRole[String(jobRoleId)]) {
$select.append(new Option('-- Prima seleziona una mansione --', '', true, false));
$select.prop('disabled', true).trigger('change.select2');
return;
}
$select.append(new Option('-- Nessuna --', '', false, false));
jobSubRolesByRole[String(jobRoleId)].forEach(function(item) {
const selected = String(item.id) === String(selectedValue || '');
const option = new Option(item.name, item.id, selected, selected);
$select.append(option);
});
$select.prop('disabled', false).trigger('change.select2');
}
function syncAddRoleVisibility() {
const authUserId = $('#addAuthUserId').val();
if (authUserId) {
$('#addRoleWrapper').removeClass('d-none');
const selectedRoleId = $('#addAuthUserId option:selected').data('role_id');
if (selectedRoleId) {
$('#addRoleId').val(String(selectedRoleId)).trigger('change');
}
} else {
$('#addRoleWrapper').addClass('d-none');
$('#addRoleId').val('').trigger('change');
}
}
function syncEditRoleVisibility() {
const authUserId = $('#editAuthUserId').val();
if (authUserId) {
$('#editRoleWrapper').removeClass('d-none');
const selectedRoleId = $('#editAuthUserId option:selected').data('role_id');
if (selectedRoleId) {
$('#editRoleId').val(String(selectedRoleId)).trigger('change');
}
} else {
$('#editRoleWrapper').addClass('d-none');
$('#editRoleId').val('').trigger('change');
}
}
function resetAddEmployeeForm() {
$('#addEmployeeForm')[0].reset();
$('#addDepartmentId, #addJobSubRoleIds, #addAuthUserId, #addRoleId').val(null).trigger('change');
$('#addRoleWrapper').addClass('d-none');
}
function buildEmployeePpeModal(data) {
const employee = data.employee || {};
const required = data.required_ppe || [];
const assigned = data.assigned_ppe || [];
$('#employeePpeName').text(((employee.first_name || '') + ' ' + (employee.last_name || '')).trim());
$('#employeePpeEmployeeId').val(employee.id || '');
$('#manualPpeSelect').val(null).trigger('change');
$('#employeePpeAssignedDate').val(new Date().toISOString().slice(0, 10));
$('#employeePpeDeliveredBy').val('');
$('#manualPpeNotes').val('');
if (employee.job_sub_role_ids) {
$('#employeePpeSubRoleBox').html('Sottomansioni: <strong>' + escapeHtml(employee.job_sub_role_name || '-') + '</strong>');
} else {
$('#employeePpeSubRoleBox').html('<span class="text-danger">Nessuna sottomansione collegata: non ci sono DPI obbligatori suggeriti.</span>');
}
if (!required.length) {
$('#employeeRequiredPpeBox').html('<div class="alert alert-light border mb-0">Nessun DPI obbligatorio associato alla sottomansione.</div>');
$('#assignMissingPpeBtn').prop('disabled', true).data('ids', []);
} else {
let missingIds = [];
let html = '';
required.forEach(function(item) {
const isAssigned = !!item.is_assigned;
if (!isAssigned) {
missingIds.push(item.ppe_item_id);
}
html += `
<div class="ppe-required-card ${isAssigned ? 'assigned' : 'missing'}">
<div class="d-flex justify-content-between gap-2 align-items-start">
<div>
<div class="fw-bold">🦺 ${escapeHtml(item.ppe_name)}</div>
<div class="small text-muted">
${escapeHtml(item.category || 'Senza categoria')}
${item.standard_reference ? ' · ' + escapeHtml(item.standard_reference) : ''}
${item.required_by_subroles ? '<br>Richiesto da: ' + escapeHtml(item.required_by_subroles) : ''}
</div>
</div>
<span class="ppe-status-pill ${isAssigned ? 'assigned' : 'missing'}">
${isAssigned ? 'Assegnato' : 'Mancante'}
</span>
</div>
</div>
`;
});
$('#employeeRequiredPpeBox').html(html);
$('#assignMissingPpeBtn').prop('disabled', missingIds.length === 0).data('ids', missingIds);
}
if (!assigned.length) {
$('#employeeAssignedPpeHistory').html('<tr><td colspan="8" class="text-center text-muted">Nessun DPI assegnato.</td></tr>');
} else {
let rows = '';
assigned.forEach(function(item) {
const canRemove = item.status === 'assigned';
const removeButton = canRemove ? `
<button type="button"
class="btn btn-sm btn-outline-danger btnRemoveEmployeePpe"
data-assignment-id="${escapeHtml(item.assignment_id)}">
Rimuovi
</button>
` : '<span class="text-muted small">-</span>';
rows += `
<tr>
<td>${escapeHtml(item.ppe_name)}</td>
<td>${escapeHtml(item.category || '-')}</td>
<td>${escapeHtml(item.assigned_date || '-')}</td>
<td>${escapeHtml(item.expiry_date || '-')}</td>
<td>${escapeHtml(item.status || '-')}</td>
<td>${escapeHtml(item.delivered_by || '-')}</td>
<td>${escapeHtml(item.notes || '')}</td>
<td>${removeButton}</td>
</tr>
`;
});
$('#employeeAssignedPpeHistory').html(rows);
}
}
function loadEmployeePpe(employeeId, showModal) {
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'get_employee_ppe');
payload.append('id', employeeId);
return fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: payload.toString()
})
.then(r => r.json())
.then(data => {
if (!data.success) {
Swal.fire('Errore', data.message || 'Errore caricamento DPI.', 'error');
return;
}
buildEmployeePpeModal(data);
if (showModal) {
$('#employeePpeModal').modal('show');
}
});
}
$(document).ready(function() {
$('#tabellaDipendenti').DataTable({
order: [
[0, 'desc']
],
pageLength: 25,
language: {
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json',
emptyTable: 'Nessun dipendente presente'
}
});
initSelect2();
$('#addEmployeeModal').on('show.bs.modal', function() {
resetAddEmployeeForm();
});
$('#addAuthUserId').on('change', syncAddRoleVisibility);
$('#editAuthUserId').on('change', syncEditRoleVisibility);
/* -------- BULK DPI ASSIGN originale -------- */
var $bulkPpeEmp = $('#bulkPpeEmployees');
$bulkPpeEmp.select2({
theme: 'bootstrap-5',
placeholder: 'Seleziona dipendenti...',
dropdownParent: $('#bulkPpeModal'),
closeOnSelect: false,
width: '100%'
});
function bulkPpeUpdateCount() {
$('#bulkPpeCount').text(($bulkPpeEmp.val() || []).length);
}
$bulkPpeEmp.on('change', bulkPpeUpdateCount);
$('#btnBulkPpe').on('click', function() {
document.getElementById('bulkPpeForm').reset();
$('#bulkPpeItems').val(null).trigger('change');
$bulkPpeEmp.val(null).trigger('change');
bulkPpeUpdateCount();
var sb = document.getElementById('bulkPpeSaveBtn');
sb.disabled = false;
sb.innerHTML = 'Assegna DPI';
});
$('#bulkPpeAddDept').on('click', function() {
var dept = $('#bulkPpeDept').val();
if (!dept) return;
var current = ($bulkPpeEmp.val() || []).map(String);
$bulkPpeEmp.find('option').each(function() {
if (this.getAttribute('data-dept') === String(dept) && current.indexOf(this.value) === -1) {
current.push(this.value);
}
});
$bulkPpeEmp.val(current).trigger('change');
});
$('#bulkPpeSelectAll').on('click', function() {
var all = $bulkPpeEmp.find('option').map(function() {
return this.value;
}).get();
$bulkPpeEmp.val(all).trigger('change');
});
$('#bulkPpeClear').on('click', function() {
$bulkPpeEmp.val(null).trigger('change');
});
$('#bulkPpeForm').on('submit', function(e) {
e.preventDefault();
var selectedPpe = $('#bulkPpeItems').val() || [];
var emps = $bulkPpeEmp.val() || [];
if (selectedPpe.length === 0) {
Swal.fire('Attenzione', 'Selezionare almeno un DPI.', 'warning');
return;
}
if (emps.length === 0) {
Swal.fire('Attenzione', 'Selezionare almeno un dipendente.', 'warning');
return;
}
var btn = document.getElementById('bulkPpeSaveBtn');
btn.disabled = true;
var orig = btn.innerHTML;
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-1"></span> Salvataggio...';
var fd = new URLSearchParams();
fd.append('ajax', '1');
fd.append('action', 'assign_bulk_employee_ppe');
fd.append('assigned_date', $('#bulkPpeDeliveryDate').val());
fd.append('delivered_by', $('#bulkPpeDeliveredBy').val());
fd.append('notes', $('#bulkPpeNotes').val());
selectedPpe.forEach(function(id) {
fd.append('ppe_ids[]', id);
});
emps.forEach(function(id) {
fd.append('employee_ids[]', id);
});
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: fd.toString()
})
.then(function(r) {
return r.json();
})
.then(function(data) {
if (data.success) {
bootstrap.Modal.getInstance(document.getElementById('bulkPpeModal')).hide();
Swal.fire({
icon: 'success',
title: 'Fatto',
text: data.message,
timer: 2000,
showConfirmButton: false
});
} else {
btn.disabled = false;
btn.innerHTML = orig;
Swal.fire('Errore', data.message || 'Errore.', 'error');
}
})
.catch(function() {
btn.disabled = false;
btn.innerHTML = orig;
Swal.fire('Errore', 'Errore di connessione.', 'error');
});
});
/* -------- ADD EMPLOYEE -------- */
$('#addEmployeeForm').on('submit', function(e) {
e.preventDefault();
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'add');
payload.append('employee_code', $('#addEmployeeCode').val().trim());
payload.append('first_name', $('#addFirstName').val().trim());
payload.append('last_name', $('#addLastName').val().trim());
payload.append('address', $('#addAddress').val().trim());
payload.append('phone', $('#addPhone').val().trim());
payload.append('email', $('#addEmail').val().trim());
payload.append('department_id', $('#addDepartmentId').val() || '');
($('#addJobSubRoleIds').val() || []).forEach(function(id) {
payload.append('job_sub_role_ids[]', id);
});
payload.append('hire_date', $('#addHireDate').val());
payload.append('status', $('#addStatus').val());
payload.append('auth_user_id', $('#addAuthUserId').val() || '');
payload.append('role_id', $('#addAuthUserId').val() ? ($('#addRoleId').val() || '') : '');
fetch('', {
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: 'Saved!',
confirmButtonColor: '#3085d6'
}).then(() => location.reload());
} else {
Swal.fire('Error', data.message || 'Unable to save employee.', 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Communication error.', 'error');
});
});
/* -------- OPEN EDIT MODAL -------- */
$(document).on('click', '.edit-employee', function() {
const btn = $(this);
$('#editEmployeeId').val(btn.data('id'));
$('#editEmployeeCode').val(btn.data('code'));
$('#editFirstName').val(btn.data('first_name'));
$('#editLastName').val(btn.data('last_name'));
$('#editDepartmentId').val(btn.data('department_id') ? String(btn.data('department_id')) : '').trigger('change');
const selectedJobSubRoleIds = (btn.attr('data-job_sub_role_ids') || '')
.split(',')
.map(function(item) {
return item.trim();
})
.filter(Boolean);
$('#editJobSubRoleIds').val(selectedJobSubRoleIds).trigger('change');
$('#editAddress').val(btn.data('address'));
$('#editPhone').val(btn.data('phone'));
$('#editEmail').val(btn.data('email'));
$('#editHireDate').val(btn.data('hire_date'));
$('#editStatus').val(btn.data('status'));
const authUserId = btn.data('auth_user_id');
$('#editAuthUserId').val(authUserId ? String(authUserId) : '').trigger('change');
const roleId = btn.data('role_id');
if (authUserId && roleId) {
$('#editRoleWrapper').removeClass('d-none');
$('#editRoleId').val(String(roleId)).trigger('change');
} else {
$('#editRoleWrapper').addClass('d-none');
$('#editRoleId').val('').trigger('change');
}
$('#editEmployeeModal').modal('show');
});
/* -------- SAVE EDIT -------- */
$('#editEmployeeForm').on('submit', function(e) {
e.preventDefault();
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'edit');
payload.append('id', $('#editEmployeeId').val());
payload.append('employee_code', $('#editEmployeeCode').val().trim());
payload.append('first_name', $('#editFirstName').val().trim());
payload.append('last_name', $('#editLastName').val().trim());
payload.append('address', $('#editAddress').val().trim());
payload.append('phone', $('#editPhone').val().trim());
payload.append('email', $('#editEmail').val().trim());
payload.append('department_id', $('#editDepartmentId').val() || '');
($('#editJobSubRoleIds').val() || []).forEach(function(id) {
payload.append('job_sub_role_ids[]', id);
});
payload.append('hire_date', $('#editHireDate').val());
payload.append('status', $('#editStatus').val());
payload.append('auth_user_id', $('#editAuthUserId').val() || '');
payload.append('role_id', $('#editAuthUserId').val() ? ($('#editRoleId').val() || '') : '');
fetch('', {
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: 'Updated!',
confirmButtonColor: '#3085d6'
}).then(() => location.reload());
} else {
Swal.fire('Error', data.message || 'Unable to update employee.', 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Communication error.', 'error');
});
});
/* -------- DELETE EMPLOYEE -------- */
$(document).on('click', '.delete-employee', function() {
const id = $(this).data('id');
const name = $(this).data('name');
Swal.fire({
title: 'Confermi la cancellazione?',
text: name ? ('Dipendente: ' + name) : 'This employee will be deleted.',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#6c757d',
confirmButtonText: 'Sì, cancella',
cancelButtonText: 'Annulla'
}).then((result) => {
if (!result.isConfirmed) return;
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'delete');
payload.append('id', id);
fetch('', {
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: 'Deleted!',
confirmButtonColor: '#3085d6'
}).then(() => location.reload());
} else {
Swal.fire('Error', data.message || 'Unable to delete employee.', 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Communication error.', 'error');
});
});
});
/* -------- EMPLOYEE PPE -------- */
$(document).on('click', '.manage-employee-ppe', function() {
const id = $(this).data('id');
loadEmployeePpe(id, true);
});
$('#assignMissingPpeBtn').on('click', function() {
const employeeId = $('#employeePpeEmployeeId').val();
const ids = $(this).data('ids') || [];
if (!ids.length) {
Swal.fire('Info', 'Non ci sono DPI obbligatori mancanti.', 'info');
return;
}
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'assign_missing_employee_ppe');
payload.append('id', employeeId);
payload.append('assigned_date', $('#employeePpeAssignedDate').val() || '');
payload.append('delivered_by', $('#employeePpeDeliveredBy').val() || '');
payload.append('notes', $('#manualPpeNotes').val() || '');
ids.forEach(function(id) {
payload.append('ppe_ids[]', id);
});
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: payload.toString()
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire('Fatto', data.message || 'DPI assegnati.', 'success');
loadEmployeePpe(employeeId, false);
} else {
Swal.fire('Errore', data.message || 'Errore.', 'error');
}
});
});
$('#assignManualPpeBtn').on('click', function() {
const employeeId = $('#employeePpeEmployeeId').val();
const ids = $('#manualPpeSelect').val() || [];
if (!ids.length) {
Swal.fire('Attenzione', 'Seleziona almeno un DPI.', 'warning');
return;
}
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'assign_employee_ppe');
payload.append('id', employeeId);
payload.append('assigned_date', $('#employeePpeAssignedDate').val() || '');
payload.append('delivered_by', $('#employeePpeDeliveredBy').val() || '');
payload.append('notes', $('#manualPpeNotes').val() || '');
ids.forEach(function(id) {
payload.append('ppe_ids[]', id);
});
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: payload.toString()
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire('Fatto', data.message || 'DPI assegnati.', 'success');
loadEmployeePpe(employeeId, false);
} else {
Swal.fire('Errore', data.message || 'Errore.', 'error');
}
});
});
$(document).on('click', '.btnRemoveEmployeePpe', function() {
const assignmentId = $(this).data('assignment-id');
const employeeId = $('#employeePpeEmployeeId').val();
Swal.fire({
title: 'Rimuovere DPI?',
text: 'Il DPI verrà segnato come restituito/rimosso dal dipendente.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Sì, rimuovi',
cancelButtonText: 'Annulla',
confirmButtonColor: '#dc2626'
}).then(function(result) {
if (!result.isConfirmed) {
return;
}
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'remove_employee_ppe');
payload.append('assignment_id', assignmentId);
payload.append('employee_id', employeeId);
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: payload.toString()
})
.then(r => r.json())
.then(data => {
if (data.success) {
Swal.fire('Fatto', data.message || 'DPI rimosso.', 'success');
loadEmployeePpe(employeeId, false);
} else {
Swal.fire('Errore', data.message || 'Errore.', 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Errore', 'Errore di comunicazione.', 'error');
});
});
});
/* -------- SKILLS -------- */
$(document).on('click', '.manage-skills', function() {
const id = $(this).data('id');
const name = $(this).data('name');
$('#skillsEmployeeId').val(id);
$('#skillsEmployeeName').text(name);
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'get_employee_skills');
payload.append('id', id);
fetch('', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: payload.toString()
})
.then(r => r.json())
.then(data => {
if (data.success) {
const currentSkills = data.skills || {};
let html = '';
let currentLine = '';
allSkills.forEach(skill => {
let line = skill.line_name || 'Generali';
if (line !== currentLine) {
if (currentLine) html += '</div>';
html += `<h6 class="mt-3">${escapeHtml(line)}</h6><div class="row">`;
currentLine = line;
}
let level = currentSkills[skill.id] || 'NON RICH.';
let options = '';
if (skill.name === 'TURNO NOTTURNO') {
options = `
<option value="si" ${level === 'si' ? 'selected' : ''}>Si</option>
<option value="no" ${level === 'no' ? 'selected' : ''}>No</option>
`;
} else {
options = `
<option value="DF" ${level === 'DF' ? 'selected' : ''}>Da formare</option>
<option value="C" ${level === 'C' ? 'selected' : ''}>Conosce l'attività</option>
<option value="CQ" ${level === 'CQ' ? 'selected' : ''}>Conoscenza media</option>
<option value="Q" ${level === 'Q' ? 'selected' : ''}>Qualificato</option>
<option value="NON RICH." ${level === 'NON RICH.' ? 'selected' : ''}>Non richiesto</option>
`;
}
html += `
<div class="col-md-6 mb-3">
<label class="form-label">${escapeHtml(skill.name)}</label>
<select name="skills[${skill.id}]" class="form-select">
${options}
</select>
</div>
`;
});
if (currentLine) html += '</div>';
$('#skillsContainer').html(html);
$('#manageSkillsModal').modal('show');
} else {
Swal.fire('Error', data.message || 'Unable to load skills.', 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Communication error.', 'error');
});
});
$('#manageSkillsForm').on('submit', function(e) {
e.preventDefault();
const id = $('#skillsEmployeeId').val();
const skills = {};
$('select[name^="skills["]').each(function() {
const skillId = $(this).attr('name').match(/\[(\d+)\]/)[1];
skills[skillId] = $(this).val();
});
const payload = new URLSearchParams();
payload.append('ajax', '1');
payload.append('action', 'save_employee_skills');
payload.append('id', id);
payload.append('skills', JSON.stringify(skills));
fetch('', {
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: 'Skills salvate!'
});
$('#manageSkillsModal').modal('hide');
} else {
Swal.fire('Error', data.message || 'Unable to save skills.', 'error');
}
})
.catch(err => {
console.error(err);
Swal.fire('Error', 'Communication error.', 'error');
});
});
});
</script>
</body>
</html>