"use client"; import { useEffect, 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 { 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 { Slider } from "@/components/ui/slider"; 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) : "", }); const formatCurrency = (value: number) => new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL", }).format(value); 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 = buildInitialValues({ budget, defaultPeriod, }); // Use form state hook for form management const { formState, resetForm, updateField } = useFormState(initialState); // Reset form when dialog opens useEffect(() => { if (dialogOpen) { resetForm(initialState); setErrorMessage(null); } }, [dialogOpen, initialState, resetForm]); // Clear error when dialog closes useEffect(() => { if (!dialogOpen) { setErrorMessage(null); } }, [dialogOpen]); const handleSubmit = (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); resetForm(initialState); return; } setErrorMessage(result.error); toast.error(result.error); }); }; 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; const parsedAmount = Number.parseFloat(formState.amount); const sliderValue = Number.isFinite(parsedAmount) ? Math.max(0, parsedAmount) : 0; const baseForSlider = Math.max(budget?.spent ?? 0, sliderValue, 1000); const sliderMax = Math.max( 1000, Math.ceil((baseForSlider * 1.5) / 100) * 100, ); 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" />
Limite atual {formatCurrency(sliderValue)}
updateField("amount", value[0]?.toFixed(2) ?? "0.00") } />
{formatCurrency(0)} {formatCurrency(sliderMax)}
{errorMessage ? (

{errorMessage}

) : null}
)}
); }