feat: moznost pozicovania cenovky vedla produktu (uloha 8)
This commit is contained in:
1 parent
a82c2d5af8
commit
0aa664b08b
3 files changed
+169
-56
No files matched your search
@@ -757,6 +757,43 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Poloha cenovky: pod / vedľa */}
|
||||
<div>
|
||||
<span className="text-[10px] text-gray-600 font-semibold block mb-1">Poloha cenovky</span>
|
||||
<div className="grid grid-cols-2 gap-1 bg-gray-200 p-0.5 rounded-lg text-[10px] font-bold text-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const products = [...module.data.products];
|
||||
products[pIdx].badgeLayout = 'below';
|
||||
updateData({ products });
|
||||
}}
|
||||
className={`py-1.5 rounded transition-all flex items-center justify-center gap-1 ${
|
||||
(p.badgeLayout || 'below') === 'below'
|
||||
? 'bg-white text-gray-900 shadow-xs'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<span>↓ Pod obrázkom</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const products = [...module.data.products];
|
||||
products[pIdx].badgeLayout = 'side';
|
||||
updateData({ products });
|
||||
}}
|
||||
className={`py-1.5 rounded transition-all flex items-center justify-center gap-1 ${
|
||||
p.badgeLayout === 'side'
|
||||
? 'bg-white text-gray-900 shadow-xs'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<span>→ Vedľa obrázka</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Rýchle ovládanie veľkosti (Width), zarovnania a paddingu */}
|
||||
<div className="bg-gray-50 p-2.5 rounded-xl border border-gray-200 space-y-2">
|
||||
{/* Šírka */}
|
||||
@@ -1343,6 +1380,45 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Poloha cenovky: pod / vedľa */}
|
||||
{p.badgeCustomTemplate && (
|
||||
<div>
|
||||
<span className="text-[10px] text-gray-600 font-semibold block mb-1">Poloha cenovky</span>
|
||||
<div className="grid grid-cols-2 gap-1 bg-gray-200 p-0.5 rounded-lg text-[10px] font-bold text-center">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const products = [...module.data.products];
|
||||
products[pIdx].badgeLayout = 'below';
|
||||
updateData({ products });
|
||||
}}
|
||||
className={`py-1.5 rounded transition-all flex items-center justify-center gap-1 ${
|
||||
(p.badgeLayout || 'below') === 'below'
|
||||
? 'bg-white text-gray-900 shadow-xs'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<span>↓ Pod obrázkom</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const products = [...module.data.products];
|
||||
products[pIdx].badgeLayout = 'side';
|
||||
updateData({ products });
|
||||
}}
|
||||
className={`py-1.5 rounded transition-all flex items-center justify-center gap-1 ${
|
||||
p.badgeLayout === 'side'
|
||||
? 'bg-white text-gray-900 shadow-xs'
|
||||
: 'text-gray-600 hover:text-gray-900'
|
||||
}`}
|
||||
>
|
||||
<span>→ Vedľa obrázka</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Rýchle ovládanie veľkosti (Width), zarovnania a paddingu pre vernostnú cenovku */}
|
||||
{p.badgeCustomTemplate && (
|
||||
<div className="bg-gray-50 p-2.5 rounded-xl border border-gray-200 space-y-2">
|
||||
|
||||
+92
-56
@@ -637,6 +637,97 @@ function renderExactProductCard(
|
||||
${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) => {
|
||||
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>`;
|
||||
}
|
||||
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(p.title || 'Cenovka')}" style="display: block; width: 100%; max-width: ${bWidth}px; margin: 0 auto;">
|
||||
</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(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">
|
||||
@@ -666,68 +757,13 @@ function renderExactProductCard(
|
||||
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;';
|
||||
}
|
||||
|
||||
// 1. Ak produkt má vlastnú dynamickú šablónu cenovky (čistý SVG vektor bez editovacích prvkov a so živou cenou)
|
||||
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>`;
|
||||
}
|
||||
|
||||
// 2. Ak je nahraná statická PNG cenovka
|
||||
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(p.title || 'Cenovka')}" style="display: block; width: 100%; max-width: ${bWidth}px; margin: 0 auto;">
|
||||
</td>
|
||||
</tr>
|
||||
</table>`;
|
||||
}
|
||||
|
||||
// 3. Predvolená COOP cenovka s plnou podporou šírky, zarovnania a odsadenia
|
||||
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>`;
|
||||
return renderBadgeContent(bWidth, bAlign, padTop, padBottom, marginStyle);
|
||||
})()}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -40,6 +40,7 @@ export interface ProductItem {
|
||||
cardBgColor?: string; // HEX napr. "#feeaea" (fantasticky), "#fef0c7" (domace), "#dcb085" (grill)
|
||||
showSlovakFlag?: boolean;
|
||||
customBadgePngUrl?: string; // Voliteľná statická PNG cenovka
|
||||
badgeLayout?: 'below' | 'side'; // Poloha cenovky: 'below' = pod obrázkom (default), 'side' = vedľa obrázka
|
||||
}
|
||||
|
||||
export interface AnnouncementModuleData {
|
||||
|
||||
Reference in new issue
Block a user