docs: redesign do README (padrão TwitterBR-MOD) — dados atualizados
CI / verify (push) Failing after 5m26s
CI / verify (push) Failing after 5m26s
This commit is contained in:
1 parent
8b12e51bc5
commit
ea05ce4a5f
1 file changed
+165
-84
@@ -1,112 +1,144 @@
|
|||||||
<div align="center">
|
<div align="center">
|
||||||
|
|
||||||
# Evandro.dev.br: portfólio
|
<img src="public/edev_logo.svg" width="110" alt="Logo do Evandro.dev.br">
|
||||||
|
|
||||||
Portfólio profissional de Evandro Fonseca Junior, com projetos, cases, serviços, currículo e contato por WhatsApp.
|
# Evandro.dev.br — portfólio
|
||||||
|
|
||||||
|
**Portfólio profissional de Evandro Fonseca Junior: projetos, cases, serviços,
|
||||||
|
currículo e contato por WhatsApp — com hero e notebook 3D progressivos que caem
|
||||||
|
para HTML puro quando o dispositivo não comporta.**
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
[Rotas](#o-que-o-site-entrega) · [Como funciona](#como-funciona) · [Execução local](#execu%C3%A7%C3%A3o-local) · [CI/CD](#cicd)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Requisitos
|
> [!NOTE]
|
||||||
|
> **Estado do projeto:** em desenvolvimento ativo (último commit: "refine portfolio
|
||||||
|
> showcase, services and quality lab"); o site de produção é `https://evandro.dev.br`,
|
||||||
|
> que é o host declarado em `app/sitemap.ts` e `app/robots.ts`. As ações externas
|
||||||
|
> de SEO/GEO (Search Console, Bing, Cloudflare, CrUX) estão descritas como runbook e
|
||||||
|
> **não executadas** em [`docs/seo-geo-operations.md`](docs/seo-geo-operations.md).
|
||||||
|
|
||||||
- Node.js `22.x`
|
> [!IMPORTANT]
|
||||||
- pnpm `9.15.9`, fixado em `package.json#packageManager`
|
> **Next.js `16.3.0` é intencional.** A `16.2.11` ainda mantinha três advisories
|
||||||
|
> transitivos de severidade alta; a `16.3.0` foi a menor versão estável da mesma
|
||||||
|
> major que deixou as auditorias atuais sem vulnerabilidades high. Não faça
|
||||||
|
> downgrade sem repetir `pnpm audit`.
|
||||||
|
|
||||||
O projeto usa Next.js `16.3.0`. Essa versão é intencional: `16.2.11` ainda mantinha três advisories transitivos de severidade alta; `16.3.0` foi a menor versão estável da mesma major que deixou as auditorias atuais sem vulnerabilidades high.
|
## O que o site entrega
|
||||||
|
|
||||||
## 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` | 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`. |
|
|
||||||
|
|
||||||
## Stack
|
|
||||||
|
|
||||||
| Área | Pacotes usados |
|
|
||||||
| --- | --- |
|
|
||||||
| Aplicação | Next.js 16.3.0, 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, Drei e Cobe. |
|
|
||||||
| 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 |
|
| Rota | Conteúdo |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `/` | Homepage com hero, notebook de projetos, perfil, competências, experiência, projetos, modalidades de trabalho e contato. |
|
| `/` | Homepage com hero, notebook de projetos, perfil, competências, experiência, projetos, modalidades de trabalho e contato |
|
||||||
| `/cases` | Índice dos seis cases. |
|
| `/cases` | Índice dos seis cases |
|
||||||
| `/cases/nutriscan` | Case NutriScan. |
|
| `/cases/nutriscan` | Case NutriScan |
|
||||||
| `/cases/twins-lab` | Case Twins Lab. |
|
| `/cases/twins-lab` | Case Twins Lab |
|
||||||
| `/cases/reddit-explorer` | Case Reddit Posts Explorer. |
|
| `/cases/reddit-explorer` | Case Reddit Posts Explorer |
|
||||||
| `/cases/sc-plus` | Case SC Plus Aquicultura. |
|
| `/cases/sc-plus` | Case SC Plus Aquicultura |
|
||||||
| `/cases/gcc-installer` | Case MinGW GCC Installer. |
|
| `/cases/gcc-installer` | Case MinGW GCC Installer |
|
||||||
| `/cases/as-servicos` | Case AS Serviços. |
|
| `/cases/as-servicos` | Case AS Serviços |
|
||||||
| `/servicos` | Índice dos seis serviços, com explicações de uso e acesso ao detalhamento técnico. |
|
| `/servicos` | Índice dos seis serviços, com explicações de uso e acesso ao detalhamento técnico |
|
||||||
| `/servicos/criacao-de-sites` | Criação de sites. |
|
| `/servicos/criacao-de-sites` | Criação de sites |
|
||||||
| `/servicos/engenharia-de-software` | Sistemas web e aplicativos mobile sob medida. |
|
| `/servicos/engenharia-de-software` | Sistemas web e aplicativos mobile sob medida |
|
||||||
| `/servicos/consultoria-nextjs` | Consultoria Next.js. |
|
| `/servicos/consultoria-nextjs` | Consultoria Next.js |
|
||||||
| `/servicos/tauri-rust` | Aplicativos desktop para Windows e Linux, com compatibilidade definida por projeto. |
|
| `/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/integracao-hardware-software-iot` | Integração de hardware, software e IoT |
|
||||||
| `/servicos/arquitetura-de-software` | Arquitetura de software. |
|
| `/servicos/arquitetura-de-software` | Arquitetura de software |
|
||||||
| `/curriculo` | Currículo em HTML, com download de `/Profile.pdf`. |
|
| `/curriculo` | Currículo em HTML, com download de `/Profile.pdf` |
|
||||||
|
| `/privacidade` | Página de privacidade e consentimento de analytics |
|
||||||
|
|
||||||
O App Router também gera `/manifest.webmanifest`, `/robots.txt`, `/sitemap.xml`, `/opengraph-image`, `/icon.svg` e `/favicon.ico`. Caminhos desconhecidos usam a página 404 do projeto. Não há API route ativa.
|
O App Router também gera `/manifest.webmanifest`, `/robots.txt`, `/sitemap.xml`,
|
||||||
|
`/opengraph-image`, `/icon.svg` 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. Sobre e Contato apontam para `/#about` e `/#contact`, com ajuste de chegada após a definição do layout do notebook.
|
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. Sobre e
|
||||||
|
Contato apontam para `/#about` e `/#contact`, com ajuste de chegada após a
|
||||||
|
definição do layout do notebook.
|
||||||
|
|
||||||
## Comportamento da aplicação
|
## Como funciona
|
||||||
|
|
||||||
### Conteúdo e project health
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A["public/portfolio-data.json<br/>+ lib/site-content.ts"] --> B["lib/portfolio-data.ts<br/>loadFeaturedProjects()"]
|
||||||
|
C["data/project-health.json<br/>snapshot versionado"] --> B
|
||||||
|
D["scripts/check-projects.mjs<br/>pnpm check:projects"] -->|rename atômico| C
|
||||||
|
B --> E["app/ — App Router<br/>homepage, cases, serviços"]
|
||||||
|
E --> F["Next.js 16.3.0<br/>build e start (Turbopack)"]
|
||||||
|
```
|
||||||
|
|
||||||
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.
|
<details>
|
||||||
|
<summary>Conteúdo e project health</summary>
|
||||||
|
|
||||||
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).
|
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.
|
||||||
|
|
||||||
### 3D progressivo e movimento reduzido
|
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).
|
||||||
|
|
||||||
O hero 3D é carregado no cliente a partir de `1024px`, sem `prefers-reduced-motion: reduce` e sem ponteiro coarse. O notebook 3D exige mais de `1024px`, hover disponível e as mesmas condições de movimento e ponteiro. Nos demais casos, o notebook entrega a navegação HTML completa sem WebGL ou movimento. No desktop, tampa, tela, abas e cursor seguem o mesmo progresso de scroll; a cena renderiza sob demanda e pausa fora do viewport ou com a página oculta.
|
</details>
|
||||||
|
|
||||||
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.
|
<details>
|
||||||
|
<summary>3D progressivo e movimento reduzido</summary>
|
||||||
|
|
||||||
### Contato
|
O hero 3D é carregado no cliente a partir de `1024px`, sem
|
||||||
|
`prefers-reduced-motion: reduce` e sem ponteiro coarse. O notebook 3D exige mais
|
||||||
|
de `1024px`, hover disponível e as mesmas condições de movimento e ponteiro. Nos
|
||||||
|
demais casos, o notebook entrega a navegação HTML completa sem WebGL ou
|
||||||
|
movimento. No desktop, tampa, tela, abas e cursor seguem o mesmo progresso de
|
||||||
|
scroll; a cena renderiza sob demanda e pausa fora do viewport ou com a página
|
||||||
|
oculta.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
### Consentimento de analytics
|
</details>
|
||||||
|
|
||||||
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.
|
<details>
|
||||||
|
<summary>Contato e consentimento de analytics</summary>
|
||||||
|
|
||||||
## Estrutura
|
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.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Stack e estrutura</summary>
|
||||||
|
|
||||||
|
| Área | Pacotes usados |
|
||||||
|
| --- | --- |
|
||||||
|
| Aplicação | Next.js 16.3.0, 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, Drei e Cobe |
|
||||||
|
| 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.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
app/ App Router, rotas e componentes da interface
|
app/ App Router, rotas e componentes da interface
|
||||||
lib/ Dados derivados, navegação, JSON-LD e project health
|
lib/ Dados derivados, navegação, JSON-LD e project health
|
||||||
public/ Conteúdo JSON, PDF, fontes, imagens e modelo 3D
|
public/ Conteúdo JSON, PDF, fontes, imagens e capturas
|
||||||
scripts/check-projects.mjs
|
scripts/check-projects.mjs
|
||||||
Atualização manual do snapshot de demos
|
Atualização manual do snapshot de demos
|
||||||
data/project-health.json
|
data/project-health.json
|
||||||
@@ -117,13 +149,44 @@ docs/project-health.md Contrato operacional do snapshot
|
|||||||
nixpacks.toml Build e start para Nixpacks
|
nixpacks.toml Build e start para Nixpacks
|
||||||
```
|
```
|
||||||
|
|
||||||
## CI e deploy
|
</details>
|
||||||
|
|
||||||
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.
|
## Limitações conhecidas
|
||||||
|
|
||||||
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.
|
- **Sem backend.** O formulário de contato não grava nada: abre `wa.me` com o
|
||||||
|
briefing preenchido.
|
||||||
|
- **Demos externas podem aparecer como indisponíveis.** O CTA de cada case reflete
|
||||||
|
o último snapshot versionado em `data/project-health.json`, não uma consulta em
|
||||||
|
tempo real (em produção o render não faz fetch externo).
|
||||||
|
- **Sem WebGL em telas pequenas, com ponteiro coarse ou movimento reduzido.** Nesses
|
||||||
|
casos o hero e o notebook entregam a versão HTML.
|
||||||
|
|
||||||
## Validação local
|
## Execução local
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Use **Node.js `22.x`** e **pnpm `9.15.9`** (fixado em `package.json#packageManager`).
|
||||||
|
> A instalação é sempre com `--frozen-lockfile`; o CI e o Nixpacks fazem o mesmo.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
corepack [email protected] install --frozen-lockfile
|
||||||
|
corepack [email protected] dev # Next.js com Turbopack
|
||||||
|
corepack [email protected] build # build de produção com Turbopack
|
||||||
|
corepack [email protected] start # serve um build já gerado
|
||||||
|
```
|
||||||
|
|
||||||
|
| Script | 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` | 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` |
|
||||||
|
|
||||||
|
Validação completa, na mesma ordem do CI:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
corepack [email protected] install --frozen-lockfile
|
corepack [email protected] install --frozen-lockfile
|
||||||
@@ -134,3 +197,21 @@ corepack [email protected] build
|
|||||||
corepack [email protected] audit --prod --audit-level=high
|
corepack [email protected] audit --prod --audit-level=high
|
||||||
corepack [email protected] audit --dev --audit-level=high
|
corepack [email protected] audit --dev --audit-level=high
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## CI/CD
|
||||||
|
|
||||||
|
O workflow único [`.github/workflows/ci.yml`](.github/workflows/ci.yml) roda em
|
||||||
|
cada `push` e `pull_request`, com permissão `contents: read`. No job `verify`
|
||||||
|
(`ubuntu-latest`) 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 Nixpacks ([`nixpacks.toml`](nixpacks.toml)) 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.
|
||||||
|
|
||||||
|
## Avisos
|
||||||
|
|
||||||
|
- As capturas em `public/assets/prints/*.png` foram feitas com Chrome headless a
|
||||||
|
partir de deploys de produção do próprio autor (evandro.dev.br) e aparecem como
|
||||||
|
conteúdo da tela do notebook. Detalhes em [`CREDITS.md`](CREDITS.md).
|
||||||
Reference in new issue
Block a user