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:
gpt-engineer-app[bot] 2025-07-02 18:07:51 +00:00
parent 05a8186a19
commit 3a1bc74f34
9 changed files with 472 additions and 126 deletions

View File

@ -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>}>

View 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;

View File

@ -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,
try {
const { data, error } = await supabase.auth.signInWithPassword({
email: email.trim().toLowerCase(),
password: password,
});
setIsLoading(false);
if (error) {
toast.error("Erro no login", {
description: "Email ou senha incorretos. Verifique seus dados."
});
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 {
toast.success("Login realizado com sucesso", {
description: "Bem-vindo de volta!"
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 o dashboard após login bem-sucedido
navigate('/dashboard');
// 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>
);
};

View File

@ -18,15 +18,26 @@ 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);
const getInitialSession = async () => {
try {
const { data: { session }, error } = await supabase.auth.getSession();
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);
@ -44,12 +55,28 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
}
setIsLoading(false);
});
}
} catch (error) {
console.error('🔐 [PROTECTED_ROUTE] Erro inesperado ao obter sessão:', error);
if (mounted) {
setIsLoading(false);
}
}
};
getInitialSession();
// Listener para mudanças de auth com debounce
let timeoutId: NodeJS.Timeout;
// 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);
(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);
@ -59,7 +86,7 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
console.log('👤 [PROTECTED_ROUTE] Email atualizado no localStorage:', session.user.email);
// Disparar evento customizado para notificar sobre o login
if (_event === 'SIGNED_IN') {
if (event === 'SIGNED_IN') {
console.log('🎉 [PROTECTED_ROUTE] Login detectado, disparando evento');
window.dispatchEvent(new CustomEvent('userLoggedIn', {
detail: { email: session.user.email }
@ -71,12 +98,16 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
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) {

View File

@ -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);

View File

@ -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}

View File

@ -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);

View File

@ -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
View 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;