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 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-09-10 16:00:00 -05:00
parent 5b899256b1
commit f8b80d9bb4
6 changed files with 32 additions and 16 deletions
+7
View File
@@ -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
+1 -1
View File
@@ -1 +1 @@
1.8.1
1.9.0
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "project-linkedcorp-backend",
"version": "1.8.1",
"version": "1.9.0",
"private": true,
"type": "module",
"main": "src/server.js",
+7 -2
View File
@@ -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);
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "project-linkedcorp-frontend",
"private": true,
"version": "1.8.1",
"version": "1.9.0",
"type": "module",
"scripts": {
"dev": "vite",
+15 -11
View File
@@ -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}
/>
<DetailPopup open={editing} title={selected ? t('common_edit') : t('user_new')} onClose={() => setEditing(false)} width={560} height={440}>