Saltar al contenido principal

Revisión de apelaciones

Los administradores revisan apelaciones de restricción — solicitudes de usuarios suspendidos o baneados que piden que se levante la restricción. Esta es la mitad orientada al administrador de Verificación → Apelaciones de restricción; la mitad orientada al usuario (enviar una apelación desde Settings → Account status) está documentada allí. Ambas se construyeron en este pase — antes appealRestriction/getAppealStatus existían solo como stubs rotos, sin ninguna superficie de revisión para administradores.

Checklist de implementación

  • Listar apelaciones pendientes (adminGetAppeals) — por defecto status: 'pending', más antiguas primero; paginado (20 por página)
  • Revisar una apelación — aprobar (adminReviewAppeal con decision: 'approved') efectivamente levanta la restricción al llamar a la lógica real de unsuspendUser/unbanUser en user-moderation.manager.js (eligiendo la que coincida con el accountStatus actual del usuario), no solo marca la apelación como revisada
  • Revisar una apelación — denegar (adminReviewAppeal con decision: 'denied') permite al administrador agregar notas explicando la decisión (el campo notes es opcional, la API no lo exige); la restricción permanece vigente y las notas, si las hay, se muestran de vuelta al usuario en su propia página de Account status

No hay una acción de revisión en lote — las apelaciones se revisan una a la vez.

Dónde vive esto

Backend

Frontendapps/frontend-admin/src/app/moderation/appeals/page.tsx (/moderation/appeals), protegido del lado del cliente por MODERATE_CONTENT, con la entrada de navegación mostrada condicionalmente en AdminLayout.tsx. Una tabla paginada muestra el usuario de cada apelación, el estado actual de su cuenta (con el motivo de suspensión/baneo si existe), y un motivo de apelación truncado; un botón "Review" abre un modal con el motivo completo y las acciones Approve/Deny — Deny revela un área de texto opcional para notas antes de confirmar la denegación (nada obliga a llenarla).

Referencia de GraphQL

query AdminGetAppeals($status: String, $limit: Int, $offset: Int) {
adminGetAppeals(status: $status, limit: $limit, offset: $offset) {
total limit offset
appeals {
id status appealType reason submittedAt reviewedAt decisionNotes
user { id username email accountStatus suspensionReason suspendedUntil }
}
}
}

mutation AdminReviewAppeal($appealId: ID!, $decision: String!, $notes: String) {
adminReviewAppeal(appealId: $appealId, decision: $decision, notes: $notes) {
success
message
appeal { id status }
}
}