import { useEffect, useState } from "react"; import { X } from "lucide-react"; import { useCreateCategory, useUpdateCategory } from "../hooks/useCategories"; import { useToast } from "../context/ToastContext"; import type { Category, CategoryType } from "../api/types"; interface Props { isOpen: boolean; onClose: () => void; category?: Category; } interface FormState { name: string; type: CategoryType; color: string; icon: string; } const DEFAULT_FORM: FormState = { name: "", type: "expense", color: "#6366f1", icon: "", }; export default function CategoryModal({ isOpen, onClose, category }: Props) { const createMutation = useCreateCategory(); const updateMutation = useUpdateCategory(); const { addToast } = useToast(); const [form, setForm] = useState(DEFAULT_FORM); const [errors, setErrors] = useState>>({}); const isEditing = !!category; const isDefault = category?.is_default ?? false; const isPending = createMutation.isPending || updateMutation.isPending; useEffect(() => { if (category) { setForm({ name: category.name, type: category.type, color: category.color ?? "#6366f1", icon: category.icon ?? "", }); } else { setForm(DEFAULT_FORM); } setErrors({}); }, [category, isOpen]); function set(key: K, value: FormState[K]) { setForm((prev) => ({ ...prev, [key]: value })); if (errors[key]) setErrors((e) => ({ ...e, [key]: undefined })); } function validate(): boolean { const errs: Partial> = {}; if (!form.name.trim()) errs.name = "Nom requis"; setErrors(errs); return Object.keys(errs).length === 0; } async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!validate()) return; if (isEditing && category) { updateMutation.mutate( { id: category.id, payload: { name: form.name.trim(), color: form.color, icon: form.icon || undefined, }, }, { onSuccess: () => { addToast({ type: "success", message: "Catégorie modifiée" }); onClose(); }, onError: () => addToast({ type: "error", message: "Erreur lors de la modification" }), }, ); } else { createMutation.mutate( { name: form.name.trim(), type: form.type, color: form.color, icon: form.icon || undefined, }, { onSuccess: () => { addToast({ type: "success", message: "Catégorie créée" }); onClose(); }, onError: () => addToast({ type: "error", message: "Erreur lors de la création" }), }, ); } } if (!isOpen) return null; const CATEGORY_TYPES: { value: CategoryType; label: string }[] = [ { value: "expense", label: "Dépense" }, { value: "income", label: "Revenu" }, { value: "both", label: "Les deux" }, ]; return (
e.stopPropagation()} > {/* Header */}

{isEditing ? "Modifier la catégorie" : "Nouvelle catégorie"}

{isDefault && (

Les catégories par défaut ne peuvent pas être modifiées en type.

)} {/* Name */}
set("name", e.target.value)} placeholder="Ex. : Alimentation" className={`w-full rounded-lg border px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-primary/30 ${ errors.name ? "border-red-400" : "border-slate-200" }`} /> {errors.name && (

{errors.name}

)}
{/* Type */}
{/* Color */}
set("color", e.target.value)} className="h-9 w-14 cursor-pointer rounded border border-slate-200 p-0.5" /> {form.color}
{/* Icon */}
set("icon", e.target.value)} placeholder="Ex. : shopping-cart" className="w-full rounded-lg border border-slate-200 px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-primary/30" />
{/* Actions */}
); }