feat: poradie fotiek v karte ako zoznam vrstiev (hore = vpredu)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub_HostacnyandClaude Opus 5.5 committed 2026-10-21 13:24:15 +02:00
1 parent 533a856303
commit 3ece5abf6a
3 files changed
+85 -20

No files matched your search

+59 -15
View File
@@ -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 }) => (
</button>
);
// Ď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 (
<div className="rounded-lg border border-gray-200 bg-white p-2 space-y-1">
<span className="text-[10px] font-bold text-gray-600">Poradie fotiek (hore = vpredu)</span>
{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 (
<div key={id} className={`flex items-center gap-2 rounded-md px-1.5 py-1 ${isMain ? 'bg-red-50' : 'bg-gray-50'}`}>
<span className="w-7 h-7 rounded bg-white border border-gray-200 overflow-hidden flex items-center justify-center shrink-0">
{url && (
// eslint-disable-next-line @next/next/no-img-element
<img src={url} alt="" className="w-full h-full object-contain" />
)}
</span>
<span className="flex-1 text-[11px] font-semibold text-gray-800">{isMain ? 'Hlavná fotka' : `Fotka ${extraIdx + 2}`}</span>
<button type="button" disabled={k === 0} onClick={() => move(id, 1)} className="p-0.5 text-gray-500 hover:text-gray-900 disabled:opacity-25" title="Dopredu">
<ChevronUp className="w-3.5 h-3.5" />
</button>
<button type="button" disabled={k === front.length - 1} onClick={() => move(id, -1)} className="p-0.5 text-gray-500 hover:text-gray-900 disabled:opacity-25" title="Dozadu">
<ChevronDown className="w-3.5 h-3.5" />
</button>
</div>
);
})}
</div>
);
};
// Ď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<{
<Slider label="Vodorovne" value={photo.x} display={`${photo.x} %`} min={0} max={100} step={1} onChange={(v) => onChange({ x: v })} />
<Slider label="Zvisle" value={photo.y} display={`${photo.y} %`} min={0} max={100} step={1} onChange={(v) => onChange({ y: v })} />
<Slider label="Veľkosť" value={photo.scale} display={`${Math.round(photo.scale * 100)} %`} min={0.2} max={2} step={0.05} onChange={(v) => onChange({ scale: v })} />
<label className="flex items-center gap-1.5 text-[10px] text-gray-600 cursor-pointer">
<input type="checkbox" checked={!!photo.behind} onChange={(e) => onChange({ behind: e.target.checked })} className="w-3 h-3 accent-red-600" />
Za hlavnou fotkou
</label>
</div>
);
@@ -355,23 +393,29 @@ export const ProductEditor: React.FC<ProductEditorProps> = ({
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)
})
}
/>
))}
<button
type="button"
onClick={() =>
onClick={() => {
const id = `ph-${Date.now()}`;
onChange({
extraPhotos: [
...(p.extraPhotos || []),
{ id: `ph-${Date.now()}`, url: '', x: 62, y: 52, scale: 0.75, behind: false }
]
})
}
extraPhotos: [...(p.extraPhotos || []), { id, url: '', x: 62, y: 52, scale: 0.75 }],
photoOrder: [...photoStack(p), id] // nová fotka ide dopredu
});
}}
className="w-full text-[10px] font-bold text-gray-600 border border-dashed border-gray-300 hover:border-gray-400 hover:bg-white rounded-lg py-1.5"
>
+ Pridať ďalšiu fotku (bundle)
</button>
{(p.extraPhotos || []).length > 0 && <PhotoOrderList product={p} onChange={(photoOrder) => onChange({ photoOrder })} />}
</Block>
{/* CENOVKA */}