fix: conectar SearchDropdown com Header e passar searchValue corretamente

This commit is contained in:
Erik
2025-12-01 16:46:47 -03:00
parent bee1af01ec
commit ef98075686
6 changed files with 348 additions and 14 deletions

36
docs/GIT_CREDENTIALS.md Normal file
View File

@@ -0,0 +1,36 @@
# Git Credentials - OCTTO Engenharia
## Repositório
- **URL:** https://git.stackbyte.cloud/erik/octto-engenharia.git
- **Usuário:** erik
- **Token:** 1ada354bbbf548b5ff2c2e2419d15368f3b70a05
## Configuração Git Automática
Para não precisar inserir credenciais toda vez, execute:
```bash
cd frontend
git remote set-url origin https://erik:1ada354bbbf548b5ff2c2e2419d15368f3b70a05@git.stackbyte.cloud/erik/octto-engenharia.git
```
## Push Rápido
```bash
cd frontend
git add .
git commit -m "sua mensagem aqui"
git push origin main
```
## Configuração SSH (Alternativa Segura)
Se preferir não armazenar credenciais em texto plano:
1. Gere uma chave SSH: `ssh-keygen -t ed25519`
2. Adicione a chave pública no Git (Settings > SSH Keys)
3. Configure: `git remote set-url origin git@git.stackbyte.cloud:erik/octto-engenharia.git`
---
**Salvo em:** `docs/GIT_CREDENTIALS.md`
**Acesso:** Sempre que precisar fazer push, consulte este arquivo

View File

