Criação da tela/função de suporte

This commit is contained in:
Caio Miguel Lima Nunes 2025-10-14 19:04:07 -03:00
parent a01c62440e
commit 28f12b467c
2 changed files with 569 additions and 39 deletions

View File

@ -1,33 +1,31 @@
/* src/components/Header/Header.css */
.header-container { .header-container {
width: 100%; width: 100%;
position: absolute; /* Permite posicionamento livre sobre o conteúdo */ position: absolute;
top: 0; top: 0;
left: 0; left: 0;
display: flex; display: flex;
justify-content: flex-end; /* Alinha os elementos do container à direita */ justify-content: flex-end;
padding: 10px 20px; padding: 10px 20px;
box-sizing: border-box; box-sizing: border-box;
z-index: 1000; /* Garante que fique acima de outros elementos */ z-index: 1000;
} }
.right-corner-elements { .right-corner-elements {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 20px; /* Espaço entre o telefone e a seção de perfil */ gap: 20px;
} }
/* --- ÍCONE DE TELEFONE --- */
.phone-icon-container { .phone-icon-container {
font-size: 24px; font-size: 24px;
cursor: pointer; cursor: pointer;
padding: 5px; /* Área clicável um pouco maior */ padding: 5px;
} }
.phone-icon {
display: block; /* Garante que o emoji fique bem centralizado */ .phone-icon {
display: block;
} }
/* --- SEÇÃO DE PERFIL --- */
.profile-section { .profile-section {
position: relative; position: relative;
display: flex; display: flex;
@ -37,19 +35,18 @@
.profile-picture-container { .profile-picture-container {
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: 50%; /* Círculo */ border-radius: 50%;
overflow: hidden; overflow: hidden;
cursor: pointer; cursor: pointer;
border: 2px solid #ccc; /* Borda simples */ border: 2px solid #ccc;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.2); box-shadow: 0 0 5px rgba(0, 0, 0, 0.2);
} }
.profile-placeholder { .profile-placeholder {
width: 100%; width: 100%;
height: 100%; height: 100%;
background-color: #A9A9A9; /* Cor cinza escura para o fundo */ background-color: #A9A9A9;
border-radius: 50%; border-radius: 50%;
/* Adicionando um ícone simples de pessoa em branco para simular o ícone */
position: relative; position: relative;
} }
@ -64,13 +61,12 @@
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="%23FFFFFF" d="M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm-45.7 48C79.8 304 0 383.8 0 482.3c0 16.7 13.5 30.2 30.2 30.2h387.6c16.7 0 30.2-13.5 30.2-30.2 0-98.5-79.8-178.3-178.3-178.3h-45.7z"/></svg>'); background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="%23FFFFFF" d="M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm-45.7 48C79.8 304 0 383.8 0 482.3c0 16.7 13.5 30.2 30.2 30.2h387.6c16.7 0 30.2-13.5 30.2-30.2 0-98.5-79.8-178.3-178.3-178.3h-45.7z"/></svg>');
background-size: contain; background-size: contain;
background-repeat: no-repeat; background-repeat: no-repeat;
opacity: 0.8; /* Suaviza um pouco o ícone */ opacity: 0.8;
} }
/* --- DROPDOWN (MENU) --- */
.profile-dropdown { .profile-dropdown {
position: absolute; position: absolute;
top: 50px; /* Posição abaixo da foto de perfil (40px + 10px de espaço) */ top: 50px;
right: 0; right: 0;
background-color: white; background-color: white;
border: 1px solid #ddd; border: 1px solid #ddd;
@ -78,9 +74,9 @@
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
z-index: 10; z-index: 1000;
min-width: 150px; min-width: 150px;
overflow: hidden; /* Garante que bordas fiquem visíveis */ overflow: hidden;
} }
.dropdown-button { .dropdown-button {
@ -99,9 +95,311 @@
} }
.logout-button { .logout-button {
color: #cc0000; /* Cor vermelha para o botão de logout */ color: #cc0000;
} }
.logout-button:hover { .logout-button:hover {
background-color: #ffe0e0; background-color: #ffe0e0;
}
.suporte-card-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: flex-end;
align-items: flex-start;
z-index: 2000;
}
.suporte-card-container {
position: relative;
z-index: 2001;
margin-top: 80px;
margin-right: 20px;
}
.suporte-card {
background-color: white;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
padding: 2rem;
max-width: 400px;
width: 100%;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.suporte-titulo {
font-size: 1.5rem;
font-weight: bold;
color: #333;
margin-bottom: 0.5rem;
}
.suporte-subtitulo {
color: #666;
margin-bottom: 1.5rem;
line-height: 1.4;
}
.contato-item {
margin-bottom: 1.5rem;
padding: 1rem;
border-radius: 8px;
background-color: #f8f9fa;
transition: background-color 0.2s;
}
.contato-item:hover {
background-color: #e9ecef;
}
.contato-item.clickable {
cursor: pointer;
background-color: #e3f2fd;
}
.contato-item.clickable:hover {
background-color: #bbdefb;
}
.contato-info {
display: flex;
flex-direction: column;
}
.contato-nome {
font-weight: 600;
color: #333;
margin-bottom: 0.5rem;
font-size: 1rem;
}
.contato-descricao {
color: #666;
font-size: 0.95rem;
}
.contato-item:last-child {
margin-bottom: 0;
}
.chat-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: flex-end;
align-items: flex-start;
z-index: 3000;
}
.chat-container {
position: relative;
z-index: 3001;
margin-top: 80px;
margin-right: 20px;
}
.chat-online {
background-color: white;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
width: 350px;
height: 500px;
display: flex;
flex-direction: column;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.chat-header {
background-color: #1e3a8a;
color: white;
padding: 1rem;
border-radius: 12px 12px 0 0;
display: flex;
justify-content: space-between;
align-items: center;
}
.chat-titulo {
color: white;
margin: 0;
font-size: 1.1rem;
}
.fechar-chat {
background: none;
border: none;
color: white;
font-size: 1.5rem;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.fechar-chat:hover {
background-color: rgba(255, 255, 255, 0.2);
border-radius: 50%;
}
.chat-mensagens {
flex: 1;
padding: 1rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1rem;
}
.mensagem {
max-width: 80%;
padding: 0.75rem;
border-radius: 12px;
position: relative;
}
.mensagem.usuario {
align-self: flex-end;
background-color: #e3f2fd;
border-bottom-right-radius: 4px;
}
.mensagem.suporte {
align-self: flex-start;
background-color: #f5f5f5;
border-bottom-left-radius: 4px;
}
.mensagem-texto {
margin-bottom: 0.25rem;
word-wrap: break-word;
}
.mensagem-hora {
font-size: 0.7rem;
color: #666;
text-align: right;
}
.mensagem.suporte .mensagem-hora {
text-align: left;
}
.chat-input {
display: flex;
padding: 1rem;
border-top: 1px solid #e0e0e0;
gap: 0.5rem;
background-color: white;
border-radius: 0 0 12px 12px;
}
.chat-campo {
flex: 1;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 20px;
outline: none;
font-size: 0.9rem;
background-color: white;
}
.chat-campo:focus {
border-color: #1e3a8a;
}
.chat-enviar {
background-color: #1e3a8a;
color: white;
border: none;
padding: 0.75rem 1rem;
border-radius: 20px;
cursor: pointer;
font-size: 0.9rem;
}
.chat-enviar:hover {
background-color: #1e40af;
}
/* Modal de Logout */
.logout-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.logout-modal-content {
background-color: white;
padding: 2rem;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.2);
max-width: 400px;
width: 90%;
text-align: center;
}
.logout-modal-content h3 {
margin-bottom: 1rem;
color: #333;
font-size: 1.25rem;
}
.logout-modal-content p {
margin-bottom: 2rem;
color: #666;
line-height: 1.4;
}
.logout-modal-buttons {
display: flex;
gap: 1rem;
justify-content: center;
}
.logout-cancel-button {
padding: 0.75rem 1.5rem;
border: 1px solid #ccc;
border-radius: 8px;
background-color: transparent;
color: #333;
cursor: pointer;
font-size: 0.9rem;
transition: background-color 0.2s;
}
.logout-cancel-button:hover {
background-color: #f0f0f0;
}
.logout-confirm-button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
background-color: #dc3545;
color: white;
cursor: pointer;
font-size: 0.9rem;
transition: background-color 0.2s;
}
.logout-confirm-button:hover {
background-color: #c82333;
} }

