Ir al contenido

Banner Características Técnicas

El tipo más completo: imagen principal, párrafo, lista de características con íconos, y botones de acción configurables.

En el paneltipo en el JSONToken de la clave
Banner Características TécnicasRICH_DESCRIPTIONrich_description
{
tipo: 'RICH_DESCRIPTION';
titulo: string | null;
texto: string | null; // párrafo principal
imagen: { url: string; alt: string | null } | null; // imagen hero
items: Array<{ // lista de características
texto: string;
icono: string | null; // URL suelta, no objeto
orden: number;
}>;
acciones?: Array<Accion>; // opcional — ver abajo
}
type Accion = {
tipo: 'guia_tallas' | 'manual' | 'manual-uso';
modo: 'image' | 'link' | 'pdf';
etiqueta: string | null;
// Presente según `modo`:
imagen?: { url: string; alt: string | null }; // modo === 'image'
enlace?: {
// modo === 'link'
url: string;
etiqueta: string | null;
nueva_pestana: boolean;
};
pdf?: { url: string }; // modo === 'pdf'
};

modo dice cómo entregar el recurso y qué campo leer. tipo dice qué es, para poner una etiqueta por defecto cuando etiqueta es null.

modoCampo presenteRender sugerido
imageimagenAbrir en modal o lightbox.
linkenlaceUn <a> común.
pdfpdfUn <a> que abre o descarga.
{
"rich_description-product-333333174-cmqic8fcm000ueq2b5gm7zg8q": {
"tipo": "RICH_DESCRIPTION",
"titulo": "Características técnicas",
"texto": "Diseñada para uso diario intensivo, con materiales de grado industrial.",
"imagen": {
"url": "https://cdn.example.com/hero.jpg",
"alt": "Producto sobre mesa de trabajo"
},
"items": [
{
"texto": "Resistente al agua IPX7",
"icono": "https://cdn.example.com/agua.svg",
"orden": 0
},
{ "texto": "Batería de 18 horas", "icono": null, "orden": 1 }
],
"acciones": [
{
"tipo": "guia_tallas",
"modo": "image",
"imagen": { "url": "https://cdn.example.com/tallas.png", "alt": null },
"etiqueta": null
},
{
"tipo": "manual",
"modo": "pdf",
"pdf": { "url": "https://cdn.example.com/manual.pdf" },
"etiqueta": "Descargar manual"
}
],
"estado": { "lifecycle": "active", "storefrontVisible": true },
"orden": 1
}
}

Lo específico del tipo es el despacho de las acciones:

// Etiquetas por defecto cuando el comerciante no puso una.
const ETIQUETAS = {
guia_tallas: 'Guía de tallas',
manual: 'Manual',
'manual-uso': 'Manual de uso',
};
function renderAccion(accion) {
const texto = escapar(accion.etiqueta ?? ETIQUETAS[accion.tipo] ?? 'Ver más');
switch (accion.modo) {
case 'image':
// No es un enlace: abre un modal con la imagen.
return `<button type="button" data-modal="${accion.imagen.url}">${texto}</button>`;
case 'link':
return `<a href="${accion.enlace.url}"
${accion.enlace.nueva_pestana ? 'target="_blank" rel="noopener"' : ''}>
${escapar(accion.enlace.etiqueta ?? accion.etiqueta ?? ETIQUETAS[accion.tipo])}
</a>`;
case 'pdf':
return `<a href="${accion.pdf.url}" target="_blank" rel="noopener">${texto}</a>`;
default:
// Modo desconocido: no romper, simplemente no renderizar.
return '';
}
}

Y la lista de características, donde el ícono es decorativo porque el texto de al lado ya dice lo mismo:

function renderCaracteristica(item) {
return `<li>
${item.icono ? `<img src="${item.icono}" alt="" width="24" height="24">` : ''}
<span>${escapar(item.texto)}</span>
</li>`;
}
  • acciones puede no venir. Es opcional: usa seccion.acciones ?? [].
  • Despacha por modo, no por tipo. Leer accion.enlace cuando modo es "image" da undefined.
  • Incluye un caso default en el switch: si mañana aparece un modo nuevo, tu tema lo ignora en vez de romperse.