Identidade visual

This commit is contained in:
m1guelmcf 2025-11-13 10:48:23 -03:00
parent 74ad727ec4
commit 40e6746f84
2 changed files with 196 additions and 136 deletions

View File

@ -1,10 +1,8 @@
// Caminho: app/login/page.tsx
"use client";
import {usersService} from "@/services/usersApi.mjs";
import { usersService } from "@/services/usersApi.mjs";
import { LoginForm } from "@/components/LoginForm";
import Link from "next/link";
import Image from "next/image";
@ -14,70 +12,67 @@ import { ArrowLeft, X } from "lucide-react";
import { useState } from "react";
import RenderFromTemplateContext from "next/dist/client/components/render-from-template-context";
export default function LoginPage() {
const [isModalOpen, setIsModalOpen] = useState(false);
const [email, setEmail] = useState("");
const [isLoading, setIsLoading] = useState(false);
const [message, setMessage] = useState<{ type: "success" | "error"; text: string } | null>(null);
const [message, setMessage] = useState<{
type: "success" | "error";
text: string;
} | null>(null);
const handleOpenModal = () => {
// Tenta pegar o email do input do formulário de login
const emailInput = document.querySelector('input[type="email"]') as HTMLInputElement;
const emailInput = document.querySelector(
'input[type="email"]'
) as HTMLInputElement;
if (emailInput?.value) {
setEmail(emailInput.value);
}
setIsModalOpen(true);
};
const handleResetPassword = async () => {
if (!email.trim()) {
setMessage({ type: "error", text: "Por favor, insira um e-mail válido." });
return;
}
if (!email.trim()) {
setMessage({
type: "error",
text: "Por favor, insira um e-mail válido.",
});
return;
}
setIsLoading(true);
setMessage(null);
setIsLoading(true);
setMessage(null);
try {
// Chama o método que já faz o fetch corretamente
const data = await usersService.resetPassword(email);
console.log("Resposta resetPassword:", data);
setMessage({
type: "success",
text: "E-mail de recuperação enviado! Verifique sua caixa de entrada.",
});
setTimeout(() => {
setIsModalOpen(false);
setMessage(null);
setEmail("");
}, 2000);
} catch (error) {
console.error("Erro no reset de senha:", error);
setMessage({
type: "error",
text:
error instanceof Error
? error.message
: "Erro ao enviar e-mail. Tente novamente.",
});
} finally {
setIsLoading(false);
}
};
try {
// Chama o método que já faz o fetch corretamente
const data = await usersService.resetPassword(email);
console.log("Resposta resetPassword:", data);
setMessage({
type: "success",
text: "E-mail de recuperação enviado! Verifique sua caixa de entrada.",
});
setTimeout(() => {
setIsModalOpen(false);
setMessage(null);
setEmail("");
}, 2000);
} catch (error) {
console.error("Erro no reset de senha:", error);
setMessage({
type: "error",
text:
error instanceof Error
? error.message
: "Erro ao enviar e-mail. Tente novamente.",
});
} finally {
setIsLoading(false);
}
};
const closeModal = () => {
setIsModalOpen(false);
@ -85,30 +80,46 @@ export default function LoginPage() {
setEmail("");
};
return (
<>
<div className="min-h-screen grid grid-cols-1 lg:grid-cols-2">
{/* PAINEL ESQUERDO: O Formulário */}
<div className="relative flex flex-col items-center justify-center p-8 bg-background">
{/* Link para Voltar */}
<div className="absolute top-8 left-8">
<Link href="/" className="inline-flex items-center text-muted-foreground hover:text-primary transition-colors font-medium">
<Link
href="/"
className="inline-flex items-center text-muted-foreground hover:text-primary transition-colors font-medium"
>
<ArrowLeft className="w-4 h-4 mr-2" />
Voltar à página inicial
</Link>
</div>
{/* O contêiner principal que agora terá a sombra e o estilo de card */}
<div className="w-full max-w-md bg-card p-10 rounded-2xl shadow-xl">
<div className="text-center mb-8">
<h1 className="text-3xl font-bold text-foreground">Acesse sua conta</h1>
<p className="text-muted-foreground mt-2">Bem-vindo(a) de volta ao MedConnect!</p>
{/* NOVO: Bloco da Logo e Nome (Painel Esquerdo) */}
<div className="flex items-center justify-center space-x-3 mb-8">
<img
src="/Logo MedConnect.png" // Caminho da sua logo
alt="Logo MediConnect"
className="w-16 h-16 object-contain" // Mesmo tamanho que usamos na página inicial
/>
<span className="text-3xl font-extrabold text-primary">
MedConnect
</span>
</div>
{/* FIM: Bloco da Logo e Nome */}
<div className="text-center mb-8">
{/* Título de boas-vindas movido para baixo da logo */}
<h1 className="text-3xl font-bold text-foreground">
Acesse sua conta
</h1>
<p className="text-muted-foreground mt-2">
Bem-vindo(a) de volta ao MedConnect!
</p>
</div>
<LoginForm>
{/* Children para o LoginForm */}
@ -122,9 +133,10 @@ export default function LoginPage() {
</div>
</LoginForm>
<div className="mt-6 text-center text-sm">
<span className="text-muted-foreground">Não tem uma conta de paciente? </span>
<span className="text-muted-foreground">
Não tem uma conta de paciente?{" "}
</span>
<Link href="/patient/register">
<span className="font-semibold text-primary hover:underline cursor-pointer">
Crie uma agora
@ -141,7 +153,7 @@ export default function LoginPage() {
src="https://images.unsplash.com/photo-1576091160550-2173dba999ef?q=80&w=2070"
alt="Médica utilizando um tablet na clínica MedConnect"
fill
style={{ objectFit: 'cover' }}
style={{ objectFit: "cover" }}
priority
/>
{/* Camada de sobreposição para escurecer a imagem e destacar o texto */}
@ -156,15 +168,13 @@ export default function LoginPage() {
Tecnologia e Cuidado a Serviço da Sua Saúde.
</h2>
<p className="mt-4 text-lg text-primary-foreground/80">
Acesse seu portal para uma experiência de saúde integrada, segura e eficiente.
Acesse seu portal para uma experiência de saúde integrada, segura
e eficiente.
</p>
</div>
</div>
</div>
{/* Modal de Recuperação de Senha */}
{isModalOpen && (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm">
@ -177,20 +187,23 @@ export default function LoginPage() {
<X className="w-5 h-5" />
</button>
{/* Cabeçalho */}
<div className="mb-6">
<h2 className="text-2xl font-bold text-foreground">Recuperar Senha</h2>
<h2 className="text-2xl font-bold text-foreground">
Recuperar Senha
</h2>
<p className="text-muted-foreground mt-2">
Insira seu e-mail e enviaremos um link para redefinir sua senha.
</p>
</div>
{/* Input de e-mail */}
<div className="space-y-4">
<div>
<label htmlFor="email" className="block text-sm font-medium text-foreground mb-2">
<label
htmlFor="email"
className="block text-sm font-medium text-foreground mb-2"
>
E-mail
</label>
<Input
@ -204,7 +217,6 @@ export default function LoginPage() {
/>
</div>
{/* Mensagem de feedback */}
{message && (
<div
@ -218,7 +230,6 @@ export default function LoginPage() {
</div>
)}
{/* Botões */}
<div className="flex gap-3 pt-2">
<Button

View File

@ -12,8 +12,33 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Search, Bell, Calendar, User, LogOut, ChevronLeft, ChevronRight, Home, CalendarCheck2, ClipboardPlus, SquareUserRound, CalendarClock, Users, SquareUser, ClipboardList, Stethoscope, ClipboardMinus } from "lucide-react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import {
Search,
Bell,
Calendar,
User,
LogOut,
ChevronLeft,
ChevronRight,
Home,
CalendarCheck2,
ClipboardPlus,
SquareUserRound,
CalendarClock,
Users,
SquareUser,
ClipboardList,
Stethoscope,
ClipboardMinus,
} from "lucide-react";
import SidebarUserSection from "@/components/ui/userToolTip";
interface UserData {
@ -38,7 +63,6 @@ interface UserData {
is_anonymous: boolean;
}
interface MenuItem {
href: string;
icon: React.ElementType;
@ -69,34 +93,32 @@ export default function Sidebar({ children }: SidebarProps) {
id: userInfo.id ?? "",
email: userInfo.email ?? "",
app_metadata: {
user_role: userInfo.app_metadata?.user_role ?? "patient",
user_role: userInfo.app_metadata?.user_role ?? "patient",
},
user_metadata: {
cpf: userInfo.user_metadata?.cpf ?? "",
email_verified: userInfo.user_metadata?.email_verified ?? false,
full_name: userInfo.user_metadata?.full_name ?? "",
phone_mobile: userInfo.user_metadata?.phone_mobile ?? "",
role: userInfo.user_metadata?.role ?? "",
cpf: userInfo.user_metadata?.cpf ?? "",
email_verified: userInfo.user_metadata?.email_verified ?? false,
full_name: userInfo.user_metadata?.full_name ?? "",
phone_mobile: userInfo.user_metadata?.phone_mobile ?? "",
role: userInfo.user_metadata?.role ?? "",
},
identities:
userInfo.identities?.map((identity: any) => ({
userInfo.identities?.map((identity: any) => ({
identity_id: identity.identity_id ?? "",
id: identity.id ?? "",
user_id: identity.user_id ?? "",
provider: identity.provider ?? "",
})) ?? [],
})) ?? [],
is_anonymous: userInfo.is_anonymous ?? false,
});
setRole(userInfo.user_metadata?.role)
});
setRole(userInfo.user_metadata?.role);
} else {
// O redirecionamento para /login já estava correto. Ótimo!
router.push("/login");
}
}, [router]);
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 1024) {
setSidebarCollapsed(true);
@ -133,61 +155,85 @@ export default function Sidebar({ children }: SidebarProps) {
const SetMenuItems = (role: any) => {
const patientItems: MenuItem[] = [
{ href: "/patient/dashboard", icon: Home, label: "Dashboard" },
{ href: "/patient/schedule", icon: CalendarClock, label: "Agendar Consulta" },
{ href: "/patient/appointments", icon: CalendarCheck2, label: "Minhas Consultas" },
{ href: "/patient/reports", icon: ClipboardPlus, label: "Meus Laudos" },
{ href: "/patient/profile", icon: SquareUser, label: "Meus Dados" },
]
{ href: "/patient/dashboard", icon: Home, label: "Dashboard" },
{
href: "/patient/schedule",
icon: CalendarClock,
label: "Agendar Consulta",
},
{
href: "/patient/appointments",
icon: CalendarCheck2,
label: "Minhas Consultas",
},
{ href: "/patient/reports", icon: ClipboardPlus, label: "Meus Laudos" },
{ href: "/patient/profile", icon: SquareUser, label: "Meus Dados" },
];
const doctorItems: MenuItem[] = [
{ href: "/doctor/dashboard", icon: Home, label: "Dashboard" },
{ href: "/doctor/medicos", icon: Users, label: "Gestão de Pacientes" },
{ href: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" },
{ href: "/doctor/disponibilidade", icon: ClipboardList, label: "Disponibilidade" },
]
{ href: "/doctor/dashboard", icon: Home, label: "Dashboard" },
{ href: "/doctor/medicos", icon: Users, label: "Gestão de Pacientes" },
{ href: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" },
{
href: "/doctor/disponibilidade",
icon: ClipboardList,
label: "Disponibilidade",
},
];
const secretaryItems: MenuItem[] = [
{ href: "/secretary/dashboard", icon: Home, label: "Dashboard" },
{ href: "/secretary/appointments", icon: CalendarCheck2, label: "Consultas" },
{ href: "/secretary/schedule", icon: CalendarClock, label: "Agendar Consulta" },
{ href: "/secretary/pacientes", icon: Users, label: "Gestão de Pacientes" },
]
{ href: "/secretary/dashboard", icon: Home, label: "Dashboard" },
{
href: "/secretary/appointments",
icon: CalendarCheck2,
label: "Consultas",
},
{
href: "/secretary/schedule",
icon: CalendarClock,
label: "Agendar Consulta",
},
{
href: "/secretary/pacientes",
icon: Users,
label: "Gestão de Pacientes",
},
];
const managerItems: MenuItem[] = [
{ href: "/manager/dashboard", icon: Home, label: "Dashboard" },
{ href: "#", icon: ClipboardMinus, label: "Relatórios gerenciais" },
{ href: "/manager/usuario", icon: Users, label: "Gestão de Usuários" },
{ href: "/manager/home", icon: Stethoscope, label: "Gestão de Médicos" },
{ href: "/manager/pacientes", icon: Users, label: "Gestão de Pacientes" },
{ href: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" }, //adicionar botão de voltar pra pagina anterior
]
{ href: "/manager/dashboard", icon: Home, label: "Dashboard" },
{ href: "#", icon: ClipboardMinus, label: "Relatórios gerenciais" },
{ href: "/manager/usuario", icon: Users, label: "Gestão de Usuários" },
{ href: "/manager/home", icon: Stethoscope, label: "Gestão de Médicos" },
{ href: "/manager/pacientes", icon: Users, label: "Gestão de Pacientes" },
{ href: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" }, //adicionar botão de voltar pra pagina anterior
];
let menuItems: MenuItem[];
switch (role) {
case "gestor":
menuItems = managerItems;
break;
case "admin":
menuItems = managerItems;
break;
case "medico":
menuItems = doctorItems;
break;
case "secretaria":
menuItems = secretaryItems;
break;
case "paciente":
menuItems = patientItems;
break;
default:
menuItems = patientItems;
break;
case "gestor":
menuItems = managerItems;
break;
case "admin":
menuItems = managerItems;
break;
case "medico":
menuItems = doctorItems;
break;
case "secretaria":
menuItems = secretaryItems;
break;
case "paciente":
menuItems = patientItems;
break;
default:
menuItems = patientItems;
break;
}
return menuItems;
}
};
const menuItems = SetMenuItems(role)
const menuItems = SetMenuItems(role);
if (!userData) {
return (
@ -207,9 +253,12 @@ export default function Sidebar({ children }: SidebarProps) {
<div className="p-4 border-b border-gray-200 flex items-center justify-between">
{!sidebarCollapsed && (
<div className="flex items-center gap-2">
<div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center">
<div className="w-4 h-4 bg-white rounded-sm"></div>
</div>
{/* 🛑 SUBSTITUIÇÃO: Usando a tag <img> com o caminho da logo */}
<img
src="/Logo MedConnect.png" // Use o arquivo da logo (ou /android-chrome-512x512.png)
alt="Logo MediConnect"
className="w-12 h-12 object-contain" // Define o tamanho para w-8 h-8 (32px)
/>
<span className="font-semibold text-gray-900">MedConnect</span>
</div>
)}
@ -250,11 +299,11 @@ export default function Sidebar({ children }: SidebarProps) {
})}
</nav>
<SidebarUserSection
userData={userData}
sidebarCollapsed={false}
handleLogout={handleLogout}
isActive={role === "paciente"? false: true}>
</SidebarUserSection>
userData={userData}
sidebarCollapsed={false}
handleLogout={handleLogout}
isActive={role === "paciente" ? false : true}
></SidebarUserSection>
</div>
<div