View File

@ -1,58 +1,290 @@
// src/components/Header/Header.jsx import React, { useState, useRef, useEffect } from 'react';
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import './Header.css'; import './Header.css';
const Header = () => { const Header = () => {
const [isDropdownOpen, setIsDropdownOpen] = useState(false); 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 navigate = useNavigate(); const navigate = useNavigate();
const chatInputRef = useRef(null);
const mensagensContainerRef = useRef(null);
useEffect(() => {
if (isChatOpen && chatInputRef.current) {
chatInputRef.current.focus();
}
}, [isChatOpen]);
useEffect(() => {
if (mensagensContainerRef.current) {
mensagensContainerRef.current.scrollTop = mensagensContainerRef.current.scrollHeight;
}
}, [mensagens]);
// Funções de Logout (do seu código)
const handleLogoutClick = () => {
setShowLogoutModal(true);
setIsDropdownOpen(false);
};
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) {
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);
}
}
}
clearAuthData();
navigate("/login");
} catch (error) {
console.error("Erro durante logout:", error);
clearAuthData();
navigate("/login");
} finally {
setShowLogoutModal(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);
});
});
}
};
const handleLogoutCancel = () => setShowLogoutModal(false);
const handleProfileClick = () => { const handleProfileClick = () => {
setIsDropdownOpen(!isDropdownOpen); setIsDropdownOpen(!isDropdownOpen);
if (isSuporteCardOpen) setIsSuporteCardOpen(false);
if (isChatOpen) setIsChatOpen(false);
}; };
const handleViewProfile = () => { const handleViewProfile = () => {
// Redireciona para uma página de perfil (Rota que adicionaremos no App.js)
navigate('/perfil'); navigate('/perfil');
setIsDropdownOpen(false); setIsDropdownOpen(false);
}; };
const handleLogout = () => { const handleSuporteClick = () => {
// Ação de Logout: Exibe um alerta e redireciona para a tela de Login setIsSuporteCardOpen(!isSuporteCardOpen);
alert('Você foi desconectado. Executando ação de logout...'); if (isDropdownOpen) setIsDropdownOpen(false);
setIsDropdownOpen(false); if (isChatOpen) setIsChatOpen(false);
navigate('/login');
}; };
const handleSupportClick = () => { const handleCloseSuporteCard = () => {
// Funcionalidade de suporte (futuramente implementada em TelefoneSuporte) setIsSuporteCardOpen(false);
alert('Função de Suporte de Telefone em desenvolvimento.');
}; };
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]);
}, 1000);
};
const SuporteCard = () => (
<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={handleChatClick}>
<div className="contato-info">
<div className="contato-nome">Chat Online</div>
<div className="contato-descricao">Disponível 24/7</div>
</div>
</div>
</div>
);
const ChatOnline = () => (
<div className="chat-online">
<div className="chat-header">
<h3 className="chat-titulo">Chat de Suporte</h3>
<button type="button" className="fechar-chat" onClick={handleCloseChat}>×</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={handleEnviarMensagem}>
<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>
);
return ( return (
<div className="header-container"> <div className="header-container">
<div className="right-corner-elements"> <div className="right-corner-elements">
<div className="phone-icon-container" onClick={handleSuporteClick}>
{/* Ícone de Telefone */}
<div className="phone-icon-container" onClick={handleSupportClick}>
<span className="phone-icon" role="img" aria-label="telefone">📞</span> <span className="phone-icon" role="img" aria-label="telefone">📞</span>
</div> </div>
{/* Seção de Perfil com Dropdown */}
<div className="profile-section"> <div className="profile-section">
<div className="profile-picture-container" onClick={handleProfileClick}> <div className="profile-picture-container" onClick={handleProfileClick}>
{/* O div "profile-placeholder" simula a foto de perfil circular colorida */}
<div className="profile-placeholder"></div> <div className="profile-placeholder"></div>
</div> </div>
{isDropdownOpen && ( {isDropdownOpen && (
<div className="profile-dropdown"> <div className="profile-dropdown">
<button onClick={handleViewProfile} className="dropdown-button">Ver Perfil</button> <button type="button" onClick={handleViewProfile} className="dropdown-button">Ver Perfil</button>
<button onClick={handleLogout} className="dropdown-button logout-button">Sair (Logout)</button> <button type="button" onClick={handleLogoutClick} className="dropdown-button logout-button">Sair (Logout)</button>
</div> </div>
)} )}
</div> </div>
</div> </div>
{/* Modal de Logout */}
{showLogoutModal && (
<div className="logout-modal-overlay">
<div className="logout-modal-content">
<h3>Confirmar Logout</h3>
<p>Tem certeza que deseja encerrar a sessão?</p>
<div className="logout-modal-buttons">
<button onClick={handleLogoutCancel} className="logout-cancel-button">
Cancelar
</button>
<button onClick={handleLogoutConfirm} className="logout-confirm-button">
Sair
</button>
</div>
</div>
</div>
)}
{isSuporteCardOpen && (
<div className="suporte-card-overlay" onClick={handleCloseSuporteCard}>
<div className="suporte-card-container" onClick={(e) => e.stopPropagation()}>
<SuporteCard />
</div>
</div>
)}
{isChatOpen && (
<div className="chat-overlay">
<div className="chat-container">
<ChatOnline />
</div>
</div>
)}
</div> </div>
); );
}; };