197 lines
7.3 KiB
TypeScript
197 lines
7.3 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState, useEffect } from 'react';
|
|
import { BadgeTemplate } from '@/types/newsletter';
|
|
import { supabase } from '@/lib/supabase';
|
|
import { Tag, Plus, Check, Trash2, X } from 'lucide-react';
|
|
|
|
interface BadgeManagerModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onSelectBadge: (badge: BadgeTemplate) => void;
|
|
}
|
|
|
|
export const BadgeManagerModal: React.FC<BadgeManagerModalProps> = ({
|
|
isOpen,
|
|
onClose,
|
|
onSelectBadge
|
|
}) => {
|
|
const [badges, setBadges] = useState<BadgeTemplate[]>([]);
|
|
const [loading, setLoading] = useState(false);
|
|
const [creating, setCreating] = useState(false);
|
|
const [newBadgeName, setNewBadgeName] = useState('');
|
|
const [newBadgeColor, setNewBadgeColor] = useState('#e2001a');
|
|
const [newBadgeText, setNewBadgeText] = useState('SUPER CENA');
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
loadBadges();
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const loadBadges = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const { data, error } = await supabase.from('badges').select('*').order('created_at', { ascending: false });
|
|
if (data && !error) {
|
|
setBadges(
|
|
data.map((d: any) => ({
|
|
id: d.id,
|
|
name: d.name,
|
|
svgRaw: d.svg_raw,
|
|
layersConfig: d.layers_config || []
|
|
}))
|
|
);
|
|
}
|
|
} catch (err) {
|
|
console.error('Error loading badges:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const createBadge = async () => {
|
|
if (!newBadgeName) return;
|
|
|
|
const svg = `<svg viewBox="0 0 160 80" xmlns="http://www.w3.org/2000/svg"><rect width="160" height="80" rx="8" fill="${newBadgeColor}"/></svg>`;
|
|
const layers = [
|
|
{ id: 'l1', type: 'text', text: newBadgeText, x: 80, y: 35, fontSize: 18, fontWeight: '900', fill: '#ffffff', align: 'center' },
|
|
{ id: 'l2', type: 'text', text: '{zlava}', x: 80, y: 65, fontSize: 24, fontWeight: '900', fill: '#ffd100', align: 'center' }
|
|
];
|
|
|
|
try {
|
|
const { data, error } = await supabase.from('badges').insert([
|
|
{
|
|
name: newBadgeName,
|
|
svg_raw: svg,
|
|
layers_config: layers
|
|
}
|
|
]).select();
|
|
|
|
if (data && !error) {
|
|
loadBadges();
|
|
setCreating(false);
|
|
setNewBadgeName('');
|
|
}
|
|
} catch (err) {
|
|
console.error('Error creating badge:', err);
|
|
}
|
|
};
|
|
|
|
if (!isOpen) return null;
|
|
|
|
return (
|
|
<div className="fixed inset-0 z-50 bg-black/60 backdrop-blur-xs flex items-center justify-center p-4">
|
|
<div className="bg-white rounded-2xl shadow-2xl max-w-2xl w-full max-h-[85vh] overflow-hidden flex flex-col">
|
|
{/* Header */}
|
|
<div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between">
|
|
<div className="flex items-center gap-2">
|
|
<Tag className="w-5 h-5 text-red-600" />
|
|
<h2 className="text-lg font-bold text-gray-900">Správca grafických cenoviek</h2>
|
|
</div>
|
|
<button onClick={onClose} className="p-1 text-gray-400 hover:text-gray-600 rounded-lg">
|
|
<X className="w-5 h-5" />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="p-6 overflow-y-auto space-y-4">
|
|
<div className="flex justify-between items-center mb-2">
|
|
<p className="text-sm text-gray-500">
|
|
Vyberte cenovku pre produkt alebo navrhnite vlastnú.
|
|
</p>
|
|
<button
|
|
onClick={() => setCreating(!creating)}
|
|
className="text-xs font-bold text-red-600 hover:text-red-700 flex items-center gap-1 border border-red-200 px-3 py-1.5 rounded-lg hover:bg-red-50"
|
|
>
|
|
<Plus className="w-3.5 h-3.5" />
|
|
Nová cenovka
|
|
</button>
|
|
</div>
|
|
|
|
{/* Create Form */}
|
|
{creating && (
|
|
<div className="p-4 bg-gray-50 rounded-xl border border-gray-200 space-y-3">
|
|
<h3 className="text-xs font-bold text-gray-800 uppercase tracking-wider">Vytvoriť cenovku</h3>
|
|
<div className="grid grid-cols-2 gap-3">
|
|
<div>
|
|
<label className="block text-xs font-semibold text-gray-600 mb-1">Názov cenovky</label>
|
|
<input
|
|
type="text"
|
|
value={newBadgeName}
|
|
onChange={(e) => setNewBadgeName(e.target.value)}
|
|
placeholder="Napr. Modrá novinka"
|
|
className="w-full text-sm border border-gray-300 rounded-lg p-2"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-xs font-semibold text-gray-600 mb-1">Text cenovky</label>
|
|
<input
|
|
type="text"
|
|
value={newBadgeText}
|
|
onChange={(e) => setNewBadgeText(e.target.value)}
|
|
placeholder="Napr. EXTRA CENA"
|
|
className="w-full text-sm border border-gray-300 rounded-lg p-2"
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-xs font-semibold text-gray-600 mb-1">Farba pozadia</label>
|
|
<div className="flex items-center gap-2">
|
|
<input
|
|
type="color"
|
|
value={newBadgeColor}
|
|
onChange={(e) => setNewBadgeColor(e.target.value)}
|
|
className="w-8 h-8 rounded-md cursor-pointer border-0"
|
|
/>
|
|
<span className="text-xs font-mono">{newBadgeColor}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="flex justify-end gap-2 pt-2">
|
|
<button
|
|
onClick={() => setCreating(false)}
|
|
className="px-3 py-1.5 text-xs font-semibold text-gray-600"
|
|
>
|
|
Zrušiť
|
|
</button>
|
|
<button
|
|
onClick={createBadge}
|
|
className="px-4 py-1.5 text-xs font-bold bg-red-600 text-white rounded-lg hover:bg-red-700"
|
|
>
|
|
Uložiť do databázy
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Badges List */}
|
|
<div className="grid grid-cols-2 gap-4">
|
|
{badges.map((b) => (
|
|
<div
|
|
key={b.id}
|
|
className="border border-gray-200 rounded-xl p-4 flex flex-col items-center justify-between hover:border-red-400 hover:shadow-md transition-all group bg-white"
|
|
>
|
|
<span className="text-xs font-bold text-gray-700 mb-2">{b.name}</span>
|
|
<div
|
|
className="w-36 h-20 flex items-center justify-center relative my-2 overflow-hidden rounded-lg shadow-xs"
|
|
dangerouslySetInnerHTML={{ __html: b.svgRaw || '' }}
|
|
/>
|
|
<button
|
|
onClick={() => {
|
|
onSelectBadge(b);
|
|
onClose();
|
|
}}
|
|
className="mt-3 w-full py-1.5 text-xs font-bold text-white bg-gray-900 group-hover:bg-red-600 rounded-lg transition-colors flex items-center justify-center gap-1.5"
|
|
>
|
|
<Check className="w-3.5 h-3.5" />
|
|
Použiť na produkt
|
|
</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|