Refactor: Split Transacoes.tsx into smaller components

Refactored the `TransacoesPage` component into smaller, more manageable components to improve code organization and readability. This includes extracting the table, form, and dialog related logic into separate files. Unused imports and files were also removed.
This commit is contained in:
gpt-engineer-app[bot] 2025-05-20 23:11:39 +00:00
parent 9d02a2203d
commit afb53c1429
6 changed files with 368 additions and 263 deletions

View File

@ -0,0 +1,87 @@
import React from 'react';
import { TransactionForm } from '@/components/dashboard/TransactionForm';
import { DespesaCartaoFormSelect } from '@/components/credito/DespesaCartaoFormSelect';
import { Transaction } from '@/types/financialTypes';
import { CartaoCredito } from '@/types/cartaoTypes';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
interface TransactionDialogsProps {
isDialogOpen: boolean;
isCartaoCreditoDialogOpen: boolean;
tipoForm: 'receita' | 'despesa';
selectedTransaction: Transaction | null;
isEditing: boolean;
cartoes: CartaoCredito[];
onTransactionSuccess: () => void;
onDespesaCartaoSuccess: () => void;
onCloseDialog: () => void;
onCloseCartaoCreditoDialog: () => void;
}
export const TransactionDialogs = ({
isDialogOpen,
isCartaoCreditoDialogOpen,
tipoForm,
selectedTransaction,
isEditing,
cartoes,
onTransactionSuccess,
onDespesaCartaoSuccess,
onCloseDialog,
onCloseCartaoCreditoDialog
}: TransactionDialogsProps) => {
return (
<>
{/* Dialog para transações */}
<Dialog open={isDialogOpen} onOpenChange={onCloseDialog}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle>
{isEditing
? `Editar ${tipoForm === 'receita' ? 'Receita' : 'Despesa'}`
: `Nova ${tipoForm === 'receita' ? 'Receita' : 'Despesa'}`
}
</DialogTitle>
<DialogDescription>
{isEditing
? 'Edite os campos para atualizar a transação.'
: `Preencha os campos para registrar uma nova ${tipoForm === 'receita' ? 'receita' : 'despesa'}.`
}
</DialogDescription>
</DialogHeader>
<TransactionForm
onSuccess={onTransactionSuccess}
onCancel={onCloseDialog}
defaultTipo={tipoForm}
transaction={selectedTransaction}
isEditing={isEditing}
/>
</DialogContent>
</Dialog>
{/* Dialog para despesas de cartão */}
<Dialog open={isCartaoCreditoDialogOpen} onOpenChange={onCloseCartaoCreditoDialog}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle>Nova Despesa de Cartão</DialogTitle>
<DialogDescription>
Selecione o cartão e preencha os detalhes da despesa.
</DialogDescription>
</DialogHeader>
<DespesaCartaoFormSelect
cartoes={cartoes}
onSuccess={onDespesaCartaoSuccess}
onCancel={onCloseCartaoCreditoDialog}
/>
</DialogContent>
</Dialog>
</>
);
};

View File

@ -0,0 +1,43 @@
import React from 'react';
import { Button } from '@/components/ui/button';
import { PlusCircle, CreditCard } from 'lucide-react';
interface TransactionHeaderProps {
onOpenDialog: (tipo: 'receita' | 'despesa') => void;
onOpenCartaoCreditoDialog: () => void;
}
export const TransactionHeader = ({
onOpenDialog,
onOpenCartaoCreditoDialog
}: TransactionHeaderProps) => {
return (
<div className="flex justify-between items-center">
<h1 className="text-2xl font-bold tracking-tight">Todas as Transações</h1>
<div className="flex gap-2">
<Button
className="flex items-center gap-2 bg-finance-green hover:bg-finance-green/90"
onClick={() => onOpenDialog('receita')}
>
<PlusCircle className="h-4 w-4" />
Nova Receita
</Button>
<Button
className="flex items-center gap-2 bg-finance-red hover:bg-finance-red/90"
onClick={() => onOpenDialog('despesa')}
>
<PlusCircle className="h-4 w-4" />
Nova Despesa
</Button>
<Button
className="flex items-center gap-2"
onClick={onOpenCartaoCreditoDialog}
>
<CreditCard className="h-4 w-4" />
Despesa Cartão
</Button>
</div>
</div>
);
};

View File

