Backend (commerce-backend):
- Módulo product-review (tabla nueva product_review; migración generada con
medusa db:generate, no toca tablas existentes).
- GET /store/reviews?product_id (solo aprobadas), /store/reviews/summary,
GET /store/reviews/me y POST /store/reviews con auth de cliente: exige que
el pedido sea del cliente, no esté cancelado y contenga el producto
("Compra verificada"); 1 reseña por cliente y producto; nace "pending".
- /admin/reviews (+ /:id POST status, DELETE) y página "Reseñas" en el admin
para aprobar/rechazar/borrar.
Storefront (ecommerce):
- /cuenta/pedidos ("Mis pedidos"): historial real + diálogo "Califica tu
compra" (radiogroup accesible, flechas de teclado).
- Ficha: sección de reseñas con "Compra verificada" y estrellas junto al
título; tarjetas: promedio y cantidad (una llamada /api/reviews/summary).
- Accesos: "Mis pedidos y reseñas" en la cuenta y en Ayuda.
Validado en entorno descartable (Postgres/Redis/Meilisearch/Medusa propios):
E2E de API 20/20 (incluye pedido interno por draft order, cancelación y
borrado), E2E de tienda con navegador 15/15, admin "Reseñas" verificado en
build de producción. Revisado por frontend-critic (contraste de estrellas
amber-600, patrón radiogroup).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_018r19s4H8MBGegqToqw5guP
25 lines
1.3 KiB
TypeScript
25 lines
1.3 KiB
TypeScript
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
|
import { MedusaError } from "@medusajs/framework/utils";
|
|
import { PRODUCT_REVIEW_MODULE } from "../../../../modules/product-review";
|
|
import type ProductReviewModuleService from "../../../../modules/product-review/service";
|
|
|
|
const STATUSES = ["pending", "approved", "rejected"] as const;
|
|
|
|
// POST /admin/reviews/:id { status } -- aprobar / rechazar / volver a
|
|
// pendiente. DELETE /admin/reviews/:id -- borrar (ej. reseñas de prueba).
|
|
export async function POST(req: MedusaRequest<{ status?: string }>, res: MedusaResponse) {
|
|
const status = req.body?.status;
|
|
if (!status || !STATUSES.includes(status as (typeof STATUSES)[number])) {
|
|
throw new MedusaError(MedusaError.Types.INVALID_DATA, `status debe ser uno de: ${STATUSES.join(", ")}`);
|
|
}
|
|
const reviews = req.scope.resolve<ProductReviewModuleService>(PRODUCT_REVIEW_MODULE);
|
|
const review = await reviews.updateProductReviews({ id: req.params.id, status: status as (typeof STATUSES)[number] });
|
|
res.json({ review });
|
|
}
|
|
|
|
export async function DELETE(req: MedusaRequest, res: MedusaResponse) {
|
|
const reviews = req.scope.resolve<ProductReviewModuleService>(PRODUCT_REVIEW_MODULE);
|
|
await reviews.deleteProductReviews(req.params.id);
|
|
res.json({ id: req.params.id, deleted: true });
|
|
}
|