1449 lines
52 KiB
PHP
1449 lines
52 KiB
PHP
<?php
|
|
include('include/headscript.php');
|
|
|
|
if (function_exists('requirePermission')) {
|
|
requirePermission('hr.employees.view');
|
|
}
|
|
|
|
$db = DBHandlerSelect::getInstance();
|
|
$pdo = $db->getConnection();
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Upload configuration
|
|
|--------------------------------------------------------------------------
|
|
| The page is expected to be inside /public.
|
|
| Files will be stored in /public/uploads/ppe.
|
|
| The database stores the relative browser path, for example:
|
|
| uploads/ppe/ppe_20260604_123456_abcd.jpg
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
const PPE_UPLOAD_RELATIVE_DIR = 'uploads/ppe';
|
|
const PPE_UPLOAD_MAX_SIZE = 5242880; // 5 MB
|
|
|
|
$ppeUploadAbsoluteDir = __DIR__ . '/' . PPE_UPLOAD_RELATIVE_DIR;
|
|
|
|
if (!is_dir($ppeUploadAbsoluteDir)) {
|
|
mkdir($ppeUploadAbsoluteDir, 0755, true);
|
|
}
|
|
|
|
function jsonResponse(bool $success, string $message = '', array $extra = []): void
|
|
{
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
echo json_encode(array_merge([
|
|
'success' => $success,
|
|
'message' => $message,
|
|
], $extra));
|
|
exit;
|
|
}
|
|
|
|
function cleanString(?string $value): string
|
|
{
|
|
return trim((string)$value);
|
|
}
|
|
|
|
function cleanNullableString(?string $value): ?string
|
|
{
|
|
$value = trim((string)$value);
|
|
return $value === '' ? null : $value;
|
|
}
|
|
|
|
function toActiveValue($value): int
|
|
{
|
|
return ((string)$value === '1') ? 1 : 0;
|
|
}
|
|
|
|
function normalizePathForDb(string $path): string
|
|
{
|
|
return str_replace('\\', '/', $path);
|
|
}
|
|
|
|
function deletePpePhotoIfExists(?string $relativePath): void
|
|
{
|
|
if (!$relativePath) {
|
|
return;
|
|
}
|
|
|
|
$relativePath = normalizePathForDb($relativePath);
|
|
|
|
if (strpos($relativePath, PPE_UPLOAD_RELATIVE_DIR . '/') !== 0) {
|
|
return;
|
|
}
|
|
|
|
$absolutePath = __DIR__ . '/' . $relativePath;
|
|
|
|
if (is_file($absolutePath)) {
|
|
unlink($absolutePath);
|
|
}
|
|
}
|
|
|
|
function uploadPpePhoto(array $file): ?string
|
|
{
|
|
if (!isset($file['error']) || $file['error'] === UPLOAD_ERR_NO_FILE) {
|
|
return null;
|
|
}
|
|
|
|
if ($file['error'] !== UPLOAD_ERR_OK) {
|
|
throw new RuntimeException('Errore durante il caricamento della foto.');
|
|
}
|
|
|
|
if (($file['size'] ?? 0) > PPE_UPLOAD_MAX_SIZE) {
|
|
throw new RuntimeException('La foto supera la dimensione massima consentita di 5 MB.');
|
|
}
|
|
|
|
$tmpPath = $file['tmp_name'] ?? '';
|
|
|
|
if (!is_uploaded_file($tmpPath)) {
|
|
throw new RuntimeException('File caricato non valido.');
|
|
}
|
|
|
|
$finfo = new finfo(FILEINFO_MIME_TYPE);
|
|
$mimeType = $finfo->file($tmpPath);
|
|
|
|
$allowedMimeTypes = [
|
|
'image/jpeg' => 'jpg',
|
|
'image/png' => 'png',
|
|
'image/webp' => 'webp',
|
|
];
|
|
|
|
if (!isset($allowedMimeTypes[$mimeType])) {
|
|
throw new RuntimeException('Formato foto non consentito. Sono ammessi JPG, PNG e WEBP.');
|
|
}
|
|
|
|
$extension = $allowedMimeTypes[$mimeType];
|
|
$randomName = bin2hex(random_bytes(6));
|
|
$fileName = 'ppe_' . date('Ymd_His') . '_' . $randomName . '.' . $extension;
|
|
|
|
$absoluteDestination = __DIR__ . '/' . PPE_UPLOAD_RELATIVE_DIR . '/' . $fileName;
|
|
$relativeDestination = PPE_UPLOAD_RELATIVE_DIR . '/' . $fileName;
|
|
|
|
if (!move_uploaded_file($tmpPath, $absoluteDestination)) {
|
|
throw new RuntimeException('Impossibile salvare la foto nella cartella DPI.');
|
|
}
|
|
|
|
return normalizePathForDb($relativeDestination);
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| AJAX actions
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
|
try {
|
|
$action = $_POST['action'];
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Save PPE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
if ($action === 'save_ppe') {
|
|
$id = isset($_POST['id']) && $_POST['id'] !== '' ? (int)$_POST['id'] : null;
|
|
|
|
$name = cleanString($_POST['name'] ?? '');
|
|
$description = cleanNullableString($_POST['description'] ?? null);
|
|
$category = cleanNullableString($_POST['category'] ?? null);
|
|
$standardReference = cleanNullableString($_POST['standard_reference'] ?? null);
|
|
$validityMonths = isset($_POST['validity_months']) && $_POST['validity_months'] !== '' ? (int)$_POST['validity_months'] : null;
|
|
$sortOrder = isset($_POST['sort_order']) && $_POST['sort_order'] !== '' ? (int)$_POST['sort_order'] : 999;
|
|
$isActive = toActiveValue($_POST['is_active'] ?? 1);
|
|
$removePhoto = (int)($_POST['remove_photo'] ?? 0) === 1;
|
|
|
|
if ($name === '') {
|
|
jsonResponse(false, 'Il nome del DPI è obbligatorio.');
|
|
}
|
|
|
|
$uploadedPhotoPath = null;
|
|
|
|
if (isset($_FILES['photo'])) {
|
|
$uploadedPhotoPath = uploadPpePhoto($_FILES['photo']);
|
|
}
|
|
|
|
if ($id) {
|
|
$stmt = $pdo->prepare("
|
|
SELECT photo
|
|
FROM ppe_items
|
|
WHERE id = ?
|
|
LIMIT 1
|
|
");
|
|
$stmt->execute([$id]);
|
|
$existingPhoto = $stmt->fetchColumn();
|
|
|
|
if ($existingPhoto === false) {
|
|
jsonResponse(false, 'DPI non trovato.');
|
|
}
|
|
|
|
$finalPhoto = $existingPhoto;
|
|
|
|
if ($removePhoto) {
|
|
deletePpePhotoIfExists($existingPhoto);
|
|
$finalPhoto = null;
|
|
}
|
|
|
|
if ($uploadedPhotoPath) {
|
|
deletePpePhotoIfExists($existingPhoto);
|
|
$finalPhoto = $uploadedPhotoPath;
|
|
}
|
|
|
|
$stmt = $pdo->prepare("
|
|
UPDATE ppe_items
|
|
SET name = :name,
|
|
description = :description,
|
|
category = :category,
|
|
photo = :photo,
|
|
standard_reference = :standard_reference,
|
|
validity_months = :validity_months,
|
|
sort_order = :sort_order,
|
|
is_active = :is_active,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = :id
|
|
");
|
|
|
|
$stmt->execute([
|
|
':name' => $name,
|
|
':description' => $description,
|
|
':category' => $category,
|
|
':photo' => $finalPhoto,
|
|
':standard_reference' => $standardReference,
|
|
':validity_months' => $validityMonths,
|
|
':sort_order' => $sortOrder,
|
|
':is_active' => $isActive,
|
|
':id' => $id,
|
|
]);
|
|
|
|
jsonResponse(true, 'DPI aggiornato correttamente.');
|
|
}
|
|
|
|
$stmt = $pdo->prepare("
|
|
INSERT INTO ppe_items
|
|
(
|
|
name,
|
|
description,
|
|
category,
|
|
photo,
|
|
standard_reference,
|
|
validity_months,
|
|
sort_order,
|
|
is_active,
|
|
created_at,
|
|
updated_at
|
|
)
|
|
VALUES
|
|
(
|
|
:name,
|
|
:description,
|
|
:category,
|
|
:photo,
|
|
:standard_reference,
|
|
:validity_months,
|
|
:sort_order,
|
|
:is_active,
|
|
CURRENT_TIMESTAMP,
|
|
CURRENT_TIMESTAMP
|
|
)
|
|
");
|
|
|
|
$stmt->execute([
|
|
':name' => $name,
|
|
':description' => $description,
|
|
':category' => $category,
|
|
':photo' => $uploadedPhotoPath,
|
|
':standard_reference' => $standardReference,
|
|
':validity_months' => $validityMonths,
|
|
':sort_order' => $sortOrder,
|
|
':is_active' => $isActive,
|
|
]);
|
|
|
|
jsonResponse(true, 'DPI creato correttamente.');
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Get PPE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
if ($action === 'get_ppe') {
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
|
|
if ($id <= 0) {
|
|
jsonResponse(false, 'ID DPI non valido.');
|
|
}
|
|
|
|
$stmt = $pdo->prepare("
|
|
SELECT
|
|
id,
|
|
name,
|
|
description,
|
|
category,
|
|
photo,
|
|
standard_reference,
|
|
validity_months,
|
|
sort_order,
|
|
is_active
|
|
FROM ppe_items
|
|
WHERE id = ?
|
|
LIMIT 1
|
|
");
|
|
$stmt->execute([$id]);
|
|
$ppe = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if (!$ppe) {
|
|
jsonResponse(false, 'DPI non trovato.');
|
|
}
|
|
|
|
jsonResponse(true, '', [
|
|
'ppe' => $ppe,
|
|
]);
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Delete PPE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
if ($action === 'delete_ppe') {
|
|
$id = (int)($_POST['id'] ?? 0);
|
|
|
|
if ($id <= 0) {
|
|
jsonResponse(false, 'ID DPI non valido.');
|
|
}
|
|
|
|
$stmt = $pdo->prepare("
|
|
SELECT photo
|
|
FROM ppe_items
|
|
WHERE id = ?
|
|
LIMIT 1
|
|
");
|
|
$stmt->execute([$id]);
|
|
$photo = $stmt->fetchColumn();
|
|
|
|
if ($photo === false) {
|
|
jsonResponse(false, 'DPI non trovato.');
|
|
}
|
|
|
|
$pdo->beginTransaction();
|
|
|
|
$stmt = $pdo->prepare("
|
|
DELETE FROM ppe_items
|
|
WHERE id = ?
|
|
");
|
|
$stmt->execute([$id]);
|
|
|
|
$pdo->commit();
|
|
|
|
deletePpePhotoIfExists($photo ?: null);
|
|
|
|
jsonResponse(true, 'DPI eliminato correttamente.');
|
|
}
|
|
|
|
jsonResponse(false, 'Azione non riconosciuta.');
|
|
} catch (Throwable $e) {
|
|
if ($pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
|
|
jsonResponse(false, 'Errore: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Page data
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
$stmt = $pdo->prepare("
|
|
SELECT
|
|
id,
|
|
name,
|
|
description,
|
|
category,
|
|
photo,
|
|
standard_reference,
|
|
validity_months,
|
|
sort_order,
|
|
is_active,
|
|
created_at,
|
|
updated_at
|
|
FROM ppe_items
|
|
ORDER BY sort_order ASC, name ASC
|
|
");
|
|
$stmt->execute();
|
|
$ppeItems = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$totalPpe = count($ppeItems);
|
|
$totalActivePpe = 0;
|
|
$totalWithPhoto = 0;
|
|
|
|
$categories = [];
|
|
|
|
foreach ($ppeItems as $ppe) {
|
|
if ((int)$ppe['is_active'] === 1) {
|
|
$totalActivePpe++;
|
|
}
|
|
|
|
if (!empty($ppe['photo'])) {
|
|
$totalWithPhoto++;
|
|
}
|
|
|
|
if (!empty($ppe['category'])) {
|
|
$categories[$ppe['category']] = true;
|
|
}
|
|
}
|
|
|
|
$totalCategories = count($categories);
|
|
|
|
?>
|
|
<!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 DPI - <?= htmlspecialchars($titlewebsite ?? '', ENT_QUOTES, 'UTF-8'); ?></title>
|
|
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
|
|
|
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
|
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
|
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
|
|
|
<style>
|
|
body {
|
|
font-size: 1.03rem;
|
|
background: #f8fafc;
|
|
}
|
|
|
|
.card {
|
|
border: 0;
|
|
border-radius: 18px;
|
|
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.08);
|
|
}
|
|
|
|
.page-title-box {
|
|
background: linear-gradient(135deg, #e8f1ff 0%, #ffffff 70%);
|
|
border: 1px solid #dbeafe;
|
|
border-radius: 18px;
|
|
padding: 18px 20px;
|
|
margin-bottom: 18px;
|
|
}
|
|
|
|
.page-title-box h4 {
|
|
color: #1f2d3d;
|
|
font-weight: 800;
|
|
}
|
|
|
|
.page-title-box p {
|
|
color: #64748b;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.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.08);
|
|
transition: all 0.2s ease-in-out;
|
|
}
|
|
|
|
.back-dashboard:hover {
|
|
background-color: #b9d3ff !important;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.btn-main-action {
|
|
background: #2563eb;
|
|
color: #ffffff;
|
|
border: 1px solid #2563eb;
|
|
border-radius: 10px;
|
|
font-weight: 700;
|
|
padding: 10px 18px;
|
|
box-shadow: 0 5px 14px rgba(37, 99, 235, 0.22);
|
|
transition: all 0.2s ease-in-out;
|
|
}
|
|
|
|
.btn-main-action:hover {
|
|
color: #ffffff;
|
|
background: #1d4ed8;
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.stat-card {
|
|
background: #ffffff;
|
|
border: 1px solid #e2e8f0;
|
|
border-radius: 16px;
|
|
padding: 16px;
|
|
height: 100%;
|
|
}
|
|
|
|
.stat-label {
|
|
color: #64748b;
|
|
font-size: 0.87rem;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: .04em;
|
|
}
|
|
|
|
.stat-number {
|
|
color: #0f172a;
|
|
font-size: 1.7rem;
|
|
font-weight: 800;
|
|
margin-top: 6px;
|
|
}
|
|
|
|
.table thead {
|
|
background-color: #cfe3ff;
|
|
color: #1f2d3d;
|
|
}
|
|
|
|
#tabPpeItems {
|
|
table-layout: fixed;
|
|
width: 100% !important;
|
|
}
|
|
|
|
#tabPpeItems th,
|
|
#tabPpeItems td {
|
|
vertical-align: middle;
|
|
}
|
|
|
|
#tabPpeItems th:nth-child(1),
|
|
#tabPpeItems td:nth-child(1) {
|
|
width: 75px;
|
|
text-align: center;
|
|
}
|
|
|
|
#tabPpeItems th:nth-child(2),
|
|
#tabPpeItems td:nth-child(2) {
|
|
width: 105px;
|
|
text-align: center;
|
|
}
|
|
|
|
#tabPpeItems th:nth-child(4),
|
|
#tabPpeItems td:nth-child(4) {
|
|
width: 150px;
|
|
text-align: center;
|
|
}
|
|
|
|
#tabPpeItems th:nth-child(5),
|
|
#tabPpeItems td:nth-child(5) {
|
|
width: 160px;
|
|
text-align: center;
|
|
}
|
|
|
|
#tabPpeItems th:nth-child(6),
|
|
#tabPpeItems td:nth-child(6) {
|
|
width: 110px;
|
|
text-align: center;
|
|
}
|
|
|
|
#tabPpeItems th:nth-child(7),
|
|
#tabPpeItems td:nth-child(7) {
|
|
width: 105px;
|
|
text-align: center;
|
|
}
|
|
|
|
#tabPpeItems th:nth-child(8),
|
|
#tabPpeItems td:nth-child(8) {
|
|
width: 230px;
|
|
text-align: center;
|
|
}
|
|
|
|
.ppe-name {
|
|
font-weight: 800;
|
|
color: #0f172a;
|
|
}
|
|
|
|
.ppe-description {
|
|
color: #64748b;
|
|
font-size: 0.92rem;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
.ppe-thumb {
|
|
width: 74px;
|
|
height: 74px;
|
|
object-fit: cover;
|
|
border-radius: 14px;
|
|
border: 1px solid #dbeafe;
|
|
background: #f8fafc;
|
|
}
|
|
|
|
.ppe-no-photo {
|
|
width: 74px;
|
|
height: 74px;
|
|
border-radius: 14px;
|
|
border: 1px dashed #cbd5e1;
|
|
background: #f8fafc;
|
|
color: #94a3b8;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 0.78rem;
|
|
font-weight: 700;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
.badge-active {
|
|
background: #dcfce7;
|
|
color: #166534;
|
|
border: 1px solid #bbf7d0;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.badge-inactive {
|
|
background: #fee2e2;
|
|
color: #991b1b;
|
|
border: 1px solid #fecaca;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.badge-category {
|
|
background: #eff6ff;
|
|
color: #1e40af;
|
|
border: 1px solid #bfdbfe;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.action-btn {
|
|
border-radius: 9px;
|
|
font-weight: 700;
|
|
}
|
|
|
|
.modal-content {
|
|
border: 0;
|
|
border-radius: 18px;
|
|
box-shadow: 0 24px 60px rgba(15, 23, 42, 0.22);
|
|
}
|
|
|
|
.modal-header {
|
|
border-bottom: 1px solid #e2e8f0;
|
|
background: #f8fafc;
|
|
border-radius: 18px 18px 0 0;
|
|
}
|
|
|
|
.modal-title {
|
|
font-weight: 800;
|
|
color: #1f2d3d;
|
|
}
|
|
|
|
.form-label {
|
|
font-weight: 700;
|
|
color: #334155;
|
|
}
|
|
|
|
.form-control,
|
|
.form-select {
|
|
border-radius: 10px;
|
|
}
|
|
|
|
.photo-preview-box {
|
|
border: 1px dashed #cbd5e1;
|
|
background: #f8fafc;
|
|
border-radius: 16px;
|
|
padding: 14px;
|
|
min-height: 150px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.photo-preview-img {
|
|
max-width: 100%;
|
|
max-height: 220px;
|
|
border-radius: 14px;
|
|
border: 1px solid #e2e8f0;
|
|
display: none;
|
|
}
|
|
|
|
.photo-preview-empty {
|
|
color: #94a3b8;
|
|
font-weight: 700;
|
|
text-align: center;
|
|
}
|
|
|
|
.view-photo {
|
|
width: 100%;
|
|
max-height: 360px;
|
|
object-fit: contain;
|
|
border-radius: 16px;
|
|
border: 1px solid #e2e8f0;
|
|
background: #f8fafc;
|
|
}
|
|
|
|
.detail-label {
|
|
color: #64748b;
|
|
font-size: 0.82rem;
|
|
font-weight: 800;
|
|
text-transform: uppercase;
|
|
letter-spacing: .04em;
|
|
}
|
|
|
|
.detail-value {
|
|
color: #0f172a;
|
|
font-weight: 700;
|
|
margin-bottom: 12px;
|
|
}
|
|
</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="page-title-box d-flex justify-content-between align-items-center flex-wrap gap-3">
|
|
<div>
|
|
<h4 class="mb-1">Gestione DPI</h4>
|
|
<p>Anagrafica dei Dispositivi di Protezione Individuale, con foto, categorie, validità e standard di riferimento.</p>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button type="button" class="btn back-dashboard" onclick="history.back();">
|
|
↩️ Indietro
|
|
</button>
|
|
|
|
<button type="button" class="btn btn-main-action" id="btnOpenCreatePpe">
|
|
+ Nuovo DPI
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-3 mb-3">
|
|
<div class="col-12 col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-label">DPI totali</div>
|
|
<div class="stat-number"><?= (int)$totalPpe; ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-label">DPI attivi</div>
|
|
<div class="stat-number"><?= (int)$totalActivePpe; ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-label">Categorie</div>
|
|
<div class="stat-number"><?= (int)$totalCategories; ?></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-3">
|
|
<div class="stat-card">
|
|
<div class="stat-label">Con foto</div>
|
|
<div class="stat-number"><?= (int)$totalWithPhoto; ?></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card p-3">
|
|
<div class="card-header d-flex justify-content-between align-items-center flex-wrap gap-2">
|
|
<h5 class="mb-0">Elenco DPI</h5>
|
|
|
|
<div class="d-flex align-items-center gap-2 flex-wrap">
|
|
<label class="fw-semibold mb-0">Stato:</label>
|
|
<select id="filterStatus" class="form-select form-select-sm" style="width: 180px;">
|
|
<option value="">Tutti</option>
|
|
<option value="Attivo">Solo attivi</option>
|
|
<option value="Inattivo">Solo inattivi</option>
|
|
</select>
|
|
|
|
<label class="fw-semibold mb-0 ms-2">Categoria:</label>
|
|
<select id="filterCategory" class="form-select form-select-sm" style="width: 220px;">
|
|
<option value="">Tutte</option>
|
|
<?php foreach (array_keys($categories) as $category): ?>
|
|
<option value="<?= htmlspecialchars($category, ENT_QUOTES, 'UTF-8'); ?>">
|
|
<?= htmlspecialchars($category, ENT_QUOTES, 'UTF-8'); ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card-body">
|
|
<div class="table-responsive">
|
|
<table id="tabPpeItems" class="table table-striped align-middle" style="width:100%;">
|
|
<thead>
|
|
<tr>
|
|
<th>ID</th>
|
|
<th>Foto</th>
|
|
<th>DPI</th>
|
|
<th>Categoria</th>
|
|
<th>Standard</th>
|
|
<th>Validità</th>
|
|
<th>Stato</th>
|
|
<th>Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tbody>
|
|
<?php foreach ($ppeItems as $ppe): ?>
|
|
<?php
|
|
$ppeId = (int)$ppe['id'];
|
|
$isActive = (int)$ppe['is_active'] === 1;
|
|
$photo = $ppe['photo'] ?? null;
|
|
?>
|
|
<tr data-ppe-id="<?= $ppeId; ?>">
|
|
<td><?= $ppeId; ?></td>
|
|
|
|
<td>
|
|
<?php if (!empty($photo)): ?>
|
|
<img src="<?= htmlspecialchars($photo, ENT_QUOTES, 'UTF-8'); ?>"
|
|
class="ppe-thumb"
|
|
alt="<?= htmlspecialchars($ppe['name'], ENT_QUOTES, 'UTF-8'); ?>">
|
|
<?php else: ?>
|
|
<div class="ppe-no-photo">No foto</div>
|
|
<?php endif; ?>
|
|
</td>
|
|
|
|
<td>
|
|
<div class="ppe-name">
|
|
<?= htmlspecialchars($ppe['name'], ENT_QUOTES, 'UTF-8'); ?>
|
|
</div>
|
|
|
|
<?php if (!empty($ppe['description'])): ?>
|
|
<div class="ppe-description">
|
|
<?= htmlspecialchars($ppe['description'], ENT_QUOTES, 'UTF-8'); ?>
|
|
</div>
|
|
<?php else: ?>
|
|
<div class="ppe-description">
|
|
Nessuna descrizione
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div class="text-muted small mt-1">
|
|
Ordine: <?= (int)$ppe['sort_order']; ?>
|
|
</div>
|
|
</td>
|
|
|
|
<td>
|
|
<?php if (!empty($ppe['category'])): ?>
|
|
<span class="badge badge-category">
|
|
<?= htmlspecialchars($ppe['category'], ENT_QUOTES, 'UTF-8'); ?>
|
|
</span>
|
|
<?php else: ?>
|
|
<span class="text-muted">-</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
|
|
<td>
|
|
<?= !empty($ppe['standard_reference'])
|
|
? htmlspecialchars($ppe['standard_reference'], ENT_QUOTES, 'UTF-8')
|
|
: '<span class="text-muted">-</span>'; ?>
|
|
</td>
|
|
|
|
<td>
|
|
<?php if (!empty($ppe['validity_months'])): ?>
|
|
<?= (int)$ppe['validity_months']; ?> mesi
|
|
<?php else: ?>
|
|
<span class="text-muted">Nessuna</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
|
|
<td>
|
|
<?php if ($isActive): ?>
|
|
<span class="badge badge-active">Attivo</span>
|
|
<?php else: ?>
|
|
<span class="badge badge-inactive">Inattivo</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
|
|
<td>
|
|
<div class="d-flex justify-content-center flex-wrap gap-1">
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline-primary action-btn btnViewPpe"
|
|
data-ppe-id="<?= $ppeId; ?>">
|
|
Visualizza
|
|
</button>
|
|
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline-warning action-btn btnEditPpe"
|
|
data-ppe-id="<?= $ppeId; ?>">
|
|
Edita
|
|
</button>
|
|
|
|
<button type="button"
|
|
class="btn btn-sm btn-outline-danger action-btn btnDeletePpe"
|
|
data-ppe-id="<?= $ppeId; ?>"
|
|
data-ppe-name="<?= htmlspecialchars($ppe['name'], ENT_QUOTES, 'UTF-8'); ?>">
|
|
Cancella
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
|
|
<?php if (!$ppeItems): ?>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="text-muted small mt-3">
|
|
Le foto vengono salvate nella cartella <strong><?= PPE_UPLOAD_RELATIVE_DIR; ?></strong>. Nel database viene salvato solo il percorso relativo.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<?php include('include/footer.php'); ?>
|
|
</div>
|
|
|
|
<!-- PPE FORM MODAL -->
|
|
<div class="modal fade" id="ppeModal" tabindex="-1" aria-labelledby="ppeModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-xl modal-dialog-centered">
|
|
<form id="ppeForm" class="modal-content" enctype="multipart/form-data">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="ppeModalLabel">Nuovo DPI</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
<input type="hidden" name="action" value="save_ppe">
|
|
<input type="hidden" name="id" id="ppe_id">
|
|
<input type="hidden" name="remove_photo" id="ppe_remove_photo" value="0">
|
|
|
|
<div class="row g-4">
|
|
<div class="col-12 col-lg-8">
|
|
<div class="row g-3">
|
|
<div class="col-12 col-md-8">
|
|
<label class="form-label" for="ppe_name">Nome DPI *</label>
|
|
<input type="text" class="form-control" name="name" id="ppe_name" required maxlength="255">
|
|
</div>
|
|
|
|
<div class="col-12 col-md-4">
|
|
<label class="form-label" for="ppe_category">Categoria</label>
|
|
<input type="text" class="form-control" name="category" id="ppe_category" maxlength="100" list="ppeCategoryList">
|
|
<datalist id="ppeCategoryList">
|
|
<option value="Head">
|
|
<option value="Eyes">
|
|
<option value="Hands">
|
|
<option value="Feet">
|
|
<option value="Respiratory">
|
|
<option value="Hearing">
|
|
<option value="Body">
|
|
<option value="Fall protection">
|
|
<?php foreach (array_keys($categories) as $category): ?>
|
|
<option value="<?= htmlspecialchars($category, ENT_QUOTES, 'UTF-8'); ?>">
|
|
<?php endforeach; ?>
|
|
</datalist>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-5">
|
|
<label class="form-label" for="ppe_standard_reference">Standard di riferimento</label>
|
|
<input type="text" class="form-control" name="standard_reference" id="ppe_standard_reference" maxlength="255" placeholder="Es. EN ISO 20345">
|
|
</div>
|
|
|
|
<div class="col-12 col-md-3">
|
|
<label class="form-label" for="ppe_validity_months">Validità mesi</label>
|
|
<input type="number" class="form-control" name="validity_months" id="ppe_validity_months" min="0" placeholder="Es. 12">
|
|
</div>
|
|
|
|
<div class="col-12 col-md-2">
|
|
<label class="form-label" for="ppe_sort_order">Ordine</label>
|
|
<input type="number" class="form-control" name="sort_order" id="ppe_sort_order" value="999" min="0">
|
|
</div>
|
|
|
|
<div class="col-12 col-md-2">
|
|
<label class="form-label" for="ppe_is_active">Stato</label>
|
|
<select class="form-select" name="is_active" id="ppe_is_active">
|
|
<option value="1">Attivo</option>
|
|
<option value="0">Inattivo</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="col-12">
|
|
<label class="form-label" for="ppe_description">Descrizione</label>
|
|
<textarea class="form-control" name="description" id="ppe_description" rows="5"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-12 col-lg-4">
|
|
<label class="form-label" for="ppe_photo">Foto DPI</label>
|
|
|
|
<div class="photo-preview-box mb-3">
|
|
<img src="" id="ppe_photo_preview" class="photo-preview-img" alt="Anteprima DPI">
|
|
<div id="ppe_photo_empty" class="photo-preview-empty">
|
|
Nessuna foto selezionata
|
|
</div>
|
|
</div>
|
|
|
|
<input type="file" class="form-control" name="photo" id="ppe_photo" accept="image/jpeg,image/png,image/webp">
|
|
|
|
<div class="text-muted small mt-2">
|
|
Formati ammessi: JPG, PNG, WEBP. Dimensione massima: 5 MB.
|
|
</div>
|
|
|
|
<button type="button" class="btn btn-outline-danger btn-sm mt-3 d-none" id="btnRemovePhoto">
|
|
Rimuovi foto
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Annulla</button>
|
|
<button type="submit" class="btn btn-primary">Salva DPI</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- PPE VIEW MODAL -->
|
|
<div class="modal fade" id="ppeViewModal" tabindex="-1" aria-labelledby="ppeViewModalLabel" aria-hidden="true">
|
|
<div class="modal-dialog modal-xl modal-dialog-centered modal-dialog-scrollable">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<div>
|
|
<h5 class="modal-title" id="ppeViewModalLabel">Dettaglio DPI</h5>
|
|
<div class="text-muted small" id="ppeViewSubtitle"></div>
|
|
</div>
|
|
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
|
|
</div>
|
|
|
|
<div class="modal-body">
|
|
<div class="row g-4">
|
|
<div class="col-12 col-lg-5">
|
|
<div id="ppeViewPhotoBox"></div>
|
|
</div>
|
|
|
|
<div class="col-12 col-lg-7">
|
|
<div class="detail-label">Nome</div>
|
|
<div class="detail-value" id="ppeViewName"></div>
|
|
|
|
<div class="row">
|
|
<div class="col-12 col-md-6">
|
|
<div class="detail-label">Categoria</div>
|
|
<div class="detail-value" id="ppeViewCategory"></div>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-6">
|
|
<div class="detail-label">Stato</div>
|
|
<div class="detail-value" id="ppeViewStatus"></div>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-6">
|
|
<div class="detail-label">Standard</div>
|
|
<div class="detail-value" id="ppeViewStandard"></div>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-6">
|
|
<div class="detail-label">Validità</div>
|
|
<div class="detail-value" id="ppeViewValidity"></div>
|
|
</div>
|
|
|
|
<div class="col-12 col-md-6">
|
|
<div class="detail-label">Ordine</div>
|
|
<div class="detail-value" id="ppeViewSortOrder"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="detail-label">Descrizione</div>
|
|
<div class="detail-value" id="ppeViewDescription"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-warning" id="btnEditFromView">
|
|
Edita
|
|
</button>
|
|
|
|
<button type="button" class="btn btn-light" data-bs-dismiss="modal">
|
|
Chiudi
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php include('jsinclude.php'); ?>
|
|
|
|
<script>
|
|
let dt;
|
|
let ppeModal;
|
|
let ppeViewModal;
|
|
let currentViewPpeId = null;
|
|
|
|
function htmlEscape(value) {
|
|
return String(value ?? '')
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function showSuccess(message) {
|
|
Swal.fire({
|
|
icon: 'success',
|
|
title: message,
|
|
timer: 1100,
|
|
showConfirmButton: false
|
|
}).then(() => {
|
|
window.location.reload();
|
|
});
|
|
}
|
|
|
|
function showError(message) {
|
|
Swal.fire({
|
|
icon: 'error',
|
|
title: 'Errore',
|
|
text: message || 'Operazione non riuscita'
|
|
});
|
|
}
|
|
|
|
function ajaxPost(data) {
|
|
return fetch(window.location.href, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
},
|
|
body: new URLSearchParams(data)
|
|
})
|
|
.then(async response => {
|
|
const text = await response.text();
|
|
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch (e) {
|
|
throw new Error('Risposta non JSON: ' + text);
|
|
}
|
|
});
|
|
}
|
|
|
|
function ajaxForm(form) {
|
|
const formData = new FormData(form);
|
|
|
|
return fetch(window.location.href, {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(async response => {
|
|
const text = await response.text();
|
|
|
|
try {
|
|
return JSON.parse(text);
|
|
} catch (e) {
|
|
throw new Error('Risposta non JSON: ' + text);
|
|
}
|
|
});
|
|
}
|
|
|
|
function resetPpeForm() {
|
|
$('#ppeForm')[0].reset();
|
|
|
|
$('#ppe_id').val('');
|
|
$('#ppe_remove_photo').val('0');
|
|
$('#ppe_sort_order').val('999');
|
|
$('#ppe_is_active').val('1');
|
|
|
|
$('#ppe_photo_preview').attr('src', '').hide();
|
|
$('#ppe_photo_empty').show().text('Nessuna foto selezionata');
|
|
$('#btnRemovePhoto').addClass('d-none');
|
|
}
|
|
|
|
function setPhotoPreview(photoPath) {
|
|
if (photoPath) {
|
|
$('#ppe_photo_preview').attr('src', photoPath).show();
|
|
$('#ppe_photo_empty').hide();
|
|
$('#btnRemovePhoto').removeClass('d-none');
|
|
} else {
|
|
$('#ppe_photo_preview').attr('src', '').hide();
|
|
$('#ppe_photo_empty').show().text('Nessuna foto selezionata');
|
|
$('#btnRemovePhoto').addClass('d-none');
|
|
}
|
|
}
|
|
|
|
function loadPpeIntoForm(ppeId, openAfterLoad = true) {
|
|
return ajaxPost({
|
|
action: 'get_ppe',
|
|
id: ppeId
|
|
})
|
|
.then(data => {
|
|
if (!data.success) {
|
|
showError(data.message);
|
|
return false;
|
|
}
|
|
|
|
resetPpeForm();
|
|
|
|
$('#ppeModalLabel').text('Modifica DPI');
|
|
$('#ppe_id').val(data.ppe.id);
|
|
$('#ppe_name').val(data.ppe.name);
|
|
$('#ppe_description').val(data.ppe.description || '');
|
|
$('#ppe_category').val(data.ppe.category || '');
|
|
$('#ppe_standard_reference').val(data.ppe.standard_reference || '');
|
|
$('#ppe_validity_months').val(data.ppe.validity_months || '');
|
|
$('#ppe_sort_order').val(data.ppe.sort_order || 999);
|
|
$('#ppe_is_active').val(data.ppe.is_active);
|
|
|
|
setPhotoPreview(data.ppe.photo || '');
|
|
|
|
if (openAfterLoad) {
|
|
ppeModal.show();
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
ppeModal = new bootstrap.Modal(document.getElementById('ppeModal'));
|
|
ppeViewModal = new bootstrap.Modal(document.getElementById('ppeViewModal'));
|
|
|
|
dt = $('#tabPpeItems').DataTable({
|
|
order: [
|
|
[6, 'asc'],
|
|
[2, 'asc']
|
|
],
|
|
pageLength: 25,
|
|
language: {
|
|
url: 'https://cdn.datatables.net/plug-ins/1.13.6/i18n/it-IT.json'
|
|
}
|
|
});
|
|
|
|
$('#filterStatus').on('change', function() {
|
|
dt.column(6).search($(this).val()).draw();
|
|
});
|
|
|
|
$('#filterCategory').on('change', function() {
|
|
dt.column(3).search($(this).val()).draw();
|
|
});
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Create PPE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
$(document).on('click', '#btnOpenCreatePpe', function() {
|
|
resetPpeForm();
|
|
$('#ppeModalLabel').text('Nuovo DPI');
|
|
ppeModal.show();
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Edit PPE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
$(document).on('click', '.btnEditPpe', function() {
|
|
const ppeId = $(this).data('ppe-id');
|
|
|
|
loadPpeIntoForm(ppeId).catch(err => {
|
|
showError(err.message);
|
|
});
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Save PPE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
$('#ppeForm').on('submit', function(e) {
|
|
e.preventDefault();
|
|
|
|
ajaxForm(this)
|
|
.then(data => {
|
|
if (data.success) {
|
|
showSuccess(data.message);
|
|
} else {
|
|
showError(data.message);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
showError(err.message);
|
|
});
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Delete PPE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
$(document).on('click', '.btnDeletePpe', function() {
|
|
const ppeId = $(this).data('ppe-id');
|
|
const ppeName = $(this).data('ppe-name');
|
|
|
|
Swal.fire({
|
|
icon: 'warning',
|
|
title: 'Confermi la cancellazione?',
|
|
html: `Il DPI <strong>${htmlEscape(ppeName)}</strong> verrà eliminato definitivamente.<br>Se è già collegato ad assegnazioni o sottomansioni, il database potrebbe bloccare la cancellazione.`,
|
|
showCancelButton: true,
|
|
confirmButtonText: 'Sì, cancella',
|
|
cancelButtonText: 'Annulla',
|
|
confirmButtonColor: '#dc2626'
|
|
}).then(result => {
|
|
if (!result.isConfirmed) {
|
|
return;
|
|
}
|
|
|
|
ajaxPost({
|
|
action: 'delete_ppe',
|
|
id: ppeId
|
|
})
|
|
.then(data => {
|
|
if (data.success) {
|
|
showSuccess(data.message);
|
|
} else {
|
|
showError(data.message);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
showError(err.message);
|
|
});
|
|
});
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| View PPE
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
$(document).on('click', '.btnViewPpe', function() {
|
|
const ppeId = $(this).data('ppe-id');
|
|
currentViewPpeId = ppeId;
|
|
|
|
ajaxPost({
|
|
action: 'get_ppe',
|
|
id: ppeId
|
|
})
|
|
.then(data => {
|
|
if (!data.success) {
|
|
showError(data.message);
|
|
return;
|
|
}
|
|
|
|
const ppe = data.ppe;
|
|
|
|
$('#ppeViewModalLabel').text('Dettaglio DPI');
|
|
$('#ppeViewSubtitle').text(ppe.name || '');
|
|
|
|
$('#ppeViewName').text(ppe.name || '-');
|
|
$('#ppeViewCategory').html(ppe.category ? `<span class="badge badge-category">${htmlEscape(ppe.category)}</span>` : '<span class="text-muted">-</span>');
|
|
$('#ppeViewStandard').text(ppe.standard_reference || '-');
|
|
$('#ppeViewValidity').text(ppe.validity_months ? `${ppe.validity_months} mesi` : 'Nessuna validità impostata');
|
|
$('#ppeViewSortOrder').text(ppe.sort_order || '999');
|
|
$('#ppeViewDescription').text(ppe.description || 'Nessuna descrizione');
|
|
|
|
if (String(ppe.is_active) === '1') {
|
|
$('#ppeViewStatus').html('<span class="badge badge-active">Attivo</span>');
|
|
} else {
|
|
$('#ppeViewStatus').html('<span class="badge badge-inactive">Inattivo</span>');
|
|
}
|
|
|
|
if (ppe.photo) {
|
|
$('#ppeViewPhotoBox').html(`
|
|
<img src="${htmlEscape(ppe.photo)}" class="view-photo" alt="${htmlEscape(ppe.name)}">
|
|
`);
|
|
} else {
|
|
$('#ppeViewPhotoBox').html(`
|
|
<div class="photo-preview-box" style="min-height:260px;">
|
|
<div class="photo-preview-empty">Nessuna foto disponibile</div>
|
|
</div>
|
|
`);
|
|
}
|
|
|
|
ppeViewModal.show();
|
|
})
|
|
.catch(err => {
|
|
showError(err.message);
|
|
});
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Edit from view modal
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
$(document).on('click', '#btnEditFromView', function() {
|
|
if (!currentViewPpeId) {
|
|
return;
|
|
}
|
|
|
|
ppeViewModal.hide();
|
|
|
|
setTimeout(function() {
|
|
loadPpeIntoForm(currentViewPpeId).catch(err => {
|
|
showError(err.message);
|
|
});
|
|
}, 250);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Photo preview
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
$('#ppe_photo').on('change', function() {
|
|
const file = this.files && this.files[0] ? this.files[0] : null;
|
|
|
|
if (!file) {
|
|
return;
|
|
}
|
|
|
|
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
|
|
|
|
if (!allowedTypes.includes(file.type)) {
|
|
showError('Formato non consentito. Usa JPG, PNG oppure WEBP.');
|
|
$(this).val('');
|
|
return;
|
|
}
|
|
|
|
if (file.size > 5242880) {
|
|
showError('La foto supera la dimensione massima consentita di 5 MB.');
|
|
$(this).val('');
|
|
return;
|
|
}
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.onload = function(e) {
|
|
$('#ppe_remove_photo').val('0');
|
|
$('#ppe_photo_preview').attr('src', e.target.result).show();
|
|
$('#ppe_photo_empty').hide();
|
|
$('#btnRemovePhoto').removeClass('d-none');
|
|
};
|
|
|
|
reader.readAsDataURL(file);
|
|
});
|
|
|
|
/*
|
|
|--------------------------------------------------------------------------
|
|
| Remove photo
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
$('#btnRemovePhoto').on('click', function() {
|
|
$('#ppe_photo').val('');
|
|
$('#ppe_remove_photo').val('1');
|
|
$('#ppe_photo_preview').attr('src', '').hide();
|
|
$('#ppe_photo_empty').show().text('Foto rimossa. Verrà eliminata al salvataggio.');
|
|
$('#btnRemovePhoto').addClass('d-none');
|
|
});
|
|
</script>
|
|
|
|
</body>
|
|
|
|
</html>
|