diff --git a/src/components/editor/ProductEditor.tsx b/src/components/editor/ProductEditor.tsx
index 6abc51c..c8432bf 100644
--- a/src/components/editor/ProductEditor.tsx
+++ b/src/components/editor/ProductEditor.tsx
@@ -4,10 +4,10 @@
// texty, fotka (zoom/posun), cenovka (šablóna, veľkosť, ceny), dostupnosť a ďalšie možnosti.
import React, { useEffect, useRef } from 'react';
import { BadgeTemplate, CardProduct, ProductLabel, ProductPhoto } from '@/types/newsletter';
-import { ChevronDown, Trash2, Pencil, Globe, Layers } from 'lucide-react';
+import { ChevronDown, ChevronUp, Trash2, Pencil, Globe, Layers } from 'lucide-react';
import { ImageUploadInput } from './ImageUploadInput';
import { pickAlt } from '@/lib/altText';
-import { DEFAULT_STORES_OFFSET_X, DEFAULT_STORES_OFFSET_Y, DEFAULT_STORES_SIZE, badgeWidthFor, productCardAlt } from '@/lib/productCard';
+import { DEFAULT_STORES_OFFSET_X, DEFAULT_STORES_OFFSET_Y, DEFAULT_STORES_SIZE, MAIN_PHOTO, badgeWidthFor, photoStack, productCardAlt } from '@/lib/productCard';
export const PRODUCT_BG_PRESETS = [
{ label: 'Červená', sub: 'Fantasticky', color: '#feeaea', border: '#f5d4d4' },
@@ -105,7 +105,49 @@ const ResetLink: React.FC<{ onClick: () => void }> = ({ onClick }) => (
);
-// Ďalšia fotka v karte (bundle): obrázok, poloha, veľkosť, pred/za hlavnou fotkou
+// Poradie fotiek ako vrstvy: navrchu zoznamu je fotka najviac vpredu
+const PhotoOrderList: React.FC<{ product: CardProduct; onChange: (order: string[]) => void }> = ({ product, onChange }) => {
+ const stack = photoStack(product); // odzadu dopredu
+ const extras = product.extraPhotos || [];
+ const move = (id: string, dir: 1 | -1) => {
+ const i = stack.indexOf(id);
+ const j = i + dir;
+ if (j < 0 || j >= stack.length) return;
+ const next = [...stack];
+ [next[i], next[j]] = [next[j], next[i]];
+ onChange(next);
+ };
+ const front = [...stack].reverse();
+ return (
+
+
Poradie fotiek (hore = vpredu)
+ {front.map((id, k) => {
+ const isMain = id === MAIN_PHOTO;
+ const extraIdx = extras.findIndex((f) => f.id === id);
+ const url = isMain ? product.imageUrl : extras[extraIdx]?.url;
+ return (
+
+
+ {url && (
+ // eslint-disable-next-line @next/next/no-img-element
+
+ )}
+
+
{isMain ? 'Hlavná fotka' : `Fotka ${extraIdx + 2}`}
+
+
+
+ );
+ })}
+
+ );
+};
+
+// Ďalšia fotka v karte (bundle): obrázok, poloha, veľkosť
const ExtraPhotoEditor: React.FC<{
index: number;
photo: ProductPhoto;
@@ -123,10 +165,6 @@ const ExtraPhotoEditor: React.FC<{
onChange({ x: v })} />
onChange({ y: v })} />
onChange({ scale: v })} />
-
);
@@ -355,23 +393,29 @@ export const ProductEditor: React.FC = ({
index={i}
photo={f}
onChange={(patch) => onChange({ extraPhotos: (p.extraPhotos || []).map((x, j) => (j === i ? { ...x, ...patch } : x)) })}
- onRemove={() => onChange({ extraPhotos: (p.extraPhotos || []).filter((_, j) => j !== i) })}
+ onRemove={() =>
+ onChange({
+ extraPhotos: (p.extraPhotos || []).filter((_, j) => j !== i),
+ photoOrder: photoStack(p).filter((id) => id !== f.id)
+ })
+ }
/>
))}
+
+ {(p.extraPhotos || []).length > 0 && onChange({ photoOrder })} />}
{/* CENOVKA */}
diff --git a/src/lib/productCard.ts b/src/lib/productCard.ts
index b6923c3..00044e8 100644
--- a/src/lib/productCard.ts
+++ b/src/lib/productCard.ts
@@ -208,7 +208,7 @@ export function cardVisualKey(p: CardProduct, ctx: CardContext): string {
p.cardPriceMain, p.cardPriceSub, p.cardDiscount, p.cardUnitPrice,
p.noCardPriceMain, p.noCardPriceSub, p.noCardDiscount, p.noCardUnitPrice,
p.stores, p.storesOffsetX ?? DEFAULT_STORES_OFFSET_X, p.storesOffsetY ?? DEFAULT_STORES_OFFSET_Y, p.storesSize ?? DEFAULT_STORES_SIZE,
- p.labels || [], p.extraPhotos || []
+ p.labels || [], p.extraPhotos || [], photoStack(p)
];
const str = JSON.stringify(relevant);
let h1 = 5381;
@@ -221,6 +221,20 @@ export function cardVisualKey(p: CardProduct, ctx: CardContext): string {
return `cv-${(h1 >>> 0).toString(36)}${(h2 >>> 0).toString(36)}`;
}
+// Poradie fotiek v karte odzadu dopredu ('main' = hlavná fotka). Uložené poradie sa doplní o nové fotky
+// (dopredu) a očistí o zmazané; bez uloženého poradia platí staršie nastavenie „za hlavnou fotkou“.
+export const MAIN_PHOTO = 'main';
+export function photoStack(p: Pick): string[] {
+ const extras = p.extraPhotos || [];
+ const ids = new Set([MAIN_PHOTO, ...extras.map((f) => f.id)]);
+ if (p.photoOrder?.length) {
+ const order = p.photoOrder.filter((id) => ids.has(id));
+ for (const id of ids) if (!order.includes(id)) order.push(id);
+ return order;
+ }
+ return [...extras.filter((f) => f.behind).map((f) => f.id), MAIN_PHOTO, ...extras.filter((f) => !f.behind).map((f) => f.id)];
+}
+
// Ďalšia fotka v karte (bundle): poloha v % vizuálu, veľkosť voči hlavnej fotke
function renderExtraPhoto(f: ProductPhoto, mode: RenderMode): string {
if (!f.url) return '';
@@ -290,9 +304,15 @@ function renderVisualPreview(p: CardProduct, ctx: CardContext, key: string): str
const insetRight = bgBox ? Math.round(bgBox.right * bw) : 0;
return `
- ${(p.extraPhotos || []).filter((f) => f.behind).map((f) => renderExtraPhoto(f, ctx.mode)).join('')}
- ${p.imageUrl ? `
)})
` : ''}
- ${(p.extraPhotos || []).filter((f) => !f.behind).map((f) => renderExtraPhoto(f, ctx.mode)).join('')}
+ ${photoStack(p)
+ .map((id) => {
+ if (id === MAIN_PHOTO) {
+ return p.imageUrl ? `
)})
` : '';
+ }
+ const f = (p.extraPhotos || []).find((x) => x.id === id);
+ return f ? renderExtraPhoto(f, ctx.mode) : '';
+ })
+ .join('')}
${(p.labels || []).map(renderLabel).join('')}
${stores ? `
${stores}
` : ''}
diff --git a/src/types/newsletter.ts b/src/types/newsletter.ts
index 6450206..7e63122 100644
--- a/src/types/newsletter.ts
+++ b/src/types/newsletter.ts
@@ -52,6 +52,7 @@ export interface ProductItem {
storesSize?: number; // výška jedného štítka predajne v px (nezávislá od veľkosti cenovky)
labels?: ProductLabel[]; // vlastné štítky na fotke (napr. „PODIEL MÄSA 92 %“)
extraPhotos?: ProductPhoto[]; // ďalšie fotky v karte (bundle) popri hlavnej fotke
+ photoOrder?: string[]; // poradie fotiek odzadu dopredu: id ďalších fotiek a 'main' (hlavná fotka)
imageScale?: number; // Zoom fotky (1 = 100 %)
imageOffsetX?: number; // Posun fotky vodorovne v px (+ doprava)
imageOffsetY?: number; // Posun fotky zvisle v px (+ nadol)
@@ -76,7 +77,7 @@ export interface ProductPhoto {
x: number; // stred fotky, % šírky vizuálu (0–100)
y: number; // stred fotky, % výšky vizuálu (0–100)
scale: number; // 1 = rovnaká veľkosť ako hlavná fotka bez zoomu
- behind?: boolean; // za hlavnou fotkou (inak pred ňou)
+ behind?: boolean; // (staršie) za hlavnou fotkou – nahradené poradím photoOrder
}
// Vlastný štítok na fotke produktu