guisilvagomes 9ec07aeea3 Update
2025-10-09 10:09:35 -03:00

789 lines
28 KiB
TypeScript

import React, { useState } from "react";
import { User, Mail, Lock } from "lucide-react";
import toast from "react-hot-toast";
import { useNavigate } from "react-router-dom";
import { useAuth } from "../hooks/useAuth";
const LoginPaciente: React.FC = () => {
const [formData, setFormData] = useState({
email: "",
senha: "",
});
const [loading, setLoading] = useState(false);
const [showCadastro, setShowCadastro] = useState(false);
const [cadastroData, setCadastroData] = useState({
nome: "",
email: "",
senha: "",
confirmarSenha: "",
telefone: "",
cpf: "",
dataNascimento: "",
convenio: "",
altura: "",
peso: "",
cep: "",
logradouro: "",
bairro: "",
cidade: "",
estado: "",
});
// Função para buscar endereço pelo CEP
const buscarEnderecoPorCEP = async (cep: string) => {
if (!cep || cep.replace(/\D/g, "").length < 8) return;
try {
const response = await fetch(
`https://viacep.com.br/ws/${cep.replace(/\D/g, "")}/json/`
);
const data = await response.json();
if (data.erro) {
toast.error("CEP não encontrado");
return;
}
setCadastroData((prev) => ({
...prev,
logradouro: data.logradouro || "",
bairro: data.bairro || "",
cidade: data.localidade || "",
estado: data.uf || "",
}));
} catch {
toast.error("Erro ao buscar CEP");
}
};
const navigate = useNavigate();
const { loginPaciente } = useAuth();
// Credenciais fixas para LOGIN LOCAL de paciente
const LOCAL_PATIENT = {
email: "pedro.araujo@mediconnect.com",
senha: "local123",
nome: "Pedro Araujo",
id: "pedro.araujo@mediconnect.com",
} as const;
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
try {
console.log("[LoginPaciente] Fazendo login com email:", formData.email);
// Fazer login via API Supabase
const authService = (await import("../services/authService")).default;
const loginResult = await authService.login({
email: formData.email,
password: formData.senha,
});
if (!loginResult.success) {
console.log("[LoginPaciente] Erro no login:", loginResult.error);
toast.error(loginResult.error || "Email ou senha incorretos");
setLoading(false);
return;
}
console.log("[LoginPaciente] Login bem-sucedido!", loginResult.data);
// Verificar se o token foi salvo
const tokenStore = (await import("../services/tokenStore")).default;
const token = tokenStore.getAccessToken();
const refreshToken = tokenStore.getRefreshToken();
console.log("[LoginPaciente] Token salvo:", token ? "SIM" : "NÃO");
console.log(
"[LoginPaciente] Refresh token salvo:",
refreshToken ? "SIM" : "NÃO"
);
if (!token) {
console.error(
"[LoginPaciente] Token não foi salvo! Dados do login:",
loginResult.data
);
toast.error("Erro ao salvar credenciais de autenticação");
setLoading(false);
return;
}
// Buscar dados do paciente da API
const { listPatients } = await import("../services/pacienteService");
const pacientesResult = await listPatients({ search: formData.email });
console.log(
"[LoginPaciente] Resultado da busca de pacientes:",
pacientesResult
);
const paciente = pacientesResult.data?.[0];
if (paciente) {
console.log("[LoginPaciente] Paciente encontrado:", {
id: paciente.id,
nome: paciente.nome,
email: paciente.email,
});
const ok = await loginPaciente({
id: paciente.id,
nome: paciente.nome,
email: paciente.email,
});
if (ok) {
console.log("[LoginPaciente] Navegando para /acompanhamento");
navigate("/acompanhamento");
} else {
console.error("[LoginPaciente] loginPaciente retornou false");
toast.error("Erro ao processar login");
}
} else {
console.log("[LoginPaciente] Paciente não encontrado na lista");
toast.error(
"Dados do paciente não encontrados. Entre em contato com o suporte."
);
}
} catch (error) {
console.error("[LoginPaciente] Erro no login:", error);
toast.error("Erro ao fazer login. Tente novamente.");
} finally {
setLoading(false);
}
};
const handleCadastro = async (e: React.FormEvent) => {
e.preventDefault();
// Redirecionar para a página de cadastro dedicada
navigate("/cadastro-paciente");
};
// Login LOCAL: cria uma sessão de paciente sem chamar a API
const handleLoginLocal = async () => {
const email = formData.email.trim();
const senha = formData.senha;
console.log("[LoginPaciente] Login local - tentando com API primeiro");
// Tentar fazer login via API mesmo no modo "local"
setLoading(true);
try {
// Fazer login via API Supabase
const authService = (await import("../services/authService")).default;
const loginResult = await authService.login({
email: email,
password: senha,
});
if (!loginResult.success) {
console.log(
"[LoginPaciente] Login via API falhou, usando modo local sem token"
);
console.log("[LoginPaciente] Erro:", loginResult.error);
// Fallback: validar credenciais locais hardcoded
if (email !== LOCAL_PATIENT.email || senha !== LOCAL_PATIENT.senha) {
toast.error("Credenciais inválidas");
setLoading(false);
return;
}
// Login local SEM token (modo de desenvolvimento)
toast(
"⚠️ Modo local ativo: algumas funcionalidades podem não funcionar sem API",
{
icon: "⚠️",
duration: 5000,
}
);
const ok = await loginPaciente({
id: LOCAL_PATIENT.id,
nome: LOCAL_PATIENT.nome,
email: LOCAL_PATIENT.email,
});
if (ok) {
navigate("/acompanhamento");
} else {
toast.error("Não foi possível iniciar a sessão local");
}
setLoading(false);
return;
}
console.log("[LoginPaciente] Login via API bem-sucedido!");
// Verificar se o token foi salvo
const tokenStore = (await import("../services/tokenStore")).default;
const token = tokenStore.getAccessToken();
console.log("[LoginPaciente] Token salvo:", token ? "SIM" : "NÃO");
// Buscar dados do paciente da API
const { listPatients } = await import("../services/pacienteService");
const pacientesResult = await listPatients({ search: email });
const paciente = pacientesResult.data?.[0];
if (paciente) {
console.log(
"[LoginPaciente] Paciente encontrado na API:",
paciente.nome
);
const ok = await loginPaciente({
id: paciente.id,
nome: paciente.nome,
email: paciente.email,
});
if (ok) {
navigate("/acompanhamento");
} else {
toast.error("Erro ao processar login");
}
} else {
console.log(
"[LoginPaciente] Paciente não encontrado na API, usando dados locais"
);
const ok = await loginPaciente({
id: email,
nome: email.split("@")[0],
email: email,
});
if (ok) {
navigate("/acompanhamento");
} else {
toast.error("Erro ao processar login");
}
}
} catch (err) {
console.error("[LoginPaciente] Erro no login:", err);
toast.error("Erro ao fazer login");
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-white dark:from-gray-900 dark:to-gray-950 flex items-center justify-center p-4 transition-colors">
<div className="max-w-md w-full">
{/* Header */}
<div className="text-center mb-8">
<div className="bg-gradient-to-r from-blue-700 to-blue-400 dark:from-blue-800 dark:to-blue-500 w-16 h-16 rounded-full flex items-center justify-center mx-auto mb-4 shadow-md">
<User className="w-8 h-8 text-white" />
</div>
<h1 className="text-3xl font-bold text-gray-900 dark:text-gray-100 mb-2">
{showCadastro ? "Criar Conta" : "Área do Paciente"}
</h1>
<p className="text-gray-600 dark:text-gray-400">
{showCadastro
? "Preencha seus dados para criar sua conta"
: "Faça login para acompanhar suas consultas"}
</p>
</div>
{/* Formulário */}
<div
className="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-8 border border-transparent dark:border-gray-700 transition-colors"
aria-live="polite"
>
{!showCadastro ? (
/* Formulário de Login */
<form onSubmit={handleLogin} className="space-y-6" noValidate>
<div>
<label
htmlFor="login_email"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
>
Email
</label>
<div className="relative">
<Mail className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
<input
id="login_email"
type="email"
value={formData.email}
onChange={(e) =>
setFormData((prev) => ({
...prev,
email: e.target.value,
}))
}
className="form-input pl-10 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
placeholder="seu@email.com"
required
autoComplete="email"
/>
</div>
</div>
<div>
<label
htmlFor="login_password"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2"
>
Senha
</label>
<div className="relative">
<Lock className="absolute left-3 top-1/2 transform -translate-y-1/2 w-5 h-5 text-gray-400" />
<input
id="login_password"
type="password"
value={formData.senha}
onChange={(e) =>
setFormData((prev) => ({
...prev,
senha: e.target.value,
}))
}
className="form-input pl-10 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
placeholder="Sua senha"
required
autoComplete="current-password"
/>
</div>
</div>
{/** Botão original (remoto) comentado a pedido **/}
{/**
<button
type="submit"
disabled={loading}
className="w-full bg-gradient-to-r from-blue-700 to-blue-400 text-white py-3 px-4 rounded-lg font-medium hover:from-blue-800 hover:to-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
>
{loading ? "Entrando..." : "Entrar"}
</button>
**/}
<button
type="button"
onClick={handleLoginLocal}
disabled={loading}
className="w-full bg-gradient-to-r from-blue-700 to-blue-400 text-white py-3 px-4 rounded-lg font-medium hover:from-blue-800 hover:to-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
>
{loading ? "Entrando..." : "Entrar"}
</button>
<p className="mt-1 text-xs text-gray-500 dark:text-gray-400 text-center">
Credenciais locais: <strong>{LOCAL_PATIENT.email}</strong> /
<strong> {LOCAL_PATIENT.senha}</strong>
</p>
</form>
) : (
/* Formulário de Cadastro */
<form onSubmit={handleCadastro} className="space-y-4" noValidate>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label
htmlFor="cad_nome"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Nome Completo
</label>
<input
id="cad_nome"
type="text"
value={cadastroData.nome}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
nome: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
required
autoComplete="name"
/>
</div>
<div>
<label
htmlFor="cad_cpf"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
CPF
</label>
<input
id="cad_cpf"
type="text"
value={cadastroData.cpf}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
cpf: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
placeholder="000.000.000-00"
required
inputMode="numeric"
pattern="^\d{3}\.\d{3}\.\d{3}-\d{2}$|^\d{11}$"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label
htmlFor="cad_cep"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
CEP
</label>
<input
id="cad_cep"
type="text"
value={cadastroData.cep}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
cep: e.target.value,
}))
}
onBlur={() => buscarEnderecoPorCEP(cadastroData.cep)}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
placeholder="00000-000"
required
inputMode="numeric"
pattern="^\d{5}-?\d{3}$"
autoComplete="postal-code"
/>
</div>
<div>
<label
htmlFor="cad_logradouro"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Logradouro
</label>
<input
id="cad_logradouro"
type="text"
value={cadastroData.logradouro}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
logradouro: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
required
autoComplete="address-line1"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label
htmlFor="cad_bairro"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Bairro
</label>
<input
id="cad_bairro"
type="text"
value={cadastroData.bairro}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
bairro: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
required
autoComplete="address-line2"
/>
</div>
<div>
<label
htmlFor="cad_cidade"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Cidade
</label>
<input
id="cad_cidade"
type="text"
value={cadastroData.cidade}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
cidade: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
required
autoComplete="address-level2"
/>
</div>
</div>
<div>
<label
htmlFor="cad_estado"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Estado
</label>
<input
id="cad_estado"
type="text"
value={cadastroData.estado}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
estado: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
required
autoComplete="address-level1"
/>
</div>
<div>
<label
htmlFor="cad_email"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Email
</label>
<input
id="cad_email"
type="email"
value={cadastroData.email}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
email: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
required
autoComplete="email"
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label
htmlFor="cad_senha"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Senha
</label>
<input
id="cad_senha"
type="password"
value={cadastroData.senha}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
senha: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
minLength={6}
required
autoComplete="new-password"
/>
</div>
<div>
<label
htmlFor="cad_confirma_senha"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Confirmar Senha
</label>
<input
id="cad_confirma_senha"
type="password"
value={cadastroData.confirmarSenha}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
confirmarSenha: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
required
autoComplete="new-password"
aria-invalid={
cadastroData.confirmarSenha !== "" &&
cadastroData.confirmarSenha !== cadastroData.senha
}
aria-describedby={
cadastroData.confirmarSenha !== "" &&
cadastroData.confirmarSenha !== cadastroData.senha
? "cad_senha_help"
: undefined
}
/>
{cadastroData.confirmarSenha !== "" &&
cadastroData.confirmarSenha !== cadastroData.senha && (
<p
id="cad_senha_help"
className="mt-1 text-xs text-red-400"
>
As senhas não coincidem.
</p>
)}
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label
htmlFor="cad_telefone"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Telefone
</label>
<input
id="cad_telefone"
type="tel"
value={cadastroData.telefone}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
telefone: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
placeholder="(11) 99999-9999"
required
inputMode="numeric"
pattern="^\(?\d{2}\)?\s?9?\d{4}-?\d{4}$"
autoComplete="tel"
/>
</div>
<div>
<label
htmlFor="cad_data_nasc"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Data de Nascimento
</label>
<input
id="cad_data_nasc"
type="date"
value={cadastroData.dataNascimento}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
dataNascimento: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
required
autoComplete="bday"
/>
</div>
</div>
<div>
<label
htmlFor="cad_convenio"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Convênio
</label>
<select
id="cad_convenio"
value={cadastroData.convenio}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
convenio: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
>
<option value="">Selecione</option>
<option value="Particular">Particular</option>
<option value="Unimed">Unimed</option>
<option value="Bradesco Saúde">Bradesco Saúde</option>
<option value="SulAmérica">SulAmérica</option>
<option value="Amil">Amil</option>
<option value="NotreDame">NotreDame</option>
</select>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<label
htmlFor="cad_altura"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Altura (cm)
</label>
<input
id="cad_altura"
type="number"
value={cadastroData.altura}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
altura: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
placeholder="170"
min="50"
max="250"
/>
</div>
<div>
<label
htmlFor="cad_peso"
className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1"
>
Peso (kg)
</label>
<input
id="cad_peso"
type="number"
value={cadastroData.peso}
onChange={(e) =>
setCadastroData((prev) => ({
...prev,
peso: e.target.value,
}))
}
className="form-input dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-100"
placeholder="70"
min="20"
max="300"
step="0.1"
/>
</div>
</div>
<div className="flex space-x-4">
<button
type="button"
onClick={() => setShowCadastro(false)}
className="flex-1 bg-gray-100 text-gray-700 py-3 px-4 rounded-lg font-medium hover:bg-gray-200 transition-colors"
>
Voltar
</button>
<button
type="submit"
disabled={loading}
className="flex-1 bg-gradient-to-r from-blue-700 to-blue-400 text-white py-3 px-4 rounded-lg font-medium hover:from-blue-800 hover:to-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-all"
>
{loading ? "Cadastrando..." : "Cadastrar"}
</button>
</div>
</form>
)}
</div>
</div>
</div>
);
};
export default LoginPaciente;