From be48b04ee7f26426e2923b438568cb9b19990ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alessandro=20Gon=C3=A7aalves?= Date: Tue, 21 Oct 2025 00:49:20 -0300 Subject: [PATCH] fix: ajuste no fechamento do sheet --- package-lock.json | 24 ++ package.json | 1 + src/app/DRE/teste.tsx | 602 ++++++++++++++++++++++++++++-------- src/components/ui/label.tsx | 24 ++ src/components/ui/sheet.tsx | 138 +++++++++ 5 files changed, 658 insertions(+), 131 deletions(-) create mode 100644 src/components/ui/label.tsx create mode 100644 src/components/ui/sheet.tsx diff --git a/package-lock.json b/package-lock.json index d37ba04..99c4410 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.3", "@tanstack/react-table": "^8.21.3", @@ -1726,6 +1727,29 @@ } } }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "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-popper": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", diff --git a/package.json b/package.json index 6a855d1..2c24f01 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-label": "^2.1.7", "@radix-ui/react-select": "^2.2.6", "@radix-ui/react-slot": "^1.2.3", "@tanstack/react-table": "^8.21.3", diff --git a/src/app/DRE/teste.tsx b/src/app/DRE/teste.tsx index 8a8fa62..496e985 100644 --- a/src/app/DRE/teste.tsx +++ b/src/app/DRE/teste.tsx @@ -1,8 +1,27 @@ "use client"; -import { LoaderPinwheel, ChevronDown, ChevronRight } from "lucide-react"; +import { LoaderPinwheel, ChevronDown, ChevronRight, Filter } from "lucide-react"; import { useEffect, useState } from "react"; import AnaliticoComponent from "./analitico"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Sheet, + SheetContent, + SheetDescription, + SheetFooter, + SheetHeader, + SheetTitle, + SheetTrigger, +} from "@/components/ui/sheet"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; interface DREItem { codfilial: string; @@ -34,7 +53,7 @@ interface HierarchicalRow { export default function Teste() { const [data, setData] = useState([]); - const [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [expandedGroups, setExpandedGroups] = useState>(new Set()); const [expandedSubgrupos, setExpandedSubgrupos] = useState>( @@ -45,6 +64,28 @@ export default function Teste() { ); const [mesesDisponiveis, setMesesDisponiveis] = useState([]); + // Estados para filtros + const [filtros, setFiltros] = useState({ + periodoDe: "", + periodoAte: "", + grupo: "Todos", + subgrupo: "Todos", + centroCusto: "Todos", + conta: "Todas", + valorMin: "", + valorMax: "", + buscaTextual: "" + }); + const [isFilterOpen, setIsFilterOpen] = useState(false); + const [dadosFiltrados, setDadosFiltrados] = useState([]); + const [filtrosAplicados, setFiltrosAplicados] = useState(false); + + // Estados para opções dos filtros + const [opcoesGrupos, setOpcoesGrupos] = useState([]); + const [opcoesSubgrupos, setOpcoesSubgrupos] = useState([]); + const [opcoesCentrosCusto, setOpcoesCentrosCusto] = useState([]); + const [opcoesContas, setOpcoesContas] = useState([]); + // Estados para analítico const [analiticoFiltros, setAnaliticoFiltros] = useState({ dataInicio: "", @@ -57,9 +98,56 @@ export default function Teste() { const [linhaSelecionada, setLinhaSelecionada] = useState(null); useEffect(() => { - fetchData(); + // Carregar períodos disponíveis da API + carregarPeriodosDisponiveis(); + + // Inicializar filtros com período atual + const agora = new Date(); + const anoAtual = agora.getFullYear(); + const mesAtual = String(agora.getMonth() + 1).padStart(2, '0'); + const periodoAtual = `${anoAtual}-${mesAtual}`; + + setFiltros(prev => ({ + ...prev, + periodoDe: `${anoAtual}-01`, + periodoAte: periodoAtual + })); }, []); + const carregarPeriodosDisponiveis = async () => { + try { + const response = await fetch("/api/dre-oracle"); + if (!response.ok) { + throw new Error(`Erro HTTP: ${response.status}`); + } + + const dadosCompletos = await response.json(); + + // Extrair períodos únicos dos dados + const periodosUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.data_competencia))].sort(); + setMesesDisponiveis(periodosUnicos); + + // Extrair grupos únicos + const gruposUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.grupo))].sort(); + setOpcoesGrupos(gruposUnicos); + + // Extrair subgrupos únicos + const subgruposUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.subgrupo))].sort(); + setOpcoesSubgrupos(subgruposUnicos); + + // Extrair centros de custo únicos + const centrosCustoUnicos = [...new Set(dadosCompletos.map((item: DREItem) => item.centro_custo))].sort(); + setOpcoesCentrosCusto(centrosCustoUnicos); + + // Extrair contas únicas + const contasUnicas = [...new Set(dadosCompletos.map((item: DREItem) => item.conta))].sort(); + setOpcoesContas(contasUnicas); + + } catch (error) { + console.error("Erro ao carregar períodos:", error); + } + }; + const fetchData = async () => { try { setLoading(true); @@ -195,6 +283,139 @@ export default function Teste() { setExpandedCentros(newExpanded); }; + const handleFiltroChange = (campo: string, valor: string) => { + setFiltros(prev => ({ + ...prev, + [campo]: valor + })); + }; + + const limparFiltros = () => { + const agora = new Date(); + const anoAtual = agora.getFullYear(); + const mesAtual = String(agora.getMonth() + 1).padStart(2, '0'); + const periodoAtual = `${anoAtual}-${mesAtual}`; + + setFiltros({ + periodoDe: `${anoAtual}-01`, + periodoAte: periodoAtual, + grupo: "Todos", + subgrupo: "Todos", + centroCusto: "Todos", + conta: "Todas", + valorMin: "", + valorMax: "", + buscaTextual: "" + }); + + // Limpar dados da tabela + setData([]); + setDadosFiltrados([]); + setFiltrosAplicados(false); + setMesesDisponiveis([]); + }; + + const aplicarFiltros = async () => { + // Fechar o Sheet primeiro + setIsFilterOpen(false); + + // Aguardar um pouco para a animação de fechamento + setTimeout(async () => { + try { + setLoading(true); + setError(null); + + // Carregar dados da API + const response = await fetch("/api/dre-oracle"); + if (!response.ok) { + throw new Error(`Erro HTTP: ${response.status}`); + } + + const dadosCompletos = await response.json(); + + // Aplicar filtros nos dados + let dadosFiltrados = dadosCompletos; + + // Filtro por período + if (filtros.periodoDe && filtros.periodoAte) { + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => { + const dataItem = item.data_competencia; + return dataItem >= filtros.periodoDe && dataItem <= filtros.periodoAte; + }); + } + + // Filtro por grupo + if (filtros.grupo !== "Todos") { + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => + item.grupo === filtros.grupo + ); + } + + // Filtro por subgrupo + if (filtros.subgrupo !== "Todos") { + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => + item.subgrupo === filtros.subgrupo + ); + } + + // Filtro por centro de custo + if (filtros.centroCusto !== "Todos") { + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => + item.centro_custo === filtros.centroCusto + ); + } + + // Filtro por conta + if (filtros.conta !== "Todas") { + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => + item.conta === filtros.conta + ); + } + + // Filtro por valor mínimo + if (filtros.valorMin) { + const valorMin = parseFloat(filtros.valorMin.replace(',', '.')); + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => + parseFloat(item.valor) >= valorMin + ); + } + + // Filtro por valor máximo + if (filtros.valorMax) { + const valorMax = parseFloat(filtros.valorMax.replace(',', '.')); + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => + parseFloat(item.valor) <= valorMax + ); + } + + // Filtro por busca textual + if (filtros.buscaTextual) { + const termoBusca = filtros.buscaTextual.toLowerCase(); + dadosFiltrados = dadosFiltrados.filter((item: DREItem) => + item.grupo.toLowerCase().includes(termoBusca) || + item.subgrupo.toLowerCase().includes(termoBusca) || + item.centro_custo.toLowerCase().includes(termoBusca) || + item.conta.toLowerCase().includes(termoBusca) + ); + } + + setData(dadosFiltrados); + setDadosFiltrados(dadosFiltrados); + setFiltrosAplicados(true); + + // Extrair meses únicos dos dados filtrados + const mesesUnicos = [...new Set(dadosFiltrados.map(item => item.data_competencia))].sort(); + setMesesDisponiveis(mesesUnicos); + + } catch (error) { + console.error("Erro ao aplicar filtros:", error); + setError(error instanceof Error ? error.message : "Erro desconhecido"); + } finally { + setLoading(false); + } + }, 300); // Aguardar 300ms para a animação de fechamento + }; + const calcularValoresPorMes = (items: DREItem[]): Record => { const valoresPorMes: Record = {}; @@ -587,113 +808,9 @@ export default function Teste() { } }; - if (loading) { - return ( -
-
-
- {/*
- - - -
*/} -
-

