O hero mostra 'Mais de X projetos desenvolvidos' com o detalhamento de publicos e privados; sem a contagem (token ausente ou API fora) o item some. A home passa a revalidar uma vez por dia. README documenta o token. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
201 lines
16 KiB
Markdown
201 lines
16 KiB
Markdown
<div align="center">
|
||
|
||
# Evandro.dev.br: software sob medida
|
||
|
||
Site comercial de Evandro Fonseca Junior para projetos de software sob medida: sistemas empresariais, aplicativos e integrações, com cases e contato direto por WhatsApp.
|
||
|
||
</div>
|
||
|
||
## Requisitos
|
||
|
||
- Node.js `22.x`
|
||
- pnpm `9.15.9`, fixado em `package.json#packageManager`
|
||
|
||
O projeto usa Next.js `16.3.3`, patch de segurança que corrige [GHSA-p293-qw3h-jr36](https://github.com/advisories/GHSA-p293-qw3h-jr36) e [GHSA-2xp9-vwfh-vxw4](https://github.com/advisories/GHSA-2xp9-vwfh-vxw4). A atualização substitui `16.3.0`, afetada por esses avisos. As auditorias de produção e desenvolvimento continuam sendo verificadas na CI.
|
||
|
||
## Início rápido
|
||
|
||
```bash
|
||
corepack [email protected] install --frozen-lockfile
|
||
corepack [email protected] dev
|
||
```
|
||
|
||
O servidor de desenvolvimento usa Turbopack. Para executar o artefato de produção:
|
||
|
||
```bash
|
||
corepack [email protected] build
|
||
corepack [email protected] start
|
||
```
|
||
|
||
## Scripts
|
||
|
||
| Comando | Comportamento |
|
||
| --- | --- |
|
||
| `pnpm dev` | Inicia o Next.js em desenvolvimento com Turbopack. |
|
||
| `pnpm build` | Gera o build de produção com Turbopack. |
|
||
| `pnpm start` | Serve um build já gerado. |
|
||
| `pnpm lint` | Executa `biome check`. |
|
||
| `pnpm typecheck` | Gera os tipos de rota com `next typegen` e executa TypeScript sem emitir arquivos. |
|
||
| `pnpm format` | Formata o repositório com Biome. |
|
||
| `pnpm test` | Executa a suíte Jest. |
|
||
| `pnpm test:watch` | Executa Jest em modo watch. |
|
||
| `pnpm check:projects` | Verifica URLs de demonstração e substitui atomicamente `data/project-health.json`. |
|
||
|
||
## Contagem de projetos do GitHub
|
||
|
||
O número "Mais de X projetos desenvolvidos" no topo da home vem da API do GitHub (`lib/github-stats.ts`), consultada só no servidor e renovada uma vez por dia (`revalidate = 86400` em `app/page.tsx`). Conta repositórios próprios, públicos e privados, sem forks e sem arquivados.
|
||
|
||
Para incluir os privados, defina `GITHUB_STATS_TOKEN` no ambiente de build e de execução (não use o prefixo `NEXT_PUBLIC_`). O token deve ser um [fine-grained personal access token](https://github.com/settings/personal-access-tokens/new) com acesso a todos os repositórios da conta e uma única permissão, **Metadata: Read-only**. Sem o token, ou se a consulta falhar, o número simplesmente não aparece.
|
||
|
||
## Stack
|
||
|
||
| Área | Pacotes usados |
|
||
| --- | --- |
|
||
| Aplicação | Next.js 16.3.3, React 19.1.0, React DOM 19.1.0 e TypeScript 5.9.2. |
|
||
| Estilos e composição | Tailwind CSS 4, `clsx` e `tailwind-merge`. |
|
||
| Movimento | Motion 12 e GSAP 3. |
|
||
| 3D e gráficos | Three.js, React Three Fiber e Drei. |
|
||
| Interface | Lucide React. |
|
||
| Qualidade | Biome, Jest, Testing Library e ambiente JSDOM. |
|
||
|
||
Todas as dependências diretas estão fixadas em versões exatas no manifesto; o lockfile é instalado sem atualização implícita.
|
||
|
||
## Rotas
|
||
|
||
| Rota | Conteúdo |
|
||
| --- | --- |
|
||
| `/` | Homepage comercial com hero, notebook 3D, projetos, soluções, processo de trabalho, engenharia com IA, critérios de entrega, perfil e contato. |
|
||
| `/cases` | Índice dos seis cases. |
|
||
| `/cases/nutriscan` | Case NutriScan. |
|
||
| `/cases/twins-lab` | Case Twins Lab. |
|
||
| `/cases/reddit-explorer` | Case Reddit Posts Explorer. |
|
||
| `/cases/sc-plus` | Case SC Plus Aquicultura. |
|
||
| `/cases/gcc-installer` | Case MinGW GCC Installer. |
|
||
| `/cases/as-servicos` | Case AS Serviços. |
|
||
| `/servicos` | Página de destino comercial: sites, sistemas e aplicativos, contato direto no WhatsApp, cases, processo e acesso aos seis serviços. |
|
||
| `/servicos/criacao-de-sites` | Criação de sites. |
|
||
| `/servicos/engenharia-de-software` | Sistemas web e aplicativos mobile sob medida. |
|
||
| `/servicos/sob-medida` | Redirecionamento permanente (308) para `/servicos/engenharia-de-software`, preservando query string, UTMs e identificadores de clique. |
|
||
| `/servicos/consultoria-nextjs` | Consultoria Next.js. |
|
||
| `/servicos/tauri-rust` | Aplicativos desktop para Windows e Linux, com compatibilidade definida por projeto. |
|
||
| `/servicos/integracao-hardware-software-iot` | Integração de hardware, software e IoT. |
|
||
| `/servicos/arquitetura-de-software` | Arquitetura de software. |
|
||
| `/curriculo` | Currículo em HTML, com download de `/Profile.pdf`. |
|
||
|
||
O App Router também gera `/manifest.webmanifest`, `/robots.txt`, `/sitemap.xml`, `/opengraph-image`, `/icon.png`, `/apple-icon.png` e `/favicon.ico`. Caminhos desconhecidos usam a página 404 do projeto. Não há API route ativa.
|
||
|
||
As páginas de serviços mantêm escopo, exemplos ilustrativos e etapas visíveis; tecnologias e decisões de implementação ficam em um `<details>` nativo. Na homepage, competências e experiência também ficam recolhidas. O header prioriza Soluções (`/#modalities`), Projetos (`/cases`), Como trabalho (`/#process`), Sobre (`/#about`) e Contato (`/#contact`), com ajuste de chegada após a definição do layout do notebook. Currículo e download do PDF são links secundários no final do rodapé.
|
||
|
||
## Comportamento da aplicação
|
||
|
||
### Conteúdo e project health
|
||
|
||
O conteúdo principal vem de `public/portfolio-data.json` e `lib/site-content.ts`. A seleção da homepage usa o campo `featured`; o snapshot de disponibilidade não remove cards. Ele apenas controla o estado do CTA de cada demo: disponível, indisponível, desconhecido ou sem demo.
|
||
|
||
Em produção, a renderização só lê o snapshot versionado e não consulta demos externas. Em desenvolvimento, um snapshot ausente ou com mais de 15 minutos é atualizado em memória. A atualização manual opcional é `pnpm check:projects` e grava o arquivo por rename atômico. O contrato completo está em [`docs/project-health.md`](docs/project-health.md).
|
||
|
||
### Posicionamento comercial e uso de IA
|
||
|
||
A comunicação apresenta Evandro como responsável direto pelo projeto, sem sugerir equipe, disponibilidade garantida, prazo fixo ou certificação. Os cards de projetos reutilizam os desafios e resumos dos cases quando disponíveis. O antigo painel de telemetria fictícia foi removido; os critérios de qualidade não representam notas de auditoria.
|
||
|
||
Cada card de projeto aparece ao entrar na tela, observado individualmente e sem recorte por `clip-path`. A revelação não depende de uma porcentagem visível do grid inteiro: em celulares, a lista de uma coluna pode ser várias vezes mais alta que a tela e tornar esse gatilho inalcançável.
|
||
|
||
`app/components/EngineeringWithAI.tsx` explica arquitetura, permissões, validação e recuperação de dados, com três relatos históricos de 2025 e links junto de cada afirmação:
|
||
|
||
- Lovable: exposição relatada pela Semafor e CVE-2025-48757, incluindo a contestação do fornecedor no NVD; exposição não é apresentada como roubo comprovado.
|
||
- Replit: exclusão de dados e restauração integral, conforme a resposta oficial da empresa.
|
||
- Base44: falha de autenticação identificada pela Wiz, corrigida em menos de 24 horas, sem evidência reportada de exploração maliciosa.
|
||
|
||
As obrigações dos pequenos agentes de tratamento são referenciadas à Resolução CD/ANPD nº 2/2022, arts. 6º, 12 e 13. Ao atualizar esse conteúdo, preservar datas, limites dos relatos e a distinção entre vulnerabilidade, exposição e incidente confirmado.
|
||
|
||
O formulário valida os campos no navegador e abre um rascunho no WhatsApp para revisão. Ele não envia a mensagem automaticamente e orienta a não informar senhas nem dados pessoais de clientes.
|
||
|
||
A seção de contato tem título estático único, canais diretos à esquerda e formulário à direita (empilhados no celular). A silhueta do WhatsApp aparece no contato direto, no cabeçalho do formulário e no botão verde. O formulário não se move no hover; campos mantêm rótulos, validação nativa e foco visível. O link direto dispensa preenchimento e o e-mail usa `mailto:`.
|
||
|
||
Na página `/servicos` e nos seis detalhamentos, `WhatsAppLink` oferece um link nativo com mensagem contextual, sem exigir o formulário da home. Funciona mesmo sem consentimento de analytics ou JavaScript; a medição depende da hidratação e da tag. A página principal prioriza as três intenções dos anúncios antes das especialidades técnicas e usa os cases existentes NutriScan e SC Plus como exemplos de trabalho, não como promessa de retorno comercial.
|
||
|
||
### 3D progressivo e movimento reduzido
|
||
|
||
O hero combina a mensagem comercial com três placas 3D: interface, integrações e dados. A interface usa a captura real do NutriScan, recortada acima do rodapé; as outras camadas são ilustrativas, não uma declaração da arquitetura do projeto. Ponteiro, arraste horizontal e quatro botões permitem explorar a composição. O toque vertical continua rolando a página. A renderização para quando os movimentos terminam, fora do viewport e com a aba oculta. Movimento reduzido ou falha gráfica mantêm a captura HTML, os controles e o link do case disponíveis.
|
||
|
||
O notebook 3D permanece em celulares, tablets e desktop: tampa, tela, abas e cursor seguem o progresso do scroll. No mobile, a câmera adapta o enquadramento e botões externos à tela do notebook permitem selecionar os projetos. A cena renderiza sob demanda e pausa fora do viewport ou com a página oculta. Movimento reduzido, falha de WebGL ou perda de contexto ativam a navegação HTML sem animação.
|
||
|
||
As quatro capturas desktop reais ficam em `public/assets/notebook/`, em WebP de 1600 × 1000. A troca aguarda a decodificação da próxima imagem, mantendo a anterior visível durante o carregamento ou uma falha.
|
||
|
||
Uma entrada normal na homepage começa no topo. A recarga também volta ao início, removendo o fragmento deixado por uma navegação anterior. Links diretos como `/#about` continuam chegando à seção após a estabilização do notebook; rolagem deliberada durante a abertura cancela esse ajuste. A política inicial de scroll é aplicada antes da hidratação, sem disputar com a rolagem suave dos links posteriores. Assim que a chegada se resolve, a restauração volta a pertencer ao navegador: Voltar e Avançar restauram a posição do histórico em vez de forçar o topo.
|
||
|
||
### Identidade visual
|
||
|
||
O header usa `--site-header-height`: 60px abaixo de 1280px e 68px a partir desse breakpoint. A mesma variável define a posição do menu, o respiro inicial do hero e a compensação das âncoras. O menu mobile tem fundo opaco; a barra fica levemente translúcida ao rolar. Header e hero usam Space Grotesk para leitura comercial, reservando a fonte monoespaçada aos detalhes técnicos das demais seções.
|
||
|
||
Os quatro arquivos recebidos estão preservados, com nomes descritivos, em `imagens/originais/`. As versões prontas para uso ficam em `public/brand/`:
|
||
|
||
| Arquivo | Aplicação |
|
||
| --- | --- |
|
||
| `wordmark-dark.webp` | Logo horizontal transparente, branca e lima, no cabeçalho escuro. |
|
||
| `wordmark-light.webp` | Logo horizontal transparente, escura e lima, no rodapé claro. |
|
||
| `stacked-light.webp` | Logo empilhada transparente, reservada para composições maiores em fundo claro. |
|
||
| `mark-dark.svg` / `mark-light.svg` | Símbolo vetorial para fundos escuros e claros, traçado a partir do original limpo. |
|
||
| `mark-dark.png` | Símbolo de 512px incorporado aos cards Open Graph de todas as rotas. |
|
||
| `icon-192.png` | Ícone de instalação do manifesto. |
|
||
|
||
`app/icon.png` (512px), `app/apple-icon.png` (180px) e `app/favicon.ico` (16–256px) usam o símbolo sobre fundo escuro. As variantes mantêm as formas originais; o recorte transparente com bordas danificadas permanece arquivado, sem uso na interface.
|
||
|
||
### Contato
|
||
|
||
O formulário da homepage valida os campos nativos de nome, e-mail, tipo e contexto. Ao enviar, ele abre `wa.me` em uma nova aba com o briefing preenchido; não existe backend de formulário. O rodapé mantém contato direto por e-mail.
|
||
|
||
### Consentimento de analytics
|
||
|
||
Google Analytics só é carregado depois de aceite explícito. A decisão fica em `evandro.analytics-consent.v1`; recusar não monta o loader. As preferências permanecem acessíveis e a revogação envia consentimento negado, ativa a flag de desabilitação, remove o loader e expira apenas cookies `_ga`/`_ga_*` acessíveis.
|
||
|
||
O único loader `gtag.js` configura GA4 `G-QTT9QT0T3S` e Google Ads `AW-18432694647`. Os IDs `GT-M3K4CKQT` e `GT-NBBR3PC2` informados no painel são identificadores das tags, não instalações adicionais. A integração segue https://developers.google.com/tag-platform/gtagjs/configure. `ad_storage`, `ad_user_data` e `ad_personalization` permanecem negados, com `ads_data_redaction` ativo. A tag só é montada em produção.
|
||
|
||
### Medição do contato e Google Ads
|
||
|
||
`lib/analytics.ts` envia `whatsapp_open` exclusivamente ao GA4 após o acionamento de um link ou a submissão válida do formulário. O evento representa **tentativa de abrir uma conversa**, não mensagem recebida, lead confirmado ou venda. O contato não espera a resposta do Analytics. Eventos recusados ou ocorridos antes do consentimento não são guardados para envio posterior.
|
||
|
||
Parâmetros enviados: `service` (rótulo estático), `placement` (posição do CTA) e `page_path`. A URL do evento não inclui query, fragmento ou referenciador; rotas dinâmicas são agrupadas como `/servicos/[slug]` e `/cases/[slug]`. Nome, e-mail, mensagem digitada e URL de destino do WhatsApp não entram nesse evento. A distinção do serviço usa o parâmetro `service`.
|
||
|
||
Posições: `service_hub_hero`, `service_hub_intent`, `service_hub_footer`, `service_detail_hero`, `service_detail_footer`, `contact_direct` e `contact_form`. Isso permite comparar entradas de contato pelo tipo de serviço e pela posição na página. Cliques pelo menu de contexto, bloqueadores e falhas de rede podem não ser medidos.
|
||
|
||
**Configuração externa ainda necessária:** verificar `whatsapp_open` no GA4, registrá-lo como evento principal, vincular a propriedade à conta Ads correta e importar essa ação de conversão. Conferir a ação existente antes para evitar dupla contagem; não importar simultaneamente um clique automático como a mesma conversão. Para analisar os parâmetros em Explorações, cadastrar dimensões personalizadas de escopo de evento para `service` e `placement`. Nenhum rótulo de conversão Ads foi inventado e nenhuma configuração da conta foi alterada. A medição otimiza intenção de contato, não vendas.
|
||
|
||
Também revisar as opções de medição otimizada da propriedade, sobretudo eventos automáticos de cliques externos e formulários, pois essa configuração não é controlada por este código. Referência dos eventos: https://developers.google.com/analytics/devguides/collection/ga4/events.
|
||
|
||
Verificação local: build de produção, navegação responsiva, redirecionamento com parâmetros e requisição real `whatsapp_open` gerada pela tag. Requisições de coleta foram interceptadas antes do envio para não contaminar os relatórios; isso **não comprova recebimento no GA4 nem atribuição no Ads**.
|
||
|
||
## Estrutura
|
||
|
||
```text
|
||
app/ App Router, rotas e componentes da interface
|
||
lib/ Dados derivados, navegação, JSON-LD e project health
|
||
public/ Conteúdo JSON, PDF, fontes, imagens e modelo 3D
|
||
scripts/check-projects.mjs
|
||
Atualização manual do snapshot de demos
|
||
data/project-health.json
|
||
Última observação versionada das demos
|
||
docs/project-health.md Contrato operacional do snapshot
|
||
.github/workflows/ci.yml
|
||
Gate único de integração contínua
|
||
nixpacks.toml Build e start para Nixpacks
|
||
```
|
||
|
||
## CI e deploy
|
||
|
||
O workflow único em `.github/workflows/ci.yml` roda em cada `push` e `pull_request` com permissão `contents: read`. Ele instala Node 22 e pnpm conforme `packageManager`, restaura o cache do pnpm e executa, nesta ordem: frozen install, lint, typecheck, Jest serial, build e auditorias de produção e desenvolvimento com limite high. O passo de typecheck gera os tipos de rota antes do `tsc`: `next-env.d.ts` não é versionado e, em uma árvore recém-clonada, é ele que declara os módulos de imagem importados pelo código.
|
||
|
||
O Nixpacks usa `nodejs_22`, `pnpm-9_x`, `pnpm install --frozen-lockfile`, `pnpm run build` e `pnpm run start`. Não há etapa de deploy nem secret de CI neste repositório.
|
||
|
||
## Validação local
|
||
|
||
```bash
|
||
corepack [email protected] install --frozen-lockfile
|
||
corepack [email protected] lint
|
||
corepack [email protected] typecheck
|
||
corepack [email protected] test -- --runInBand
|
||
corepack [email protected] build
|
||
corepack [email protected] audit --prod --audit-level=high
|
||
corepack [email protected] audit --dev --audit-level=high
|
||
```
|