docs: adiciona estrutura de governança por contexto (AGENT, DOMAIN, DESIGN_SYSTEM, TASKS)

This commit is contained in:
Tiago Ribeiro 2026-03-07 17:31:44 -03:00
parent 722d7bf82a
commit ebbbda5328
2 changed files with 102 additions and 74 deletions

28
frontend/DESIGN_SYSTEM.md Normal file
View file

@ -0,0 +1,28 @@
# SaveInMed - Design System
Diretrizes visuais para o frontend do SaveInMed.
## 🎨 Paleta de Cores
- **Primária**: `blue-600` (#2563EB) - Botões principais, links ativos.
- **Sucesso**: `green-600` (#16A34A) - Confirmações, status "Entregue".
- **Aviso**: `yellow-500` (#EAB308) - Alertas, status "Pendente".
- **Erro**: `red-600` (#DC2626) - Botões de deletar, mensagens de erro.
- **Fundo**: `gray-50` (#F9FAFB) - Background das páginas.
## 🔡 Tipografia
- **Títulos**: Inter ou Sans-serif, font-bold, text-gray-900.
- **Corpo**: Inter, text-gray-600.
## 🧩 Componentes Padrão
- **Botões**:
- `bg-blue-600 hover:bg-blue-700 text-white rounded-lg px-4 py-2 transition-colors`
- **Inputs**:
- `border border-gray-300 focus:ring-2 focus:ring-blue-500 rounded-lg`
- **Cards**:
- `bg-white shadow-sm border border-gray-100 rounded-xl p-6`
## 📦 Ícones
- **Biblioteca**: `Heroicons 24/outline`
- **Home**: `HomeIcon`
- **Carrinho**: `ShoppingCartIcon`
- **Usuário**: `UserIcon`

View file

@ -15,21 +15,21 @@ const LoginPageContent = () => {
const { setEmpresaId } = useEmpresa(); const { setEmpresaId } = useEmpresa();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
// Estados do formulÃÆ’ƒÂ¡rio // Estados do formulário
const [email, setEmail] = useState<string>(""); // Email do usuÃÆ’ƒÂ¡rio const [email, setEmail] = useState<string>(""); // Email do usuário
const [password, setPassword] = useState<string>(""); // Senha do usuÃÆ’ƒÂ¡rio const [password, setPassword] = useState<string>(""); // Senha do usuário
const [name, setName] = useState<string>(""); // Nome completo (apenas para registro) const [name, setName] = useState<string>(""); // Nome completo (apenas para registro)
const [loading, setLoading] = useState<boolean>(false); // Estado de carregamento const [loading, setLoading] = useState<boolean>(false); // Estado de carregamento
const [error, setError] = useState<string>(""); // Mensagens de erro const [error, setError] = useState<string>(""); // Mensagens de erro
const [isLogin, setIsLogin] = useState<boolean>(true); // Alterna entre login e registro const [isLogin, setIsLogin] = useState<boolean>(true); // Alterna entre login e registro
const [checkingAuth, setCheckingAuth] = useState<boolean>(true); // VerificaÃÆÃ†â€™Ã‚ÂÂ§ÃÆÃ†â€™Ã‚Â£o inicial de autenticaÃÆÃ†â€™Ã‚ÂÂ§ÃÆÃ†â€™Ã‚Â£o const [checkingAuth, setCheckingAuth] = useState<boolean>(true); // Verificação inicial de autenticação
const [showPassword, setShowPassword] = useState<boolean>(false); // Controla visibilidade da senha const [showPassword, setShowPassword] = useState<boolean>(false); // Controla visibilidade da senha
const [accessToken, setAccessToken] = useState<string>(""); // Token de acesso da API const [accessToken, setAccessToken] = useState<string>(""); // Token de acesso da API
const [showSuccessModal, setShowSuccessModal] = useState<boolean>(false); // Modal de sucesso do cadastro const [showSuccessModal, setShowSuccessModal] = useState<boolean>(false); // Modal de sucesso do cadastro
const [showPendingModal, setShowPendingModal] = useState<boolean>(false); // Modal de cadastro pendente const [showPendingModal, setShowPendingModal] = useState<boolean>(false); // Modal de cadastro pendente
/** /**
* Verifica parÃÆÃâÃâšÂ¢metros da URL para definir aba inicial * Verifica par¢metros da URL para definir aba inicial
*/ */
useEffect(() => { useEffect(() => {
const tab = searchParams.get('tab'); const tab = searchParams.get('tab');
@ -39,13 +39,13 @@ const LoginPageContent = () => {
}, [searchParams]); }, [searchParams]);
/** /**
* Verifica se o usuÃÆÃâÃâšÂ¡rio jÃÆÃâÃâšÂ¡ estÃÆÃâÃâšÂ¡ autenticado ao carregar a pÃÆÃâÃâšÂ¡gina * Verifica se o usuário está autenticado ao carregar a gina
* Se estiver autenticado, redireciona para o dashboard * Se estiver autenticado, redireciona para o dashboard
*/ */
useEffect(() => { useEffect(() => {
const checkAuth = async () => { const checkAuth = async () => {
try { try {
// Verificar se hÃÆ’ƒÂ¡ token armazenado // Verificar se há token armazenado
const storedToken = localStorage.getItem('access_token'); const storedToken = localStorage.getItem('access_token');
if (!storedToken) { if (!storedToken) {
@ -53,14 +53,14 @@ const LoginPageContent = () => {
return; return;
} }
// Verificar autenticaÃÆ’ÂÂ§ÃÆ’£o usando API com o token no header Authorization // Verificar autenticação usando API com o token no header Authorization
const response = await fetch( const response = await fetch(
`${API_V1_BASE_URL}/auth/me`, `${API_V1_BASE_URL}/auth/me`,
{ {
method: "GET", method: "GET",
headers: { headers: {
"accept": "application/json", "accept": "application/json",
"Authorization": `Bearer ${storedToken}`, // Usar Authorization ao invÃÆ’ƒÂ©s de Cookie "Authorization": `Bearer ${storedToken}`, // Usar Authorization ao invés de Cookie
}, },
} }
); );
@ -71,7 +71,7 @@ const LoginPageContent = () => {
router.push("/dashboard"); router.push("/dashboard");
} else { } else {
const errorText = await response.text(); const errorText = await response.text();
// Limpar token invÃÆ’ƒÂ¡lido // Limpar token inválido
localStorage.removeItem('access_token'); localStorage.removeItem('access_token');
} }
} catch (error) { } catch (error) {
@ -83,9 +83,9 @@ const LoginPageContent = () => {
}, [router]); }, [router]);
/** /**
* FunÃÆÂÂ§ÃÆÂ£o para realizar login do usuÃÆÂ¡rio usando API * Função para realizar login do usuário usando API
* @param email - Email do usuÃÆÃâÃâšÂ¡rio (usado como identificador) * @param email - Email do usuário (usado como identificador)
* @param password - Senha do usuÃÆÃâÃâšÂ¡rio * @param password - Senha do usuário
*/ */
const login = async (email: string, password: string) => { const login = async (email: string, password: string) => {
setLoading(true); setLoading(true);
@ -108,32 +108,32 @@ const LoginPageContent = () => {
}) })
}); });
// Ler e logar o corpo da resposta (usando clone para nÃÆ’ƒÂ£o consumir o body original) // Ler e logar o corpo da resposta (usando clone para não consumir o body original)
const respClone = response.clone(); const respClone = response.clone();
const respText = await respClone.text(); const respText = await respClone.text();
let respBody: any = respText; let respBody: any = respText;
try { try {
respBody = JSON.parse(respText); respBody = JSON.parse(respText);
} catch { } catch {
// corpo nÃÆÃ†â€™Ã‚Â£o ÃÆ’ƒÂ© JSON, manter como texto // corpo não é JSON, manter como texto
} }
if (!response.ok) { if (!response.ok) {
// Extrair mensagem de erro do backend // Extrair mensagem de erro do backend
let errorMessage = respBody?.message || respBody?.error || respBody?.detail || respText; let errorMessage = respBody?.message || respBody?.error || respBody?.detail || respText;
// Tratar cÃÆÃ†â€™Ã‚Â³digos de status especÃÆÃ†â€™Ã‚Â­ficos // Tratar códigos de status específicos
if (response.status === 401) { if (response.status === 401) {
errorMessage = "Email ou senha incorretos. Verifique suas credenciais."; errorMessage = "Email ou senha incorretos. Verifique suas credenciais.";
} else if (response.status === 403) { } else if (response.status === 403) {
errorMessage = "Acesso negado. Sua conta pode estar inativa."; errorMessage = "Acesso negado. Sua conta pode estar inativa.";
} else if (response.status === 404) { } else if (response.status === 404) {
errorMessage = "UsuÃÆÃ†â€™Ã‚Â¡rio nÃÆÃ†â€™Ã‚Â£o encontrado. Verifique o email informado."; errorMessage = "Usuário não encontrado. Verifique o email informado.";
} else if (response.status >= 500) { } else if (response.status >= 500) {
errorMessage = "Erro interno do servidor. Tente novamente em alguns minutos."; errorMessage = "Erro interno do servidor. Tente novamente em alguns minutos.";
} }
// Aplicar traduÃÆÃ†â€™Ã‚ÂÂ§ÃÆÃ†â€™Ã‚Â£o se disponÃÆÃ†â€™Ã‚Â­vel // Aplicar tradução se disponível
const translatedError = translateError(errorMessage); const translatedError = translateError(errorMessage);
throw new Error(translatedError); throw new Error(translatedError);
} }
@ -158,24 +158,24 @@ const LoginPageContent = () => {
if (meResponse.ok) { if (meResponse.ok) {
const userData = await meResponse.json(); const userData = await meResponse.json();
} else { } else {
console.log("ÃÆ’¢ÂÃɉ۪ Falha no /me:", await meResponse.text()); console.log("¢Ãɉ۪ Falha no /me:", await meResponse.text());
} }
} else { } else {
throw new Error("Token nÃÆ’ƒÂ£o recebido do servidor"); throw new Error("Token não recebido do servidor");
} }
// 4. Armazenar informaÃÆÃ†â€™Ã‚ÂÂ§ÃÆÃ†â€™Ã‚Âµes do usuÃÆÃ†â€™Ã‚Â¡rio no localStorage // 4. Armazenar informaçµes do usuário no localStorage
if (loginData.user) { if (loginData.user) {
localStorage.setItem('user', JSON.stringify(loginData.user)); localStorage.setItem('user', JSON.stringify(loginData.user));
// Verificar se o registro estÃÆ’ƒÂ¡ completo // Verificar se o registro está completo
if (loginData.user["registro-completo"] === false) { if (loginData.user["registro-completo"] === false) {
setShowPendingModal(true); setShowPendingModal(true);
return; // NÃÆ’ƒÂ£o continuar com o redirecionamento return; // Não continuar com o redirecionamento
} }
// Armazenar ID do endereÃÆÃ†â€™Ã‚Â§o se disponÃÆÃ†â€™Ã‚Â­vel // Armazenar ID do endereço se disponível
// Backend pode retornar "endereco" (singular) ou "enderecos" (plural array) // Backend pode retornar "endereco" (singular) ou "enderecos" (plural array)
let enderecoId = loginData.user.endereco; let enderecoId = loginData.user.endereco;
@ -189,7 +189,7 @@ const LoginPageContent = () => {
} }
} }
// 5. Verificar e armazenar empresa ID se disponÃÆ’ƒÂ­vel // 5. Verificar e armazenar empresa ID se disponível
if (loginData.user?.empresaId) { if (loginData.user?.empresaId) {
setEmpresaId(loginData.user.empresaId); setEmpresaId(loginData.user.empresaId);
} }
@ -198,16 +198,16 @@ const LoginPageContent = () => {
router.push("/dashboard"); router.push("/dashboard");
} catch (error: any) { } catch (error: any) {
console.error("ÃÆ’¢ÂÃɉ۪ Erro no login:", error); console.error("¢Ãɉ۪ Erro no login:", error);
// Definir mensagem de erro amigÃÆ’ƒÂ¡vel // Definir mensagem de erro amigável
let friendlyMessage = error.message; let friendlyMessage = error.message;
// Se for um erro de rede // Se for um erro de rede
if (error.name === 'TypeError' || error.message.includes('fetch')) { if (error.name === 'TypeError' || error.message.includes('fetch')) {
friendlyMessage = "Erro de conexÃÆ’ƒÂ£o. Verifique sua internet e tente novamente."; friendlyMessage = "Erro de conexão. Verifique sua internet e tente novamente.";
} }
// Se for um erro genÃÆ’ƒÂ©rico sem mensagem clara // Se for um erro genérico sem mensagem clara
else if (!error.message || error.message.length < 5) { else if (!error.message || error.message.length < 5) {
friendlyMessage = "Erro inesperado. Verifique suas credenciais e tente novamente."; friendlyMessage = "Erro inesperado. Verifique suas credenciais e tente novamente.";
} }
@ -219,7 +219,7 @@ const LoginPageContent = () => {
}; };
/** /**
* FunÃÆÂÂ§ÃÆÂ£o para registrar novo usuÃÆÂ¡rio usando API * Função para registrar novo usuário usando API
* Cria conta e mostra mensagem de sucesso * Cria conta e mostra mensagem de sucesso
*/ */
const register = async () => { const register = async () => {
@ -235,12 +235,12 @@ const LoginPageContent = () => {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
}, },
body: JSON.stringify({ body: JSON.stringify({
identificador: email, // identificador = email do usuÃÆ’ƒÂ¡rio (usado no login) identificador: email, // identificador = email do usuário (usado no login)
email: email, email: email,
nome: name, // nome = nome completo do usuÃÆ’ƒÂ¡rio nome: name, // nome = nome completo do usuário
senha: password, senha: password,
nivel: "owner", // valor estÃÆ’ƒÂ¡tico nivel: "owner", // valor estático
"registro-completo": false // valor estÃÆÃ†â€™Ã‚Â¡tico - registro incompleto por padrÃÆÃ†â€™Ã‚Â£o "registro-completo": false // valor estático - registro incompleto por padrão
}) })
}); });
@ -248,25 +248,25 @@ const LoginPageContent = () => {
if (!response.ok) { if (!response.ok) {
const errorData = await response.json().catch(() => ({})); const errorData = await response.json().catch(() => ({}));
// Tratar cÃÆÃ†â€™Ã‚Â³digos de status especÃÆÃ†â€™Ã‚Â­ficos // Tratar códigos de status específicos
let errorMessage = errorData.message || 'Falha no registro'; let errorMessage = errorData.message || 'Falha no registro';
if (response.status === 409 || errorMessage.includes('already exists') || errorMessage.includes('jÃÆ’ƒÂ¡ existe')) { if (response.status === 409 || errorMessage.includes('already exists') || errorMessage.includes('já existe')) {
errorMessage = 'Este email jÃÆ’ƒÂ¡ estÃÆÃ†â€™Ã‚Â¡ cadastrado. Tente fazer login ou use outro email.'; errorMessage = 'Este email já está cadastrado. Tente fazer login ou use outro email.';
} else if (response.status === 400) { } else if (response.status === 400) {
errorMessage = 'Dados invÃÆÃ†â€™Ã‚Â¡lidos. Verifique se todos os campos estÃÆÃ†â€™Ã‚Â£o preenchidos corretamente.'; errorMessage = 'Dados inválidos. Verifique se todos os campos estão preenchidos corretamente.';
} else if (response.status >= 500) { } else if (response.status >= 500) {
errorMessage = 'Erro interno do servidor. Tente novamente em alguns minutos.'; errorMessage = 'Erro interno do servidor. Tente novamente em alguns minutos.';
} }
// Aplicar traduÃÆÃ†â€™Ã‚ÂÂ§ÃÆÃ†â€™Ã‚Â£o se disponÃÆÃ†â€™Ã‚Â­vel // Aplicar tradução se disponível
const translatedError = translateError(errorMessage); const translatedError = translateError(errorMessage);
throw new Error(translatedError); throw new Error(translatedError);
} }
const registerData = await response.json(); const registerData = await response.json();
// 2. Fazer login automÃÆÃ†â€™Ã‚Â¡tico apÃÆÃ†â€™Ã‚Â³s registro para obter token // 2. Fazer login automático após registro para obter token
const loginResponse = await fetch(`${baseUrl}/auth/login`, { const loginResponse = await fetch(`${baseUrl}/auth/login`, {
method: 'POST', method: 'POST',
headers: { headers: {
@ -282,13 +282,13 @@ const LoginPageContent = () => {
}); });
if (!loginResponse.ok) { if (!loginResponse.ok) {
console.error("ÃÆ’¢ÂÃɉ۪ Erro no login automÃÆÃ†â€™Ã‚Â¡tico:", loginResponse.status); console.error("¢Ãɉ۪ Erro no login automático:", loginResponse.status);
throw new Error('Erro ao fazer login automÃÆÃ†â€™Ã‚Â¡tico apÃÆÃ†â€™Ã‚Â³s registro'); throw new Error('Erro ao fazer login automático após registro');
} }
const loginData = await loginResponse.json(); const loginData = await loginResponse.json();
// 3. Armazenar token e dados do usuÃÆ’ƒÂ¡rio // 3. Armazenar token e dados do usuário
if (loginData.access_token) { if (loginData.access_token) {
localStorage.setItem('access_token', loginData.access_token); localStorage.setItem('access_token', loginData.access_token);
} }
@ -297,20 +297,20 @@ const LoginPageContent = () => {
localStorage.setItem('user', JSON.stringify(loginData.user)); localStorage.setItem('user', JSON.stringify(loginData.user));
} }
// 4. Redirecionar para completar registro com token vÃÆ’ƒÂ¡lido // 4. Redirecionar para completar registro com token válido
router.push(`/completar-registro?nome=${encodeURIComponent(name)}&email=${encodeURIComponent(email)}`); router.push(`/completar-registro?nome=${encodeURIComponent(name)}&email=${encodeURIComponent(email)}`);
} catch (error: any) { } catch (error: any) {
console.error("ÃÆ’¢ÂÃɉ۪ Erro no registro:", error); console.error("¢Ãɉ۪ Erro no registro:", error);
// Definir mensagem de erro amigÃÆ’ƒÂ¡vel // Definir mensagem de erro amigável
let friendlyMessage = error.message; let friendlyMessage = error.message;
// Se for um erro de rede // Se for um erro de rede
if (error.name === 'TypeError' || error.message.includes('fetch')) { if (error.name === 'TypeError' || error.message.includes('fetch')) {
friendlyMessage = "Erro de conexÃÆ’ƒÂ£o. Verifique sua internet e tente novamente."; friendlyMessage = "Erro de conexão. Verifique sua internet e tente novamente.";
} }
// Se for um erro genÃÆ’ƒÂ©rico sem mensagem clara // Se for um erro genérico sem mensagem clara
else if (!error.message || error.message.length < 5) { else if (!error.message || error.message.length < 5) {
friendlyMessage = "Erro inesperado durante o cadastro. Tente novamente."; friendlyMessage = "Erro inesperado durante o cadastro. Tente novamente.";
} }
@ -322,11 +322,11 @@ const LoginPageContent = () => {
}; };
/** /**
* FunÃÆÃâÃâšÂÂ§ÃÆÃâÃâšÂ£o para notificar administrador sobre novo cadastro * Função para notificar administrador sobre novo cadastro
*/ */
const notifyAdmin = async (userData: { nome: string, email: string, identificador: string }) => { const notifyAdmin = async (userData: { nome: string, email: string, identificador: string }) => {
try { try {
// Enviar notificaÃÆ’ƒÂÂÂ§ÃÆ’ƒÂµes via API (usando Resend para emails reais) // Enviar notificaçµes via API (usando Resend para emails reais)
await fetch('/api/notify-admin-resend', { await fetch('/api/notify-admin-resend', {
method: 'POST', method: 'POST',
headers: { headers: {
@ -335,13 +335,13 @@ const LoginPageContent = () => {
body: JSON.stringify(userData) body: JSON.stringify(userData)
}); });
} catch (error) { } catch (error) {
console.error("Erro ao enviar notificaÃÆ’ƒÂÂÂ§ÃÆ’ƒÂµes:", error); console.error("Erro ao enviar notificaçµes:", error);
// NÃÆÃ†â€™Ã‚Â£o bloquear o cadastro por erro de notificaÃÆÃ†â€™Ã‚ÂÂ§ÃÆÃ†â€™Ã‚Â£o // Não bloquear o cadastro por erro de notificação
} }
}; };
/** /**
* FunÃÆÃâÃâšÂÂ§ÃÆÃâÃâšÂ£o para fechar modal de sucesso e redirecionar * Função para fechar modal de sucesso e redirecionar
*/ */
const handleSuccessModalClose = () => { const handleSuccessModalClose = () => {
setShowSuccessModal(false); setShowSuccessModal(false);
@ -349,25 +349,25 @@ const LoginPageContent = () => {
}; };
/** /**
* FunÃÆÃâÃâšÂÂ§ÃÆÃâÃâšÂ£o para fechar modal de cadastro pendente * Função para fechar modal de cadastro pendente
*/ */
const handlePendingModalClose = () => { const handlePendingModalClose = () => {
setShowPendingModal(false); setShowPendingModal(false);
// Fazer logout para limpar dados do usuÃÆ’ƒÂ¡rio // Fazer logout para limpar dados do usuário
localStorage.removeItem('access_token'); localStorage.removeItem('access_token');
localStorage.removeItem('user'); localStorage.removeItem('user');
setAccessToken(""); setAccessToken("");
// Redirecionar para pÃÆ’ƒÂ¡gina inicial // Redirecionar para página inicial
router.push('/'); router.push('/');
}; };
// Tela de carregamento durante verificaÃÆÃ†â€™Ã‚ÂÂ§ÃÆÃ†â€™Ã‚Â£o de autenticaÃÆÃ†â€™Ã‚ÂÂ§ÃÆÃ†â€™Ã‚Â£o // Tela de carregamento durante verificação de autenticação
if (checkingAuth) { if (checkingAuth) {
return ( return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center"> <div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center"> <div className="text-center">
<div className="w-16 h-16 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div> <div className="w-16 h-16 border-4 border-blue-600 border-t-transparent rounded-full animate-spin mx-auto mb-4"></div>
<p className="text-gray-600">Verificando autenticaÃÆÃâÃâšÂÂ§ÃÆÃâÃâšÂ£o...</p> <p className="text-gray-600">Verificando autenticação...</p>
</div> </div>
</div> </div>
); );
@ -376,7 +376,7 @@ const LoginPageContent = () => {
return ( return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center p-4"> <div className="min-h-screen bg-gray-50 flex items-center justify-center p-4">
<div className="bg-white rounded-2xl shadow-xl overflow-hidden w-full max-w-md"> <div className="bg-white rounded-2xl shadow-xl overflow-hidden w-full max-w-md">
{/* CabeÃÆ’ƒÂ§alho com logo e slogan */} {/* Cabeçalho com logo e slogan */}
<div className="bg-gradient-to-r from-blue-600 to-blue-700 p-1 text-center"> <div className="bg-gradient-to-r from-blue-600 to-blue-700 p-1 text-center">
<div className="mx-auto -mb-2 flex items-center justify-center"> <div className="mx-auto -mb-2 flex items-center justify-center">
<Image <Image
@ -392,9 +392,9 @@ const LoginPageContent = () => {
<p className="text-gray-300">Plataforma B2B de Medicamentos</p> <p className="text-gray-300">Plataforma B2B de Medicamentos</p>
</div> </div>
{/* FormulÃÆ’ƒÂ¡rio de login/registro */} {/* Formulário de login/registro */}
<div className="p-6"> <div className="p-6">
{/* BotÃÆ’ƒÂµes de alternÃÆ’ƒÂ¢ncia entre Login e Cadastro */} {/* Botµes de altern¢ncia entre Login e Cadastro */}
<div className="flex bg-gray-100 rounded-lg p-1 mb-6"> <div className="flex bg-gray-100 rounded-lg p-1 mb-6">
<button <button
onClick={() => setIsLogin(true)} onClick={() => setIsLogin(true)}
@ -464,7 +464,7 @@ const LoginPageContent = () => {
</div> </div>
<input <input
type="text" type="text"
placeholder="JoÃÆ’ƒÂ£o Silva" placeholder="João Silva"
value={name} value={name}
onChange={(e) => setName(e.target.value)} onChange={(e) => setName(e.target.value)}
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors text-gray-900 placeholder-gray-500" className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors text-gray-900 placeholder-gray-500"
@ -527,7 +527,7 @@ const LoginPageContent = () => {
</div> </div>
<input <input
type={showPassword ? "text" : "password"} type={showPassword ? "text" : "password"}
placeholder="ÃÆ’¢â‚¬ÂÂÂ¢ÃÆ’¢â‚¬ÂÂÂ¢ÃÆ’¢â‚¬ÂÂÂ¢ÃÆ’¢â‚¬ÂÂÂ¢ÃÆ’¢â‚¬ÂÂÂ¢ÃÆ’¢â‚¬ÂÂÂ¢ÃÆ’¢â‚¬ÂÂÂ¢ÃÆ’¢â‚¬Â¢" placeholder="¢â‚¬¢Â¢Ã¢â€šÂ¬¢Â¢Ã¢â€šÂ¬¢Â¢Ã¢â€šÂ¬¢Â¢Ã¢â€šÂ¬¢Â¢Ã¢â€šÂ¬¢Â¢Ã¢â€šÂ¬¢Â¢Ã¢â€šÂ¬¢"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
className="w-full pl-10 pr-12 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors text-gray-900 placeholder-gray-500" className="w-full pl-10 pr-12 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-colors text-gray-900 placeholder-gray-500"
@ -576,7 +576,7 @@ const LoginPageContent = () => {
</div> </div>
</div> </div>
{/* BotÃÆ’ƒÂµes de Submit */} {/* Botµes de Submit */}
<div className="space-y-3 pt-2"> <div className="space-y-3 pt-2">
{isLogin ? ( {isLogin ? (
<button <button
@ -708,18 +708,18 @@ const LoginPageContent = () => {
</h2> </h2>
</div> </div>
{/* ConteÃÆ’ƒÂºdo do Modal */} {/* Conteúdo do Modal */}
<div className="p-6 text-center"> <div className="p-6 text-center">
<div className="mb-6"> <div className="mb-6">
<p className="text-gray-700 text-lg leading-relaxed"> <p className="text-gray-700 text-lg leading-relaxed">
Seu cadastro foi enviado com <strong>sucesso</strong>! Seu cadastro foi enviado com <strong>sucesso</strong>!
</p> </p>
<p className="text-gray-600 mt-3"> <p className="text-gray-600 mt-3">
Nossa equipe entrarÃÆÃâÃâšÂ¡ em contato em breve para finalizar seu acesso ÃÆÃâÃâšÂ  plataforma. Nossa equipe entrará em contato em breve para finalizar seu acesso   plataforma.
</p> </p>
</div> </div>
{/* ÃÆ’ƒÂcone decorativo */} {/* cone decorativo */}
<div className="flex justify-center mb-6"> <div className="flex justify-center mb-6">
<div className="flex space-x-2"> <div className="flex space-x-2">
<div className="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div> <div className="w-3 h-3 bg-green-500 rounded-full animate-pulse"></div>
@ -728,7 +728,7 @@ const LoginPageContent = () => {
</div> </div>
</div> </div>
{/* BotÃÆ’ƒÂ£o de OK */} {/* Botão de OK */}
<button <button
onClick={handleSuccessModalClose} onClick={handleSuccessModalClose}
className="w-full bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white font-semibold py-4 px-6 rounded-xl transition-all duration-200 transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-green-200" className="w-full bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white font-semibold py-4 px-6 rounded-xl transition-all duration-200 transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-green-200"
@ -766,21 +766,21 @@ const LoginPageContent = () => {
</h2> </h2>
</div> </div>
{/* ConteÃÆ’ƒÂºdo do Modal */} {/* Conteúdo do Modal */}
<div className="p-6 text-center"> <div className="p-6 text-center">
<div className="mb-6"> <div className="mb-6">
<p className="text-gray-700 text-lg leading-relaxed"> <p className="text-gray-700 text-lg leading-relaxed">
Seu cadastro estÃÆÃâÃâšÂ¡ <strong>pendente de validaÃÆÃâÃâšÂÂ§ÃÆÃâÃâšÂ£o</strong>! Seu cadastro está <strong>pendente de validação</strong>!
</p> </p>
<p className="text-gray-600 mt-3"> <p className="text-gray-600 mt-3">
Nossa equipe ainda estÃÆÃâÃâšÂ¡ analisando suas informaÃÆÃâÃâšÂÂ§ÃÆÃâÃâšÂµes. VocÃÆÃâÃâšÂª receberÃÆÃâÃâšÂ¡ uma confirmaÃÆÃâÃâšÂÂ§ÃÆÃâÃâšÂ£o em breve. Nossa equipe ainda está analisando suas informaçµes. Você receberá uma confirmação em breve.
</p> </p>
<p className="text-gray-600 mt-3"> <p className="text-gray-600 mt-3">
Qualquer dÃÆÃâÃâšÂºvida, entre em contato conosco. Qualquer dúvida, entre em contato conosco.
</p> </p>
</div> </div>
{/* ÃÆ’ƒÂcone decorativo */} {/* cone decorativo */}
<div className="flex justify-center mb-6"> <div className="flex justify-center mb-6">
<div className="flex space-x-2"> <div className="flex space-x-2">
<div className="w-3 h-3 bg-yellow-500 rounded-full animate-pulse"></div> <div className="w-3 h-3 bg-yellow-500 rounded-full animate-pulse"></div>
@ -789,7 +789,7 @@ const LoginPageContent = () => {
</div> </div>
</div> </div>
{/* BotÃÆ’ƒÂ£o de OK */} {/* Botão de OK */}
<button <button
onClick={handlePendingModalClose} onClick={handlePendingModalClose}
className="w-full bg-gradient-to-r from-yellow-500 to-orange-500 hover:from-yellow-600 hover:to-orange-600 text-white font-semibold py-4 px-6 rounded-xl transition-all duration-200 transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-yellow-200" className="w-full bg-gradient-to-r from-yellow-500 to-orange-500 hover:from-yellow-600 hover:to-orange-600 text-white font-semibold py-4 px-6 rounded-xl transition-all duration-200 transform hover:scale-105 focus:outline-none focus:ring-4 focus:ring-yellow-200"