Filtros
Formulário
O padrão de listagem do Brain: Toolbar com busca, troca de visão e FilterButton; os filtros aplicados logo abaixo, cada um removível; depois as abas com contador, que contam a lista já filtrada. O FilterDrawer guarda um rascunho: Aplicar entrega, fechar sem aplicar descarta.
Substitui no Brain:ui filter-buttonui active-filtersGavetaDeFiltrospartners FilterButton / ActiveFilters
Listagem com filtros, como no Brain
Filtros ativos:
Conciliação automática
"use client";
import {
ActiveFilters,
Badge,
FilterButton,
FilterDrawer,
FormField,
MultiCombobox,
SearchInput,
SegmentedControl,
Tabs,
TabsContent,
TabsList,
TabsTrigger,
Toolbar,
} from "@paytime/ui";
import { useState } from "react";
const AREAS = [
{ value: "comercial", label: "Comercial" },
{ value: "financeiro", label: "Financeiro" },
{ value: "operacoes", label: "Operações" },
];
const PROJECTS = [
{ name: "Novo checkout", area: "comercial", archived: false },
{ name: "Conciliação automática", area: "financeiro", archived: false },
{ name: "Onboarding de parceiros", area: "operacoes", archived: false },
{ name: "Migração do ERP", area: "financeiro", archived: true },
];
export default function Example() {
const [search, setSearch] = useState("");
const [view, setView] = useState("tabela");
const [isDrawerOpen, setDrawerOpen] = useState(false);
const [draft, setDraft] = useState<string[]>([]);
const [areas, setAreas] = useState<string[]>(["financeiro"]);
const labelOf = (value: string) => AREAS.find((area) => area.value === value)?.label ?? value;
const filtered = PROJECTS.filter(
(project) =>
(areas.length === 0 || areas.includes(project.area)) &&
project.name.toLowerCase().includes(search.trim().toLowerCase()),
);
// Counts use the filtered list, so a badge never disagrees with the rows shown.
const active = filtered.filter((project) => !project.archived);
const archived = filtered.filter((project) => project.archived);
return (
<div>
<Toolbar
className="mb-4"
search={
<SearchInput
placeholder="Buscar projeto"
aria-label="Buscar projeto"
value={search}
onChange={(event) => setSearch(event.target.value)}
/>
}
>
<SegmentedControl
appearance="tabs"
aria-label="Visão da listagem"
value={view}
onValueChange={setView}
options={[
{ value: "tabela", label: "Tabela" },
{ value: "cartoes", label: "Cartões" },
]}
/>
<FilterButton
count={areas.length}
onClick={() => {
setDraft(areas);
setDrawerOpen(true);
}}
/>
</Toolbar>
<ActiveFilters
filters={areas.map((value) => ({ key: value, label: labelOf(value) }))}
onRemove={(key) => setAreas((current) => current.filter((value) => value !== key))}
onClear={() => setAreas([])}
/>
<Tabs defaultValue="operacao">
<TabsList className="mb-4">
<TabsTrigger value="operacao">
Em operação
<Badge size="sm">{active.length}</Badge>
</TabsTrigger>
<TabsTrigger value="arquivados">
Arquivados
<Badge size="sm">{archived.length}</Badge>
</TabsTrigger>
</TabsList>
<TabsContent value="operacao" className="mt-0 text-[13px]">
{active.map((project) => project.name).join(" · ") || "Nenhum projeto com esses filtros."}
</TabsContent>
<TabsContent value="arquivados" className="mt-0 text-[13px]">
{archived.map((project) => project.name).join(" · ") || "Nenhum projeto com esses filtros."}
</TabsContent>
</Tabs>
<FilterDrawer open={isDrawerOpen} onOpenChange={setDrawerOpen} onApply={() => setAreas(draft)} onClear={() => setDraft([])}>
<FormField label="Área">
<MultiCombobox values={draft} onValuesChange={setDraft} options={AREAS} />
</FormField>
</FilterDrawer>
</div>
);
}