Skip to content

feat(admin): implementar moderación de productos para panel de administración - #120

Merged
DavidVillarM merged 2 commits into
devfrom
OM-308-BE-Revision-de-Productos-Admin
Apr 21, 2026
Merged

DavidVillarM merged 2 commits into
devfrom
OM-308-BE-Revision-de-Productos-Admin

Conversation

@josediaz33

@josediaz33 josediaz33 commented Apr 21, 2026 •

Copy link
Copy Markdown
Collaborator
  • Primera funcionalidad de moderación de contenido del panel admin
  • Los productos tienen approval_status (PENDING/ACTIVE/REJECTED) y visible
  • Al aprobar: producto visible en catálogo público
  • Al rechazar: producto oculto + notificación automática al vendedor
  • Sigue el mismo patrón de aprobación/rechazo que ya existía en admin/stores

Endpoints:

  • GET /api/admin/products — lista productos con filtros y paginación
  • PATCH /api/admin/products/:id/status — aprueba o rechaza un producto

Filtros del GET:

  • search: busca por nombre de producto o nombre de tienda (case insensitive)
  • approvalStatus: PENDING | ACTIVE | REJECTED
  • page + limit: paginación vía parsePagination middleware

Lógica del PATCH:

  • ACTIVE: approval_status=ACTIVE, visible=true, rejection_reason=null
  • REJECTED: approval_status=REJECTED, visible=false, rejection_reason=reason requerido
  • Validación de estado idempotente (evita re-aprobar o re-rechazar)
  • Notificación al vendedor creada en la misma transacción al rechazar

Summary by CodeRabbit

Notas de Lanzamiento

  • Nuevas Funcionalidades
    • Gestión mejorada de productos en el panel de administración con capacidad de búsqueda y filtrado por estado de aprobación.
    • Actualización de estados de productos (Aprobado/Rechazado) con notificaciones automáticas a vendedores cuando se rechaza un producto.

…stración

Contexto:
- Primera funcionalidad de moderación de contenido del panel admin
- Los productos tienen approval_status (PENDING/ACTIVE/REJECTED) y visible
- Al aprobar: producto visible en catálogo público
- Al rechazar: producto oculto + notificación automática al vendedor
- Sigue el mismo patrón de aprobación/rechazo que ya existía en admin/stores

Endpoints:
- GET  /api/admin/products — lista productos con filtros y paginación
- PATCH /api/admin/products/:id/status — aprueba o rechaza un producto

Filtros del GET:
- search: busca por nombre de producto o nombre de tienda (case insensitive)
- approvalStatus: PENDING | ACTIVE | REJECTED
- page + limit: paginación vía parsePagination middleware

Lógica del PATCH:
- ACTIVE: approval_status=ACTIVE, visible=true, rejection_reason=null
- REJECTED: approval_status=REJECTED, visible=false, rejection_reason=reason requerido
- Validación de estado idempotente (evita re-aprobar o re-rechazar)
- Notificación al vendedor creada en la misma transacción al rechazar
@coderabbitai

coderabbitai Bot commented Apr 21, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@josediaz33 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 38 minutes and 9 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 38 minutes and 9 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: f7558e40-88f5-4b09-b80f-158aad145e8b

📥 Commits

Reviewing files that changed from the base of the PR and between 36d6c21 and f5d2f76.

📒 Files selected for processing (3)
  • src/modules/admin/products/admin-products.controller.js
  • src/modules/admin/products/admin-products.service.js
  • tests/unit/admin/admin-products.test.js
📝 Walkthrough

Walkthrough

Se agregaron rutas, controlador y servicios para funcionalidad de administración de productos, incluyendo listado con filtros y actualización de estado de aprobación. El enrutador se montó en la aplicación Express y se incluyeron pruebas unitarias para validar la lógica de negocio.

Changes