@@ -0,0 +1,64 @@
## 💰 FATURA DE COBRANÇA
---
### DETALHES DO SERVIÇO
**Prestador de Serviço:** IdealPages
**Cliente:** OCTTO Engenharia
**Data de Emissão:** 1 de Dezembro de 2025
**Data de Vencimento:** 4 de Dezembro de 2025
---
### 📋 DESCRIÇÃO DO SERVIÇO
Implementação completa do sistema de gerenciamento dinâmico do site:
- Sistema de logotipo dinâmico
- Painel de configurações reorganizado (4 abas)
- Sistema de informações de contato dinâmicas
- Badge de prestador de serviço
- Sistema de backup completo
- Barra de admin inteligente
---
### 💵 VALORES
| Item | Valor |
|------|-------|
| Implementação do Sistema de Gerenciamento Dinâmico | R$ 2.700,00 |
| **TOTAL** | **R$ 2.700,00** |
---
### 📅 CONDIÇÕES DE PAGAMENTO
**Vencimento:** 4 de Dezembro de 2025
**Método:** PIX / Transferência Bancária
---
### 🔗 LINK DE PAGAMENTO
**Pague via PIX clicando no link abaixo:**
👉 [https://pix.sejaefi.com.br/pagar/be73df383d9d78370e79e7f6f62af92b9a6415fb.html](https://pix.sejaefi.com.br/pagar/be73df383d9d78370e79e7f6f62af92b9a6415fb.html)
---
### 📞 CONTATO
**Email:** erik@idealpages.com.br
**Empresa:** IdealPages
---
**Atenciosamente,**
**IdealPages**
*Desenvolvimento de Soluções Web*
---
*Documento de cobrança referente aos serviços de desenvolvimento web prestados para OCTTO Engenharia.*

View File

@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fatura de Cobrança - IdealPages</title>
</head>
<body style="margin: 0; padding: 0; font-family: Arial, sans-serif; background-color: #f4f4f4;">
<table width="100%" cellpadding="0" cellspacing="0" style="background-color: #f4f4f4;">
<tr>
<td align="center" style="padding: 20px;">
<table width="600" cellpadding="0" cellspacing="0" style="background-color: white; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
<!-- Header -->
<tr>
<td style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); padding: 40px 20px; text-align: center; color: white;">
<h1 style="margin: 0 0 10px 0; font-size: 28px; font-weight: bold;">💰 FATURA DE COBRANÇA</h1>
<p style="margin: 0; font-size: 14px; opacity: 0.9;">IdealPages - Desenvolvimento de Soluções Web</p>
</td>
</tr>
<!-- Content -->
<tr>
<td style="padding: 40px 30px;">
<!-- Details -->
<table width="100%" cellpadding="0" cellspacing="0" style="margin-bottom: 40px; border-bottom: 1px solid #eee; padding-bottom: 40px;">
<tr>
<td width="50%" style="padding-bottom: 20px;">
<p style="margin: 0 0 5px 0; font-size: 12px; color: #999; text-transform: uppercase; font-weight: bold;">Prestador de Serviço</p>
<p style="margin: 0; font-size: 15px; color: #333; font-weight: bold;">IdealPages</p>
</td>
<td width="50%" style="padding-bottom: 20px;">
<p style="margin: 0 0 5px 0; font-size: 12px; color: #999; text-transform: uppercase; font-weight: bold;">Cliente</p>
<p style="margin: 0; font-size: 15px; color: #333; font-weight: bold;">OCTTO Engenharia</p>
</td>
</tr>
<tr>
<td width="50%">
<p style="margin: 0 0 5px 0; font-size: 12px; color: #999; text-transform: uppercase; font-weight: bold;">Data de Emissão</p>
<p style="margin: 0; font-size: 15px; color: #333;">1 de Dezembro de 2025</p>
</td>
<td width="50%">
<p style="margin: 0 0 5px 0; font-size: 12px; color: #999; text-transform: uppercase; font-weight: bold;">Data de Vencimento</p>
<p style="margin: 0; font-size: 15px; color: #d9534f; font-weight: bold;">4 de Dezembro de 2025</p>
</td>
</tr>
</table>
<!-- Services -->
<h2 style="margin: 0 0 15px 0; font-size: 16px; font-weight: bold; color: #333; padding-bottom: 10px; border-bottom: 2px solid #667eea;">📋 Serviços Prestados</h2>
<ul style="margin: 0 0 30px 0; padding: 0; list-style: none;">
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Desenvolvimento do site institucional</li>
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Domínio incluso (R$ 40,00)</li>
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Hospedagem e suporte grátis por 6 meses (R$ 149,99/mês)</li>
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Sistema de logotipo dinâmico</li>
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Painel de configurações reorganizado (4 abas)</li>
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Sistema de informações de contato dinâmicas</li>
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Badge de prestador de serviço</li>
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Sistema de backup completo</li>
<li style="padding: 8px 0; color: #555; font-size: 14px;"><span style="color: #667eea; font-weight: bold; margin-right: 10px;"></span>Barra de admin inteligente</li>
</ul>
<!-- Pricing -->
<h2 style="margin: 30px 0 15px 0; font-size: 16px; font-weight: bold; color: #333; padding-bottom: 10px; border-bottom: 2px solid #667eea;">💵 Valores</h2>
<table width="100%" cellpadding="0" cellspacing="0" style="margin: 20px 0; background: #f9f9f9; border-collapse: collapse;">
<tr>
<th style="background: #667eea; color: white; padding: 12px; text-align: left; font-weight: bold; border: 1px solid #ddd;">Descrição</th>
<th style="background: #667eea; color: white; padding: 12px; text-align: right; font-weight: bold; border: 1px solid #ddd;">Valor</th>
</tr>
<tr>
<td style="padding: 12px; border: 1px solid #eee;">Implementação do Sistema de Gerenciamento Dinâmico</td>
<td style="padding: 12px; text-align: right; border: 1px solid #eee;">R$ 2.700,00</td>
</tr>
<!-- Economia aplicada (não altera o valor do PIX) -->
<tr>
<td style="padding: 12px; border: 1px solid #eee; color: #2f6b2f; font-weight: bold;">Economia aplicada: Hospedagem + Suporte (6× R$ 149,99) + Domínio (R$ 40,00)</td>
<td style="padding: 12px; text-align: right; border: 1px solid #eee; color: #2f6b2f; font-weight: bold;">- R$ 939,94</td>
</tr>
<tr>
<td style="padding: 12px; background: #f0f0f0; font-weight: bold; border: 1px solid #eee;">TOTAL A PAGAR</td>
<td style="padding: 12px; text-align: right; background: #f0f0f0; font-weight: bold; border: 1px solid #eee;"><span style="color: #667eea; font-size: 20px;">R$ 2.700,00</span></td>
</tr>
</table>
<!-- Savings note -->
<div style="background: #e8f3ff; border-left: 4px solid #4da3ff; padding: 12px 15px; margin: 10px 0 25px 0; border-radius: 4px;">
<p style="margin: 0; color: #244; font-size: 13px;">
Economia total para os 6 meses: <strong>R$ 939,94</strong> (R$ 149,99/mês de hospedagem + suporte + domínio R$ 40,00).
</p>
</div>
<!-- Warning -->
<div style="background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0; border-radius: 4px;">
<p style="margin: 0; color: #856404; font-size: 14px;"><strong>⚠️ Vencimento:</strong> 4 de Dezembro de 2025</p>
</div>
<!-- Payment -->
<div style="background: #f0f7ff; padding: 25px; margin: 30px 0; text-align: center; border-radius: 8px;">
<h3 style="margin: 0 0 15px 0; color: #333; font-size: 16px;">🔗 LINK DE PAGAMENTO</h3>
<p style="color: #666; margin: 0 0 15px 0; font-size: 14px;">Clique no botão abaixo para pagar via PIX</p>
<a href="https://pix.sejaefi.com.br/pagar/be73df383d9d78370e79e7f6f62af92b9a6415fb.html" style="display: inline-block; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px 40px; border-radius: 5px; text-decoration: none; font-weight: bold; font-size: 16px;">PAGAR AGORA</a>
<p style="color: #667eea; text-decoration: underline; font-size: 12px; margin: 10px 0 0 0; word-break: break-all;">https://pix.sejaefi.com.br/pagar/be73df383d9d78370e79e7f6f62af92b9a6415fb.html</p>
</div>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="background: #f5f5f5; padding: 30px 20px; text-align: center; border-top: 1px solid #eee;">
<h4 style="margin: 0 0 10px 0; color: #333; font-size: 14px; font-weight: bold;">IdealPages</h4>
<p style="margin: 0 0 5px 0; color: #666; font-size: 13px;">📧 Email: erik@idealpages.com.br</p>
<p style="margin: 0 0 15px 0; color: #666; font-size: 13px;">🌐 Desenvolvimento de Soluções Web</p>
<p style="margin: 0; color: #999; font-size: 11px; border-top: 1px solid #ddd; padding-top: 15px;">Documento de cobrança referente aos serviços de desenvolvimento web prestados para OCTTO Engenharia.</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>

