416 lines
13 KiB
JavaScript
416 lines
13 KiB
JavaScript
//Nesta página falta: ajustar caminho do CSS
|
||
|
||
import { 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; |