- DRE Gerencial -

-

- Demonstração do Resultado do Exercício -

-
-
-
+ // Loading será tratado dentro do componente principal -
-
-
- -
-

- Carregando dados... -

-

- Aguarde enquanto processamos as informações -

-
-
-
- ); - } - - if (error) { - return ( -
-
-
- {/*
- - - -
*/} -
-

- DRE Gerencial -

-

- Demonstração do Resultado do Exercício -

-
-
-
- -
-
-
- - - -
-

- Erro ao carregar DRE Gerencial -

-

- {error} -

-
-
-
- ); - } + // Error será tratado dentro do componente principal const hierarchicalData = buildHierarchicalData(); @@ -701,33 +818,255 @@ export default function Teste() {
{/* Header Section */}
-
- {/*
- - - -
*/} -
-

DRE Gerencial

-

- Demonstração do Resultado do Exercício -

+
+
+
+

DRE Gerencial

+

+ Demonstração do Resultado do Exercício +

+
+ + {/* Botão de Filtro */} + + + + + + + Filtros + + Ajuste os critérios e clique em Pesquisar para atualizar a visão. + + + +
+ {/* Período */} +
+ +
+
+ + +
+
+ + +
+
+
+ + {/* Grupo */} +
+ + +
+ + {/* Subgrupo */} +
+ + +
+ + {/* Centro de Custo */} +
+ + +
+ + {/* Conta */} +
+ + +
+ + {/* Valor */} +
+ +
+
+ +
+ R$ + handleFiltroChange('valorMin', e.target.value)} + className="pl-8" + placeholder="0,00" + /> +
+
+
+ +
+ R$ + handleFiltroChange('valorMax', e.target.value)} + className="pl-8" + placeholder="0,00" + /> +
+
+
+
+ + {/* Busca Textual */} +
+ + handleFiltroChange('buscaTextual', e.target.value)} + placeholder="Pesquise por grupo, subgrupo, centro ou conta" + /> +
+
+ + + + + + +
+
+ {/* Loading quando aplicando filtros */} + {loading && ( +
+
+
+ +
+
+

+ Aplicando filtros... +

+

+ Aguarde enquanto processamos os dados. +

+
+
+
+ )} + + {/* Erro */} + {error && !loading && ( +
+
+
+ + + +
+
+

+ Erro ao carregar dados +

+

{error}

+ +
+
+
+ )} + + {/* Mensagem quando não há dados */} + {!filtrosAplicados && !loading && !error && ( +
+
+
+ +
+
+

+ Nenhum dado exibido +

+

+ Clique no botão "Filtros" para definir os critérios de busca e visualizar os dados do DRE. +

+ +
+
+
+ )} + {/* Table Container */} -
+ {filtrosAplicados && !loading && !error && ( +
{/* Table Header */}
@@ -829,6 +1168,7 @@ export default function Teste() { ))}
+ )} {/* Componente Analítico */} {!loading && data.length > 0 && ( diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..683faa7 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,24 @@ +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" +) + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)) +Label.displayName = LabelPrimitive.Root.displayName + +export { Label } diff --git a/src/components/ui/sheet.tsx b/src/components/ui/sheet.tsx new file mode 100644 index 0000000..d3422d0 --- /dev/null +++ b/src/components/ui/sheet.tsx @@ -0,0 +1,138 @@ +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = SheetPrimitive.Portal + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +}