diff --git a/docs/plans/compartilhar-conteudos-home-whatsapp.md b/docs/plans/compartilhar-conteudos-home-whatsapp.md new file mode 100644 index 00000000..f58c4283 --- /dev/null +++ b/docs/plans/compartilhar-conteudos-home-whatsapp.md @@ -0,0 +1,85 @@ +# Compartilhar conteúdos da home no WhatsApp (S4) + +Status: registrado +Atualizado em: 2026-08-17 +Issue: #19 +Priority: P2 +Model: composer-2.5 +Impeccable: B — botão discreto de compartilhar por card de conteúdo (não compete com o CTA) +Rascunho UI: docs/plans/secao-conteudos-home-ui-draft.html + PNG embutido abaixo +Appetite: ~0,5–1 dia eng; botão por card + wa.me com mensagem pré-configurada + copiar link +Responsável: — + +## Intenção + +Depois que a seção de conteúdos da home estiver funcional (S1+S2+S3), cada card de conteúdo vira compartilhável no WhatsApp — o canal nº 1 de compartilhamento no Brasil. O visitante manda o artigo/vídeo/post para alguém com uma mensagem pronta (título + link), sem sair da home. É a fatia de viralidade do board: conteúdo circulando de volta para a campanha na reta final. + +## Persona e fluxo + +- **Persona / contexto:** visitante (eleitor, liderança, simpatizante) no meio de funil que achou um conteúdo bom e quer mandar para o grupo/família. +- **Job principal:** repassar um conteúdo específico da home para alguém no WhatsApp com um clique, com texto pronto. +- **Fluxo desejado:** vê o card → toca no botão de compartilhar (ícone discreto no canto do card) → opção "Compartilhar no WhatsApp" (abre `wa.me` com mensagem pré-configurada contendo título + link do conteúdo) e/ou "Copiar link" → cola onde quiser. +- **Anti-goals de produto:** o botão de compartilhar NUNCA compete com o CTA primário da página (botão discreto, não é o foco do card); não é compartilhamento por seção inteira (v1 é por card); sem WhatsApp Business API (é `wa.me` do próprio remetente). + +### Esboço de fluxo (B) + +```text +[card de conteúdo] → [botão compartilhar (ícone)] → [menu: WhatsApp / Copiar link] +→ [wa.me com mensagem "Olha isso do Solla: — "] → [remetente envia pelo próprio WhatsApp] +``` + +### Rascunho UI (B) + +![Rascunho UI — compartilhar](secao-conteudos-home-ui-draft-compartilhar.png) + +Fonte iterável: [`secao-conteudos-home-ui-draft.html`](secao-conteudos-home-ui-draft.html) (cena `compartilhar`). + +## Objetivo e aceite + +- Todo card de conteúdo da seção (Artigo/YouTube/Instagram) tem um botão discreto de compartilhar (canto do card, sem competir com o CTA primário da página). +- Ação principal: abre `wa.me` com mensagem pré-configurada **por fonte** (decisão do cliente): artigo → "Olha isso do Solla: {título} — {link}" · vídeo → "Olha esse vídeo do Solla: {título} — {link}" · post IG → "Olha esse post do Solla: {título} — {link}". Link: artigo → página canônica do artigo; YT/IG → URL da plataforma. Nova aba com `noopener`. +- Ação secundária: "Copiar link" (navegador/clipboard) com feedback de "copiado". +- O botão é acessível (aria-label, alvo de toque ≥44px no mobile) e não muda o layout do card. No mobile, o botão vive no card único do carrossel (1 conteúdo por tela — decisão do cliente); no desktop, no canto de cada card do bento. +- Sem migration/schema/Consent — o fluxo é montagem de URL no cliente, sem escrita no servidor. +- Mensagem padrão honesta (ex.: "Olha isso do Solla: {título} — {link}") — sem texto enganoso. + +## Dados (intenção) + +- **Vou apresentar dados?** Não — só a URL compartilhada. (Se a assessoria quiser medir circulação, UTM por card é um item futuro separado.) +- **Decisões desbloqueadas:** o visitante decide compartilhar; nenhum dado é coletado por este fluxo. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** componente client pequeno no card de conteúdo da seção S1/S2/S3 (`src/components/`), reusando `buildWhatsAppUrl` (`src/utilities/phone.ts`) e o padrão de copiar link já existente (ex.: `ShareNucleusDialog`/`campaignListShare` — ver `docs/plans/compartilhar-pagina.md` como precedente de wa.me + copiar). +- **Precedente a olhar:** `compartilhar-pagina.md` (padrão `wa.me` + copiar link + `noopener`), `src/utilities/phone.ts` (`buildWhatsAppUrl`), plano-site §7.1 ("Compartilhamento por seção" — ideia do cliente). +- **Risco de acoplamento:** o botão vive no card generalizado da seção — implementar no ponto único de renderização do card (não em três lugares por plataforma). + +## Dependências + +- **S3** (seção completa: artigos + YouTube + Instagram) — dura (conteúdos funcionais antes de compartilhar). + +## Fora de escopo + +- Compartilhar por outros canais (e-mail, X, threads) — só WhatsApp + copiar link no v1. +- UTM por seção/card e medição de circulação (item futuro — ideia §7.1 do plano-site). +- Imagem OG por seção/card (geração de card de preview) — item futuro (§7.1). +- WhatsApp Business API / disparo em massa (vedado pela Res. TSE 23.610; é `wa.me` do próprio remetente). + +## Rabbit holes de produto + +- **Botão de compartilhar competindo com o CTA.** Se alguém "só completar": dois pesos visuais por card. **Corte:** ícone discreto no canto, sem destaque de cor de marca. +- **Compartilhar a seção inteira.** Se alguém "só completar": mensagem gigante com N links. **Corte:** v1 é por card, um link por mensagem. +- **Telemetria disfarçada de compartilhar.** Se alguém "só completar": vira rastreador de clique. **Corte:** nada é coletado neste item; UTM fica para item futuro explícito. + +## Questões em aberto (produto) + +- **Mensagem padrão por fonte?** **Opções:** A) template único "Olha isso do Solla: {título} — {link}" | B) específica por fonte (artigo/vídeo/post). **Recomendação:** B — decisão do cliente nesta sessão. _(decidido — B)_ + +## Referências + +- GitHub Issue #19 +- Rascunho UI (gate): `docs/plans/secao-conteudos-home-ui-draft.html` (cena `compartilhar`) + PNG acima +- `docs/campanha/plano-site-campanha-2026.md` §7.1 — "Compartilhamento por seção" (ideia do cliente registrada) +- `docs/plans/compartilhar-pagina.md` — precedente de wa.me + copiar link no repo +- `src/utilities/phone.ts` — `buildWhatsAppUrl` +- `docs/plans/secao-conteudos-home-artigos.md` (S1) — estrutura do card onde o botão entra diff --git a/docs/plans/secao-conteudos-home-artigos.md b/docs/plans/secao-conteudos-home-artigos.md new file mode 100644 index 00000000..cb97a3c2 --- /dev/null +++ b/docs/plans/secao-conteudos-home-artigos.md @@ -0,0 +1,93 @@ +# Seção de conteúdos na home de campanha — Artigos (S1) + +Status: registrado +Atualizado em: 2026-08-17 +Issue: #17 +Priority: P1 +Model: composer-2.5 +Impeccable: B — nova seção na home pública (3ª posição), padrão visual do wireframe já aprovado +Rascunho UI: docs/plans/secao-conteudos-home-ui-draft.html + PNGs embutidos abaixo +Appetite: ~1 dia eng; seção com cards de artigos reusando o sistema `post` existente +Responsável: — + +## Intenção + +A home pública de campanha (jorgesolla1313.com.br) não tem nenhuma zona de conteúdo atualizado: ela é estática entre o hero e o CTA. Na reta final (eleição 04/10), as coisas acontecem rápido e o site precisa mostrar atividade. O wireframe já tem a seção "Acompanhe de perto" (board de conteúdo) aprovada — ela só não foi implementada. Este item é a primeira fatia da seção, com **artigos** do site (a fonte que já existe no Teqo); YouTube e Instagram entram nas fatias seguintes (S2/S3), em série, para a estrutura não divergir. + +## Persona e fluxo + +- **Persona / contexto:** visitante da home de campanha (eleitor, liderança, imprensa) — celular na maioria dos casos, chegou pelo WhatsApp/redes, no meio de funil. +- **Job principal:** ver que a campanha está viva agora (prova de atividade) e ler um conteúdo recente sem sair do clima da página. +- **Fluxo desejado:** rola a home → encontra a seção (3ª, logo antes de "Por que essa eleição importa") → vê cards de artigos recentes com badge "Artigo" → clica e vai para a página do artigo (rota canônica do post) → pode voltar e continuar na home. +- **Anti-goals de produto:** a seção NÃO vira um feed infinito; NÃO compete com o CTA primário; NÃO mostra conteúdo invisível/oculto (mecanismo `hidden`/`isPostVisible` segue valendo — fail-closed). + +### Esboço de fluxo (B) + +```text +[home: hero → prova social] → [SEÇÃO: eyebrow + título + cards artigos] → [clique no card] +→ [página do artigo: /[type]/[category]/[slug]] → [voltar à home] +``` + +### Rascunho UI (B) + +![Rascunho UI — desktop](secao-conteudos-home-ui-draft-desktop.png) + +![Rascunho UI — mobile](secao-conteudos-home-ui-draft-mobile.png) + +Fonte iterável: [`secao-conteudos-home-ui-draft.html`](secao-conteudos-home-ui-draft.html). Nesta fatia S1, o bento mostra só cards "Artigo" (a cena `desktop` já ilustra a posição final com as 3 fontes para referência). + +## Objetivo e aceite + +- A home pública exibe uma nova seção na 3ª posição (entre a prova social e "Por que essa eleição importa"), com eyebrow/título/subtitulo e links de acompanhar conforme o wireframe. +- A seção lista artigos recentes visíveis do sistema `post` existente (mesma regra de visibilidade do site: `isPostVisible` fail-closed — conteúdo oculto por tag `hidden` não aparece). +- Cada card mostra: badge "Artigo", imagem de capa, título e meta (data/categoria); o card inteiro é um link para a página canônica do artigo (`getPostCanonicalPath`), abrindo na mesma aba. +- Layout: bento 1 card grande + 4 pequenos no desktop; **no mobile (~390px) a seção vira um carrossel de 1 conteúdo por tela** (desliza entre os cards, com indicador de página), sem quebrar o fluxo da página. +- Se não houver artigos visíveis: a seção inteira é ocultada (nada de zona morta), mantendo Prova → "Por que essa eleição importa" (cena `vazio` do rascunho). +- Zero mudança em schema/migration/Consent: artigos leem o `post` collection com os helpers de cache existentes (`getVisiblePosts` / tag `posts`). + +## Dados (intenção) + +- **Vou apresentar dados?** Sim, superfície neste item — mas são **conteúdos** (artigos), não métricas. +- **Decisões desbloqueadas:** o visitante decide qual artigo ler (clique); nenhum número novo de campanha é inventado aqui. +- **Forma:** *adiada ao plano de implementação* — restrição de produto: meta = data/categoria do post (já exibidas no site), sem contadores inventados. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/app/(frontend)/(home)/page.tsx` (inserir a seção entre prova e problema), novo componente de seção/card em `src/components/`, estilos no bloco `[data-theme='campaign-site']` de `src/app/(frontend)/styles.css` (padrão das seções atuais), leitura via `src/utilities/posts.ts` (`getVisiblePosts`, `getPostCanonicalPath`, `formatPostDate`). +- **Precedente a olhar:** wireframe `docs/campanha/wireframe-solla-1313.html` (seção 7, board IG+YT — o design aprovado a adaptar para artigos); seções atuais da home (`campaign-problem`, `campaign-flags`) para o ritmo visual; `src/components/PostCard.tsx` e a rota `/artigos` para o formato de card de post existente. +- **Risco de acoplamento:** a seção respeita o mecanismo eleitoral `hidden`/`isPostVisible` (nada de bypass); o cache público é o tag `posts` existente (revalidação já cobre); não duplicar o conceito de "pessoa" nem criar collection nova. + +## Dependências + +- Nenhuma (primeira fatia). S2 (YouTube) e S3 (Instagram) dependem desta. +- Soft: `docs/campanha/plano-site-campanha-2026.md` — decisões de produto do site (board §4.2, posição discutida na §3). + +## Fora de escopo + +- YouTube e Instagram na seção → S2 e S3 (deste lote). +- Compartilhamento WhatsApp → S4 (issue separada). +- Player de vídeo inline, lightbox, qualquer embed de rede no load (decisão do plano-site: clique abre na plataforma; sem iframe no carregamento). +- Curadoria manual do feed (é automático pelo mais recente), configuração no admin, "pausar feed" (kill switch) → S2/S3. +- Redesign da home, alteração de CTA primário, outras seções do wireframe. + +## Rabbit holes de produto + +- **Feed infinito / paginação.** Se alguém "só completar": a seção vira uma lista sem fim competindo com o CTA. **Corte neste item:** bento fixo (1 + 4), sem paginação; link "Ver artigos →" aponta para a rota `/artigos` existente. +- **Segunda fonte de "artigos" paralela ao `post`.** Se alguém "só completar": duplica o sistema de notícias. **Corte:** só o `post` collection com a visibilidade pública existente. +- **Vazio honesto.** A seção oculta quando sem conteúdo — não exibe placeholder de "em breve" queimando o meio de funil. + +## Questões em aberto (produto) + +- **Posição exata da seção?** **Opções:** A) 3ª seção, logo antes de "Por que essa eleição importa" (recomendação — traz conteúdo atualizado pra cima; decisão do cliente nesta sessão) | B) posição 7 do wireframe (após "Quem é Jorge Solla"). **Recomendação:** A. _(decidido — A)_ +- **Quais artigos entram no bento?** **Opções:** A) os N mais recentes visíveis, qualquer tipo de post (noticia/campanha/artigo/evento) | B) só `noticia` (igual à rota `/artigos`). **Recomendação:** A — a home de campanha agrega tudo que estiver visível; o filtro por tipo fica barato depois se a assessoria pedir. _(decidido — A)_ +- **Mobile: 1 conteúdo por tela?** **Opções:** A) carrossel de 1 card por vez (decisão do cliente nesta sessão) | B) empilhado. **Recomendação:** A. _(decidido — A)_ + +## Referências + +- GitHub Issue #17 +- Rascunho UI (gate): `docs/plans/secao-conteudos-home-ui-draft.html` + PNGs acima +- Wireframe aprovado: `docs/campanha/wireframe-solla-1313.html` (seção 7 "Acompanhe de perto") +- Plano geral do site: `docs/campanha/plano-site-campanha-2026.md` (§4.2 engenharia do board, §3 posições) +- `src/utilities/posts.ts` — visibilidade/cache/rotas canônicas de artigos +- `src/app/(frontend)/(home)/page.tsx` — onde a seção entra +- `AGENTS.md` — Posts & Tags (visibilidade `hidden` fail-closed, cache `posts`) diff --git a/docs/plans/secao-conteudos-home-instagram.md b/docs/plans/secao-conteudos-home-instagram.md new file mode 100644 index 00000000..15b5c2e9 --- /dev/null +++ b/docs/plans/secao-conteudos-home-instagram.md @@ -0,0 +1,93 @@ +# Seção de conteúdos na home de campanha — Instagram (S3) + +Status: registrado +Atualizado em: 2026-08-17 +Issue: #20 +Priority: P1 +Model: cursor-grok-4.5-medium +Impeccable: B — estende a seção S1/S2 com cards de Instagram (mesma estrutura, badge "Instagram") +Rascunho UI: docs/plans/secao-conteudos-home-ui-draft.html + PNGs embutidos abaixo +Appetite: ~1–2 dias eng; integração de leitura com a Graph API + cards na seção +Responsável: — + +## Intenção + +A seção "Acompanhe de perto" (S1 artigos + S2 YouTube) ganha os posts do Instagram oficial (`@depjorgesolla`). Instagram é onde a campanha mais posta no dia a dia (bastidores, caravanas, memes, avisos) — é a fonte que dá o ritmo de "atividade agora". A fatia segue o desenho aprovado do plano-site: automático via Instagram Graph API (conta Business/Creator + app Meta), servidor-side, sem script de embed no load; clique abre o post **na plataforma**. + +## Persona e fluxo + +- **Persona / contexto:** visitante da home de campanha, majoritariamente mobile, que quer ver a campanha pulsando hoje. +- **Job principal:** ver os posts recentes do Instagram sem sair do fluxo da página e abrir o que chamar atenção direto no app/rede. +- **Fluxo desejado:** rola até a seção → vê cards 1:1 com badge "Instagram", legenda e data → clica → abre o post no Instagram (nova aba, `noopener`), mantendo a home aberta. +- **Anti-goals de produto:** nenhum script/embed pesado do Instagram no load (LCP do mobile — decisão explícita); sem lightbox; o board nunca quebra se o token expirar; IG só liga depois do setup da Meta (review leva dias — sequenciar go-live). + +### Esboço de fluxo (B) + +```text +[seção S1+S2 + cards IG] → [clique no card IG] → [instagram.com/... nova aba] → [volta à home intacta] +Token expirado/API fora → [mantém último snapshot / oculta só os cards IG — nunca quebra a página] +``` + +### Rascunho UI (B) + +![Rascunho UI — desktop](secao-conteudos-home-ui-draft-desktop.png) + +![Rascunho UI — mobile](secao-conteudos-home-ui-draft-mobile.png) + +Fonte iterável: [`secao-conteudos-home-ui-draft.html`](secao-conteudos-home-ui-draft.html). A cena `desktop` mostra o bento final; nesta fatia S3 entram os cards Instagram (1:1). + +## Objetivo e aceite + +- A seção passa a incluir os posts mais recentes da conta oficial: cards 1:1 com badge "Instagram", legenda (título) e data; clique abre o post na plataforma (nova aba, `noopener`). **Mobile: carrossel de 1 conteúdo por tela (decisão do cliente).** +- Fonte automática: leitura servidor-side via Instagram Graph API (token long-lived com refresh, perfil Business/Creator) com cache — nenhuma chave no cliente, nenhum embed no load. +- **Exclusão por item (obrigatório — posts de grade):** o perfil do Instagram tem imagens publicadas SÓ para montar o grid do feed (ex.: grade 3×3 em mosaico), que não são conteúdo real de campanha. O admin precisa marcar posts específicos como "não exibir" (por ID da mídia), e eles somem do board — o carrossel/bento pula para o próximo elegível. Sem esse mecanismo, a seção pode mostrar mosaicos sem sentido; é o requisito nº 1 desta fatia. +- Fail-closed: token expirado/API fora/quota → mantém último snapshot em cache; sem snapshot, a seção segue com as outras fontes — a página nunca quebra nem mostra erro. +- **Configuração no admin** conforme plano-site §4.2 (sem trava de schema nesta intenção); **sem credenciais IG configuradas, os cards IG simplesmente não aparecem** (a seção vive com artigos + YouTube). +- Kill switch "pausar feed" do plano-site vale para o board como um todo. +- Zero mudança em schema de conteúdo público; migrações não destrutivas no máximo (config no admin). + +## Dados (intenção) + +- **Vou apresentar dados?** Sim, superfície neste item — metadados do post (imagem, legenda, data) vindos da plataforma. +- **Decisões desbloqueadas:** o visitante decide qual post abrir; nada de métricas inventadas. +- **Forma:** *adiada ao plano de implementação* — restrição de produto: legenda/data como a plataforma mostra; sem contadores derivados. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** mesmo componente de seção/card de S1/S2 (`src/components/`, `src/app/(frontend)/(home)/page.tsx`, bloco `campaign-site` em `styles.css`); loader servidor-side novo com cache (`src/utilities/` ou `src/lib/`); config no admin (global do Payload, grupo `Configurações`). +- **Precedente a olhar:** S1/S2 (estrutura); `docs/campanha/plano-site-campanha-2026.md` §4.2 (Graph API, token long-lived com refresh ~60 dias, kill switch, "IG só liga após setup da Meta"); `src/utilities/posts.ts` (padrão de cache). +- **Risco de acoplamento:** mesma generalização da seção — card de conteúdo único (Artigo/YouTube/Instagram) sem código duplicado por plataforma; o setup da Meta é sequenciamento de go-live (externo), não bloqueia a implementação. + +## Dependências + +- **S2** (estrutura da seção completa: artigos + YouTube) — dura. +- Soft: `docs/campanha/plano-site-campanha-2026.md` §4.2 + pendência §5 ("mix mandato/campanha no board"). + +## Fora de escopo + +- Compartilhamento WhatsApp → S4 (issue separada). +- Script de embed do Instagram no load (proibido pelo plano-site). +- Curadoria manual além da exclusão por item (etiquetas, ordenação manual, destaques); stories; DMs; estatísticas além do básico do post; outras contas. +- Setup da Meta (app/credenciais) é tarefa da assessoria/produção — a implementação só consome as credenciais quando existirem. + +## Rabbit holes de produto + +- **Embed no load.** Se alguém "só completar": o script do Instagram derruba o LCP (decisão explícita do plano-site). **Corte:** thumbnail + link para a plataforma. +- **Refresh de token como feature de produto.** Se alguém "só completar": vira um painel de OAuth. **Corte:** token long-lived com refresh automático conforme o plano-site; falha → fail-closed (snapshot/oculta). +- **IG sem credencial = erro.** Se alguém "só completar": a seção mostra erro de configuração. **Corte:** sem credencial, cards IG não existem — nada de aviso. +- **Exclusão vira curadoria pesada.** Se alguém "só completar": lista enorme de IDs, razões obrigatórias, aprovação. **Corte:** marcar/desmarcar item (thumbnail + ID) no admin; sem workflow. + +## Questões em aberto (produto) + +- **Mix mandato/campanha no board** — mesma questão da S2: **Opções:** A) automático (recomendação — pendência já registrada no plano-site §5, decidir com a assessoria) | B) filtrar por hashtag/tag. **Recomendação:** A. _(decidido — A + exclusão por item)_ +- **Exclusão por item: o que aparece para marcar?** **Opções:** A) o admin vê a lista dos últimos posts (thumbnail + ID) e marca os que não devem aparecer (recomendação — sem isso, marcar por ID puro é inviável para a assessoria) | B) só campo de IDs com documentação. **Recomendação:** A — a interface de exclusão é parte desta fatia. _(assumido — validar com produto)_ +- **Grade do feed (grid) entra?** **Opções:** A) posts de grade entram no automático e são excluídos um a um pela assessoria quando aparecem (recomendação — simples, transparente) | B) tentar detectar grade por heurística e já ocultar. **Recomendação:** A — heurística de "post de grade" é frágil (o IG não sinaliza); a exclusão manual resolve o caso real. _(assumido — validar com produto)_ + +## Referências + +- GitHub Issue #20 +- Rascunho UI (gate): `docs/plans/secao-conteudos-home-ui-draft.html` + PNGs acima +- `docs/campanha/plano-site-campanha-2026.md` §4.2 — decisões do board IG (Graph API, token, kill switch, sem embed) e §5 pendência "mix mandato/campanha" +- Wireframe: `docs/campanha/wireframe-solla-1313.html` (seção 7) +- `docs/plans/secao-conteudos-home-artigos.md` (S1) e `secao-conteudos-home-youtube.md` (S2) +- `AGENTS.md` — padrões de cache, admin em pt, convenções de nome diff --git a/docs/plans/secao-conteudos-home-ui-draft-compartilhar.png b/docs/plans/secao-conteudos-home-ui-draft-compartilhar.png new file mode 100644 index 00000000..bb4a872e Binary files /dev/null and b/docs/plans/secao-conteudos-home-ui-draft-compartilhar.png differ diff --git a/docs/plans/secao-conteudos-home-ui-draft-desktop.png b/docs/plans/secao-conteudos-home-ui-draft-desktop.png new file mode 100644 index 00000000..a71fba8c Binary files /dev/null and b/docs/plans/secao-conteudos-home-ui-draft-desktop.png differ diff --git a/docs/plans/secao-conteudos-home-ui-draft-mobile.png b/docs/plans/secao-conteudos-home-ui-draft-mobile.png new file mode 100644 index 00000000..d47061b4 Binary files /dev/null and b/docs/plans/secao-conteudos-home-ui-draft-mobile.png differ diff --git a/docs/plans/secao-conteudos-home-ui-draft-vazio.png b/docs/plans/secao-conteudos-home-ui-draft-vazio.png new file mode 100644 index 00000000..c0d577f7 Binary files /dev/null and b/docs/plans/secao-conteudos-home-ui-draft-vazio.png differ diff --git a/docs/plans/secao-conteudos-home-ui-draft.html b/docs/plans/secao-conteudos-home-ui-draft.html new file mode 100644 index 00000000..97dd6868 --- /dev/null +++ b/docs/plans/secao-conteudos-home-ui-draft.html @@ -0,0 +1,285 @@ + + + + + + + UI draft — Seção de conteúdos na home de campanha + + +
+

