177 lines
6.3 KiB
TypeScript

/**
* Página de Callback do Magic Link
* Processa o token do magic link e autentica o usuário
*/
import { useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import { supabase } from "../lib/supabase";
import { Loader2, CheckCircle, XCircle } from "lucide-react";
import toast from "react-hot-toast";
export default function AuthCallback() {
const navigate = useNavigate();
const [status, setStatus] = useState<"loading" | "success" | "error">(
"loading"
);
const [message, setMessage] = useState("Processando autenticação...");
useEffect(() => {
const handleCallback = async () => {
try {
console.log("[AuthCallback] Iniciando processamento");
// Verificar se é um token de recovery
const hash = window.location.hash;
const hashParams = new URLSearchParams(hash.substring(1));
const accessToken = hashParams.get("access_token");
const type = hashParams.get("type");
console.log("[AuthCallback] Hash:", hash);
console.log("[AuthCallback] Type:", type);
console.log("[AuthCallback] Access Token presente:", !!accessToken);
// Se for recovery, redirecionar para página de reset
if (type === "recovery" && accessToken) {
console.log(
"[AuthCallback] ✅ Token de recovery detectado, redirecionando para /reset-password"
);
setStatus("success");
setMessage("Redirecionando para página de redefinição de senha...");
// Redirecionar preservando o hash com o token
setTimeout(() => {
navigate(`/reset-password${hash}`, { replace: true });
}, 1000);
return;
}
console.log("[AuthCallback] Processando magic link normal");
// Supabase automaticamente processa os query params
const {
data: { session },
error,
} = await supabase.auth.getSession();
if (error) {
console.error("[AuthCallback] Erro ao obter sessão:", error);
throw error;
}
if (!session) {
throw new Error(
"Nenhuma sessão encontrada. O link pode ter expirado."
);
}
console.log("[AuthCallback] Sessão obtida:", {
user: session.user.email,
role: session.user.role,
});
// Magic link ou qualquer callback com sessão válida:
// Salvar tokens diretamente no localStorage
console.log("[AuthCallback] Salvando tokens e user no localStorage");
localStorage.setItem("mediconnect_access_token", session.access_token);
localStorage.setItem("mediconnect_refresh_token", session.refresh_token);
localStorage.setItem(
"mediconnect_user",
JSON.stringify({
id: session.user.id,
email: session.user.email,
nome: session.user.user_metadata?.full_name || session.user.email,
role: session.user.user_metadata?.role || "paciente",
})
);
console.log("[AuthCallback] Autenticação concluída");
setStatus("success");
setMessage("Autenticado com sucesso! Redirecionando...");
toast.success("Login realizado com sucesso!");
// Redirecionar baseado no contexto salvo ou role do usuário
setTimeout(() => {
// Verificar se há redirecionamento salvo do magic link
const savedRedirect = localStorage.getItem("magic_link_redirect");
if (savedRedirect) {
console.log("[AuthCallback] Redirecionando para:", savedRedirect);
localStorage.removeItem("magic_link_redirect"); // Limpar após uso
navigate(savedRedirect, { replace: true });
return;
}
// Fallback: redirecionar baseado no role
const userRole = session.user.user_metadata?.role || "paciente";
console.log("[AuthCallback] Redirecionando baseado no role:", userRole);
switch (userRole) {
case "medico":
navigate("/painel-medico", { replace: true });
break;
case "secretaria":
navigate("/painel-secretaria", { replace: true });
break;
case "paciente":
default:
navigate("/acompanhamento", { replace: true });
break;
}
}, 1500);
} catch (err: any) {
console.error("[AuthCallback] Erro:", err);
setStatus("error");
setMessage(err.message || "Erro ao processar autenticação");
toast.error(err.message || "Erro na autenticação");
}
};
handleCallback();
}, [navigate]);
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-white dark:from-gray-900 dark:to-gray-950 flex items-center justify-center p-4">
<div className="max-w-md w-full bg-white dark:bg-gray-800 rounded-lg shadow-lg p-8 text-center">
{status === "loading" && (
<>
<Loader2 className="w-16 h-16 text-blue-600 dark:text-blue-400 mx-auto mb-4 animate-spin" />
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-2">
Autenticando
</h1>
<p className="text-gray-600 dark:text-gray-400">{message}</p>
</>
)}
{status === "success" && (
<>
<CheckCircle className="w-16 h-16 text-green-600 dark:text-green-400 mx-auto mb-4" />
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-2">
Sucesso!
</h1>
<p className="text-gray-600 dark:text-gray-400">{message}</p>
</>
)}
{status === "error" && (
<>
<XCircle className="w-16 h-16 text-red-600 dark:text-red-400 mx-auto mb-4" />
<h1 className="text-2xl font-bold text-gray-900 dark:text-gray-100 mb-2">
Erro na Autenticação
</h1>
<p className="text-gray-600 dark:text-gray-400 mb-6">{message}</p>
<button
onClick={() => navigate("/")}
className="px-6 py-3 bg-blue-600 text-white rounded-lg font-medium hover:bg-blue-700 transition-colors"
>
Voltar ao Início
</button>
</>
)}
</div>
</div>
);
}