feat: implement modal functionality for linking teachers and adding new classes with improved UI
All checks were successful
Build & Push / Build & Push image (push) Successful in 41s
All checks were successful
Build & Push / Build & Push image (push) Successful in 41s
This commit is contained in:
@@ -95,6 +95,21 @@
|
||||
.notification.success { background: var(--success); }
|
||||
.notification.error { background: var(--danger); }
|
||||
.notification.warning { background: var(--warning); }
|
||||
.modal-overlay { display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1000;align-items:center;justify-content:center; }
|
||||
.modal-overlay.active { display:flex; }
|
||||
.modal-inner { background:white;border-radius:12px;padding:1.5rem;max-width:440px;width:90%;box-shadow:0 20px 60px rgba(0,0,0,.3); }
|
||||
.modal-inner h2 { font-size:1.05rem;margin-bottom:.35rem; }
|
||||
.modal-inner p { font-size:.82rem;color:var(--gray-500);margin-bottom:1rem; }
|
||||
.modal-buttons { display:flex;gap:.5rem;justify-content:flex-end;margin-top:1.1rem; }
|
||||
.form-input { width:100%;padding:.55rem .75rem;border:1px solid var(--gray-300);border-radius:6px;font-size:.9rem;margin-top:.5rem; }
|
||||
.form-input:focus { outline:none;border-color:var(--primary);box-shadow:0 0 0 3px rgba(79,70,229,.1); }
|
||||
.form-error { font-size:.82rem;color:var(--danger);margin-top:.5rem;min-height:1.2em; }
|
||||
.klas-chip-card { background:var(--gray-50);border:1px solid var(--gray-200);border-radius:8px;padding:.6rem .85rem;display:flex;align-items:center;justify-content:space-between;gap:.5rem; }
|
||||
.klas-chip-card .klas-info { flex:1;cursor:pointer; }
|
||||
.klas-chip-card .klas-name { font-weight:600;color:var(--gray-800); }
|
||||
.klas-chip-card .klas-teachers { font-size:.75rem;color:var(--gray-500);margin-top:.15rem; }
|
||||
.klas-chip-card .btn-delete { background:none;border:1px solid var(--gray-200);border-radius:6px;padding:.3rem .45rem;cursor:pointer;color:var(--gray-400);font-size:.85rem;transition:all .15s;flex-shrink:0; }
|
||||
.klas-chip-card .btn-delete:hover { background:var(--danger);border-color:var(--danger);color:white; }
|
||||
.legend-footer { padding: .85rem 1.25rem; border-top: 1px solid var(--gray-200); display: flex; gap: 1.25rem; flex-wrap: wrap; font-size: .78rem; color: var(--gray-600); }
|
||||
.legend-item { display: flex; align-items: center; gap: .35rem; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@@ -114,11 +129,12 @@
|
||||
.btn-secondary { background:#334155!important;color:#e2e8f0!important; }
|
||||
.diff-row-same { background:#064e3b!important; }
|
||||
.diff-row-differ { background:#451a03!important; }
|
||||
#koppelingModal > div { background:#1e293b!important;color:#e2e8f0!important; }
|
||||
.modal-overlay .modal-inner { background:#1e293b!important;color:#e2e8f0!important; }
|
||||
#koppelingCheckboxes label { color:#e2e8f0!important; }
|
||||
#koppelingCheckboxes label:hover { background:#263548!important; }
|
||||
#koppelingCheckboxes span { color:#94a3b8!important; }
|
||||
#koppelingCheckboxes input[type=checkbox] { accent-color:var(--primary); }
|
||||
.modal-overlay .form-input { background:#0f172a!important;color:#e2e8f0!important;border-color:#334155!important; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
@@ -262,18 +278,32 @@
|
||||
</div>
|
||||
|
||||
<!-- Modal: leerkrachten koppelen -->
|
||||
<div id="koppelingModal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1000;align-items:center;justify-content:center;">
|
||||
<div style="background:white;border-radius:12px;padding:1.5rem;max-width:420px;width:90%;box-shadow:0 20px 60px rgba(0,0,0,.3);">
|
||||
<h2 style="font-size:1.1rem;margin-bottom:.5rem;" id="koppelingModalTitle">Leerkrachten koppelen</h2>
|
||||
<p style="font-size:.82rem;color:var(--gray-500);margin-bottom:1rem;">Selecteer de leerkrachten voor deze klas.</p>
|
||||
<div id="koppelingCheckboxes" style="display:flex;flex-direction:column;gap:.4rem;max-height:280px;overflow-y:auto;margin-bottom:1rem;"></div>
|
||||
<div style="display:flex;gap:.5rem;justify-content:flex-end;">
|
||||
<div id="koppelingModal" class="modal-overlay">
|
||||
<div class="modal-inner">
|
||||
<h2 id="koppelingModalTitle">Leerkrachten koppelen</h2>
|
||||
<p>Selecteer de leerkrachten voor deze klas.</p>
|
||||
<div id="koppelingCheckboxes" style="display:flex;flex-direction:column;gap:.4rem;max-height:280px;overflow-y:auto;"></div>
|
||||
<div class="modal-buttons">
|
||||
<button id="btnKoppelingAnnuleer" class="btn btn-secondary">Annuleren</button>
|
||||
<button id="btnKoppelingOpslaan" class="btn btn-primary">Opslaan</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: nieuwe klas -->
|
||||
<div id="nieuweKlasModal" class="modal-overlay">
|
||||
<div class="modal-inner">
|
||||
<h2>Nieuwe klas toevoegen</h2>
|
||||
<p>Geef een naam op voor de nieuwe klas (bv. "1A", "3B").</p>
|
||||
<input type="text" id="nieuweKlasNaam" class="form-input" placeholder="Naam van de klas...">
|
||||
<div class="form-error" id="nieuweKlasError"></div>
|
||||
<div class="modal-buttons">
|
||||
<button id="btnNieuweKlasAnnuleer" class="btn btn-secondary">Annuleren</button>
|
||||
<button id="btnNieuweKlasBevestig" class="btn btn-primary">Aanmaken</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="notification" id="notification"></div>
|
||||
@@ -321,8 +351,21 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||
// Export knoppen
|
||||
document.getElementById('btnExportCSV').addEventListener('click', exportCSV);
|
||||
document.getElementById('btnNieuweKlas').addEventListener('click', openNieuweKlasDialog);
|
||||
document.getElementById('btnNieuweKlasAnnuleer').addEventListener('click', () => {
|
||||
document.getElementById('nieuweKlasModal').classList.remove('active');
|
||||
});
|
||||
document.getElementById('btnNieuweKlasBevestig').addEventListener('click', bevestigNieuweKlas);
|
||||
document.getElementById('nieuweKlasNaam').addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') bevestigNieuweKlas();
|
||||
});
|
||||
// Sluit modals bij klik op overlay
|
||||
['koppelingModal','nieuweKlasModal'].forEach(id => {
|
||||
document.getElementById(id).addEventListener('click', e => {
|
||||
if (e.target.id === id) document.getElementById(id).classList.remove('active');
|
||||
});
|
||||
});
|
||||
document.getElementById('btnKoppelingAnnuleer').addEventListener('click', () => {
|
||||
document.getElementById('koppelingModal').style.display = 'none';
|
||||
document.getElementById('koppelingModal').classList.remove('active');
|
||||
});
|
||||
document.getElementById('btnKoppelingOpslaan').addEventListener('click', saveKoppeling);
|
||||
document.getElementById('btnExportPDF').addEventListener('click', exportPDF);
|
||||
@@ -470,7 +513,7 @@ function renderKlasProgress() {
|
||||
if (!klassen.length) { el.innerHTML = '<div class="empty">Geen klassen</div>'; return; }
|
||||
// Filter op leerkrachten (niet directeurs/ICT)
|
||||
const teacherUsers = allUsers.filter(u => u.role === 'teacher' || u.role === 'director');
|
||||
el.innerHTML = '<div class="vak-grid">' + klassen.map(k => {
|
||||
el.innerHTML = '<div style="display:flex;flex-direction:column;gap:.5rem;">' + klassen.map(k => {
|
||||
const vakken = byClass[k.id] || {};
|
||||
let g=0,o=0,r=0;
|
||||
Object.values(vakken).forEach(goals => Object.values(goals).forEach(s => {
|
||||
@@ -747,17 +790,14 @@ async function renderKoppelingTab() {
|
||||
|
||||
// Filter op leerkrachten (niet directeurs/ICT)
|
||||
const teacherUsers = allUsers.filter(u => u.role === 'teacher' || u.role === 'director');
|
||||
el.innerHTML = '<div class="vak-grid">' + klassen.map(k => {
|
||||
el.innerHTML = '<div style="display:flex;flex-direction:column;gap:.5rem;">' + klassen.map(k => {
|
||||
const teachers = (k.teachers||[]).map(t => t.full_name).join(', ') || '<em style="color:var(--gray-400)">Geen leerkrachten</em>';
|
||||
return `<div class="vak-card" style="position:relative;">
|
||||
<div class="vak-card-header" style="cursor:pointer;" data-action="openKoppeling" data-id="${k.id}" data-name="${k.name.replace(/"/g,'"')}" data-teachers="${JSON.stringify((k.teachers||[]).map(t=>t.id)).replace(/"/g,'"')}">
|
||||
<h3>🏫 ${k.name}</h3>
|
||||
<span style="font-size:.75rem;background:var(--primary);color:white;padding:.2rem .5rem;border-radius:4px;">Wijzigen</span>
|
||||
return `<div class="klas-chip-card">
|
||||
<div class="klas-info" data-action="openKoppeling" data-id="${k.id}" data-name="${k.name.replace(/"/g,'"')}" data-teachers="${JSON.stringify((k.teachers||[]).map(t=>t.id)).replace(/"/g,'"')}">
|
||||
<div class="klas-name">🏫 ${k.name} <span style="font-size:.72rem;background:var(--primary);color:white;padding:.1rem .4rem;border-radius:4px;margin-left:.35rem;">Wijzigen</span></div>
|
||||
<div class="klas-teachers">${teachers}</div>
|
||||
</div>
|
||||
<div style="font-size:.82rem;color:var(--gray-600);margin-top:.4rem;">${teachers}</div>
|
||||
<button data-action="deleteKlas" data-id="${k.id}" data-name="${k.name.replace(/"/g,'"')}"
|
||||
style="position:absolute;top:.5rem;right:.5rem;background:none;border:none;cursor:pointer;font-size:1rem;color:var(--gray-400);line-height:1;"
|
||||
title="Klas verwijderen">🗑</button>
|
||||
<button class="btn-delete" data-action="deleteKlas" data-id="${k.id}" data-name="${k.name.replace(/"/g,'"')}" title="Klas verwijderen">🗑</button>
|
||||
</div>`;
|
||||
}).join('') + '</div>';
|
||||
}
|
||||
@@ -795,19 +835,28 @@ document.addEventListener('click', function(e) {
|
||||
<span>${u.full_name} <span style="color:var(--gray-400);font-size:.8rem;">(${u.email})</span></span>
|
||||
</label>`).join('');
|
||||
}
|
||||
document.getElementById('koppelingModal').style.display = 'flex';
|
||||
document.getElementById('koppelingModal').classList.add('active');
|
||||
});
|
||||
|
||||
async function openNieuweKlasDialog() {
|
||||
const name = prompt('Naam van de nieuwe klas:');
|
||||
if (!name || !name.trim()) return;
|
||||
function openNieuweKlasDialog() {
|
||||
document.getElementById('nieuweKlasNaam').value = '';
|
||||
document.getElementById('nieuweKlasError').textContent = '';
|
||||
document.getElementById('nieuweKlasModal').classList.add('active');
|
||||
setTimeout(() => document.getElementById('nieuweKlasNaam').focus(), 50);
|
||||
}
|
||||
|
||||
async function bevestigNieuweKlas() {
|
||||
const name = document.getElementById('nieuweKlasNaam').value.trim();
|
||||
const errEl = document.getElementById('nieuweKlasError');
|
||||
if (!name) { errEl.textContent = 'Vul een naam in.'; return; }
|
||||
const res = await fetch('/api/classes', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({name: name.trim()})
|
||||
body: JSON.stringify({name})
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) { showNotification(data.error || 'Aanmaken mislukt', 'error'); return; }
|
||||
if (!res.ok) { errEl.textContent = data.error || 'Aanmaken mislukt'; return; }
|
||||
document.getElementById('nieuweKlasModal').classList.remove('active');
|
||||
showNotification(`Klas "${data.class.name}" aangemaakt`, 'success');
|
||||
await loadOverview();
|
||||
}
|
||||
@@ -823,7 +872,7 @@ async function saveKoppeling() {
|
||||
body: JSON.stringify({ teacher_ids: checked })
|
||||
});
|
||||
if (!res.ok) { showNotification('Opslaan mislukt', 'error'); return; }
|
||||
document.getElementById('koppelingModal').style.display = 'none';
|
||||
document.getElementById('koppelingModal').classList.remove('active');
|
||||
showNotification('Koppeling opgeslagen', 'success');
|
||||
await loadOverview(); // herlaad zodat klas-chips bijgewerkt worden
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user