fix: toon gekoppelde ouders in de gebruikerslijst van de school (v0.3.59-beta)
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 28s
All checks were successful
dev - build & deploy naar test / build-and-deploy (push) Successful in 28s
- Ouders (school_id NULL) vielen buiten elke schoolgescopeerde gebruikerslijst; /admin/users levert nu ook de ouders die aan leerlingen van de school gekoppeld zijn (groepsleiding: alleen ouders van leerlingen uit eigen klassen) - Ouder-rijen tonen de namen van hun gekoppelde kinderen (childNames) - Servertest voor de gekoppelde-ouders-lijst Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0149FgUQvuwxKKEdvQGmNngF
This commit is contained in:
parent
5f9ffbfdde
commit
42fc323ab1
5 changed files with 52 additions and 2 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.3.58-beta
|
||||
0.3.59-beta
|
||||
|
|
|
|||
|
|
@ -205,6 +205,10 @@
|
|||
if(u.role==="pupil"){
|
||||
const cls = CLASSES.find(c=>Number(c.id)===Number(u.classId));
|
||||
row.appendChild(h("span","am-role", T(roleKey(u.role)) + (cls ? ` · ${cls.name}` : "")));
|
||||
}else if(u.role==="parent"){
|
||||
/* de school ziet bij elke ouder direct om welke kinderen het gaat */
|
||||
const kids = (u.childNames||[]).join(", ");
|
||||
row.appendChild(h("span","am-role", T(roleKey(u.role)) + (kids ? ` · 🧒 ${kids}` : "")));
|
||||
}else{
|
||||
const names = (u.classIds||[])
|
||||
.map(cid=>CLASSES.find(c=>Number(c.id)===Number(cid))?.name)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"use strict";
|
||||
/* version — shown until /api/version resolves (or if the fetch fails, e.g. offline).
|
||||
Kept in sync by hand with the VERSION file at the repo root on every release. */
|
||||
const VERSION = "0.3.58-beta";
|
||||
const VERSION = "0.3.59-beta";
|
||||
(function(){
|
||||
const tag = document.getElementById("verTag");
|
||||
tag.textContent = "v"+VERSION;
|
||||
|
|
|
|||
25
src/api.js
25
src/api.js
|
|
@ -389,11 +389,20 @@ export default async function api(app) {
|
|||
// teacher de eigen school maar leerlingen alleen uit de eigen klas(sen).
|
||||
app.get('/admin/users', async (req, reply) => {
|
||||
need(req, reply, PERMISSIONS['users.manage']);
|
||||
// Ouders hebben school_id NULL en vallen daardoor buiten elke schoollijst;
|
||||
// een school ziet wél de ouders die aan haar eigen leerlingen gekoppeld zijn.
|
||||
const linkedParents = async (schoolId, extra = '', params = []) => (await pool.query(
|
||||
`SELECT DISTINCT p.* FROM users p
|
||||
JOIN parent_children pc ON pc.parent_id = p.id
|
||||
JOIN users k ON k.id = pc.pupil_id
|
||||
WHERE k.school_id = $1 ${extra} ORDER BY lower(p.username)`,
|
||||
[schoolId, ...params])).rows;
|
||||
let rows;
|
||||
if (req.user.allRoles.includes('super')) {
|
||||
rows = (req.query.school
|
||||
? await pool.query('SELECT * FROM users WHERE school_id = $1 ORDER BY role, lower(username)', [req.query.school])
|
||||
: await pool.query('SELECT * FROM users ORDER BY school_id NULLS FIRST, role, lower(username)')).rows;
|
||||
if (req.query.school) rows = rows.concat(await linkedParents(req.query.school));
|
||||
} else if (teacherOnly(req)) {
|
||||
rows = (await pool.query(
|
||||
`SELECT * FROM users
|
||||
|
|
@ -401,8 +410,11 @@ export default async function api(app) {
|
|||
AND (role != 'pupil' OR class_id IN (SELECT class_id FROM class_teachers WHERE user_id = $2))
|
||||
ORDER BY role, lower(username)`,
|
||||
[req.user.school_id, req.user.id])).rows;
|
||||
rows = rows.concat(await linkedParents(req.user.school_id,
|
||||
'AND k.class_id IN (SELECT class_id FROM class_teachers WHERE user_id = $2)', [req.user.id]));
|
||||
} else {
|
||||
rows = (await pool.query('SELECT * FROM users WHERE school_id = $1 ORDER BY role, lower(username)', [req.user.school_id])).rows;
|
||||
rows = rows.concat(await linkedParents(req.user.school_id));
|
||||
}
|
||||
// extra stafrollen per gebruiker erbij ophalen (in één keer, niet per rij)
|
||||
const roleRows = rows.length
|
||||
|
|
@ -422,11 +434,24 @@ export default async function api(app) {
|
|||
const key = String(r.user_id);
|
||||
classesByUser.set(key, [...(classesByUser.get(key) || []), Number(r.class_id)]);
|
||||
});
|
||||
// kindernamen bij ouder-rijen, zodat de school ziet wie bij wie hoort
|
||||
const parentIds = rows.filter((u) => u.role === 'parent').map((u) => u.id);
|
||||
const kidRows = parentIds.length
|
||||
? (await pool.query(
|
||||
`SELECT pc.parent_id, k.display_name, k.username FROM parent_children pc
|
||||
JOIN users k ON k.id = pc.pupil_id WHERE pc.parent_id = ANY($1)`, [parentIds])).rows
|
||||
: [];
|
||||
const kidsByParent = new Map();
|
||||
kidRows.forEach((r) => {
|
||||
const key = String(r.parent_id);
|
||||
kidsByParent.set(key, [...(kidsByParent.get(key) || []), r.display_name || r.username]);
|
||||
});
|
||||
return { users: rows.map((u) => ({
|
||||
...publicUser({ ...u, extra_roles: extraByUser.get(String(u.id)) || [] }),
|
||||
linkCode: u.link_code || null,
|
||||
everLoggedIn: !!u.last_login_at,
|
||||
classIds: classesByUser.get(String(u.id)) || [],
|
||||
...(u.role === 'parent' ? { childNames: kidsByParent.get(String(u.id)) || [] } : {}),
|
||||
})) };
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -288,6 +288,27 @@ test('afwijzen verwijdert alleen het verzoek; andere school mag niet afhandelen'
|
|||
await app.close(); await app2.close();
|
||||
});
|
||||
|
||||
test('schoolbeheerder ziet gekoppelde ouders (met kindernamen) in de gebruikerslijst', async () => {
|
||||
const { app } = await makeApp({ user: admin, respond: (sql) => {
|
||||
if (sql.startsWith('SELECT * FROM users WHERE school_id')) return { rows: [
|
||||
{ id: 50, username: 'lena', role: 'pupil', school_id: 2, class_id: 5 },
|
||||
] };
|
||||
if (sql.includes('JOIN parent_children pc ON pc.parent_id = p.id')) return { rows: [
|
||||
{ id: 20, username: 'ouder', display_name: 'Ouder', role: 'parent', school_id: null, class_id: null },
|
||||
] };
|
||||
if (sql.includes('FROM parent_children pc') && sql.includes('WHERE pc.parent_id = ANY')) return { rows: [
|
||||
{ parent_id: 20, display_name: 'Lena', username: 'lena' },
|
||||
] };
|
||||
return undefined;
|
||||
} });
|
||||
const res = await app.inject({ method: 'GET', url: '/api/admin/users', cookies });
|
||||
assert.equal(res.statusCode, 200, res.body);
|
||||
const p = res.json().users.find((u) => u.role === 'parent');
|
||||
assert.ok(p, 'ouder hoort in de schoollijst te staan');
|
||||
assert.deepEqual(p.childNames, ['Lena']);
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('thuiskind logt voortgang met de gekoppelde ouder als leerkracht', async () => {
|
||||
const { app, calls } = await makeApp({ user: homeChild, respond: (sql) => {
|
||||
if (sql.includes('FROM parent_children WHERE pupil_id')) return { rows: [{ parent_id: 20 }] };
|
||||
|
|
|
|||
Loading…
Reference in a new issue