import { Edit2, Pipette, Plus, Trash2 } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; import { categoriesApi } from '../../api/client'; import { useTranslation } from '../../i18n'; import { getApiErrorMessage } from '../../types'; import { CATEGORY_ICON_MAP, ICON_LABELS, getCategoryIcon } from '../shared/categoryIcons'; import { useToast } from '../shared/Toast'; const PRESET_COLORS = [ '#6366f1', '#8b5cf6', '#ec4899', '#ef4444', '#f97316', '#f59e0b', '#10b981', '#06b6d4', '#3b82f6', '#84cc16', '#6b7280', '#1f2937', ]; const ICON_NAMES = Object.keys(CATEGORY_ICON_MAP); export default function CategoryManager() { const [categories, setCategories] = useState([]); const [showForm, setShowForm] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState({ name: '', color: '#6366f1', icon: 'MapPin' }); const [isSaving, setIsSaving] = useState(false); const [isLoading, setIsLoading] = useState(true); const colorInputRef = useRef(null); const toast = useToast(); const { t } = useTranslation(); useEffect(() => { loadCategories(); }, []); const loadCategories = async () => { setIsLoading(true); try { const data = await categoriesApi.list(); setCategories(data.categories || []); } catch (err: unknown) { toast.error(t('categories.toast.loadError')); } finally { setIsLoading(false); } }; const handleStartEdit = (cat) => { setEditingId(cat.id); setForm({ name: cat.name, color: cat.color || '#6366f1', icon: cat.icon || 'MapPin' }); setShowForm(false); }; const handleStartCreate = () => { setEditingId(null); setForm({ name: '', color: '#6366f1', icon: 'MapPin' }); setShowForm(true); }; const handleCancel = () => { setShowForm(false); setEditingId(null); }; const handleSave = async () => { if (!form.name.trim()) { toast.error(t('categories.toast.nameRequired')); return; } setIsSaving(true); try { if (editingId) { const result = await categoriesApi.update(editingId, form); setCategories((prev) => prev.map((c) => (c.id === editingId ? result.category : c))); setEditingId(null); toast.success(t('categories.toast.updated')); } else { const result = await categoriesApi.create(form); setCategories((prev) => [...prev, result.category]); setShowForm(false); toast.success(t('categories.toast.created')); } setForm({ name: '', color: '#6366f1', icon: 'MapPin' }); } catch (err: unknown) { toast.error(getApiErrorMessage(err, t('categories.toast.saveError'))); } finally { setIsSaving(false); } }; const handleDelete = async (id) => { if (!confirm(t('categories.confirm.delete'))) return; try { await categoriesApi.delete(id); setCategories((prev) => prev.filter((c) => c.id !== id)); toast.success(t('categories.toast.deleted')); } catch (err: unknown) { toast.error(getApiErrorMessage(err, t('categories.toast.deleteError'))); } }; const isPresetColor = PRESET_COLORS.includes(form.color); const PreviewIcon = getCategoryIcon(form.icon); const categoryForm = (
setForm((prev) => ({ ...prev, name: e.target.value }))} placeholder={t('categories.namePlaceholder')} className="w-full rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-slate-400" autoFocus />
{ICON_NAMES.map((name) => { const Icon = CATEGORY_ICON_MAP[name]; const isSelected = form.icon === name; return ( ); })}
{PRESET_COLORS.map((color) => (
{t('categories.preview')}: {form.name || t('categories.defaultName')}
); return (

{t('categories.title')}

{t('categories.subtitle')}

{showForm &&
{categoryForm}
} {isLoading ? (
) : categories.length === 0 ? (

{t('categories.empty')}

) : (
{categories.map((cat) => { const Icon = getCategoryIcon(cat.icon); return (
{editingId === cat.id ? (
{categoryForm}
) : (
{cat.name} {cat.color}
)}
); })}
)}
); }