Files
zibo-dashboard/public/userarea/manutenzioni/maintenances.php
T
2026-08-25 09:04:07 +02:00

382 lines
21 KiB
PHP

<?php include(__DIR__ . '/../include/headscript.php'); ?>
<?php
require_once __DIR__ . '/include/functions.php';
$pdo = mnt_pdo();
if (!userCan('maintenance.maintenances.view')) {
http_response_code(403);
exit('Permesso negato.');
}
$canManage = userCan('maintenance.manage');
// ---------------------------------------------------------------- filters
$filterType = (string)($_GET['type'] ?? '');
$filterState = (string)($_GET['state'] ?? '');
$filterCategory = isset($_GET['category']) && is_numeric($_GET['category']) ? (int)$_GET['category'] : null;
$filterCritical = !empty($_GET['critical']);
$filterMine = !empty($_GET['mine']);
$where = ["m.is_active = 1", "e.status = 'active'"];
$params = [];
if (array_key_exists($filterType, mnt_intervention_types())) {
$where[] = "m.intervention_type = ?";
$params[] = $filterType;
}
if ($filterCategory) {
$where[] = "e.category_id = ?";
$params[] = $filterCategory;
}
if ($filterCritical) {
$where[] = "m.is_critical = 1";
}
// "Assigned to me" resolves the logged-in user to their employee record
if ($filterMine) {
$stmt = $pdo->prepare("SELECT id FROM employees WHERE auth_user_id = ? LIMIT 1");
$stmt->execute([(int)$iduserlogin]);
$myEmployeeId = (int)$stmt->fetchColumn();
if ($myEmployeeId) {
$where[] = "(m.assignee_employee_id = ? OR m.supervisor_employee_id = ?)";
$params[] = $myEmployeeId;
$params[] = $myEmployeeId;
} else {
$filterMine = false;
}
}
$stmt = $pdo->prepare("
SELECT m.*,
e.id AS equipment_id, e.name AS equipment_name,
c.name AS category_name, c.color AS category_color,
CONCAT(a.first_name, ' ', a.last_name) AS assignee_name,
CONCAT(s.first_name, ' ', s.last_name) AS supervisor_name,
sup.supplier_name
FROM maint_maintenances m
INNER JOIN inv_equipment e ON e.id = m.equipment_id
LEFT JOIN inv_categories c ON c.id = e.category_id
LEFT JOIN employees a ON a.id = m.assignee_employee_id
LEFT JOIN employees s ON s.id = m.supervisor_employee_id
LEFT JOIN suppliers sup ON sup.idsupplier = m.supplier_id
WHERE " . implode(' AND ', $where) . "
ORDER BY (m.next_due_date IS NULL) ASC, m.next_due_date ASC, e.name ASC
");
$stmt->execute($params);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
// The schedule state depends on each maintenance's own lead time, so it is
// computed in PHP rather than in SQL.
$maintenances = [];
$counters = ['overdue' => 0, 'due_soon' => 0, 'ok' => 0, 'none' => 0];
foreach ($rows as $row) {
$state = mnt_due_state($row['next_due_date'], (int)$row['alert_days']);
$counters[$state]++;
if ($filterState !== '' && $filterState !== $state) {
continue;
}
$row['state'] = $state;
$maintenances[] = $row;
}
// Paging happens here and not in SQL: the state of each task and the four
// counter tiles are computed in PHP over the whole result set, so a LIMIT in
// the query would leave the tiles counting one page instead of everything.
[$page, $perPage] = mnt_page_params();
$totalMaintenances = count($maintenances);
$totalPages = max(1, (int)ceil($totalMaintenances / $perPage));
$page = min($page, $totalPages);
$maintenances = array_slice($maintenances, ($page - 1) * $perPage, $perPage);
$formData = mnt_form_data($pdo);
$typeLabels = mnt_intervention_types();
$executionLabels = mnt_execution_types();
$MNT_TITLE = 'Manutenzioni';
?>
<!doctype html>
<html lang="it">
<head>
<?php include __DIR__ . '/include/page_head.php'; ?>
</head>
<body>
<?php include __DIR__ . '/include/wrapper_open.php'; ?>
<?php include(__DIR__ . '/../include/navbar.php'); ?>
<?php include(__DIR__ . '/../include/topbar.php'); ?>
<div class="page-wrapper">
<div class="page-content">
<div class="card mnt-card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
<h5><i class="fa-solid fa-screwdriver-wrench me-2"></i>Manutenzioni</h5>
<div class="header-actions d-flex gap-2 flex-wrap">
<a href="manutenzioni/calendar.php" class="btn btn-mnt-outline d-inline-flex align-items-center gap-2">
<i class="fa-solid fa-calendar-days"></i><span>Calendario</span>
</a>
<a href="manutenzioni/index.php" class="btn btn-mnt-outline d-inline-flex align-items-center gap-2">
<i class="fa-solid fa-boxes-stacked"></i><span>Registro</span>
</a>
</div>
</div>
<div class="card-body">
<div class="row g-2 mb-3">
<?php
$tiles = [
['key' => 'overdue', 'label' => 'Scadute', 'class' => 'mnt-badge-overdue'],
['key' => 'due_soon', 'label' => 'In scadenza', 'class' => 'mnt-badge-soon'],
['key' => 'ok', 'label' => 'In regola', 'class' => 'mnt-badge-ok'],
['key' => 'none', 'label' => 'Senza scadenza', 'class' => 'mnt-badge-none'],
];
?>
<?php foreach ($tiles as $tile): ?>
<div class="col-6 col-md-3">
<?php
// Percorso completo, non "?...": con <base href="/userarea/">
// un href di sola query porterebbe alla dashboard del template.
// page is dropped: changing the filter changes the result
// set, and staying on page 3 of the previous one is nonsense.
$tileQuery = $_GET;
unset($tileQuery['page']);
$tileUrl = 'manutenzioni/maintenances.php?' . http_build_query(
array_merge($tileQuery, ['state' => $filterState === $tile['key'] ? '' : $tile['key']])
);
?>
<a class="d-block text-decoration-none" href="<?= mnt_h($tileUrl) ?>">
<div class="mnt-item-card mnt-tile mnt-tile-<?= $tile['key'] ?> mb-0 <?= $filterState === $tile['key'] ? 'mnt-tile-active' : '' ?>">
<div class="ic-title" style="font-size:1.4rem"><?= (int)$counters[$tile['key']] ?></div>
<div class="ic-meta"><?= $tile['label'] ?></div>
</div>
</a>
</div>
<?php endforeach; ?>
</div>
<form class="mnt-filter-bar" method="get" id="filterForm">
<input type="hidden" name="state" value="<?= mnt_h($filterState) ?>">
<select class="form-select" name="type">
<option value="">Programmate e straordinarie</option>
<?php foreach ($typeLabels as $value => $label): ?>
<option value="<?= mnt_h($value) ?>" <?= $filterType === $value ? 'selected' : '' ?>>
Solo <?= mnt_h(mb_strtolower($label)) ?>
</option>
<?php endforeach; ?>
</select>
<select class="form-select" name="category">
<option value="">Tutte le categorie</option>
<?php foreach ($formData['categories'] as $category): ?>
<option value="<?= (int)$category['id'] ?>" <?= $filterCategory === (int)$category['id'] ? 'selected' : '' ?>>
<?= mnt_h($category['name']) ?>
</option>
<?php endforeach; ?>
</select>
<div class="form-check d-flex align-items-center gap-2 ms-1">
<input class="form-check-input mt-0" type="checkbox" name="critical" value="1" id="fCritical" <?= $filterCritical ? 'checked' : '' ?>>
<label class="form-check-label" for="fCritical">Solo critiche</label>
</div>
<div class="form-check d-flex align-items-center gap-2 ms-1">
<input class="form-check-input mt-0" type="checkbox" name="mine" value="1" id="fMine" <?= $filterMine ? 'checked' : '' ?>>
<label class="form-check-label" for="fMine">Assegnate a me</label>
</div>
<?php if ($perPage !== 25): ?>
<input type="hidden" name="per_page" value="<?= (int)$perPage ?>">
<?php endif; ?>
<!-- No «Filtra» button: selects and checkboxes all submit on change,
so it never had anything left to do. Kept hidden only as the
no-JavaScript fallback. -->
<button type="submit" class="visually-hidden">Filtra</button>
<a href="manutenzioni/maintenances.php" class="btn btn-mnt-outline btn-reset-filters d-inline-flex align-items-center gap-2">
<i class="fa-solid fa-xmark"></i><span>Azzera filtri</span>
</a>
</form>
<?php if (!$maintenances): ?>
<div class="empty-state">
<i class="fa-solid fa-screwdriver-wrench"></i>
<p>Nessuna manutenzione con questi filtri.</p>
</div>
<?php else: ?>
<div id="maintenanceList">
<!-- CARD -->
<div class="d-xl-none">
<?php foreach ($maintenances as $maintenance): ?>
<?php $badge = mnt_due_badge($maintenance['state'], $maintenance['frequency_unit']); ?>
<div class="mnt-item-card" data-id="<?= (int)$maintenance['id'] ?>"
style="--row-color: <?= $maintenance['is_critical'] ? '#dc3545' : mnt_h($maintenance['category_color'] ?? '#e9ecef') ?>">
<div class="ic-title"><?= mnt_h($maintenance['equipment_name']) ?></div>
<div class="ic-meta">
<?php if ($maintenance['code']): ?><strong><?= mnt_h($maintenance['code']) ?></strong> · <?php endif; ?>
<?= mnt_h($maintenance['title']) ?>
</div>
<div class="ic-meta">
Prossimo: <strong><?= mnt_format_date($maintenance['next_due_date']) ?></strong>
<span class="mnt-badge <?= $badge['class'] ?> ms-1"><?= $badge['label'] ?></span>
</div>
<div class="ic-actions">
<a class="btn-action btn-action-view" href="manutenzioni/equipment.php?id=<?= (int)$maintenance['equipment_id'] ?>">
<i class="fa-solid fa-eye"></i>
</a>
<?php if ($canManage): ?>
<button class="btn-action btn-action-done btn-register" title="Registra intervento">
<i class="fa-solid fa-check"></i>
</button>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- DESKTOP -->
<!-- Table only from 1200px up: measured, the 6 columns need
~1280px to fit, so on a tablet it only scrolls sideways. -->
<div class="d-none d-xl-block table-responsive">
<table class="table table-hover align-middle mb-0">
<thead>
<tr>
<th class="mnt-equipment-cell">Attrezzatura</th>
<th>Manutenzione</th>
<th class="mnt-freq-cell">Frequenza</th>
<th class="mnt-person-cell">Incaricato</th>
<th class="text-center">Prossima</th>
<th class="text-center" style="width:130px">Azioni</th>
</tr>
</thead>
<tbody>
<?php foreach ($maintenances as $maintenance): ?>
<?php $badge = mnt_due_badge($maintenance['state'], $maintenance['frequency_unit']); ?>
<tr data-id="<?= (int)$maintenance['id'] ?>">
<td class="mnt-equipment-cell">
<span class="d-flex align-items-start gap-2">
<span class="mnt-cat-dot mt-1" style="background: <?= mnt_h($maintenance['category_color'] ?? '#adb5bd') ?>"></span>
<span class="fw-semibold mnt-title-clamp" style="color:var(--mnt-heading)"
title="<?= mnt_h($maintenance['equipment_name']) ?>"><?= mnt_h($maintenance['equipment_name']) ?></span>
</span>
<div class="small text-muted"><?= mnt_h($maintenance['category_name'] ?? '') ?></div>
</td>
<td class="mnt-title-cell">
<?php if ($maintenance['code']): ?>
<span class="mnt-badge mnt-badge-soft me-1"><?= mnt_h($maintenance['code']) ?></span>
<?php endif; ?>
<?php if ($maintenance['is_critical']): ?>
<span class="mnt-badge mnt-badge-critical ms-1">Critica</span>
<?php endif; ?>
<div class="mnt-title-clamp" title="<?= mnt_h($maintenance['title']) ?>">
<?= mnt_h($maintenance['title']) ?>
</div>
<div class="small text-muted">
<?= mnt_h($typeLabels[$maintenance['intervention_type']] ?? '') ?>
· <?= mnt_h($executionLabels[$maintenance['execution_type']] ?? '') ?>
<?php if ($maintenance['supplier_name']): ?>
— <?= mnt_h($maintenance['supplier_name']) ?>
<?php endif; ?>
</div>
</td>
<td class="small mnt-freq-cell">
<div class="mnt-title-clamp"><?= mnt_h(mnt_format_frequency(
$maintenance['frequency_value'] !== null ? (int)$maintenance['frequency_value'] : null,
$maintenance['frequency_unit'],
$maintenance['frequency_note']
)) ?></div>
</td>
<td class="small mnt-person-cell">
<?= mnt_h($maintenance['assignee_name'] ?: '—') ?>
<?php if ($maintenance['supervisor_name']): ?>
<div class="text-muted"><?= mnt_h($maintenance['supervisor_name']) ?></div>
<?php endif; ?>
</td>
<td class="text-center">
<div><?= mnt_format_date($maintenance['next_due_date']) ?></div>
<span class="mnt-badge <?= $badge['class'] ?>"><?= $badge['label'] ?></span>
</td>
<td class="text-center">
<div class="d-inline-flex gap-1">
<a class="btn-action btn-action-view" title="Apri scheda"
href="manutenzioni/equipment.php?id=<?= (int)$maintenance['equipment_id'] ?>">
<i class="fa-solid fa-eye"></i>
</a>
<?php if ($canManage): ?>
<button class="btn-action btn-action-done btn-register" title="Registra intervento">
<i class="fa-solid fa-check"></i>
</button>
<?php endif; ?>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
<?php
$mntPagerUrl = 'manutenzioni/maintenances.php';
$mntPagerPage = $page;
$mntPagerPages = $totalPages;
$mntPagerTotal = $totalMaintenances;
$mntPagerPer = $perPage;
include __DIR__ . '/include/pagination.php';
?>
<?php endif; ?>
</div>
</div>
</div>
</div>
<?php include(__DIR__ . '/../include/footer.php'); ?>
</div>
<?php if ($canManage): ?>
<?php include __DIR__ . '/include/intervention_modal.php'; ?>
<?php endif; ?>
<?php include(__DIR__ . '/../jsinclude.php'); ?>
<script>
$(function() {
$('#filterForm select, #filterForm input[type=checkbox]').on('change', function() {
$('#filterForm').submit();
});
<?php if ($canManage): ?>
$('#maintenanceList').on('click', '.btn-register', function() {
const id = $(this).closest('[data-id]').data('id');
$.getJSON('manutenzioni/ajax/get_maintenance.php', {
id: id
})
.done(res => {
if (!res.success) {
Swal.fire({
icon: 'error',
title: 'Errore',
text: res.message
});
return;
}
mntOpenInterventionModal(null, {
id: res.maintenance.id,
title: res.maintenance.title,
equipment_name: res.maintenance.equipment_name,
supplier_id: res.maintenance.supplier_id
});
});
});
<?php endif; ?>
});
</script>
</body>
</html>