'use client' import { useState, useEffect, useCallback } from 'react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { useToast } from '@/components/ui/use-toast' import { ChevronDown, ChevronRight, Plus, Pencil, Trash2, Search, Upload, Loader2, X, Check, LayoutGrid, ImageIcon, Info, } from 'lucide-react' interface LibraryIcon { id: string name: string mimeType: string iconType: string categoryId: string categoryName: string } interface MySymbol { id: string iconId: string name: string customName: string | null baseName: string mimeType: string iconType: string categoryName: string sortOrder: number } export function SymbolManager() { const { toast } = useToast() const [loading, setLoading] = useState(true) const [library, setLibrary] = useState([]) const [mySymbols, setMySymbols] = useState([]) // Library UI state const [librarySearch, setLibrarySearch] = useState('') const [expandedCategories, setExpandedCategories] = useState>(new Set()) const [libraryCollapsed, setLibraryCollapsed] = useState(false) // My Symbols UI state const [editingId, setEditingId] = useState(null) const [editName, setEditName] = useState('') const fetchData = useCallback(async () => { setLoading(true) try { const res = await fetch('/api/tenant/symbols') if (res.ok) { const data = await res.json() setLibrary(data.library || []) setMySymbols(data.mySymbols || []) // Auto-collapse library if tenant has own symbols if ((data.mySymbols || []).length > 0) { setLibraryCollapsed(true) } } } catch {} setLoading(false) }, []) useEffect(() => { fetchData() }, [fetchData]) // Add symbol from library to my collection const addSymbol = async (iconId: string, customName?: string) => { try { const res = await fetch('/api/tenant/symbols', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ iconId, customName }), }) if (res.ok) { const symbol = await res.json() setMySymbols(prev => [...prev, symbol]) toast({ title: 'Symbol hinzugefügt' }) } } catch { toast({ title: 'Fehler', variant: 'destructive' }) } } // Rename a symbol const renameSymbol = async (id: string, customName: string) => { try { await fetch('/api/tenant/symbols', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id, customName }), }) setMySymbols(prev => prev.map(s => s.id === id ? { ...s, name: customName || s.baseName, customName: customName || null } : s )) setEditingId(null) setEditName('') toast({ title: 'Umbenannt' }) } catch { toast({ title: 'Fehler', variant: 'destructive' }) } } // Remove a symbol const removeSymbol = async (id: string) => { try { await fetch('/api/tenant/symbols', { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id }), }) setMySymbols(prev => prev.filter(s => s.id !== id)) toast({ title: 'Symbol entfernt' }) } catch { toast({ title: 'Fehler', variant: 'destructive' }) } } // Toggle category expand/collapse const toggleCategory = (cat: string) => { setExpandedCategories(prev => { const next = new Set(prev) next.has(cat) ? next.delete(cat) : next.add(cat) return next }) } // Group library icons by category const filteredLibrary = library.filter(icon => !librarySearch || icon.name.toLowerCase().includes(librarySearch.toLowerCase()) ) const libraryGrouped = filteredLibrary.reduce>((acc, icon) => { const key = icon.categoryName if (!acc[key]) acc[key] = [] acc[key].push(icon) return acc }, {}) if (loading) { return (
Symbole laden...
) } return (
{/* ===== MEINE SYMBOLE (always on top, prominent) ===== */}

Meine Symbole ({mySymbols.length})

{mySymbols.length === 0 ? (

Noch keine eigenen Symbole definiert.

Füge Symbole aus der Bibliothek unten hinzu oder lade eigene SVGs hoch.

) : (
{mySymbols.map(sym => (
{sym.name}
{/* Name / Edit */} {editingId === sym.id ? (
setEditName(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') renameSymbol(sym.id, editName) if (e.key === 'Escape') { setEditingId(null); setEditName('') } }} className="h-6 text-[10px] px-1" autoFocus />
) : (

{ setEditingId(sym.id); setEditName(sym.name) }} > {sym.name}

)} {/* Hover actions */}
{/* Custom name badge */} {sym.customName && (
)}
))}
)}
{/* ===== UPLOAD HINWEIS ===== */}
Tipp: Eigene Symbole am besten als SVG hochladen — diese werden in jeder Grösse scharf dargestellt. PNG/JPEG sind auch möglich, können aber bei Vergrösserung unscharf werden.
{/* ===== STANDARD-BIBLIOTHEK (collapsible) ===== */}
{!libraryCollapsed && (
{/* Search */}
setLibrarySearch(e.target.value)} className="pl-9" />
{/* Categories */} {Object.entries(libraryGrouped).sort(([a], [b]) => a.localeCompare(b)).map(([catName, icons]) => (
{expandedCategories.has(catName) && (
{icons.map(icon => { const alreadyAdded = mySymbols.some(s => s.iconId === icon.id) return ( ) })}
)}
))} {Object.keys(libraryGrouped).length === 0 && (
{librarySearch ? 'Keine Symbole gefunden.' : 'Keine Standard-Symbole vorhanden.'}
)}
)}
) }