From cc374ea7eb53b9fd7484acad142e8a580b2aa36e Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Mon, 17 Aug 2026 22:19:55 -0300 Subject: [PATCH] =?UTF-8?q?feat(PUB5):=20slogan=20'Mais=20sa=C3=BAde=20mai?= =?UTF-8?q?s=20futuro'=20no=20hero=20e=20rodap=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CHANGELOG-AGENTS.md | 2 + docs/changelog/2026-08-17-pub5.md | 1 + .../slogan-mais-saude-mais-futuro-impl.md | 60 +++++++++++++++++++ src/components/CampaignFooter.tsx | 2 +- src/components/CampaignHero.tsx | 6 +- tests/e2e/frontend.e2e.spec.ts | 2 +- tests/unit/campaignHero.unit.spec.tsx | 4 +- tests/unit/campaignHome.unit.spec.tsx | 4 +- 8 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 docs/changelog/2026-08-17-pub5.md create mode 100644 docs/plans/slogan-mais-saude-mais-futuro-impl.md diff --git a/docs/CHANGELOG-AGENTS.md b/docs/CHANGELOG-AGENTS.md index 31e9b9f2..2afc7ea1 100644 --- a/docs/CHANGELOG-AGENTS.md +++ b/docs/CHANGELOG-AGENTS.md @@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente --- +**PUB5** — slogan da campanha "Mais saúde mais futuro" no hero e no rodapé da home (jorgesolla1313.com.br), no lugar de "Um mandato do tamanho da Bahia" (discurso do mandato vigente; o novo é da campanha 2026 — saúde é o tema-âncora do candidato). Troca de copy cirúrgica, sem redesenho nem migration: h1 do hero em duas linhas uppercase "MAIS SAÚDE / MAIS FUTURO" com `aria-label` acompanhando ("MAIS SAÚDE MAIS FUTURO"), segunda linha já em `font-black` (anatomia existente), e o rodapé em sentence case com ponto ("Mais saúde mais futuro."). Testes que pinavam a string antiga atualizados (`campaignHero.unit`, `campaignHome.unit`, `frontend.e2e`). Fora de escopo decidido no gate: cabeçalho de artigos ("O mandato do tamanho da Bahia") e docs históricos. Verificação: unit 2269 + int 791 verdes, e2e frontend 8/8 em prod mode (`E2E_PROD=1`, `.next-e2e`), lint/format/typecheck/cycles/knip/build verdes; render conferido por screenshot + DOM (hero duas linhas, rodapé com ponto). `/simplify` 3 reviewers: sem P0/P1; P2 corrigidos no impl plan (redação + line refs); débitos descartados por serem convenções pré-existentes ou decisões do gate. + **Recently resolved (2026-08-17):** **OPS51** — o banco de produção saiu do Neon para o Postgres do homeserver (`teqo_1313`, container `teqo-1313`), sem mudança de config: o `~/stack/teqo-1313.env` já apontava para o banco preparado (criado 16/08 com role própria + cadeia completa de 56 migrations + zero conteúdo — verificado), então restaurar = a produção "apareceu" no container. **Divergência deliberada da intenção (questão A/B validada como B):** rejeitado criar `teqo_prod` novo — o argumento "o banco do 1313 tem vida própria" não se sustenta (o site 1313 É a produção deste repo) e o OPS53 já trata `teqo_1313` como produção. Execução 100% no homeserver (PII nunca tocou a workstation): URL do Neon puxada do Vercel (`vercel env pull`, projeto `jorgesolla`, `DATABASE_URL_UNPOOLED` → `NEON_DATABASE_URL` em `~/stack/.env` chmod 600), `pg_dump -Fc` (Neon PG 17.10) → `/srv/hdd/backups/teqo-neon-pre-migracao/teqo-neon-full-20260817-204800.dump` (5,8 MB, sha256 `f12196f2…`), `pg_restore --no-owner --no-privileges` como a role `teqo_1313` (drop schema + grant, padrão do `db-pull`), verificado: contagens por tabela idênticas ao Neon (1919 contatos, 39 posts, 1487 assinaturas, 1484 subscrições, 35 campaign_users, 392 lideranças, 2 pledges, 435 municípios, 6 atividades; supporter segue 0 — blocker LGPD ainda de pé), sequences no topo (`last_value == max(id)`), objetos com dono `teqo_1313`, `payload migrate` no-op (56 = 56, serviço de maintenance `teqo-1313-migrate`), 3 credenciais WebAuthn + 34 sessions + 5 chaves Consent restauradas (RP ID não mudou — biometria segue valendo). Smoke sem deploy: `/`, artigo com conteúdo restaurado, `/campanha/login`, `/admin`, `POST /api/revalidate` (`posts` + `global_privacy-policy`), WebAuthn `login-options`, barreira 307 sem sessão — verdes; smoke de browser (login, biometria, convites) validado manualmente. **Rollback documentado:** Neon nunca foi escrito (só lido); voltar = re-apontar `DATABASE_URL` do `teqo-1313.env` para o Neon + `compose up -d`. **Débito registrado (→ OPS52):** as 40 rows de `media` têm URLs relativas (`/api/media/file/…`) sem arquivos em lugar nenhum — capas dão 500 no homeserver e **404 no próprio Vercel/produção atual** (estado pré-existente; migração manteve paridade). Repo: plano da intenção concluído, `AGENTS.md` e help de `db:pull`/`check-push-chain` sem a referência ao Neon; infra-solla `STATE.md` atualizado à parte. **Recently resolved (2026-08-16):** **CAMPAIGN-HOME-RESPONSIVE**: a primeira dobra da home pública agora acompanha as referências do Penpot em 393, 768, 1024, 1280, 1366 e 1920 px. Retrato, aliados, marca, texto, CTAs e prova parlamentar interpolam entre os tamanhos com `clamp()`, `vw` e `dvh`, sem saltos por proporção de tela nem overflow horizontal; texto, ações e condecorações permanecem no fluxo e crescem com os ajustes de espaçamento da WCAG 1.4.12. No desktop, a escala e a posição horizontal das fotos dependem somente da largura: Solla, Lula e Wagner ficam ancorados ao rodapé real do hero até quando o conteúdo aumenta a seção, e um palco centralizado de 1920 px preserva a proximidade entre retrato e texto em monitores ultrawide. O E2E fixa a geometria das seis referências, larguras intermediárias, transições de proporção, desktops baixos, ultrawide e os overrides de acessibilidade; o caso de regressão também foi executado no Firefox. Sem migration, schema ou mudança de conteúdo. diff --git a/docs/changelog/2026-08-17-pub5.md b/docs/changelog/2026-08-17-pub5.md new file mode 100644 index 00000000..c069df72 --- /dev/null +++ b/docs/changelog/2026-08-17-pub5.md @@ -0,0 +1 @@ +**PUB5** — slogan da campanha "Mais saúde mais futuro" no hero e no rodapé da home (jorgesolla1313.com.br), no lugar de "Um mandato do tamanho da Bahia" (discurso do mandato vigente; o novo é da campanha 2026 — saúde é o tema-âncora do candidato). Troca de copy cirúrgica, sem redesenho nem migration: h1 do hero em duas linhas uppercase "MAIS SAÚDE / MAIS FUTURO" com `aria-label` acompanhando ("MAIS SAÚDE MAIS FUTURO"), segunda linha já em `font-black` (anatomia existente), e o rodapé em sentence case com ponto ("Mais saúde mais futuro."). Testes que pinavam a string antiga atualizados (`campaignHero.unit`, `campaignHome.unit`, `frontend.e2e`). Fora de escopo decidido no gate: cabeçalho de artigos ("O mandato do tamanho da Bahia") e docs históricos. Verificação: unit 2269 + int 791 verdes, e2e frontend 8/8 em prod mode (`E2E_PROD=1`, `.next-e2e`), lint/format/typecheck/cycles/knip/build verdes; render conferido por screenshot + DOM (hero duas linhas, rodapé com ponto). `/simplify` 3 reviewers: sem P0/P1; P2 corrigidos no impl plan (redação + line refs); débitos descartados por serem convenções pré-existentes ou decisões do gate. diff --git a/docs/plans/slogan-mais-saude-mais-futuro-impl.md b/docs/plans/slogan-mais-saude-mais-futuro-impl.md new file mode 100644 index 00000000..341b119e --- /dev/null +++ b/docs/plans/slogan-mais-saude-mais-futuro-impl.md @@ -0,0 +1,60 @@ +# Impl: Slogan da campanha "Mais saúde mais futuro" no hero e no rodapé + +Status: aprovado +Atualizado em: 2026-08-17 +Issue: #15 +Intenção: docs/plans/slogan-mais-saude-mais-futuro.md +Appetite restante: herdado (~0,25–0,5 dia; diff real é troca de 4 strings em 2 componentes + 3 testes) + +## Leitura da intenção + +- **Outcome:** o hero e o rodapé da home de campanha exibem o slogan novo "Mais saúde mais futuro" no lugar de "Um mandato do tamanho da Bahia", com o aria-label do h1 acompanhando e os testes que pinam a string antiga atualizados. +- **O que NÃO negociar:** copy fixa no componente (nada de campo editável no admin); anatomia do hero/rodapé intacta (sem redesenho — a referência do Penpot vale só para o texto); cabeçalho da página de artigos NÃO muda (decidido no gate). +- **O que reavaliar:** a hipótese de "segunda linha em peso maior" já é a anatomia atual do componente (`font-medium` → `font-black`) — não há mudança de classes, só de texto. + +## Abordagem recomendada + +```mermaid +flowchart LR + A[CampaignHero.tsx] -->|swap de 2 spans + aria-label| H1["MAIS SAÚDE / MAIS FUTURO"] + B[CampaignFooter.tsx] -->|swap de 1 linha| P["Mais saúde mais futuro."] + T[tests: hero + home + e2e] -->|atualiza name pinado| N["MAIS SAÚDE MAIS FUTURO"] +``` + +**Opções consideradas:** A | B | C +**Recomendação:** A — troca inline das strings nos dois componentes — porque é o menor diff possível, mantém a copy no componente (convenção atual) e toca exatamente o aceite. A estrutura de duas linhas já existe; a primeira linha segue `font-medium` e a segunda `font-black` (peso maior), como hoje. +**Rejeitadas:** + +- B — extrair o slogan para uma constante módulo compartilhado (`src/lib/campaignSlogan.ts`): duas ocorrências não justificam abstração; criaria um módulo novo para esconder duas strings. +- C — tornar o slogan editável via global/collection no admin: anti-goal explícito da intenção ("fora do pedido — copy fixa no componente, como hoje"). + +### Componentes / mudanças + +- **`CampaignHero`** (`src/components/CampaignHero.tsx:88-93`): `aria-label` → `"MAIS SAÚDE MAIS FUTURO"`; primeiro span `Mais saúde`, segundo span `Mais futuro` (JSX em title case; o `uppercase` já vem do CSS do h1, como hoje). +- **`CampaignFooter`** (`src/components/CampaignFooter.tsx:22`): linha do bloco de identificação → `Mais saúde mais futuro.` (sentence case com ponto — padrão tipográfico do rodapé, decidido no gate). +- **Migration:** sem migration (troca de strings em componentes). +- **Access / Consent:** N/A. +- **UI:** Impeccable A (encaixe de copy em superfícies existentes); anatomia, pesos e layout intactos; slogan novo é mais curto, sem risco de quebra de linha. + +## Fases verificáveis + +1. **Swap de strings** — `CampaignHero.tsx` + `CampaignFooter.tsx`. +2. **Testes** — atualizar os três nomes pinados: `tests/unit/campaignHero.unit.spec.tsx:21`, `tests/unit/campaignHome.unit.spec.tsx:24`, `tests/e2e/frontend.e2e.spec.ts:37` → `'MAIS SAÚDE MAIS FUTURO'` (mesmo formato do name atual: caixa alta, sem ponto). +3. **Gates** — `pnpm test` (unit+int), e2e do frontend, `tsc --noEmit`, `pnpm lint`, `pnpm format:check`, knip, `pnpm check:cycles`, `pnpm build`. + +## Rabbit holes / Não escopo (engenharia) + +- Caçar a frase antiga em `docs/campanha/*`, `.opencode/` ou skills — registro histórico, não superfície pública (corte da própria intenção). +- `src/app/(frontend)/artigos/page.tsx` ("O mandato do tamanho da Bahia") — fora de escopo decidido no gate. +- Ajustar pesos/classes do h1 para "ficar mais parecido com o Penpot" — o hero real já tem a segunda linha em `font-black`; mudar mais é redesenho. + +## Riscos e mitigação + +- **Name exato nos testes:** `getByRole` com `name` é match exato do accessible name (vem do `aria-label`) — o valor novo tem que bater sem variação de caixa/ponto. Mitigação: usar exatamente `'MAIS SAÚDE MAIS FUTURO'`, espelhando o padrão atual. +- **Rodapé sem teste pinado:** nenhum teste cobre a linha do rodapé — troca não quebra suíte, e o e2e da home segue cobrindo o hero. Sem teste novo (aceite não pede). + +## Aceite de engenharia + +- [x] Aceite de produto da intenção ainda coberto (hero duas linhas uppercase + peso maior na segunda; rodapé sentence case com ponto; frase antiga fora das superfícies públicas; testes atualizados) +- [x] Invariantes AGENTS/engineering-standards (sem migration, sem novo módulo, copy no dono) +- [x] Testes de domínio previstos onde strings pinadas mudam (3 arquivos) diff --git a/src/components/CampaignFooter.tsx b/src/components/CampaignFooter.tsx index 0e69d765..f895b2bc 100644 --- a/src/components/CampaignFooter.tsx +++ b/src/components/CampaignFooter.tsx @@ -19,7 +19,7 @@ export const CampaignFooter = () => (

