Files
Coop-NL/src/lib/emailRenderer.ts
T
Jakub_HostacnyandClaude Opus 5.5 3bd93fde20 feat: premenne {cena_s_kartou} a {zlava_s_kartou} v cenovkach (uloha 12)
- bezne produkty maju volitelne polia Cena s kartou / Zlava s kartou,
  vernostne produkty pouzivaju svoje existujuce hodnoty
- editor cenoviek: tlacidla na pridanie vrstiev a nahlad premennych
- jedinecne ID novych vrstiev (rychle pridanie viacerych vrstiev)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 10:30:32 +02:00

927 lines
63 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NewsletterCampaign, ProductItem } from '@/types/newsletter';
import { SUPABASE_ASSETS_BASE } from './defaultCampaign';
import { renderCleanBadgeSvg } from './badgeUtils';
import { pickAlt } from './altText';
export function renderEmailHtml(campaign: NewsletterCampaign): string {
const renderedModules = campaign.modules.map((m) => renderModule(m, campaign)).join('\n');
return `<!DOCTYPE html>
<html lang="sk" xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="x-apple-disable-message-reformatting">
<meta name="color-scheme" content="light only">
<meta name="supported-color-schemes" content="light">
<meta name="format-detection" content="telephone=no, date=no, address=no, email=no, url=no">
<title>${escapeHtml(campaign.title)}</title>
<!-- Web Font (Moderný font Poppins) -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700;800;900&display=swap" rel="stylesheet">
<style>
/* Vynútený light režim — bráni dark mode inverzii v klientoch */
:root {
color-scheme: light only;
supported-color-schemes: light;
}
/* CSS Reset pre e-maily */
html, body {
margin: 0 auto !important;
padding: 0 !important;
height: 100% !important;
width: 100% !important;
font-family: 'Poppins', Helvetica, Arial, sans-serif !important;
background-color: #001a33;
color-scheme: light only;
-webkit-color-scheme: light only;
}
* {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
}
table, td {
mso-table-lspace: 0pt !important;
mso-table-rspace: 0pt !important;
}
img {
-ms-interpolation-mode: bicubic;
border: 0;
height: auto;
line-height: 100%;
outline: none;
text-decoration: none;
max-width: 100%;
}
a {
text-decoration: none;
}
/* Responzívne štýly pre mobilné zariadenia */
@media screen and (max-width: 600px) {
.email-container {
width: 100% !important;
margin: auto !important;
padding: 0 10px !important;
box-sizing: border-box;
}
.stack-column,
.stack-column-center {
display: block !important;
width: 100% !important;
max-width: 100% !important;
direction: ltr !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
.stack-column-center {
text-align: center !important;
}
.mobile-padding {
padding-left: 15px !important;
padding-right: 15px !important;
}
.mobile-spacer {
height: 15px !important;
display: block !important;
}
.btn {
display: inline-block !important;
width: auto !important;
box-sizing: border-box;
}
.glass-product {
margin-bottom: 15px !important;
}
.logo-img {
width: 90px !important;
}
.header-link {
display: block !important;
margin: 3px 0 3px auto !important;
padding: 4px 10px !important;
width: 75px !important;
font-size: 9px !important;
text-align: center !important;
margin-right: 0 !important;
}
.mobile-header-cell {
padding: 3px 0 !important;
}
.stack-column-center a {
display: inline-block !important;
width: auto !important;
margin: 5px 3px !important;
vertical-align: middle;
}
.top-msg { font-size: 11px !important; line-height: 1.4 !important; }
.top-tip { font-size: 10px !important; padding: 1px 6px !important; }
.top-link { font-size: 11px !important; }
}
/* ====== DARK MODE OVERRIDE — Outlook.com / O365 web / dark-mode klienty ====== */
[data-ogsc] body, [data-ogsb] body { background-color: #001a33 !important; }
[data-ogsc] .bg-gradient-mso, [data-ogsb] .bg-gradient-mso { background-color: #001a33 !important; }
[data-ogsc] .card-fantasticky, [data-ogsb] .card-fantasticky { background-color: #feeaea !important; }
[data-ogsc] .card-domace, [data-ogsb] .card-domace { background-color: #fef0c7 !important; }
[data-ogsc] .card-grill, [data-ogsb] .card-grill { background-color: #dcb085 !important; }
[data-ogsc] .card-vernostna, [data-ogsb] .card-vernostna { background-color: #e6e1d9 !important; }
[data-ogsc] .card-white-bg, [data-ogsb] .card-white-bg { background-color: #fefefe !important; }
[data-ogsc] .force-text-dark, [data-ogsb] .force-text-dark { color: #1a1a1a !important; }
[data-ogsc] .force-text-white, [data-ogsb] .force-text-white { color: #fefefe !important; }
[data-ogsc] .force-bg-red, [data-ogsb] .force-bg-red { background-color: #e2001a !important; }
[data-ogsc] .force-bg-blue, [data-ogsb] .force-bg-blue { background-color: #003399 !important; }
[data-ogsc] .force-bg-darkblue, [data-ogsb] .force-bg-darkblue { background-color: #29446f !important; }
[data-ogsc] .force-bg-yellow, [data-ogsb] .force-bg-yellow { background-color: #f9ba3c !important; }
[data-ogsc] .force-bg-yellow2, [data-ogsb] .force-bg-yellow2 { background-color: #ffde00 !important; }
[data-ogsc] .force-bg-orange, [data-ogsb] .force-bg-orange { background-color: #f68e1e !important; }
[data-ogsc] .force-bg-green, [data-ogsb] .force-bg-green { background-color: #005239 !important; }
[data-ogsc] .force-bg-articleblue, [data-ogsb] .force-bg-articleblue { background-color: #5c85a8 !important; }
@media (prefers-color-scheme: dark) {
:root { color-scheme: light only !important; supported-color-schemes: light !important; }
html, body, table, td, div, p, span, h1, h2, h3, h4 { color-scheme: light only !important; }
body, .bg-gradient-mso { background-color: #001a33 !important; }
.card-fantasticky { background-color: #feeaea !important; }
.card-domace { background-color: #fef0c7 !important; }
.card-grill { background-color: #dcb085 !important; }
.card-vernostna { background-color: #e6e1d9 !important; }
.card-white-bg { background-color: #fefefe !important; }
.force-text-dark { color: #1a1a1a !important; }
.force-text-white { color: #fefefe !important; }
.force-bg-red { background-color: #e2001a !important; }
.force-bg-blue { background-color: #003399 !important; }
.force-bg-darkblue { background-color: #29446f !important; }
.force-bg-yellow { background-color: #f9ba3c !important; }
.force-bg-yellow2 { background-color: #ffde00 !important; }
.force-bg-orange { background-color: #f68e1e !important; }
.force-bg-green { background-color: #005239 !important; }
.force-bg-articleblue { background-color: #5c85a8 !important; }
}
</style>
<!--[if mso]>
<style type="text/css">
body, table, td { font-family: Arial, Helvetica, sans-serif !important; }
.bg-gradient-mso { background-color: #001a33 !important; }
</style>
<![endif]-->
</head>
<body class="body bg-gradient-mso" width="100%" bgcolor="#001a33" style="margin: 0; padding: 0 !important; mso-line-height-rule: exactly; background-color: #001a33;">
<!-- Preheader (skrytý — zobrazí sa v náhľade doručenej pošty) -->
<div style="display: none; max-height: 0; max-width: 0; overflow: hidden; opacity: 0; font-size: 1px; line-height: 1px; color: #fefefe; mso-hide: all;">
${escapeHtml(campaign.preheader || '')}
</div>
<div style="display: none; max-height: 0; max-width: 0; overflow: hidden; opacity: 0; font-size: 1px; line-height: 1px; color: #fefefe; mso-hide: all;">
&#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204; &#847; &#8204;
</div>
<center style="width: 100%; background-color: #001a33;">
<table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#001a33" style="background-color: #001a33; background-image: linear-gradient(135deg, #005bb5 0%, #000d1a 100%);">
<tr>
<td align="center" valign="top">
<!--[if mso | IE]>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="600" align="center" style="width:600px;"><tr><td align="center" valign="top" style="padding-top:15px; padding-bottom:30px;">
<![endif]-->
<div style="max-width: 600px; margin: 0 auto; padding-top: 15px; padding-bottom: 30px;" class="email-container">
${renderedModules}
</div>
<!--[if mso | IE]>
</td></tr></table>
<![endif]-->
</td>
</tr>
</table>
</center>
</body>
</html>`;
}
export function renderSingleModuleHtml(m: any, campaign: NewsletterCampaign): string {
return renderModule(m, campaign);
}
function renderModule(m: any, campaign: NewsletterCampaign): string {
switch (m.type) {
case 'announcement':
if (m.data?.enabled === false) {
return '';
}
return `
<!-- 0. TOP OZNAMOVACIA LIŠTA -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin: 15px 0 20px 0;">
<tr>
<td align="center">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" class="force-bg-red" bgcolor="#e2001a" style="background-color: #e2001a; background: linear-gradient(to right, #ff3d4f, #e2001a); border: 0; border-radius: 12px; box-shadow: 0 10px 25px rgba(226, 0, 26, 0.3);">
<tr>
<td style="padding: 10px 20px; text-align: center;">
<!--[if mso]><table role="presentation" border="0" cellspacing="0" cellpadding="0" align="center" width="600"><tr><td align="center" valign="middle"><![endif]-->
<div style="display: inline-block; vertical-align: middle;" class="stack-column-center">
<span class="top-tip" style="background-color: #fefefe; color: #e2001a; font-size: 10px; font-weight: 800; padding: 2px 8px; border: 0; border-radius: 4px; margin: 4px 5px; vertical-align: middle; display: inline-block; letter-spacing: 1px; box-shadow: 0 2px 5px rgba(0,0,0,0.1);">${escapeHtml(m.data.tipBadge || 'TIP')}</span>
</div>
<div style="display: inline-block; vertical-align: middle;" class="stack-column-center">
<span class="top-msg force-text-white" style="color: #fefefe; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; vertical-align: middle; display: inline-block; padding: 5px 10px;">${escapeHtml(m.data.text || '')}</span>
</div>
${m.data.linkText ? `
<div style="display: inline-block; vertical-align: middle;" class="stack-column-center">
<a href="${escapeHtml(m.data.linkUrl || '#')}" class="top-link" style="color: #ffde00; font-size: 11px; font-weight: 800; text-decoration: none; text-transform: uppercase; display: inline-block; padding: 5px 10px;">${escapeHtml(m.data.linkText)} <span style="font-size: 14px; line-height: 1;">&rsaquo;</span></a>
</div>` : ''}
<!--[if mso]></td></tr></table><![endif]-->
</td>
</tr>
</table>
</td>
</tr>
</table>`;
case 'header':
const rawNav = m.data.navLinks && m.data.navLinks.length > 0 ? m.data.navLinks : [
{ label: 'Letáky', url: 'https://www.coop.sk/sk/letaky', variant: 'outline' },
{ label: 'COOP Klub', url: 'https://www.coopklub.sk', variant: 'filled' }
];
const navLinks = rawNav.map((link: any) => {
if (link.variant === 'filled') {
return `<a href="${escapeHtml(link.url || '#')}" class="header-link force-text-white force-bg-blue" style="color: #fefefe; font-size: 12px; font-weight: 700; text-decoration: none; text-transform: uppercase; padding: 10px 16px; background-color: #003399; border-radius: 20px; display: inline-block; letter-spacing: 0.5px; box-shadow: 0 4px 10px rgba(0, 51, 153, 0.2);">${escapeHtml(link.label || 'Odkaz')}</a>`;
}
return `<a href="${escapeHtml(link.url || '#')}" class="header-link" style="color: #e2001a; font-size: 12px; font-weight: 700; text-decoration: none; text-transform: uppercase; padding: 8px 16px; border: 2px solid #e2001a; border-radius: 20px; margin-right: 6px; display: inline-block; letter-spacing: 0.5px;">${escapeHtml(link.label || 'Odkaz')}</a>`;
}).join('\n');
return `
<!-- 1. HLAVIČKA -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" class="card-white card-white-bg" bgcolor="#fefefe" style="background-color: #fefefe; border: 0; border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); overflow: hidden;">
<tr>
<td style="padding: 15px 20px;" class="mobile-padding">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td width="35%" valign="middle" style="text-align: left;" class="mobile-header-cell">
<a href="${escapeHtml(m.data.logoLink || 'https://www.coop.sk')}" target="_blank">
<img src="${escapeHtml(m.data.logoUrl)}" width="130" alt="${escapeHtml(pickAlt(m.data.logoAlt, 'COOP Jednota'))}" style="display: inline-block; border: 0; border-radius: 8px;" class="logo-img">
</a>
</td>
<td width="65%" valign="middle" style="text-align: right;" class="mobile-header-cell">
${navLinks}
</td>
</tr>
</table>
</td>
</tr>
</table>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td height="25" style="font-size: 25px; line-height: 25px;">&nbsp;</td></tr></table>`;
case 'banner':
return `
<!-- KAMPAŇ BANNER -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" class="card-white card-white-bg" bgcolor="#fefefe" style="background-color: #fefefe; border: 0; border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); overflow: hidden;">
<tr>
<td>
<a href="${escapeHtml(m.data.linkUrl || '#')}">
<img src="${escapeHtml(m.data.imageUrl)}" width="600" alt="${escapeHtml(pickAlt(m.data.altText, m.data.title, m.title, 'COOP Jednota'))}" style="display: block; border: 0; width: 100%; max-width: 100%; border-radius: ${m.data.title || m.data.description || m.data.buttonText ? '16px 16px 0 0' : '16px'};">
</a>
</td>
</tr>
${m.data.title || m.data.description || m.data.buttonText ? `
<tr>
<td style="padding: 30px 20px; text-align: center;" class="mobile-padding">
${m.data.title ? `<h1 class="force-text-dark" style="margin: 0 0 15px 0; font-size: 24px; color: #1a1a1a; font-weight: 700;">${escapeHtml(m.data.title)}</h1>` : ''}
${m.data.description ? `<p class="force-text-dark" style="margin: 0 0 25px 0; font-size: 15px; line-height: 1.5; color: #555555;">${escapeHtml(m.data.description)}</p>` : ''}
${m.data.buttonText ? `
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;" class="btn-table">
<tr>
<td style="text-align: center;">
<a href="${escapeHtml(m.data.buttonUrl || '#')}" class="btn force-text-white" style="background-color: ${m.data.buttonBgColor || '#e2001a'}; color: #fefefe; font-family: 'Poppins', sans-serif; font-size: 13px; font-weight: 800; text-decoration: none; padding: 12px 30px; border-radius: 25px; display: inline-block; text-transform: uppercase; letter-spacing: 0.5px; box-shadow: 0 4px 12px rgba(226, 0, 26, 0.35);">${escapeHtml(m.data.buttonText)}</a>
</td>
</tr>
</table>` : ''}
</td>
</tr>` : ''}
</table>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td height="25" style="font-size: 25px; line-height: 25px;">&nbsp;</td></tr></table>`;
case 'products_grid':
const globalValidity = m.data.globalValidity || '';
const products: ProductItem[] = m.data.products || [];
const cardBg = m.data.cardBgColor || '#feeaea';
const cardBorder = m.data.cardBorderColor || '#f5d4d4';
const badgeHdrBg = m.data.badgeHeaderBg || '#fefefe';
const badgeHdrColor = m.data.badgeHeaderColor || '#000000';
const badgePrcBg = m.data.badgePriceBg || '#e2001a';
const badgePrcColor = m.data.badgePriceColor || '#fefefe';
const badgeCustomTxt = m.data.badgeHeaderCustomText || null;
const cols = m.data.columns === 1 ? 1 : 2;
const rows: ProductItem[][] = [];
for (let i = 0; i < products.length; i += cols) {
rows.push(products.slice(i, i + cols));
}
const rowsHtml = rows.map((row) => {
if (cols === 1) {
const p = row[0];
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-bottom: 12px;">
<tr>
<td width="100%" valign="top">
${renderExactProductCard(p, globalValidity, cardBg, cardBorder, badgeHdrBg, badgeHdrColor, badgePrcBg, badgePrcColor, badgeCustomTxt, m.data.sectionTitle)}
</td>
</tr>
</table>`;
}
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="1" style="margin-bottom: 12px;">
<tr>
${row.map((p, idx) => `
<td width="50%" valign="top" height="100%" style="padding-${idx === 0 ? 'right: 6px' : 'left: 6px'}; padding-bottom: 0; height: 100%;">
${renderExactProductCard(p, globalValidity, cardBg, cardBorder, badgeHdrBg, badgeHdrColor, badgePrcBg, badgePrcColor, badgeCustomTxt, m.data.sectionTitle)}
</td>`).join('\n')}
${row.length === 1 ? '<td width="50%" valign="top" height="100%" style="height: 100%;"></td>' : ''}
</tr>
</table>
`;
}).join('\n');
return `
<!-- PRODUKTOVÁ SEKCIA: ${escapeHtml(m.data.sectionTitle || '')} -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
${m.data.sectionTitle ? `
<tr>
<td style="text-align: center; padding-top: 25px; padding-bottom: 25px;">
<h2 class="force-text-white" style="margin: 0; font-size: 24px; color: #fefefe; font-weight: 900; text-transform: uppercase; text-shadow: 0px 2px 5px rgba(0,0,0,0.5);">${escapeHtml(m.data.sectionTitle)}</h2>
</td>
</tr>` : ''}
<tr>
<td>
${rowsHtml}
</td>
</tr>
</table>`;
case 'loyalty_club':
if (m.data.enabled === false) {
return '';
}
const lProducts = m.data.products || [];
return `
<!-- 7. VERNOSTNÉ ZĽAVY (COOP KLUB) -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="text-align: center; padding-top: 25px; padding-bottom: 25px;">
<h2 class="force-text-white" style="margin: 0; font-size: 24px; color: #fefefe; font-weight: 900; text-transform: uppercase; text-shadow: 0px 2px 5px rgba(0,0,0,0.5);">${escapeHtml(m.data.sectionTitle || 'Vernostné zľavy')}</h2>
</td>
</tr>
<!-- Banner karta -->
<tr>
<td style="padding-bottom: 20px;">
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" class="card-white card-vernostna" bgcolor="#e6e1d9" style="background-color: #e6e1d9; border: 0; border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15); overflow: hidden;">
<tr>
<td>
<a href="${escapeHtml(m.data.headerLink || '#')}">
<img src="${escapeHtml(m.data.headerImageUrl)}" width="600" alt="${escapeHtml(pickAlt(m.data.headerImageAlt, m.data.title, m.data.sectionTitle, 'Vernostné zľavy'))}" style="display: block; border: 0; width: 100%; max-width: 100%; border-radius: 16px;">
</a>
</td>
</tr>
</table>
</td>
</tr>
<!-- Produktové karty - 2 vedľa seba -->
<tr>
<td>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
${lProducts.map((p: any, idx: number) => `
<td width="50%" valign="top" height="100%" style="padding-${idx === 0 ? 'right: 6px' : 'left: 6px'}; padding-bottom: 0; height: 100%;">
${renderExactLoyaltyCard(p, m.data.globalValidity || '2. 5. – 15. 5. 2026')}
</td>`).join('\n')}
</tr>
</table>
</td>
</tr>
</table>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td height="25" style="font-size: 25px; line-height: 25px;">&nbsp;</td></tr></table>`;
case 'articles_row':
const it1 = m.data.item1;
const it2 = m.data.item2;
// Ak ide o letáky
if (it1?.btnText) {
return `
<!-- DVA STĹPCE - LETÁKY -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="1">
<tr>
<!-- Leták 1 -->
<td width="50%" valign="top" height="100%" style="padding-right: 6px; height: 100%;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" bgcolor="${it1.bgColor}" style="background-color: ${it1.bgColor}; border: 0; border-radius: 16px; overflow: hidden; box-shadow: 0 8px 25px rgba(0, 0, 0, 0.25); height: 100%; min-height: 100%;">
<tr height="1">
<td valign="top" style="line-height: 0; height: 1px;">
<a href="${escapeHtml(it1.linkUrl || '#')}">
<img src="${escapeHtml(it1.imageUrl)}" width="300" alt="${escapeHtml(pickAlt(it1.altText, it1.title, m.data.title, m.title, 'Aktuálny leták'))}" style="display: block; border: 0; width: 100%; max-width: 100%;">
</a>
</td>
</tr>
<tr>
<td valign="bottom" style="padding: 20px 15px 25px 15px; text-align: center; height: 100%;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" style="height: 100%;">
<tr>
<td valign="top" style="text-align: center; min-height: 44px; padding-bottom: 15px;">
<h3 class="force-text-white" style="margin: 0; font-size: 17px; color: ${it1.textColor || '#fefefe'}; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; line-height: 1.25;">${escapeHtml(it1.title)}</h3>
</td>
</tr>
<tr height="1">
<td valign="bottom" align="center" style="text-align: center; height: 1px;">
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;" class="btn-table">
<tr>
<td style="text-align: center;">
<a href="${escapeHtml(it1.linkUrl || '#')}" class="btn force-text-white" style="background-color: ${it1.btnBgColor || '#e2001a'}; color: #fefefe; font-family: 'Poppins', sans-serif; font-size: 12px; font-weight: 800; text-decoration: none; padding: 10px 24px; border-radius: 25px; display: inline-block; text-transform: uppercase; letter-spacing: 0.5px; box-shadow: 0 4px 12px rgba(226, 0, 26, 0.35);">${escapeHtml(it1.btnText)}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
<!-- Leták 2 -->
<td width="50%" valign="top" height="100%" style="padding-left: 6px; height: 100%;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" bgcolor="${it2.bgColor}" style="background-color: ${it2.bgColor}; border: 0; border-radius: 16px; overflow: hidden; box-shadow: 0 8px 25px rgba(0, 0, 0, 0.25); height: 100%; min-height: 100%;">
<tr height="1">
<td valign="top" style="line-height: 0; height: 1px;">
<a href="${escapeHtml(it2.linkUrl || '#')}">
<img src="${escapeHtml(it2.imageUrl)}" width="300" alt="${escapeHtml(pickAlt(it2.altText, it2.title, m.data.title, m.title, 'Aktuálny leták'))}" style="display: block; border: 0; width: 100%; max-width: 100%;">
</a>
</td>
</tr>
<tr>
<td valign="bottom" style="padding: 20px 15px 25px 15px; text-align: center; height: 100%;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" style="height: 100%;">
<tr>
<td valign="top" style="text-align: center; min-height: 44px; padding-bottom: 15px;">
<h3 style="margin: 0; font-size: 17px; color: ${it2.textColor || '#29446f'}; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; line-height: 1.25;">${escapeHtml(it2.title)}</h3>
</td>
</tr>
<tr height="1">
<td valign="bottom" align="center" style="text-align: center; height: 1px;">
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: auto;" class="btn-table">
<tr>
<td style="text-align: center;">
<a href="${escapeHtml(it2.linkUrl || '#')}" class="btn force-text-white" style="background-color: ${it2.btnBgColor || '#29446f'}; color: #fefefe; font-family: 'Poppins', sans-serif; font-size: 12px; font-weight: 800; text-decoration: none; padding: 10px 24px; border-radius: 25px; display: inline-block; text-transform: uppercase; letter-spacing: 0.5px; box-shadow: 0 4px 12px rgba(41, 68, 111, 0.3);">${escapeHtml(it2.btnText)}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td height="25" style="font-size: 25px; line-height: 25px;">&nbsp;</td></tr></table>`;
}
// Inak články / recepty
return `
<!-- ČLÁNKY / RECEPTY (2 stĺpce) -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="1">
<tr>
<!-- Článok 1 -->
<td width="50%" valign="top" height="100%" style="padding-right: 6px; padding-bottom: 20px; height: 100%;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" bgcolor="${it1.bgColor}" style="background-color: ${it1.bgColor}; border: 0; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.15); height: 100%; min-height: 100%;">
<tr height="240">
<td background="${escapeHtml(it1.imageUrl)}" bgcolor="${it1.bgColor}" width="300" height="240" valign="bottom" style="background-image: url('${escapeHtml(it1.imageUrl)}'); background-size: cover; background-position: center; height: 240px;">
<!--[if gte mso 9]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:300px;height:240px;">
<v:fill type="frame" src="${escapeHtml(it1.imageUrl)}" color="${it1.bgColor}" />
<v:textbox inset="0,0,0,0">
<![endif]-->
<div style="background: linear-gradient(to bottom, rgba(246, 142, 30, 0), rgba(246, 142, 30, 1)); height: 80px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%">
<tr>
<td align="center" valign="bottom" style="padding-bottom: 10px;">
<img src="${escapeHtml(it1.badgeImgUrl)}" width="70" alt="${escapeHtml(pickAlt(it1.altText, it1.title, 'Recepty'))}" style="display: block; border: 0; margin: 0 auto;">
</td>
</tr>
</table>
</div>
<!--[if gte mso 9]></v:textbox></v:rect><![endif]-->
</td>
</tr>
<tr>
<td style="padding: 15px 15px 25px 15px; text-align: center; height: 100%;" valign="middle">
<h3 class="force-text-white" style="margin: 0; font-size: 17px; color: #fefefe; font-weight: 800; text-transform: uppercase; letter-spacing: 0.8px; line-height: 1.25;">${escapeHtml(it1.title)}</h3>
</td>
</tr>
</table>
</td>
<!-- Článok 2 -->
<td width="50%" valign="top" height="100%" style="padding-left: 6px; padding-bottom: 20px; height: 100%;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" bgcolor="${it2.bgColor}" style="background-color: ${it2.bgColor}; border: 0; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.15); height: 100%; min-height: 100%;">
<tr height="240">
<td background="${escapeHtml(it2.imageUrl)}" bgcolor="${it2.bgColor}" width="300" height="240" valign="bottom" style="background-image: url('${escapeHtml(it2.imageUrl)}'); background-size: cover; background-position: center; height: 240px;">
<!--[if gte mso 9]>
<v:rect xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false" style="width:300px;height:240px;">
<v:fill type="frame" src="${escapeHtml(it2.imageUrl)}" color="${it2.bgColor}" />
<v:textbox inset="0,0,0,0">
<![endif]-->
<div style="background: linear-gradient(to bottom, rgba(92, 133, 168, 0), rgba(92, 133, 168, 1)); height: 80px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%">
<tr>
<td align="center" valign="bottom" style="padding-bottom: 10px;">
<img src="${escapeHtml(it2.badgeImgUrl)}" width="70" alt="${escapeHtml(pickAlt(it2.altText, it2.title, 'Časopis Jednota'))}" style="display: block; border: 0; margin: 0 auto;">
</td>
</tr>
</table>
</div>
<!--[if gte mso 9]></v:textbox></v:rect><![endif]-->
</td>
</tr>
<tr>
<td style="padding: 15px 15px 25px 15px; text-align: center; height: 100%;" valign="middle">
<h3 class="force-text-white" style="margin: 0; font-size: 17px; color: #fefefe; font-weight: 800; text-transform: uppercase; letter-spacing: 0.8px; line-height: 1.25;">${escapeHtml(it2.title)}</h3>
</td>
</tr>
</table>
</td>
</tr>
</table>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td height="25" style="font-size: 25px; line-height: 25px;">&nbsp;</td></tr></table>`;
case 'footer':
return `
<!-- 9. PÄTIČKA (1:1 s originálnou šablónou) -->
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" class="card-white card-white-bg" bgcolor="#fefefe" style="background-color: #fefefe; border: 0; border-radius: 16px; padding: 40px 20px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);">
<tr>
<td style="padding: 30px 10px;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<!-- Sekcia 1: Aplikácia -->
<td width="33.33%" valign="top" class="stack-column-center" style="padding: 10px; text-align: center;">
<h4 class="force-text-dark" style="margin: 0 0 15px 0; font-size: 14px; color: #444444; font-weight: 700; text-align: center;">Aplikácia</h4>
<a href="${escapeHtml(m.data.googlePlayUrl || 'https://play.google.com/store/apps/details?id=sk.coop.jednota.klub')}" target="_blank" style="display: block; margin-bottom: 10px;">
<img src="${SUPABASE_ASSETS_BASE}logo-google_play.png" width="120" alt="Google Play" style="display: block; border: 0; margin: 0 auto;">
</a>
<a href="${escapeHtml(m.data.appStoreUrl || 'https://apps.apple.com/sk/app/coop-jednota/id1044610196')}" target="_blank" style="display: block;">
<img src="${SUPABASE_ASSETS_BASE}logo-app_store.png" width="120" alt="App Store" style="display: block; border: 0; margin: 0 auto;">
</a>
</td>
<!-- Sekcia 2: Sociálne siete -->
<td width="33.33%" valign="top" class="stack-column-center" style="padding: 10px; text-align: center;">
<h4 class="force-text-dark" style="margin: 0 0 15px 0; font-size: 14px; color: #444444; font-weight: 700; text-align: center;">Sledujte nás</h4>
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="center" style="margin: 0 auto;">
<tr>
<td style="padding: 0 5px;"><a href="${escapeHtml(m.data.socials?.facebook || '#')}" target="_blank"><img src="${SUPABASE_ASSETS_BASE}ikona_fb.png" width="30" alt="Facebook" style="display: block; border: 0;"></a></td>
<td style="padding: 0 5px;"><a href="${escapeHtml(m.data.socials?.instagram || '#')}" target="_blank"><img src="${SUPABASE_ASSETS_BASE}ikona_ig.png" width="30" alt="Instagram" style="display: block; border: 0;"></a></td>
<td style="padding: 0 5px;"><a href="${escapeHtml(m.data.socials?.youtube || '#')}" target="_blank"><img src="${SUPABASE_ASSETS_BASE}ikona_yt.png" width="30" alt="YouTube" style="display: block; border: 0;"></a></td>
<td style="padding: 0 5px;"><a href="${escapeHtml(m.data.socials?.tiktok || '#')}" target="_blank"><img src="${SUPABASE_ASSETS_BASE}ikona_tt.png" width="30" alt="TikTok" style="display: block; border: 0;"></a></td>
</tr>
</table>
</td>
<!-- Sekcia 3: Naše projekty -->
<td width="33.33%" valign="top" class="stack-column-center" style="padding: 10px; text-align: center;">
<h4 class="force-text-dark" style="margin: 0 0 15px 0; font-size: 14px; color: #444444; font-weight: 700; text-align: center;">Projekty</h4>
<a href="${escapeHtml(m.data.foundationUrl || 'https://www.nadaciacoop.sk')}" target="_blank" style="display: block; margin-bottom: 10px;">
<img src="${SUPABASE_ASSETS_BASE}logo-COOP_nadacia.png" width="120" alt="Nadácia COOP Jednota" style="display: block; border: 0; margin: 0 auto;">
</a>
<a href="${escapeHtml(m.data.recipesUrl || 'https://recepty.coop.sk')}" target="_blank" style="display: block;">
<img src="${SUPABASE_ASSETS_BASE}logo-netradicne_recepty.png" width="120" alt="Netradičné recepty" style="display: block; border: 0; margin: 0 auto;">
</a>
</td>
</tr>
</table>
<!-- Disclaimer a Unsubscribe -->
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="margin-top: 15px; border-top: 0; padding-top: 15px;">
<tr>
<td class="force-text-dark" style="text-align: center; font-size: 11px; color: #666666; line-height: 1.5;">
<a href="${escapeHtml(m.data.recipesUrl || 'https://recepty.coop.sk')}" style="color: #444444; text-decoration: underline;">Prihláste sa</a> aj na odber newslettera (Ne)tradičných receptov.
<div style="height: 8px; line-height: 8px; font-size: 1px;">&nbsp;</div>
Ak sa chcete znova prihlásiť na odber newslettera, <a href="https://www.coop.sk" style="color: #444444; text-decoration: underline;">kliknite sem.</a>
<div style="height: 8px; line-height: 8px; font-size: 1px;">&nbsp;</div>
Pre odhlásenie z odoberania newslettera <a href="${escapeHtml(m.data.unsubscribeUrl || '*|UNSUB|*')}" style="color: #444444; text-decoration: underline;">kliknite sem.</a><br><br>
${m.data.disclaimer}
</td>
</tr>
</table>
</td>
</tr>
</table>`;
default:
return '';
}
}
function renderExactProductCard(
p: ProductItem,
globalValidity: string,
cardBg: string,
cardBorder: string,
badgeHdrBg: string,
badgeHdrColor: string,
badgePrcBg: string,
badgePrcColor: string,
badgeCustomTxt: string | null,
sectionTitle?: string
): string {
const validity = p.validityOverride || globalValidity;
const effectiveCardBg = p.cardBgColor || cardBg || '#feeaea';
const storesHtml = `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="right">
${p.stores?.potraviny ? `<tr><td align="right" style="line-height: 0;"><table role="presentation" cellspacing="0" cellpadding="0" border="0" align="right"><tr><td style="border: 0; background-color: #e2001a; padding: 0; line-height: 0;"><img src="${SUPABASE_ASSETS_BASE}stit_potraviny.png" height="10" alt="Potraviny" style="display: block; height: 10px; width: auto; border: 0;"></td></tr></table></td></tr>` : ''}
${p.stores?.supermarket ? `<tr><td align="right" style="line-height: 0;"><table role="presentation" cellspacing="0" cellpadding="0" border="0" align="right"><tr><td style="border: 0; background-color: #f9890e; padding: 0; line-height: 0;"><img src="${SUPABASE_ASSETS_BASE}stit_supermarket.png" height="10" alt="Supermarket" style="display: block; height: 10px; width: auto; border: 0;"></td></tr></table></td></tr>` : ''}
${p.stores?.tempo ? `<tr><td align="right" style="line-height: 0;"><table role="presentation" cellspacing="0" cellpadding="0" border="0" align="right"><tr><td style="border: 0; background-color: #005239; padding: 0; line-height: 0;"><img src="${SUPABASE_ASSETS_BASE}stit_tempo.png" height="10" alt="Tempo" style="display: block; height: 10px; width: auto; border: 0;"></td></tr></table></td></tr>` : ''}
</table>`;
const badgeLayout = p.badgeLayout || 'below';
// Pomocná funkcia na render samotnej cenovky (bez obalového <td>)
const renderBadgeContent = (bWidth: number, bAlign: string, padTop: number, padBottom: number, marginStyle: string) => {
// Vlastná PNG cenovka (výnimka) má prednosť pred šablónou; po jej odstránení sa vráti šablóna
if (p.customBadgePngUrl) {
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="${bAlign}" style="${marginStyle} width: ${bWidth}px; padding-top: ${padTop}px; padding-bottom: ${padBottom}px;">
<tr>
<td align="${bAlign}">
<img src="${escapeHtml(p.customBadgePngUrl)}" width="${bWidth}" alt="${escapeHtml(pickAlt(p.title, 'Cenovka'))}" style="display: block; width: 100%; max-width: ${bWidth}px; margin: 0 auto;">
</td>
</tr>
</table>`;
}
if (p.badgeCustomTemplate) {
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="${bAlign}" style="${marginStyle} width: ${bWidth}px; padding-top: ${padTop}px; padding-bottom: ${padBottom}px;">
<tr>
<td align="${bAlign}">
${renderCleanBadgeSvg(p.badgeCustomTemplate, p, bWidth)}
</td>
</tr>
</table>`;
}
const mainFontSize = Math.max(28, Math.round(bWidth * 0.54));
const subFontSize = Math.max(14, Math.round(bWidth * 0.27));
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="${bAlign}" style="${marginStyle} width: ${bWidth}px; padding-top: ${padTop}px; padding-bottom: ${padBottom}px;">
<tr>
<td align="${bAlign}">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" style="background-color: ${badgeHdrBg}; border: 0; border-radius: 12px; box-shadow: 0 4px 10px rgba(0,0,0,0.18); width: ${bWidth}px; overflow: hidden;">
${badgeCustomTxt ? `
<tr>
<td bgcolor="${badgeHdrBg}" style="padding: 4px 4px; text-align: center; background-color: ${badgeHdrBg};">
<span style="font-family: 'Arial Narrow', Arial, sans-serif; font-size: 10px; color: ${badgeHdrColor}; font-weight: 800; text-transform: uppercase;">${escapeHtml(badgeCustomTxt)}</span>
</td>
</tr>` : (p.discountText ? `
<tr>
<td style="padding: 2px 4px; text-align: right; background-color: ${badgeHdrBg};">
<span style="font-family: 'Arial Narrow', Arial, sans-serif; font-size: 9px; color: ${badgeHdrColor}; font-weight: 700; text-transform: lowercase;">${escapeHtml(p.badgeText || 'zľava')}</span>
<span style="font-family: 'Arial Narrow', Arial, sans-serif; font-size: 20px; color: ${badgeHdrColor}; font-weight: 900; line-height: 1; margin-left: 2px;">${escapeHtml(p.discountText)}</span>
</td>
</tr>` : '')}
<tr>
<td class="force-text-white" bgcolor="${badgePrcBg}" style="background-color: ${badgePrcBg}; padding: 4px 2px 2px 2px; font-family: 'Arial Narrow', Arial, sans-serif; color: ${badgePrcColor}; font-weight: 900; line-height: 1; text-align: center; border-radius: 0 0 12px 12px;">
<span style="font-size: ${mainFontSize}px;">${escapeHtml(p.priceMain)}</span><sup style="font-size: ${subFontSize}px; vertical-align: top; line-height: 1.2;">${escapeHtml(p.priceSub)}</sup>
</td>
</tr>
</table>
</td>
</tr>
</table>`;
};
if (badgeLayout === 'side') {
// Rozloženie: obrázok vľavo | cenovka vpravo
const bWidth = p.badgeWidth || (p.badgeCustomTemplate ? (p.badgeCustomTemplate.width || 110) : 95);
const bAlign = 'center';
const padTop = p.badgePaddingTop ?? (p.badgeCustomTemplate?.paddingTop ?? 0);
const padBottom = p.badgePaddingBottom ?? (p.badgeCustomTemplate?.paddingBottom ?? 0);
const marginStyle = 'margin: 0 auto;';
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" class="glass-product" bgcolor="${effectiveCardBg}" style="background-color: ${effectiveCardBg}; border: 0; border-radius: 16px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); height: 100%; min-height: 100%;">
<tr height="1">
<td colspan="2" style="padding: 15px 15px 0 15px; height: 1px;" valign="top">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="font-size: 9px; color: #666666; font-weight: 500;" valign="top">${escapeHtml(validity)}</td>
<td align="right">${storesHtml}</td>
</tr>
</table>
</td>
</tr>
<tr>
<td width="58%" height="160" style="padding: 8px 4px 8px 8px; text-align: center; height: 160px;" valign="middle">
<img src="${escapeHtml(p.imageUrl)}" width="180" alt="${escapeHtml(pickAlt(p.imageAlt, p.title, sectionTitle, 'Produkt'))}" style="display: block; width: 100%; max-width: 180px; margin: 0 auto; border-radius: 8px;">
</td>
<td width="42%" style="padding: 8px 8px 8px 4px; text-align: center;" valign="middle">
${renderBadgeContent(bWidth, bAlign, padTop, padBottom, marginStyle)}
${p.unitPrice ? `<div style="font-size: 8px; color: #666666; font-weight: 500; font-family: Arial, sans-serif; text-align: center; margin-top: 4px;">${escapeHtml(p.unitPrice)}</div>` : ''}
</td>
</tr>
<tr>
<td colspan="2" height="54" style="padding: 0 8px 10px 8px; text-align: center; height: 54px; min-height: 54px;" valign="top">
<h3 class="force-text-dark" style="margin: 0; font-size: 15px; color: #1a1a1a; font-weight: 600; line-height: 1.25;">${escapeHtml(p.title)}</h3>
</td>
</tr>
</table>`;
}
// Predvolené rozloženie: cenovka pod obrázkom
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" class="glass-product" bgcolor="${effectiveCardBg}" style="background-color: ${effectiveCardBg}; border: 0; border-radius: 16px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); height: 100%; min-height: 100%;">
<tr height="1">
<td style="padding: 15px 15px 0 15px; height: 1px;" valign="top">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="font-size: 9px; color: #666666; font-weight: 500;" valign="top">${escapeHtml(validity)}</td>
<td align="right">${storesHtml}</td>
</tr>
</table>
</td>
</tr>
<tr height="160">
<td height="160" style="padding: 0 8px 15px 8px; text-align: center; height: 160px;" valign="middle">
<img src="${escapeHtml(p.imageUrl)}" width="220" alt="${escapeHtml(pickAlt(p.imageAlt, p.title, sectionTitle, 'Produkt'))}" style="display: block; width: 100%; max-width: 220px; margin: 0 auto; border-radius: 8px;">
</td>
</tr>
<tr>
<td height="54" style="padding: 0 8px 10px 8px; text-align: center; height: 54px; min-height: 54px;" valign="top">
<h3 class="force-text-dark" style="margin: 0; font-size: 15px; color: #1a1a1a; font-weight: 600; line-height: 1.25;">${escapeHtml(p.title)}</h3>
</td>
</tr>
<tr height="1">
<td style="padding: 0 8px 4px 8px; text-align: center;" valign="bottom">
${(() => {
const bWidth = p.badgeWidth || (p.badgeCustomTemplate ? (p.badgeCustomTemplate.width || 110) : 95);
const bAlign = p.badgeAlign || (p.badgeCustomTemplate ? (p.badgeCustomTemplate.align || 'center') : 'center');
const padTop = p.badgePaddingTop ?? (p.badgeCustomTemplate?.paddingTop ?? 0);
const padBottom = p.badgePaddingBottom ?? (p.badgeCustomTemplate?.paddingBottom ?? 0);
let marginStyle = 'margin: 0 auto;';
if (bAlign === 'left') {
marginStyle = 'margin: 0 auto 0 0; margin-right: auto; margin-left: 0;';
} else if (bAlign === 'right') {
marginStyle = 'margin: 0 0 0 auto; margin-left: auto; margin-right: 0;';
}
return renderBadgeContent(bWidth, bAlign, padTop, padBottom, marginStyle);
})()}
</td>
</tr>
<tr height="1">
<td align="center" valign="bottom" style="padding: 2px 8px 12px 8px; text-align: center; height: 1px;">
${p.unitPrice ? `<div style="font-size: 9px; color: #666666; font-weight: 500; font-family: Arial, sans-serif; text-align: center;">${escapeHtml(p.unitPrice)}</div>` : '<div style="font-size: 9px; line-height: 9px; height: 9px;">&nbsp;</div>'}
</td>
</tr>
</table>`;
}
function renderExactLoyaltyCard(p: any, validity: string): string {
const cardValidity = p.validityOverride || validity;
const storesHtml = `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="right">
${p.stores?.potraviny ? `<tr><td align="right" style="line-height: 0;"><table role="presentation" cellspacing="0" cellpadding="0" border="0" align="right"><tr><td style="border: 0; background-color: #e2001a; padding: 0; line-height: 0;"><img src="${SUPABASE_ASSETS_BASE}stit_potraviny.png" height="10" alt="Potraviny" style="display: block; height: 10px; width: auto; border: 0;"></td></tr></table></td></tr>` : ''}
${p.stores?.supermarket ? `<tr><td align="right" style="line-height: 0;"><table role="presentation" cellspacing="0" cellpadding="0" border="0" align="right"><tr><td style="border: 0; background-color: #f9890e; padding: 0; line-height: 0;"><img src="${SUPABASE_ASSETS_BASE}stit_supermarket.png" height="10" alt="Supermarket" style="display: block; height: 10px; width: auto; border: 0;"></td></tr></table></td></tr>` : ''}
${p.stores?.tempo ? `<tr><td align="right" style="line-height: 0;"><table role="presentation" cellspacing="0" cellpadding="0" border="0" align="right"><tr><td style="border: 0; background-color: #005239; padding: 0; line-height: 0;"><img src="${SUPABASE_ASSETS_BASE}stit_tempo.png" height="10" alt="Tempo" style="display: block; height: 10px; width: auto; border: 0;"></td></tr></table></td></tr>` : ''}
</table>`;
const loyaltyBg = p.cardBgColor || '#e6e1d9';
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" height="100%" class="glass-product card-vernostna" bgcolor="${loyaltyBg}" style="background-color: ${loyaltyBg}; border: 0; border-radius: 16px; box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15); height: 100%; min-height: 100%;">
<tr height="1">
<td style="padding: 15px 15px 0 15px; height: 1px;" valign="top">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%">
<tr>
<td style="font-size: 9px; color: #666666; font-weight: 500;" valign="top">${escapeHtml(cardValidity)}</td>
<td align="right">${storesHtml}</td>
</tr>
</table>
</td>
</tr>
<tr height="1">
<td style="padding: 0 8px 15px 8px; text-align: center; height: 160px;" valign="middle">
<img src="${escapeHtml(p.imageUrl)}" width="220" alt="${escapeHtml(pickAlt(p.imageAlt, p.title, 'Produkt'))}" style="display: block; width: 100%; max-width: 220px; margin: 0 auto; border-radius: 8px;">
</td>
</tr>
<tr>
<td style="padding: 0 8px 10px 8px; text-align: center; height: 100%; min-height: 44px;" valign="top">
<h3 class="force-text-dark" style="margin: 0; font-size: 15px; color: #1a1a1a; font-weight: 600; line-height: 1.25;">${escapeHtml(p.title)}</h3>
</td>
</tr>
<tr height="1">
<td style="padding: 0 8px ${p.badgeCustomTemplate || p.customBadgePngUrl ? '4px' : '12px'} 8px; text-align: center; height: 1px;" valign="bottom">
${(() => {
// 1. Ak má vernostný produkt vlastnú PNG cenovku (prednosť) alebo dynamickú šablónu cenovky
if (p.customBadgePngUrl || p.badgeCustomTemplate) {
const bWidth = p.badgeWidth || p.badgeCustomTemplate?.width || 120;
const bAlign = p.badgeAlign || p.badgeCustomTemplate?.align || 'center';
const padTop = p.badgePaddingTop ?? (p.badgeCustomTemplate?.paddingTop ?? 0);
const padBottom = p.badgePaddingBottom ?? (p.badgeCustomTemplate?.paddingBottom ?? 0);
let marginStyle = 'margin: 0 auto;';
if (bAlign === 'left') {
marginStyle = 'margin: 0 auto 0 0; margin-right: auto; margin-left: 0;';
} else if (bAlign === 'right') {
marginStyle = 'margin: 0 0 0 auto; margin-left: auto; margin-right: 0;';
}
const loyaltyProductData = {
priceMain: p.cardPriceMain || p.priceMain || '0',
priceSub: p.cardPriceSub || p.priceSub || '00',
discountText: p.cardDiscount || p.discountText || '',
unitPrice: p.cardUnitPrice || p.unitPrice || '',
cardPriceMain: p.cardPriceMain,
cardPriceSub: p.cardPriceSub,
cardDiscount: p.cardDiscount
};
return `
<table role="presentation" cellspacing="0" cellpadding="0" border="0" align="${bAlign}" style="${marginStyle} width: ${bWidth}px; padding-top: ${padTop}px; padding-bottom: ${padBottom}px;">
<tr>
<td align="${bAlign}">
${p.customBadgePngUrl
? `<img src="${escapeHtml(p.customBadgePngUrl)}" width="${bWidth}" alt="${escapeHtml(pickAlt(p.title, 'Cenovka'))}" style="display: block; width: 100%; max-width: ${bWidth}px; margin: 0 auto;">`
: renderCleanBadgeSvg(p.badgeCustomTemplate, loyaltyProductData, bWidth)}
</td>
</tr>
</table>`;
}
// 2. Predvolený Dual Sticker Block (Bez karty / S kartou) 1:1
return `
<table align="center" role="presentation" cellspacing="0" cellpadding="0" border="0" style="margin: 0 auto; width: 140px;">
<tr>
<td colspan="2">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" style="width: 140px; margin: 0 auto;">
<tr>
<!-- Bez karty -->
<td width="42%" valign="bottom" style="padding-right: 0;">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="background-color: #fefefe; border: 0; border-radius: 10px 0 0 10px; box-shadow: -2px 4px 8px rgba(0,0,0,0.12); overflow: hidden;">
<tr>
<td class="force-bg-red" bgcolor="#e2001a" style="background-color: #e2001a; padding: 4px 2px; text-align: center;">
<div style="font-size: 6px; color: #fefefe; font-weight: 700; text-transform: uppercase; line-height: 1; margin-bottom: 1px;">Bez karty</div>
<div style="font-family: 'Arial Narrow', Arial, sans-serif; font-size: 8px; color: #fefefe; font-weight: 900; line-height: 1;">zľava ${escapeHtml(p.noCardDiscount)}</div>
</td>
</tr>
<tr>
<td style="padding: 2px; text-align: center; color: #000000; font-family: 'Arial Narrow', Arial, sans-serif; font-weight: 900; line-height: 1;">
<span style="font-size: 20px;">${escapeHtml(p.noCardPriceMain)}</span><sup style="font-size: 10px; vertical-align: top; line-height: 1.2;">${escapeHtml(p.noCardPriceSub)}</sup>
</td>
</tr>
</table>
</td>
<!-- S kartou -->
<td width="58%" valign="top">
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%" style="background-color: #fefefe; border: 0; border-radius: 0 10px 10px 0; box-shadow: 4px 4px 12px rgba(0,0,0,0.15); overflow: hidden;">
<tr>
<td style="background-color: #fefefe; padding: 5px 4px 2px 4px; text-align: right;">
<div style="font-size: 7px; color: #e2001a; font-weight: 800; text-transform: uppercase; line-height: 1; text-align: center; margin-bottom: 1px;">S kartou</div>
<div style="text-align: right; line-height: 1;">
<span style="font-family: 'Arial Narrow', Arial, sans-serif; font-size: 8px; color: #000000; font-weight: 700; text-transform: lowercase;">zľava</span>
<span style="font-family: 'Arial Narrow', Arial, sans-serif; font-size: 18px; color: #000000; font-weight: 900; margin-left: 1px;">${escapeHtml(p.cardDiscount)}</span>
</div>
</td>
</tr>
<tr>
<td class="force-bg-red force-text-white" bgcolor="#e2001a" style="background-color: #e2001a; padding: 3px 2px; text-align: center; color: #fefefe; font-family: 'Arial Narrow', Arial, sans-serif; font-weight: 900; line-height: 1; border-radius: 0 0 10px 0;">
<span style="font-size: 48px;">${escapeHtml(p.cardPriceMain)}</span><sup style="font-size: 24px; vertical-align: top; line-height: 1.2;">${escapeHtml(p.cardPriceSub)}</sup>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td width="42%" align="center" style="padding-top: 6px;">
<div style="font-size: 8px; color: #666666; opacity: 0.7; font-weight: 500; font-family: Arial, sans-serif;">${escapeHtml(p.noCardUnitPrice)}</div>
</td>
<td width="58%" align="center" style="padding-top: 6px;">
<div style="font-size: 9px; color: #e2001a; font-weight: 800; font-family: Arial, sans-serif;">${escapeHtml(p.cardUnitPrice)}</div>
</td>
</tr>
</table>`;
})()}
</td>
</tr>
${p.badgeCustomTemplate || p.customBadgePngUrl ? `
<tr height="1">
<td align="center" valign="bottom" style="padding: 2px 8px 12px 8px; text-align: center; height: 1px;">
${p.cardUnitPrice || p.unitPrice ? `<div style="font-size: 9px; color: #666666; font-weight: 500; font-family: Arial, sans-serif; text-align: center;">${escapeHtml(p.cardUnitPrice || p.unitPrice)}</div>` : '<div style="font-size: 9px; line-height: 9px; height: 9px;">&nbsp;</div>'}
</td>
</tr>` : ''}
</table>`;
}
function escapeHtml(str: string): string {
if (!str) return '';
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}