104
docs/e-mail-final.md Normal file
View File

@@ -0,0 +1,104 @@
---
**IdealPages**
Desenvolvimento de Soluções Web
---
## Prezado Cliente,
Segue resumo das atividades realizadas e concluídas em seu site institucional:
---
### 📋 SERVIÇOS IMPLEMENTADOS
#### 1. **Sistema de Logotipo Dinâmico**
- Implementação de upload de logotipo através do painel administrativo
- O logotipo é automaticamente exibido em todo o site (cabeçalho, rodapé e painel admin)
- Substituição automática do ícone padrão pelo logotipo personalizado
- Fallback inteligente mantém ícone caso nenhum logotipo seja configurado
#### 2. **Painel de Configurações Reorganizado**
- **4 abas funcionais para fácil gerenciamento:**
- **Personalização**: Cores e branding do site
- **Logotipo**: Upload e gerenciamento do logo
- **Contato**: Telefone, WhatsApp, email e endereço atualizados em tempo real
- **Backup**: Sistema completo de backup e restauração
#### 3. **Sistema de Informações de Contato Dinâmicas**
- Telefone, WhatsApp, email e endereço gerenciáveis via painel admin
- Exibição automática na página de contato
- Atualização em tempo real (sem necessidade de recarregar o site)
- Integração com rodapé do site
#### 4. **Badge de Prestador de Serviço**
- Sistema de mostrar/ocultar badge de "Prestador de Serviço Oficial"
- Personalizável com nome da marca parceira
- Visível no hero da página inicial e no rodapé
#### 5. **Sistema de Backup Completo**
- Backup automático do banco de dados PostgreSQL
- Backup de arquivos de mídia do MinIO
- Compressão automática em .tar.gz
- Opção de download local ou upload para nuvem
- Sistema de restauração rápida
#### 6. **Barra de Admin Inteligente**
- Indicador visual quando usuário está logado
- Acesso rápido ao painel administrativo
- Aparece apenas para usuários autenticados
---
### ✅ FUNCIONALIDADES EM OPERAÇÃO
✓ Logotipo dinâmico em Header, Footer e Painel Admin
✓ Página de contato atualizada automaticamente com dados das Settings
✓ Formulário de contato funcional e integrado
✓ Sistema de backup e restauração completo
✓ Painel admin com 4 abas organizadas e intuitivas
✓ Badge de parceria configurável e dinâmico
✓ Atualização em tempo real sem recarregar página
✓ Suporte a múltiplos idiomas (PT, EN, ES)
✓ Modo claro e escuro em toda interface
---
### 💰 DETALHES DA COBRANÇA
**Valor Total:** R$ 2.700,00
**Vencimento:** 4 de Dezembro de 2025
**Descrição do Serviço:**
Implementação completa do sistema de gerenciamento dinâmico do site (Logo, Configurações, Contato e Backup)
---
### 📞 PRÓXIMOS PASSOS
1. Acesse o painel admin em `https://octtoengenharia.com.br/acesso`
2. Faça login com as credenciais padrão:
- **Email:** admin@occto.com
- **Senha:** admin
3. Vá para **Configurações > Logotipo** e faça upload do seu logo
4. Configure as informações de contato em **Configurações > Contato**
5. Personalize as cores em **Configurações > Personalização**
6. Crie backups regularmente em **Configurações > Backup**
⚠️ **Importante:** Recomendamos alterar a senha padrão na primeira acesso.
---
### 📧 SUPORTE
Em caso de dúvidas ou necessidade de ajustes, entre em contato:
📧 **Email:** erik@idealpages.com.br
🌐 **Empresa:** IdealPages
---
**Atenciosamente,**
**IdealPages**
*Desenvolvimento de Soluções Web*
---

