fix: ajuste nos filtros gerais

This commit is contained in:
Alessandro Gonçaalves 2025-10-21 17:28:50 -03:00
parent 7d65d4583d
commit 69a66a6cab
4 changed files with 225 additions and 43 deletions

31
package-lock.json generated
View File

@ -8,6 +8,7 @@
"name": "dre-gerencial", "name": "dre-gerencial",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-select": "^2.2.6", "@radix-ui/react-select": "^2.2.6",
@ -1536,6 +1537,36 @@
} }
} }
}, },
"node_modules/@radix-ui/react-checkbox": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz",
"integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-collection": { "node_modules/@radix-ui/react-collection": {
"version": "1.1.7", "version": "1.1.7",
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",

View File

@ -9,6 +9,7 @@
"lint": "eslint" "lint": "eslint"
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-checkbox": "^1.3.3",
"@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-select": "^2.2.6", "@radix-ui/react-select": "^2.2.6",

View File

@ -6,6 +6,7 @@ import AnaliticoComponent from "./analitico";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Checkbox } from "@/components/ui/checkbox";
import { import {
Sheet, Sheet,
SheetContent, SheetContent,
@ -77,6 +78,10 @@ export default function Teste() {
valorMax: "", valorMax: "",
buscaTextual: "" buscaTextual: ""
}); });
// Estados para multi-seleção
const [centrosCustoSelecionados, setCentrosCustoSelecionados] = useState<string[]>([]);
const [contasSelecionadas, setContasSelecionadas] = useState<string[]>([]);
const [isFilterOpen, setIsFilterOpen] = useState(false); const [isFilterOpen, setIsFilterOpen] = useState(false);
const [dadosFiltrados, setDadosFiltrados] = useState<DREItem[]>([]); const [dadosFiltrados, setDadosFiltrados] = useState<DREItem[]>([]);
const [filtrosAplicados, setFiltrosAplicados] = useState(false); const [filtrosAplicados, setFiltrosAplicados] = useState(false);
@ -144,6 +149,10 @@ export default function Teste() {
const contasUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.conta))].sort() as string[]; const contasUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.conta))].sort() as string[];
setOpcoesContas(contasUnicas); setOpcoesContas(contasUnicas);
// Inicializar com todos os itens selecionados
setCentrosCustoSelecionados(centrosCustoUnicos);
setContasSelecionadas(contasUnicas);
} catch (error) { } catch (error) {
console.error("Erro ao carregar períodos:", error); console.error("Erro ao carregar períodos:", error);
} }
@ -304,6 +313,43 @@ export default function Teste() {
})); }));
}; };
// Funções para multi-seleção
const toggleCentroCusto = (centro: string) => {
setCentrosCustoSelecionados(prev => {
if (prev.includes(centro)) {
return prev.filter(c => c !== centro);
} else {
return [...prev, centro];
}
});
};
const toggleConta = (conta: string) => {
setContasSelecionadas(prev => {
if (prev.includes(conta)) {
return prev.filter(c => c !== conta);
} else {
return [...prev, conta];
}
});
};
const selecionarTodosCentros = () => {
setCentrosCustoSelecionados(opcoesCentrosCusto);
};
const limparCentros = () => {
setCentrosCustoSelecionados([]);
};
const selecionarTodasContas = () => {
setContasSelecionadas(opcoesContas);
};
const limparContas = () => {
setContasSelecionadas([]);
};
const limparFiltros = () => { const limparFiltros = () => {
const agora = new Date(); const agora = new Date();
const anoAtual = agora.getFullYear(); const anoAtual = agora.getFullYear();
@ -322,11 +368,18 @@ export default function Teste() {
buscaTextual: "" buscaTextual: ""
}); });
// Limpar multi-seleções
setCentrosCustoSelecionados([]);
setContasSelecionadas([]);
// Limpar dados da tabela // Limpar dados da tabela
setData([]); setData([]);
setDadosFiltrados([]); setDadosFiltrados([]);
setFiltrosAplicados(false); setFiltrosAplicados(false);
setMesesDisponiveis([]); setMesesDisponiveis([]);
// Recarregar opções e selecionar todos novamente
carregarPeriodosDisponiveis();
}; };
const aplicarFiltros = async () => { const aplicarFiltros = async () => {
@ -372,17 +425,17 @@ export default function Teste() {
); );
} }
// Filtro por centro de custo // Filtro por centro de custo (multi-seleção)
if (filtros.centroCusto !== "Todos") { if (centrosCustoSelecionados.length > 0) {
dadosFiltrados = dadosFiltrados.filter((item: DREItem) => dadosFiltrados = dadosFiltrados.filter((item: DREItem) =>
item.centro_custo === filtros.centroCusto centrosCustoSelecionados.includes(item.centro_custo)
); );
} }
// Filtro por conta // Filtro por conta (multi-seleção)
if (filtros.conta !== "Todas") { if (contasSelecionadas.length > 0) {
dadosFiltrados = dadosFiltrados.filter((item: DREItem) => dadosFiltrados = dadosFiltrados.filter((item: DREItem) =>
item.conta === filtros.conta contasSelecionadas.includes(item.conta)
); );
} }
@ -496,10 +549,10 @@ export default function Teste() {
// Agrupar por grupo // Agrupar por grupo
const grupos = data.reduce((acc, item) => { const grupos = data.reduce((acc, item) => {
if (!acc[item.grupo]) { if (!acc[item.grupo]) {
acc[item.grupo] = []; acc[item.grupo] = [];
} }
acc[item.grupo].push(item); acc[item.grupo].push(item);
return acc; return acc;
}, {} as Record<string, DREItem[]>); }, {} as Record<string, DREItem[]>);
@ -739,7 +792,8 @@ export default function Teste() {
{row.grupo} {row.grupo}
</span> </span>
{isCalculado && ( {isCalculado && (
<span className="text-blue-600 font-bold text-sm"></span> // <span className="text-blue-600 font-bold text-sm">⚡</span>
<></>
)} )}
</div> </div>
</button> </button>
@ -818,7 +872,7 @@ export default function Teste() {
const hierarchicalData = buildHierarchicalData(); const hierarchicalData = buildHierarchicalData();
return ( return (
<div className="w-full max-w-none mx-auto p-6"> <div className="w-full max-w-none mx-auto p-6">
{/* Header Section */} {/* Header Section */}
<div className="mb-6"> <div className="mb-6">
@ -829,8 +883,8 @@ export default function Teste() {
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
Demonstração do Resultado do Exercício Demonstração do Resultado do Exercício
</p> </p>
</div>
</div> </div>
</div>
{/* Botão de Filtro */} {/* Botão de Filtro */}
<Sheet open={isFilterOpen} onOpenChange={setIsFilterOpen}> <Sheet open={isFilterOpen} onOpenChange={setIsFilterOpen}>
@ -865,7 +919,7 @@ export default function Teste() {
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
<div> <div>
<Label htmlFor="periodo-ate" className="text-xs text-gray-500">ATÉ</Label> <Label htmlFor="periodo-ate" className="text-xs text-gray-500">ATÉ</Label>
<Select value={filtros.periodoAte} onValueChange={(value) => handleFiltroChange('periodoAte', value)}> <Select value={filtros.periodoAte} onValueChange={(value) => handleFiltroChange('periodoAte', value)}>
@ -878,7 +932,7 @@ export default function Teste() {
))} ))}
</SelectContent> </SelectContent>
</Select> </Select>
</div> </div>
</div> </div>
</div> </div>
@ -914,37 +968,103 @@ export default function Teste() {
</Select> </Select>
</div>*/} </div>*/}
{/* Centro de Custo {/* Centro de Custo */}
<div className="grid gap-3"> <div className="grid gap-3">
<Label htmlFor="centro-custo">CENTRO DE CUSTO</Label> <div className="flex items-center justify-between">
<Select value={filtros.centroCusto} onValueChange={(value) => handleFiltroChange('centroCusto', value)}> <Label htmlFor="centro-custo">CENTRO DE CUSTO</Label>
<SelectTrigger> <div className="flex gap-2">
<SelectValue placeholder="Selecione" /> <Button
</SelectTrigger> type="button"
<SelectContent> variant="outline"
<SelectItem value="Todos">Todos</SelectItem> size="sm"
{opcoesCentrosCusto.map(centro => ( onClick={selecionarTodosCentros}
<SelectItem key={centro} value={centro}>{centro}</SelectItem> className="text-xs h-6 px-2"
))} >
</SelectContent> Todos
</Select> </Button>
</div>*/} <Button
type="button"
variant="outline"
size="sm"
onClick={limparCentros}
className="text-xs h-6 px-2"
>
Limpar
</Button>
</div>
</div>
<div className="max-h-32 overflow-y-auto border rounded-md p-2 space-y-2">
{opcoesCentrosCusto.map(centro => (
<div key={centro} className="flex items-center space-x-2">
<Checkbox
id={`centro-${centro}`}
checked={centrosCustoSelecionados.includes(centro)}
onCheckedChange={() => toggleCentroCusto(centro)}
/>
<Label
htmlFor={`centro-${centro}`}
className="text-sm font-normal cursor-pointer flex-1"
>
{centro}
</Label>
</div>
))}
</div>
{centrosCustoSelecionados.length > 0 && (
<div className="text-xs text-gray-500">
{centrosCustoSelecionados.length} centro(s) selecionado(s)
</div>
)}
</div>
{/* Conta {/* Conta */}
<div className="grid gap-3"> <div className="grid gap-3">
<Label htmlFor="conta">CONTA</Label> <div className="flex items-center justify-between">
<Select value={filtros.conta} onValueChange={(value) => handleFiltroChange('conta', value)}> <Label htmlFor="conta">CONTA</Label>
<SelectTrigger> <div className="flex gap-2">
<SelectValue placeholder="Selecione" /> <Button
</SelectTrigger> type="button"
<SelectContent> variant="outline"
<SelectItem value="Todas">Todas</SelectItem> size="sm"
{opcoesContas.map(conta => ( onClick={selecionarTodasContas}
<SelectItem key={conta} value={conta}>{conta}</SelectItem> className="text-xs h-6 px-2"
))} >
</SelectContent> Todas
</Select> </Button>
</div>*/} <Button
type="button"
variant="outline"
size="sm"
onClick={limparContas}
className="text-xs h-6 px-2"
>
Limpar
</Button>
</div>
</div>
<div className="max-h-32 overflow-y-auto border rounded-md p-2 space-y-2">
{opcoesContas.map(conta => (
<div key={conta} className="flex items-center space-x-2">
<Checkbox
id={`conta-${conta}`}
checked={contasSelecionadas.includes(conta)}
onCheckedChange={() => toggleConta(conta)}
/>
<Label
htmlFor={`conta-${conta}`}
className="text-sm font-normal cursor-pointer flex-1"
>
{conta}
</Label>
</div>
))}
</div>
{contasSelecionadas.length > 0 && (
<div className="text-xs text-gray-500">
{contasSelecionadas.length} conta(s) selecionada(s)
</div>
)}
</div>
{/* Valor {/* Valor
<div className="grid gap-3"> <div className="grid gap-3">

View File

@ -0,0 +1,30 @@
"use client"
import * as React from "react"
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
import { Check } from "lucide-react"
import { cn } from "@/lib/utils"
const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
"peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
className
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn("flex items-center justify-center text-current")}
>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
))
Checkbox.displayName = CheckboxPrimitive.Root.displayName
export { Checkbox }