PIVOT 1.1.0

This commit is contained in:
marijn committed 2026-10-14 13:34:37 +02:00
commit 8a53234eb6
161 files changed
+31109

No files matched your search

+180
View File
@@ -0,0 +1,180 @@
{% extends "base.html" %}
{% block title %}Smartschool beheer{% endblock %}
{% block page_title %}Smartschool beheer{% endblock %}
{% block main_class %}medium{% endblock %}
{% block extra_css %}
<style>
#coAccountModal { z-index: 3000; }
#passwordModal { z-index: 4000; } /* reset-venster altijd bovenop de co-accountlijst */
</style>
{% endblock %}
{% block content %}
<div class="card">
<div class="page-head"><h2>🏫 Wachtwoord &amp; info</h2></div>
<form class="search-bar" method="POST" action="{{ url_for('/ss.smartschool') }}" onsubmit="showLoader()">
<input type="text" name="query" placeholder="Zoek op naam, voornaam, gebruikersnaam of internnummer..." value="{{ query or '' }}">
<button type="submit" class="btn-search">🔍 Zoeken</button>
</form>
{% if results %}
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Internnummer</th>
<th>Naam</th>
<th>Gebruikersnaam</th>
<th class="actions">Acties</th>
</tr>
</thead>
<tbody>
{% for user in results %}
<tr>
<td><strong>{{ user.internnummer }}</strong></td>
<td>{{ user.voornaam }} {{ user.naam }}</td>
<td>{{ user.gebruikersnaam }}</td>
<td class="actions">
<button type="button" class="btn-danger btn-sm" onclick="openResetModal('{{ user.internnummer }}', '{{ user.voornaam }} {{ user.naam }}', 0)">🔑 Reset WW</button>
<button type="button" class="btn-secondary btn-sm" onclick="viewCoAccounts('{{ user.gebruikersnaam }}')">👪 Co-accounts</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% elif query %}
<p class="muted">Geen resultaten gevonden voor "{{ query }}".</p>
<p class="muted small">Op een deel van een naam zoekt PIVOT in de leerlingenlijst. Staat die nog leeg,
haal dan eerst de leerlingen op via Leerlingen › Informat &amp; SS sync, of zoek op het volledige internnummer of de gebruikersnaam.</p>
{% endif %}
</div>
{% endblock %}
{% block modals %}
<div id="passwordModal" class="modal-overlay">
<div class="modal-content">
<span class="close" onclick="closeModal('passwordModal')">&times;</span>
<h3>Wachtwoord reset</h3>
<p id="displayUser" style="font-weight:600; color: var(--primary-color);"></p>
<div class="field">
<label for="modalPasswordInput">Nieuw wachtwoord</label>
<div class="input-group">
<input type="text" id="modalPasswordInput" placeholder="Nieuw wachtwoord">
<button type="button" class="btn-light" title="Willekeurig genereren" onclick="generateRandomPass()">🎲</button>
</div>
</div>
<label class="check-box">
<input type="checkbox" id="pdfCheckbox"> PDF genereren voor deze persoon
</label>
<div class="modal-footer">
<button type="button" class="btn-ghost" onclick="closeModal('passwordModal')">Annuleren</button>
<button type="button" onclick="submitPasswordReset()">Bevestig reset</button>
</div>
</div>
</div>
<div id="coAccountModal" class="modal-overlay">
<div class="modal-content wide">
<span class="close" onclick="closeModal('coAccountModal')">&times;</span>
<h3>Gekoppelde co-accounts</h3>
<div id="coAccountList"></div>
<div class="modal-footer">
<button type="button" onclick="closeModal('coAccountModal')">Sluiten</button>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
let currentTargetUser = "";
let currentResetType = 0;
let currentTargetName = "";
function closeModal(id) { document.getElementById(id).style.display = 'none'; }
function openResetModal(identifier, fullname, accType = 0) {
currentTargetUser = identifier;
currentResetType = accType;
currentTargetName = fullname;
document.getElementById('displayUser').innerText = fullname + (accType === 0 ? " (Leerling)" : " (Co-account)");
document.getElementById('modalPasswordInput').value = "";
document.getElementById('pdfCheckbox').checked = false;
document.getElementById('passwordModal').style.display = 'flex';
}
function generateRandomPass() {
const chars = "abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789";
let pass = "";
for (let i = 0; i < 10; i++) pass += chars.charAt(Math.floor(Math.random() * chars.length));
document.getElementById('modalPasswordInput').value = pass;
}
function submitPasswordReset() {
const newPass = document.getElementById('modalPasswordInput').value;
const genPdf = document.getElementById('pdfCheckbox').checked;
if (!newPass) return alert("Geen wachtwoord ingevuld!");
showLoader();
fetch("{{ url_for('/ss.smartschool_reset') }}", {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: `username=${encodeURIComponent(currentTargetUser)}&new_password=${encodeURIComponent(newPass)}&account_type=${currentResetType}&generate_pdf=${genPdf}&display_name=${encodeURIComponent(currentTargetName)}`
})
.then(res => {
const contentType = res.headers.get("content-type");
if (contentType && contentType.includes("application/pdf")) {
return res.blob().then(blob => ({ type: 'pdf', data: blob }));
}
return res.json().then(data => ({ type: 'json', data: data }));
})
.then(result => {
hideLoader();
if (result.type === 'pdf') {
const url = window.URL.createObjectURL(result.data);
const a = document.createElement('a');
a.href = url;
a.download = `Account_${currentTargetName}.pdf`;
document.body.appendChild(a);
a.click();
closeModal('passwordModal');
} else if (result.data.status === "success") {
alert("Wachtwoord succesvol aangepast!");
closeModal('passwordModal');
} else {
alert("Fout: " + result.data.message);
}
}).catch(() => { hideLoader(); alert("Fout bij verbinding."); });
}
function viewCoAccounts(username) {
showLoader();
fetch("{{ url_for('/ss.smartschool_coaccounts') }}", {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: `username=${encodeURIComponent(username)}`
})
.then(res => res.json())
.then(data => {
hideLoader();
let html = '<div class="table-wrap"><table><thead><tr><th>Relatie</th><th>Naam</th><th class="actions">Actie</th></tr></thead><tbody>';
data.accounts.forEach(acc => {
const fullName = `${acc.voornaam} ${acc.naam}`;
html += `<tr>
<td>${acc.type}</td>
<td>${fullName}</td>
<td class="actions"><button type="button" class="btn-danger btn-sm" onclick="openResetModal('${acc.parent_internnummer}', '${fullName}', ${acc.index})">🔑 Reset</button></td>
</tr>`;
});
html += '</tbody></table></div>';
document.getElementById('coAccountList').innerHTML = html;
document.getElementById('coAccountModal').style.display = 'flex';
})
.catch(() => { hideLoader(); alert("Fout bij ophalen co-accounts."); });
}
</script>
{% endblock %}