Deputado Federal · PT · Bahia
- Um mandato do tamanho da Bahia. + Mais saúde mais futuro.

diff --git a/src/components/CampaignHero.tsx b/src/components/CampaignHero.tsx index 35636dc8..09734909 100644 --- a/src/components/CampaignHero.tsx +++ b/src/components/CampaignHero.tsx @@ -85,11 +85,11 @@ export const CampaignHero = () => (

- Um mandato do - tamanho da Bahia + Mais saúde + Mais futuro

diff --git a/tests/e2e/frontend.e2e.spec.ts b/tests/e2e/frontend.e2e.spec.ts index ec479aa1..d0455797 100644 --- a/tests/e2e/frontend.e2e.spec.ts +++ b/tests/e2e/frontend.e2e.spec.ts @@ -34,7 +34,7 @@ test.describe('Frontend', () => { const heading = page.getByRole('heading', { level: 1, - name: 'UM MANDATO DO TAMANHO DA BAHIA', + name: 'MAIS SAÚDE MAIS FUTURO', }) await expect(heading).toBeVisible() diff --git a/tests/unit/campaignHero.unit.spec.tsx b/tests/unit/campaignHero.unit.spec.tsx index e0f5d728..0091a69c 100644 --- a/tests/unit/campaignHero.unit.spec.tsx +++ b/tests/unit/campaignHero.unit.spec.tsx @@ -18,9 +18,7 @@ describe('CampaignHero', () => { it('apresenta a candidatura e mantém um único CTA primário', () => { render() - expect( - screen.getByRole('heading', { level: 1, name: 'UM MANDATO DO TAMANHO DA BAHIA' }), - ).toBeTruthy() + expect(screen.getByRole('heading', { level: 1, name: 'MAIS SAÚDE MAIS FUTURO' })).toBeTruthy() const supportLink = screen.getByRole('link', { name: /Quero apoiar/i }) expect(supportLink.getAttribute('href')).toBe('https://apoiar.me/jorgesolla') diff --git a/tests/unit/campaignHome.unit.spec.tsx b/tests/unit/campaignHome.unit.spec.tsx index 8b8e909d..765b1c40 100644 --- a/tests/unit/campaignHome.unit.spec.tsx +++ b/tests/unit/campaignHome.unit.spec.tsx @@ -21,9 +21,7 @@ describe('Campaign home', () => { it('monta as quatro seções previstas e o rodapé eleitoral', async () => { render(await HomePage()) - expect( - screen.getByRole('heading', { level: 1, name: 'UM MANDATO DO TAMANHO DA BAHIA' }), - ).toBeTruthy() + expect(screen.getByRole('heading', { level: 1, name: 'MAIS SAÚDE MAIS FUTURO' })).toBeTruthy() expect(screen.getByText('3.333')).toBeTruthy() expect(screen.getByText('1.031')).toBeTruthy() expect(screen.getByText('3º')).toBeTruthy()