Merge branch 'feat/resenas-productos'
Build and Push Medusa / Escaneo de Secretos (Gitleaks) (push) Successful in 1m21s
Build and Push Medusa / Construir y publicar Medusa (push) Successful in 38m24s
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (push) Successful in 49s
Build and Push Frontend / Construir y Subir Imagen (push) Failing after 33s

This commit is contained in:
devops committed 2026-09-27 12:23:49 +00:00
commit 619228168d
28 files changed
+1276 -2

No files matched your search

@@ -0,0 +1,111 @@
// Reseñas de productos + pedidos del cliente (Store API de Medusa, modulo
// propio commerce-backend/src/modules/product-review). Server-side y sin
// cache: las reseñas cambian al aprobarse en el admin y los pedidos son
// estado de sesion.
function backendUrl(): string {
const value = process.env.MEDUSA_BACKEND_URL;
if (!value) throw new Error("MEDUSA_BACKEND_URL no está configurado.");
return value.replace(/\/$/, "");
}
async function medusaFetch<T>(path: string, init?: { method?: string; body?: unknown; token?: string }): Promise<T> {
const publishableKey = process.env.MEDUSA_PUBLISHABLE_KEY;
const response = await fetch(`${backendUrl()}${path}`, {
method: init?.method ?? "GET",
headers: {
"Content-Type": "application/json",
...(publishableKey ? { "x-publishable-api-key": publishableKey } : {}),
...(init?.token ? { Authorization: `Bearer ${init.token}` } : {}),
},
body: init?.body ? JSON.stringify(init.body) : undefined,
cache: "no-store",
});
const data = await response.json().catch(() => ({}));
if (!response.ok) {
const message = typeof data.message === "string" ? data.message : `Error ${response.status}`;
throw Object.assign(new Error(message), { status: response.status });
}
return data as T;
}
export interface PublicReview {
id: string;
author_name: string;
rating: number;
title: string | null;
content: string;
created_at: string;
verified_purchase: boolean;
}
export interface ReviewSummary {
average: number;
count: number;
}
export interface MyReview {
id: string;
product_id: string;
order_id: string;
rating: number;
status: "pending" | "approved" | "rejected";
}
export interface CustomerOrder {
id: string;
displayId: number | null;
createdAt: string;
status: string;
total: number | null;
currency: string;
items: { productId: string; title: string; thumbnail: string | null; handle: string | null; quantity: number }[];
}
export function getProductReviews(productId: string) {
return medusaFetch<{ reviews: PublicReview[]; average: number; count: number }>(`/store/reviews?product_id=${encodeURIComponent(productId)}`);
}
export async function getReviewSummary(): Promise<Record<string, ReviewSummary>> {
return (await medusaFetch<{ summary: Record<string, ReviewSummary> }>("/store/reviews/summary")).summary;
}
export async function getMyReviews(token: string): Promise<MyReview[]> {
return (await medusaFetch<{ reviews: MyReview[] }>("/store/reviews/me", { token })).reviews;
}
export function createReview(token: string, body: { product_id: string; order_id: string; rating: number; title?: string; content: string }) {
return medusaFetch<{ review: { id: string; status: string } }>("/store/reviews", { method: "POST", body, token });
}
type MedusaOrder = {
id: string;
display_id?: number | null;
created_at: string;
status: string;
total?: number | null;
currency_code?: string | null;
items?: { product_id?: string | null; product_title?: string | null; title?: string | null; thumbnail?: string | null; product_handle?: string | null; quantity?: number }[];
};
export async function getMyOrders(token: string): Promise<CustomerOrder[]> {
const fields = "id,display_id,created_at,status,total,currency_code,*items";
const data = await medusaFetch<{ orders: MedusaOrder[] }>(`/store/orders?limit=50&order=-created_at&fields=${encodeURIComponent(fields)}`, { token });
return data.orders.map((order) => ({
id: order.id,
displayId: order.display_id ?? null,
createdAt: order.created_at,
status: order.status,
total: typeof order.total === "number" ? order.total : null,
currency: (order.currency_code ?? "cop").toUpperCase(),
items: (order.items ?? [])
.filter((item) => item.product_id)
.map((item) => ({
productId: item.product_id as string,
title: item.product_title || item.title || "Producto",
thumbnail: item.thumbnail ?? null,
handle: item.product_handle ?? null,
quantity: item.quantity ?? 1,
})),
}));
}