347 lines
20 KiB
PHP
347 lines
20 KiB
PHP
<?php include('../../include/headscript.php'); ?>
|
|
<?php
|
|
$db = DBHandlerSelect::getInstance();
|
|
$pdo = $db->getConnection();
|
|
|
|
$subjects = $pdo->query("
|
|
SELECT s.*,
|
|
(SELECT COUNT(*) FROM scad_deadlines d WHERE d.subject_id = s.id) AS deadline_count,
|
|
(SELECT COUNT(*) FROM scad_deadlines d WHERE d.subject_id = s.id AND d.status <> 'completed') AS open_count
|
|
FROM scad_subjects s
|
|
ORDER BY s.name
|
|
")->fetchAll(PDO::FETCH_ASSOC);
|
|
?>
|
|
<!doctype html>
|
|
<html lang="it">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<?php
|
|
$scriptDir = dirname($_SERVER['SCRIPT_NAME']);
|
|
// subjects/index.php -> scadenzario -> userarea
|
|
$baseHref = dirname(dirname($scriptDir)) . '/';
|
|
?>
|
|
<base href="<?= $baseHref ?>">
|
|
<?php include('../../cssinclude.php'); ?>
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<title>Scadenzario - Argomenti</title>
|
|
<script>if(window.innerWidth>1024)document.addEventListener('DOMContentLoaded',function(){document.getElementById('appWrapper').classList.add('toggled')})</script>
|
|
<style>
|
|
:root {
|
|
--scad-primary: #5a8fd8;
|
|
--scad-primary-hover: #4578c0;
|
|
--scad-heading: #2c3e6b;
|
|
--scad-card-bg: linear-gradient(135deg, #f0f4ff 0%, #e8eeff 100%);
|
|
--scad-card-border: #dde4f0;
|
|
}
|
|
.scad-card { border: none; border-radius: 0.75rem; box-shadow: 0 2px 12px rgba(0,0,0,0.06); overflow: hidden; }
|
|
.scad-card .card-header { background: var(--scad-card-bg); border-bottom: 1px solid var(--scad-card-border); padding: 1rem 1.25rem; }
|
|
.scad-card .card-header h5 { font-weight: 700; color: var(--scad-heading); margin: 0; font-size: 1.1rem; letter-spacing: -0.01em; }
|
|
.scad-card .card-body { padding: 1.25rem; }
|
|
|
|
.btn-scad-primary { background: var(--scad-primary); border: none; color: #fff; font-weight: 600; font-size: 0.85rem; padding: 0.5rem 1rem; border-radius: 0.5rem; transition: all 0.2s; }
|
|
.btn-scad-primary:hover { background: var(--scad-primary-hover); color: #fff; transform: translateY(-1px); box-shadow: 0 4px 12px rgba(90,143,216,0.35); }
|
|
.btn-scad-outline { background: transparent; border: 1.5px solid var(--scad-primary); color: var(--scad-primary); font-weight: 600; font-size: 0.85rem; padding: 0.45rem 1rem; border-radius: 0.5rem; transition: all 0.2s; }
|
|
.btn-scad-outline:hover { background: var(--scad-primary); color: #fff; transform: translateY(-1px); }
|
|
|
|
.btn-action { width: 32px; height: 32px; padding: 0; display: inline-flex; align-items: center; justify-content: center; border: none; border-radius: 0.4rem; font-size: 0.85rem; transition: all 0.15s; }
|
|
.btn-action-edit { background: rgba(90,143,216,0.12); color: var(--scad-primary); }
|
|
.btn-action-edit:hover { background: var(--scad-primary); color: #fff; }
|
|
.btn-action-delete { background: rgba(220,53,69,0.12); color: #dc3545; }
|
|
.btn-action-delete:hover { background: #dc3545; color: #fff; }
|
|
.btn-action-history { background: rgba(108,117,125,0.12); color: #495057; }
|
|
.btn-action-history:hover { background: #495057; color: #fff; }
|
|
|
|
.color-swatch { width: 28px; height: 28px; border-radius: 6px; display: inline-block; border: 1px solid rgba(0,0,0,0.08); vertical-align: middle; }
|
|
.subject-row { border-left: 4px solid var(--row-color, #e9ecef); }
|
|
|
|
/* Mobile cards */
|
|
.subject-card {
|
|
background: #fff;
|
|
border: 1px solid var(--scad-card-border);
|
|
border-left: 5px solid var(--row-color, #e9ecef);
|
|
border-radius: 0.6rem;
|
|
padding: 0.85rem 0.95rem;
|
|
margin-bottom: 0.6rem;
|
|
box-shadow: 0 1px 3px rgba(0,0,0,0.04);
|
|
}
|
|
.subject-card .sc-header {
|
|
display: flex; align-items: center; gap: 0.6rem;
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.subject-card .sc-swatch {
|
|
width: 22px; height: 22px; border-radius: 5px;
|
|
border: 1px solid rgba(0,0,0,0.08); flex-shrink: 0;
|
|
}
|
|
.subject-card .sc-name {
|
|
font-weight: 700; color: var(--scad-heading);
|
|
font-size: 0.95rem; flex: 1; word-break: break-word;
|
|
}
|
|
.subject-card .sc-stats {
|
|
display: flex; gap: 0.75rem; font-size: 0.8rem; color: #6c757d;
|
|
margin-bottom: 0.6rem;
|
|
}
|
|
.subject-card .sc-stats strong { color: var(--scad-heading); }
|
|
.subject-card .sc-actions {
|
|
display: flex; gap: 0.4rem; justify-content: flex-end;
|
|
}
|
|
|
|
.empty-state { text-align: center; padding: 3rem 1rem; color: #6c757d; }
|
|
.empty-state i { font-size: 3rem; opacity: 0.3; margin-bottom: 1rem; }
|
|
|
|
/* Color picker swatches */
|
|
.color-picker-grid { display: grid; grid-template-columns: repeat(10, 1fr); gap: 0.4rem; margin-bottom: 0.75rem; }
|
|
.color-picker-swatch { width: 100%; aspect-ratio: 1; border-radius: 6px; cursor: pointer; border: 2px solid transparent; transition: all 0.15s; }
|
|
.color-picker-swatch:hover { transform: scale(1.1); }
|
|
.color-picker-swatch.selected { border-color: #2c3e6b; transform: scale(1.1); box-shadow: 0 2px 8px rgba(44,62,107,0.3); }
|
|
|
|
@media (max-width: 575.98px) {
|
|
.scad-card .card-header { flex-direction: column; gap: 0.75rem; align-items: flex-start !important; }
|
|
.header-actions { width: 100%; }
|
|
.header-actions .btn { width: 100%; justify-content: center; }
|
|
}
|
|
</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">
|
|
|
|
<nav aria-label="breadcrumb" class="mb-3">
|
|
<ol class="breadcrumb" style="background:transparent;padding:0;margin:0;font-size:0.85rem">
|
|
<li class="breadcrumb-item"><a href="scadenzario/index.php">Scadenzario</a></li>
|
|
<li class="breadcrumb-item active" aria-current="page">Argomenti</li>
|
|
</ol>
|
|
</nav>
|
|
|
|
<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-tags me-2"></i>Argomenti</h5>
|
|
<div class="header-actions d-flex gap-2 flex-wrap">
|
|
<a href="scadenzario/index.php" class="btn btn-scad-outline d-inline-flex align-items-center gap-2">
|
|
<i class="fa-solid fa-arrow-left"></i><span>Scadenzario</span>
|
|
</a>
|
|
<button class="btn btn-scad-primary d-inline-flex align-items-center gap-2" id="btnAddSubject">
|
|
<i class="fa-solid fa-plus"></i><span>Nuovo Argomento</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<?php if (count($subjects) === 0): ?>
|
|
<div class="empty-state">
|
|
<i class="fa-solid fa-tags"></i>
|
|
<p>Nessun argomento definito.<br>Clicca <strong>"Nuovo Argomento"</strong> per aggiungere il primo.</p>
|
|
</div>
|
|
<?php else: ?>
|
|
<div id="subjectsList">
|
|
<!-- MOBILE: Cards (< md) -->
|
|
<div class="d-md-none">
|
|
<?php foreach ($subjects as $s): ?>
|
|
<div class="subject-card"
|
|
style="--row-color: <?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"
|
|
data-id="<?= (int)$s['id'] ?>"
|
|
data-name="<?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?>"
|
|
data-color="<?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"
|
|
data-in-use="<?= (int)$s['deadline_count'] ?>">
|
|
<div class="sc-header">
|
|
<span class="sc-swatch" style="background: <?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"></span>
|
|
<span class="sc-name"><?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?></span>
|
|
</div>
|
|
<div class="sc-stats">
|
|
<span>Scadenze: <strong><?= (int)$s['deadline_count'] ?></strong></span>
|
|
<span>Aperte: <strong><?= (int)$s['open_count'] ?></strong></span>
|
|
</div>
|
|
<div class="sc-actions">
|
|
<a href="scadenzario/index.php?subject_id=<?= (int)$s['id'] ?>" class="btn-action btn-action-history" title="Storico scadenze">
|
|
<i class="fa-solid fa-clock-rotate-left"></i>
|
|
</a>
|
|
<button class="btn-action btn-action-edit btn-edit" title="Modifica"><i class="fa-solid fa-pen"></i></button>
|
|
<button class="btn-action btn-action-delete btn-delete" title="Elimina"><i class="fa-solid fa-trash"></i></button>
|
|
</div>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
|
|
<!-- DESKTOP: Table (>= md) -->
|
|
<div class="d-none d-md-block">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:60px">Colore</th>
|
|
<th>Nome</th>
|
|
<th class="text-center" style="width:120px">Scadenze</th>
|
|
<th class="text-center" style="width:120px">Aperte</th>
|
|
<th class="text-center" style="width:180px">Azioni</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($subjects as $s): ?>
|
|
<tr class="subject-row"
|
|
style="--row-color: <?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"
|
|
data-id="<?= (int)$s['id'] ?>"
|
|
data-name="<?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?>"
|
|
data-color="<?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"
|
|
data-in-use="<?= (int)$s['deadline_count'] ?>">
|
|
<td><span class="color-swatch" style="background: <?= htmlspecialchars($s['color'], ENT_QUOTES, 'UTF-8') ?>"></span></td>
|
|
<td class="fw-semibold" style="color:var(--scad-heading)"><?= htmlspecialchars($s['name'], ENT_QUOTES, 'UTF-8') ?></td>
|
|
<td class="text-center"><?= (int)$s['deadline_count'] ?></td>
|
|
<td class="text-center"><?= (int)$s['open_count'] ?></td>
|
|
<td class="text-center">
|
|
<div class="d-inline-flex gap-1">
|
|
<a href="scadenzario/index.php?subject_id=<?= (int)$s['id'] ?>" class="btn-action btn-action-history" title="Storico scadenze">
|
|
<i class="fa-solid fa-clock-rotate-left"></i>
|
|
</a>
|
|
<button class="btn-action btn-action-edit btn-edit" title="Modifica"><i class="fa-solid fa-pen"></i></button>
|
|
<button class="btn-action btn-action-delete btn-delete" title="Elimina"><i class="fa-solid fa-trash"></i></button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
<?php include('../../include/footer.php'); ?>
|
|
</div>
|
|
|
|
<!-- Subject Modal -->
|
|
<div class="modal fade" id="subjectModal" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title" id="subjectModalTitle">Nuovo Argomento</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Chiudi"></button>
|
|
</div>
|
|
<form id="subjectForm">
|
|
<div class="modal-body">
|
|
<input type="hidden" id="subjId" name="id" value="">
|
|
<div class="mb-3">
|
|
<label for="subjName" class="form-label fw-semibold">Nome <span class="text-danger">*</span></label>
|
|
<input type="text" class="form-control" id="subjName" name="name" maxlength="100" required>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label class="form-label fw-semibold">Colore</label>
|
|
<div class="color-picker-grid" id="colorPickerGrid"></div>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<input type="color" class="form-control form-control-color" id="subjColor" name="color" value="#6c757d" style="width:56px;height:38px;padding:2px">
|
|
<input type="text" class="form-control" id="subjColorText" maxlength="7" placeholder="#RRGGBB" style="max-width:130px;font-family:monospace">
|
|
<span class="text-muted small">Personalizzato</span>
|
|
</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-scad-primary">Salva</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php include('../../jsinclude.php'); ?>
|
|
<script>
|
|
$(function () {
|
|
const PRESET_COLORS = [
|
|
'#dc3545','#e8930c','#ffc107','#198754','#20c997',
|
|
'#0dcaf0','#0d6efd','#5a8fd8','#6f42c1','#d63384',
|
|
'#6c757d','#495057','#212529','#8b4513','#795548',
|
|
'#b88a44','#e83e8c','#17a2b8','#28a745','#343a40'
|
|
];
|
|
|
|
function buildPicker(selected) {
|
|
const $grid = $('#colorPickerGrid').empty();
|
|
PRESET_COLORS.forEach(c => {
|
|
const $sw = $('<div class="color-picker-swatch"></div>')
|
|
.css('background', c)
|
|
.attr('data-color', c);
|
|
if (c.toLowerCase() === (selected || '').toLowerCase()) $sw.addClass('selected');
|
|
$sw.on('click', function () {
|
|
$('#colorPickerGrid .color-picker-swatch').removeClass('selected');
|
|
$(this).addClass('selected');
|
|
$('#subjColor').val(c);
|
|
$('#subjColorText').val(c);
|
|
});
|
|
$grid.append($sw);
|
|
});
|
|
}
|
|
|
|
function openModal(data) {
|
|
const isEdit = !!data;
|
|
$('#subjectModalTitle').text(isEdit ? 'Modifica Argomento' : 'Nuovo Argomento');
|
|
$('#subjId').val(isEdit ? data.id : '');
|
|
$('#subjName').val(isEdit ? data.name : '');
|
|
const color = isEdit ? data.color : '#6c757d';
|
|
$('#subjColor').val(color);
|
|
$('#subjColorText').val(color);
|
|
buildPicker(color);
|
|
new bootstrap.Modal('#subjectModal').show();
|
|
}
|
|
|
|
$('#btnAddSubject').on('click', () => openModal(null));
|
|
|
|
$('#subjectsList').on('click', '.btn-edit', function () {
|
|
const $tr = $(this).closest('[data-id]');
|
|
openModal({ id: $tr.data('id'), name: $tr.data('name'), color: $tr.data('color') });
|
|
});
|
|
|
|
$('#subjectsList').on('click', '.btn-delete', function () {
|
|
const $tr = $(this).closest('[data-id]');
|
|
const inUse = parseInt($tr.data('in-use') || 0, 10);
|
|
const name = $tr.data('name');
|
|
if (inUse > 0) {
|
|
Swal.fire({ icon: 'warning', title: 'Impossibile eliminare',
|
|
text: `L'argomento "${name}" è utilizzato in ${inUse} scadenz${inUse === 1 ? 'a' : 'e'}.` });
|
|
return;
|
|
}
|
|
Swal.fire({
|
|
title: `Eliminare "${name}"?`,
|
|
icon: 'warning', showCancelButton: true,
|
|
confirmButtonText: 'Elimina', cancelButtonText: 'Annulla',
|
|
confirmButtonColor: '#dc3545'
|
|
}).then(r => {
|
|
if (!r.isConfirmed) return;
|
|
$.post('scadenzario/subjects/ajax/delete_subject.php', { id: $tr.data('id') })
|
|
.done(res => {
|
|
if (res.success) { location.reload(); }
|
|
else { Swal.fire({ icon: 'error', title: 'Errore', text: res.message }); }
|
|
})
|
|
.fail(() => Swal.fire({ icon: 'error', title: 'Errore di rete' }));
|
|
});
|
|
});
|
|
|
|
$('#subjColor').on('input', function () { $('#subjColorText').val($(this).val()); });
|
|
$('#subjColorText').on('input', function () {
|
|
const v = $(this).val();
|
|
if (/^#[0-9A-Fa-f]{6}$/.test(v)) $('#subjColor').val(v);
|
|
});
|
|
|
|
$('#subjectForm').on('submit', function (e) {
|
|
e.preventDefault();
|
|
const payload = {
|
|
id: $('#subjId').val(),
|
|
name: $('#subjName').val().trim(),
|
|
color: $('#subjColor').val()
|
|
};
|
|
if (!payload.name) { Swal.fire({ icon: 'warning', title: 'Nome obbligatorio' }); return; }
|
|
$.post('scadenzario/subjects/ajax/save_subject.php', payload)
|
|
.done(res => {
|
|
if (res.success) { location.reload(); }
|
|
else { Swal.fire({ icon: 'error', title: 'Errore', text: res.message }); }
|
|
})
|
|
.fail(() => Swal.fire({ icon: 'error', title: 'Errore di rete' }));
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|