feat: duplikovanie sablon cenoviek v editore (uloha 11)

Ikona Duplikovat pri sablone vytvori kopiu "(kopia)", hned ju ulozi
a otvori na upravu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub_HostacnyandClaude Opus 5.5 committed 2026-10-19 10:23:33 +02:00
1 parent 21320e792b
commit 23ac4dc830
1 file changed
+59 -13
+59 -13
View File
@@ -4,7 +4,22 @@ 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 { Tag, Plus, Check, Trash2, X, Upload, Eye, Move, AlignLeft, AlignCenter, AlignRight, ZoomIn, ZoomOut } from 'lucide-react';
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
function toBadgeDbPayload(badge: BadgeTemplate) {
return {
name: badge.name,
svg_raw: badge.svgRaw,
layers_config: {
layers: badge.layersConfig,
width: badge.width || 110,
align: badge.align || 'center',
paddingTop: badge.paddingTop ?? 4,
paddingBottom: badge.paddingBottom ?? 4
}
};
}
// Priblíženie plátna v editore: rozsah posuvníka a kroky tlačidiel +/−
const CANVAS_ZOOM_MIN = 0.5;
@@ -401,6 +416,35 @@ export const BadgeEditorModal: React.FC<BadgeEditorModalProps> = ({
});
};
// Duplikovanie šablóny: kópia sa hneď uloží (Supabase, inak localStorage) a otvorí na úpravu
const duplicateBadgeTemplate = async (badge: BadgeTemplate) => {
const copy: BadgeTemplate = {
...JSON.parse(JSON.stringify(badge)),
id: `badge-${Date.now()}`,
name: `${badge.name} (kópia)`
};
let saved = copy;
try {
const { data, error } = await supabase
.from('badges')
.insert([toBadgeDbPayload(copy)])
.select();
if (!error && data && data[0]) {
saved = { ...copy, id: data[0].id };
}
} catch (e) {}
const list = [...badges];
const idx = list.findIndex((b) => b.id === badge.id);
list.splice(idx + 1, 0, saved);
setBadges(list);
setActiveBadge(saved);
setSelectedLayerId(saved.layersConfig?.[0]?.id ?? null);
try {
localStorage.setItem('coop_custom_badges', JSON.stringify(list));
} catch (e) {}
};
const deleteBadgeTemplate = async (badgeId: string) => {
if (confirm('Naozaj chcete zmazať túto šablónu cenovky?')) {
// Predvolené šablóny existujú len v prehliadači (localStorage), v databáze nie sú
@@ -423,17 +467,7 @@ export const BadgeEditorModal: React.FC<BadgeEditorModalProps> = ({
if (!activeBadge) return;
setIsSavingDb(true);
try {
const payload: any = {
name: activeBadge.name,
svg_raw: activeBadge.svgRaw,
layers_config: {
layers: activeBadge.layersConfig,
width: activeBadge.width || 110,
align: activeBadge.align || 'center',
paddingTop: activeBadge.paddingTop ?? 4,
paddingBottom: activeBadge.paddingBottom ?? 4
}
};
const payload = toBadgeDbPayload(activeBadge);
let savedBadge: BadgeTemplate | null = null;
const isExistingUuid = activeBadge.id && !activeBadge.id.startsWith('badge-');
@@ -646,17 +680,29 @@ export const BadgeEditorModal: React.FC<BadgeEditorModalProps> = ({
>
<div className="flex items-center justify-between mb-1">
<p className="text-xs font-bold text-gray-800 truncate">{b.name}</p>
<div className="flex items-center opacity-0 group-hover:opacity-100 transition-opacity">
<button
onClick={(e) => {
e.stopPropagation();
duplicateBadgeTemplate(b);
}}
className="text-gray-300 hover:text-blue-600 p-1 rounded transition-colors"
title="Duplikovať šablónu"
>
<Copy className="w-3.5 h-3.5" />
</button>
{/* Zmazať sa dá každá šablóna, aj staršie predvolené (badge-preset-*) uložené v prehliadači */}
<button
onClick={(e) => {
e.stopPropagation();
deleteBadgeTemplate(b.id);
}}
className="text-gray-300 hover:text-red-600 p-1 rounded transition-colors opacity-0 group-hover:opacity-100"
className="text-gray-300 hover:text-red-600 p-1 rounded transition-colors"
title="Zmazať šablónu"
>
<Trash2 className="w-3.5 h-3.5" />
</button>
</div>
</div>
<div
className="w-full h-12 flex items-center justify-center transform scale-75 origin-center overflow-hidden pointer-events-none"