diff --git a/src/components/filters/MonthFilter.tsx b/src/components/filters/MonthFilter.tsx new file mode 100644 index 0000000..5e29b06 --- /dev/null +++ b/src/components/filters/MonthFilter.tsx @@ -0,0 +1,63 @@ + +import React from 'react'; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Calendar } from 'lucide-react'; + +interface MonthFilterProps { + selectedMonth: string; + onMonthChange: (month: string) => void; + className?: string; +} + +export const MonthFilter = ({ selectedMonth, onMonthChange, className }: MonthFilterProps) => { + const months = [ + { value: '2024-01', label: 'Janeiro 2024' }, + { value: '2024-02', label: 'Fevereiro 2024' }, + { value: '2024-03', label: 'Março 2024' }, + { value: '2024-04', label: 'Abril 2024' }, + { value: '2024-05', label: 'Maio 2024' }, + { value: '2024-06', label: 'Junho 2024' }, + { value: '2024-07', label: 'Julho 2024' }, + { value: '2024-08', label: 'Agosto 2024' }, + { value: '2024-09', label: 'Setembro 2024' }, + { value: '2024-10', label: 'Outubro 2024' }, + { value: '2024-11', label: 'Novembro 2024' }, + { value: '2024-12', label: 'Dezembro 2024' }, + { value: '2025-01', label: 'Janeiro 2025' }, + { value: '2025-02', label: 'Fevereiro 2025' }, + { value: '2025-03', label: 'Março 2025' }, + { value: '2025-04', label: 'Abril 2025' }, + { value: '2025-05', label: 'Maio 2025' }, + { value: '2025-06', label: 'Junho 2025' }, + { value: '2025-07', label: 'Julho 2025' }, + { value: '2025-08', label: 'Agosto 2025' }, + { value: '2025-09', label: 'Setembro 2025' }, + { value: '2025-10', label: 'Outubro 2025' }, + { value: '2025-11', label: 'Novembro 2025' }, + { value: '2025-12', label: 'Dezembro 2025' }, + ]; + + // Função para obter o mês atual no formato YYYY-MM + const getCurrentMonth = () => { + const now = new Date(); + return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; + }; + + return ( +
+ + +
+ ); +}; diff --git a/src/hooks/useTransactions.ts b/src/hooks/useTransactions.ts index 9068a7e..f65951c 100644 --- a/src/hooks/useTransactions.ts +++ b/src/hooks/useTransactions.ts @@ -1,4 +1,3 @@ - import { useState, useEffect } from 'react'; import { Transaction } from '@/types/financialTypes'; import { CartaoCredito } from '@/types/cartaoTypes'; @@ -6,7 +5,11 @@ import { useToast } from '@/hooks/use-toast'; import { getTransacoes } from '@/services/transacao'; import { getCartoes } from '@/services/cartaoCreditoService'; -export const useTransactions = () => { +interface UseTransactionsProps { + monthFilter?: string; +} + +export const useTransactions = ({ monthFilter }: UseTransactionsProps = {}) => { const [transactions, setTransactions] = useState([]); const [isLoading, setIsLoading] = useState(true); const [isDialogOpen, setIsDialogOpen] = useState(false); @@ -20,8 +23,8 @@ export const useTransactions = () => { const loadTransactions = async () => { try { setIsLoading(true); - console.log("Carregando todas as transações..."); - const data = await getTransacoes(); + console.log("Carregando transações com filtro:", monthFilter); + const data = await getTransacoes(monthFilter); console.log(`${data.length} transações carregadas com sucesso`); setTransactions(data); } catch (error) { @@ -53,7 +56,7 @@ export const useTransactions = () => { useEffect(() => { loadTransactions(); loadCartoes(); - }, []); + }, [monthFilter]); const handleTransactionSuccess = () => { setIsDialogOpen(false); diff --git a/src/integrations/supabase/types.ts b/src/integrations/supabase/types.ts index 12e3391..8638603 100644 --- a/src/integrations/supabase/types.ts +++ b/src/integrations/supabase/types.ts @@ -144,6 +144,39 @@ export type Database = { } Relationships: [] } + conversas_zap: { + Row: { + created_at: string + date_time: string | null + id: number + id_transacao_gerada: number | null + login: string | null + msg_recebida: string | null + remote_jid: string | null + status_processamento: string | null + } + Insert: { + created_at?: string + date_time?: string | null + id?: number + id_transacao_gerada?: number | null + login?: string | null + msg_recebida?: string | null + remote_jid?: string | null + status_processamento?: string | null + } + Update: { + created_at?: string + date_time?: string | null + id?: number + id_transacao_gerada?: number | null + login?: string | null + msg_recebida?: string | null + remote_jid?: string | null + status_processamento?: string | null + } + Relationships: [] + } despesas_cartao: { Row: { cartao_id: string | null @@ -294,10 +327,11 @@ export type Database = { empresa: string | null id: string instancia_zap: string | null - nome: string + nome: string | null remote_jid: string | null - senha: string + senha: string | null status_instancia: string | null + webhook: string | null whatsapp: string } Insert: { @@ -306,10 +340,11 @@ export type Database = { empresa?: string | null id?: string instancia_zap?: string | null - nome: string + nome?: string | null remote_jid?: string | null - senha: string + senha?: string | null status_instancia?: string | null + webhook?: string | null whatsapp: string } Update: { @@ -318,10 +353,11 @@ export type Database = { empresa?: string | null id?: string instancia_zap?: string | null - nome?: string + nome?: string | null remote_jid?: string | null - senha?: string + senha?: string | null status_instancia?: string | null + webhook?: string | null whatsapp?: string } Relationships: [] diff --git a/src/pages/Index.tsx b/src/pages/Index.tsx index e848e97..cdc8a34 100644 --- a/src/pages/Index.tsx +++ b/src/pages/Index.tsx @@ -5,6 +5,7 @@ import SummaryCard from '@/components/dashboard/SummaryCard'; import TransactionsTable from '@/components/dashboard/TransactionsTable'; import CategoryChart from '@/components/dashboard/CategoryChart'; import MonthlyChart from '@/components/dashboard/MonthlyChart'; +import { MonthFilter } from '@/components/filters/MonthFilter'; import { Wallet, ArrowUp, ArrowDown, PiggyBank, CreditCard } from 'lucide-react'; import { Transaction, CategorySummary, MonthlyData } from '@/types/financialTypes'; import { useToast } from "@/components/ui/use-toast"; @@ -24,6 +25,14 @@ const Dashboard = () => { const [totals, setTotals] = useState({ receitas: 0, despesas: 0, saldo: 0, cartoes: 0 }); const { toast } = useToast(); + // Função para obter o mês atual no formato YYYY-MM + const getCurrentMonth = () => { + const now = new Date(); + return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; + }; + + const [selectedMonth, setSelectedMonth] = useState(getCurrentMonth()); + // Use useCallback to ensure this function doesn't change on every render const setupUserId = useCallback(() => { const storedUserId = localStorage.getItem('userId'); @@ -42,19 +51,19 @@ const Dashboard = () => { setupUserId(); }, [setupUserId]); - // Load data only once when component mounts + // Load data when component mounts or month changes useEffect(() => { async function loadData() { try { setIsLoading(true); - console.log("Iniciando carregamento de dados..."); + console.log("Iniciando carregamento de dados para o mês:", selectedMonth); - // Buscar todos os dados necessários + // Buscar todos os dados necessários com filtro de mês const [transacoesData, totalsData, categoriesData, monthlyDataResult, cartoesData] = await Promise.all([ - getTransacoes(), - getTransactionSummary(), - getCategorySummary(), - getMonthlyData(), + getTransacoes(selectedMonth), + getTransactionSummary(selectedMonth), + getCategorySummary('despesa', selectedMonth), + getMonthlyData(), // Monthly data não precisa de filtro pois já mostra todos os meses getCartoes() ]); @@ -67,7 +76,8 @@ const Dashboard = () => { categories: categoriesData.length, monthlyData: monthlyDataResult.length, cartoes: cartoesData.length, - totalCartoes: totalCartoes + totalCartoes: totalCartoes, + mesEscolhido: selectedMonth }); setTransactions(transacoesData); @@ -82,7 +92,7 @@ const Dashboard = () => { toast({ title: "Dados carregados com sucesso", - description: "Seus dados financeiros foram atualizados" + description: `Dados financeiros de ${selectedMonth} foram atualizados` }); } catch (error) { console.error("Erro ao carregar dados:", error); @@ -97,9 +107,8 @@ const Dashboard = () => { } loadData(); - // Ensure toast doesn't cause re-renders by removing it from dependencies // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [selectedMonth]); const formatCurrency = (value: number) => { return new Intl.NumberFormat('pt-BR', { @@ -111,14 +120,30 @@ const Dashboard = () => { // Calculate total of all expenses (regular + credit cards) const totalDespesasGeral = totals.despesas + totals.cartoes; + // Função para formatar o mês para exibição + const formatMonthDisplay = (month: string) => { + const [year, monthNum] = month.split('-'); + const months = [ + 'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', + 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' + ]; + return `${months[parseInt(monthNum) - 1]} ${year}`; + }; + return (
-

Dashboard Financeiro

-

- Última atualização: {new Date().toLocaleDateString('pt-BR')} -

+
+

Dashboard Financeiro

+

+ Dados de: {formatMonthDisplay(selectedMonth)} +

+
+
diff --git a/src/pages/Transacoes.tsx b/src/pages/Transacoes.tsx index dc6f49e..ff95294 100644 --- a/src/pages/Transacoes.tsx +++ b/src/pages/Transacoes.tsx @@ -1,13 +1,22 @@ -import React from 'react'; +import React, { useState } from 'react'; import Layout from '@/components/layout/Layout'; import TransactionsTable from '@/components/dashboard/TransactionsTable'; import { useTransactions } from '@/hooks/useTransactions'; import { TransactionHeader } from '@/components/transacoes/TransactionHeader'; import { TransactionSummaryCards } from '@/components/transacoes/TransactionSummaryCards'; import { TransactionDialogs } from '@/components/transacoes/TransactionDialogs'; +import { MonthFilter } from '@/components/filters/MonthFilter'; const TransacoesPage = () => { + // Função para obter o mês atual no formato YYYY-MM + const getCurrentMonth = () => { + const now = new Date(); + return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; + }; + + const [selectedMonth, setSelectedMonth] = useState(getCurrentMonth()); + const { transactions, isLoading, @@ -29,11 +38,34 @@ const TransacoesPage = () => { handleOpenDialog, handleOpenCartaoCreditoDialog, loadTransactions - } = useTransactions(); + } = useTransactions({ monthFilter: selectedMonth }); + + // Função para formatar o mês para exibição + const formatMonthDisplay = (month: string) => { + const [year, monthNum] = month.split('-'); + const months = [ + 'Janeiro', 'Fevereiro', 'Março', 'Abril', 'Maio', 'Junho', + 'Julho', 'Agosto', 'Setembro', 'Outubro', 'Novembro', 'Dezembro' + ]; + return `${months[parseInt(monthNum) - 1]} ${year}`; + }; return (
+
+
+

Transações

+

+ Dados de: {formatMonthDisplay(selectedMonth)} +

+
+ +
+ 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`); + // Apply month filter if provided + if (monthFilter) { + const startDate = `${monthFilter}-01`; + const year = parseInt(monthFilter.split('-')[0]); + const month = parseInt(monthFilter.split('-')[1]); + const endDate = new Date(year, month, 0).toISOString().split('T')[0]; // Last day of the month + + query = query + .gte('quando', startDate) + .lte('quando', `${endDate}T23:59:59.999Z`); + } + + const { data, error } = await query; + if (error) { console.error('Erro ao buscar resumo das transações:', error); throw new Error('Não foi possível carregar o resumo das transações'); @@ -56,12 +71,13 @@ export async function getTransactionSummary() { } /** - * Get category summary for transactions + * Get category summary for transactions with optional month filter * @param tipoFiltro Filter by transaction type ('receita', 'despesa', or 'all') + * @param monthFilter - Optional month filter in format 'YYYY-MM' * @returns Promise with category summary data */ -export async function getCategorySummary(tipoFiltro: string = 'despesa'): Promise { - console.log(`Buscando resumo de categorias para tipo: ${tipoFiltro}`); +export async function getCategorySummary(tipoFiltro: string = 'despesa', monthFilter?: string): Promise { + console.log(`Buscando resumo de categorias para tipo: ${tipoFiltro}`, monthFilter ? `Filtro do mês: ${monthFilter}` : "Sem filtro de mês"); const normalizedEmail = getUserEmail(); @@ -73,12 +89,26 @@ export async function getCategorySummary(tipoFiltro: string = 'despesa'): Promis // Get user groups by email const groupIds = await getUserGroups(normalizedEmail); - // Fetch category summary with enhanced filter based on email (login) or group_id - const { data, error } = await supabase + // Build the query with month filter if provided + let query = supabase .from('transacoes') .select('categoria, valor, tipo') .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`); + // Apply month filter if provided + if (monthFilter) { + const startDate = `${monthFilter}-01`; + const year = parseInt(monthFilter.split('-')[0]); + const month = parseInt(monthFilter.split('-')[1]); + const endDate = new Date(year, month, 0).toISOString().split('T')[0]; // Last day of the month + + query = query + .gte('quando', startDate) + .lte('quando', `${endDate}T23:59:59.999Z`); + } + + const { data, error } = await query; + if (error) { console.error('Erro ao buscar resumo de categorias:', error); throw new Error('Não foi possível carregar o resumo por categoria'); diff --git a/src/services/transacao/transacaoFetchService.ts b/src/services/transacao/transacaoFetchService.ts index af088d0..a1c3de1 100644 --- a/src/services/transacao/transacaoFetchService.ts +++ b/src/services/transacao/transacaoFetchService.ts @@ -4,11 +4,12 @@ import { Transaction } from "@/types/financialTypes"; import { getUserEmail, getUserGroups } from "./baseService"; /** - * Fetch all transactions for the current user + * Fetch all transactions for the current user with optional month filter + * @param monthFilter - Optional month filter in format 'YYYY-MM' * @returns Promise with array of transactions */ -export async function getTransacoes(): Promise { - console.log("Buscando transações do Supabase..."); +export async function getTransacoes(monthFilter?: string): Promise { + console.log("Buscando transações do Supabase...", monthFilter ? `Filtro do mês: ${monthFilter}` : "Sem filtro de mês"); // Get the user's email from localStorage const normalizedEmail = getUserEmail(); @@ -24,13 +25,29 @@ export async function getTransacoes(): Promise { const groupIds = await getUserGroups(normalizedEmail); console.log(`Encontrados ${groupIds.length} grupos vinculados ao usuário:`, groupIds); - // Fetch transactions with filter based on email (login) or group_id - const { data, error } = await supabase + // Build the query with month filter if provided + let query = supabase .from('transacoes') .select('*') .or(`login.eq.${normalizedEmail},${groupIds.length > 0 ? `grupo_id.in.(${groupIds.map(id => `"${id}"`).join(',')})` : ''}`) .order('quando', { ascending: false }); + // Apply month filter if provided + if (monthFilter) { + const startDate = `${monthFilter}-01`; + const year = parseInt(monthFilter.split('-')[0]); + const month = parseInt(monthFilter.split('-')[1]); + const endDate = new Date(year, month, 0).toISOString().split('T')[0]; // Last day of the month + + query = query + .gte('quando', startDate) + .lte('quando', `${endDate}T23:59:59.999Z`); + + console.log(`Aplicando filtro de mês: ${startDate} até ${endDate}`); + } + + const { data, error } = await query; + if (error) { console.error('Erro ao buscar transações:', error); throw new Error('Não foi possível carregar as transações');