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

View File

@ -12,8 +12,33 @@ import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import {
import { Search, Bell, Calendar, User, LogOut, ChevronLeft, ChevronRight, Home, CalendarCheck2, ClipboardPlus, SquareUserRound, CalendarClock, Users, SquareUser, ClipboardList, Stethoscope, ClipboardMinus } from "lucide-react"; 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"; import SidebarUserSection from "@/components/ui/userToolTip";
interface UserData { interface UserData {
@ -38,7 +63,6 @@ interface UserData {
is_anonymous: boolean; is_anonymous: boolean;
} }
interface MenuItem { interface MenuItem {
href: string; href: string;
icon: React.ElementType; icon: React.ElementType;
@ -87,7 +111,7 @@ export default function Sidebar({ children }: SidebarProps) {
})) ?? [], })) ?? [],
is_anonymous: userInfo.is_anonymous ?? false, is_anonymous: userInfo.is_anonymous ?? false,
}); });
setRole(userInfo.user_metadata?.role) setRole(userInfo.user_metadata?.role);
} else { } else {
// O redirecionamento para /login já estava correto. Ótimo! // O redirecionamento para /login já estava correto. Ótimo!
router.push("/login"); router.push("/login");
@ -95,8 +119,6 @@ export default function Sidebar({ children }: SidebarProps) {
}, [router]); }, [router]);
useEffect(() => { useEffect(() => {
const handleResize = () => { const handleResize = () => {
if (window.innerWidth < 1024) { if (window.innerWidth < 1024) {
setSidebarCollapsed(true); setSidebarCollapsed(true);
@ -134,25 +156,49 @@ export default function Sidebar({ children }: SidebarProps) {
const SetMenuItems = (role: any) => { const SetMenuItems = (role: any) => {
const patientItems: MenuItem[] = [ const patientItems: MenuItem[] = [
{ href: "/patient/dashboard", icon: Home, label: "Dashboard" }, { 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/schedule",
icon: CalendarClock,
label: "Agendar Consulta",
},
{
href: "/patient/appointments",
icon: CalendarCheck2,
label: "Minhas Consultas",
},
{ href: "/patient/reports", icon: ClipboardPlus, label: "Meus Laudos" }, { href: "/patient/reports", icon: ClipboardPlus, label: "Meus Laudos" },
{ href: "/patient/profile", icon: SquareUser, label: "Meus Dados" }, { href: "/patient/profile", icon: SquareUser, label: "Meus Dados" },
] ];
const doctorItems: MenuItem[] = [ const doctorItems: MenuItem[] = [
{ href: "/doctor/dashboard", icon: Home, label: "Dashboard" }, { href: "/doctor/dashboard", icon: Home, label: "Dashboard" },
{ href: "/doctor/medicos", icon: Users, label: "Gestão de Pacientes" }, { href: "/doctor/medicos", icon: Users, label: "Gestão de Pacientes" },
{ href: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" }, { href: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" },
{ href: "/doctor/disponibilidade", icon: ClipboardList, label: "Disponibilidade" }, {
] href: "/doctor/disponibilidade",
icon: ClipboardList,
label: "Disponibilidade",
},
];
const secretaryItems: MenuItem[] = [ const secretaryItems: MenuItem[] = [
{ href: "/secretary/dashboard", icon: Home, label: "Dashboard" }, { href: "/secretary/dashboard", icon: Home, label: "Dashboard" },
{ href: "/secretary/appointments", icon: CalendarCheck2, label: "Consultas" }, {
{ href: "/secretary/schedule", icon: CalendarClock, label: "Agendar Consulta" }, href: "/secretary/appointments",
{ href: "/secretary/pacientes", icon: Users, label: "Gestão de Pacientes" }, 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[] = [ const managerItems: MenuItem[] = [
{ href: "/manager/dashboard", icon: Home, label: "Dashboard" }, { href: "/manager/dashboard", icon: Home, label: "Dashboard" },
@ -161,7 +207,7 @@ export default function Sidebar({ children }: SidebarProps) {
{ href: "/manager/home", icon: Stethoscope, label: "Gestão de Médicos" }, { href: "/manager/home", icon: Stethoscope, label: "Gestão de Médicos" },
{ href: "/manager/pacientes", icon: Users, label: "Gestão de Pacientes" }, { 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: "/doctor/consultas", icon: CalendarCheck2, label: "Consultas" }, //adicionar botão de voltar pra pagina anterior
] ];
let menuItems: MenuItem[]; let menuItems: MenuItem[];
switch (role) { switch (role) {
@ -185,9 +231,9 @@ export default function Sidebar({ children }: SidebarProps) {
break; break;
} }
return menuItems; return menuItems;
} };
const menuItems = SetMenuItems(role) const menuItems = SetMenuItems(role);
if (!userData) { if (!userData) {
return ( 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"> <div className="p-4 border-b border-gray-200 flex items-center justify-between">
{!sidebarCollapsed && ( {!sidebarCollapsed && (
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="w-8 h-8 bg-blue-600 rounded-lg flex items-center justify-center"> {/* 🛑 SUBSTITUIÇÃO: Usando a tag <img> com o caminho da logo */}
<div className="w-4 h-4 bg-white rounded-sm"></div> <img
</div> 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> <span className="font-semibold text-gray-900">MedConnect</span>
</div> </div>
)} )}
@ -253,8 +302,8 @@ export default function Sidebar({ children }: SidebarProps) {
userData={userData} userData={userData}
sidebarCollapsed={false} sidebarCollapsed={false}
handleLogout={handleLogout} handleLogout={handleLogout}
isActive={role === "paciente"? false: true}> isActive={role === "paciente" ? false : true}
</SidebarUserSection> ></SidebarUserSection>
</div> </div>
<div <div