Refactor: Split DespesaCartaoFormSelect.tsx

Split DespesaCartaoFormSelect.tsx into smaller, more manageable components.
This commit is contained in:
gpt-engineer-app[bot] 2025-05-21 09:50:32 +00:00
parent 5e9b6ca74a
commit 1cb5be29cc
4 changed files with 242 additions and 187 deletions

View File

@ -0,0 +1,37 @@
import { FormControl, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { CartaoCredito } from "@/types/cartaoTypes";
interface CartaoSelectFieldProps {
cartoes: CartaoCredito[];
value: string;
onChange: (value: string) => void;
formatCartaoLabel: (cartao: CartaoCredito) => string;
}
export function CartaoSelectField({ cartoes, value, onChange, formatCartaoLabel }: CartaoSelectFieldProps) {
return (
<FormItem>
<FormLabel>Cartão de Crédito</FormLabel>
<Select
onValueChange={onChange}
defaultValue={value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Selecione um cartão" />
</SelectTrigger>
</FormControl>
<SelectContent>
{cartoes.map((cartao) => (
<SelectItem key={cartao.id} value={cartao.id}>
{formatCartaoLabel(cartao)}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
);
}

View File

@ -0,0 +1,57 @@
import { CalendarIcon } from "lucide-react";
import { format } from "date-fns";
import { ptBR } from "date-fns/locale";
import { FormControl, FormItem, FormLabel, FormMessage } from "@/components/ui/form";
import { Button } from "@/components/ui/button";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { Calendar } from "@/components/ui/calendar";
import { cn } from "@/lib/utils";
interface DatePickerFieldProps {
value: Date;
onChange: (date: Date | undefined) => void;
label: string;
}
export function DatePickerField({ value, onChange, label }: DatePickerFieldProps) {
return (
<FormItem className="flex flex-col">
<FormLabel>{label}</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-full pl-3 text-left font-normal",
!value && "text-muted-foreground"
)}
>
{value ? (
format(value, "dd/MM/yyyy", { locale: ptBR })
) : (
<span>Selecione uma data</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={value}
onSelect={onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
locale={ptBR}
className="p-3 pointer-events-auto"
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
);
}

View File

@ -1,49 +1,11 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { useToast } from '@/components/ui/use-toast';
import { CartaoCredito } from '@/types/cartaoTypes';
import { criarDespesa } from '@/services/cartao/despesasService';
import { CalendarIcon } from 'lucide-react';
import { format } from 'date-fns';
import { ptBR } from 'date-fns/locale';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from '@/components/ui/form';
import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Button } from '@/components/ui/button';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Calendar } from "@/components/ui/calendar";
import { cn } from '@/lib/utils';
const despesaCartaoSchema = z.object({
cartao_id: z.string().min(1, { message: 'Selecione um cartão' }),
valor: z.number().positive({ message: 'O valor deve ser maior que zero' }),
data_despesa: z.date({
required_error: "Selecione uma data",
}),
descricao: z.string().min(1, { message: 'Descrição é obrigatória' }),
});
type DespesaCartaoFormValues = z.infer<typeof despesaCartaoSchema>;
import { CartaoCredito } from '@/types/cartaoTypes';
import { DespesaCartaoFormValues, useDespesaCartaoForm } from '@/hooks/useDespesaCartaoForm';
import { CartaoSelectField } from './CartaoSelectField';
import { DatePickerField } from './DatePickerField';
interface DespesaCartaoFormSelectProps {
cartoes: CartaoCredito[];
@ -52,90 +14,13 @@ interface DespesaCartaoFormSelectProps {
}
export function DespesaCartaoFormSelect({ cartoes, onSuccess, onCancel }: DespesaCartaoFormSelectProps) {
const [isSubmitting, setIsSubmitting] = useState(false);
const { toast } = useToast();
const [selectedCartao, setSelectedCartao] = useState<CartaoCredito | null>(null);
const form = useForm<DespesaCartaoFormValues>({
resolver: zodResolver(despesaCartaoSchema),
defaultValues: {
valor: undefined,
data_despesa: new Date(),
descricao: '',
}
});
const handleCartaoChange = (cartaoId: string) => {
const cartao = cartoes.find(c => c.id === cartaoId);
setSelectedCartao(cartao || null);
};
const formatCartaoLabel = (cartao: CartaoCredito) => {
let label = cartao.nome;
if (cartao.banco) label += ` - ${cartao.banco}`;
if (cartao.bandeira) label += ` (${cartao.bandeira})`;
return label;
};
async function onSubmit(data: DespesaCartaoFormValues) {
if (!selectedCartao) {
toast({
title: "Erro",
description: "Selecione um cartão primeiro",
variant: "destructive"
});
return;
}
setIsSubmitting(true);
try {
const formattedDate = format(data.data_despesa, 'yyyy-MM-dd');
// Garantir que temos um cartao_codigo, mesmo que seja gerado na hora
const cartaoCodigo = selectedCartao.cartao_codigo || selectedCartao.nome;
console.log('Enviando dados para criar despesa:', {
cartao_id: data.cartao_id,
cartao_nome: selectedCartao.nome,
cartao_codigo: cartaoCodigo,
valor: data.valor,
data_despesa: formattedDate,
descricao: data.descricao
});
const resultado = await criarDespesa(
data.cartao_id,
cartaoCodigo,
data.valor,
formattedDate,
data.descricao
);
if (resultado) {
toast({
title: "Despesa adicionada",
description: "Despesa do cartão registrada com sucesso",
});
onSuccess();
} else {
toast({
title: "Erro ao salvar",
description: "Não foi possível salvar a despesa do cartão",
variant: "destructive"
});
}
} catch (error) {
console.error('Erro ao processar formulário:', error);
toast({
title: "Erro inesperado",
description: "Ocorreu um erro ao processar sua solicitação",
variant: "destructive"
});
} finally {
setIsSubmitting(false);
}
}
const {
form,
isSubmitting,
handleCartaoChange,
formatCartaoLabel,
onSubmit
} = useDespesaCartaoForm({ cartoes, onSuccess, onCancel });
return (
<Form {...form}>
@ -144,30 +29,15 @@ export function DespesaCartaoFormSelect({ cartoes, onSuccess, onCancel }: Despes
control={form.control}
name="cartao_id"
render={({ field }) => (
<FormItem>
<FormLabel>Cartão de Crédito</FormLabel>
<Select
onValueChange={(value) => {
field.onChange(value);
handleCartaoChange(value);
}}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Selecione um cartão" />
</SelectTrigger>
</FormControl>
<SelectContent>
{cartoes.map((cartao) => (
<SelectItem key={cartao.id} value={cartao.id}>
{formatCartaoLabel(cartao)}
</SelectItem>
))}
</SelectContent>
</Select>
<FormMessage />
</FormItem>
<CartaoSelectField
cartoes={cartoes}
value={field.value}
onChange={(value) => {
field.onChange(value);
handleCartaoChange(value);
}}
formatCartaoLabel={formatCartaoLabel}
/>
)}
/>
@ -195,42 +65,11 @@ export function DespesaCartaoFormSelect({ cartoes, onSuccess, onCancel }: Despes
control={form.control}
name="data_despesa"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Data da Despesa</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={cn(
"w-full pl-3 text-left font-normal",
!field.value && "text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "dd/MM/yyyy", { locale: ptBR })
) : (
<span>Selecione uma data</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date) =>
date > new Date() || date < new Date("1900-01-01")
}
initialFocus
locale={ptBR}
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
<DatePickerField
value={field.value}
onChange={field.onChange}
label="Data da Despesa"
/>
)}
/>

View File

@ -0,0 +1,122 @@
import { useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import * as z from 'zod';
import { useToast } from '@/hooks/use-toast';
import { CartaoCredito } from '@/types/cartaoTypes';
import { criarDespesa } from '@/services/cartao/despesasService';
import { format } from 'date-fns';
const despesaCartaoSchema = z.object({
cartao_id: z.string().min(1, { message: 'Selecione um cartão' }),
valor: z.number().positive({ message: 'O valor deve ser maior que zero' }),
data_despesa: z.date({
required_error: "Selecione uma data",
}),
descricao: z.string().min(1, { message: 'Descrição é obrigatória' }),
});
export type DespesaCartaoFormValues = z.infer<typeof despesaCartaoSchema>;
interface UseDespesaCartaoFormProps {
cartoes: CartaoCredito[];
onSuccess: () => void;
onCancel: () => void;
}
export function useDespesaCartaoForm({ cartoes, onSuccess, onCancel }: UseDespesaCartaoFormProps) {
const [isSubmitting, setIsSubmitting] = useState(false);
const { toast } = useToast();
const [selectedCartao, setSelectedCartao] = useState<CartaoCredito | null>(null);
const form = useForm<DespesaCartaoFormValues>({
resolver: zodResolver(despesaCartaoSchema),
defaultValues: {
valor: undefined,
data_despesa: new Date(),
descricao: '',
}
});
const handleCartaoChange = (cartaoId: string) => {
const cartao = cartoes.find(c => c.id === cartaoId);
setSelectedCartao(cartao || null);
};
const formatCartaoLabel = (cartao: CartaoCredito) => {
let label = cartao.nome;
if (cartao.banco) label += ` - ${cartao.banco}`;
if (cartao.bandeira) label += ` (${cartao.bandeira})`;
return label;
};
async function onSubmit(data: DespesaCartaoFormValues) {
if (!selectedCartao) {
toast({
title: "Erro",
description: "Selecione um cartão primeiro",
variant: "destructive"
});
return;
}
setIsSubmitting(true);
try {
const formattedDate = format(data.data_despesa, 'yyyy-MM-dd');
// Garantir que temos um cartao_codigo, mesmo que seja gerado na hora
const cartaoCodigo = selectedCartao.cartao_codigo || selectedCartao.nome;
console.log('Enviando dados para criar despesa:', {
cartao_id: data.cartao_id,
cartao_nome: selectedCartao.nome,
cartao_codigo: cartaoCodigo,
valor: data.valor,
data_despesa: formattedDate,
descricao: data.descricao
});
const resultado = await criarDespesa(
data.cartao_id,
cartaoCodigo,
data.valor,
formattedDate,
data.descricao
);
if (resultado) {
toast({
title: "Despesa adicionada",
description: "Despesa do cartão registrada com sucesso",
});
onSuccess();
} else {
toast({
title: "Erro ao salvar",
description: "Não foi possível salvar a despesa do cartão",
variant: "destructive"
});
}
} catch (error) {
console.error('Erro ao processar formulário:', error);
toast({
title: "Erro inesperado",
description: "Ocorreu um erro ao processar sua solicitação",
variant: "destructive"
});
} finally {
setIsSubmitting(false);
}
}
return {
form,
isSubmitting,
selectedCartao,
handleCartaoChange,
formatCartaoLabel,
onSubmit
};
}