View File

@@ -10,6 +10,7 @@ import SearchDropdown from './SearchDropdown';
export default function Header() { export default function Header() {
const [isSearchOpen, setIsSearchOpen] = useState(false); const [isSearchOpen, setIsSearchOpen] = useState(false);
const [searchValue, setSearchValue] = useState('');
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const [isLoggedIn, setIsLoggedIn] = useState(false); const [isLoggedIn, setIsLoggedIn] = useState(false);
const [logo, setLogo] = useState<string | null>(null); const [logo, setLogo] = useState<string | null>(null);
@@ -145,12 +146,19 @@ export default function Header() {
type="text" type="text"
placeholder={t('nav.search')} placeholder={t('nav.search')}
autoFocus autoFocus
onBlur={() => setIsSearchOpen(false)} value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
onBlur={() => setTimeout(() => setIsSearchOpen(false), 200)}
className="bg-transparent border-none outline-none text-sm w-full text-gray-600 dark:text-gray-200 placeholder-gray-400" className="bg-transparent border-none outline-none text-sm w-full text-gray-600 dark:text-gray-200 placeholder-gray-400"
/> />
)} )}
</div> </div>
<SearchDropdown isOpen={isSearchOpen} onClose={() => setIsSearchOpen(false)} /> <SearchDropdown
isOpen={isSearchOpen}
searchValue={searchValue}
onSearchChange={setSearchValue}
onClose={() => setIsSearchOpen(false)}
/>
</div> </div>
<nav className="flex items-center gap-6 mr-4"> <nav className="flex items-center gap-6 mr-4">

View File

