deadline widget

This commit is contained in:
2026-04-19 09:14:19 +03:00
parent 1fadc22178
commit dd5edab2f3
4 changed files with 395 additions and 112 deletions
+203 -65
View File
@@ -13,6 +13,16 @@ if ($filterSubjectId) {
if (!$filterSubjectName) { $filterSubjectId = null; }
}
// Optional filter: limit to deadlines assigned to the current user (directly OR via department)
$filterMy = !empty($_GET['filter_my']);
$filterMyEmployee = null;
if ($filterMy) {
$st = $pdo->prepare("SELECT id, department FROM employees WHERE auth_user_id = ? LIMIT 1");
$st->execute([(int)$iduserlogin]);
$filterMyEmployee = $st->fetch(PDO::FETCH_ASSOC) ?: null;
if (!$filterMyEmployee) { $filterMy = false; }
}
$sql = "
SELECT d.*,
s.name AS subject_name,
@@ -26,11 +36,22 @@ $sql = "
LEFT JOIN scad_deadline_employee de ON de.deadline_id = d.id
LEFT JOIN employees e ON e.id = de.employee_id
";
$where = [];
$params = [];
if ($filterSubjectId) {
$sql .= " WHERE d.subject_id = ?";
$where[] = "d.subject_id = ?";
$params[] = $filterSubjectId;
}
if ($filterMy && $filterMyEmployee) {
$where[] = "(d.id IN (SELECT deadline_id FROM scad_deadline_employee WHERE employee_id = ?)"
. " OR (? <> '' AND FIND_IN_SET(?, REPLACE(d.departments, ', ', ',')) > 0))";
$params[] = (int)$filterMyEmployee['id'];
$params[] = (string)($filterMyEmployee['department'] ?? '');
$params[] = (string)($filterMyEmployee['department'] ?? '');
}
if (!empty($where)) {
$sql .= " WHERE " . implode(' AND ', $where);
}
$sql .= " GROUP BY d.id ORDER BY (d.status = 'completed') ASC, d.due_date ASC";
$stmt = $pdo->prepare($sql);
@@ -277,10 +298,29 @@ $today = date('Y-m-d');
justify-content: space-between;
}
.header-menu-btn {
width: 38px !important;
height: 38px !important;
padding: 0 !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
line-height: 1 !important;
font-size: 1rem !important;
position: relative;
}
.header-menu-btn i {
display: block;
line-height: 1;
margin: 0;
font-size: 1rem;
}
.header-menu-btn::after { display: none !important; }
@media (max-width: 575.98px) {
.scad-card .card-header { flex-direction: column; gap: 0.75rem; }
.header-actions { width: 100%; }
.header-actions .btn { width: 100%; justify-content: center; }
.scad-card .card-header { flex-direction: column; gap: 0.75rem; align-items: flex-start !important; }
.header-actions { width: 100%; justify-content: flex-end; }
.header-actions #btnAddDeadline { flex: 1; justify-content: center; }
.filter-bar .form-select { width: 100%; }
}
</style>
@@ -292,6 +332,8 @@ $today = date('Y-m-d');
<div class="page-wrapper">
<div class="page-content">
<?php include(__DIR__ . '/include/my_deadlines_widget.php'); ?>
<?php if ($filterSubjectId): ?>
<div class="subject-filter-banner">
<div>
@@ -306,60 +348,105 @@ $today = date('Y-m-d');
</div>
<?php endif; ?>
<!-- Filter Bar -->
<div class="filter-bar mb-3">
<div class="row g-2 align-items-center mb-2">
<div class="col-12 col-sm-6 col-md-auto">
<select id="filterStatus" class="form-select">
<?php if ($filterSubjectId): ?>
<option value="" selected>Stato: Tutti</option>
<option value="non-completata">Stato: Non completate</option>
<?php else: ?>
<option value="non-completata" selected>Stato: Non completate</option>
<option value="">Stato: Tutti</option>
<?php endif; ?>
<option value="attiva">Attive</option>
<option value="in-scadenza">In scadenza</option>
<option value="scaduta">Scadute</option>
<option value="completata">Completate</option>
</select>
</div>
<div class="col-12 col-sm-6 col-md-auto">
<select id="filterSubject" class="form-select">
<option value="">Argomento: Tutti</option>
<?php foreach ($subjects as $s): ?>
<option value="<?= (int)$s['id'] ?>" <?= $filterSubjectId === (int)$s['id'] ? 'selected' : '' ?>><?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 col-sm-6 col-md-auto">
<select id="filterDepartment" class="form-select">
<option value="">Reparto: Tutti</option>
<?php foreach ($departments as $dept): ?>
<option value="<?= htmlspecialchars($dept, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($dept, ENT_QUOTES, 'UTF-8') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="col-12 col-sm-6 col-md-auto">
<select id="filterEmployee" class="form-select">
<option value="">Responsabile: Tutti</option>
<?php foreach ($employees as $emp): ?>
<option value="<?= htmlspecialchars(trim($emp['first_name'] . ' ' . $emp['last_name']), ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars(trim($emp['first_name'] . ' ' . $emp['last_name']), ENT_QUOTES, 'UTF-8') ?></option>
<?php endforeach; ?>
</select>
</div>
<?php if ($filterMy): ?>
<div class="subject-filter-banner">
<div>
<i class="fa-solid fa-user-check me-2"></i>
<strong>Le mie scadenze</strong>
<span class="text-muted ms-2">(assegnate a me o al reparto <?= htmlspecialchars($filterMyEmployee['department'] ?? '—', ENT_QUOTES, 'UTF-8') ?>)</span>
</div>
<div class="row g-2 align-items-center">
<div class="col-12 col-sm-6 col-md-auto">
<input type="text" class="form-control" id="filterDueRange" placeholder="Scadenza: da — a" readonly style="font-size:0.85rem;border-color:#d0d9e8;min-width:220px;background:#fff">
</div>
<div class="col-12 col-sm-6 col-md-auto">
<input type="text" class="form-control" id="filterCheckRange" placeholder="Controllo: da — a" readonly style="font-size:0.85rem;border-color:#d0d9e8;min-width:220px;background:#fff">
</div>
<div class="col-12 col-sm-auto">
<button id="btnResetFilters" class="btn btn-sm btn-light border w-100 w-sm-auto" title="Reset filtri">
<i class="fa-solid fa-rotate-left me-1"></i> Reset
</button>
<a href="scadenzario/index.php" class="btn btn-sm btn-light border">
<i class="fa-solid fa-xmark me-1"></i> Tutte le scadenze
</a>
</div>
<?php endif; ?>
<!-- Filter Toolbar -->
<div class="filter-toolbar mb-3">
<div class="d-flex align-items-center gap-2">
<button type="button" class="btn btn-scad-outline d-inline-flex align-items-center gap-2 flex-shrink-0" data-bs-toggle="modal" data-bs-target="#filtersModal">
<i class="fa-solid fa-filter"></i>
<span>Filtri</span>
<span id="filterCountBadge" class="badge bg-primary rounded-pill d-none" style="font-size:0.7rem">0</span>
</button>
<button id="btnResetFilters" type="button" class="btn btn-light border d-inline-flex align-items-center justify-content-center gap-1 flex-shrink-0" title="Reset filtri" style="min-width:38px;height:38px">
<i class="fa-solid fa-rotate-left"></i>
<span class="d-none d-sm-inline">Reset</span>
</button>
<span id="activeFiltersSummary" class="text-muted small text-truncate d-none d-md-inline"></span>
</div>
<div id="activeFiltersSummaryMobile" class="text-muted small d-md-none mt-1" style="padding-left:0.25rem"></div>
</div>
<!-- Filters Modal -->
<div class="modal fade" id="filtersModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title"><i class="fa-solid fa-filter me-2"></i>Filtri scadenze</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label fw-semibold">Stato</label>
<select id="filterStatus" class="form-select">
<?php if ($filterSubjectId): ?>
<option value="" selected>Tutti</option>
<option value="non-completata">Non completate</option>
<?php else: ?>
<option value="non-completata" selected>Non completate</option>
<option value="">Tutti</option>
<?php endif; ?>
<option value="attiva">Attive</option>
<option value="in-scadenza">In scadenza</option>
<option value="scaduta">Scadute</option>
<option value="completata">Completate</option>
</select>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Argomento</label>
<select id="filterSubject" class="form-select">
<option value="">Tutti</option>
<?php foreach ($subjects as $s): ?>
<option value="<?= (int)$s['id'] ?>" <?= $filterSubjectId === (int)$s['id'] ? 'selected' : '' ?>><?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Reparto</label>
<select id="filterDepartment" class="form-select">
<option value="">Tutti</option>
<?php foreach ($departments as $dept): ?>
<option value="<?= htmlspecialchars($dept, ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($dept, ENT_QUOTES, 'UTF-8') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Responsabile</label>
<select id="filterEmployee" class="form-select">
<option value="">Tutti</option>
<?php foreach ($employees as $emp): ?>
<option value="<?= htmlspecialchars(trim($emp['first_name'] . ' ' . $emp['last_name']), ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars(trim($emp['first_name'] . ' ' . $emp['last_name']), ENT_QUOTES, 'UTF-8') ?></option>
<?php endforeach; ?>
</select>
</div>
<div class="mb-3">
<label class="form-label fw-semibold">Data scadenza (intervallo)</label>
<input type="text" class="form-control" id="filterDueRange" placeholder="da — a" readonly>
</div>
<div class="mb-0">
<label class="form-label fw-semibold">Data ultimo controllo (intervallo)</label>
<input type="text" class="form-control" id="filterCheckRange" placeholder="da — a" readonly>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-light border" id="btnResetFiltersModal">
<i class="fa-solid fa-rotate-left me-1"></i> Reset
</button>
<button type="button" class="btn btn-scad-primary" data-bs-dismiss="modal">
<i class="fa-solid fa-check me-1"></i> Applica
</button>
</div>
</div>
</div>
</div>
@@ -367,20 +454,34 @@ $today = date('Y-m-d');
<!-- Main Card -->
<div class="card scad-card">
<div class="card-header d-flex align-items-center justify-content-between flex-wrap gap-2">
<h5><i class="fa-solid fa-calendar-check me-2"></i>Lista Scadenze</h5>
<h5 class="d-none d-md-flex align-items-center"><i class="fa-solid fa-calendar-check me-2"></i>Lista Scadenze</h5>
<div class="header-actions d-flex gap-2 flex-wrap">
<a href="scadenzario/subjects/index.php" class="btn btn-scad-outline d-inline-flex align-items-center gap-2">
<!-- Desktop (≥md): tutte le azioni visibili -->
<a href="scadenzario/subjects/index.php" class="btn btn-scad-outline d-none d-md-inline-flex align-items-center gap-2">
<i class="fa-solid fa-tags"></i><span>Argomenti</span>
</a>
<a href="scadenzario/calendar.php" class="btn btn-scad-outline d-inline-flex align-items-center gap-2">
<a href="scadenzario/calendar.php" class="btn btn-scad-outline d-none d-md-inline-flex align-items-center gap-2">
<i class="fa-solid fa-calendar-days"></i><span>Calendario</span>
</a>
<button class="btn btn-scad-outline d-inline-flex align-items-center gap-2" id="btnStampa">
<button class="btn btn-scad-outline d-none d-md-inline-flex align-items-center gap-2" id="btnStampa">
<i class="fa-solid fa-print"></i><span>Stampa</span>
</button>
<button class="btn btn-scad-primary d-inline-flex align-items-center gap-2" id="btnAddDeadline">
<i class="fa-solid fa-plus"></i><span>Nuova Scadenza</span>
</button>
<!-- Mobile (<md): menu a tendina con le altre azioni -->
<div class="dropdown d-md-none">
<button class="btn btn-scad-outline header-menu-btn" type="button" data-bs-toggle="dropdown" aria-expanded="false" title="Altre azioni">
<i class="fa-solid fa-bars"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item d-flex align-items-center gap-2" href="scadenzario/subjects/index.php"><i class="fa-solid fa-tags"></i> Argomenti</a></li>
<li><a class="dropdown-item d-flex align-items-center gap-2" href="scadenzario/calendar.php"><i class="fa-solid fa-calendar-days"></i> Calendario</a></li>
<li><button type="button" class="dropdown-item d-flex align-items-center gap-2" id="btnStampaMobile"><i class="fa-solid fa-print"></i> Stampa</button></li>
</ul>
</div>
</div>
</div>
<div class="card-body">
@@ -705,6 +806,7 @@ $today = date('Y-m-d');
onChange: function() {
if (table) table.draw();
filterCards();
if (typeof updateFilterBadge === 'function') updateFilterBadge();
}
};
var fpDue = flatpickr('#filterDueRange', fpOpts);
@@ -832,11 +934,34 @@ $today = date('Y-m-d');
});
}
function updateFilterBadge() {
var active = 0;
var summary = [];
var st = $('#filterStatus').val();
if (st && st !== 'non-completata') { active++; summary.push($('#filterStatus option:selected').text()); }
var subj = $('#filterSubject').val();
if (subj) { active++; summary.push('Argomento: ' + $('#filterSubject option:selected').text()); }
var dept = $('#filterDepartment').val();
if (dept) { active++; summary.push('Reparto: ' + dept); }
var emp = $('#filterEmployee').val();
if (emp) { active++; summary.push('Responsabile: ' + emp); }
if (fpDue.selectedDates.length) { active++; summary.push('Scadenza: ' + $('#filterDueRange').val()); }
if (fpCheck.selectedDates.length) { active++; summary.push('Controllo: ' + $('#filterCheckRange').val()); }
var $badge = $('#filterCountBadge');
if (active > 0) $badge.text(active).removeClass('d-none');
else $badge.addClass('d-none');
var summaryText = summary.length ? summary.slice(0, 2).join(' • ') + (summary.length > 2 ? ' +' + (summary.length - 2) : '') : '';
$('#activeFiltersSummary, #activeFiltersSummaryMobile').text(summaryText);
}
$('#filterStatus, #filterDepartment, #filterEmployee, #filterSubject').on('change', function() {
if (table) table.draw();
filterCards();
updateFilterBadge();
});
$('#btnResetFilters').on('click', function() {
function resetFilters() {
$('#filterStatus').val('non-completata');
$('#filterDepartment').val('');
$('#filterEmployee').val('');
@@ -845,11 +970,21 @@ $today = date('Y-m-d');
fpCheck.clear();
if (table) table.draw();
filterCards();
});
updateFilterBadge();
}
$('#btnResetFilters, #btnResetFiltersModal').on('click', resetFilters);
// Apply URL filters (e.g. from dashboard widgets)
(function () {
var qs = new URLSearchParams(window.location.search);
var qStatus = qs.get('filter_status');
if (qStatus) $('#filterStatus').val(qStatus);
})();
// Apply default filter on load
if (table) table.draw();
filterCards();
updateFilterBadge();
// --- Modal ---
var modal = new bootstrap.Modal(document.getElementById('deadlineModal'));
@@ -1085,7 +1220,7 @@ $today = date('Y-m-d');
}
// Stampa
document.getElementById('btnStampa').addEventListener('click', function() {
function doStampa() {
var params = [];
var s = $('#filterStatus').val();
var d = $('#filterDepartment').val();
@@ -1100,7 +1235,10 @@ $today = date('Y-m-d');
if (chkDates.length >= 1) params.push('check_from=' + formatDate(chkDates[0]));
if (chkDates.length >= 2) params.push('check_to=' + formatDate(chkDates[1]));
window.open('scadenzario/print.php' + (params.length ? '?' + params.join('&') : ''), '_blank');
});
}
document.getElementById('btnStampa').addEventListener('click', doStampa);
var btnStampaMobile = document.getElementById('btnStampaMobile');
if (btnStampaMobile) btnStampaMobile.addEventListener('click', doStampa);
});
</script>
</body>