416 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/components/Header/Header.jsx
import React, { useState, useRef, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { useNavigate, useLocation } from 'react-router-dom';
import './Header.css';
const Header = () => {
// --- hooks (sempre na mesma ordem) ---
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [isSuporteCardOpen, setIsSuporteCardOpen] = useState(false);
const [isChatOpen, setIsChatOpen] = useState(false);
const [mensagem, setMensagem] = useState('');
const [mensagens, setMensagens] = useState([]);
const [showLogoutModal, setShowLogoutModal] = useState(false);
const [avatarUrl, setAvatarUrl] = useState(null);
const navigate = useNavigate();
const location = useLocation();
const chatInputRef = useRef(null);
const mensagensContainerRef = useRef(null);
// foco quando abre chat
useEffect(() => {
if (isChatOpen && chatInputRef.current) {
chatInputRef.current.focus();
}
}, [isChatOpen]);
// scroll automático quando nova mensagem
useEffect(() => {
if (mensagensContainerRef.current) {
mensagensContainerRef.current.scrollTop = mensagensContainerRef.current.scrollHeight;
}
}, [mensagens]);
// carrega avatar se existir
useEffect(() => {
const loadAvatar = () => {
const localAvatar = localStorage.getItem('user_avatar');
if (localAvatar) setAvatarUrl(localAvatar);
};
loadAvatar();
const onStorage = () => loadAvatar();
window.addEventListener('storage', onStorage);
return () => window.removeEventListener('storage', onStorage);
}, []);
// ESC fecha qualquer overlay/portal aberto (logout / suporte / chat)
useEffect(() => {
const onKey = (e) => {
if (e.key === 'Escape') {
if (showLogoutModal) setShowLogoutModal(false);
if (isSuporteCardOpen) setIsSuporteCardOpen(false);
if (isChatOpen) setIsChatOpen(false);
}
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [showLogoutModal, isSuporteCardOpen, isChatOpen]);
// --- handlers logout (mantive comportamento) ---
const handleLogoutClick = () => {
setShowLogoutModal(true);
setIsDropdownOpen(false);
};
const clearAuthData = () => {
["token","authToken","userToken","access_token","user","auth","userData"].forEach(key => {
localStorage.removeItem(key);
sessionStorage.removeItem(key);
});
if (window.caches) {
caches.keys().then(names => {
names.forEach(name => {
if (name.includes("auth") || name.includes("api")) caches.delete(name);
});
}).catch(()=>{});
}
};
const handleLogoutConfirm = async () => {
try {
const token =
localStorage.getItem("token") ||
localStorage.getItem("authToken") ||
localStorage.getItem("userToken") ||
localStorage.getItem("access_token") ||
sessionStorage.getItem("token") ||
sessionStorage.getItem("authToken");
if (token) {
try {
await fetch("https://mock.apidog.com/m1/1053378-0-default/auth/v1/logout", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
} catch (err) {
// ignora erro de rede / endpoint — prossegue para limpar local
console.warn('logout endpoint error (ignored):', err);
}
}
clearAuthData();
navigate('/login');
} catch (err) {
console.error('Erro no logout:', err);
clearAuthData();
navigate('/login');
} finally {
setShowLogoutModal(false);
}
};
const handleLogoutCancel = () => setShowLogoutModal(false);
// --- profile / suporte / chat handlers ---
const handleProfileClick = () => {
setIsDropdownOpen(!isDropdownOpen);
if (isSuporteCardOpen) setIsSuporteCardOpen(false);
if (isChatOpen) setIsChatOpen(false);
};
const handleViewProfile = () => {
navigate('/perfil');
setIsDropdownOpen(false);
};
const handleSuporteClick = () => {
setIsSuporteCardOpen((s) => !s);
setIsDropdownOpen(false);
if (isChatOpen) setIsChatOpen(false);
};
const handleCloseSuporteCard = () => setIsSuporteCardOpen(false);
const handleChatClick = () => {
setIsChatOpen(true);
setIsSuporteCardOpen(false);
setMensagens([
{
id: 1,
texto: 'Olá! Bem-vindo ao suporte Mediconnect. Como podemos ajudar você hoje?',
remetente: 'suporte',
hora: new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })
}
]);
};
const handleCloseChat = () => {
setIsChatOpen(false);
setMensagem('');
};
const handleEnviarMensagem = (e) => {
e.preventDefault();
if (mensagem.trim() === '') return;
const novaMensagemUsuario = {
id: Date.now(),
texto: mensagem,
remetente: 'usuario',
hora: new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })
};
setMensagens(prev => [...prev, novaMensagemUsuario]);
setMensagem('');
setTimeout(() => {
if (chatInputRef.current) chatInputRef.current.focus();
}, 0);
setTimeout(() => {
const respostas = [
'Entendi sua dúvida. Vou verificar isso para você.',
'Obrigado pela informação. Estou analisando seu caso.',
'Pode me dar mais detalhes sobre o problema?',
'Já encaminhei sua solicitação para nossa equipe técnica.',
'Vou ajudar você a resolver isso!'
];
const respostaSuporte = {
id: Date.now() + 1,
texto: respostas[Math.floor(Math.random() * respostas.length)],
remetente: 'suporte',
hora: new Date().toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })
};
setMensagens(prev => [...prev, respostaSuporte]);
}, 900);
};
// --- subcomponentes (UI) ---
const SuporteCardContent = ({ onOpenChat }) => (
<div className="suporte-card">
<h2 className="suporte-titulo">Suporte</h2>
<p className="suporte-subtitulo">Entre em contato conosco através dos canais abaixo</p>
<div className="contato-item">
<div className="contato-info">
<div className="contato-nome">Email</div>
<div className="contato-descricao">suporte@mediconnect.com</div>
</div>
</div>
<div className="contato-item">
<div className="contato-info">
<div className="contato-nome">Telefone</div>
<div className="contato-descricao">(11) 3333-4444</div>
</div>
</div>
<div className="contato-item clickable" onClick={onOpenChat} role="button" tabIndex={0}>
<div className="contato-info">
<div className="contato-nome">Chat Online</div>
<div className="contato-descricao">Disponível 24/7</div>
</div>
</div>
</div>
);
const ChatOnlineContent = ({ mensagens, onSend, onClose }) => (
<div className="chat-online" role="dialog" aria-modal="true">
<div className="chat-header">
<h3 className="chat-titulo">Chat de Suporte</h3>
<button type="button" className="fechar-chat" onClick={onClose} aria-label="Fechar chat">×</button>
</div>
<div className="chat-mensagens" ref={mensagensContainerRef}>
{mensagens.map((msg) => (
<div key={msg.id} className={`mensagem ${msg.remetente}`}>
<div className="mensagem-texto">{msg.texto}</div>
<div className="mensagem-hora">{msg.hora}</div>
</div>
))}
</div>
<form className="chat-input" onSubmit={onSend}>
<input
ref={chatInputRef}
type="text"
value={mensagem}
onChange={(e) => setMensagem(e.target.value)}
placeholder="Digite sua mensagem..."
className="chat-campo"
autoFocus
/>
<button type="submit" className="chat-enviar">Enviar</button>
</form>
</div>
);
// --- portals: Logout / Suporte / Chat (garante top-most e clickable) ---
const PortalWrapper = ({ children, z = 99999 }) => {
if (typeof document === 'undefined') return null;
return createPortal(
<div style={{ position: 'fixed', inset: 0, zIndex: z }}>
{children}
</div>,
document.body
);
};
const LogoutModalPortal = ({ onCancel, onConfirm }) => {
if (typeof document === 'undefined') return null;
return createPortal(
<div
className="logout-modal-overlay"
style={{
position: 'fixed',
top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'rgba(0,0,0,0.5)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
zIndex: 110000
}}
onClick={onCancel}
>
<div
className="logout-modal-content"
style={{
backgroundColor: 'white',
padding: '1.6rem',
borderRadius: '12px',
boxShadow: '0 8px 24px rgba(0,0,0,0.2)',
maxWidth: '480px',
width: '100%',
textAlign: 'center'
}}
onClick={(e) => e.stopPropagation()}
>
<h3 style={{ marginTop: 0 }}>Confirmar Logout</h3>
<p>Tem certeza que deseja encerrar a sessão?</p>
<div style={{ display: 'flex', gap: '1rem', justifyContent: 'center', marginTop: '1rem' }}>
<button onClick={onCancel} className="logout-cancel-button">Cancelar</button>
<button onClick={onConfirm} className="logout-confirm-button">Sair</button>
</div>
</div>
</div>,
document.body
);
};
const SuportePortal = ({ onClose, children }) => {
if (typeof document === 'undefined') return null;
return createPortal(
<div
className="suporte-card-overlay"
style={{
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'transparent',
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'flex-start',
zIndex: 105000,
pointerEvents: 'auto'
}}
onClick={onClose}
>
<div
className="suporte-card-container"
style={{ marginTop: '80px', marginRight: '20px' }}
onClick={(e) => e.stopPropagation()}
>
{children}
</div>
</div>,
document.body
);
};
const ChatPortal = ({ onClose, children }) => {
if (typeof document === 'undefined') return null;
return createPortal(
<div
className="chat-overlay"
style={{
position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
backgroundColor: 'rgba(0,0,0,0.5)',
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'flex-start',
zIndex: 115000,
pointerEvents: 'auto'
}}
onClick={onClose}
>
<div
className="chat-container"
style={{ marginTop: '80px', marginRight: '20px' }}
onClick={(e) => e.stopPropagation()}
>
{children}
</div>
</div>,
document.body
);
};
// --- evita render na rota de login (mantendo hooks invocados) ---
if (location.pathname === '/login') {
return null;
}
// --- JSX principal (header visual) ---
return (
<div className="header-container" style={{ pointerEvents: 'auto' }}>
<div className="right-corner-elements">
<div
className="phone-icon-container"
onClick={handleSuporteClick}
role="button"
tabIndex={0}
style={{ pointerEvents: 'auto' }}
>
<span className="phone-icon" role="img" aria-label="telefone">📞</span>
</div>
<div className="profile-section" style={{ pointerEvents: 'auto' }}>
<div className="profile-picture-container" onClick={handleProfileClick} role="button" tabIndex={0}>
<div className="profile-placeholder"></div>
</div>
{isDropdownOpen && (
<div className="profile-dropdown" onClick={(e) => e.stopPropagation()}>
<button type="button" onClick={handleViewProfile} className="dropdown-button">Ver Perfil</button>
<button type="button" onClick={handleLogoutClick} className="dropdown-button logout-button">Sair (Logout)</button>
</div>
)}
</div>
</div>
{/* logout modal via portal */}
{showLogoutModal && <LogoutModalPortal onCancel={handleLogoutCancel} onConfirm={handleLogoutConfirm} />}
{/* suporte portal */}
{isSuporteCardOpen && (
<SuportePortal onClose={handleCloseSuporteCard}>
<SuporteCardContent onOpenChat={handleChatClick} />
</SuportePortal>
)}
{/* chat portal */}
{isChatOpen && (
<ChatPortal onClose={handleCloseChat}>
<ChatOnlineContent mensagens={mensagens} onSend={handleEnviarMensagem} onClose={handleCloseChat} />
</ChatPortal>
)}
</div>
);
};
export default Header;