Alteracao da pagina inicial e na pagina de login
This commit is contained in:
parent
fcbcb9988f
commit
cbcb7b54fd
@ -138,7 +138,7 @@ export default function LoginPage() {
|
||||
Não tem uma conta de paciente?{" "}
|
||||
</span>
|
||||
<Link href="/patient/register">
|
||||
<span className="font-semibold text-primary hover:underline cursor-pointer">
|
||||
<span className="font-semibold text-blue-600 hover:text-blue-700 hover:underline cursor-pointer">
|
||||
Crie uma agora
|
||||
</span>
|
||||
</Link>
|
||||
@ -232,18 +232,21 @@ export default function LoginPage() {
|
||||
|
||||
{/* Botões */}
|
||||
<div className="flex gap-3 pt-2">
|
||||
{/* Botão Cancelar – Azul contornado */}
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={closeModal}
|
||||
disabled={isLoading}
|
||||
className="flex-1"
|
||||
className="flex-1 bg-blue-600 hover:bg-blue-700 text-white"
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
|
||||
{/* Botão Resetar Senha – Azul sólido */}
|
||||
<Button
|
||||
onClick={handleResetPassword}
|
||||
disabled={isLoading}
|
||||
className="flex-1"
|
||||
className="flex-1 bg-blue-600 hover:bg-blue-700 text-white"
|
||||
>
|
||||
{isLoading ? "Enviando..." : "Resetar Senha"}
|
||||
</Button>
|
||||
|
||||
175
app/page.tsx
175
app/page.tsx
@ -3,50 +3,49 @@
|
||||
import Link from "next/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useState } from "react";
|
||||
import { Stethoscope, Baby, Microscope } from "lucide-react";
|
||||
|
||||
export default function InicialPage() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex flex-col bg-background">
|
||||
{/* Barra superior de informações */}
|
||||
<div className="bg-primary text-primary-foreground text-sm py-2 px-4 md:px-6 flex justify-between items-center">
|
||||
<div className="min-h-screen flex flex-col bg-white font-sans scroll-smooth text-[#1E2A78]">
|
||||
{/* Barra superior */}
|
||||
<div className="bg-[#1E2A78] text-white text-sm py-2 px-4 md:px-6 flex justify-between items-center">
|
||||
<span className="hidden sm:inline">Horário: 08h00 - 21h00</span>
|
||||
<span>Email: contato@mediconnect.com</span>
|
||||
<span className="hover:underline cursor-pointer transition">
|
||||
Email: contato@mediconnect.com
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Header principal - Com Logo REAL */}
|
||||
<header className="bg-card shadow-md py-4 px-4 md:px-6 flex justify-between items-center relative">
|
||||
{/* Agrupamento do Logo e Nome do Site */}
|
||||
<a href="#home" className="flex items-center space-x-1 cursor-pointer">
|
||||
{/* 1. IMAGEM/LOGO REAL: Referenciando o arquivo placeholder-logo.png na pasta public */}
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-md py-4 px-4 md:px-6 flex justify-between items-center relative sticky top-0 z-50 backdrop-blur-md">
|
||||
<a href="#home" className="flex items-center space-x-2 cursor-pointer">
|
||||
<img
|
||||
src="/android-chrome-512x512.png" // O caminho se inicia a partir da pasta 'public'
|
||||
src="/android-chrome-512x512.png"
|
||||
alt="Logo MediConnect"
|
||||
className="w-14 h-14 object-contain" // ALTERADO: Aumentado para w-14 h-14
|
||||
className="w-20 h-20 object-contain transition-transform hover:scale-105"
|
||||
/>
|
||||
|
||||
{/* 2. NOME DO SITE */}
|
||||
<h1 className="text-2xl font-bold text-primary">MediConnect</h1>
|
||||
<h1 className="text-2xl font-extrabold text-[#1E2A78] tracking-tight">
|
||||
MediConnect
|
||||
</h1>
|
||||
</a>
|
||||
|
||||
{/* Botão do menu hambúrguer para telas menores */}
|
||||
{/* Menu Mobile */}
|
||||
<div className="md:hidden flex items-center space-x-4">
|
||||
{/* O botão de login agora estará sempre aqui, fora do menu */}
|
||||
<Link href="/login">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="rounded-full px-4 py-2 text-sm border-2 transition cursor-pointer"
|
||||
className="rounded-full px-4 py-2 text-sm border-2 border-[#007BFF] text-[#007BFF] hover:bg-[#007BFF] hover:text-white transition"
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</Link>
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className="text-primary-foreground focus:outline-none"
|
||||
className="text-[#1E2A78] focus:outline-none"
|
||||
>
|
||||
<svg
|
||||
className="w-6 h-6 text-primary"
|
||||
className="w-6 h-6"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
@ -71,114 +70,140 @@ export default function InicialPage() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Navegação principal */}
|
||||
{/* Navegação */}
|
||||
<nav
|
||||
className={`${
|
||||
isMenuOpen ? "block" : "hidden"
|
||||
} absolute top-[76px] left-0 w-full bg-card shadow-md py-4 md:relative md:top-auto md:left-auto md:w-auto md:block md:bg-transparent md:shadow-none z-10`}
|
||||
} absolute top-[76px] left-0 w-full bg-white shadow-md py-4 md:relative md:top-auto md:left-auto md:w-auto md:block md:bg-transparent md:shadow-none transition-all duration-300 z-10`}
|
||||
>
|
||||
<div className="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-6 text-muted-foreground font-medium items-center">
|
||||
<Link href="#home" className="hover:text-primary">
|
||||
<div className="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-8 text-gray-600 font-medium items-center">
|
||||
<Link href="#home" className="hover:text-[#007BFF] transition">
|
||||
Home
|
||||
</Link>
|
||||
<a href="#about" className="hover:text-primary">
|
||||
<a href="#about" className="hover:text-[#007BFF] transition">
|
||||
Sobre
|
||||
</a>
|
||||
<a href="#departments" className="hover:text-primary">
|
||||
<a href="#departments" className="hover:text-[#007BFF] transition">
|
||||
Departamentos
|
||||
</a>
|
||||
<a href="#doctors" className="hover:text-primary">
|
||||
<a href="#doctors" className="hover:text-[#007BFF] transition">
|
||||
Médicos
|
||||
</a>
|
||||
<a href="#contact" className="hover:text-primary">
|
||||
<a href="#contact" className="hover:text-[#007BFF] transition">
|
||||
Contato
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Botão de Login para telas maiores (md e acima) */}
|
||||
{/* Login Desktop */}
|
||||
<div className="hidden md:flex space-x-4">
|
||||
<Link href="/login">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="rounded-full px-6 py-2 border-2 transition cursor-pointer"
|
||||
className="rounded-full px-6 py-2 border-2 border-[#007BFF] text-[#007BFF] hover:bg-[#007BFF] hover:text-white transition"
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Seção principal de destaque */}
|
||||
<section className="flex flex-col md:flex-row items-center justify-between px-6 md:px-10 lg:px-20 py-16 bg-background text-center md:text-left">
|
||||
{/* Hero Section */}
|
||||
<section className="flex flex-col md:flex-row items-center justify-between px-6 md:px-10 lg:px-20 py-20 bg-gradient-to-r from-[#1E2A78] via-[#007BFF] to-[#00BFFF] text-white">
|
||||
<div className="max-w-lg mx-auto md:mx-0">
|
||||
<h2 className="text-muted-foreground uppercase text-sm">
|
||||
<h2 className="uppercase text-sm tracking-widest opacity-80">
|
||||
Bem-vindo à Saúde Digital
|
||||
</h2>
|
||||
<h1 className="text-3xl sm:text-4xl lg:text-5xl font-extrabold text-foreground leading-tight mt-2">
|
||||
<h1 className="text-4xl sm:text-5xl lg:text-6xl font-extrabold leading-tight mt-2 drop-shadow-lg">
|
||||
Soluções Médicas <br /> & Cuidados com a Saúde
|
||||
</h1>
|
||||
<p className="text-muted-foreground mt-4 text-sm sm:text-base">
|
||||
<p className="mt-4 text-base leading-relaxed opacity-90">
|
||||
Excelência em saúde há mais de 25 anos. Atendimento médico com
|
||||
qualidade, segurança e carinho.
|
||||
</p>
|
||||
<div className="mt-6 flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 justify-center md:justify-start">
|
||||
<Button>Nossos Serviços</Button>
|
||||
<Button variant="secondary">Saiba Mais</Button>
|
||||
<div className="mt-8 flex flex-col sm:flex-row space-y-4 sm:space-y-0 sm:space-x-4 justify-center md:justify-start">
|
||||
<Button className="px-8 py-3 text-base font-semibold bg-white text-[#1E2A78] hover:bg-[#EAF4FF] transition-all shadow-md">
|
||||
Nossos Serviços
|
||||
</Button>
|
||||
<Button className="px-8 py-3 text-base font-semibold bg-white text-[#1E2A78] hover:bg-[#EAF4FF] transition-all shadow-md">
|
||||
Saiba Mais
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-10 md:mt-0 flex justify-center">
|
||||
<img
|
||||
src="https://t4.ftcdn.net/jpg/03/20/52/31/360_F_320523164_tx7Rdd7I2XDTvvKfz2oRuRpKOPE5z0ni.jpg"
|
||||
alt="Médico"
|
||||
className="w-60 sm:w-80 lg:w-96 h-auto object-cover rounded-lg shadow-lg"
|
||||
className="w-72 sm:w-96 lg:w-[28rem] h-auto object-cover rounded-2xl shadow-xl "
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Seção de serviços */}
|
||||
<section className="py-16 px-6 md:px-10 lg:px-20 bg-card">
|
||||
<h2 className="text-center text-2xl sm:text-3xl font-bold text-foreground">
|
||||
{/* Serviços */}
|
||||
<section
|
||||
id="departments"
|
||||
className="py-20 px-6 md:px-10 lg:px-20 bg-[#F8FBFF]"
|
||||
>
|
||||
<h2 className="text-center text-3xl sm:text-4xl font-extrabold text-[#1E2A78]">
|
||||
Cuidados completos para a sua saúde
|
||||
</h2>
|
||||
<p className="text-center text-muted-foreground mt-2 text-sm sm:text-base">
|
||||
<p className="text-center text-gray-600 mt-3 text-base">
|
||||
Serviços médicos que oferecemos
|
||||
</p>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 mt-10 max-w-5xl mx-auto">
|
||||
<div className="p-6 bg-background rounded-xl shadow hover:shadow-lg transition">
|
||||
<h3 className="text-xl font-semibold text-primary">
|
||||
Clínica Geral
|
||||
</h3>
|
||||
<p className="text-muted-foreground mt-2 text-sm">
|
||||
Seu primeiro passo para o cuidado. Atendimento focado na prevenção
|
||||
e no diagnóstico inicial.
|
||||
</p>
|
||||
<Button className="mt-4 w-full">Agendar</Button>
|
||||
</div>
|
||||
<div className="p-6 bg-background rounded-xl shadow hover:shadow-lg transition">
|
||||
<h3 className="text-xl font-semibold text-primary">Pediatria</h3>
|
||||
<p className="text-muted-foreground mt-2 text-sm">
|
||||
Cuidado gentil e especializado para garantir a saúde e o
|
||||
desenvolvimento de crianças e adolescentes.
|
||||
</p>
|
||||
<Button className="mt-4 w-full">Agendar</Button>
|
||||
</div>
|
||||
<div className="p-6 bg-background rounded-xl shadow hover:shadow-lg transition">
|
||||
<h3 className="text-xl font-semibold text-primary">Exames</h3>
|
||||
<p className="text-muted-foreground mt-2 text-sm">
|
||||
Resultados rápidos e precisos em exames laboratoriais e de imagem
|
||||
essenciais para seu diagnóstico.
|
||||
</p>
|
||||
<Button className="mt-4 w-full">Agendar</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10 mt-12 max-w-6xl mx-auto">
|
||||
{/* Card */}
|
||||
{[
|
||||
{
|
||||
title: "Clínica Geral",
|
||||
desc: "Seu primeiro passo para o cuidado. Atendimento focado na prevenção e no diagnóstico inicial.",
|
||||
Icon: Stethoscope,
|
||||
},
|
||||
{
|
||||
title: "Pediatria",
|
||||
desc: "Cuidado gentil e especializado para garantir a saúde e o desenvolvimento de crianças e adolescentes.",
|
||||
Icon: Baby,
|
||||
},
|
||||
{
|
||||
title: "Exames",
|
||||
desc: "Resultados rápidos e precisos em exames laboratoriais e de imagem essenciais para seu diagnóstico.",
|
||||
Icon: Microscope,
|
||||
},
|
||||
].map(({ title, desc, Icon }, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="p-8 bg-white rounded-2xl shadow-md hover:shadow-xl transition-all duration-300 border border-[#E0E9FF] group"
|
||||
>
|
||||
<div className="flex items-center space-x-3">
|
||||
<Icon className="text-[#007BFF] w-6 h-6 group-hover:scale-110 transition-transform" />
|
||||
<h3 className="text-xl font-semibold">{title}</h3>
|
||||
</div>
|
||||
<p className="text-gray-600 mt-3 text-sm leading-relaxed">
|
||||
{desc}
|
||||
</p>
|
||||
<Button className="mt-6 w-full bg-[#007BFF] hover:bg-[#005FCC] text-white transition">
|
||||
Agendar
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Footer */}
|
||||
<footer className="bg-primary text-primary-foreground py-6 text-center text-sm">
|
||||
<p>© 2025 MediConnect</p>
|
||||
<footer className="bg-[#1E2A78] text-white py-8 text-center text-sm">
|
||||
<div className="space-y-2">
|
||||
<p>© 2025 MediConnect — Todos os direitos reservados</p>
|
||||
<div className="flex justify-center space-x-6 opacity-80">
|
||||
<a href="#about" className="hover:text-[#00BFFF] transition">
|
||||
Sobre
|
||||
</a>
|
||||
<a href="#departments" className="hover:text-[#00BFFF] transition">
|
||||
Serviços
|
||||
</a>
|
||||
<a href="#contact" className="hover:text-[#00BFFF] transition">
|
||||
Contato
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -16,138 +16,215 @@ import { Eye, EyeOff, Mail, Lock, Loader2 } from "lucide-react";
|
||||
import { usersService } from "@/services/usersApi.mjs";
|
||||
|
||||
interface LoginFormProps {
|
||||
children?: React.ReactNode;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
interface FormState {
|
||||
email: string;
|
||||
password: string;
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
export function LoginForm({ children }: LoginFormProps) {
|
||||
const [form, setForm] = useState<FormState>({ email: "", password: "" });
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const [form, setForm] = useState<FormState>({ email: "", password: "" });
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
|
||||
const [userRoles, setUserRoles] = useState<string[]>([]);
|
||||
const [authenticatedUser, setAuthenticatedUser] = useState<any>(null);
|
||||
const [userRoles, setUserRoles] = useState<string[]>([]);
|
||||
const [authenticatedUser, setAuthenticatedUser] = useState<any>(null);
|
||||
|
||||
/**
|
||||
* --- NOVA FUNÇÃO ---
|
||||
* Finaliza o login com o perfil de dashboard escolhido e redireciona.
|
||||
*/
|
||||
const handleRoleSelection = (selectedDashboardRole: string, user: any) => {
|
||||
if (!user) {
|
||||
toast({ title: "Erro de Sessão", description: "Não foi possível encontrar os dados do usuário. Tente novamente.", variant: "destructive" });
|
||||
setUserRoles([]);
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* --- NOVA FUNÇÃO ---
|
||||
* Finaliza o login com o perfil de dashboard escolhido e redireciona.
|
||||
*/
|
||||
const handleRoleSelection = (selectedDashboardRole: string, user: any) => {
|
||||
if (!user) {
|
||||
toast({
|
||||
title: "Erro de Sessão",
|
||||
description:
|
||||
"Não foi possível encontrar os dados do usuário. Tente novamente.",
|
||||
variant: "destructive",
|
||||
});
|
||||
setUserRoles([]);
|
||||
return;
|
||||
}
|
||||
|
||||
const roleInLowerCase = selectedDashboardRole.toLowerCase();
|
||||
console.log("Salvando no localStorage com o perfil:", roleInLowerCase);
|
||||
const roleInLowerCase = selectedDashboardRole.toLowerCase();
|
||||
console.log("Salvando no localStorage com o perfil:", roleInLowerCase);
|
||||
|
||||
const completeUserInfo = { ...user, user_metadata: { ...user.user_metadata, role: roleInLowerCase } };
|
||||
localStorage.setItem("user_info", JSON.stringify(completeUserInfo));
|
||||
|
||||
let redirectPath = "";
|
||||
switch (selectedDashboardRole) {
|
||||
case "gestor": redirectPath = "/manager/dashboard"; break;
|
||||
case "admin": redirectPath = "/manager/dashboard"; break;
|
||||
case "medico": redirectPath = "/doctor/dashboard"; break;
|
||||
case "secretaria": redirectPath = "/secretary/dashboard"; break;
|
||||
case "paciente": redirectPath = "/patient/dashboard"; break;
|
||||
}
|
||||
|
||||
if (redirectPath) {
|
||||
toast({ title: `Entrando como ${selectedDashboardRole}...` });
|
||||
router.push(redirectPath);
|
||||
} else {
|
||||
toast({ title: "Erro", description: "Perfil selecionado inválido.", variant: "destructive" });
|
||||
}
|
||||
const completeUserInfo = {
|
||||
...user,
|
||||
user_metadata: { ...user.user_metadata, role: roleInLowerCase },
|
||||
};
|
||||
localStorage.setItem("user_info", JSON.stringify(completeUserInfo));
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user_info");
|
||||
let redirectPath = "";
|
||||
switch (selectedDashboardRole) {
|
||||
case "gestor":
|
||||
redirectPath = "/manager/dashboard";
|
||||
break;
|
||||
case "admin":
|
||||
redirectPath = "/manager/dashboard";
|
||||
break;
|
||||
case "medico":
|
||||
redirectPath = "/doctor/dashboard";
|
||||
break;
|
||||
case "secretaria":
|
||||
redirectPath = "/secretary/dashboard";
|
||||
break;
|
||||
case "paciente":
|
||||
redirectPath = "/patient/dashboard";
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
const authData = await login(form.email, form.password);
|
||||
const user = authData.user;
|
||||
if (!user || !user.id) {
|
||||
throw new Error("Resposta de autenticação inválida.");
|
||||
}
|
||||
if (redirectPath) {
|
||||
toast({ title: `Entrando como ${selectedDashboardRole}...` });
|
||||
router.push(redirectPath);
|
||||
} else {
|
||||
toast({
|
||||
title: "Erro",
|
||||
description: "Perfil selecionado inválido.",
|
||||
variant: "destructive",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const rolesData = await api.get(`/rest/v1/user_roles?user_id=eq.${user.id}&select=role`);
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsLoading(true);
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user_info");
|
||||
|
||||
const me = await usersService.getMeSimple()
|
||||
console.log(me.roles)
|
||||
try {
|
||||
const authData = await login(form.email, form.password);
|
||||
const user = authData.user;
|
||||
if (!user || !user.id) {
|
||||
throw new Error("Resposta de autenticação inválida.");
|
||||
}
|
||||
|
||||
if (!me.roles || me.roles.length === 0) {
|
||||
throw new Error("Nenhum perfil de acesso foi encontrado para este usuário.");
|
||||
}
|
||||
const rolesData = await api.get(
|
||||
`/rest/v1/user_roles?user_id=eq.${user.id}&select=role`
|
||||
);
|
||||
|
||||
handleRoleSelection(me.roles[0], user);
|
||||
const me = await usersService.getMeSimple();
|
||||
console.log(me.roles);
|
||||
|
||||
} catch (error) {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user_info");
|
||||
toast({
|
||||
title: "Erro no Login",
|
||||
description: error instanceof Error ? error.message : "Ocorreu um erro inesperado.",
|
||||
variant: "destructive",
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
if (!me.roles || me.roles.length === 0) {
|
||||
throw new Error(
|
||||
"Nenhum perfil de acesso foi encontrado para este usuário."
|
||||
);
|
||||
}
|
||||
|
||||
// Estado para guardar os botões de seleção de perfil
|
||||
const [roleSelectionUI, setRoleSelectionUI] = useState<React.ReactNode | null>(null);
|
||||
handleRoleSelection(me.roles[0], user);
|
||||
} catch (error) {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user_info");
|
||||
toast({
|
||||
title: "Erro no Login",
|
||||
description:
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "Ocorreu um erro inesperado.",
|
||||
variant: "destructive",
|
||||
});
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="w-full bg-transparent border-0 shadow-none">
|
||||
<CardContent className="p-0">
|
||||
{!roleSelectionUI ? (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">E-mail</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
||||
<Input id="email" type="email" placeholder="seu.email@exemplo.com" value={form.email} onChange={(e) => setForm({ ...form, email: e.target.value })} className="pl-10 h-11" required disabled={isLoading} autoComplete="username" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Senha</Label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
||||
<Input id="password" type={showPassword ? "text" : "password"} placeholder="Digite sua senha" value={form.password} onChange={(e) => setForm({ ...form, password: e.target.value })} className="pl-10 pr-12 h-11" required disabled={isLoading} autoComplete="current-password" />
|
||||
<button type="button" onClick={() => setShowPassword(!showPassword)} className="absolute right-2 top-1/2 -translate-y-1/2 h-8 w-8 p-0 text-muted-foreground hover:text-foreground" disabled={isLoading}>
|
||||
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Button type="submit" className="w-full h-11 text-base font-semibold" disabled={isLoading}>
|
||||
{isLoading ? <Loader2 className="w-5 h-5 animate-spin" /> : "Entrar"}
|
||||
</Button>
|
||||
</form>
|
||||
) : (
|
||||
<div className="space-y-4 animate-in fade-in-50">
|
||||
<h3 className="text-lg font-medium text-center text-foreground">Você tem múltiplos perfis</h3>
|
||||
<p className="text-sm text-muted-foreground text-center">Selecione com qual perfil deseja entrar:</p>
|
||||
<div className="flex flex-col space-y-3 pt-2">
|
||||
{userRoles.map((role) => (
|
||||
<Button key={role} variant="outline" className="h-11 text-base" onClick={() => handleRoleSelection(role, authenticatedUser)}>
|
||||
Entrar como: {role.charAt(0).toUpperCase() + role.slice(1)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
// Estado para guardar os botões de seleção de perfil
|
||||
const [roleSelectionUI, setRoleSelectionUI] =
|
||||
useState<React.ReactNode | null>(null);
|
||||
|
||||
return (
|
||||
<Card className="w-full bg-transparent border-0 shadow-none">
|
||||
<CardContent className="p-0">
|
||||
{!roleSelectionUI ? (
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">E-mail</Label>
|
||||
<div className="relative">
|
||||
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
placeholder="seu.email@exemplo.com"
|
||||
value={form.email}
|
||||
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||
className="pl-10 h-11 focus-visible:ring-blue-600 focus-visible:ring-2"
|
||||
required
|
||||
disabled={isLoading}
|
||||
autoComplete="username"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Senha</Label>
|
||||
<div className="relative">
|
||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
||||
<Input
|
||||
id="password"
|
||||
type={showPassword ? "text" : "password"}
|
||||
placeholder="Digite sua senha"
|
||||
value={form.password}
|
||||
onChange={(e) =>
|
||||
setForm({ ...form, password: e.target.value })
|
||||
}
|
||||
className="pl-10 pr-12 h-11 focus-visible:ring-blue-600 focus-visible:ring-2"
|
||||
required
|
||||
disabled={isLoading}
|
||||
autoComplete="current-password"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowPassword(!showPassword)}
|
||||
className="absolute right-2 top-1/2 -translate-y-1/2 h-8 w-8 p-0 text-muted-foreground hover:text-foreground"
|
||||
disabled={isLoading}
|
||||
>
|
||||
{showPassword ? (
|
||||
<EyeOff className="w-5 h-5" />
|
||||
) : (
|
||||
<Eye className="w-5 h-5" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full h-11 bg-blue-600 hover:bg-blue-700 text-white"
|
||||
disabled={isLoading}
|
||||
>
|
||||
{isLoading ? (
|
||||
<Loader2 className="w-5 h-5 animate-spin" />
|
||||
) : (
|
||||
"Entrar"
|
||||
)}
|
||||
</Button>
|
||||
</form>
|
||||
) : (
|
||||
<div className="space-y-4 animate-in fade-in-50">
|
||||
<h3 className="text-lg font-medium text-center text-foreground">
|
||||
Você tem múltiplos perfis
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground text-center">
|
||||
Selecione com qual perfil deseja entrar:
|
||||
</p>
|
||||
<div className="flex flex-col space-y-3 pt-2">
|
||||
{userRoles.map((role) => (
|
||||
<Button
|
||||
key={role}
|
||||
variant="outline"
|
||||
className="h-11 text-base"
|
||||
onClick={() => handleRoleSelection(role, authenticatedUser)}
|
||||
>
|
||||
Entrar como: {role.charAt(0).toUpperCase() + role.slice(1)}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{children}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user