Identidade visual
This commit is contained in:
parent
74ad727ec4
commit
40e6746f84
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user