feat: posun stitkov predajni pri kazdom produkte zvlast

Posuvniky Vodorovne / Zvisle su v nastaveniach cenovky produktu
(aj vernostneho) namiesto celej sekcie.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub_HostacnyandClaude Opus 5.5 committed 2026-10-19 16:52:34 +02:00
1 parent a2d009bcb8
commit 139e3569d0
3 files changed
+42 -21

No files matched your search

+24 -16
View File
@@ -29,7 +29,7 @@ interface ConfigPanelProps {
availableBadges?: BadgeTemplate[]; availableBadges?: BadgeTemplate[];
} }
// Posun štítkov predajní voči hornej hrane cenovky (pre celú sekciu) // Posun štítkov predajní voči hornej hrane cenovky (pre jeden produkt)
const StoresOffsetControl: React.FC<{ const StoresOffsetControl: React.FC<{
x: number; x: number;
y: number; y: number;
@@ -67,7 +67,7 @@ const StoresOffsetControl: React.FC<{
</div> </div>
{row('Vodorovne', x, -80, 80, 'storesOffsetX', '+ doprava, − doľava')} {row('Vodorovne', x, -80, 80, 'storesOffsetX', '+ doprava, − doľava')}
{row('Zvisle', y, -40, 40, 'storesOffsetY', '+ vyššie nad cenovku, − nižšie cez horný okraj cenovky')} {row('Zvisle', y, -40, 40, 'storesOffsetY', '+ vyššie nad cenovku, − nižšie cez horný okraj cenovky')}
<p className="text-[10px] text-gray-500">Štítky sú pri hornej hrane cenovky a posúvajú sa spolu s ňou. Platí pre celú sekciu.</p> <p className="text-[10px] text-gray-500">Štítky sú pri hornej hrane cenovky a posúvajú sa spolu s ňou.</p>
</div> </div>
); );
}; };
@@ -613,13 +613,6 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
</label> </label>
</div> </div>
{/* Štítky predajní (Potraviny / Supermarket / Tempo) nad hornou hranou cenovky */}
<StoresOffsetControl
x={module.data.storesOffsetX || 0}
y={module.data.storesOffsetY ?? DEFAULT_STORES_OFFSET_Y}
onChange={(patch) => updateData(patch)}
/>
{/* Rozloženie produktov (Počet produktov na riadok) */} {/* Rozloženie produktov (Počet produktov na riadok) */}
<div> <div>
<label className="block text-xs font-semibold text-gray-700 mb-1"> <label className="block text-xs font-semibold text-gray-700 mb-1">
@@ -1093,6 +1086,17 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
}} }}
/> />
{/* Štítky predajní (Potraviny / Supermarket / Tempo) pri hornej hrane cenovky */}
<StoresOffsetControl
x={p.storesOffsetX ?? module.data.storesOffsetX ?? 0}
y={p.storesOffsetY ?? module.data.storesOffsetY ?? DEFAULT_STORES_OFFSET_Y}
onChange={(patch) => {
const products = [...module.data.products];
products[pIdx] = { ...products[pIdx], ...patch };
updateData({ products });
}}
/>
{/* Tlačidlá aplikácie cenovky */} {/* Tlačidlá aplikácie cenovky */}
{p.badgeCustomTemplate && ( {p.badgeCustomTemplate && (
<div className="space-y-1.5"> <div className="space-y-1.5">
@@ -1366,13 +1370,6 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
</label> </label>
</div> </div>
{/* Štítky predajní (Potraviny / Supermarket / Tempo) nad hornou hranou cenovky */}
<StoresOffsetControl
x={module.data.storesOffsetX || 0}
y={module.data.storesOffsetY ?? DEFAULT_STORES_OFFSET_Y}
onChange={(patch) => updateData(patch)}
/>
{/* Farba pozadia vernostných kariet */} {/* Farba pozadia vernostných kariet */}
<div className="bg-gray-50 p-3.5 rounded-xl border border-gray-200 space-y-2.5"> <div className="bg-gray-50 p-3.5 rounded-xl border border-gray-200 space-y-2.5">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@@ -1798,6 +1795,17 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
}} }}
/> />
{/* Štítky predajní (Potraviny / Supermarket / Tempo) pri hornej hrane cenovky */}
<StoresOffsetControl
x={p.storesOffsetX ?? module.data.storesOffsetX ?? 0}
y={p.storesOffsetY ?? module.data.storesOffsetY ?? DEFAULT_STORES_OFFSET_Y}
onChange={(patch) => {
const products = [...module.data.products];
products[pIdx] = { ...products[pIdx], ...patch };
updateData({ products });
}}
/>
{/* Tlačidlá aplikácie cenovky */} {/* Tlačidlá aplikácie cenovky */}
{p.badgeCustomTemplate && ( {p.badgeCustomTemplate && (
<div className="space-y-1.5"> <div className="space-y-1.5">
+13 -3
View File
@@ -792,12 +792,17 @@ function renderExactProductCard(
const effectiveCardBg = p.cardBgColor || cardBg || '#feeaea'; const effectiveCardBg = p.cardBgColor || cardBg || '#feeaea';
const storesHtml = renderStoresHtml(p); const storesHtml = renderStoresHtml(p);
// Posun štítkov je nastavenie produktu (staršie kampane mohli mať hodnotu na sekcii)
const productStoresOffset: StoresOffset = {
x: p.storesOffsetX ?? storesOffset.x,
y: p.storesOffsetY ?? storesOffset.y
};
const badgeLayout = p.badgeLayout || 'below'; const badgeLayout = p.badgeLayout || 'below';
// Cenovka so štítkami predajní nad hornou hranou (bez obalového <td>) // Cenovka so štítkami predajní nad hornou hranou (bez obalového <td>)
const renderBadgeContent = (bWidth: number, bAlign: string, padTop: number, padBottom: number, marginStyle: string) => const renderBadgeContent = (bWidth: number, bAlign: string, padTop: number, padBottom: number, marginStyle: string) =>
attachStoresToBadge(renderBadgeOnly(bWidth, bAlign, 0, padBottom, marginStyle), storesHtml, bWidth, bAlign, marginStyle, padTop, storesOffset); attachStoresToBadge(renderBadgeOnly(bWidth, bAlign, 0, padBottom, marginStyle), storesHtml, bWidth, bAlign, marginStyle, padTop, productStoresOffset);
// Pomocná funkcia na render samotnej cenovky (bez obalového <td>) // Pomocná funkcia na render samotnej cenovky (bez obalového <td>)
const renderBadgeOnly = (bWidth: number, bAlign: string, padTop: number, padBottom: number, marginStyle: string) => { const renderBadgeOnly = (bWidth: number, bAlign: string, padTop: number, padBottom: number, marginStyle: string) => {
@@ -958,6 +963,11 @@ function renderExactProductCard(
function renderExactLoyaltyCard(p: any, validity: string, storesOffset: StoresOffset = {}): string { function renderExactLoyaltyCard(p: any, validity: string, storesOffset: StoresOffset = {}): string {
const cardValidity = p.validityOverride || validity; const cardValidity = p.validityOverride || validity;
const storesHtml = renderStoresHtml(p); const storesHtml = renderStoresHtml(p);
// Posun štítkov je nastavenie produktu (staršie kampane mohli mať hodnotu na sekcii)
const productStoresOffset: StoresOffset = {
x: p.storesOffsetX ?? storesOffset.x,
y: p.storesOffsetY ?? storesOffset.y
};
const loyaltyBg = p.cardBgColor || '#e6e1d9'; const loyaltyBg = p.cardBgColor || '#e6e1d9';
return ` return `
@@ -1027,7 +1037,7 @@ function renderExactLoyaltyCard(p: any, validity: string, storesOffset: StoresOf
: renderCleanBadgeSvg(p.badgeCustomTemplate, loyaltyProductData, bWidth)} : renderCleanBadgeSvg(p.badgeCustomTemplate, loyaltyProductData, bWidth)}
</td> </td>
</tr> </tr>
</table>`, storesHtml, bWidth, bAlign, marginStyle, padTop, storesOffset); </table>`, storesHtml, bWidth, bAlign, marginStyle, padTop, productStoresOffset);
} }
// 2. Predvolený Dual Sticker Block (Bez karty / S kartou) 1:1 // 2. Predvolený Dual Sticker Block (Bez karty / S kartou) 1:1
@@ -1084,7 +1094,7 @@ function renderExactLoyaltyCard(p: any, validity: string, storesOffset: StoresOf
<div style="font-size: 9px; color: #e2001a; font-weight: 800; font-family: Arial, sans-serif;">${escapeHtml(p.cardUnitPrice)}</div> <div style="font-size: 9px; color: #e2001a; font-weight: 800; font-family: Arial, sans-serif;">${escapeHtml(p.cardUnitPrice)}</div>
</td> </td>
</tr> </tr>
</table>`, storesHtml, 140, 'center', 'margin: 0 auto;', 0, storesOffset); </table>`, storesHtml, 140, 'center', 'margin: 0 auto;', 0, productStoresOffset);
})()} })()}
</td> </td>
</tr> </tr>
+5 -2
View File
@@ -47,6 +47,8 @@ export interface ProductItem {
showSlovakFlag?: boolean; showSlovakFlag?: boolean;
customBadgePngUrl?: string; // Voliteľná statická PNG cenovka customBadgePngUrl?: string; // Voliteľná statická PNG cenovka
imageAlt?: string; // Ručný alt text fotky (inak názov produktu) imageAlt?: string; // Ručný alt text fotky (inak názov produktu)
storesOffsetX?: number; // posun štítkov predajní voči cenovke (+ doprava)
storesOffsetY?: number; // posun štítkov predajní voči hornej hrane cenovky (+ vyššie)
imageScale?: number; // Zoom fotky (1 = 100 %) imageScale?: number; // Zoom fotky (1 = 100 %)
imageOffsetX?: number; // Posun fotky vodorovne v px (+ doprava) imageOffsetX?: number; // Posun fotky vodorovne v px (+ doprava)
imageOffsetY?: number; // Posun fotky zvisle v px (+ nadol) imageOffsetY?: number; // Posun fotky zvisle v px (+ nadol)
@@ -114,8 +116,9 @@ export interface ProductsGridModuleData {
cardBorderColor?: string; cardBorderColor?: string;
columns?: 1 | 2; columns?: 1 | 2;
showCardValidity?: boolean; // platnosť na kartách produktov (predvolene skrytá) showCardValidity?: boolean; // platnosť na kartách produktov (predvolene skrytá)
storesOffsetX?: number; // posun štítkov predajní voči cenovke (+ doprava) // staršie kampane: posun štítkov na úrovni sekcie (dnes sa nastavuje pri produkte)
storesOffsetY?: number; // posun štítkov predajní voči hornej hrane cenovky (+ vyššie) storesOffsetX?: number;
storesOffsetY?: number;
} }
export interface LoyaltyClubModuleData { export interface LoyaltyClubModuleData {