feat: vernostne zlavy - pri neparnom pocte je prvy produkt cez cely riadok
Parny pocet: vsetky produkty po 2 v riadku. Neparny: prvy produkt sam cez celu sirku (vacsi vizual), ostatne po 2. Export obrazkov kariet pouziva rovnaku sirku karty ako nahlad. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b13a447c64
commit
23f23c8903
3 files changed
+38
-15
No files matched your search
@@ -3,7 +3,7 @@
|
||||
// a jeho adresa sa zapamätá pri produkte (cardImage). Pri ďalšom exporte sa nezmenené karty nenahrávajú znova.
|
||||
import { toJpeg } from 'html-to-image';
|
||||
import { CardProduct, NewsletterCampaign } from '@/types/newsletter';
|
||||
import { cardContextForModule, cardVisualKey, cardVisualHeight } from './productCard';
|
||||
import { cardContextFor, cardVisualKey, cardVisualHeight } from './productCard';
|
||||
|
||||
async function uploadJpeg(dataUrl: string, fileName: string): Promise<string> {
|
||||
const blob = await (await fetch(dataUrl)).blob();
|
||||
@@ -26,8 +26,8 @@ export async function prepareCardImages(
|
||||
campaign.modules.forEach((m, mIdx) => {
|
||||
const isCards = m.type === 'products_grid' || (m.type === 'loyalty_club' && m.data?.enabled !== false);
|
||||
if (!isCards) return;
|
||||
const ctx = cardContextForModule(m, 'preview');
|
||||
(m.data.products || []).forEach((p: CardProduct, pIdx: number) => {
|
||||
const ctx = cardContextFor(m, pIdx, 'preview');
|
||||
const key = cardVisualKey(p, ctx);
|
||||
if (p.cardImage?.key !== key) {
|
||||
jobs.push({ mIdx, pIdx, key, bg: p.cardBgColor || ctx.cardBg, width: ctx.cardWidth });
|
||||
|
||||
@@ -2,7 +2,7 @@ import { NewsletterCampaign } from '@/types/newsletter';
|
||||
import { SUPABASE_ASSETS_BASE } from './defaultCampaign';
|
||||
import { pickAlt } from './altText';
|
||||
import { escapeHtml, escapeWithBreaks } from './html';
|
||||
import { RenderMode, cardContextForModule, renderProductGrid } from './productCard';
|
||||
import { RenderMode, renderProductGrid } from './productCard';
|
||||
|
||||
// E-mail na export (ZIP/Brevo): produktové karty použijú obrázky pripravené cez prepareCardImages()
|
||||
export function renderEmailHtml(campaign: NewsletterCampaign): string {
|
||||
@@ -392,7 +392,7 @@ function renderModule(m: any, campaign: NewsletterCampaign, mode: RenderMode = '
|
||||
<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);">${escapeWithBreaks(m.data.sectionTitle)}</h2>
|
||||
</td>
|
||||
</tr>` : ''}
|
||||
${renderProductGrid(m.data.products || [], cardContextForModule(m, mode))}
|
||||
${renderProductGrid(m, mode)}
|
||||
</table>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td height="12" style="font-size: 12px; line-height: 12px;"> </td></tr></table>`;
|
||||
|
||||
@@ -423,7 +423,7 @@ function renderModule(m: any, campaign: NewsletterCampaign, mode: RenderMode = '
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Produktové karty -->
|
||||
${renderProductGrid(m.data.products || [], cardContextForModule(m, mode))}
|
||||
${renderProductGrid(m, mode)}
|
||||
</table>
|
||||
<table role="presentation" cellspacing="0" cellpadding="0" border="0" width="100%"><tr><td height="25" style="font-size: 25px; line-height: 25px;"> </td></tr></table>`;
|
||||
|
||||
|
||||
+33
-10
@@ -372,23 +372,46 @@ export function renderProductCard(p: CardProduct, ctx: CardContext): string {
|
||||
</table>`;
|
||||
}
|
||||
|
||||
// Mriežka kariet (1 alebo 2 na riadok) s rovnakou výškou kariet v riadku
|
||||
export function renderProductGrid(products: CardProduct[], ctx: CardContext): string {
|
||||
const cols = ctx.cardWidth > 300 ? 1 : 2;
|
||||
const rows: CardProduct[][] = [];
|
||||
for (let i = 0; i < products.length; i += cols) rows.push(products.slice(i, i + cols));
|
||||
return rows
|
||||
// Kontext konkrétnej karty: vo vernostných zľavách pri nepárnom počte je prvý produkt cez celý riadok
|
||||
export function cardContextFor(m: NewsletterModule, index: number, mode: RenderMode): CardContext {
|
||||
const base = cardContextForModule(m, mode);
|
||||
const count = (m.data.products || []).length;
|
||||
if (base.variant === 'loyalty' && count % 2 === 1 && index === 0) return { ...base, cardWidth: 600 };
|
||||
return base;
|
||||
}
|
||||
|
||||
// Riadky mriežky (indexy produktov): 1 na riadok, alebo po 2;
|
||||
// vernostné zľavy s nepárnym počtom: prvý produkt sám cez celý riadok, ostatné po 2
|
||||
function gridRows(m: NewsletterModule, count: number, oneColumn: boolean): number[][] {
|
||||
const idx = Array.from({ length: count }, (_, i) => i);
|
||||
if (oneColumn) return idx.map((i) => [i]);
|
||||
const rows: number[][] = [];
|
||||
let start = 0;
|
||||
if (m.type === 'loyalty_club' && count % 2 === 1) {
|
||||
rows.push([0]);
|
||||
start = 1;
|
||||
}
|
||||
for (let i = start; i < count; i += 2) rows.push(idx.slice(i, i + 2));
|
||||
return rows;
|
||||
}
|
||||
|
||||
// Mriežka kariet s rovnakou výškou kariet v riadku
|
||||
export function renderProductGrid(m: NewsletterModule, mode: RenderMode): string {
|
||||
const products: CardProduct[] = m.data.products || [];
|
||||
const oneColumn = cardContextForModule(m, mode).cardWidth > 300;
|
||||
return gridRows(m, products.length, oneColumn)
|
||||
.map((row, rIdx) => {
|
||||
const spacer = rIdx > 0 ? '<tr><td height="12" style="font-size: 12px; line-height: 12px;"> </td></tr>' : '';
|
||||
const cells = row
|
||||
.map(
|
||||
(p, idx) => `
|
||||
<td width="${cols === 1 ? 100 : 50}%" valign="top" height="1" style="${cols === 1 ? '' : idx === 0 ? 'padding-right: 6px;' : 'padding-left: 6px;'} height: 100%;">
|
||||
${renderProductCard(p, ctx)}
|
||||
(i, idx) => `
|
||||
<td width="${row.length === 1 ? 100 : 50}%" valign="top" height="1" style="${row.length === 1 ? '' : idx === 0 ? 'padding-right: 6px;' : 'padding-left: 6px;'} height: 100%;">
|
||||
${renderProductCard(products[i], cardContextFor(m, i, mode))}
|
||||
</td>`
|
||||
)
|
||||
.join('');
|
||||
const filler = cols === 2 && row.length === 1 ? '<td width="50%" style="padding-left: 6px;"></td>' : '';
|
||||
// posledný riadok s 1 produktom v 2-stĺpcovej mriežke (bežné sekcie) – produkt ostane v ľavej polovici
|
||||
const filler = !oneColumn && row.length === 1 && cardContextFor(m, row[0], mode).cardWidth <= 300 ? '<td width="50%" style="padding-left: 6px;"></td>' : '';
|
||||
return `${spacer}
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
Reference in new issue
Block a user