diff --git a/.gitignore b/.gitignore index e0917c2..2de64cb 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ zadanie/ valut/ html-sablona/ .agent/ + +.vercel +.env* diff --git a/src/app/page.tsx b/src/app/page.tsx index b6151bf..b2585c8 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,7 +1,7 @@ 'use client'; -import React, { useState, useEffect, useRef } from 'react'; -import { NewsletterCampaign, NewsletterModule, ProductItem, BadgeTemplate } from '@/types/newsletter'; +import React, { useState, useEffect } from 'react'; +import { NewsletterCampaign, NewsletterModule, ProductItem } from '@/types/newsletter'; import { DEFAULT_CAMPAIGN, SUPABASE_ASSETS_BASE } from '@/lib/defaultCampaign'; import { renderEmailHtml } from '@/lib/emailRenderer'; import { supabase } from '@/lib/supabase'; @@ -16,7 +16,6 @@ import { ChevronUp, ChevronDown, Copy, Trash2 } from 'lucide-react'; export default function Home() { const [campaign, setCampaign] = useState(DEFAULT_CAMPAIGN); const [selectedModuleId, setSelectedModuleId] = useState('mod-products-fantasticky'); - const [previewMode, setPreviewMode] = useState<'desktop' | 'mobile'>('desktop'); const [isSaving, setIsSaving] = useState(false); const [isExportingBrevo, setIsExportingBrevo] = useState(false); const [brevoUrl, setBrevoUrl] = useState(null); @@ -24,9 +23,8 @@ export default function Home() { // Modály const [isImportOpen, setIsImportOpen] = useState(false); const [isBadgeModalOpen, setIsBadgeModalOpen] = useState(false); - const [activeProductIdForBadge, setActiveProductIdForBadge] = useState(null); - // Načítanie existujúceho draftu z databázy alebo inicializácia + // Načítanie existujúceho draftu z databázy useEffect(() => { async function loadInitial() { try { @@ -38,16 +36,19 @@ export default function Home() { if (data && data.length > 0) { const dbItem = data[0]; - setCampaign({ - id: dbItem.id, - title: dbItem.title, - preheader: dbItem.data.preheader || DEFAULT_CAMPAIGN.preheader, - status: dbItem.status, - brevoCampaignId: dbItem.brevo_campaign_id, - modules: dbItem.data.modules || DEFAULT_CAMPAIGN.modules - }); - if (dbItem.brevo_campaign_id) { - setBrevoUrl(`https://app.brevo.com/camp/detail/email/${dbItem.brevo_campaign_id}`); + // Ak má databáza rovnakú štruktúru, načítame ju, inak necháme DEFAULT_CAMPAIGN 1:1 + if (dbItem.data && dbItem.data.modules && dbItem.data.modules.length >= 6) { + setCampaign({ + id: dbItem.id, + title: dbItem.title, + preheader: dbItem.data.preheader || DEFAULT_CAMPAIGN.preheader, + status: dbItem.status, + brevoCampaignId: dbItem.brevo_campaign_id, + modules: dbItem.data.modules + }); + if (dbItem.brevo_campaign_id) { + setBrevoUrl(`https://app.brevo.com/camp/detail/email/${dbItem.brevo_campaign_id}`); + } } } } catch (err) { @@ -63,8 +64,7 @@ export default function Home() { setIsSaving(true); try { if (!campaign.id) { - // Vytvorenie nového záznamu - const { data, error } = await supabase + const { data } = await supabase .from('templates') .insert([ { @@ -82,7 +82,6 @@ export default function Home() { setCampaign((prev) => ({ ...prev, id: data[0].id })); } } else { - // Update existujúceho await supabase .from('templates') .update({ @@ -105,10 +104,8 @@ export default function Home() { return () => clearTimeout(timer); }, [campaign.title, campaign.preheader, campaign.modules]); - // Vybraný modul const selectedModule = campaign.modules.find((m) => m.id === selectedModuleId) || null; - // Aktualizácia modulu const handleUpdateModule = (updated: NewsletterModule) => { setCampaign((prev) => ({ ...prev, @@ -116,7 +113,6 @@ export default function Home() { })); }; - // Posun modulu hore/dole const moveModule = (index: number, direction: 'up' | 'down') => { const newModules = [...campaign.modules]; const targetIndex = direction === 'up' ? index - 1 : index + 1; @@ -129,7 +125,6 @@ export default function Home() { setCampaign((prev) => ({ ...prev, modules: newModules })); }; - // Duplikácia modulu const duplicateModule = (index: number) => { const original = campaign.modules[index]; const clone: NewsletterModule = { @@ -144,7 +139,6 @@ export default function Home() { setSelectedModuleId(clone.id); }; - // Zmazanie modulu const deleteModule = (index: number) => { if (campaign.modules.length <= 1) return; const newModules = campaign.modules.filter((_, i) => i !== index); @@ -152,7 +146,6 @@ export default function Home() { setSelectedModuleId(newModules[0]?.id || null); }; - // Pridanie nového modulu const handleAddModule = (type: any) => { let newMod: NewsletterModule; if (type === 'banner') { @@ -162,12 +155,12 @@ export default function Home() { title: 'Nový Banner', data: { imageUrl: `${SUPABASE_ASSETS_BASE}aktualne_letaky.jpg`, - linkUrl: 'https://www.coop.sk', + linkUrl: '#', altText: 'Aktuálne letáky', title: 'Prelistujte si naše najnovšie letáky online', description: 'Všetky akcie a zľavy na jednom mieste priamo vo vašom mobile.', buttonText: 'Pozrieť letáky', - buttonUrl: 'https://www.coop.sk', + buttonUrl: '#', buttonBgColor: '#e2001a' } }; @@ -179,6 +172,12 @@ export default function Home() { data: { sectionTitle: 'AKČNÁ PONUKA', globalValidity: '2. 5. – 15. 5. 2026', + cardBgColor: '#feeaea', + cardBorderColor: '#f5d4d4', + badgeHeaderBg: '#fefefe', + badgeHeaderColor: '#000000', + badgePriceBg: '#e2001a', + badgePriceColor: '#fefefe', products: [ { id: `p-${Date.now()}-1`, @@ -204,7 +203,6 @@ export default function Home() { setSelectedModuleId(newMod.id); }; - // Hromadný import produktov do aktuálne vybraného modulu const handleImportProducts = (imported: ProductItem[]) => { if (!selectedModuleId) return; const target = campaign.modules.find((m) => m.id === selectedModuleId); @@ -223,11 +221,8 @@ export default function Home() { handleUpdateModule(updated); }; - // Export do ZIP (JSZip) const handleExportZip = async () => { const rawHtml = renderEmailHtml(campaign); - - // Inlinovanie CSS pred zabalením let finalHtml = rawHtml; try { const res = await fetch('/api/inline-css', { @@ -238,29 +233,19 @@ export default function Home() { const data = await res.json(); if (data.html) finalHtml = data.html; } catch (e) { - console.warn('Inliner fallback to raw html:', e); + console.warn('Inliner fallback:', e); } const zip = new JSZip(); zip.file('index.html', finalHtml); - - // Pridáme README návod - zip.file( - 'README.txt', - 'COOP Jednota Newsletter Export\n\nSúbor index.html obsahuje finálny skompletizovaný a inlinovaný HTML kód pripravený na odoslanie cez ľubovoľný e-mailový systém (Brevo, Mailchimp, Outlook).' - ); - const blob = await zip.generateAsync({ type: 'blob' }); saveAs(blob, `coop-newsletter-${new Date().toISOString().split('T')[0]}.zip`); }; - // Export do Brevo (API v3) const handleExportBrevo = async () => { setIsExportingBrevo(true); try { const rawHtml = renderEmailHtml(campaign); - - // Inlinovanie let finalHtml = rawHtml; try { const res = await fetch('/api/inline-css', { @@ -286,7 +271,6 @@ export default function Home() { if (resData.success && resData.campaignId) { setBrevoUrl(resData.brevoUrl); - // Uloženie stavu do Supabase if (campaign.id) { await supabase .from('templates') @@ -307,13 +291,14 @@ export default function Home() { } }; + // Kompilácia celého HTML pre responzívny náhľad v reálnom čase + const fullHtml = renderEmailHtml(campaign); + return ( -
+
{/* 1. Horná lišta */} {}} onExportZip={handleExportZip} @@ -323,17 +308,13 @@ export default function Home() { onAddModule={handleAddModule} /> - {/* 2. Hlavný pracovný priestor (2 zóny: Stredné plátno a Pravý panel) */} + {/* 2. Hlavný pracovný priestor */}
- {/* Stredné plátno - Živý WYSIWYG náhľad */} -
-
- {/* Zoznam modulov na plátne */} -
+ {/* Stredné plátno - Živý responzívny e-mail v iframe pre 100% izoláciu CSS a médií */} +
+
+ {/* Zoznam modulov pre priamu editáciu */} +
{campaign.modules.map((mod, idx) => { const isSelected = mod.id === selectedModuleId; @@ -341,25 +322,28 @@ export default function Home() {
setSelectedModuleId(mod.id)} - className={`relative group cursor-pointer transition-all duration-200 mb-3 rounded-xl border-2 ${ + className={`relative group cursor-pointer transition-all duration-200 rounded-2xl border-2 ${ isSelected ? 'border-red-500 ring-4 ring-red-500/20' - : 'border-transparent hover:border-slate-600' + : 'border-transparent hover:border-slate-700/60' }`} > {/* Lišta s nástrojmi pre označený modul */} {isSelected && ( -
+
+ + {mod.title} +
)} - {/* Vykreslenie modulu cez iframe alebo dangerouslySetInnerHTML */} + {/* Vykreslenie modulu 1:1 */}
{/* Pravý konfiguračný panel */} -
); diff --git a/src/components/editor/TopBar.tsx b/src/components/editor/TopBar.tsx index aeca4a3..d142a50 100644 --- a/src/components/editor/TopBar.tsx +++ b/src/components/editor/TopBar.tsx @@ -5,9 +5,6 @@ import { Download, Send, ExternalLink, - Smartphone, - Monitor, - Save, CheckCircle2, Plus } from 'lucide-react'; @@ -15,8 +12,6 @@ import { NewsletterCampaign } from '@/types/newsletter'; interface TopBarProps { campaign: NewsletterCampaign; - previewMode: 'desktop' | 'mobile'; - setPreviewMode: (m: 'desktop' | 'mobile') => void; isSaving: boolean; onSave: () => void; onExportZip: () => void; @@ -28,8 +23,6 @@ interface TopBarProps { export const TopBar: React.FC = ({ campaign, - previewMode, - setPreviewMode, isSaving, onSave, onExportZip, @@ -40,23 +33,23 @@ export const TopBar: React.FC = ({ }) => { return (
- {/* Ľavá časť: Logo a názov */} + {/* Ľavá časť: Logo a stav */}
-
-
- CJ +
+
+ COOP

- {campaign.title || 'COOP Newsletter'} + {campaign.title || 'COOP Jednota Newsletter'}

-
- +
+ {isSaving ? ( - Ukladám do Supabase... + Ukladám do Supabase... ) : ( - - Uložené v Supabase + + Uložené v databáze (Auto-save) )} @@ -64,54 +57,30 @@ export const TopBar: React.FC = ({
- {/* Pridať modul dropdown */} -
+ {/* Pridanie sekcie */} +
- {/* Stredná časť: Prepínač Desktop / Mobil */} -
- - -
- {/* Pravá časť: Export do ZIP a Brevo */} -
+
{brevoCampaignUrl ? ( @@ -119,17 +88,17 @@ export const TopBar: React.FC = ({ href={brevoCampaignUrl} target="_blank" rel="noreferrer" - className="flex items-center gap-1.5 text-xs font-bold text-emerald-700 bg-emerald-50 border border-emerald-300 hover:bg-emerald-100 px-3.5 py-2 rounded-xl transition-all" + className="flex items-center gap-1.5 text-xs font-bold text-emerald-700 bg-emerald-50 border border-emerald-300 hover:bg-emerald-100 px-4 py-2 rounded-xl transition-all shadow-xs" > - Otvoriť v Brevo + Otvoriť v Brevo ) : ( )} diff --git a/src/lib/defaultCampaign.ts b/src/lib/defaultCampaign.ts index 3e101c4..fa79ba7 100644 --- a/src/lib/defaultCampaign.ts +++ b/src/lib/defaultCampaign.ts @@ -1,4 +1,4 @@ -import { NewsletterCampaign } from '@/types/newsletter'; +import { NewsletterCampaign, ProductItem } from '@/types/newsletter'; export const SUPABASE_ASSETS_BASE = 'https://dqexxfspkylqttrtxsww.supabase.co/storage/v1/object/public/newsletter-assets/'; @@ -6,13 +6,14 @@ export const SUPABASE_ASSETS_BASE = export const DEFAULT_CAMPAIGN: NewsletterCampaign = { id: '', title: 'COOP Jednota Newsletter', - preheader: 'Vyhrajte 3 autá KIA Sportage. Mliečne novinky, fantasticky nízke ceny, grilovanie aj vernostné zľavy s COOP Klubom.', + preheader: + 'Vyhrajte 3 autá KIA Sportage. Mliečne novinky, fantasticky nízke ceny, grilovanie aj vernostné zľavy s COOP Klubom.', status: 'draft', modules: [ { id: 'mod-top-announcement', type: 'announcement', - title: 'Top oznamovacia lišta', + title: '0. Top oznamovacia lišta', data: { tipBadge: 'TIP', text: 'Vyhrajte auto s COOP Jednotou', @@ -24,7 +25,7 @@ export const DEFAULT_CAMPAIGN: NewsletterCampaign = { { id: 'mod-header', type: 'header', - title: 'Hlavička s logom', + title: '1. Hlavička s logom a menu', data: { logoUrl: `${SUPABASE_ASSETS_BASE}logo_coop.png`, logoLink: 'https://www.coop.sk', @@ -37,40 +38,47 @@ export const DEFAULT_CAMPAIGN: NewsletterCampaign = { { id: 'mod-banner-1', type: 'banner', - title: 'Hlavný Banner - Súťaž KIA', + title: '2. Kampaň 1: Súťaž KIA', data: { imageUrl: `${SUPABASE_ASSETS_BASE}nws_podklady-velka_noc.jpg`, - linkUrl: 'https://www.coop.sk', + linkUrl: '#', altText: 'Súťaž o autá', title: 'Hrajte o 3 autá a ďalšie skvelé ceny!', - description: 'Nakúpte aspoň za 25 eur a hrajte o 3 autá KIA Sportage a tisíce okamžitých výhier.', + description: + 'Nakúpte aspoň za 25 eur a hrajte o 3 autá KIA Sportage a tisíce okamžitých výhier.', buttonText: 'Chcem vyhrať', - buttonUrl: 'https://www.coop.sk', + buttonUrl: '#', buttonBgColor: '#e2001a' } }, { id: 'mod-banner-2', type: 'banner', - title: 'Banner - Mliečne výrobky', + title: '3. Kampaň 2: Mliečne výrobky', data: { imageUrl: `${SUPABASE_ASSETS_BASE}nws_podklady-nove_obaly.jpg`, - linkUrl: 'https://www.coop.sk', - altText: 'Mliečne výrobky', + linkUrl: '#', + altText: 'Mliečne výrobky v novom balení', title: 'Tradične kvalitné mliečne výrobky v novom balení', description: '', buttonText: 'Nechajte sa ohúriť', - buttonUrl: 'https://www.coop.sk', + buttonUrl: '#', buttonBgColor: '#003399' } }, { id: 'mod-products-fantasticky', type: 'products_grid', - title: 'Fantasticky nízke ceny', + title: '4. Fantasticky nízke ceny (Červená)', data: { sectionTitle: 'Fantasticky nízke ceny!', globalValidity: '2. 5. – 15. 5. 2026', + cardBgColor: '#feeaea', + cardBorderColor: '#f5d4d4', + badgeHeaderBg: '#fefefe', + badgeHeaderColor: '#000000', + badgePriceBg: '#e2001a', + badgePriceColor: '#fefefe', products: [ { id: 'p1', @@ -104,10 +112,16 @@ export const DEFAULT_CAMPAIGN: NewsletterCampaign = { { id: 'mod-products-domace', type: 'products_grid', - title: 'Domáce a vždy čerstvé', + title: '5. Domáce a vždy čerstvé (Oranžová)', data: { sectionTitle: 'DOMÁCE A VŽDY ČERSTVÉ', globalValidity: '2. 5. – 15. 5. 2026', + cardBgColor: '#fef0c7', + cardBorderColor: '#e6d49a', + badgeHeaderBg: '#fefefe', + badgeHeaderColor: '#000000', + badgePriceBg: '#f9890e', + badgePriceColor: '#fefefe', products: [ { id: 'p3', @@ -119,42 +133,184 @@ export const DEFAULT_CAMPAIGN: NewsletterCampaign = { discountText: '34%', badgeText: 'zľava', cardBgColor: '#fef0c7', - showSlovakFlag: true, + showSlovakFlag: false, stores: { potraviny: false, supermarket: true, tempo: true } }, { id: 'p4', - title: 'Cibuľa žltá neskorá praná 1 kg', + title: 'Vajcia z podstielkového chovu M 15 ks 1 bal.', imageUrl: `${SUPABASE_ASSETS_BASE}produkt_4.png`, - priceMain: '0', - priceSub: '79', - unitPrice: '1kg = 0,79 €', - discountText: '27%', + priceMain: '3', + priceSub: '99', + unitPrice: '1ks = 0,27 €', + discountText: '20%', badgeText: 'zľava', cardBgColor: '#fef0c7', - showSlovakFlag: true, + showSlovakFlag: false, stores: { potraviny: false, supermarket: true, tempo: true } } ] } }, + { + id: 'mod-products-grill', + type: 'products_grid', + title: '6. Grilujeme chutne a výhodne (Zelená/Žltá)', + data: { + sectionTitle: 'GRILUJEME CHUTNE A VÝHODNE', + globalValidity: '2. 5. – 15. 5. 2026', + cardBgColor: '#dcb085', + cardBorderColor: '#b8895c', + badgeHeaderBg: '#ffde00', + badgeHeaderColor: '#e2001a', + badgeHeaderCustomText: 'Skvelá cena', + badgePriceBg: '#005239', + badgePriceColor: '#fefefe', + products: [ + { + id: 'p5', + title: 'Kuracie krídla v BBQ 1 kg', + imageUrl: `${SUPABASE_ASSETS_BASE}produkt_5.png`, + priceMain: '3', + priceSub: '99', + unitPrice: '1kg = 3,99 €', + discountText: '', + badgeText: 'Skvelá cena', + cardBgColor: '#dcb085', + showSlovakFlag: false, + stores: { potraviny: false, supermarket: false, tempo: true } + }, + { + id: 'p6', + title: 'Hellmann\'s omáčka 2 druhy 250 g – 255 g', + imageUrl: `${SUPABASE_ASSETS_BASE}produkt_6.png`, + priceMain: '1', + priceSub: '99', + unitPrice: '1kg = 7,96 €', + discountText: '38%', + badgeText: 'zľava', + cardBgColor: '#dcb085', + showSlovakFlag: false, + stores: { potraviny: false, supermarket: false, tempo: true } + } + ] + } + }, + { + id: 'mod-loyalty-club', + type: 'loyalty_club', + title: '7. Vernostné zľavy (COOP Klub)', + data: { + sectionTitle: 'Vernostné zľavy', + headerImageUrl: `${SUPABASE_ASSETS_BASE}vernostna_karta-hlavicka_transparent.png`, + headerLink: '#', + globalValidity: '2. 5. – 15. 5. 2026', + cardBgColor: '#e6e1d9', + cardBorderColor: '#cfc8bb', + products: [ + { + id: 'lp1', + title: 'Jojo želé 3 druhy 80 g', + imageUrl: `${SUPABASE_ASSETS_BASE}vernostna_karta-produkt_1.png`, + // Bez karty + noCardDiscount: '21%', + noCardPriceMain: '0', + noCardPriceSub: '79', + noCardUnitPrice: '1kg = 9,88 €', + // S kartou + cardDiscount: '38%', + cardPriceMain: '0', + cardPriceSub: '62', + cardUnitPrice: '1kg = 7,75 €', + stores: { potraviny: true, supermarket: true, tempo: true }, + showSlovakFlag: true + }, + { + id: 'lp2', + title: 'Margot tyčinka 3 druhy 40 g', + imageUrl: `${SUPABASE_ASSETS_BASE}vernostna_karta-produkt_2.png`, + // Bez karty + noCardDiscount: '23%', + noCardPriceMain: '0', + noCardPriceSub: '45', + noCardUnitPrice: '1kg = 11,25 €', + // S kartou + cardDiscount: '40%', + cardPriceMain: '0', + cardPriceSub: '35', + cardUnitPrice: '1kg = 8,75 €', + stores: { potraviny: true, supermarket: true, tempo: true }, + showSlovakFlag: true + } + ] + } + }, + { + id: 'mod-flyers-row', + type: 'articles_row', + title: '8. Aktuálne a Rozšírené letáky', + data: { + item1: { + title: 'Aktuálne letáky', + imageUrl: `${SUPABASE_ASSETS_BASE}aktualne_letaky.jpg`, + linkUrl: '#', + btnText: 'Zobraziť', + btnBgColor: '#e2001a', + bgColor: '#29446f', + textColor: '#fefefe' + }, + item2: { + title: 'Rozšírený leták', + imageUrl: `${SUPABASE_ASSETS_BASE}online_letak.jpg`, + linkUrl: '#', + btnText: 'Prelistovať', + btnBgColor: '#29446f', + bgColor: '#f9ba3c', + textColor: '#29446f' + } + } + }, + { + id: 'mod-recipes-row', + type: 'articles_row', + title: '9. Články a Recepty', + data: { + item1: { + title: 'Veľkonočný
baranček', + imageUrl: `${SUPABASE_ASSETS_BASE}clanok_barancek.png`, + badgeImgUrl: `${SUPABASE_ASSETS_BASE}clanky-netradicne_recepty.png`, + linkUrl: '#', + bgColor: '#f68e1e', + textColor: '#fefefe' + }, + item2: { + title: 'Vajíčko
magický amulet', + imageUrl: `${SUPABASE_ASSETS_BASE}clanok_vajicka.png`, + badgeImgUrl: `${SUPABASE_ASSETS_BASE}clanky-casopis_jednota.png`, + linkUrl: '#', + bgColor: '#5c85a8', + textColor: '#fefefe' + } + } + }, { id: 'mod-footer', type: 'footer', - title: 'Pätička', + title: '10. Pätička so sieťami, projektmi a aplikáciami', data: { - disclaimer: 'Tento newsletter má len informatívny charakter. Ceny a ponuka tovaru sa môžu na jednotlivých predajniach líšiť. Ponuka platí do vypredania zásob.', - unsubscribeText: 'Ak si už neželáte dostávať náš newsletter, môžete sa odhlásiť tu.', - unsubscribeUrl: '{{ unsubscribe }}', + appStoreUrl: 'https://apps.apple.com/sk/app/coop-jednota/id1044610196', + googlePlayUrl: 'https://play.google.com/store/apps/details?id=sk.coop.jednota.klub', + foundationUrl: 'https://www.nadaciacoop.sk', + recipesUrl: 'https://recepty.coop.sk', socials: { - facebook: 'https://www.facebook.com/coopjednotaslovensko', - instagram: 'https://www.instagram.com/coopjednotaslovensko', - tiktok: 'https://www.tiktok.com/@coopjednota', - youtube: 'https://www.youtube.com/c/COOPJednotaSlovensko' + facebook: 'https://www.facebook.com/coopjednota', + instagram: 'https://www.instagram.com/coopjednota/', + youtube: 'https://www.youtube.com/user/COOPJednotaSR', + tiktok: 'https://www.tiktok.com/@coopjednota' }, - appStoreUrl: 'https://apps.apple.com/sk/app/coop-jednota/id1527814838', - googlePlayUrl: 'https://play.google.com/store/apps/details?id=sk.coop.app', - foundationUrl: 'https://www.nadaciacoop.sk' + disclaimer: + 'Tento e-mail ste dostali, pretože ste členom COOP Jednota Klubu.
© 2026 COOP Jednota Slovensko, spotrebné družstvo.', + unsubscribeUrl: '*|UNSUB|*' } } ] diff --git a/src/lib/emailRenderer.ts b/src/lib/emailRenderer.ts index a594ffc..3a34980 100644 --- a/src/lib/emailRenderer.ts +++ b/src/lib/emailRenderer.ts @@ -8,15 +8,24 @@ export function renderEmailHtml(campaign: NewsletterCampaign): string { - + + ${escapeHtml(campaign.title)} + + + + - - + + +
${escapeHtml(campaign.preheader || '')}
+
+ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ ͏ ‌ +
+
@@ -56,7 +186,9 @@ export function renderEmailHtml(campaign: NewsletterCampaign): string { - +
@@ -69,165 +201,363 @@ function renderModule(m: any, campaign: NewsletterCampaign): string { switch (m.type) { case 'announcement': return ` - + - - - + + +
- - - - -
-
- ${escapeHtml(m.data.tipBadge || 'TIP')} -
-
- ${escapeHtml(m.data.text || '')} -
- ${m.data.linkText ? ` - ` : ''} -
-
+ + + + + +
`; case 'header': - const navLinksHtml = (m.data.navLinks || []) - .map((link: any) => { - if (link.variant === 'filled') { - return `${escapeHtml(link.label)}`; - } - return `${escapeHtml(link.label)}`; - }) - .join('\n'); + const navLinks = (m.data.navLinks || []).map((link: any) => { + if (link.variant === 'filled') { + return `${escapeHtml(link.label)}`; + } + return `${escapeHtml(link.label)}`; + }).join('\n'); return ` - - - - - -
- - - - - -
- - COOP Jednota - - - ${navLinksHtml} -
-
`; + + + + + + +
 
`; case 'banner': return ` - - - - - - ${m.data.title || m.data.description || m.data.buttonText ? ` - - - ` : ''} -
- - ${escapeHtml(m.data.altText || '')} - -
- ${m.data.title ? `

${escapeHtml(m.data.title)}

` : ''} - ${m.data.description ? `

${escapeHtml(m.data.description)}

` : ''} - ${m.data.buttonText ? ` - - - - -
- ${escapeHtml(m.data.buttonText)} -
` : ''} -
`; + + + + + + ${m.data.title || m.data.description || m.data.buttonText ? ` + + + ` : ''} + +
 
`; 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 rows: ProductItem[][] = []; for (let i = 0; i < products.length; i += 2) { rows.push(products.slice(i, i + 2)); } - const rowsHtml = rows - .map( - (row) => ` + const rowsHtml = rows.map((row) => ` - - ${row - .map( - (p, idx) => ` - ` - ) - .join('\n')} - ${row.length === 1 ? '' : ''} - -
- ${renderProductCard(p, globalValidity)} -
` - ) - .join('\n'); + + ${row.map((p, idx) => ` + + ${renderExactProductCard(p, globalValidity, cardBg, cardBorder, badgeHdrBg, badgeHdrColor, badgePrcBg, badgePrcColor, badgeCustomTxt)} + `).join('\n')} + ${row.length === 1 ? '' : ''} + + + `).join('\n'); return ` - - - ${m.data.sectionTitle ? ` - - - ` : ''} - - - + +
-

${escapeHtml(m.data.sectionTitle)}

-
- ${rowsHtml} -
+ ${m.data.sectionTitle ? ` + + + ` : ''} + + +
+

${escapeHtml(m.data.sectionTitle)}

+
+ ${rowsHtml} +
`; + case 'loyalty_club': + const lProducts = m.data.products || []; + return ` + + + + + + + + + + + + + +
+

${escapeHtml(m.data.sectionTitle || 'Vernostné zľavy')}

+
+ + + + + +
+ + + ${lProducts.map((p: any, idx: number) => ` + `).join('\n')} + +
+ ${renderExactLoyaltyCard(p, m.data.globalValidity || '2. 5. – 15. 5. 2026')} +
+
+
 
`; + + case 'articles_row': + const it1 = m.data.item1; + const it2 = m.data.item2; + + // Ak ide o letáky + if (it1?.btnText) { + return ` + + + + + + + + +
+ + + + + + + +
+ + ${escapeHtml(it1.title)} + +
+

${escapeHtml(it1.title)}

+ + + + + +
+
+ + + + + + + +
+ + ${escapeHtml(it2.title)} + +
+

${escapeHtml(it2.title)}

+ + + + + +
+
+
 
`; + } + + // Inak články / recepty + return ` + + + + + + + + +
+ + + + + + + +
+ +
+ + + + +
+ Recepty +
+
+ +
+

${it1.title}

+
+
+ + + + + + + +
+ +
+ + + + +
+ Časopis Jednota +
+
+ +
+

${it2.title}

+
+
+
 
`; + case 'footer': return ` - - - - - + +
- - - - -

- ${escapeHtml(m.data.disclaimer || '')} -

- - -

- ${escapeHtml(m.data.unsubscribeText || 'Odhlásiť sa')} -

-
+ + + `; default: @@ -235,71 +565,182 @@ function renderModule(m: any, campaign: NewsletterCampaign): string { } } -function renderProductCard(p: ProductItem, globalValidity: string): string { +function renderExactProductCard( + p: ProductItem, + globalValidity: string, + cardBg: string, + cardBorder: string, + badgeHdrBg: string, + badgeHdrColor: string, + badgePrcBg: string, + badgePrcColor: string, + badgeCustomTxt: string | null +): string { const validity = p.validityOverride || globalValidity; - const bgColor = p.cardBgColor || '#feeaea'; const storesHtml = ` - ${p.stores?.potraviny ? `` : ''} - ${p.stores?.supermarket ? `` : ''} - ${p.stores?.tempo ? `` : ''} + ${p.stores?.potraviny ? `` : ''} + ${p.stores?.supermarket ? `` : ''} + ${p.stores?.tempo ? `` : ''}
P
S
T
P
S
T
`; return ` - - - - - - - - - - - -
- - - - - -
${escapeHtml(validity)}${storesHtml}
-
- ${escapeHtml(p.title)} -
-

${escapeHtml(p.title)}

-
- - - - - + + + + + + + + + + + +
- - ${p.discountText ? ` - - - ` : ''} - - - +
- ${escapeHtml(p.badgeText || 'zľava')} - ${escapeHtml(p.discountText)} -
- ${escapeHtml(p.priceMain)}${escapeHtml(p.priceSub)} -
+ + - - ${p.showSlovakFlag ? ` - - - ` : ''} - - ${p.unitPrice ? `
${escapeHtml(p.unitPrice)}
` : ''} -
+ ${escapeHtml(p.title)} +
+

${escapeHtml(p.title)}

+
+ + + + + + ${p.showSlovakFlag ? ` + + + ` : ''} +
+ + ${badgeCustomTxt ? ` + + + ` : (p.discountText ? ` + + + ` : '')} + + + +
+ ${escapeHtml(badgeCustomTxt)} +
+ ${escapeHtml(p.badgeText || 'zľava')} + ${escapeHtml(p.discountText)} +
+ ${escapeHtml(p.priceMain)}${escapeHtml(p.priceSub)} +
+
+ SK +
+ ${p.unitPrice ? `
${escapeHtml(p.unitPrice)}
` : ''} +
`; +} + +function renderExactLoyaltyCard(p: any, validity: string): string { + const storesHtml = ` + + ${p.stores?.potraviny ? `` : ''} + ${p.stores?.supermarket ? `` : ''} + ${p.stores?.tempo ? `` : ''} +
P
S
T
`; + + return ` + + + + + + + + + + + + + `; }