diff --git a/src/components/auth/ProtectedRoute.tsx b/src/components/auth/ProtectedRoute.tsx index 6af17a0..f1f6831 100644 --- a/src/components/auth/ProtectedRoute.tsx +++ b/src/components/auth/ProtectedRoute.tsx @@ -125,8 +125,8 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => { return ; } - // Se tem sessão mas perfil não está completo, redirecionar para completar perfil - if (session && !isProfileComplete) { + // Se tem sessão mas perfil não está completo E não está na página de completar perfil, redirecionar + if (session && !isProfileComplete && location.pathname !== '/complete-profile') { console.log('🚨 [PROTECTED_ROUTE] Redirecionando para complete-profile - perfil incompleto'); return ; } diff --git a/src/hooks/useDespesaCartaoForm.ts b/src/hooks/useDespesaCartaoForm.ts index ac0da26..b19c796 100644 --- a/src/hooks/useDespesaCartaoForm.ts +++ b/src/hooks/useDespesaCartaoForm.ts @@ -5,7 +5,8 @@ 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/despesas'; // Updated import path +import { criarDespesa } from '@/services/cartao/despesas'; +import { buscarCartaoPorReferencia, gerarCodigoCartao } from '@/services/cartao/cartaoCodigoUtils'; import { format } from 'date-fns'; const despesaCartaoSchema = z.object({ @@ -42,6 +43,7 @@ export function useDespesaCartaoForm({ cartoes, onSuccess, onCancel }: UseDespes const handleCartaoChange = (cartaoId: string) => { const cartao = cartoes.find(c => c.id === cartaoId); setSelectedCartao(cartao || null); + console.log('🔄 Cartão selecionado:', cartao?.nome); }; const formatCartaoLabel = (cartao: CartaoCredito) => { @@ -66,12 +68,26 @@ export function useDespesaCartaoForm({ cartoes, onSuccess, onCancel }: UseDespes 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; + // Usar busca inteligente para encontrar cartão por referência + let cartaoEncontrado = buscarCartaoPorReferencia(cartoes, selectedCartao.nome); - console.log('Enviando dados para criar despesa:', { - cartao_id: data.cartao_id, - cartao_nome: selectedCartao.nome, + if (!cartaoEncontrado) { + cartaoEncontrado = selectedCartao; + console.log('⚠️ Usando cartão selecionado diretamente:', selectedCartao.nome); + } else { + console.log('✅ Cartão encontrado via busca inteligente:', cartaoEncontrado.nome); + } + + // Garantir que temos um cartao_codigo, gerando se necessário + let cartaoCodigo = cartaoEncontrado.cartao_codigo; + if (!cartaoCodigo) { + cartaoCodigo = gerarCodigoCartao(cartaoEncontrado.nome, cartaoEncontrado.banco); + console.log('🔧 Código gerado para o cartão:', cartaoCodigo); + } + + console.log('📝 Enviando dados para criar despesa:', { + cartao_id: cartaoEncontrado.id, + cartao_nome: cartaoEncontrado.nome, cartao_codigo: cartaoCodigo, valor: data.valor, data_despesa: formattedDate, @@ -79,7 +95,7 @@ export function useDespesaCartaoForm({ cartoes, onSuccess, onCancel }: UseDespes }); const resultado = await criarDespesa( - data.cartao_id, + cartaoEncontrado.id, cartaoCodigo, data.valor, formattedDate, @@ -100,7 +116,7 @@ export function useDespesaCartaoForm({ cartoes, onSuccess, onCancel }: UseDespes }); } } catch (error) { - console.error('Erro ao processar formulário:', error); + console.error('❌ Erro ao processar formulário:', error); toast({ title: "Erro inesperado", description: "Ocorreu um erro ao processar sua solicitação", diff --git a/src/services/cartao/cartaoCodigoUtils.ts b/src/services/cartao/cartaoCodigoUtils.ts index 6c132fa..435c1d8 100644 --- a/src/services/cartao/cartaoCodigoUtils.ts +++ b/src/services/cartao/cartaoCodigoUtils.ts @@ -1,20 +1,113 @@ +import { CartaoCredito } from '@/types/cartaoTypes'; + /** - * Utility functions for generating and handling credit card codes + * Busca um cartão por código ou nome, usando correspondência flexível */ +export const buscarCartaoPorReferencia = (cartoes: CartaoCredito[], referencia: string): CartaoCredito | null => { + if (!referencia || cartoes.length === 0) { + return null; + } -/** - * Generates a unique code for a credit card - * @param nome Card name - * @param banco Bank name - * @returns Unique card code - */ -export function gerarCartaoCodigo(nome: string, banco: string): string { - const timestamp = new Date().getTime(); - const randomStr = Math.random().toString(36).substring(2, 7); - // Remove spaces and special characters - const nomeSanitizado = nome.replace(/[^a-zA-Z0-9]/g, '').substring(0, 8); - const bancoSanitizado = banco.replace(/[^a-zA-Z0-9]/g, '').substring(0, 5); + const referenciaLimpa = referencia.toLowerCase().trim(); - return `${nomeSanitizado}_${bancoSanitizado}_${randomStr}`; + // 1. Busca exata por nome + let cartaoEncontrado = cartoes.find(cartao => + cartao.nome.toLowerCase().trim() === referenciaLimpa + ); + + if (cartaoEncontrado) { + console.log('🎯 Cartão encontrado por nome exato:', cartaoEncontrado.nome); + return cartaoEncontrado; + } + + // 2. Busca por palavras-chave no nome (correspondência parcial) + const palavrasReferencia = referenciaLimpa.split(' ').filter(p => p.length > 1); + + if (palavrasReferencia.length > 0) { + cartaoEncontrado = cartoes.find(cartao => { + const nomeCartao = cartao.nome.toLowerCase(); + return palavrasReferencia.some(palavra => nomeCartao.includes(palavra)); + }); + + if (cartaoEncontrado) { + console.log('🎯 Cartão encontrado por palavra-chave:', cartaoEncontrado.nome); + return cartaoEncontrado; + } + } + + // 3. Busca por banco ou bandeira + cartaoEncontrado = cartoes.find(cartao => { + const banco = cartao.banco?.toLowerCase() || ''; + const bandeira = cartao.bandeira?.toLowerCase() || ''; + return banco.includes(referenciaLimpa) || bandeira.includes(referenciaLimpa); + }); + + if (cartaoEncontrado) { + console.log('🎯 Cartão encontrado por banco/bandeira:', cartaoEncontrado.nome); + return cartaoEncontrado; + } + + // 4. Busca difusa (proximidade de caracteres) + cartaoEncontrado = cartoes.find(cartao => { + const similarity = calcularSimilaridade(referenciaLimpa, cartao.nome.toLowerCase()); + return similarity > 0.6; // 60% de similaridade + }); + + if (cartaoEncontrado) { + console.log('🎯 Cartão encontrado por similaridade:', cartaoEncontrado.nome); + return cartaoEncontrado; + } + + console.log('❌ Nenhum cartão encontrado para referência:', referencia); + return null; +}; + +/** + * Calcula a similaridade entre duas strings usando Levenshtein Distance + */ +function calcularSimilaridade(str1: string, str2: string): number { + const len1 = str1.length; + const len2 = str2.length; + + if (len1 === 0) return len2 === 0 ? 1 : 0; + if (len2 === 0) return 0; + + const matrix = Array(len1 + 1).fill(null).map(() => Array(len2 + 1).fill(null)); + + for (let i = 0; i <= len1; i++) matrix[i][0] = i; + for (let j = 0; j <= len2; j++) matrix[0][j] = j; + + for (let i = 1; i <= len1; i++) { + for (let j = 1; j <= len2; j++) { + const cost = str1[i - 1] === str2[j - 1] ? 0 : 1; + matrix[i][j] = Math.min( + matrix[i - 1][j] + 1, + matrix[i][j - 1] + 1, + matrix[i - 1][j - 1] + cost + ); + } + } + + const maxLen = Math.max(len1, len2); + return 1 - matrix[len1][len2] / maxLen; } + +/** + * Gera um código de cartão baseado no nome + */ +export const gerarCodigoCartao = (nome: string, banco?: string): string => { + const nomeSimplificado = nome + .toLowerCase() + .replace(/[^a-z0-9\s]/g, '') + .replace(/\s+/g, '_') + .substring(0, 20); + + const bancoSimplificado = banco + ? banco.toLowerCase().replace(/[^a-z0-9]/g, '').substring(0, 10) + : ''; + + return bancoSimplificado + ? `${bancoSimplificado}_${nomeSimplificado}` + : nomeSimplificado; +};