Fix: Remove WhatsApp instance dependency.
Removed WhatsApp instance connection requirement for group creation. Removed "Reconectar WhatsApp" button. Fixed Google sign-in redirect issue to complete profile and then dashboard. Added password recovery option.
This commit is contained in:
parent
05a8186a19
commit
3a1bc74f34
@ -1,4 +1,3 @@
|
||||
|
||||
import { Toaster } from "@/components/ui/sonner";
|
||||
import { TooltipProvider } from "@/components/ui/tooltip";
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
@ -8,6 +7,7 @@ import { supabase } from "@/integrations/supabase/client";
|
||||
import { useAuthStore } from "@/stores/authStore";
|
||||
import ProtectedRoute from "@/components/auth/ProtectedRoute";
|
||||
import Layout from "@/components/layout/Layout";
|
||||
import ResetPassword from '@/pages/ResetPassword';
|
||||
|
||||
// Pages
|
||||
import Index from "./pages/Index";
|
||||
@ -61,6 +61,7 @@ function App() {
|
||||
<Route path="/" element={<Landing />} />
|
||||
<Route path="/auth" element={<Auth />} />
|
||||
<Route path="/email-confirmation" element={<EmailConfirmation />} />
|
||||
<Route path="/reset-password" element={<ResetPassword />} />
|
||||
|
||||
{/* Rotas protegidas */}
|
||||
<Route path="/" element={<ProtectedRoute><Layout><Outlet /></Layout></ProtectedRoute>}>
|
||||
|
||||
121
src/components/auth/ForgotPasswordForm.tsx
Normal file
121
src/components/auth/ForgotPasswordForm.tsx
Normal file
@ -0,0 +1,121 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { ArrowLeft, Mail, CheckCircle2 } from "lucide-react";
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
|
||||
interface ForgotPasswordFormProps {
|
||||
onBackToLogin: () => void;
|
||||
}
|
||||
|
||||
const ForgotPasswordForm = ({ onBackToLogin }: ForgotPasswordFormProps) => {
|
||||
const [email, setEmail] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [emailSent, setEmailSent] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
setError('');
|
||||
|
||||
try {
|
||||
const { error } = await supabase.auth.resetPasswordForEmail(email, {
|
||||
redirectTo: `${window.location.origin}/reset-password`,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
setError(error.message);
|
||||
} else {
|
||||
setEmailSent(true);
|
||||
}
|
||||
} catch (error) {
|
||||
setError('Erro inesperado. Tente novamente.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (emailSent) {
|
||||
return (
|
||||
<Card className="w-full max-w-md">
|
||||
<CardHeader className="space-y-1 text-center">
|
||||
<div className="flex justify-center mb-4">
|
||||
<CheckCircle2 className="h-12 w-12 text-green-500" />
|
||||
</div>
|
||||
<CardTitle className="text-2xl">Email enviado!</CardTitle>
|
||||
<CardDescription>
|
||||
Enviamos um link para redefinir sua senha para <strong>{email}</strong>
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="bg-blue-50 p-4 rounded-lg">
|
||||
<p className="text-sm text-blue-800">
|
||||
Verifique sua caixa de entrada e spam. O link expira em 1 hora.
|
||||
</p>
|
||||
</div>
|
||||
<Button onClick={onBackToLogin} variant="outline" className="w-full">
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Voltar ao login
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="w-full max-w-md">
|
||||
<CardHeader className="space-y-1 text-center">
|
||||
<div className="flex justify-center mb-4">
|
||||
<Mail className="h-12 w-12 text-blue-500" />
|
||||
</div>
|
||||
<CardTitle className="text-2xl">Esqueceu sua senha?</CardTitle>
|
||||
<CardDescription>
|
||||
Digite seu email e enviaremos um link para redefinir sua senha
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="seu@email.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||||
{isLoading ? "Enviando..." : "Enviar link de recuperação"}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
onClick={onBackToLogin}
|
||||
className="w-full"
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Voltar ao login
|
||||
</Button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPasswordForm;
|
||||
@ -1,73 +1,117 @@
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { supabase } from "@/integrations/supabase/client";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface LoginFormProps {
|
||||
isLoading: boolean;
|
||||
setIsLoading: (isLoading: boolean) => void;
|
||||
setIsLoading: (loading: boolean) => void;
|
||||
onForgotPassword: () => void;
|
||||
}
|
||||
|
||||
const LoginForm = ({ isLoading, setIsLoading }: LoginFormProps) => {
|
||||
const LoginForm = ({ isLoading, setIsLoading, onForgotPassword }: LoginFormProps) => {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [error, setError] = useState('');
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [loginEmail, setLoginEmail] = useState('');
|
||||
const [loginSenha, setLoginSenha] = useState('');
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
setError('');
|
||||
|
||||
const { error } = await supabase.auth.signInWithPassword({
|
||||
email: loginEmail,
|
||||
password: loginSenha,
|
||||
});
|
||||
|
||||
setIsLoading(false);
|
||||
|
||||
if (error) {
|
||||
toast.error("Erro no login", {
|
||||
description: "Email ou senha incorretos. Verifique seus dados."
|
||||
try {
|
||||
const { data, error } = await supabase.auth.signInWithPassword({
|
||||
email: email.trim().toLowerCase(),
|
||||
password: password,
|
||||
});
|
||||
} else {
|
||||
toast.success("Login realizado com sucesso", {
|
||||
description: "Bem-vindo de volta!"
|
||||
});
|
||||
// Redirecionar para o dashboard após login bem-sucedido
|
||||
navigate('/dashboard');
|
||||
|
||||
if (error) {
|
||||
console.error('Erro no login:', error);
|
||||
|
||||
if (error.message.includes('Invalid login credentials')) {
|
||||
setError('Email ou senha incorretos. Verifique suas credenciais.');
|
||||
} else if (error.message.includes('Email not confirmed')) {
|
||||
setError('Email não confirmado. Verifique sua caixa de entrada.');
|
||||
} else {
|
||||
setError(error.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.user) {
|
||||
console.log('✅ Login realizado com sucesso:', data.user.email);
|
||||
|
||||
toast.success("Login realizado com sucesso!", {
|
||||
description: `Bem-vindo, ${data.user.email}!`,
|
||||
});
|
||||
|
||||
// Redirecionar para dashboard
|
||||
navigate('/');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Erro inesperado no login:', error);
|
||||
setError('Erro inesperado. Tente novamente.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleLogin} className="space-y-4">
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="login-email"
|
||||
placeholder="Email"
|
||||
id="email"
|
||||
type="email"
|
||||
value={loginEmail}
|
||||
onChange={(e) => setLoginEmail(e.target.value)}
|
||||
disabled={isLoading}
|
||||
placeholder="seu@email.com"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
required
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Senha</Label>
|
||||
<Input
|
||||
id="login-senha"
|
||||
placeholder="Senha"
|
||||
id="password"
|
||||
type="password"
|
||||
value={loginSenha}
|
||||
onChange={(e) => setLoginSenha(e.target.value)}
|
||||
disabled={isLoading}
|
||||
placeholder="Sua senha"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert variant="destructive">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||||
{isLoading ? "Entrando..." : "Entrar"}
|
||||
</Button>
|
||||
|
||||
<div className="text-center">
|
||||
<Button
|
||||
type="button"
|
||||
variant="link"
|
||||
onClick={onForgotPassword}
|
||||
className="text-sm text-muted-foreground hover:text-primary"
|
||||
>
|
||||
Esqueceu sua senha?
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
@ -18,65 +18,96 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
|
||||
|
||||
const setLoggedIn = useAuthStore((state) => state.setLoggedIn);
|
||||
const setUser = useAuthStore((state) => state.setUser);
|
||||
const setSession = useAuthStore((state) => state.setSession);
|
||||
const isProfileComplete = useAuthStore((state) => state.isProfileComplete);
|
||||
|
||||
// Hook para verificar completude do perfil
|
||||
const { isChecking } = useProfileCompletion(session?.user?.email || '');
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
// Verificar sessão inicial
|
||||
supabase.auth.getSession().then(({ data: { session } }) => {
|
||||
console.log('🔐 [PROTECTED_ROUTE] Sessão inicial:', session?.user?.email);
|
||||
setSession(session);
|
||||
setLoggedIn(!!session);
|
||||
setUser(session?.user ? { id: session.user.id } : null);
|
||||
|
||||
if (session?.user?.email) {
|
||||
localStorage.setItem('userEmail', session.user.email);
|
||||
console.log('👤 [PROTECTED_ROUTE] Email salvo no localStorage:', session.user.email);
|
||||
const getInitialSession = async () => {
|
||||
try {
|
||||
const { data: { session }, error } = await supabase.auth.getSession();
|
||||
|
||||
// Disparar evento customizado para notificar outros componentes sobre o login
|
||||
window.dispatchEvent(new CustomEvent('userLoggedIn', {
|
||||
detail: { email: session.user.email }
|
||||
}));
|
||||
} else {
|
||||
localStorage.removeItem('userEmail');
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
});
|
||||
|
||||
// Listener para mudanças de auth
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange(
|
||||
(_event, session) => {
|
||||
console.log('🔄 [PROTECTED_ROUTE] Mudança de auth:', _event, session?.user?.email);
|
||||
setSession(session);
|
||||
setLoggedIn(!!session);
|
||||
setUser(session?.user ? { id: session.user.id } : null);
|
||||
|
||||
if (session?.user?.email) {
|
||||
localStorage.setItem('userEmail', session.user.email);
|
||||
console.log('👤 [PROTECTED_ROUTE] Email atualizado no localStorage:', session.user.email);
|
||||
if (error) {
|
||||
console.error('🔐 [PROTECTED_ROUTE] Erro ao obter sessão:', error);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
console.log('🔐 [PROTECTED_ROUTE] Sessão inicial:', session?.user?.email || 'Nenhuma sessão');
|
||||
setSession(session);
|
||||
setLoggedIn(!!session);
|
||||
setUser(session?.user ? { id: session.user.id } : null);
|
||||
|
||||
// Disparar evento customizado para notificar sobre o login
|
||||
if (_event === 'SIGNED_IN') {
|
||||
console.log('🎉 [PROTECTED_ROUTE] Login detectado, disparando evento');
|
||||
if (session?.user?.email) {
|
||||
localStorage.setItem('userEmail', session.user.email);
|
||||
console.log('👤 [PROTECTED_ROUTE] Email salvo no localStorage:', session.user.email);
|
||||
|
||||
// Disparar evento customizado para notificar outros componentes sobre o login
|
||||
window.dispatchEvent(new CustomEvent('userLoggedIn', {
|
||||
detail: { email: session.user.email }
|
||||
}));
|
||||
} else {
|
||||
localStorage.removeItem('userEmail');
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem('userEmail');
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('🔐 [PROTECTED_ROUTE] Erro inesperado ao obter sessão:', error);
|
||||
if (mounted) {
|
||||
setIsLoading(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setIsLoading(false);
|
||||
getInitialSession();
|
||||
|
||||
// Listener para mudanças de auth com debounce
|
||||
let timeoutId: NodeJS.Timeout;
|
||||
|
||||
const { data: { subscription } } = supabase.auth.onAuthStateChange(
|
||||
(event, session) => {
|
||||
console.log('🔄 [PROTECTED_ROUTE] Mudança de auth:', event, session?.user?.email || 'Sem sessão');
|
||||
|
||||
// Debounce para evitar múltiplas atualizações
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(() => {
|
||||
if (mounted) {
|
||||
setSession(session);
|
||||
setLoggedIn(!!session);
|
||||
setUser(session?.user ? { id: session.user.id } : null);
|
||||
|
||||
if (session?.user?.email) {
|
||||
localStorage.setItem('userEmail', session.user.email);
|
||||
console.log('👤 [PROTECTED_ROUTE] Email atualizado no localStorage:', session.user.email);
|
||||
|
||||
// Disparar evento customizado para notificar sobre o login
|
||||
if (event === 'SIGNED_IN') {
|
||||
console.log('🎉 [PROTECTED_ROUTE] Login detectado, disparando evento');
|
||||
window.dispatchEvent(new CustomEvent('userLoggedIn', {
|
||||
detail: { email: session.user.email }
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
localStorage.removeItem('userEmail');
|
||||
}
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
}, 100); // Debounce de 100ms
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
clearTimeout(timeoutId);
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [setLoggedIn, setUser]);
|
||||
}, [setLoggedIn, setUser, setSession]);
|
||||
|
||||
// Mostrar loading enquanto verifica auth ou perfil
|
||||
if (isLoading || isChecking) {
|
||||
|
||||
@ -14,22 +14,22 @@ const SocialLoginButtons = () => {
|
||||
console.log('🔐 Iniciando login com Google...');
|
||||
console.log('🌐 URL atual:', window.location.origin);
|
||||
|
||||
// Determinar a URL de redirecionamento correta baseada no hostname - SEMPRE para /dashboard
|
||||
// Determinar a URL de redirecionamento correta baseada no hostname
|
||||
let redirectUrl;
|
||||
const hostname = window.location.hostname;
|
||||
|
||||
if (hostname === 'localhost') {
|
||||
redirectUrl = 'http://localhost:3000/dashboard';
|
||||
redirectUrl = 'http://localhost:3000/';
|
||||
} else if (hostname.includes('financehome.innova1001.com.br')) {
|
||||
redirectUrl = 'https://financehome.innova1001.com.br/dashboard';
|
||||
redirectUrl = 'https://financehome.innova1001.com.br/';
|
||||
} else if (hostname.includes('lovableproject.com')) {
|
||||
redirectUrl = `${window.location.origin}/dashboard`;
|
||||
redirectUrl = `${window.location.origin}/`;
|
||||
} else {
|
||||
// Fallback para qualquer outro domínio - sempre para dashboard
|
||||
redirectUrl = `${window.location.origin}/dashboard`;
|
||||
// Fallback para qualquer outro domínio
|
||||
redirectUrl = `${window.location.origin}/`;
|
||||
}
|
||||
|
||||
console.log('🔗 URL de redirecionamento (DASHBOARD):', redirectUrl);
|
||||
console.log('🔗 URL de redirecionamento:', redirectUrl);
|
||||
|
||||
const { data, error } = await supabase.auth.signInWithOAuth({
|
||||
provider: 'google',
|
||||
@ -37,7 +37,7 @@ const SocialLoginButtons = () => {
|
||||
redirectTo: redirectUrl,
|
||||
queryParams: {
|
||||
access_type: 'offline',
|
||||
prompt: 'consent',
|
||||
prompt: 'select_account',
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -51,6 +51,7 @@ const SocialLoginButtons = () => {
|
||||
});
|
||||
} else {
|
||||
console.log('✅ Redirecionamento iniciado...');
|
||||
// Não fazer nada aqui, deixar o redirecionamento acontecer naturalmente
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Erro geral no login com Google:', error);
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useExistingInstanceCheck } from '@/hooks/whatsapp/useExistingInstanceCheck';
|
||||
import { useGroupCreation } from '@/hooks/whatsappGroups/useGroupCreation';
|
||||
import { listarGruposWhatsApp } from '@/services/gruposWhatsAppService';
|
||||
import LoadingState from './LoadingState';
|
||||
import NoInstanceState from './NoInstanceState';
|
||||
import GroupCreationForm from './GroupCreationForm';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
|
||||
import { Info, Loader2 } from 'lucide-react';
|
||||
import { Info } from 'lucide-react';
|
||||
import { useGroupCreation } from '@/hooks/whatsappGroups/useGroupCreation';
|
||||
|
||||
interface CreateGroupFormProps {
|
||||
userEmail: string;
|
||||
@ -20,13 +18,6 @@ const CreateGroupForm = ({ userEmail, onSuccess }: CreateGroupFormProps) => {
|
||||
const [jaTemGrupo, setJaTemGrupo] = useState(false);
|
||||
const [grupoExistente, setGrupoExistente] = useState<any>(null);
|
||||
|
||||
const {
|
||||
hasExistingInstance,
|
||||
checkingExistingInstance,
|
||||
existingInstanceData,
|
||||
recheckInstance
|
||||
} = useExistingInstanceCheck(userEmail);
|
||||
|
||||
const { cadastrando, handleCadastrarGrupo } = useGroupCreation(userEmail, onSuccess);
|
||||
|
||||
// Verificar se o usuário já tem grupos cadastrados
|
||||
@ -56,12 +47,11 @@ const CreateGroupForm = ({ userEmail, onSuccess }: CreateGroupFormProps) => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
recheckInstance();
|
||||
verificarGruposExistentes();
|
||||
}, [userEmail]);
|
||||
|
||||
if (checkingExistingInstance || verificandoGrupos) {
|
||||
return <LoadingState message="Verificando instância WhatsApp e grupos existentes..." />;
|
||||
if (verificandoGrupos) {
|
||||
return <LoadingState message="Verificando grupos existentes..." />;
|
||||
}
|
||||
|
||||
// Se já tem grupo, mostrar informação
|
||||
@ -93,18 +83,14 @@ const CreateGroupForm = ({ userEmail, onSuccess }: CreateGroupFormProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Se não tem instância conectada, mostrar estado de sem instância
|
||||
if (!hasExistingInstance) {
|
||||
return <NoInstanceState userInstance={existingInstanceData} />;
|
||||
}
|
||||
|
||||
const handleSubmit = (nomeGrupo: string) => {
|
||||
handleCadastrarGrupo(nomeGrupo, existingInstanceData);
|
||||
// Agora pode criar grupo sem verificar instância
|
||||
handleCadastrarGrupo(nomeGrupo, { whatsapp: userEmail });
|
||||
};
|
||||
|
||||
return (
|
||||
<GroupCreationForm
|
||||
userInstance={existingInstanceData!}
|
||||
userInstance={{ instancia_zap: userEmail, status_instancia: 'ativo', whatsapp: userEmail }}
|
||||
userEmail={userEmail}
|
||||
cadastrando={cadastrando}
|
||||
onSubmit={handleSubmit}
|
||||
|
||||
@ -7,7 +7,7 @@ import { Label } from '@/components/ui/label';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { Loader2, AlertCircle, CheckCircle2, Info } from 'lucide-react';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
import { verificarInstanciaWhatsApp, listarGruposWhatsApp } from '@/services/gruposWhatsAppService';
|
||||
import { listarGruposWhatsApp } from '@/services/gruposWhatsAppService';
|
||||
import { findOrCreateWhatsAppGroup } from '@/services/whatsAppGroupsService';
|
||||
import { createWorkflowInN8n } from '@/services/n8nWorkflowService';
|
||||
import { createEvolutionWebhook } from '@/services/whatsApp/webhookService';
|
||||
@ -25,8 +25,6 @@ const CreateGroupFormSimple = ({ userEmail, onSuccess }: CreateGroupFormProps) =
|
||||
const [verificandoGrupos, setVerificandoGrupos] = useState(true);
|
||||
const [jaTemGrupo, setJaTemGrupo] = useState(false);
|
||||
const [grupoExistente, setGrupoExistente] = useState<any>(null);
|
||||
const [webhookEnviado, setWebhookEnviado] = useState(false);
|
||||
const [workflowAtivado, setWorkflowAtivado] = useState(false);
|
||||
const [mensagemStatus, setMensagemStatus] = useState<{
|
||||
tipo: 'info' | 'success' | 'error';
|
||||
texto: string;
|
||||
@ -85,23 +83,8 @@ const CreateGroupFormSimple = ({ userEmail, onSuccess }: CreateGroupFormProps) =
|
||||
|
||||
setCarregando(true);
|
||||
setMensagemStatus(null);
|
||||
setWebhookEnviado(false);
|
||||
setWorkflowAtivado(false);
|
||||
|
||||
try {
|
||||
// Verificar se o usuário tem instância WhatsApp
|
||||
setMensagemStatus({ tipo: 'info', texto: 'Verificando sua instância do WhatsApp...' });
|
||||
|
||||
const instanciaInfo = await verificarInstanciaWhatsApp();
|
||||
|
||||
if (!instanciaInfo.hasInstance) {
|
||||
setMensagemStatus({
|
||||
tipo: 'error',
|
||||
texto: 'Você precisa ter uma instância do WhatsApp conectada. Acesse o menu "WhatsApp" primeiro.'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Criar ou encontrar grupo
|
||||
setMensagemStatus({ tipo: 'info', texto: 'Cadastrando grupo...' });
|
||||
|
||||
@ -122,7 +105,6 @@ const CreateGroupFormSimple = ({ userEmail, onSuccess }: CreateGroupFormProps) =
|
||||
try {
|
||||
console.log(`🔔 Enviando webhook ativarworkflow para usuário: ${userEmail}`);
|
||||
await activateUserWorkflow(userEmail);
|
||||
setWorkflowAtivado(true);
|
||||
console.log('✅ [GRUPO] Webhook ativarworkflow enviado com sucesso no cadastro do grupo');
|
||||
} catch (workflowError) {
|
||||
console.error('❌ Erro ao enviar webhook ativarworkflow:', workflowError);
|
||||
@ -136,7 +118,6 @@ const CreateGroupFormSimple = ({ userEmail, onSuccess }: CreateGroupFormProps) =
|
||||
try {
|
||||
console.log(`🔔 Enviando email para N8N configurar webhook: ${userEmail}`);
|
||||
await createEvolutionWebhook(userEmail);
|
||||
setWebhookEnviado(true);
|
||||
console.log('✅ [GRUPO] Email enviado com sucesso para N8N no cadastro do grupo');
|
||||
} catch (webhookError) {
|
||||
console.error('❌ Erro ao enviar email para N8N:', webhookError);
|
||||
|
||||
@ -6,16 +6,17 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Boxes } from "@/components/ui/background-boxes";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ArrowLeft } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import LoginForm from '@/components/auth/LoginForm';
|
||||
import RegisterForm from '@/components/auth/RegisterForm';
|
||||
import SocialLoginButtons from '@/components/auth/SocialLoginButtons';
|
||||
import AuthSecurityFeatures from '@/components/auth/AuthSecurityFeatures';
|
||||
import ForgotPasswordForm from '@/components/auth/ForgotPasswordForm';
|
||||
import { toast } from "sonner";
|
||||
|
||||
const Auth = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState("login");
|
||||
const [showForgotPassword, setShowForgotPassword] = useState(false);
|
||||
const location = useLocation();
|
||||
|
||||
// Show success message when redirected from registration or email confirmation
|
||||
@ -32,6 +33,35 @@ const Auth = () => {
|
||||
}
|
||||
}, [location.state]);
|
||||
|
||||
const handleForgotPassword = () => {
|
||||
setShowForgotPassword(true);
|
||||
};
|
||||
|
||||
const handleBackToLogin = () => {
|
||||
setShowForgotPassword(false);
|
||||
setActiveTab("login");
|
||||
};
|
||||
|
||||
if (showForgotPassword) {
|
||||
return (
|
||||
<div className="min-h-screen relative w-full overflow-hidden bg-slate-900 flex items-center justify-center px-4">
|
||||
<div className="absolute inset-0 w-full h-full bg-slate-900 z-10 [mask-image:radial-gradient(transparent,white)] pointer-events-none" />
|
||||
<Boxes />
|
||||
|
||||
<Link to="/" className="absolute top-4 left-4 z-30">
|
||||
<Button variant="ghost" size="sm" className="text-white hover:bg-white/10">
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Voltar
|
||||
</Button>
|
||||
</Link>
|
||||
|
||||
<div className="relative z-20 bg-white/95 backdrop-blur-sm border-white/20 rounded-lg">
|
||||
<ForgotPasswordForm onBackToLogin={handleBackToLogin} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen relative w-full overflow-hidden bg-slate-900 flex items-center justify-center px-4">
|
||||
<div className="absolute inset-0 w-full h-full bg-slate-900 z-10 [mask-image:radial-gradient(transparent,white)] pointer-events-none" />
|
||||
@ -66,7 +96,11 @@ const Auth = () => {
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="login" className="space-y-4">
|
||||
<LoginForm isLoading={isLoading} setIsLoading={setIsLoading} />
|
||||
<LoginForm
|
||||
isLoading={isLoading}
|
||||
setIsLoading={setIsLoading}
|
||||
onForgotPassword={handleForgotPassword}
|
||||
/>
|
||||
<SocialLoginButtons />
|
||||
</TabsContent>
|
||||
|
||||
|
||||
147
src/pages/ResetPassword.tsx
Normal file
147
src/pages/ResetPassword.tsx
Normal file
@ -0,0 +1,147 @@
|
||||
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||
import { CheckCircle2, Lock } from "lucide-react";
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { toast } from "sonner";
|
||||
|
||||
const ResetPassword = () => {
|
||||
const navigate = useNavigate();
|
||||
const [password, setPassword] = useState('');
|
||||
const [confirmPassword, setConfirmPassword] = useState('');
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [success, setSuccess] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Verificar se há um token de recuperação na URL
|
||||
const hashParams = new URLSearchParams(window.location.hash.substring(1));
|
||||
const accessToken = hashParams.get('access_token');
|
||||
const refreshToken = hashParams.get('refresh_token');
|
||||
|
||||
if (!accessToken || !refreshToken) {
|
||||
toast.error("Link inválido ou expirado");
|
||||
navigate('/auth');
|
||||
}
|
||||
}, [navigate]);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
setError('');
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
setError('As senhas não coincidem');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length < 6) {
|
||||
setError('A senha deve ter pelo menos 6 caracteres');
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { error } = await supabase.auth.updateUser({
|
||||
password: password
|
||||
});
|
||||
|
||||
if (error) {
|
||||
setError(error.message);
|
||||
} else {
|
||||
setSuccess(true);
|
||||
toast.success("Senha redefinida com sucesso!");
|
||||
setTimeout(() => {
|
||||
navigate('/auth');
|
||||
}, 3000);
|
||||
}
|
||||
} catch (error) {
|
||||
setError('Erro inesperado. Tente novamente.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (success) {
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
|
||||
<Card className="w-full max-w-md">
|
||||
<CardHeader className="space-y-1 text-center">
|
||||
<div className="flex justify-center mb-4">
|
||||
<CheckCircle2 className="h-12 w-12 text-green-500" />
|
||||
</div>
|
||||
<CardTitle className="text-2xl">Senha redefinida!</CardTitle>
|
||||
<CardDescription>
|
||||
Sua senha foi alterada com sucesso. Você será redirecionado para o login.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex items-center justify-center bg-gray-50 px-4">
|
||||
<Card className="w-full max-w-md">
|
||||
<CardHeader className="space-y-1 text-center">
|
||||
<div className="flex justify-center mb-4">
|
||||
<Lock className="h-12 w-12 text-blue-500" />
|
||||
</div>
|
||||
<CardTitle className="text-2xl">Nova senha</CardTitle>
|
||||
<CardDescription>
|
||||
Digite sua nova senha
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Nova senha</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
placeholder="Digite sua nova senha"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
disabled={isLoading}
|
||||
minLength={6}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="confirmPassword">Confirmar senha</Label>
|
||||
<Input
|
||||
id="confirmPassword"
|
||||
type="password"
|
||||
placeholder="Confirme sua nova senha"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
required
|
||||
disabled={isLoading}
|
||||
minLength={6}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert variant="destructive">
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Button type="submit" className="w-full" disabled={isLoading}>
|
||||
{isLoading ? "Salvando..." : "Redefinir senha"}
|
||||
</Button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResetPassword;
|
||||
Loading…
Reference in New Issue
Block a user