382 lines
21 KiB
PHP
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>
|