11 KiB
C222 — Central de Conteúdos — apagar uma peça
Status: rascunho Atualizado em: 2026-09-24 Issue: #1330 Priority: P2 Impeccable: B — encaixe na Central de Conteúdos (lista + detalhe): ação de apagar com confirmação Design UI: docs/plans/central-conteudos-apagar-peca-ui-design.html Appetite: ~1 dia eng; um outcome verificável Responsável: —
Intenção
A Central de Conteúdos (C211) resolveu o garimpo: a assessoria envia, cataloga e publica as peças de campanha em /campanha/comunicacao/conteudos. Mas deixou um beco declarado — não existe nenhuma superfície de apagar; o delete ficou restrito ao admin do Payload como anti-goal aceito ("campanha nunca perde um arquivo por acidente"). Consequência: uma peça errada, duplicada ou que ninguém vai usar fica na Central e na pública para sempre; só um admin do Payload consegue removê-la, e a assessoria, que é quem sabe que a peça não presta, não tem como limpar a própria mesa.
O pedido humano reabre esse anti-goal de propósito — exatamente como o C183 (#1105, docs/plans/biblioteca-cortes-apagar-e-falhas.md) reabriu o "deletar corte não existe na UI" dos cortes do acervo. O pedido é direto: poder apagar uma peça de conteúdo, com confirmação explícita.
Persona e fluxo
- Persona / contexto: assessoria de comunicação (
communicator) e coordenação/candidatura, em/campanha/comunicacao/conteudos, na mesa ou no celular, sob prazo — quem não é da vertical (advisor,leader) segue negado. - Job principal: "tirar da Central uma peça que não serve mais — errada, duplicada ou vencida — sem depender do admin do Payload".
- Fluxo desejado: abre a Central → vê a peça que não serve na lista (ou no detalhe) → "Apagar" → a confirmação avisa o efeito (se publicada, o link público morre para quem já recebeu) → confirma → a peça some da lista interna, some da Central pública e o arquivo privado sai junto.
- Anti-goals de produto: não virar lixeira/desfazer (soft delete) nem restauração; sem lote; sem editor de peça; sem métricas/telemetria de exclusão; sem dashboard nova; não mexer no kill switch de publicação (despublicar continua o gesto reversível e existente).
- Reabertura de anti-goal (produto): o C211 (
docs/plans/central-conteudos-ingestao.md) declarou "sem delete na Central; só admin do Payload" como guardrail aceito. O pedido humano reabre de propósito. Ao apagar, respeitar o contrato público: uma peça publicada tem link/conteudos/<slug>já distribuível, então apagar é ação explícita e avisada — nunca em silêncio.
Esboço de fluxo (B)
[Central de Conteúdos: lista] → peça que não serve (na lista ou no detalhe)
→ [Apagar] → confirmação: "Apagar esta peça?" (avisa o link público se publicada)
→ confirma → some da lista interna, some da Central pública, arquivo privado sai junto
[outcome: Central limpa, sem peça fantasma e sem arquivo órfão]
Design UI (B)
- Design UI (gate):
docs/plans/central-conteudos-apagar-peca-ui-design.html— encaixe na lista (ação por linha, junto de Abrir/Reprocessar) e no detalhe; a distribuição exata das cenas é do design.
Objetivo e aceite
- A Central (lista e detalhe) permite apagar uma peça, com confirmação explícita; após apagar, a peça some da Central e não aparece na pública.
- Apagar peça publicada avisa, no diálogo, que o link
/conteudos/<slug>deixa de funcionar para quem já recebeu; despublicar continua a alternativa reversível (não é pré-requisito). - "Apagar" existe em qualquer estado — inclusive
processando(saída de quem quer cancelar um processamento travado) efalhou. (assumido — validar no gate) - O arquivo privado da peça sai junto (mídia exclusiva da peça, sem resíduo no storage); os contadores de circulação morrem com a peça — não há dashboard a limpar. (assumido — validar no gate)
- Quem pode apagar: a mesma audiência da vertical que hoje cria/edita/publica (
communicator,coordinator,candidate) + admin do Payload;advisor/leadernegados (fail-closed). - Apagar é definitivo (hard delete, sem lixeira/desfazer) e por peça, sem lote na v1.
Dados (intenção)
- Vou apresentar dados? Não — é uma decisão de estado do item (apagar), não um painel.
- Decisões desbloqueadas: a assessoria decide o que sai da Central; nenhuma leitura agregada nova.
- Forma: adiada ao plano de implementação — restrição de produto: sem contador de exclusão, sem auditoria exposta, sem telemetria de "peças apagadas".
Dados da decisão (literais)
- Ação:
Apagar(na lista e no detalhe). - Diálogo: título
Apagar esta peça?· botõesCancelar/Apagar. - Corpo (peça publicada):
O link público /conteudos/<slug> deixa de funcionar para quem já recebeu. Esta ação não pode ser desfeita. - Corpo (rascunho/em processamento/falhou):
Esta ação não pode ser desfeita. - Estados: processamento
processando|pronto|falhou; publicaçãorascunho|publicado— apagar disponível em todos. - Rotas internas:
/campanha/comunicacao/conteudos(lista) e/campanha/comunicacao/conteudos/[id](detalhe). - Contrato público: peça publicada aparece em
/conteudos/<slug>(S27) e serve a mídia em/conteudos/<slug>/midia; apagar quebra esses links para quem já recebeu — daí o aviso. - Acesso:
communicator,coordinator,candidateapagam;advisoreleadernegados (fail-closed); admin do Payload mantém o delete.
Direção no codebase (hipótese)
- Áreas prováveis: lista
src/app/(campaign)/campanha/(app)/comunicacao/conteudos/page.tsx(src/components/campaign/content/ContentPieceTable.tsx,ContentPieceCardList.tsx) e detalhe.../conteudos/[id]/page.tsx; access emsrc/collections/ContentPiece.ts(odeletea reabrir) e predicados emsrc/utilities/access/contentPieces.ts; leitura pública emsrc/utilities/content/contentPieceReads.ts; mídia privadasrc/collections/ContentMedia.ts. - Precedente a olhar: C183 —
src/components/campaign/speech/SpeechCutDeleteDialog.tsx(AlertDialog, aviso do link público, botão destructive) edocs/plans/biblioteca-cortes-apagar-e-falhas.md; cascata de mídia própria emsrc/collections/Speech.ts(deleteSpeechAssets). - Risco de acoplamento: a lista pública é cacheada (tag
contentPieces) e hoje só oafterChangea busta — apagar precisa refletir na pública sem peça fantasma; o job de processamento tolera a row sumida (cancelar emprocessandonão pode derrubar o worker); eventos de circulação (ContentEvent, C213) referenciam a peça porsubjectIdtexto, sem FK — órfãos inertes, não limpar.
Dependências
- Nenhuma — o C211 está entregue; o S27 é o contrato público a respeitar.
Fora de escopo
- Lote (apagar várias peças), lixeira/desfazer e restauração (soft delete).
- Limpar eventos órfãos de circulação (C213) — são inertes e não aparecem em lugar nenhum após a peça sumir.
- Métricas/telemetria de exclusão e dashboard nova.
- Apagar mídia compartilhada de outra peça (não existe) e qualquer mudança na página pública além de a peça sumir.
- Mexer no kill switch de publicação: despublicar continua o gesto reversível; editor de peça fica de fora.
Rabbit holes de produto
- "Já que apaga, deixa uma lixeira pra desfazer." Se alguém "só completar": soft delete, retenção e restauração. Corte neste item: apagar é definitivo, com confirmação; sem lixeira.
- "Já que apaga, apaga em lote." Se alguém "só completar": seleção múltipla, confirmação em massa, progresso. Corte neste item: uma peça por vez (v1); lote é item próprio se o uso pedir.
- "Já que apaga, limpa os eventos órfãos." Se alguém "só completar": varredura/reconciliação de
ContentEvent. Corte neste item: eventos órfãos são inertes; sem faxina de analytics.
Questões em aberto (produto)
- Apagar em qualquer estado ou bloquear
processando? Opções: A) qualquer estado, inclusiveprocessando(cancelar job travado) | B) só depois depronto/falhou. Recomendação: A — quem quer cancelar um processamento travado precisa da saída; o job tolera a row sumida. (assumido — validar no gate) - Ação na lista ou só no detalhe? Opções: A) só no detalhe | B) na lista (junto de Abrir/Reprocessar) e no detalhe. Recomendação: B — a limpeza acontece na lista e a confirmação evita o clique errado. (assumido — validar no gate)
- Arquivo privado sai junto ou fica órfão adiado? Opções: A) sai junto com a peça | B) fica órfão, como no C183. Recomendação: A — aqui a mídia é exclusiva da peça (o público serve o mesmo objeto), então resíduo não faz sentido; o "como" fica para o plano de implementação. (assumido — validar no gate)
- Publicada, apagar é definitivo ou redireciona para "Despublicar"? Opções: A) apaga de vez com aviso | B) só despublica. Recomendação: A — despublicar é reversível e já existe; quem quer apagar quer remover de vez. (assumido — validar no gate)
Referências
- GitHub Issue: #1330
- Design UI (gate):
docs/plans/central-conteudos-apagar-peca-ui-design.html - Precedente:
docs/plans/biblioteca-cortes-apagar-e-falhas.md(C183, #1105) ·src/components/campaign/speech/SpeechCutDeleteDialog.tsx - Planos irmãos:
docs/plans/central-conteudos-ingestao.md(C211, anti-goal reaberto) ·docs/plans/central-conteudos-publica.md(S27, contrato do link) ·docs/plans/central-conteudos-analytics.md(C213, eventos) - Arquivos-pista:
src/collections/ContentPiece.ts·src/collections/ContentMedia.ts·src/utilities/access/contentPieces.ts·src/utilities/content/contentPieceReads.ts·src/components/campaign/content/ContentPieceTable.tsx·src/components/campaign/content/ContentPieceCardList.tsx·src/app/(campaign)/campanha/(app)/comunicacao/conteudos/page.tsxe[id]/page.tsx - Testes a tocar:
tests/int/contentPiece.int.spec.ts·tests/e2e/campaignSpeechAcervo.e2e.spec.ts(seção C211) ·tests/unit/speechCutDeleteDialog.unit.spec.tsx(molde do diálogo) AGENTS.md— vertical Comunicação, fail-closed, mídia privada/S3
Self-score (shaping)
5/5 — (1) fatia = um outcome verificável (apagar uma peça e ela sumir da Central e da pública, sem resíduo de arquivo); (2) appetite ~1 dia declarado e o escopo cabe (encaixe na lista/detalhe, sem lote/lixeira/telemetria); (3) persona, job e aceite em linguagem de usuário, com a reabertura do anti-goal e o contrato do link público explícitos; (4) direção no codebase é hipótese com precedente nomeado, sem contrato; (5) zero decisão dura de engenharia (formato da ação, cascata de mídia e bust de cache ficam para o plano de implementação — aqui só se declara quem pode apagar e o efeito de produto).