feat: produktove karty v style letaku a zjednoduseny panel produktu

Karta podla sablony coop_nws_009: fotka cez celu kartu, cenovka cez fotku
vpravo dole, stitky predajni nad cenovkou, pod tym texty.
- v editore sa vizual sklada zivo (HTML/CSS); pri exporte (ZIP/Brevo) sa ulozi
  ako JPG a do e-mailu ide ako jeden obrazok - funguje rovnako vo vsetkych
  klientoch; nezmenene karty sa znova nenahravaju
- /api/img: proxy obrazkov pre nahlad (inak by sa vizual nedal ulozit)
- novy ProductEditor pre bezne aj vernostne produkty: Texty, Fotka (zoom/posun),
  Cenovka (sablona, velkost, ceny), Dostupnost, Dalsie moznosti; zbalene po produktoch
- alt texty kariet s cenou a zlavou, mobilne styly textu, ochrana farieb
  v tmavom rezime (zelena karta, Apple Mail)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub_HostacnyandClaude Opus 5.5 committed 2026-10-19 17:06:48 +02:00
1 parent 139e3569d0
commit 25771afc79
9 files changed
+1009 -1756

No files matched your search

+15 -2
View File
@@ -13,6 +13,7 @@ import { ProductImportModal } from '@/components/editor/ProductImportModal';
import { BadgeEditorModal } from '@/components/editor/BadgeEditorModal';
import { SavedCampaignsModal } from '@/components/editor/SavedCampaignsModal';
import JSZip from 'jszip';
import { prepareCardImages } from '@/lib/cardImages';
import { saveAs } from 'file-saver';
import { ChevronUp, ChevronDown, Copy, Trash2, Anchor, Lock } from 'lucide-react';
@@ -432,8 +433,19 @@ export default function Home() {
}
};
// Pred exportom sa vizuály produktových kariet uložia ako obrázky (nezmenené sa nenahrávajú znova)
const prepareForExport = async (): Promise<NewsletterCampaign> => {
const { campaign: prepared, failed } = await prepareCardImages(campaign);
setCampaign((prev) => ({ ...prev, modules: prepared.modules }));
if (failed > 0) {
alert(`${failed} kariet produktov sa nepodarilo uložiť ako obrázok – v e-maile budú v jednoduchšej podobe.`);
}
return prepared;
};
const handleExportZip = async () => {
const rawHtml = renderEmailHtml(campaign);
const prepared = await prepareForExport();
const rawHtml = renderEmailHtml(prepared);
let finalHtml = rawHtml;
try {
const res = await fetch('/api/inline-css', {
@@ -456,7 +468,8 @@ export default function Home() {
const handleExportBrevo = async (updateExisting = false) => {
setIsExportingBrevo(true);
try {
const rawHtml = renderEmailHtml(campaign);
const prepared = await prepareForExport();
const rawHtml = renderEmailHtml(prepared);
let finalHtml = rawHtml;
try {
const res = await fetch('/api/inline-css', {