Cohort / File(s) Resumen
Configuración de Rutas Admin
src/app.js, src/modules/admin/index.js
Se agregó importación y montaje de adminProductsRoutes en el endpoint /api/admin/products alongside las rutas existentes de admin.
Feature Admin Productos
src/modules/admin/products/admin-products.routes.js, src/modules/admin/products/admin-products.controller.js, src/modules/admin/products/admin-products.service.js
Se implementó nueva funcionalidad para listar productos con filtros de búsqueda y estado de aprobación, y actualizar el estado de aprobación de productos. El servicio incluye validación, lógica de transacción con notificaciones condicionales al rechazar, y consultas dinámicas con paginación.
Pruebas Admin Productos
tests/unit/admin/admin-products.test.js
Suite de pruebas unitarias que valida getProductsAdminService (filtrado, paginación, mapeo de campos) y updateProductApprovalStatusService (validación de entrada, restricciones de estado, efectos de notificación).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

Suggested reviewers

  • Benjakr04
  • leoAchu16
  • CrisNAC

Poem

🐰 Un conejo saltó por las rutas,
Productos a aprobar sin disputa,
Con filtros y transacciones bien puestas,
Las pruebas garantizan respuestas,
¡Nueva feature lista, sin minucia! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed El título describe con precisión la funcionalidad principal agregada: implementa la moderación de productos en el panel administrativo, reflejando fielmente los cambios en el changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch OM-308-BE-Revision-de-Productos-Admin

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/unit/admin/admin-products.test.js (1)

272-278: Mock innecesario: la validación de reason ocurre antes del findFirst.

En updateProductApprovalStatusService la validación de reason (L113-115) se evalúa antes del prisma.products.findFirst (L117), por lo que el mock prisma.products.findFirst.mockResolvedValue(mockProduct) nunca se consume en este test. No afecta la correctness del test, pero puede confundir al lector haciéndole creer que se testea idempotencia + falta de reason juntos.

