From 3bd93fde207b59dd989a2c3ed95db552d77888aa Mon Sep 17 00:00:00 2001 From: Jandl-hostacny Date: Mon, 19 Oct 2026 10:30:32 +0200 Subject: [PATCH] feat: premenne {cena_s_kartou} a {zlava_s_kartou} v cenovkach (uloha 12) - bezne produkty maju volitelne polia Cena s kartou / Zlava s kartou, vernostne produkty pouzivaju svoje existujuce hodnoty - editor cenoviek: tlacidla na pridanie vrstiev a nahlad premennych - jedinecne ID novych vrstiev (rychle pridanie viacerych vrstiev) Co-Authored-By: Claude Opus 5.5 --- src/app/page.tsx | 3 + src/components/editor/BadgeEditorModal.tsx | 78 ++++++++++++++-------- src/components/editor/ConfigPanel.tsx | 46 +++++++++++++ src/lib/badgeUtils.ts | 46 +++++++++---- src/lib/emailRenderer.ts | 5 +- src/types/newsletter.ts | 3 + 6 files changed, 138 insertions(+), 43 deletions(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 4b99026..a13d593 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -835,6 +835,9 @@ export default function Home() { currentPriceMain={targetProduct?.priceMain || targetProduct?.cardPriceMain || '1'} currentPriceSub={targetProduct?.priceSub || targetProduct?.cardPriceSub || '99'} currentUnitPrice={targetProduct?.unitPrice || targetProduct?.cardUnitPrice || '1kg = 4,91 €'} + currentCardPriceMain={targetProduct?.cardPriceMain || undefined} + currentCardPriceSub={targetProduct?.cardPriceSub || undefined} + currentCardDiscount={targetProduct?.cardDiscount || undefined} /> {/* Modál Moje newslettre (Ukladanie, duplikácia, vytváranie a prepínanie) */} diff --git a/src/components/editor/BadgeEditorModal.tsx b/src/components/editor/BadgeEditorModal.tsx index 2f244ba..ba83896 100644 --- a/src/components/editor/BadgeEditorModal.tsx +++ b/src/components/editor/BadgeEditorModal.tsx @@ -3,7 +3,7 @@ import React, { useState, useRef, useEffect } from 'react'; import { BadgeTemplate, BadgeLayer } from '@/types/newsletter'; import { supabase } from '@/lib/supabase'; -import { DEFAULT_BADGE_TEMPLATES, renderCleanBadgeSvg, svgToCleanPng } from '@/lib/badgeUtils'; +import { DEFAULT_BADGE_TEMPLATES, renderCleanBadgeSvg, svgToCleanPng, BADGE_PRICE_TOKEN_RE, badgePriceParts, replaceBadgeTextVars } from '@/lib/badgeUtils'; import { Tag, Plus, Check, Trash2, X, Upload, Eye, Move, AlignLeft, AlignCenter, AlignRight, ZoomIn, ZoomOut, Copy } from 'lucide-react'; // Riadok tabuľky badges v Supabase pre danú šablónu @@ -34,6 +34,9 @@ interface BadgeEditorModalProps { currentPriceMain?: string; currentPriceSub?: string; currentUnitPrice?: string; + currentCardPriceMain?: string; + currentCardPriceSub?: string; + currentCardDiscount?: string; } export const BadgeEditorModal: React.FC = ({ @@ -43,7 +46,10 @@ export const BadgeEditorModal: React.FC = ({ currentDiscount = '25%', currentPriceMain = '1', currentPriceSub = '99', - currentUnitPrice = '1kg = 4,91 €' + currentUnitPrice = '1kg = 4,91 €', + currentCardPriceMain = '1', + currentCardPriceSub = '49', + currentCardDiscount = '30%' }) => { const POPULAR_FONTS = [ { name: 'Poppins', source: 'google' as const }, @@ -391,7 +397,7 @@ export const BadgeEditorModal: React.FC = ({ const addTextLayer = (presetText: string = 'Text') => { if (!activeBadge) return; const newL: BadgeLayer = { - id: `l-${Date.now()}`, + id: `l-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`, type: 'text', text: presetText, x: 70, @@ -538,6 +544,17 @@ export const BadgeEditorModal: React.FC = ({ }; // Vykreslenie vrstvy v React náhľade s centami ako horný index (sup) a BEZ znaku € + // Ceny produktu, pre ktorý sa cenovka upravuje (náhľad premenných) + const previewProduct = { + priceMain: currentPriceMain, + priceSub: currentPriceSub, + discountText: currentDiscount, + unitPrice: currentUnitPrice, + cardPriceMain: currentCardPriceMain, + cardPriceSub: currentCardPriceSub, + cardDiscount: currentCardDiscount + }; + const renderLayerVisualContent = (layer: BadgeLayer) => { const raw = layer.text || ''; const baseFontSize = layer.fontSize || 16; @@ -545,12 +562,14 @@ export const BadgeEditorModal: React.FC = ({ const subY = layer.indexOffsetY !== undefined ? `${layer.indexOffsetY}px` : '-10px'; const subX = layer.indexOffsetX !== undefined ? `${layer.indexOffsetX}px` : '2px'; - if (raw.includes('{cena}')) { - const parts = raw.split('{cena}'); + const priceMatch = raw.match(BADGE_PRICE_TOKEN_RE); + if (priceMatch) { + const [tokMain, tokSub] = badgePriceParts(priceMatch[1], previewProduct); + const idx = priceMatch.index || 0; return ( - {parts[0]} - {currentPriceMain} + {replaceBadgeTextVars(raw.slice(0, idx), previewProduct)} + {tokMain} = ({ lineHeight: 1 }} > - {currentPriceSub} + {tokSub} - {parts[1]} + {replaceBadgeTextVars(raw.slice(idx + priceMatch[0].length), previewProduct)} ); } @@ -588,13 +607,7 @@ export const BadgeEditorModal: React.FC = ({ ); } - const replaced = raw - .replace(/{cela_suma}/g, currentPriceMain) - .replace(/{zlava}/g, currentDiscount.replace('-', '')) - .replace(/{jednotkova_cena}/g, currentUnitPrice) - .replace(/{mena}/g, '€'); - - return replaced; + return replaceBadgeTextVars(raw, previewProduct); }; // Aplikovanie cenovky na produkt: čisté vektorové SVG a čistý PNG (BEZ akéhokoľvek screenshotu nástrojov/rámčekov) @@ -604,12 +617,7 @@ export const BadgeEditorModal: React.FC = ({ try { const cleanSvg = renderCleanBadgeSvg( activeBadge, - { - priceMain: currentPriceMain, - priceSub: currentPriceSub, - discountText: currentDiscount, - unitPrice: currentUnitPrice - }, + previewProduct, activeBadge.width || 110 ); @@ -709,12 +717,7 @@ export const BadgeEditorModal: React.FC = ({ dangerouslySetInnerHTML={{ __html: renderCleanBadgeSvg( b, - { - priceMain: currentPriceMain, - priceSub: currentPriceSub, - discountText: currentDiscount, - unitPrice: currentUnitPrice - }, + previewProduct, 90 ) }} @@ -876,6 +879,13 @@ export const BadgeEditorModal: React.FC = ({ {'{zlava}'} → {currentDiscount.replace('-', '')} + + {'{cena_s_kartou}'} → {currentCardPriceMain} + {currentCardPriceSub} + + + {'{zlava_s_kartou}'} → {currentCardDiscount.replace('-', '')} + {'{mena}'} → € @@ -1032,6 +1042,18 @@ export const BadgeEditorModal: React.FC = ({ > + Zľava % + +