"use client"; import { useCallback, useEffect, useMemo, useState, useTransition, } from "react"; import { toast } from "sonner"; import { createBudgetAction, updateBudgetAction, } from "@/app/(dashboard)/orcamentos/actions"; import { CategoryIcon } from "@/components/categorias/category-icon"; import { PeriodPicker } from "@/components/period-picker"; import { Button } from "@/components/ui/button"; import { CurrencyInput } from "@/components/ui/currency-input"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { useControlledState } from "@/hooks/use-controlled-state"; import { useFormState } from "@/hooks/use-form-state"; import type { Budget, BudgetCategory, BudgetFormValues } from "./types"; interface BudgetDialogProps { mode: "create" | "update"; trigger?: React.ReactNode; budget?: Budget; categories: BudgetCategory[]; defaultPeriod: string; open?: boolean; onOpenChange?: (open: boolean) => void; } const buildInitialValues = ({ budget, defaultPeriod, }: { budget?: Budget; defaultPeriod: string; }): BudgetFormValues => ({ categoriaId: budget?.category?.id ?? "", period: budget?.period ?? defaultPeriod, amount: budget ? (Math.round(budget.amount * 100) / 100).toFixed(2) : "", }); export function BudgetDialog({ mode, trigger, budget, categories, defaultPeriod, open, onOpenChange, }: BudgetDialogProps) { const [errorMessage, setErrorMessage] = useState(null); const [isPending, startTransition] = useTransition(); // Use controlled state hook for dialog open state const [dialogOpen, setDialogOpen] = useControlledState( open, false, onOpenChange, ); const initialState = useMemo( () => buildInitialValues({ budget, defaultPeriod, }), [budget, defaultPeriod], ); // Use form state hook for form management const { formState, updateField, setFormState } = useFormState(initialState); // Reset form when dialog opens useEffect(() => { if (dialogOpen) { setFormState(initialState); setErrorMessage(null); } }, [dialogOpen, initialState, setFormState]); // Clear error when dialog closes useEffect(() => { if (!dialogOpen) { setErrorMessage(null); } }, [dialogOpen]); const handleSubmit = useCallback( (event: React.FormEvent) => { event.preventDefault(); setErrorMessage(null); if (mode === "update" && !budget?.id) { const message = "Orçamento inválido."; setErrorMessage(message); toast.error(message); return; } if (formState.categoriaId.length === 0) { const message = "Selecione uma categoria."; setErrorMessage(message); toast.error(message); return; } if (formState.period.length === 0) { const message = "Informe o período."; setErrorMessage(message); toast.error(message); return; } if (formState.amount.length === 0) { const message = "Informe o valor limite."; setErrorMessage(message); toast.error(message); return; } const payload = { categoriaId: formState.categoriaId, period: formState.period, amount: formState.amount, }; startTransition(async () => { const result = mode === "create" ? await createBudgetAction(payload) : await updateBudgetAction({ id: budget?.id ?? "", ...payload, }); if (result.success) { toast.success(result.message); setDialogOpen(false); setFormState(initialState); return; } setErrorMessage(result.error); toast.error(result.error); }); }, [budget?.id, formState, initialState, mode, setDialogOpen, setFormState], ); const title = mode === "create" ? "Novo orçamento" : "Editar orçamento"; const description = mode === "create" ? "Defina um limite de gastos para acompanhar suas despesas." : "Atualize os detalhes do orçamento selecionado."; const submitLabel = mode === "create" ? "Salvar orçamento" : "Atualizar orçamento"; const disabled = categories.length === 0; return ( {trigger ? {trigger} : null} {title} {description} {disabled ? (
Cadastre pelo menos uma categoria de despesa para criar um orçamento.
) : (
updateField("period", value)} className="w-full" />
updateField("amount", value)} />
{errorMessage ? (

{errorMessage}

) : null}
)}
); }