diff --git a/src/components/Header/Header.css b/src/components/Header/Header.css index f51e9310..9ff5a507 100644 --- a/src/components/Header/Header.css +++ b/src/components/Header/Header.css @@ -487,4 +487,15 @@ width: calc(100vw - 20px); max-width: none; } -} \ No newline at end of file +} + +/* permite que cliques "passem" através do header (exceto para os elementos interativos) */ +.header-container { + pointer-events: none; /* header não captura cliques */ +} + +/* mas permite que os controles no canto (telefone e profile) continuem clicáveis */ +.phone-icon-container, +.profile-section { + pointer-events: auto; +} diff --git a/src/components/Header/Header.jsx b/src/components/Header/Header.jsx index b1d28067..45b4d56a 100644 --- a/src/components/Header/Header.jsx +++ b/src/components/Header/Header.jsx @@ -1,8 +1,11 @@ +// src/components/Header/Header.jsx import React, { useState, useRef, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { createPortal } from 'react-dom'; +import { useNavigate, useLocation } from 'react-router-dom'; import './Header.css'; const Header = () => { + // --- Hooks (sempre chamados na mesma ordem) --- const [isDropdownOpen, setIsDropdownOpen] = useState(false); const [isSuporteCardOpen, setIsSuporteCardOpen] = useState(false); const [isChatOpen, setIsChatOpen] = useState(false); @@ -11,9 +14,11 @@ const Header = () => { const [showLogoutModal, setShowLogoutModal] = useState(false); const [avatarUrl, setAvatarUrl] = useState(null); const navigate = useNavigate(); + const location = useLocation(); const chatInputRef = useRef(null); const mensagensContainerRef = useRef(null); + // --- Efeitos --- useEffect(() => { const loadAvatar = () => { const localAvatar = localStorage.getItem('user_avatar'); @@ -44,7 +49,18 @@ const Header = () => { } }, [mensagens]); - // --- Logout --- + // Fecha modal com ESC (útil para logout) + useEffect(() => { + const onKey = (e) => { + if (e.key === 'Escape' && showLogoutModal) { + setShowLogoutModal(false); + } + }; + window.addEventListener('keydown', onKey); + return () => window.removeEventListener('keydown', onKey); + }, [showLogoutModal]); + + // --- Lógica e handlers --- const handleLogoutClick = () => { setShowLogoutModal(true); setIsDropdownOpen(false); @@ -65,26 +81,21 @@ const Header = () => { sessionStorage.getItem("authToken"); if (token) { - const response = await fetch( - "https://mock.apidog.com/m1/1053378-0-default/auth/v1/logout", - { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${token}`, - }, - } - ); - - if (response.status === 204) console.log("Logout realizado com sucesso"); - else if (response.status === 401) console.log("Token inválido ou expirado"); - else { - try { - const errorData = await response.json(); - console.error("Erro no logout:", errorData); - } catch { - console.error("Erro no logout - status:", response.status); - } + // tentativa de logout no backend (se houver) + 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) { + // não interrompe o fluxo se a API falhar — prosseguimos para limpar local + console.warn('Erro ao chamar endpoint de logout (ignorado):', err); } } @@ -105,12 +116,13 @@ const Header = () => { sessionStorage.removeItem(key); }); + // tenta limpar caches relacionados se existirem if (window.caches) { caches.keys().then(names => { names.forEach(name => { if (name.includes("auth") || name.includes("api")) caches.delete(name); }); - }); + }).catch(()=>{ /* ignore */ }); } }; @@ -157,7 +169,6 @@ const Header = () => { e.preventDefault(); if (mensagem.trim() === '') return; - // Mensagem do usuário const novaMensagemUsuario = { id: Date.now(), texto: mensagem, @@ -177,7 +188,6 @@ const Header = () => { const data = await response.json(); - // Resposta da IA const respostaSuporte = { id: Date.now() + 1, texto: data.resposta || data.reply || "Desculpe, não consegui processar sua pergunta no momento 😅", @@ -198,6 +208,7 @@ const Header = () => { } }; + // --- Subcomponentes --- const SuporteCard = () => (

Suporte

@@ -257,6 +268,82 @@ const Header = () => {
); + // --- Modal de logout renderizado via Portal (garante top-most e clique) --- + const LogoutModalPortal = ({ onCancel, onConfirm }) => { + const modalContent = ( +
+
e.stopPropagation()} + > +

Confirmar Logout

+

Tem certeza que deseja encerrar a sessão?

+
+ + +
+
+
+ ); + + // garante que exista document antes de criar portal (SSRed apps podem não ter) + if (typeof document === 'undefined') return null; + return createPortal(modalContent, document.body); + }; + + // --- Agora sim: condicional de render baseado na rota --- + if (location.pathname === '/login') { + return null; + } + + // --- JSX principal --- return (
@@ -282,22 +369,9 @@ const Header = () => {
- {/* Modal de Logout */} + {/* Modal de Logout via portal */} {showLogoutModal && ( -
-
-

Confirmar Logout

-

Tem certeza que deseja encerrar a sessão?

-
- - -
-
-
+ )} {isSuporteCardOpen && ( @@ -319,4 +393,4 @@ const Header = () => { ); }; -export default Header; \ No newline at end of file +export default Header;