diff --git a/src/components/avisos/ContasRecorrentesList.tsx b/src/components/avisos/ContasRecorrentesList.tsx index 95cc36a..c8446a9 100644 --- a/src/components/avisos/ContasRecorrentesList.tsx +++ b/src/components/avisos/ContasRecorrentesList.tsx @@ -1,20 +1,29 @@ -import React, { useState, useEffect } from 'react'; -import { supabase } from '@/integrations/supabase/client'; -import { useToast } from '@/hooks/use-toast'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import React, { useState } from 'react'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; +import { supabase } from '@/integrations/supabase/client'; +import { useAuthStore } from '@/stores/authStore'; +import { Edit, Trash2, Calendar, Clock, DollarSign, Bell } from 'lucide-react'; +import { toast } from 'sonner'; +import { format } from 'date-fns'; +import { ptBR } from 'date-fns/locale'; import FiltroMesAno from './FiltroMesAno'; import StatusTags from './StatusTags'; interface ContaRecorrente { id: string; - nome: string; - valor: number; + nome_conta: string; + descricao: string | null; + valor: number | null; dia_vencimento: number; - email_usuario: string; - ativa: boolean; + hora_aviso: string; + dias_antecedencia: number; + ativo: boolean; created_at: string; + email_usuario: string | null; } interface StatusPagamento { @@ -28,149 +37,277 @@ interface AvisoEnviado { } const ContasRecorrentesList = () => { - const [contas, setContas] = useState([]); - const [statusPagamentos, setStatusPagamentos] = useState<{[key: string]: StatusPagamento}>({}); - const [avisosEnviados, setAvisosEnviados] = useState<{[key: string]: AvisoEnviado}>({}); - const [loading, setLoading] = useState(true); - const [mesAno, setMesAno] = useState(() => { - const now = new Date(); - return { mes: now.getMonth() + 1, ano: now.getFullYear() }; + const { user, session } = useAuthStore(); + const queryClient = useQueryClient(); + const [deletingId, setDeletingId] = useState(null); + const [hoveredCard, setHoveredCard] = useState(null); + + // Estado para filtro de mês/ano - inicializa com mês/ano atual + const dataAtual = new Date(); + const [mesAno, setMesAno] = useState({ + mes: dataAtual.getMonth() + 1, + ano: dataAtual.getFullYear() }); - const { toast } = useToast(); - const formatCurrency = (value: number) => { + // Buscar email do usuário logado + const userEmail = session?.user?.email || user?.email; + + const { data: contas, isLoading } = useQuery({ + queryKey: ['contas-recorrentes', userEmail], + queryFn: async () => { + if (!userEmail) return []; + + const { data, error } = await supabase + .from('contas_recorrentes') + .select('*') + .eq('email_usuario', userEmail) + .order('created_at', { ascending: false }); + + if (error) throw error; + return data as ContaRecorrente[]; + }, + enabled: !!userEmail + }); + + // Buscar status de pagamento para todas as contas do período selecionado + const { data: statusPagamentos } = useQuery({ + queryKey: ['status-pagamentos', mesAno.mes, mesAno.ano, contas?.map(c => c.id)], + queryFn: async () => { + if (!contas || contas.length === 0) return {}; + + const { data, error } = await supabase + .from('status_pagamento_mensal') + .select('conta_id, status, valor_pago, data_pagamento') + .in('conta_id', contas.map(c => c.id)) + .eq('mes', mesAno.mes) + .eq('ano', mesAno.ano); + + if (error) throw error; + + // Converter array em objeto indexado por conta_id + const statusMap: Record = {}; + data?.forEach(status => { + statusMap[status.conta_id] = status; + }); + + return statusMap; + }, + enabled: !!contas && contas.length > 0 + }); + + // Buscar avisos enviados para todas as contas do período selecionado + const { data: avisosEnviados } = useQuery({ + queryKey: ['avisos-enviados', mesAno.mes, mesAno.ano, contas?.map(c => c.id)], + queryFn: async () => { + if (!contas || contas.length === 0) return {}; + + // Criar range de datas para o mês/ano selecionado + const inicioMes = new Date(mesAno.ano, mesAno.mes - 1, 1); + const fimMes = new Date(mesAno.ano, mesAno.mes, 0); + + const { data, error } = await supabase + .from('avisos_enviados') + .select('conta_id, data_aviso') + .in('conta_id', contas.map(c => c.id)) + .gte('data_aviso', inicioMes.toISOString().split('T')[0]) + .lte('data_aviso', fimMes.toISOString().split('T')[0]); + + if (error) throw error; + + // Converter array em objeto indexado por conta_id (pega o primeiro aviso do mês) + const avisosMap: Record = {}; + data?.forEach(aviso => { + if (!avisosMap[aviso.conta_id]) { + avisosMap[aviso.conta_id] = aviso; + } + }); + + return avisosMap; + }, + enabled: !!contas && contas.length > 0 + }); + + const handleDelete = async (id: string) => { + setDeletingId(id); + + try { + const { error } = await supabase + .from('contas_recorrentes') + .delete() + .eq('id', id); + + if (error) throw error; + + toast.success('Conta removida com sucesso!'); + queryClient.invalidateQueries({ queryKey: ['contas-recorrentes'] }); + } catch (error) { + console.error('Erro ao remover conta:', error); + toast.error('Erro ao remover conta. Tente novamente.'); + } finally { + setDeletingId(null); + } + }; + + const toggleAtivo = async (id: string, ativo: boolean) => { + try { + const { error } = await supabase + .from('contas_recorrentes') + .update({ ativo: !ativo }) + .eq('id', id); + + if (error) throw error; + + toast.success(ativo ? 'Conta desativada' : 'Conta ativada'); + queryClient.invalidateQueries({ queryKey: ['contas-recorrentes'] }); + } catch (error) { + console.error('Erro ao alterar status:', error); + toast.error('Erro ao alterar status da conta.'); + } + }; + + const formatCurrency = (value: number | null) => { + if (value === null) return 'Não informado'; return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(value); }; - const loadContas = async () => { - try { - setLoading(true); - - // Buscar dados do usuário logado - const { data: { user } } = await supabase.auth.getUser(); - if (!user?.email) { - console.error('Usuário não logado'); - return; - } - - console.log('Buscando contas para o usuário:', user.email); - - // Buscar contas recorrentes do usuário - const { data: contasData, error: contasError } = await supabase - .from('contas_recorrentes') - .select('*') - .eq('email_usuario', user.email) - .eq('ativa', true) - .order('nome'); - - if (contasError) { - console.error('Erro ao buscar contas:', contasError); - throw contasError; - } - - console.log('Contas encontradas:', contasData); - setContas(contasData || []); - - // Buscar status de pagamento para cada conta no mês/ano selecionado - const statusMap: {[key: string]: StatusPagamento} = {}; - const avisosMap: {[key: string]: AvisoEnviado} = {}; - - for (const conta of contasData || []) { - // Buscar status de pagamento - const { data: statusData } = await supabase - .from('status_pagamento_mensal') - .select('status, valor_pago, data_pagamento') - .eq('conta_id', conta.id) - .eq('mes', mesAno.mes) - .eq('ano', mesAno.ano) - .single(); - - if (statusData) { - statusMap[conta.id] = statusData; - } - - // Buscar aviso enviado - const { data: avisoData } = await supabase - .from('avisos_enviados') - .select('data_aviso') - .eq('conta_id', conta.id) - .gte('data_aviso', `${mesAno.ano}-${String(mesAno.mes).padStart(2, '0')}-01`) - .lt('data_aviso', `${mesAno.ano}-${String(mesAno.mes + 1).padStart(2, '0')}-01`) - .single(); - - if (avisoData) { - avisosMap[conta.id] = avisoData; - } - } - - setStatusPagamentos(statusMap); - setAvisosEnviados(avisosMap); - - } catch (error) { - console.error('Erro ao carregar contas:', error); - toast({ - title: "Erro ao carregar contas", - description: "Não foi possível carregar as contas recorrentes", - variant: "destructive" - }); - } finally { - setLoading(false); - } - }; - - useEffect(() => { - loadContas(); - }, [mesAno]); - - if (loading) { + if (isLoading) { return ( -
-
Carregando contas...
-
+ + +
Carregando contas...
+
+
+ ); + } + + if (!userEmail) { + return ( + + + Suas Contas Recorrentes + Você precisa estar logado para ver suas contas + + ); } return (
+ {/* Filtro de Mês/Ano */} - - {contas.length === 0 ? ( + + {/* Lista de Contas */} + {!contas || contas.length === 0 ? ( - -

- Nenhuma conta recorrente encontrada para o período selecionado. -

+ + Suas Contas Recorrentes + Você ainda não cadastrou nenhuma conta recorrente + + +
+ +

Clique em "Nova Conta" para começar

+
) : ( -
- {contas.map((conta) => ( - - - - {conta.nome} - - Dia {conta.dia_vencimento} - - - - -
-
- {formatCurrency(conta.valor)} -
+
+

+ Suas Contas Recorrentes - {mesAno.mes.toString().padStart(2, '0')}/{mesAno.ano} +

+ +
+ {contas.map((conta) => ( +
setHoveredCard(conta.id)} + onMouseLeave={() => setHoveredCard(null)} + > + {hoveredCard === conta.id && ( +
+ )} + + + +
+
+ {conta.nome_conta} + {conta.descricao && ( + {conta.descricao} + )} +
+ + {conta.ativo ? 'Ativo' : 'Inativo'} + +
+
- -
- - - ))} + + {/* Tags de Status */} + + +
+
+ + Valor: + {formatCurrency(conta.valor)} +
+ +
+ + Vencimento: + Todo dia {conta.dia_vencimento} +
+ +
+ + Horário: + {conta.hora_aviso} +
+ +
+ + Antecedência: + {conta.dias_antecedencia} dia(s) +
+
+ +
+ + + +
+
+ +
+ ))} +
)}
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index a9912b7..c0342e1 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -1,3 +1,4 @@ + import { Menu, User, LogOut } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { useIsMobile } from '@/hooks/use-mobile'; @@ -64,10 +65,10 @@ export default function Header({ onMenuToggle }: HeaderProps) { )} diff --git a/src/components/ui/modern-sidebar.tsx b/src/components/ui/modern-sidebar.tsx index 438af8c..aaa494e 100644 --- a/src/components/ui/modern-sidebar.tsx +++ b/src/components/ui/modern-sidebar.tsx @@ -132,7 +132,7 @@ export const MobileSidebar = ({
setOpen(!open)} />
@@ -152,7 +152,7 @@ export const MobileSidebar = ({ )} >
setOpen(!open)} >