@ -0,0 +1,45 @@
import React from 'react';
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
interface TransactionSummaryCardsProps {
totalReceitas: number;
totalDespesas: number;
formatCurrency: (value: number) => string;
}
export const TransactionSummaryCards = ({
totalReceitas,
totalDespesas,
formatCurrency
}: TransactionSummaryCardsProps) => {
return (
<div className="grid gap-4 md:grid-cols-2">
<Card>
<CardHeader className="pb-2 bg-green-50">
<CardTitle className="text-finance-green flex items-center">
Ganhos do mês
</CardTitle>
</CardHeader>
<CardContent className="pt-4">
<p className="text-2xl font-bold text-finance-green">
{formatCurrency(totalReceitas)}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-2 bg-red-50">
<CardTitle className="text-finance-red flex items-center">
Gastos do mês
</CardTitle>
</CardHeader>
<CardContent className="pt-4">
<p className="text-2xl font-bold text-finance-red">
{formatCurrency(totalDespesas)}
</p>
</CardContent>
</Card>
</div>
);
};

View File

@ -0,0 +1,145 @@
import { useState, useEffect } from 'react';
import { Transaction } from '@/types/financialTypes';
import { CartaoCredito } from '@/types/cartaoTypes';
import { useToast } from '@/hooks/use-toast';
import { getTransacoes } from '@/services/transacaoService';
import { getCartoes } from '@/services/cartaoCreditoService';
export const useTransactions = () => {
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [tipoForm, setTipoForm] = useState<'receita' | 'despesa'>('despesa');
const [selectedTransaction, setSelectedTransaction] = useState<Transaction | null>(null);
const [isEditing, setIsEditing] = useState(false);
const [isCartaoCreditoDialogOpen, setIsCartaoCreditoDialogOpen] = useState(false);
const [cartoes, setCartoes] = useState<CartaoCredito[]>([]);
const { toast } = useToast();
const loadTransactions = async () => {
try {
setIsLoading(true);
console.log("Carregando todas as transações...");
const data = await getTransacoes();
console.log(`${data.length} transações carregadas com sucesso`);
setTransactions(data);
} catch (error) {
console.error("Erro ao carregar transações:", error);
toast({
title: "Erro ao carregar transações",
description: "Não foi possível obter os dados do Supabase",
variant: "destructive"
});
} finally {
setIsLoading(false);
}
};
const loadCartoes = async () => {
try {
const data = await getCartoes();
setCartoes(data);
} catch (error) {
console.error("Erro ao carregar cartões:", error);
toast({
title: "Erro ao carregar cartões",
description: "Não foi possível obter os dados dos cartões",
variant: "destructive"
});
}
};
useEffect(() => {
loadTransactions();
loadCartoes();
}, []);
const handleTransactionSuccess = () => {
setIsDialogOpen(false);
setSelectedTransaction(null);
setIsEditing(false);
loadTransactions();
toast({
title: isEditing ? "Transação atualizada" : "Transação registrada",
description: isEditing
? "A transação foi atualizada com sucesso"
: "A nova transação foi adicionada com sucesso",
});
};
const handleDespesaCartaoSuccess = () => {
setIsCartaoCreditoDialogOpen(false);
loadCartoes();
loadTransactions();
toast({
title: "Despesa de cartão registrada",
description: "A despesa do cartão foi adicionada com sucesso",
});
};
const handleEditTransaction = (transaction: Transaction) => {
setSelectedTransaction(transaction);
setTipoForm(transaction.tipo as 'receita' | 'despesa');
setIsEditing(true);
setIsDialogOpen(true);
};
const handleCloseDialog = () => {
setIsDialogOpen(false);
setSelectedTransaction(null);
setIsEditing(false);
};
const handleCloseCartaoCreditoDialog = () => {
setIsCartaoCreditoDialogOpen(false);
};
const handleOpenDialog = (tipo: 'receita' | 'despesa') => {
setTipoForm(tipo);
setIsEditing(false);
setSelectedTransaction(null);
setIsDialogOpen(true);
};
const handleOpenCartaoCreditoDialog = () => {
setIsCartaoCreditoDialogOpen(true);
};
// Separar transações em receitas e despesas
const receitas = transactions.filter(t => t.tipo === 'receita');
const despesas = transactions.filter(t => t.tipo === 'despesa');
// Calcular totais
const totalReceitas = receitas.reduce((sum, t) => sum + Math.abs(t.valor), 0);
const totalDespesas = despesas.reduce((sum, t) => sum + Math.abs(t.valor), 0);
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL',
}).format(value);
};
return {
transactions,
isLoading,
isDialogOpen,
tipoForm,
selectedTransaction,
isEditing,
isCartaoCreditoDialogOpen,
cartoes,
totalReceitas,
totalDespesas,
formatCurrency,
handleTransactionSuccess,
handleDespesaCartaoSuccess,
handleEditTransaction,
handleCloseDialog,
handleCloseCartaoCreditoDialog,
handleOpenDialog,
handleOpenCartaoCreditoDialog,
loadTransactions
};
};

View File

