PIVOT 1.1.0
This commit is contained in:
commit
8a53234eb6
161 files changed
+31109
No files matched your search
@@ -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 & 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 & 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')">×</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')">×</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 %}
|
||||
Reference in new issue
Block a user