Pular para o conteúdo
Ações em massa

Padrões de tela

Ações em massa

Selecionar linhas por checkbox faz uma barra flutuante aparecer na base da tela, com a contagem à esquerda e as ações do domínio à direita. Ela flutua sobre a lista — a lista continua ali, e é ela que diz o que está selecionado.
src/components/cadastros/barra-lote.tsxfixed bottom-4role=group

A barra

Superfície clara com sombra de peça flutuante, não uma ilha escura: ela precisa parecer parte da tela, não um sistema à parte.
3 selecionados
a peça real · fixed bottom-4 · rounded-2xl · shadow-md ring-1 · role=group com aria-label
PeçaReceita
Posiçãofixed bottom-4 left-1/2 -translate-x-1/2 z-40 · w-[calc(100%-2rem)] max-w-fit
Formarounded-2xl — é painel flutuante, não um controle que se toca
Superfícieborder border-border bg-surface shadow-md ring-1 ring-foreground/5
Respiropx-3 py-2 · gap-1.5 · flex-wrap (no celular ela quebra em linhas)
Contagempx-1.5 text-sm font-medium tabular-nums — com singular e plural de verdade
AçõesButton variant=ghost size=sm; a destrutiva com text-destructive hover:bg-destructive/5
LimparButton size=icon-sm com aria-label “Limpar seleção”
Semânticarole="group" + aria-label — NÃO role="toolbar": toolbar promete navegação por setas que a barra não tem
Entradasem animação: ela aparece quando a seleção existe (total === 0 → null)

NotaPor que não é pílula

O vocabulário diz “toca = pílula”. A barra não é um alvo: é um PAINEL que contém alvos. Por isso ela é canto (rounded-2xl, o degrau acima do card porque flutua) e os botões dentro dela é que são pílulas.

A seleção

O checkbox aparece na linha, não num modo separado. Não existe “entrar no modo seleção”.
Paciente
Marina Alvez
Ricardo Nunes
Helena Prado
checkbox real na linha · o cabeçalho seleciona todos os VISÍVEIS (o filtro em vigor)
RegraDetalhe
“Selecionar todos” é o VISÍVELMarcar o cabeçalho seleciona o que o filtro em vigor mostra, não o banco inteiro.
Cada checkbox tem aria-label com o nome“Selecionar Marina Alvez” — sem isso o leitor de tela anuncia cinco checkboxes idênticos.
A barra some sozinhaSeleção vazia = componente não renderiza (return null). Nada de barra fantasma com “0 selecionados”.
O alvo de toque crescepointer-coarse:size-5 — o checkbox pelado como controle primário de toque nasceu em Cadastros.

Quais ações entram

Só o que NÃO mente em massa. Esta é a regra mais importante da página, e ela é de domínio, não de layout.
EntraFica de foraPor quê
Adicionar tagMudar situação no funilO estágio anda por EVENTO real (o paciente respondeu, compareceu), nunca por seleção em massa. Mudar situação é uma pessoa por vez, na ficha.
ExportarEnviar mensagem em massaDisparo em lote é o oposto do produto: o agente conversa, não bombardeia.
Marcar “não contatar”Editar dadosEditar em massa apaga o que era específico de cada cadastro sem ninguém ver.
Excluir (com desfazer)Ver abaixo: aqui a saída é desfazer, não confirmar.

Excluir em massa é desfazer, não confirmar

O padrão que está no ar, e ele é melhor que um diálogo de confirmação: a linha some na hora e o banco só é tocado quando a janela fecha.
o desenho (use-exclusao-com-desfazer.ts)
1. clicou em Excluir  → as linhas somem da tela (pendentes) e sobe um toast
                         com "Desfazer"; NADA foi escrito no banco ainda
2. clicou em Desfazer → as linhas voltam; a janela morre sem gravar
3. o toast se despede → só AÍ grava o deletedAt (soft-delete) de quem
                         continuava pendente

Confirmar × desfazer

Dá pra desfazer? Então não pergunte antes: aja e ofereça a volta. Não dá? Aí sim pergunte, com a consequência escrita — é o caso da tarja de confirmação e do ConfirmDialog.

O commit mora no toast

Nunca num setTimeout paralelo: o Sonner PAUSA a duração no hover, e o timer de fora commitava por baixo do toast pausado — deixando um “Desfazer” visível que não desfazia. Gravar no onAutoClose/onDismiss faz o hover estender a janela honestamente.

A contagem entra na frase do toast, com singular e plural resolvidos — “1 cadastro excluído” e “3 cadastros excluídos”, nunca “os itens selecionados”.

No celular

A mesma barra, quebrando em linhas.

O que muda

A barra usa w-[calc(100%-2rem)] max-w-fit e flex-wrap: no celular ela ocupa a largura útil e as ações descem pra segunda linha em vez de espremer. O X de limpar continua sendo um alvo próprio — sair da seleção não pode ser mais difícil que entrar nela.

Se uma tela só tem UMA ação em lote, ela não precisa da barra: um botão na toolbar que age sobre a seleção resolve com menos peça.