@@ -15,10 +15,11 @@ interface Project {
interface SearchDropdownProps { interface SearchDropdownProps {
onClose?: () => void; onClose?: () => void;
isOpen: boolean; isOpen: boolean;
searchValue: string;
onSearchChange: (value: string) => void;
} }
export default function SearchDropdown({ onClose, isOpen }: SearchDropdownProps) { export default function SearchDropdown({ onClose, isOpen, searchValue, onSearchChange }: SearchDropdownProps) {
const [searchTerm, setSearchTerm] = useState('');
const [results, setResults] = useState<Project[]>([]); const [results, setResults] = useState<Project[]>([]);
const [allProjects, setAllProjects] = useState<Project[]>([]); const [allProjects, setAllProjects] = useState<Project[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@@ -48,7 +49,7 @@ export default function SearchDropdown({ onClose, isOpen }: SearchDropdownProps)
// Filtrar resultados conforme o usuário digita // Filtrar resultados conforme o usuário digita
useEffect(() => { useEffect(() => {
if (!searchTerm.trim()) { if (!searchValue.trim()) {
setResults([]); setResults([]);
return; return;
} }
@@ -58,21 +59,21 @@ export default function SearchDropdown({ onClose, isOpen }: SearchDropdownProps)
// Simular pequeno delay para evitar renderizações desnecessárias // Simular pequeno delay para evitar renderizações desnecessárias
const timer = setTimeout(() => { const timer = setTimeout(() => {
const filtered = allProjects.filter(project => const filtered = allProjects.filter(project =>
project.title.toLowerCase().includes(searchTerm.toLowerCase()) project.title.toLowerCase().includes(searchValue.toLowerCase())
); );
setResults(filtered.slice(0, 5)); // Limitar a 5 resultados setResults(filtered.slice(0, 5)); // Limitar a 5 resultados
setLoading(false); setLoading(false);
}, 300); }, 300);
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [searchTerm, allProjects]); }, [searchValue, allProjects]);
const defaultImage = 'https://images.unsplash.com/photo-1504307651254-35680f356dfd?q=80&w=200&auto=format&fit=crop'; const defaultImage = 'https://images.unsplash.com/photo-1504307651254-35680f356dfd?q=80&w=200&auto=format&fit=crop';
return ( return (
<> <>
{/* Dropdown Overlay */} {/* Dropdown Overlay */}
{isOpen && searchTerm && ( {isOpen && searchValue && (
<div className="absolute top-full left-0 right-0 mt-2 bg-white dark:bg-secondary rounded-lg shadow-xl border border-gray-200 dark:border-white/10 z-50 max-w-md w-full"> <div className="absolute top-full left-0 right-0 mt-2 bg-white dark:bg-secondary rounded-lg shadow-xl border border-gray-200 dark:border-white/10 z-50 max-w-md w-full">
{loading && ( {loading && (
<div className="p-6 text-center"> <div className="p-6 text-center">
@@ -82,10 +83,10 @@ export default function SearchDropdown({ onClose, isOpen }: SearchDropdownProps)
</div> </div>
)} )}
{!loading && results.length === 0 && searchTerm && ( {!loading && results.length === 0 && searchValue && (
<div className="p-6 text-center text-gray-500 dark:text-gray-400"> <div className="p-6 text-center text-gray-500 dark:text-gray-400">
<i className="ri-search-line text-3xl mb-3 block opacity-50"></i> <i className="ri-search-line text-3xl mb-3 block opacity-50"></i>
<p>Nenhum projeto encontrado com "{searchTerm}"</p> <p>Nenhum projeto encontrado com "{searchValue}"</p>
</div> </div>
)} )}
@@ -96,12 +97,12 @@ export default function SearchDropdown({ onClose, isOpen }: SearchDropdownProps)
key={project.id} key={project.id}
href={`${prefix}/projetos`} href={`${prefix}/projetos`}
onClick={() => { onClick={() => {
setSearchTerm(''); onSearchChange('');
onClose?.(); onClose?.();
}} }}
className="flex items-center gap-3 p-3 hover:bg-gray-50 dark:hover:bg-white/5 border-b border-gray-100 dark:border-white/10 last:border-b-0 transition-colors group" className="flex items-center gap-3 p-3 hover:bg-gray-50 dark:hover:bg-white/5 border-b border-gray-100 dark:border-white/10 last:border-b-0 transition-colors group"
> >
<div className="relative w-16 h-16 flex-shrink-0 rounded-lg overflow-hidden bg-gray-200 dark:bg-white/5"> <div className="relative w-16 h-16 shrink-0 rounded-lg overflow-hidden bg-gray-200 dark:bg-white/5">
<Image <Image
src={project.coverImage || defaultImage} src={project.coverImage || defaultImage}
alt={project.title} alt={project.title}
@@ -120,7 +121,7 @@ export default function SearchDropdown({ onClose, isOpen }: SearchDropdownProps)
</p> </p>
)} )}
</div> </div>
<i className="ri-arrow-right-line text-gray-400 group-hover:text-primary transition-colors flex-shrink-0 opacity-0 group-hover:opacity-100"></i> <i className="ri-arrow-right-line text-gray-400 group-hover:text-primary transition-colors shrink-0 opacity-0 group-hover:opacity-100"></i>
</Link> </Link>
))} ))}
@@ -128,7 +129,7 @@ export default function SearchDropdown({ onClose, isOpen }: SearchDropdownProps)
<Link <Link
href={`${prefix}/projetos`} href={`${prefix}/projetos`}
onClick={() => { onClick={() => {
setSearchTerm(''); onSearchChange('');
onClose?.(); onClose?.();
}} }}
className="flex items-center justify-center gap-2 p-3 text-primary font-medium hover:bg-primary/10 border-t border-gray-100 dark:border-white/10 transition-colors group" className="flex items-center justify-center gap-2 p-3 text-primary font-medium hover:bg-primary/10 border-t border-gray-100 dark:border-white/10 transition-colors group"