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
27 lines
904 B
TypeScript
27 lines
904 B
TypeScript
"use client";
|
|
|
|
import { create } from "zustand";
|
|
|
|
// Promedio/cantidad de reseñas aprobadas por producto, para las estrellas
|
|
// de las tarjetas. Se pide UNA vez por sesion (/api/reviews/summary) en
|
|
// vez de una llamada por tarjeta, y sin depender del cache ISR de las
|
|
// paginas (una reseña recien aprobada aparece al recargar).
|
|
interface ReviewSummaryState {
|
|
summary: Record<string, { average: number; count: number }>;
|
|
status: "idle" | "loading" | "ready";
|
|
load: () => void;
|
|
}
|
|
|
|
export const useReviewSummaryStore = create<ReviewSummaryState>()((set, get) => ({
|
|
summary: {},
|
|
status: "idle",
|
|
load: () => {
|
|
if (get().status !== "idle") return;
|
|
set({ status: "loading" });
|
|
fetch("/api/reviews/summary")
|
|
.then((response) => response.json())
|
|
.then((data) => set({ summary: data.summary ?? {}, status: "ready" }))
|
|
.catch(() => set({ status: "ready" }));
|
|
},
|
|
}));
|