Saltar a contenido

Etiquetas de producto para el storefront

Lo que la tienda necesita saber para dibujar las etiquetas que se administran desde este CMS. Escrito para quien implementa el lado del storefront, que es otra persona.

Requisito completo: RF-008. Historia #298, tarea #322.

Estado. El CMS expone la colección; el render todavía no existe. Hoy las etiquetas están escritas a mano en src/modules/common/components/product-card/index.tsx y este trabajo las reemplaza.

La consulta

Lectura pública, sin autenticar. Una sola llamada, cacheable, que se hace una vez por página y no una vez por producto.

GET /api/product-badges?limit=0&depth=0&where[enabled][equals]=true&sort=priority

Son unidades o decenas de documentos, nunca miles. depth=0 alcanza: la colección no tiene relaciones.

Cómo se asocia una etiqueta a un producto

Por el tag que el producto ya trae en el índice de Algolia, en product_tags. La etiqueta guarda un slug y el storefront compara contra él.

La regla de normalización es una sola y tiene que ser la misma de los dos lados. El CMS genera el slug con slugSinAcentos: minúsculas, sin tildes, la eñe pasa a n, todo lo que no sea a-z0-9 se reemplaza por guion y se recortan los de las puntas. El storefront aplica esa misma transformación a cada tag del producto antes de comparar.

"Solo Online"   → "solo-online"
"SOLO_ONLINE"   → "solo-online"
"Liquidación"   → "liquidacion"
"⭐ Oferta"      → "oferta"

Esto no es un detalle de estilo: hoy la tarjeta compara solo-hoy de forma literal en una línea y normalizada en otra, y por eso un producto con ese tag muestra la etiqueta y además el chip gris duplicado. Es la tarea #323.

Los pasos, en orden

  1. Filtrar por vigencia. publishFrom y publishUntil son opcionales. Vacío es sin límite. El filtro lo aplica el storefront, no la consulta, para que la respuesta siga siendo cacheable.
  2. Filtrar por contexto. contexts es una lista: card para la tarjeta, product para la ficha. Se queda con las que incluyen el contexto en el que se está dibujando.
  3. Cruzar. Normalizar cada tag del producto y quedarse con las etiquetas cuyo slug coincida.
  4. Ordenar por priority, de menor a mayor.
  5. Aplicar exclusive. Si una etiqueta con exclusive en verdadero cae en una zona, es la única que se dibuja en esa zona.
  6. Agrupar por slot y cortar según el tope que definas.

Dónde va cada una

slot dice la zona:

Valor Dónde
over-image Encima de la imagen
before-title Entre la imagen y el título
after-title Debajo del título
near-price Junto al precio
card-footer Al pie de la tarjeta

verticalPosition (top / bottom) solo llega con valor útil cuando slot es over-image. No hay posición central a propósito: la tarjeta mide entre 119 y 202 píxeles de ancho y el centro es donde está el producto.

align (left / center / right) aplica a todas las zonas.

El tope de etiquetas por zona lo decidís vos. El CMS entrega la lista ordenada y no sabe cuánto ancho hay. La referencia que se miró no pasa de dos por zona.

Cómo se ve

name es el texto a dibujar, tal cual llega, con emoji si lo tiene. No hay campo de imagen ni de ícono en esta versión.

style trae uno de cinco nombres —oferta, promo, neutro, alerta, exito— que mapean a los tokens que ya existen en el sistema de diseño, del estilo de bg-oferta y bg-solo-online. El CMS guarda un nombre, no un color: qué color le corresponde a cada nombre lo decide la tienda.

Cuando style es custom, llegan bgColor, textColor y borderColor en hexadecimal. Se aplican como estilo en línea. Una clase de Tailwind armada con un valor que viene de la base se purga en compilación y la etiqueta sale sin color.

hasBorder dice si lleva borde y shape la forma: square, rounded o pill.

Un documento de ejemplo

{
  "id": 3,
  "name": "Solo Online",
  "slug": "solo-online",
  "enabled": true,
  "publishFrom": null,
  "publishUntil": null,
  "contexts": ["card", "product"],
  "style": "promo",
  "hasBorder": false,
  "shape": "rounded",
  "slot": "before-title",
  "align": "left",
  "priority": 100,
  "exclusive": false
}

Campos que llegan y no sirven

generateSlug es una casilla del panel: dice si el slug se sigue generando solo a partir del texto. Es asunto del CMS, no de la tienda. Ignoralo.

Invalidación

La respuesta se cachea. Apagar o publicar una etiqueta se ve al vencer esa caché, no al instante, salvo que se agregue revalidación por gancho como hacen las páginas. Elegir un tiempo corto es razonable: la colección es chica y la consulta es barata.

Lo que hay hoy y se reemplaza

En product-card/index.tsx están escritas a mano: descuento, "Solo Hoy", "Solo Online", "Agotado", "Nuevo", "¡Últimas N!" y hasta dos tags sueltos como chip gris.

De esas, solo "Solo Hoy" y "Solo Online" salen de un tag y son las que esta versión reemplaza. Las otras se calculan de price_type, special_price, total_stock e is_new, y por ahora se quedan como están: el CMS todavía no sabe expresar esa condición (decisión E9 del requisito).

Queda por decidir qué pasa con un tag que no tiene etiqueta definida en el CMS (decisión E7). Lo recomendado es que no se dibuje.