Subject CRUD
This commit is contained in:
@@ -3,24 +3,46 @@
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$stmt = $pdo->query("
|
||||
// Optional filter: show only deadlines of a given subject (used by "Storico" link from subjects CRUD)
|
||||
$filterSubjectId = isset($_GET['subject_id']) && is_numeric($_GET['subject_id']) ? (int)$_GET['subject_id'] : null;
|
||||
$filterSubjectName = null;
|
||||
if ($filterSubjectId) {
|
||||
$s = $pdo->prepare("SELECT name FROM scad_subjects WHERE id = ?");
|
||||
$s->execute([$filterSubjectId]);
|
||||
$filterSubjectName = $s->fetchColumn() ?: null;
|
||||
if (!$filterSubjectName) { $filterSubjectId = null; }
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT d.*,
|
||||
s.name AS subject_name,
|
||||
s.color AS subject_color,
|
||||
GROUP_CONCAT(DISTINCT CONCAT(e.first_name, ' ', e.last_name) ORDER BY e.first_name SEPARATOR ', ') as responsabili,
|
||||
GROUP_CONCAT(DISTINCT e.department ORDER BY e.department SEPARATOR ', ') as reparti_persone,
|
||||
d.departments as reparti_assegnati,
|
||||
(SELECT COUNT(*) FROM scad_deadline_attachments att WHERE att.deadline_id = d.id) as attachment_count
|
||||
FROM scad_deadlines d
|
||||
LEFT JOIN scad_subjects s ON s.id = d.subject_id
|
||||
LEFT JOIN scad_deadline_employee de ON de.deadline_id = d.id
|
||||
LEFT JOIN employees e ON e.id = de.employee_id
|
||||
GROUP BY d.id
|
||||
ORDER BY (d.status = 'completed') ASC, d.due_date ASC
|
||||
");
|
||||
";
|
||||
$params = [];
|
||||
if ($filterSubjectId) {
|
||||
$sql .= " WHERE d.subject_id = ?";
|
||||
$params[] = $filterSubjectId;
|
||||
}
|
||||
$sql .= " GROUP BY d.id ORDER BY (d.status = 'completed') ASC, d.due_date ASC";
|
||||
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$deadlines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$employees = $pdo->query("SELECT id, first_name, last_name, department FROM employees WHERE status = 'active' ORDER BY first_name")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$departments = $pdo->query("SELECT DISTINCT department FROM employees WHERE department IS NOT NULL AND department != '' ORDER BY department")->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
$subjects = $pdo->query("SELECT id, name, color FROM scad_subjects ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$today = date('Y-m-d');
|
||||
?>
|
||||
<!doctype html>
|
||||
@@ -137,6 +159,32 @@ $today = date('Y-m-d');
|
||||
#deadlinesTable tbody tr.row-completed { opacity: 0.6; }
|
||||
#deadlinesTable tbody tr:hover { filter: brightness(0.97); }
|
||||
|
||||
/* Subject color stripe */
|
||||
#deadlinesTable tbody tr[data-subject-color] td:first-child {
|
||||
border-left: 4px solid var(--subject-color, transparent);
|
||||
}
|
||||
.subject-chip {
|
||||
display: inline-block;
|
||||
padding: 0.15rem 0.55rem;
|
||||
border-radius: 1rem;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 600;
|
||||
color: #fff;
|
||||
letter-spacing: 0.02em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.deadline-card[data-subject-color] {
|
||||
border-left: 4px solid var(--subject-color, var(--scad-card-border));
|
||||
}
|
||||
|
||||
/* Filter banner (subject history mode) */
|
||||
.subject-filter-banner {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
gap: 0.75rem; padding: 0.75rem 1rem;
|
||||
background: var(--scad-card-bg); border: 1px solid var(--scad-card-border);
|
||||
border-radius: 0.5rem; margin-bottom: 1rem; font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* Filter bar */
|
||||
.filter-bar .form-select {
|
||||
border-radius: 0.5rem;
|
||||
@@ -244,19 +292,46 @@ $today = date('Y-m-d');
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<?php if ($filterSubjectId): ?>
|
||||
<div class="subject-filter-banner">
|
||||
<div>
|
||||
<i class="fa-solid fa-filter me-2"></i>
|
||||
<strong>Storico per argomento:</strong>
|
||||
<?= htmlspecialchars($filterSubjectName, ENT_QUOTES, 'UTF-8') ?>
|
||||
<span class="text-muted ms-2">(tutte le scadenze — aperte e chiuse)</span>
|
||||
</div>
|
||||
<a href="scadenzario/index.php" class="btn btn-sm btn-light border">
|
||||
<i class="fa-solid fa-xmark me-1"></i> Rimuovi filtro
|
||||
</a>
|
||||
</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>
|
||||
@@ -294,6 +369,9 @@ $today = date('Y-m-d');
|
||||
<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>
|
||||
<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">
|
||||
<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">
|
||||
<i class="fa-solid fa-calendar-days"></i><span>Calendario</span>
|
||||
</a>
|
||||
@@ -354,12 +432,14 @@ $today = date('Y-m-d');
|
||||
<div class="deadline-card <?= $row['_cardClass'] ?>"
|
||||
data-id="<?= (int)$row['id'] ?>"
|
||||
data-status="<?= $row['_statusKey'] ?>"
|
||||
data-subject-id="<?= (int)($row['subject_id'] ?? 0) ?>"
|
||||
<?php if (!empty($row['subject_color'])): ?>data-subject-color="<?= htmlspecialchars($row['subject_color'], ENT_QUOTES, 'UTF-8') ?>" style="--subject-color: <?= htmlspecialchars($row['subject_color'], ENT_QUOTES, 'UTF-8') ?>"<?php endif; ?>
|
||||
data-department="<?= htmlspecialchars($row['reparti'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
|
||||
data-employees="<?= htmlspecialchars($row['responsabili'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
|
||||
data-due-date="<?= htmlspecialchars($row['due_date'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
|
||||
data-check-date="<?= htmlspecialchars($row['check_date'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
|
||||
<?php if ($row['category']): ?>
|
||||
<div class="text-muted mb-1" style="font-size:0.75rem;font-weight:600;text-transform:uppercase;letter-spacing:0.03em"><?= htmlspecialchars($row['category'], ENT_QUOTES, 'UTF-8') ?></div>
|
||||
<?php if (!empty($row['subject_name'])): ?>
|
||||
<div class="mb-1"><span class="subject-chip" style="background: <?= htmlspecialchars($row['subject_color'] ?: '#6c757d', ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($row['subject_name'], ENT_QUOTES, 'UTF-8') ?></span></div>
|
||||
<?php endif; ?>
|
||||
<div class="d-flex justify-content-between align-items-start mb-1">
|
||||
<a href="scadenzario/detail.php?id=<?= (int)$row['id'] ?>" class="card-topic text-decoration-none"><?= htmlspecialchars($row['topic'], ENT_QUOTES, 'UTF-8') ?></a>
|
||||
@@ -414,11 +494,19 @@ $today = date('Y-m-d');
|
||||
<tr class="<?= $row['_rowClass'] ?>"
|
||||
data-id="<?= (int)$row['id'] ?>"
|
||||
data-status="<?= $row['_statusKey'] ?>"
|
||||
data-subject-id="<?= (int)($row['subject_id'] ?? 0) ?>"
|
||||
<?php if (!empty($row['subject_color'])): ?>data-subject-color="<?= htmlspecialchars($row['subject_color'], ENT_QUOTES, 'UTF-8') ?>" style="--subject-color: <?= htmlspecialchars($row['subject_color'], ENT_QUOTES, 'UTF-8') ?>"<?php endif; ?>
|
||||
data-department="<?= htmlspecialchars($row['reparti'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
|
||||
data-employees="<?= htmlspecialchars($row['responsabili'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
|
||||
data-due-date="<?= htmlspecialchars($row['due_date'] ?? '', ENT_QUOTES, 'UTF-8') ?>"
|
||||
data-check-date="<?= htmlspecialchars($row['check_date'] ?? '', ENT_QUOTES, 'UTF-8') ?>">
|
||||
<td><?= htmlspecialchars($row['category'] ?? '—', ENT_QUOTES, 'UTF-8') ?></td>
|
||||
<td>
|
||||
<?php if (!empty($row['subject_name'])): ?>
|
||||
<span class="subject-chip" style="background: <?= htmlspecialchars($row['subject_color'] ?: '#6c757d', ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($row['subject_name'], ENT_QUOTES, 'UTF-8') ?></span>
|
||||
<?php else: ?>
|
||||
<span class="text-muted">—</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="scadenzario/detail.php?id=<?= (int)$row['id'] ?>" class="fw-semibold text-decoration-none" style="color:var(--scad-heading)"><?= htmlspecialchars($row['topic'], ENT_QUOTES, 'UTF-8') ?></a>
|
||||
<?php if ((int)$row['attachment_count'] > 0): ?>
|
||||
@@ -476,15 +564,18 @@ $today = date('Y-m-d');
|
||||
<div class="form-section-title">Informazioni principali</div>
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="dlCategory" class="form-label fw-semibold">Argomento</label>
|
||||
<input type="text" class="form-control" id="dlCategory" name="category" maxlength="100" list="categoryList" placeholder="es. Sicurezza, Ambiente, Antincendio...">
|
||||
<datalist id="categoryList">
|
||||
<?php
|
||||
$cats = $pdo->query("SELECT DISTINCT category FROM scad_deadlines WHERE category IS NOT NULL AND category != '' ORDER BY category")->fetchAll(PDO::FETCH_COLUMN);
|
||||
foreach ($cats as $c): ?>
|
||||
<option value="<?= htmlspecialchars($c, ENT_QUOTES, 'UTF-8') ?>">
|
||||
<?php endforeach; ?>
|
||||
</datalist>
|
||||
<label for="dlSubject" class="form-label fw-semibold">Argomento</label>
|
||||
<div class="d-flex gap-2">
|
||||
<select class="form-select" id="dlSubject" name="subject_id" style="flex:1">
|
||||
<option value="">— Nessuno —</option>
|
||||
<?php foreach ($subjects as $s): ?>
|
||||
<option value="<?= (int)$s['id'] ?>" data-color="<?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"><?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<a href="scadenzario/subjects/index.php" target="_blank" class="btn btn-scad-outline" title="Gestisci argomenti">
|
||||
<i class="fa-solid fa-gear"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<label for="dlLaw" class="form-label fw-semibold">Legge / Articolo</label>
|
||||
@@ -617,6 +708,15 @@ $today = date('Y-m-d');
|
||||
var fpCheck = flatpickr('#filterCheckRange', fpOpts);
|
||||
|
||||
// --- Select2 ---
|
||||
$('#dlSubject').select2({
|
||||
theme: 'bootstrap-5',
|
||||
placeholder: 'Seleziona argomento...',
|
||||
allowClear: true,
|
||||
dropdownParent: $('#deadlineModal .modal-body'),
|
||||
language: 'it',
|
||||
width: '100%'
|
||||
});
|
||||
|
||||
$('#dlDepartments').select2({
|
||||
theme: 'bootstrap-5',
|
||||
placeholder: 'Seleziona reparti...',
|
||||
@@ -645,6 +745,8 @@ $today = date('Y-m-d');
|
||||
if (statusFilter === 'non-completata') { if (row.getAttribute('data-status') === 'completata') return false; }
|
||||
else if (statusFilter && row.getAttribute('data-status') !== statusFilter) return false;
|
||||
if (deptFilter && (row.getAttribute('data-department') || '').indexOf(deptFilter) === -1) return false;
|
||||
var subjFilter = $('#filterSubject').val();
|
||||
if (subjFilter && String(row.getAttribute('data-subject-id')) !== String(subjFilter)) return false;
|
||||
var empFilter = $('#filterEmployee').val();
|
||||
if (empFilter && (row.getAttribute('data-employees') || '').indexOf(empFilter) === -1) return false;
|
||||
var dueDates = fpDue.selectedDates;
|
||||
@@ -677,11 +779,13 @@ $today = date('Y-m-d');
|
||||
function filterCards() {
|
||||
var statusVal = $('#filterStatus').val();
|
||||
var deptVal = $('#filterDepartment').val();
|
||||
var subjVal = $('#filterSubject').val();
|
||||
$('#mobileCards .deadline-card').each(function() {
|
||||
var show = true;
|
||||
if (statusVal === 'non-completata') { if ($(this).data('status') === 'completata') show = false; }
|
||||
else if (statusVal && $(this).data('status') !== statusVal) show = false;
|
||||
if (deptVal && ($(this).data('department') || '').indexOf(deptVal) === -1) show = false;
|
||||
if (subjVal && String($(this).data('subject-id')) !== String(subjVal)) show = false;
|
||||
var empVal = $('#filterEmployee').val();
|
||||
if (empVal && ($(this).data('employees') || '').indexOf(empVal) === -1) show = false;
|
||||
var dueDates = fpDue.selectedDates;
|
||||
@@ -696,7 +800,7 @@ $today = date('Y-m-d');
|
||||
});
|
||||
}
|
||||
|
||||
$('#filterStatus, #filterDepartment, #filterEmployee').on('change', function() {
|
||||
$('#filterStatus, #filterDepartment, #filterEmployee, #filterSubject').on('change', function() {
|
||||
if (table) table.draw();
|
||||
filterCards();
|
||||
});
|
||||
@@ -704,6 +808,7 @@ $today = date('Y-m-d');
|
||||
$('#filterStatus').val('non-completata');
|
||||
$('#filterDepartment').val('');
|
||||
$('#filterEmployee').val('');
|
||||
$('#filterSubject').val('');
|
||||
fpDue.clear();
|
||||
fpCheck.clear();
|
||||
if (table) table.draw();
|
||||
@@ -725,6 +830,7 @@ $today = date('Y-m-d');
|
||||
document.getElementById('dlNotifDays').value = '7';
|
||||
document.getElementById('modalTitle').textContent = 'Nuova Scadenza';
|
||||
document.getElementById('dlFiles').value = '';
|
||||
$('#dlSubject').val('').trigger('change');
|
||||
$('#dlDepartments').val(null).trigger('change');
|
||||
$('#dlEmployees').val(null).trigger('change');
|
||||
renderAttachments([]);
|
||||
@@ -839,7 +945,7 @@ $today = date('Y-m-d');
|
||||
if (!data.success) { Swal.fire('Errore', data.message, 'error'); return; }
|
||||
var d = data.data;
|
||||
document.getElementById('dlId').value = d.id;
|
||||
document.getElementById('dlCategory').value = d.category || '';
|
||||
$('#dlSubject').val(d.subject_id || '').trigger('change');
|
||||
document.getElementById('dlTopic').value = d.topic || '';
|
||||
document.getElementById('dlLaw').value = d.law_regulation || '';
|
||||
document.getElementById('dlRecurrence').value = d.recurrence_type || 'once';
|
||||
@@ -927,7 +1033,7 @@ $today = date('Y-m-d');
|
||||
if (!data.success) return;
|
||||
var d = data.data;
|
||||
document.getElementById('dlId').value = d.id;
|
||||
document.getElementById('dlCategory').value = d.category || '';
|
||||
$('#dlSubject').val(d.subject_id || '').trigger('change');
|
||||
document.getElementById('dlTopic').value = d.topic || '';
|
||||
document.getElementById('dlLaw').value = d.law_regulation || '';
|
||||
document.getElementById('dlRecurrence').value = d.recurrence_type || 'once';
|
||||
|
||||
Reference in New Issue
Block a user