📄 Card 77 [M14] — FilterToolbar Expansível
📚 Referência Genius: Toolbar de filtros permanente em todas listagens: dropdowns (Unidade, Curso, Ano letivo, Por página), busca, "Ver mais filtros" expansível com mais opções.
🎯 Objetivo
Padronizar toolbar de filtros em todas páginas de listagem:
- Filtros comuns sempre visíveis (período, paginação, busca)
- Botão "Ver mais filtros" expansível com opções avançadas
- Sincronização com URL (filtro vira query string, compartilhável)
- Presets de filtro salvos por usuário
- Lógica reutilizável (hook
useFilterState)
📚 Documentação de Referência
src/components/ui/— vários- Padrão Genius observado: combobox + botão "Ver mais filtros"
🛠️ Especificação Técnica
Componente <FilterToolbar />
interface FilterToolbarProps {
// Filtros primários (sempre visíveis)
primary?: FilterConfig[];
// Filtros avançados (em "Ver mais filtros")
advanced?: FilterConfig[];
// Busca textual
search?: { placeholder: string; value: string; onChange: (v: string) => void };
// Paginação
pagination?: { pageSize: number; onPageSizeChange: (n: number) => void };
// Estado inicial (lê da URL)
initialFromUrl?: boolean;
// Presets salvos
enablePresets?: boolean;
}
interface FilterConfig {
name: string; // chave URL
label: string;
type: 'select' | 'multiselect' | 'date' | 'dateRange' | 'radio' | 'checkbox';
options?: { label: string; value: string }[];
defaultValue?: any;
}
Hook useFilterState
const { filters, setFilter, clearFilters, syncToUrl, presets, savePreset } = useFilterState({
primary: [...],
advanced: [...],
initialFromUrl: true,
});
// filters = { unidade: 'CENTRO', curso: 'INGLÊS', ano: '2026', page: 1, pageSize: 20 }
Layout
┌─────────────────────────────────────────────────────────────┐
│ [Unidade ▼] [Curso ▼] [Ano ▼] [Por página ▼] [🔍 Buscar] │
│ [▼ Ver mais filtros] [Limpar] [💾] │
└─────────────────────────────────────────────────────────────┘
Quando expandido:
┌─────────────────────────────────────────────────────────────┐
│ [Unidade ▼] [Curso ▼] [Ano ▼] [Por página ▼] [🔍 Buscar] │
│ [▲ Ver menos filtros] │
│ ────────────────────────────────────────────────────────── │
│ Status: [ ] Ativo [ ] Inativo [ ] Pendente │
│ Data cadastro: [__/__/____] até [__/__/____] │
│ Responsável: [Buscar... │
│ │
│ [Limpar filtros] [Aplicar] │
└─────────────────────────────────────────────────────────────┘
Sincronização URL
- Filtros viram query string:
?unidade=CENTRO&curso=INGLES&page=2 - Compartilhável (link copiado mantém filtros)
- Botão voltar do navegador funciona (filtros restaurados)
Presets
- Usuário salva combinação de filtros com nome ("Meus alunos inadimplentes")
- Dropdown de presets no canto direito da toolbar
- Persiste em localStorage
✅ Critérios de Aceite
- Componente
<FilterToolbar />reutilizável - Suporta 5+ tipos de filtro (select, multiselect, date, daterange, radio, checkbox)
- "Ver mais filtros" expansível/colapsável
- Sincroniza com URL (compartilhável + voltar funciona)
- Hook
useFilterStatereutilizável - Presets salvos por usuário
- Páginas de listagem principais refatoradas
- Build passa + lint OK
🔌 Handoff
- Componente
@/components/ui/FilterToolbar - Hook
@/lib/hooks/useFilterState
🎯 Próximo
Card 78: Sistema de Abas em Forms
EXECUCAO — 15/08/2026
1. O que foi implementado
- Hook useFilterState: sincronização com URL (query string compartilhável, botão voltar restaura filtros), presets salvos por usuário em localStorage (chave por formId), contagem de filtros ativos e modo controlado.
Arquivo:
src/lib/hooks/useFilterState.ts - Componente FilterToolbar: filtros primários sempre visíveis + "Ver mais filtros" expansível (badge com nº de ativos), busca textual, seletor de itens por página, 6 tipos de filtro (select/multiselect/date/dateRange/radio/checkbox), presets com chips removíveis e botões Limpar/Aplicar.
Arquivo:
src/components/ui/FilterToolbar.tsx - Aplicado em /local/alunos: status/curso primários + matriculado-após/inadimplentes avançados, sincronizando com a query tRPC existente — filtros agora ficam na URL.
Arquivo:
src/app/(dashboard)/local/alunos/page.tsx
2. Critérios de aceite validados
| # | Critério | Status |
|---|---|---|
| 1 | FilterToolbar reutilizável (modo standalone e controlado) | OK |
| 2 | 6 tipos de filtro suportados | OK |
| 3 | Ver mais/menos filtros expansível com badge de ativos | OK |
| 4 | Sincroniza com URL (compartilhável + voltar restaura) via router.replace | OK |
| 5 | useFilterState reutilizável | OK |
| 6 | Presets por usuário (localStorage) com salvar/aplicar/remover | OK |
| 7 | Página principal de listagem refatorada (/local/alunos) | OK |
| 8 | Build + lint OK | OK |
3. Deploy DEV
- Commit deployado via CI/CD (GitHub Actions -> VPS Oracle): 4a35f51
- Smoke test: login MASTER OK + 17 paginas HTTP 200 (master, local, alunos, alunos/novo, noticias, nps, auditorias, chamados, documentos, enquetes, termos, professor, pais, aluno, atendimento)
- CSS novo confirmado no bundle servido (tokens de densidade ativos)
- URL: https://sistemaescolar.wellka.com.br
4. Contexto gerado
- Componentes M14 reutilizaveis para os proximos marcos (M10-M13): @/components/ui/{PageToolbar, FilterToolbar, FormTabs, EmptyState, DataTable} + @/lib/hooks/{useFilterState, useAutoSave} + sub-router tRPC shell.*
- Paginas novas (M10+) ja devem nascer com PageHeader + FilterToolbar + DataTable + EmptyState
5. Proximo card
Card 78: Sistema de Abas em Forms