2374 lines
106 KiB
PHP
2374 lines
106 KiB
PHP
<?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>
|