mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-12 22:13:05 +00:00
A lista /campanha/comunicacao/conteudos ganha um modo de seleção opt-in que envolve os dois corpos (tabela e card list) com um estado client compartilhado, chaveado pela URL canônica (a seleção morre em página/filtro e nunca cresce para além da página visível). Com seleção ativa, a barra flutuante aplica o mesmo veredito unitário à seleção: Publicar/Despublicar agem direto com recibo honesto; Apagar sempre confirma no CampaignDeleteDialog reusado (novo caminho onConfirm aditivo), avisando os links públicos só quando a seleção carrega publicadas. O transporte é uma rota única (POST .../conteudos/lote, campaignJsonMutationRoute, teto de 50 ids) que itera server-side as ações donas: cada peça mantém a própria transação, o gate fresco e a limpeza best-effort de mídia; a falha de uma nunca reverte as demais e o recibo separa afetadas x falhas (2 de 3 peças apagadas. 1 falhou.). Sem migration, sem Consent novo, sem rota pública (S27) e sem mudar a semântica do C222. Cobertura: int do lote (papéis, dedupe, mídia, evento, tag, falha parcial, schema), unit do builder/barra/seleção e e2e HTTP. Design tier: DEGRADED (artefato de origem sem certificação frontier) — crítica do designer aplicada no escopo do item; entrega bloqueada para sign-off humano antes do PR. Related #1388
52 lines
1.9 KiB
TypeScript
52 lines
1.9 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import {
|
|
contentPieceBatchDeleteTitle,
|
|
contentPieceBatchReceiptMessage,
|
|
contentPieceSelectionLabel,
|
|
type ContentPieceBatchOutcome,
|
|
} from '@/lib/contentPieceBatch'
|
|
|
|
const outcome = (
|
|
action: ContentPieceBatchOutcome['action'],
|
|
affected: number,
|
|
failed = 0,
|
|
): ContentPieceBatchOutcome => ({
|
|
action,
|
|
affected,
|
|
failures: Array.from({ length: failed }, (_, index) => ({
|
|
contentPieceId: index + 1,
|
|
message: 'Peça não encontrada.',
|
|
})),
|
|
})
|
|
|
|
describe('contentPieceBatch (C236)', () => {
|
|
it('writes the all-success receipt in the plan literals, singular included', () => {
|
|
expect(contentPieceBatchReceiptMessage(outcome('publicar', 3))).toBe('3 peças publicadas')
|
|
expect(contentPieceBatchReceiptMessage(outcome('despublicar', 1))).toBe('1 peça despublicada')
|
|
expect(contentPieceBatchReceiptMessage(outcome('apagar', 3))).toBe('3 peças apagadas')
|
|
expect(contentPieceBatchReceiptMessage(outcome('apagar', 1))).toBe('1 peça apagada')
|
|
})
|
|
|
|
it('never calls a partial outcome a success: it separates affected from failures', () => {
|
|
expect(contentPieceBatchReceiptMessage(outcome('apagar', 2, 1))).toBe(
|
|
'2 de 3 peças apagadas. 1 falhou.',
|
|
)
|
|
expect(contentPieceBatchReceiptMessage(outcome('publicar', 1, 2))).toBe(
|
|
'1 de 3 peças publicadas. 2 falharam.',
|
|
)
|
|
expect(contentPieceBatchReceiptMessage(outcome('despublicar', 0, 1))).toBe(
|
|
'0 de 1 peça despublicada. 1 falhou.',
|
|
)
|
|
})
|
|
|
|
it('counts the selection with the TourComposerForm singular', () => {
|
|
expect(contentPieceSelectionLabel(1)).toBe('1 selecionada')
|
|
expect(contentPieceSelectionLabel(3)).toBe('3 selecionadas')
|
|
})
|
|
|
|
it('titles the mass delete and reuses the C222 singular', () => {
|
|
expect(contentPieceBatchDeleteTitle(1)).toBe('Apagar esta peça?')
|
|
expect(contentPieceBatchDeleteTitle(3)).toBe('Apagar 3 peças selecionadas?')
|
|
})
|
|
})
|