+ Visitante da home pública de campanha (jorgesolla1313). Seção 3 da página: logo após a + prova social, imediatamente antes de "Por que essa eleição importa". Card de conteúdo + generalizado (Artigo / YouTube / Instagram) — clique leva ao artigo ou à plataforma. +

+ + +
+ +
+ 3.333 proposições + 1.031 discursos + 3º mandato +
+ + + + + + +
+

+ Por que essa eleição importa +

+

+ Eleger deputado é coisa séria. O Congresso tem nas mãos vidas reais e o SUS. +

+
+
+ + +
+ +
+ 3.333 proposições + 1.031 discursos + 3º mandato +
+ + +
+

+ Acompanhe de perto +

+

A caminhada, em tempo real

+

+ Bastidores, caravanas e as lutas do mandato: conteúdo atualizado, direto das redes. +

+ + + + + +
+ + + + + +
+

1 de 5 · deslize para ver os próximos

+
+
+ + +
+

+ Card de conteúdo com botão discreto de compartilhamento (canto do card, sem competir com + o CTA primário): abre WhatsApp com mensagem pré-configurada + opção de copiar link. +

+
+
+
+ + YouTube + + + thumb 16:9 + +
+

+ Título do vídeo recente, até duas linhas +

+ há 2 dias · 12,4 mil visualizações + +
+
+
+ + Artigo + + + capa 16:9 + +
+

