forked from RiseUP/riseup-squad21
Merge pull request #17 from m1guelmcf/pagina-inicial-login
Alteracao da pagina inicial e na pagina de login
This commit is contained in:
commit
361a651412
@ -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>
|
||||||
|
|||||||
175
app/page.tsx
175
app/page.tsx
@ -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 há mais de 25 anos. Atendimento médico com
|
Excelência em saúde há 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",
|
||||||
</div>
|
desc: "Cuidado gentil e especializado para garantir a saúde e o desenvolvimento de crianças e adolescentes.",
|
||||||
<div className="p-6 bg-background rounded-xl shadow hover:shadow-lg transition">
|
Icon: Baby,
|
||||||
<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
|
title: "Exames",
|
||||||
desenvolvimento de crianças e adolescentes.
|
desc: "Resultados rápidos e precisos em exames laboratoriais e de imagem essenciais para seu diagnóstico.",
|
||||||
</p>
|
Icon: Microscope,
|
||||||
<Button className="mt-4 w-full">Agendar</Button>
|
},
|
||||||
</div>
|
].map(({ title, desc, Icon }, index) => (
|
||||||
<div className="p-6 bg-background rounded-xl shadow hover:shadow-lg transition">
|
<div
|
||||||
<h3 className="text-xl font-semibold text-primary">Exames</h3>
|
key={index}
|
||||||
<p className="text-muted-foreground mt-2 text-sm">
|
className="p-8 bg-white rounded-2xl shadow-md hover:shadow-xl transition-all duration-300 border border-[#E0E9FF] group"
|
||||||
Resultados rápidos e precisos em exames laboratoriais e de imagem
|
>
|
||||||
essenciais para seu diagnóstico.
|
<div className="flex items-center space-x-3">
|
||||||
</p>
|
<Icon className="text-[#007BFF] w-6 h-6 group-hover:scale-110 transition-transform" />
|
||||||
<Button className="mt-4 w-full">Agendar</Button>
|
<h3 className="text-xl font-semibold">{title}</h3>
|
||||||
</div>
|
</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>
|
</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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,138 +16,215 @@ import { Eye, EyeOff, Mail, Lock, Loader2 } from "lucide-react";
|
|||||||
import { usersService } from "@/services/usersApi.mjs";
|
import { usersService } from "@/services/usersApi.mjs";
|
||||||
|
|
||||||
interface LoginFormProps {
|
interface LoginFormProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FormState {
|
interface FormState {
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LoginForm({ children }: LoginFormProps) {
|
export function LoginForm({ children }: LoginFormProps) {
|
||||||
const [form, setForm] = useState<FormState>({ email: "", password: "" });
|
const [form, setForm] = useState<FormState>({ email: "", password: "" });
|
||||||
const [showPassword, setShowPassword] = useState(false);
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
|
|
||||||
const [userRoles, setUserRoles] = useState<string[]>([]);
|
const [userRoles, setUserRoles] = useState<string[]>([]);
|
||||||
const [authenticatedUser, setAuthenticatedUser] = useState<any>(null);
|
const [authenticatedUser, setAuthenticatedUser] = useState<any>(null);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* --- NOVA FUNÇÃO ---
|
* --- NOVA FUNÇÃO ---
|
||||||
* Finaliza o login com o perfil de dashboard escolhido e redireciona.
|
* Finaliza o login com o perfil de dashboard escolhido e redireciona.
|
||||||
*/
|
*/
|
||||||
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({
|
||||||
setUserRoles([]);
|
title: "Erro de Sessão",
|
||||||
return;
|
description:
|
||||||
}
|
"Não foi possível encontrar os dados do usuário. Tente novamente.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
setUserRoles([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
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 = {
|
||||||
localStorage.setItem("user_info", JSON.stringify(completeUserInfo));
|
...user,
|
||||||
|
user_metadata: { ...user.user_metadata, role: roleInLowerCase },
|
||||||
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" });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
localStorage.setItem("user_info", JSON.stringify(completeUserInfo));
|
||||||
|
|
||||||
const handleSubmit = async (e: React.FormEvent) => {
|
let redirectPath = "";
|
||||||
e.preventDefault();
|
switch (selectedDashboardRole) {
|
||||||
setIsLoading(true);
|
case "gestor":
|
||||||
localStorage.removeItem("token");
|
redirectPath = "/manager/dashboard";
|
||||||
localStorage.removeItem("user_info");
|
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 {
|
if (redirectPath) {
|
||||||
const authData = await login(form.email, form.password);
|
toast({ title: `Entrando como ${selectedDashboardRole}...` });
|
||||||
const user = authData.user;
|
router.push(redirectPath);
|
||||||
if (!user || !user.id) {
|
} else {
|
||||||
throw new Error("Resposta de autenticação inválida.");
|
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()
|
try {
|
||||||
console.log(me.roles)
|
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) {
|
const rolesData = await api.get(
|
||||||
throw new Error("Nenhum perfil de acesso foi encontrado para este usuário.");
|
`/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) {
|
if (!me.roles || me.roles.length === 0) {
|
||||||
localStorage.removeItem("token");
|
throw new Error(
|
||||||
localStorage.removeItem("user_info");
|
"Nenhum perfil de acesso foi encontrado para este usuário."
|
||||||
toast({
|
);
|
||||||
title: "Erro no Login",
|
}
|
||||||
description: error instanceof Error ? error.message : "Ocorreu um erro inesperado.",
|
|
||||||
variant: "destructive",
|
|
||||||
});
|
|
||||||
setIsLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Estado para guardar os botões de seleção de perfil
|
handleRoleSelection(me.roles[0], user);
|
||||||
const [roleSelectionUI, setRoleSelectionUI] = useState<React.ReactNode | null>(null);
|
} 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 (
|
// Estado para guardar os botões de seleção de perfil
|
||||||
<Card className="w-full bg-transparent border-0 shadow-none">
|
const [roleSelectionUI, setRoleSelectionUI] =
|
||||||
<CardContent className="p-0">
|
useState<React.ReactNode | null>(null);
|
||||||
{!roleSelectionUI ? (
|
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
return (
|
||||||
<div className="space-y-2">
|
<Card className="w-full bg-transparent border-0 shadow-none">
|
||||||
<Label htmlFor="email">E-mail</Label>
|
<CardContent className="p-0">
|
||||||
<div className="relative">
|
{!roleSelectionUI ? (
|
||||||
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
<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 className="space-y-2">
|
||||||
</div>
|
<Label htmlFor="email">E-mail</Label>
|
||||||
</div>
|
<div className="relative">
|
||||||
<div className="space-y-2">
|
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
||||||
<Label htmlFor="password">Senha</Label>
|
<Input
|
||||||
<div className="relative">
|
id="email"
|
||||||
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
type="email"
|
||||||
<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" />
|
placeholder="seu.email@exemplo.com"
|
||||||
<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}>
|
value={form.email}
|
||||||
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
onChange={(e) => setForm({ ...form, email: e.target.value })}
|
||||||
</button>
|
className="pl-10 h-11 focus-visible:ring-blue-600 focus-visible:ring-2"
|
||||||
</div>
|
required
|
||||||
</div>
|
disabled={isLoading}
|
||||||
<Button type="submit" className="w-full h-11 text-base font-semibold" disabled={isLoading}>
|
autoComplete="username"
|
||||||
{isLoading ? <Loader2 className="w-5 h-5 animate-spin" /> : "Entrar"}
|
/>
|
||||||
</Button>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
) : (
|
<div className="space-y-2">
|
||||||
<div className="space-y-4 animate-in fade-in-50">
|
<Label htmlFor="password">Senha</Label>
|
||||||
<h3 className="text-lg font-medium text-center text-foreground">Você tem múltiplos perfis</h3>
|
<div className="relative">
|
||||||
<p className="text-sm text-muted-foreground text-center">Selecione com qual perfil deseja entrar:</p>
|
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 text-muted-foreground w-5 h-5" />
|
||||||
<div className="flex flex-col space-y-3 pt-2">
|
<Input
|
||||||
{userRoles.map((role) => (
|
id="password"
|
||||||
<Button key={role} variant="outline" className="h-11 text-base" onClick={() => handleRoleSelection(role, authenticatedUser)}>
|
type={showPassword ? "text" : "password"}
|
||||||
Entrar como: {role.charAt(0).toUpperCase() + role.slice(1)}
|
placeholder="Digite sua senha"
|
||||||
</Button>
|
value={form.password}
|
||||||
))}
|
onChange={(e) =>
|
||||||
</div>
|
setForm({ ...form, password: e.target.value })
|
||||||
</div>
|
}
|
||||||
)}
|
className="pl-10 pr-12 h-11 focus-visible:ring-blue-600 focus-visible:ring-2"
|
||||||
{children}
|
required
|
||||||
</CardContent>
|
disabled={isLoading}
|
||||||
</Card>
|
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