teqo/docs/changelog/2026-08-18-s2.md
Francisco Solla fe5b11cf24
Some checks failed
CI (PR) / e2e (1) (pull_request) Successful in 5m9s
CI / build (push) Successful in 2m20s
CI (PR) / e2e (2) (pull_request) Successful in 5m18s
PR Ready + auto-merge / ready-automerge (pull_request) Failing after 30m17s
CI / e2e (1) (push) Successful in 4m5s
CI (PR) / scope (pull_request) Successful in 32s
CI / deploy homeserver (teqo-1313) (push) Has been cancelled
CI (PR) / lint (pull_request) Successful in 42s
CI (PR) / cycles (pull_request) Successful in 34s
CI (PR) / format (pull_request) Successful in 55s
CI (PR) / docs-guards (pull_request) Successful in 8s
CI (PR) / plans-only-closes (pull_request) Successful in 7s
Main merge flips claimed issues to done + in-prod / issue-done (pull_request) Successful in 13s
CI (PR) / typecheck (pull_request) Successful in 30s
CI (PR) / knip (pull_request) Successful in 31s
CI (PR) / unit (pull_request) Successful in 50s
CI (PR) / checks (pull_request) Successful in 2s
Promote plan-issue Related on main merge / plan-issue-ready (pull_request) Has been skipped
Agent pool supervisor / tick (pull_request) Successful in 27s
CI / static (push) Successful in 1m48s
CI / e2e (2) (push) Successful in 4m7s
CI (PR) / int (pull_request) Successful in 1m49s
CI / int (push) Successful in 1m57s
CI / checks (push) Successful in 2s
CI (PR) / build (pull_request) Successful in 2m31s
feat(S2): seção de conteúdos da home de campanha inclui os vídeos do YouTube
Board "Acompanhe de perto" (S1) ganha o feed automático do canal oficial:
global SocialFeedSettings (admin-only, kill switch + toggle + maxItems +
exclusões por item), loader src/utilities/youtubeFeed.ts (Data API v3
search→statistics, unstable_cache tag social-feed + revalidate 300,
fail-closed com snapshot persistido via SQL direto — revalidateTag dentro
de unstable_cache lança no Next 15.4), card generalizado
(CampaignArticleCard → CampaignContentCard, external com noopener) e merge
por recência com o grande = mais recente elegível. Stub HTTP do YouTube no
webServer do Playwright (porta dev+1000) + 4 e2e novos no describe serial.
Migration 20260818_111748_add_social_feed_settings (não destrutiva).
Closes #18
2026-08-18 15:23:50 -03:00

3.9 KiB
Raw Blame History

S2 — seção "Acompanhe de perto" da home de campanha (jorgesolla1313.com.br) passa a incluir os vídeos do canal oficial do YouTube (@JorgeSollaDep), estendendo o board da S1. Fonte automática servidor-side: global SocialFeedSettings nova (grupo Configurações, read/update admin-only — a chave da API nunca vaza pelo REST), com kill switch enabled ("Pausar feed"), toggle youtubeEnabled, chave + channel ID, youtubeMaxItems (1–5, default 3) e excludedItems (platform+id+motivo opcional — mesmo mecanismo/lugar da S3), migration 20260818_111748_add_social_feed_settings não destrutiva (editada para conter só o DDL — o migrate:create regenerava drift de migrations hand-written sem sidecar JSON de 20260811). Loader src/utilities/youtubeFeed.ts (irmão de posts.ts, pinado no spec de convenções): parsers/format puros (formatYouTubeViews pt-BR "12,4 mil"/"1,2 mi", thumbnails maxres??high??medium), fetch Data API v3 em dois passos (search → statistics, fetchImpl/YOUTUBE_API_BASE_URL injetáveis), unstable_cache tag social-feed + revalidate: 300 (TTL da entrada, não da página — o bug do S1 era page-level); fail-closed com snapshot persistido por SQL direto (payload.db.drizzle, sem hooks — revalidateTag dentro de unstable_cache LANÇA no Next 15.4, verificado no source; o snapshot cru pré-exclusão sobrevive a edições de settings por merge do update), exclusões aplicadas na leitura. Card generalizado CampaignArticleCard → CampaignContentCard (git mv; external?: boolean → <a target="_blank" rel="noopener noreferrer">); a seção mescla artigos visíveis + vídeos elegíveis por recência (grande = mais recente — aceite S2; degrade para o comportamento S1 sem config), meta "há X · N visualizações" (relativa < 48h, absoluta além), link "YouTube →" derivado do channelId; carrossel mobile renomeado para "Conteúdos recentes". next.config ganha i.ytimg.com + host local do stub no remotePatterns. Testes: 15 units novas (formatters, parsers, eligibility, fetch com fetchImpl fake); e2e com stub HTTP node (tests/e2e/youtube-stub.mjs, webServer do Playwright vira array, porta = dev+1000; POST /__stub/state alterna ok/fail; thumbnails servidas localmente) — 4 testes novos no describe serial da seção (full-state com exclusão + link externo + carrossel, fail-closed sem snapshot, snapshot persistente com maxItems mudando na API down, kill switch) + reset de settings no beforeAll; conventions specs atualizados (pin do youtubeFeed, settle após about:blank). Verificação: unit 2306 + int 791 verdes; e2e frontend 14/14 dev e 6/6 prod-mode (E2E_PROD=1 — valida TTL+tags+snapshot no build real); lint/format/tsc/knip/cycles/build verdes. /simplify 3 reviewers (sem P0): P1 corrigido — label do kill switch invertido ("Pausar feed" marcado = ativo → "Feed ativo"); P2 absorvidos (className do link do header extraída, Promise.all posts+feed no cache frio, comentário do sentinela 0 do merge, helper único tests/helpers/youtubeStub.ts para a porta do stub, waitForHomeSectionState fundido no waitForHomeHTML com orçamento 12s do S1, precondição do persist documentada); débito REGISTRADO (#40, prio:P2, depends #18): o validate do itemId lê data.platform que é sempre undefined (Payload passa o doc inteiro; verificado no source) — produção sem validação de ID de vídeo, e o e2e depende dessa mortandade (ids do stub de 20 chars); correção em dupla documentada na issue. Descartados com justificativa: ramo maxres do search (pinned por teste), tag social-feed fora da convenção global_* (precedente election-tse/municipality-catalog), remotePattern localhost/thumbs incondicional (decisão deliberada dos 3 modos e2e), persist não-atômico (comentado); achados visuais do design-vision mapeados para a Issue #33 (separação visual branco-sobre-branco, fora de escopo por decisão do gate).