🧹 Limpieza sugerida
   it("lanza ValidationError cuando se rechaza sin reason", async () => {
-    prisma.products.findFirst.mockResolvedValue(mockProduct);
-
     await expect(
       updateProductApprovalStatusService(1, 1, { status: "REJECTED", reason: "" })
     ).rejects.toThrow(ValidationError);
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/admin/admin-products.test.js` around lines 272 - 278, The test
unnecessarily mocks prisma.products.findFirst
(prisma.products.findFirst.mockResolvedValue(mockProduct)) even though
updateProductApprovalStatusService validates the 'reason' and throws
ValidationError before any DB call; remove that mock from the "lanza
ValidationError cuando se rechaza sin reason" test so the test only asserts that
calling updateProductApprovalStatusService(1, 1, { status: "REJECTED", reason:
"" }) rejects with ValidationError, keeping the test focused and less confusing.
src/modules/admin/products/admin-products.controller.js (1)

16-30: Check de req.user?.id_user redundante dado el middleware authenticate + requireRole(ADMIN).

La ruta ya aplica authenticate y requireRole(ROLES.ADMIN), que garantizan req.user presente antes de entrar al handler. El 401 aquí nunca debería dispararse en el happy path. Es defensivo y no rompe nada, pero si se quiere mantener consistencia con los otros controllers de admin y dejar el manejo de auth centralizado en middlewares, se podría simplificar:

♻️ Simplificación opcional
 export const updateProductStatus = async (req, res, next) => {
   try {
-    if (!req.user?.id_user) {
-      return res.status(401).json({ success: false, message: "Usuario autenticado requerido" });
-    }
     const result = await updateProductApprovalStatusService(
       req.user.id_user,
       req.params.id,
       req.body
     );
     return res.status(200).json(result);
   } catch (error) {
     next(error);
   }
 };

Nota: si se elimina el parámetro adminUserId del servicio (ver comentario en admin-products.service.js), este check queda naturalmente fuera.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/modules/admin/products/admin-products.controller.js` around lines 16 -
30, The handler updateProductStatus contains a redundant auth check for
req.user?.id_user because authenticate + requireRole(ROLES.ADMIN) already
guarantee req.user; remove the early 401 block and simply call
updateProductApprovalStatusService using req.user.id_user, req.params.id and
req.body, returning the result (keep the try/catch and next(error) as-is); also
consider removing the adminUserId parameter from
updateProductApprovalStatusService (referenced by name) in a follow-up if you
fully centralize auth in middleware.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/modules/admin/products/admin-products.service.js`:
- Around line 102-165: The adminUserId parameter is accepted by
updateProductApprovalStatusService but never used; either persist it for
auditability or remove it to avoid a misleading API. If you have an approver
column (e.g., approved_by / reviewed_by) or an audit table, include adminUserId
in the prisma.products.update data (add reviewed_by: adminUserId or create a
prisma.productAudits.create entry inside the same $transaction alongside
prisma.products.update and prisma.notifications.create) and return it via the
existing productSelect/mapProductResponse; otherwise remove adminUserId from the
updateProductApprovalStatusService signature and from the controller call to
align intent across admin modules (also mirror the same change in admin/stores).
- Around line 117-163: El chequeo de estado se hace fuera de la transacción
(prisma.products.findFirst / product.approval_status === normalizedStatus)
permitiendo TOCTOU; muévelo dentro de la transacción y realiza un update
condicional que solo aplique si el approval_status actual difiere de
normalizedStatus (por ejemplo usar prisma.products.update with where: {
id_product: resolvedProductId, approval_status: { not: normalizedStatus } } y
data: updateData), ejecutar la creación de notificación
(prisma.notifications.create) dentro del mismo prisma.$transaction solo cuando
el update realmente afecte un registro, y, si el update no devolvió un registro
actualizado, lanzar ValidationError `El producto ya tiene el estado
${normalizedStatus}`; así usa los símbolos prisma.$transaction,
prisma.products.update, prisma.notifications.create, approval_status,
normalizedStatus y updateData para localizar y aplicar el cambio.

---

Nitpick comments:
In `@src/modules/admin/products/admin-products.controller.js`:
- Around line 16-30: The handler updateProductStatus contains a redundant auth
check for req.user?.id_user because authenticate + requireRole(ROLES.ADMIN)
already guarantee req.user; remove the early 401 block and simply call
updateProductApprovalStatusService using req.user.id_user, req.params.id and
req.body, returning the result (keep the try/catch and next(error) as-is); also
consider removing the adminUserId parameter from
updateProductApprovalStatusService (referenced by name) in a follow-up if you
fully centralize auth in middleware.

In `@tests/unit/admin/admin-products.test.js`:
- Around line 272-278: The test unnecessarily mocks prisma.products.findFirst
(prisma.products.findFirst.mockResolvedValue(mockProduct)) even though
updateProductApprovalStatusService validates the 'reason' and throws
ValidationError before any DB call; remove that mock from the "lanza
ValidationError cuando se rechaza sin reason" test so the test only asserts that
calling updateProductApprovalStatusService(1, 1, { status: "REJECTED", reason:
"" }) rejects with ValidationError, keeping the test focused and less confusing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0301c335-8673-4d4a-b87e-abb0b3ccc7d4

📥 Commits

Reviewing files that changed from the base of the PR and between f2d1a7a and 36d6c21.

📒 Files selected for processing (6)
  • src/app.js
  • src/modules/admin/index.js
  • src/modules/admin/products/admin-products.controller.js
  • src/modules/admin/products/admin-products.routes.js
  • src/modules/admin/products/admin-products.service.js
  • tests/unit/admin/admin-products.test.js

Comment thread src/modules/admin/products/admin-products.service.js Outdated
Comment thread src/modules/admin/products/admin-products.service.js

@DavidVillarM DavidVillarM left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funcionalidad correcta

@DavidVillarM
DavidVillarM merged commit 7ca8aea into dev Apr 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants