Merge pull request #17 from m1guelmcf/pagina-inicial-login

Alteracao da pagina inicial e na pagina de login
This commit is contained in:
DaniloSts 2025-11-18 13:38:50 -03:00 committed by GitHub
commit 361a651412
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 298 additions and 193 deletions

View File

@ -138,7 +138,7 @@ export default function LoginPage() {
Não tem uma conta de paciente?{" "} Não tem uma conta de paciente?{" "}
</span> </span>
<Link href="/patient/register"> <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 Crie uma agora
</span> </span>
</Link> </Link>
@ -232,18 +232,21 @@ export default function LoginPage() {
{/* Botões */} {/* Botões */}
<div className="flex gap-3 pt-2"> <div className="flex gap-3 pt-2">
{/* Botão Cancelar Azul contornado */}
<Button <Button
variant="outline" variant="outline"
onClick={closeModal} onClick={closeModal}
disabled={isLoading} disabled={isLoading}
className="flex-1" className="flex-1 bg-blue-600 hover:bg-blue-700 text-white"
> >
Cancelar Cancelar
</Button> </Button>
{/* Botão Resetar Senha Azul sólido */}
<Button <Button
onClick={handleResetPassword} onClick={handleResetPassword}
disabled={isLoading} disabled={isLoading}
className="flex-1" className="flex-1 bg-blue-600 hover:bg-blue-700 text-white"
> >
{isLoading ? "Enviando..." : "Resetar Senha"} {isLoading ? "Enviando..." : "Resetar Senha"}
</Button> </Button>

View File

@ -3,50 +3,49 @@
import Link from "next/link"; import Link from "next/link";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useState } from "react"; import { useState } from "react";
import { Stethoscope, Baby, Microscope } from "lucide-react";
export default function InicialPage() { export default function InicialPage() {
const [isMenuOpen, setIsMenuOpen] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false);
return ( return (
<div className="min-h-screen flex flex-col bg-background"> <div className="min-h-screen flex flex-col bg-white font-sans scroll-smooth text-[#1E2A78]">
{/* Barra superior de informações */} {/* Barra superior */}
<div className="bg-primary text-primary-foreground text-sm py-2 px-4 md:px-6 flex justify-between items-center"> <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 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> </div>
{/* Header */}
{/* Header principal - Com Logo REAL */} <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">
<header className="bg-card shadow-md py-4 px-4 md:px-6 flex justify-between items-center relative"> <a href="#home" className="flex items-center space-x-2 cursor-pointer">
{/* 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 */}
<img <img
src="/android-chrome-512x512.png" // O caminho se inicia a partir da pasta 'public' src="/android-chrome-512x512.png"
alt="Logo MediConnect" 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"
/> />
<h1 className="text-2xl font-extrabold text-[#1E2A78] tracking-tight">
{/* 2. NOME DO SITE */} MediConnect
<h1 className="text-2xl font-bold text-primary">MediConnect</h1> </h1>
</a> </a>
{/* Botão do menu hambúrguer para telas menores */} {/* Menu Mobile */}
<div className="md:hidden flex items-center space-x-4"> <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"> <Link href="/login">
<Button <Button
variant="outline" 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 Login
</Button> </Button>
</Link> </Link>
<button <button
onClick={() => setIsMenuOpen(!isMenuOpen)} onClick={() => setIsMenuOpen(!isMenuOpen)}
className="text-primary-foreground focus:outline-none" className="text-[#1E2A78] focus:outline-none"
> >
<svg <svg
className="w-6 h-6 text-primary" className="w-6 h-6"
fill="none" fill="none"
stroke="currentColor" stroke="currentColor"
viewBox="0 0 24 24" viewBox="0 0 24 24"
@ -71,114 +70,140 @@ export default function InicialPage() {
</button> </button>
</div> </div>
{/* Navegação principal */} {/* Navegação */}
<nav <nav
className={`${ className={`${
isMenuOpen ? "block" : "hidden" 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"> <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-primary"> <Link href="#home" className="hover:text-[#007BFF] transition">
Home Home
</Link> </Link>
<a href="#about" className="hover:text-primary"> <a href="#about" className="hover:text-[#007BFF] transition">
Sobre Sobre
</a> </a>
<a href="#departments" className="hover:text-primary"> <a href="#departments" className="hover:text-[#007BFF] transition">
Departamentos Departamentos
</a> </a>
<a href="#doctors" className="hover:text-primary"> <a href="#doctors" className="hover:text-[#007BFF] transition">
Médicos Médicos
</a> </a>
<a href="#contact" className="hover:text-primary"> <a href="#contact" className="hover:text-[#007BFF] transition">
Contato Contato
</a> </a>
</div> </div>
</nav> </nav>
{/* Botão de Login para telas maiores (md e acima) */} {/* Login Desktop */}
<div className="hidden md:flex space-x-4"> <div className="hidden md:flex space-x-4">
<Link href="/login"> <Link href="/login">
<Button <Button
variant="outline" 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 Login
</Button> </Button>
</Link> </Link>
</div> </div>
</header> </header>
{/* Hero Section */}
{/* 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-20 bg-gradient-to-r from-[#1E2A78] via-[#007BFF] to-[#00BFFF] text-white">
<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">
<div className="max-w-lg mx-auto md:mx-0"> <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 Bem-vindo à Saúde Digital
</h2> </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 Soluções Médicas <br /> & Cuidados com a Saúde
</h1> </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 mais de 25 anos. Atendimento médico com Excelência em saúde mais de 25 anos. Atendimento médico com
qualidade, segurança e carinho. qualidade, segurança e carinho.
</p> </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"> <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>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">
<Button variant="secondary">Saiba Mais</Button> 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> </div>
<div className="mt-10 md:mt-0 flex justify-center"> <div className="mt-10 md:mt-0 flex justify-center">
<img <img
src="https://t4.ftcdn.net/jpg/03/20/52/31/360_F_320523164_tx7Rdd7I2XDTvvKfz2oRuRpKOPE5z0ni.jpg" src="https://t4.ftcdn.net/jpg/03/20/52/31/360_F_320523164_tx7Rdd7I2XDTvvKfz2oRuRpKOPE5z0ni.jpg"
alt="Médico" 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> </div>
</section> </section>
{/* Serviços */}
{/* Seção de serviços */} <section
<section className="py-16 px-6 md:px-10 lg:px-20 bg-card"> id="departments"
<h2 className="text-center text-2xl sm:text-3xl font-bold text-foreground"> 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 Cuidados completos para a sua saúde
</h2> </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 Serviços médicos que oferecemos
</p> </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="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-10 mt-12 max-w-6xl mx-auto">
<div className="p-6 bg-background rounded-xl shadow hover:shadow-lg transition"> {/* Card */}
<h3 className="text-xl font-semibold text-primary"> {[
Clínica Geral {
</h3> title: "Clínica Geral",
<p className="text-muted-foreground mt-2 text-sm"> desc: "Seu primeiro passo para o cuidado. Atendimento focado na prevenção e no diagnóstico inicial.",
Seu primeiro passo para o cuidado. Atendimento focado na prevenção Icon: Stethoscope,
e no diagnóstico inicial. },
</p> {
<Button className="mt-4 w-full">Agendar</Button> 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> </div>
<div className="p-6 bg-background rounded-xl shadow hover:shadow-lg transition"> <p className="text-gray-600 mt-3 text-sm leading-relaxed">
<h3 className="text-xl font-semibold text-primary">Pediatria</h3> {desc}
<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> </p>
<Button className="mt-4 w-full">Agendar</Button> <Button className="mt-6 w-full bg-[#007BFF] hover:bg-[#005FCC] text-white transition">
</div> Agendar
<div className="p-6 bg-background rounded-xl shadow hover:shadow-lg transition"> </Button>
<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>
))}
</div> </div>
</section> </section>
{/* Footer */} {/* Footer */}
<footer className="bg-primary text-primary-foreground py-6 text-center text-sm"> <footer className="bg-[#1E2A78] text-white py-8 text-center text-sm">
<p>© 2025 MediConnect</p> <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> </footer>
   
</div> </div>
); );
} }

View File

@ -40,7 +40,12 @@ export function LoginForm({ children }: LoginFormProps) {
*/ */
const handleRoleSelection = (selectedDashboardRole: string, user: any) => { const handleRoleSelection = (selectedDashboardRole: string, user: any) => {
if (!user) { if (!user) {
toast({ title: "Erro de Sessão", description: "Não foi possível encontrar os dados do usuário. Tente novamente.", variant: "destructive" }); toast({
title: "Erro de Sessão",
description:
"Não foi possível encontrar os dados do usuário. Tente novamente.",
variant: "destructive",
});
setUserRoles([]); setUserRoles([]);
return; return;
} }
@ -48,23 +53,40 @@ export function LoginForm({ children }: LoginFormProps) {
const roleInLowerCase = selectedDashboardRole.toLowerCase(); const roleInLowerCase = selectedDashboardRole.toLowerCase();
console.log("Salvando no localStorage com o perfil:", roleInLowerCase); console.log("Salvando no localStorage com o perfil:", roleInLowerCase);
const completeUserInfo = { ...user, user_metadata: { ...user.user_metadata, role: roleInLowerCase } }; const completeUserInfo = {
...user,
user_metadata: { ...user.user_metadata, role: roleInLowerCase },
};
localStorage.setItem("user_info", JSON.stringify(completeUserInfo)); localStorage.setItem("user_info", JSON.stringify(completeUserInfo));
let redirectPath = ""; let redirectPath = "";
switch (selectedDashboardRole) { switch (selectedDashboardRole) {
case "gestor": redirectPath = "/manager/dashboard"; break; case "gestor":
case "admin": redirectPath = "/manager/dashboard"; break; redirectPath = "/manager/dashboard";
case "medico": redirectPath = "/doctor/dashboard"; break; break;
case "secretaria": redirectPath = "/secretary/dashboard"; break; case "admin":
case "paciente": redirectPath = "/patient/dashboard"; break; redirectPath = "/manager/dashboard";
break;
case "medico":
redirectPath = "/doctor/dashboard";
break;
case "secretaria":
redirectPath = "/secretary/dashboard";
break;
case "paciente":
redirectPath = "/patient/dashboard";
break;
} }
if (redirectPath) { if (redirectPath) {
toast({ title: `Entrando como ${selectedDashboardRole}...` }); toast({ title: `Entrando como ${selectedDashboardRole}...` });
router.push(redirectPath); router.push(redirectPath);
} else { } else {
toast({ title: "Erro", description: "Perfil selecionado inválido.", variant: "destructive" }); toast({
title: "Erro",
description: "Perfil selecionado inválido.",
variant: "destructive",
});
} }
}; };
@ -81,23 +103,29 @@ export function LoginForm({ children }: LoginFormProps) {
throw new Error("Resposta de autenticação inválida."); throw new Error("Resposta de autenticação inválida.");
} }
const rolesData = await api.get(`/rest/v1/user_roles?user_id=eq.${user.id}&select=role`); const rolesData = await api.get(
`/rest/v1/user_roles?user_id=eq.${user.id}&select=role`
);
const me = await usersService.getMeSimple() const me = await usersService.getMeSimple();
console.log(me.roles) console.log(me.roles);
if (!me.roles || me.roles.length === 0) { if (!me.roles || me.roles.length === 0) {
throw new Error("Nenhum perfil de acesso foi encontrado para este usuário."); throw new Error(
"Nenhum perfil de acesso foi encontrado para este usuário."
);
} }
handleRoleSelection(me.roles[0], user); handleRoleSelection(me.roles[0], user);
} catch (error) { } catch (error) {
localStorage.removeItem("token"); localStorage.removeItem("token");
localStorage.removeItem("user_info"); localStorage.removeItem("user_info");
toast({ toast({
title: "Erro no Login", title: "Erro no Login",
description: error instanceof Error ? error.message : "Ocorreu um erro inesperado.", description:
error instanceof Error
? error.message
: "Ocorreu um erro inesperado.",
variant: "destructive", variant: "destructive",
}); });
setIsLoading(false); setIsLoading(false);
@ -105,7 +133,8 @@ export function LoginForm({ children }: LoginFormProps) {
}; };
// Estado para guardar os botões de seleção de perfil // Estado para guardar os botões de seleção de perfil
const [roleSelectionUI, setRoleSelectionUI] = useState<React.ReactNode | null>(null); const [roleSelectionUI, setRoleSelectionUI] =
useState<React.ReactNode | null>(null);
return ( return (
<Card className="w-full bg-transparent border-0 shadow-none"> <Card className="w-full bg-transparent border-0 shadow-none">
@ -116,30 +145,78 @@ export function LoginForm({ children }: LoginFormProps) {
<Label htmlFor="email">E-mail</Label> <Label htmlFor="email">E-mail</Label>
<div className="relative"> <div className="relative">
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" /> <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" /> <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> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="password">Senha</Label> <Label htmlFor="password">Senha</Label>
<div className="relative"> <div className="relative">
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" /> <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" /> <Input
<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}> id="password"
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />} 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> </button>
</div> </div>
</div> </div>
<Button type="submit" className="w-full h-11 text-base font-semibold" disabled={isLoading}> <Button
{isLoading ? <Loader2 className="w-5 h-5 animate-spin" /> : "Entrar"} 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> </Button>
</form> </form>
) : ( ) : (
<div className="space-y-4 animate-in fade-in-50"> <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> <h3 className="text-lg font-medium text-center text-foreground">
<p className="text-sm text-muted-foreground text-center">Selecione com qual perfil deseja entrar:</p> 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"> <div className="flex flex-col space-y-3 pt-2">
{userRoles.map((role) => ( {userRoles.map((role) => (
<Button key={role} variant="outline" className="h-11 text-base" onClick={() => handleRoleSelection(role, authenticatedUser)}> <Button
key={role}
variant="outline"
className="h-11 text-base"
onClick={() => handleRoleSelection(role, authenticatedUser)}
>
Entrar como: {role.charAt(0).toUpperCase() + role.slice(1)} Entrar como: {role.charAt(0).toUpperCase() + role.slice(1)}
</Button> </Button>
))} ))}