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>
|
</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 */}
|
{/* 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">
|
<div className="bg-gray-50 p-2.5 rounded-xl border border-gray-200 space-y-2">
|
||||||
{/* Šírka */}
|
{/* Šírka */}
|
||||||
@@ -1343,6 +1380,45 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
|
|||||||
</div>
|
</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 */}
|
{/* Rýchle ovládanie veľkosti (Width), zarovnania a paddingu pre vernostnú cenovku */}
|
||||||
{p.badgeCustomTemplate && (
|
{p.badgeCustomTemplate && (
|
||||||
<div className="bg-gray-50 p-2.5 rounded-xl border border-gray-200 space-y-2">
|
<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>` : ''}
|
${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>`;
|
</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 `
|
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%;">
|
<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">
|
<tr height="1">
|
||||||
@@ -666,68 +757,13 @@ function renderExactProductCard(
|
|||||||
const bAlign = p.badgeAlign || (p.badgeCustomTemplate ? (p.badgeCustomTemplate.align || 'center') : 'center');
|
const bAlign = p.badgeAlign || (p.badgeCustomTemplate ? (p.badgeCustomTemplate.align || 'center') : 'center');
|
||||||
const padTop = p.badgePaddingTop ?? (p.badgeCustomTemplate?.paddingTop ?? 0);
|
const padTop = p.badgePaddingTop ?? (p.badgeCustomTemplate?.paddingTop ?? 0);
|
||||||
const padBottom = p.badgePaddingBottom ?? (p.badgeCustomTemplate?.paddingBottom ?? 0);
|
const padBottom = p.badgePaddingBottom ?? (p.badgeCustomTemplate?.paddingBottom ?? 0);
|
||||||
|
|
||||||
let marginStyle = 'margin: 0 auto;';
|
let marginStyle = 'margin: 0 auto;';
|
||||||
if (bAlign === 'left') {
|
if (bAlign === 'left') {
|
||||||
marginStyle = 'margin: 0 auto 0 0; margin-right: auto; margin-left: 0;';
|
marginStyle = 'margin: 0 auto 0 0; margin-right: auto; margin-left: 0;';
|
||||||
} else if (bAlign === 'right') {
|
} else if (bAlign === 'right') {
|
||||||
marginStyle = 'margin: 0 0 0 auto; margin-left: auto; margin-right: 0;';
|
marginStyle = 'margin: 0 0 0 auto; margin-left: auto; margin-right: 0;';
|
||||||
}
|
}
|
||||||
|
return renderBadgeContent(bWidth, bAlign, padTop, padBottom, marginStyle);
|
||||||
// 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>`;
|
|
||||||
})()}
|
})()}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export interface ProductItem {
|
|||||||
cardBgColor?: string; // HEX napr. "#feeaea" (fantasticky), "#fef0c7" (domace), "#dcb085" (grill)
|
cardBgColor?: string; // HEX napr. "#feeaea" (fantasticky), "#fef0c7" (domace), "#dcb085" (grill)
|
||||||
showSlovakFlag?: boolean;
|
showSlovakFlag?: boolean;
|
||||||
customBadgePngUrl?: string; // Voliteľná statická PNG cenovka
|
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 {
|
export interface AnnouncementModuleData {
|
||||||
|
|||||||
Reference in new issue
Block a user