diff --git a/backend/package.json b/backend/package.json index 40c4374..c9dadab 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-backend", - "version": "0.12.22", + "version": "0.12.23", "description": "RosterChirp backend server", "main": "src/index.js", "scripts": { diff --git a/backend/src/routes/usergroups.js b/backend/src/routes/usergroups.js index 6d30591..d9d0a15 100644 --- a/backend/src/routes/usergroups.js +++ b/backend/src/routes/usergroups.js @@ -279,7 +279,7 @@ router.patch('/:id', authMiddleware, teamManagerMiddleware, async (req, res) => } } - if (Array.isArray(memberIds) && ug.dm_group_id) { + if (Array.isArray(memberIds)) { const defaultAdmin = await queryOne(req.schema, 'SELECT id FROM users WHERE is_default_admin=TRUE'); const newIds = new Set(memberIds.map(Number).filter(Boolean)); if (defaultAdmin) newIds.delete(defaultAdmin.id); // default admin cannot be in user groups @@ -289,32 +289,36 @@ router.patch('/:id', authMiddleware, teamManagerMiddleware, async (req, res) => for (const uid of newIds) { if (!currentSet.has(uid)) { await exec(req.schema, 'INSERT INTO user_group_members (user_group_id,user_id) VALUES ($1,$2) ON CONFLICT DO NOTHING', [ug.id, uid]); - await addUserSilent(req.schema, ug.dm_group_id, uid); + if (ug.dm_group_id) await addUserSilent(req.schema, ug.dm_group_id, uid); addedUids.push(uid); } } for (const uid of currentSet) { if (!newIds.has(uid)) { await exec(req.schema, 'DELETE FROM user_group_members WHERE user_group_id=$1 AND user_id=$2', [ug.id, uid]); - await exec(req.schema, 'DELETE FROM group_members WHERE group_id=$1 AND user_id=$2', [ug.dm_group_id, uid]); - io.in(R(req.schema,'user',uid)).socketsLeave(R(req.schema,'group',ug.dm_group_id)); - io.to(R(req.schema,'user',uid)).emit('group:deleted', { groupId: ug.dm_group_id }); + if (ug.dm_group_id) { + await exec(req.schema, 'DELETE FROM group_members WHERE group_id=$1 AND user_id=$2', [ug.dm_group_id, uid]); + io.in(R(req.schema,'user',uid)).socketsLeave(R(req.schema,'group',ug.dm_group_id)); + io.to(R(req.schema,'user',uid)).emit('group:deleted', { groupId: ug.dm_group_id }); + } removedUids.push(uid); } } - // Notification rule: single user → named message; multiple users → one generic message - if (addedUids.length === 1) { - const u = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [addedUids[0]]); - await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${u?.display_name||u?.name||'A user'} has joined the conversation.`); - } else if (addedUids.length > 1) { - await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${addedUids.length} new members have joined the conversation.`); - } - if (removedUids.length === 1) { - const u = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [removedUids[0]]); - await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${u?.display_name||u?.name||'A user'} has been removed from the conversation.`); - } else if (removedUids.length > 1) { - await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${removedUids.length} members have been removed from the conversation.`); + // Notification rule (only if DM exists): single user → named message; multiple → generic + if (ug.dm_group_id) { + if (addedUids.length === 1) { + const u = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [addedUids[0]]); + await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${u?.display_name||u?.name||'A user'} has joined the conversation.`); + } else if (addedUids.length > 1) { + await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${addedUids.length} new members have joined the conversation.`); + } + if (removedUids.length === 1) { + const u = await queryOne(req.schema, 'SELECT name,display_name FROM users WHERE id=$1', [removedUids[0]]); + await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${u?.display_name||u?.name||'A user'} has been removed from the conversation.`); + } else if (removedUids.length > 1) { + await postSysMsg(req.schema, ug.dm_group_id, req.user.id, `${removedUids.length} members have been removed from the conversation.`); + } } // Propagate to multi-group DMs diff --git a/build.sh b/build.sh index de5a669..471c202 100644 --- a/build.sh +++ b/build.sh @@ -13,7 +13,7 @@ # ───────────────────────────────────────────────────────────── set -euo pipefail -VERSION="${1:-0.12.22}" +VERSION="${1:-0.12.23}" ACTION="${2:-}" REGISTRY="${REGISTRY:-}" IMAGE_NAME="rosterchirp" diff --git a/frontend/package.json b/frontend/package.json index 2414252..a533eaf 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "rosterchirp-frontend", - "version": "0.12.22", + "version": "0.12.23", "private": true, "scripts": { "dev": "vite", diff --git a/frontend/src/pages/UserManagerPage.jsx b/frontend/src/pages/UserManagerPage.jsx index 50547d4..9b4b194 100644 --- a/frontend/src/pages/UserManagerPage.jsx +++ b/frontend/src/pages/UserManagerPage.jsx @@ -173,10 +173,10 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o }); // Sync group memberships: add newly selected, remove deselected for (const gId of selectedGroupIds) { - if (!origGroupIds.has(gId)) await api.addUserToGroup(gId, user.id).catch(() => {}); + if (!origGroupIds.has(gId)) await api.addUserToGroup(gId, user.id); } for (const gId of origGroupIds) { - if (!selectedGroupIds.has(gId)) await api.removeUserFromGroup(gId, user.id).catch(() => {}); + if (!selectedGroupIds.has(gId)) await api.removeUserFromGroup(gId, user.id); } toast('User updated', 'success'); } else { @@ -191,7 +191,7 @@ function UserForm({ user, userPass, allUserGroups, onDone, onCancel, isMobile, o }); // Add to selected groups for (const gId of selectedGroupIds) { - await api.addUserToGroup(gId, newUser.id).catch(() => {}); + await api.addUserToGroup(gId, newUser.id); } toast('User created', 'success'); }