"use client"; import Image from "next/image"; import { useEffect, useMemo, useState, useTransition } from "react"; import { toast } from "sonner"; import { createPayerAction, updatePayerAction, } from "@/features/payers/actions"; import { Button } from "@/shared/components/ui/button"; import { Checkbox } from "@/shared/components/ui/checkbox"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/shared/components/ui/dialog"; import { Input } from "@/shared/components/ui/input"; import { Label } from "@/shared/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/shared/components/ui/select"; import { useControlledState } from "@/shared/hooks/use-controlled-state"; import { useFormState } from "@/shared/hooks/use-form-state"; import { DEFAULT_PAYER_AVATAR, PAYER_STATUS_OPTIONS, type PayerStatus, } from "@/shared/lib/payers/constants"; import { getAvatarSrc } from "@/shared/lib/payers/utils"; import { StatusSelectContent } from "./payer-select-items"; import type { Payer, PayerFormValues } from "./types"; type PayerCreatePayload = Parameters[0]; interface PayerDialogProps { mode: "create" | "update"; trigger?: React.ReactNode; payer?: Payer; avatarOptions: string[]; open?: boolean; onOpenChange?: (open: boolean) => void; } const buildInitialValues = ({ payer, avatarOptions, }: { payer?: Payer; avatarOptions: string[]; }): PayerFormValues => { const defaultAvatar = avatarOptions[0] ?? DEFAULT_PAYER_AVATAR; return { name: payer?.name ?? "", email: payer?.email ?? "", status: (payer?.status as PayerStatus) ?? PAYER_STATUS_OPTIONS[0], avatarUrl: payer?.avatarUrl ?? defaultAvatar, note: payer?.note ?? "", isAutoSend: payer?.isAutoSend ?? false, }; }; export function PayerDialog({ mode, trigger, payer, avatarOptions, open, onOpenChange, }: PayerDialogProps) { 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({ payer, avatarOptions }), [payer, avatarOptions], ); // Use form state hook for form management const { formState, resetForm, updateField } = useFormState(initialState); const availableAvatars = useMemo(() => { const set = new Set([ ...avatarOptions, initialState.avatarUrl, DEFAULT_PAYER_AVATAR, ]); return Array.from(set).sort((a, b) => a.localeCompare(b, "pt-BR", { sensitivity: "base" }), ); }, [avatarOptions, initialState.avatarUrl]); // Reset form when dialog opens useEffect(() => { if (dialogOpen) { resetForm(initialState); setErrorMessage(null); } }, [dialogOpen, initialState, resetForm]); const handleSubmit = (event: React.FormEvent) => { event.preventDefault(); setErrorMessage(null); const payerId = payer?.id; if (mode === "update" && !payerId) { const message = "Pagador inválido."; setErrorMessage(message); toast.error(message); return; } const emailValue = formState.email.trim(); const payload: PayerCreatePayload = { name: formState.name.trim(), status: formState.status, avatarUrl: formState.avatarUrl, email: emailValue || null, note: formState.note.trim() || null, isAutoSend: formState.isAutoSend, }; startTransition(async () => { const result = mode === "create" ? await createPayerAction(payload) : await updatePayerAction({ id: payerId ?? "", ...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 pagador" : "Editar pagador"; const description = mode === "create" ? "Selecione um avatar e informe os detalhes para criar um novo pagador." : "Atualize os detalhes do pagador selecionado."; const submitLabel = mode === "create" ? "Salvar pagador" : "Atualizar pagador"; return ( {trigger ? {trigger} : null} {title} {description}
updateField("name", event.target.value) } placeholder="Ex.: Felipe Coutinho" required />
updateField("email", event.target.value) } placeholder="Ex.: felipe@email.com" />
updateField("isAutoSend", Boolean(checked)) } aria-label="Ativar envio automático" />

Dispare cobranças e lembretes sem intervenção manual.

{availableAvatars.map((avatar) => { const isSelected = avatar === formState.avatarUrl; return ( ); })}
updateField("note", event.target.value)} placeholder="Observações sobre este pagador" />
{errorMessage ? (

{errorMessage}

) : null}
); }