+ Título do artigo, até duas linhas +

+ há 1 dia · categoria + +
+
+

+ Menu aberto (v1): "Compartilhar no WhatsApp" (mensagem pré-configurada com título + + link) e "Copiar link". +

+
+ WhatsApp ↗ +
+
+ Copiar link +
+
+
+
+ + +
+

+ Vazio / fail-closed — nenhuma fonte com conteúdo disponível (artigos invisíveis ou APIs + sem snapshot). Recomendação: a seção inteira é ocultada; a página mantém o fluxo + Prova → Por que essa eleição importa, sem zona morta. +

+
+ 3.333 proposições + 1.031 discursos + 3º mandato +
+
+

+ Por que essa eleição importa +

+

+ Eleger deputado é coisa séria. O Congresso tem nas mãos vidas reais e o SUS. +

+
+
+
+ + diff --git a/docs/plans/secao-conteudos-home-youtube.md b/docs/plans/secao-conteudos-home-youtube.md new file mode 100644 index 00000000..b17634bb --- /dev/null +++ b/docs/plans/secao-conteudos-home-youtube.md @@ -0,0 +1,93 @@ +# Seção de conteúdos na home de campanha — YouTube (S2) + +Status: registrado +Atualizado em: 2026-08-17 +Issue: #18 +Priority: P1 +Model: cursor-grok-4.5-medium +Impeccable: B — estende a seção S1 com cards de vídeo (mesma estrutura de card, badge "YouTube") +Rascunho UI: docs/plans/secao-conteudos-home-ui-draft.html + PNGs embutidos abaixo +Appetite: ~1–2 dias eng; integração de leitura com a API do YouTube + cards na seção S1 +Responsável: — + +## Intenção + +A seção "Acompanhe de perto" da home de campanha (criada em S1 com artigos) ganha os vídeos do canal oficial. O canal já existe (`@JorgeSollaDep`) e posta com frequência — vídeos são o conteúdo de maior engajamento na reta final. A fatia segue o desenho aprovado no plano do site: automático via API do YouTube, servidor-side, sem iframe no carregamento; clique abre o vídeo **na plataforma** (decisão do wireframe). + +## Persona e fluxo + +- **Persona / contexto:** visitante da home de campanha que quer ver a caminhada em movimento (bastidores, caravanas, entrevistas). +- **Job principal:** ver que o canal está ativo e assistir o vídeo mais recente sem caçar o canal. +- **Fluxo desejado:** rola até a seção → vê o card grande do último vídeo (thumbnail 16:9 + badge "YouTube" + título + views/data) e cards menores dos seguintes → clica no card → abre o vídeo no YouTube (nova aba, `noopener`), mantendo a home aberta. +- **Anti-goals de produto:** nenhum embed/player inline no v1 (decisão do plano-site §4.2: "sem iframe no load", LCP do mobile); o board não é curadoria manual; a seção nunca quebra se a API falhar. + +### Esboço de fluxo (B) + +```text +[seção S1 + cards YT] → [clique no card YT] → [youtube.com/... nova aba] → [volta à home intacta] +API indisponível → [mantém último snapshot / oculta só os cards YT — nunca quebra a página] +``` + +### Rascunho UI (B) + +![Rascunho UI — desktop](secao-conteudos-home-ui-draft-desktop.png) + +![Rascunho UI — mobile](secao-conteudos-home-ui-draft-mobile.png) + +Fonte iterável: [`secao-conteudos-home-ui-draft.html`](secao-conteudos-home-ui-draft.html). A cena `desktop` mostra o bento final (Artigo + YouTube + Instagram); nesta fatia S2 entram só os cards YouTube. + +## Objetivo e aceite + +- A seção S1 passa a incluir os vídeos mais recentes do canal oficial: card grande = último vídeo (16:9), pequenos = próximos, com badge "YouTube", título, data e visualizações. **Mobile: carrossel de 1 conteúdo por tela (decisão do cliente).** +- Fonte automática: leitura servidor-side da API pública do YouTube (dados dos últimos vídeos do canal), com cache (ISR/revalidação) — nenhuma chave no cliente, nenhum iframe no load. +- Clique no card abre o vídeo **na plataforma** (URL canônica do vídeo), em nova aba com `noopener` — sem lightbox, sem player inline (decisão do wireframe; player inline fica fora de escopo). +- **Exclusão por item (curadoria mínima):** o admin pode marcar vídeos específicos para NÃO aparecer (ex.: vídeo errado, conteúdo que não é de campanha, transmissão incompleta). Itens excluídos não são exibidos nem contam para "o último vídeo" — o board pula para o próximo elegível. Mesmo mecanismo de exclusão das outras plataformas (S3), configurado no mesmo lugar no admin. +- Fail-closed: API sem resposta/token expirado/quota estourada → mantém o último snapshot em cache se existir; sem snapshot, a seção continua com as outras fontes (ou oculta por completo se nada houver) — a página nunca quebra nem mostra erro. +- A configuração (chave/IDs + exclusões) vive no admin, seguindo o desenho do plano-site §4.2 (sem trava de schema nesta intenção — o executor define a forma); sem chave configurada, os cards YT simplesmente não aparecem. +- Zero mudança em schema de conteúdo público / migrations destrutivas; a visibilidade eleitoral `hidden` dos posts não se aplica aqui (fonte externa), mas o kill switch do feed (pausar) segue o desenho do plano-site. + +## Dados (intenção) + +- **Vou apresentar dados?** Sim, superfície neste item — metadados do vídeo (título, thumbnail, data, visualizações) vindos da plataforma. +- **Decisões desbloqueadas:** o visitante decide qual vídeo assistir (clique); visualizações são fato da plataforma (não inventar). +- **Forma:** *adiada ao plano de implementação* — restrição de produto: mostrar views como a plataforma mostra (ex. "12,4 mil"), nunca métrica derivada da campanha. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** mesmo componente de seção/card de S1 (`src/components/`, `src/app/(frontend)/(home)/page.tsx`, bloco `campaign-site` em `styles.css`); loader/fetch servidor-side novo em `src/utilities/` ou `src/lib/` com cache (padrão `unstable_cache` do repo); config no admin (global do Payload, grupo `Configurações` — sem nome de arquivo travado). +- **Precedente a olhar:** S1 (estrutura da seção); `docs/campanha/plano-site-campanha-2026.md` §4.2 (decisões: Data API v3, server-side, ISR, thumbnails CDN, sem iframe no load); padrões de cache do repo (`src/utilities/posts.ts`, `unstable_cache` + tags); `seed-posts.mjs` se precisar de referência de chamada externa com cache de download. +- **Risco de acoplamento:** seção compartilhada com S3 (Instagram) — a estrutura de card generalizada criada em S1 não pode virar código duplicado por plataforma; o kill switch de "pausar feed" do plano-site é um toggle único do board (não por plataforma na intenção — validar no plano de implementação). + +## Dependências + +- **S1** (estrutura da seção + card generalizado) — dura. +- Soft: `docs/campanha/plano-site-campanha-2026.md` §4.2 (decisões de engenharia do board). + +## Fora de escopo + +- Instagram → S3 (mesma seção, mesmo lote). +- Compartilhamento WhatsApp → S4 (issue separada). +- Player inline / lightbox / iframe no load (decisão do wireframe: clique abre na plataforma). +- Curadoria manual além da exclusão por item (playlists, etiquetas, ordenação manual, seleção de destaques); download/armazenamento dos vídeos; estatísticas além do que a API entrega (comentários, inscritos). +- Outros canais/contas (a configuração aponta um canal oficial). + +## Rabbit holes de produto + +- **Embed no load.** Se alguém "só completar": iframes de YouTube derrubam o LCP no mobile (decisão explícita do plano-site §4.2). **Corte:** thumbnail + link para a plataforma. +- **Vídeo "em destaque" com escolha manual.** Se alguém "só completar": vira curadoria semanal. **Corte:** automático (último vídeo elegível = card grande). +- **Exclusão vira curadoria pesada.** Se alguém "só completar": lista enorme de IDs, razões obrigatórias, aprovação. **Corte:** marcar/desmarcar item no admin; sem workflow. +- **Fail-closed vira erro visível.** Se alguém "só completar": mensagem de "erro ao carregar" queima a home. **Corte:** seção nunca mostra erro — snapshot ou ocultação silenciosa. + +## Questões em aberto (produto) + +- **Misturar mandato e campanha no board?** **Opções:** A) automático — expõe o que o canal postar (recomendação: pendência já registrada no plano-site §5, decidir com a assessoria antes do go-live) | B) filtrar por termo/playlist. **Recomendação:** A, com o kill switch do plano-site + exclusão por item como rede de segurança. _(decidido — A + exclusão por item)_ +- **Exclusão por item: qual granularidade?** **Opções:** A) lista de IDs excluídos por plataforma, gerenciada no admin (recomendação — simples, auditável, suficiente para vídeos errados/indesejados) | B) razão/etiqueta obrigatória por exclusão. **Recomendação:** A — o motivo pode ser um campo opcional de texto livre, sem virar workflow. _(assumido — validar com produto)_ + +## Referências + +- GitHub Issue #18 +- Rascunho UI (gate): `docs/plans/secao-conteudos-home-ui-draft.html` + PNGs acima +- `docs/campanha/plano-site-campanha-2026.md` §4.2 — decisões do board (API, cache, sem iframe, fail-closed) e §5 pendência "mix mandato/campanha" +- Wireframe: `docs/campanha/wireframe-solla-1313.html` (seção 7) +- `docs/plans/secao-conteudos-home-artigos.md` (S1 — estrutura que esta fatia estende) +- `AGENTS.md` — padrões de cache (`unstable_cache`), admin em pt, convenções de nome (identificadores em inglês)