@ -239,42 +239,6 @@ export type Database = {
}
Relationships: []
}
rodrigo_audio_messages: {
Row: {
audio_url: string | null
created_at: string
duration: number | null
id: string
instance_id: string
message_id: string | null
metadata: Json | null
sender_id: string
sender_name: string | null
}
Insert: {
audio_url?: string | null
created_at?: string
duration?: number | null
id?: string
instance_id: string
message_id?: string | null
metadata?: Json | null
sender_id: string
sender_name?: string | null
}
Update: {
audio_url?: string | null
created_at?: string
duration?: number | null
id?: string
instance_id?: string
message_id?: string | null
metadata?: Json | null
sender_id?: string
sender_name?: string | null
}
Relationships: []
}
transacoes: {
Row: {
categoria: string | null

View File

@ -1,198 +1,49 @@
import { useState, useEffect } from 'react';
import React from 'react';
import Layout from '@/components/layout/Layout';
import TransactionsTable from '@/components/dashboard/TransactionsTable';
import { TransactionForm } from '@/components/dashboard/TransactionForm';
import { Transaction } from '@/types/financialTypes';
import { useToast } from "@/hooks/use-toast";
import { getTransacoes } from '@/services/transacaoService';
import { Button } from '@/components/ui/button';
import { PlusCircle, CreditCard } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { CartaoCredito } from '@/types/cartaoTypes';
import { getCartoes } from '@/services/cartaoCreditoService';
import { DespesaCartaoFormSelect } from '@/components/credito/DespesaCartaoFormSelect';
import { useTransactions } from '@/hooks/useTransactions';
import { TransactionHeader } from '@/components/transacoes/TransactionHeader';
import { TransactionSummaryCards } from '@/components/transacoes/TransactionSummaryCards';
import { TransactionDialogs } from '@/components/transacoes/TransactionDialogs';
const TransacoesPage = () => {
const [transactions, setTransactions] = useState<Transaction[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [tipoForm, setTipoForm] = useState<'receita' | 'despesa'>('despesa');
const [selectedTransaction, setSelectedTransaction] = useState<Transaction | null>(null);
const [isEditing, setIsEditing] = useState(false);
const [isCartaoCreditoDialogOpen, setIsCartaoCreditoDialogOpen] = useState(false);
const [cartoes, setCartoes] = useState<CartaoCredito[]>([]);
const { toast } = useToast();
const loadTransactions = async () => {
try {
setIsLoading(true);
console.log("Carregando todas as transações...");
const data = await getTransacoes();
console.log(`${data.length} transações carregadas com sucesso`);
setTransactions(data);
} catch (error) {
console.error("Erro ao carregar transações:", error);
toast({
title: "Erro ao carregar transações",
description: "Não foi possível obter os dados do Supabase",
variant: "destructive"
});
} finally {
setIsLoading(false);
}
};
const loadCartoes = async () => {
try {
const data = await getCartoes();
setCartoes(data);
} catch (error) {
console.error("Erro ao carregar cartões:", error);
toast({
title: "Erro ao carregar cartões",
description: "Não foi possível obter os dados dos cartões",
variant: "destructive"
});
}
};
useEffect(() => {
loadTransactions();
loadCartoes();
}, []);
const handleTransactionSuccess = () => {
setIsDialogOpen(false);
setSelectedTransaction(null);
setIsEditing(false);
loadTransactions();
toast({
title: isEditing ? "Transação atualizada" : "Transação registrada",
description: isEditing
? "A transação foi atualizada com sucesso"
: "A nova transação foi adicionada com sucesso",
});
};
const handleDespesaCartaoSuccess = () => {
setIsCartaoCreditoDialogOpen(false);
loadCartoes(); // Recarregar lista de cartões
loadTransactions(); // Recarregar transações
toast({
title: "Despesa de cartão registrada",
description: "A despesa do cartão foi adicionada com sucesso",
});
};
const handleEditTransaction = (transaction: Transaction) => {
setSelectedTransaction(transaction);
setTipoForm(transaction.tipo as 'receita' | 'despesa');
setIsEditing(true);
setIsDialogOpen(true);
};
const handleCloseDialog = () => {
setIsDialogOpen(false);
setSelectedTransaction(null);
setIsEditing(false);
};
const handleCloseCartaoCreditoDialog = () => {
setIsCartaoCreditoDialogOpen(false);
};
// Separar transações em receitas e despesas
const receitas = transactions.filter(t => t.tipo === 'receita');
const despesas = transactions.filter(t => t.tipo === 'despesa');
// Calcular totais
const totalReceitas = receitas.reduce((sum, t) => sum + Math.abs(t.valor), 0);
const totalDespesas = despesas.reduce((sum, t) => sum + Math.abs(t.valor), 0);
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('pt-BR', {
style: 'currency',
currency: 'BRL',
}).format(value);
};
const handleOpenDialog = (tipo: 'receita' | 'despesa') => {
setTipoForm(tipo);
setIsEditing(false);
setSelectedTransaction(null);
setIsDialogOpen(true);
};
const handleOpenCartaoCreditoDialog = () => {
setIsCartaoCreditoDialogOpen(true);
};
const {
transactions,
isLoading,
isDialogOpen,
tipoForm,
selectedTransaction,
isEditing,
isCartaoCreditoDialogOpen,
cartoes,
totalReceitas,
totalDespesas,
formatCurrency,
handleTransactionSuccess,
handleDespesaCartaoSuccess,
handleEditTransaction,
handleCloseDialog,
handleCloseCartaoCreditoDialog,
handleOpenDialog,
handleOpenCartaoCreditoDialog,
loadTransactions
} = useTransactions();
return (
<Layout>
<div className="space-y-6">
<div className="flex justify-between items-center">
<h1 className="text-2xl font-bold tracking-tight">Todas as Transações</h1>
<div className="flex gap-2">
<Button
className="flex items-center gap-2 bg-finance-green hover:bg-finance-green/90"
onClick={() => handleOpenDialog('receita')}
>
<PlusCircle className="h-4 w-4" />
Nova Receita
</Button>
<Button
className="flex items-center gap-2 bg-finance-red hover:bg-finance-red/90"
onClick={() => handleOpenDialog('despesa')}
>
<PlusCircle className="h-4 w-4" />
Nova Despesa
</Button>
<Button
className="flex items-center gap-2"
onClick={handleOpenCartaoCreditoDialog}
>
<CreditCard className="h-4 w-4" />
Despesa Cartão
</Button>
</div>
</div>
<TransactionHeader
onOpenDialog={handleOpenDialog}
onOpenCartaoCreditoDialog={handleOpenCartaoCreditoDialog}
/>
{/* Resumo em Cards */}
<div className="grid gap-4 md:grid-cols-2">
<Card>
<CardHeader className="pb-2 bg-green-50">
<CardTitle className="text-finance-green flex items-center">
Ganhos do mês
</CardTitle>
</CardHeader>
<CardContent className="pt-4">
<p className="text-2xl font-bold text-finance-green">
{formatCurrency(totalReceitas)}
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="pb-2 bg-red-50">
<CardTitle className="text-finance-red flex items-center">
Gastos do mês
</CardTitle>
</CardHeader>
<CardContent className="pt-4">
<p className="text-2xl font-bold text-finance-red">
{formatCurrency(totalDespesas)}
</p>
</CardContent>
</Card>
</div>
<TransactionSummaryCards
totalReceitas={totalReceitas}
totalDespesas={totalDespesas}
formatCurrency={formatCurrency}
/>
{/* Tabela completa de transações */}
<div className="space-y-4">
@ -206,49 +57,19 @@ const TransacoesPage = () => {
/>
</div>
{/* Dialog para transações */}
<Dialog open={isDialogOpen} onOpenChange={handleCloseDialog}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle>
{isEditing
? `Editar ${tipoForm === 'receita' ? 'Receita' : 'Despesa'}`
: `Nova ${tipoForm === 'receita' ? 'Receita' : 'Despesa'}`
}
</DialogTitle>
<DialogDescription>
{isEditing
? 'Edite os campos para atualizar a transação.'
: `Preencha os campos para registrar uma nova ${tipoForm === 'receita' ? 'receita' : 'despesa'}.`
}
</DialogDescription>
</DialogHeader>
<TransactionForm
onSuccess={handleTransactionSuccess}
onCancel={handleCloseDialog}
defaultTipo={tipoForm}
transaction={selectedTransaction}
isEditing={isEditing}
/>
</DialogContent>
</Dialog>
{/* Dialog para despesas de cartão */}
<Dialog open={isCartaoCreditoDialogOpen} onOpenChange={handleCloseCartaoCreditoDialog}>
<DialogContent className="sm:max-w-[500px]">
<DialogHeader>
<DialogTitle>Nova Despesa de Cartão</DialogTitle>
<DialogDescription>
Selecione o cartão e preencha os detalhes da despesa.
</DialogDescription>
</DialogHeader>
<DespesaCartaoFormSelect
cartoes={cartoes}
onSuccess={handleDespesaCartaoSuccess}
onCancel={handleCloseCartaoCreditoDialog}
/>
</DialogContent>
</Dialog>
{/* Diálogos */}
<TransactionDialogs
isDialogOpen={isDialogOpen}
isCartaoCreditoDialogOpen={isCartaoCreditoDialogOpen}
tipoForm={tipoForm}
selectedTransaction={selectedTransaction}
isEditing={isEditing}
cartoes={cartoes}
onTransactionSuccess={handleTransactionSuccess}
onDespesaCartaoSuccess={handleDespesaCartaoSuccess}
onCloseDialog={handleCloseDialog}
onCloseCartaoCreditoDialog={handleCloseCartaoCreditoDialog}
/>
</div>
</Layout>
);