feat: tri styly textu produktu podla vzoru coop_nws_009

- nazov 15px/700, gramaz 15px/400 v tom istom riadku, jednotkova cena 11px #555
  pod nazvom, volitelna poznamka 10px #888 (napr. Ilustracne foto)
- text zarovnany vlavo s odsadenim 14px; jednotkova cena sa presunula spod cenovky
- nove polia Gramaz / mnozstvo a Poznamka pri produkte aj vernostnom produkte

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:48:42 +02:00
1 parent ffba0e0842
commit 090fd8d1ce
3 files changed
+87 -16

No files matched your search

+64 -1
View File
@@ -805,6 +805,37 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
/>
</div>
<div className="grid grid-cols-2 gap-2">
<div>
<label className="text-[10px] text-gray-500 font-semibold block mb-0.5">Gramáž / množstvo</label>
<input
type="text"
value={p.quantity || ''}
onChange={(e) => {
const products = [...module.data.products];
products[pIdx].quantity = e.target.value;
updateData({ products });
}}
placeholder="napr. 35 g"
className="w-full text-xs border border-gray-300 rounded p-1.5 bg-white"
/>
</div>
<div>
<label className="text-[10px] text-gray-500 font-semibold block mb-0.5">Poznámka (voliteľné)</label>
<input
type="text"
value={p.note || ''}
onChange={(e) => {
const products = [...module.data.products];
products[pIdx].note = e.target.value;
updateData({ products });
}}
placeholder="napr. Ilustračné foto"
className="w-full text-xs border border-gray-300 rounded p-1.5 bg-white"
/>
</div>
</div>
<ImageUploadInput
label="Obrázok produktu"
value={p.imageUrl || ''}
@@ -892,7 +923,7 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
/>
</div>
<div>
<label className="text-[10px] text-gray-500">Merná cena</label>
<label className="text-[10px] text-gray-500">Jednotková cena</label>
<input
type="text"
value={p.unitPrice}
@@ -901,6 +932,7 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
products[pIdx].unitPrice = e.target.value;
updateData({ products });
}}
placeholder="jednotková cena 9,43 EUR/kg"
className="w-full text-xs border border-gray-300 rounded p-1 bg-white text-[10px]"
/>
</div>
@@ -1457,6 +1489,37 @@ export const ConfigPanel: React.FC<ConfigPanelProps> = ({
/>
</div>
<div className="grid grid-cols-2 gap-2">
<div>
<label className="text-[10px] text-gray-500 font-semibold block mb-0.5">Gramáž / množstvo</label>
<input
type="text"
value={p.quantity || ''}
onChange={(e) => {
const products = [...module.data.products];
products[pIdx].quantity = e.target.value;
updateData({ products });
}}
placeholder="napr. 35 g"
className="w-full text-xs border border-gray-300 rounded p-1.5 bg-white"
/>
</div>
<div>
<label className="text-[10px] text-gray-500 font-semibold block mb-0.5">Poznámka (voliteľné)</label>
<input
type="text"
value={p.note || ''}
onChange={(e) => {
const products = [...module.data.products];
products[pIdx].note = e.target.value;
updateData({ products });
}}
placeholder="napr. Ilustračné foto"
className="w-full text-xs border border-gray-300 rounded p-1.5 bg-white"
/>
</div>
</div>
<ImageUploadInput
label="Obrázok produktu"
value={p.imageUrl || ''}
+19 -13
View File
@@ -713,6 +713,17 @@ function renderStackedImageRow(p: ProductItem, imgHtml: string, cellStyle: strin
<!--<![endif]-->`;
}
// Popis produktu podľa vzoru coop_nws_009: 3 štýly textu (+ voliteľná poznámka)
// názov 15px/700 · gramáž 15px/400 (v tom istom riadku) · jednotková cena 11px #555 · poznámka 10px #888
const PRODUCT_TEXT_FONT = "font-family: 'Poppins', Helvetica, Arial, sans-serif;";
function renderProductTextBlock(p: { title?: string; quantity?: string; note?: string }, unitPrice?: string): string {
return `<h3 class="prod-name force-text-dark" style="margin: 0; ${PRODUCT_TEXT_FONT} font-size: 15px; line-height: 1.25; font-weight: 700; color: #1a1a1a;">${escapeHtml(p.title || '')}${p.quantity ? ` <span style="font-weight: 400;">${escapeHtml(p.quantity)}</span>` : ''}</h3>${
unitPrice ? `
<div class="prod-unit force-text-dark" style="margin-top: 3px; ${PRODUCT_TEXT_FONT} font-size: 11px; line-height: 1.3; color: #555555;">${escapeHtml(unitPrice)}</div>` : ''}${
p.note ? `
<div class="prod-unit force-text-dark" style="margin-top: 2px; ${PRODUCT_TEXT_FONT} font-size: 10px; line-height: 1.3; color: #888888;">${escapeHtml(p.note)}</div>` : ''}`;
}
// Štítky dostupnosti v predajniach (Potraviny / Supermarket / Tempo); prázdne, ak nie je zvolená žiadna
function renderStoresHtml(p: { stores?: Partial<ProductItem["stores"]> }): string {
const tag = (color: string, file: string, alt: string) =>
@@ -866,7 +877,6 @@ function renderExactProductCard(
</td>`;
const badgeCell = `<td width="42%" style="padding: 8px 8px 8px 4px; text-align: center;" valign="middle">
${renderBadgeContent(bWidth, bAlign, padTop, padBottom, marginStyle)}
${p.unitPrice ? `<div style="font-size: 8px; color: #666666; font-weight: 500; font-family: Arial, sans-serif; text-align: center; margin-top: 4px;">${escapeHtml(p.unitPrice)}</div>` : ''}
</td>`;
if (!hasImageAdjust(p)) {
return `<tr>${imgCell}${badgeCell}</tr>`;
@@ -890,8 +900,8 @@ function renderExactProductCard(
<!--<![endif]-->`;
})()}
<tr>
<td colspan="2" height="54" style="padding: 0 8px 10px 8px; text-align: center; height: 54px; min-height: 54px;" valign="top">
<h3 class="force-text-dark" style="margin: 0; font-size: 15px; color: #1a1a1a; font-weight: 600; line-height: 1.25;">${escapeHtml(p.title)}</h3>
<td colspan="2" class="prod-pad" height="54" style="padding: 2px 14px 12px 14px; text-align: left; height: 54px; min-height: 54px;" valign="top">
${renderProductTextBlock(p, p.unitPrice)}
</td>
</tr>
</table>`;
@@ -918,8 +928,8 @@ function renderExactProductCard(
pickAlt(p.imageAlt, p.title, sectionTitle, 'Produkt')
)}
<tr>
<td height="54" style="padding: 0 8px 10px 8px; text-align: center; height: 54px; min-height: 54px;" valign="top">
<h3 class="force-text-dark" style="margin: 0; font-size: 15px; color: #1a1a1a; font-weight: 600; line-height: 1.25;">${escapeHtml(p.title)}</h3>
<td class="prod-pad" height="54" style="padding: 2px 14px 12px 14px; text-align: left; height: 54px; min-height: 54px;" valign="top">
${renderProductTextBlock(p, p.unitPrice)}
</td>
</tr>
<tr height="1">
@@ -940,9 +950,7 @@ function renderExactProductCard(
</td>
</tr>
<tr height="1">
<td align="center" valign="bottom" style="padding: 2px 8px 12px 8px; text-align: center; height: 1px;">
${p.unitPrice ? `<div style="font-size: 9px; color: #666666; font-weight: 500; font-family: Arial, sans-serif; text-align: center;">${escapeHtml(p.unitPrice)}</div>` : '<div style="font-size: 9px; line-height: 9px; height: 9px;">&nbsp;</div>'}
</td>
<td style="padding: 0 0 12px 0; height: 1px; font-size: 0; line-height: 0;">&nbsp;</td>
</tr>
</table>`;
}
@@ -979,8 +987,8 @@ function renderExactLoyaltyCard(p: any, validity: string, storesOffset: StoresOf
</td>
</tr>`}
<tr>
<td style="padding: 0 8px 10px 8px; text-align: center; height: 100%; min-height: 44px;" valign="top">
<h3 class="force-text-dark" style="margin: 0; font-size: 15px; color: #1a1a1a; font-weight: 600; line-height: 1.25;">${escapeHtml(p.title)}</h3>
<td class="prod-pad" style="padding: 2px 14px 12px 14px; text-align: left; height: 100%; min-height: 44px;" valign="top">
${renderProductTextBlock(p, p.badgeCustomTemplate || p.customBadgePngUrl ? p.cardUnitPrice || p.unitPrice : '')}
</td>
</tr>
<tr height="1">
@@ -1082,9 +1090,7 @@ function renderExactLoyaltyCard(p: any, validity: string, storesOffset: StoresOf
</tr>
${p.badgeCustomTemplate || p.customBadgePngUrl ? `
<tr height="1">
<td align="center" valign="bottom" style="padding: 2px 8px 12px 8px; text-align: center; height: 1px;">
${p.cardUnitPrice || p.unitPrice ? `<div style="font-size: 9px; color: #666666; font-weight: 500; font-family: Arial, sans-serif; text-align: center;">${escapeHtml(p.cardUnitPrice || p.unitPrice)}</div>` : '<div style="font-size: 9px; line-height: 9px; height: 9px;">&nbsp;</div>'}
</td>
<td style="padding: 0 0 12px 0; height: 1px; font-size: 0; line-height: 0;">&nbsp;</td>
</tr>` : ''}
</table>`;
}
+4 -2
View File
@@ -19,11 +19,13 @@ export interface StoreTag {
export interface ProductItem {
id: string;
title: string;
title: string; // názov (tučne), napr. "Deli tyčinka 3 druhy"
quantity?: string; // gramáž / množstvo (normálne písmo za názvom), napr. "35 g"
note?: string; // poznámka pod jednotkovou cenou, napr. "Ilustračné foto"
imageUrl: string;
priceMain: string; // napr. "1" alebo "0"
priceSub: string; // napr. "99" alebo "89"
unitPrice: string; // napr. "1kg = 4,91 €"
unitPrice: string; // jednotková cena pod názvom, napr. "jednotková cena 9,43 EUR/kg"
discountText: string; // napr. "-23%" alebo "23%"
cardPriceMain?: string; // cena s kartou (vernostná), celé eurá
cardPriceSub?: string; // cena s kartou, centy