From f8b80d9bb401bc8998af9503edb07ad4df885049 Mon Sep 17 00:00:00 2001 From: Milton Gonzalez Date: Thu, 10 Sep 2026 16:00:00 -0500 Subject: [PATCH] feat: permite rechazar (eliminar) usuarios en pending_verification Antes solo se podia rechazar un usuario en pending_approval. Una cuenta autorregistrada que nunca confirmo su correo quedaba en pending_verification para siempre, sin ninguna accion disponible en Usuarios para quitarla. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 7 +++++ VERSION | 2 +- backend/package.json | 2 +- backend/src/modules/users/service.js | 9 +++++-- frontend/package.json | 2 +- .../src/pages/Organizacion/UsersPanel.jsx | 26 +++++++++++-------- 6 files changed, 32 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec41172..d4c1f70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog - project.linkedcorp.net +## [1.9.0] - 2026-09-10 +### Added +- Organización → Usuarios: ahora se puede "Rechazar" (eliminar) también a un usuario en + `pending_verification` (autorregistro que nunca confirmó su correo) — antes esa acción solo + existía para `pending_approval`, así que una cuenta abandonada antes de verificar el correo no + tenía ninguna forma de quitarse desde la UI. + ## [1.8.1] - 2026-09-10 ### Fixed - "Agregar dependencia" en una actividad no daba ninguna señal cuando no había otra actividad del diff --git a/VERSION b/VERSION index a8fdfda..f8e233b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.8.1 +1.9.0 diff --git a/backend/package.json b/backend/package.json index 45b734f..62a2817 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "project-linkedcorp-backend", - "version": "1.8.1", + "version": "1.9.0", "private": true, "type": "module", "main": "src/server.js", diff --git a/backend/src/modules/users/service.js b/backend/src/modules/users/service.js index d1e189c..da2d5e9 100644 --- a/backend/src/modules/users/service.js +++ b/backend/src/modules/users/service.js @@ -79,10 +79,15 @@ export async function approveUser(id, payload, actorId) { return user; } +// Rechaza y borra una cuenta que aun no esta activa: tanto la que ya paso la verificacion de +// correo y espera aprobacion, como la que nunca llego a confirmar su correo (quedo abandonada en +// 'pending_verification' — antes no habia forma de quitarla desde la UI). export async function rejectUser(id, actorId) { const existing = await repo.findById(id); if (!existing) throw new HttpError(404, 'Usuario no encontrado'); - if (existing.status !== 'pending_approval') throw new HttpError(400, 'El usuario no esta pendiente de aprobacion'); - await logAudit({ userId: actorId, action: 'reject', entityType: 'user', entityId: id, detail: { email: existing.email } }); + if (!['pending_approval', 'pending_verification'].includes(existing.status)) { + throw new HttpError(400, 'El usuario no esta pendiente de aprobacion ni de verificacion'); + } + await logAudit({ userId: actorId, action: 'reject', entityType: 'user', entityId: id, detail: { email: existing.email, status: existing.status } }); await repo.remove(id); } diff --git a/frontend/package.json b/frontend/package.json index 62c9ab9..037fb1c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "project-linkedcorp-frontend", "private": true, - "version": "1.8.1", + "version": "1.9.0", "type": "module", "scripts": { "dev": "vite", diff --git a/frontend/src/pages/Organizacion/UsersPanel.jsx b/frontend/src/pages/Organizacion/UsersPanel.jsx index db04294..48518e2 100644 --- a/frontend/src/pages/Organizacion/UsersPanel.jsx +++ b/frontend/src/pages/Organizacion/UsersPanel.jsx @@ -132,19 +132,23 @@ export default function UsersPanel() { getSearchHaystack={(u) => `${u.name} ${u.email} ${u.role_name} ${u.department_name || ''}`} getRowKey={(u) => u.id} onRowClick={canManage ? startEdit : undefined} - rowActions={canManage ? (u) => ( - u.status === 'pending_approval' - ? [ + rowActions={canManage ? (u) => { + if (u.status === 'pending_approval') { + return [ { label: t('user_approve'), icon: Check, onClick: () => startApprove(u) }, { label: t('user_reject'), icon: XIcon, danger: true, onClick: () => confirmReject(u) }, - ] - : [ - { label: t('common_edit'), onClick: () => startEdit(u) }, - u.is_active - ? { label: t('common_inactive'), icon: UserX, danger: true, onClick: () => toggleActive(u) } - : { label: t('common_active'), icon: UserCheck, onClick: () => toggleActive(u) }, - ] - ) : undefined} + ]; + } + if (u.status === 'pending_verification') { + return [{ label: t('user_reject'), icon: XIcon, danger: true, onClick: () => confirmReject(u) }]; + } + return [ + { label: t('common_edit'), onClick: () => startEdit(u) }, + u.is_active + ? { label: t('common_inactive'), icon: UserX, danger: true, onClick: () => toggleActive(u) } + : { label: t('common_active'), icon: UserCheck, onClick: () => toggleActive(u) }, + ]; + } : undefined} /> setEditing(false)} width={560} height={440}>