// Príprava obrázkov produktových kariet pre export (ZIP / Brevo): // vizuál karty z náhľadu v editore (fotka + cenovka + štítky) sa uloží ako JPG, nahrá do úložiska // 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'; async function uploadJpeg(dataUrl: string, fileName: string): Promise { const blob = await (await fetch(dataUrl)).blob(); const formData = new FormData(); formData.append('file', new File([blob], fileName, { type: 'image/jpeg' })); const res = await fetch('/api/upload', { method: 'POST', body: formData }); const data = await res.json(); if (!data.success || !data.url) { throw new Error(data.error || 'Nahratie obrázka karty zlyhalo'); } return data.url; } export async function prepareCardImages( campaign: NewsletterCampaign, onProgress?: (done: number, total: number) => void ): Promise<{ campaign: NewsletterCampaign; failed: number }> { // Karty, ktorých obrázok chýba alebo je zastaraný const jobs: Array<{ mIdx: number; pIdx: number; key: string; bg: string; width: number }> = []; 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 key = cardVisualKey(p, ctx); if (p.cardImage?.key !== key) { jobs.push({ mIdx, pIdx, key, bg: p.cardBgColor || ctx.cardBg, width: ctx.cardWidth }); } }); }); const modules = campaign.modules.map((m) => ({ ...m, data: { ...m.data, products: m.data.products ? [...m.data.products] : m.data.products } })); let failed = 0; for (let i = 0; i < jobs.length; i++) { const job = jobs[i]; onProgress?.(i, jobs.length); const node = document.querySelector(`[data-card-visual="${job.key}"]`); if (!node) { failed++; continue; } try { // Presná veľkosť karty v e-maile (náhľad môže byť v užšom okne) const height = cardVisualHeight(job.width); const dataUrl = await toJpeg(node, { pixelRatio: 2, quality: 0.9, backgroundColor: job.bg, width: job.width, height, style: { width: `${job.width}px`, height: `${height}px` }, // obrázky idú cez /api/img?url=… – bez parametrov by si html-to-image pomýlil rôzne obrázky includeQueryParams: true }); const product = modules[job.mIdx].data.products[job.pIdx]; const url = await uploadJpeg(dataUrl, `karta-${product.id}.jpg`); modules[job.mIdx].data.products[job.pIdx] = { ...product, cardImage: { key: job.key, url } }; } catch (err) { console.warn('Obrázok karty sa nepodarilo pripraviť:', err); failed++; } } onProgress?.(jobs.length, jobs.length); return { campaign: { ...campaign, modules }, failed }; }