diff --git a/package-lock.json b/package-lock.json index 089cf27..1f38f5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,6 +34,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "tailwind-merge": "^3.3.1", + "vaul": "^1.1.2", "xlsx": "^0.18.5" }, "devDependencies": { @@ -11778,6 +11779,19 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/when-exit": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/when-exit/-/when-exit-2.1.4.tgz", diff --git a/package.json b/package.json index d3cea65..57db787 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "react": "^18.3.1", "react-dom": "^18.3.1", "tailwind-merge": "^3.3.1", + "vaul": "^1.1.2", "xlsx": "^0.18.5" }, "devDependencies": { diff --git a/src/app/DRE/analitico.tsx b/src/app/DRE/analitico.tsx index d6d6d1c..d2cd729 100644 --- a/src/app/DRE/analitico.tsx +++ b/src/app/DRE/analitico.tsx @@ -33,7 +33,17 @@ import { SelectItem, } from "@/components/ui/select"; import { Checkbox } from "@/components/ui/checkbox"; -import { Download, Filter, X, Search, ArrowUpDown, ArrowUp, ArrowDown } from "lucide-react"; +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerTitle, + DrawerTrigger, +} from "@/components/ui/drawer"; +import { Download, Filter, X, Search, ArrowUpDown, ArrowUp, ArrowDown, Maximize2, Minimize2 } from "lucide-react"; import * as XLSX from "xlsx"; interface AnaliticoItem { @@ -295,6 +305,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { const [loading, setLoading] = React.useState(false); const [globalFilter, setGlobalFilter] = React.useState(""); const [open, setOpen] = React.useState(false); + const [drawerOpen, setDrawerOpen] = React.useState(false); const [columnFilters, setColumnFilters] = React.useState>({}); const [columnSorts, setColumnSorts] = React.useState>({}); const [conditions, setConditions] = React.useState([ @@ -810,6 +821,324 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { setGlobalFilter(""); }; + // Função para renderizar o conteúdo principal do componente (reutilizável) + const renderAnaliticoContent = (isMaximized: boolean = false) => { + return ( + <> + {/* Filtros Externos Ativos */} + {(filtrosExternos.dataInicio || filtrosExternos.centroCusto || filtrosExternos.codigoGrupo || filtrosExternos.codigoConta) && ( +
+
+ Filtros aplicados pela tabela DRE Gerencial: +
+ {filtrosExternos.dataInicio && filtrosExternos.dataFim && ( + + Período: {filtrosExternos.dataInicio} a {filtrosExternos.dataFim} + + )} + {filtrosExternos.centroCusto && ( + + Centro: {filtrosExternos.centroCusto} + + )} + {filtrosExternos.codigoGrupo && ( + + Grupo: {filtrosExternos.codigoGrupo} + + )} + {filtrosExternos.codigoConta && ( + + Conta: {filtrosExternos.codigoConta} + + )} +
+
+ )} + + {/* Controls */} +
+ {data.length > 0 && ( + + )} + +
+ + {/* DataGridPro */} + + +
+
+

+ Total de Registros: {sortedAndFilteredData.length} +

+
+ Valor Total: + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(valorTotal)} + +
+
+
+ +
+ row.id || `row-${row.recnum || Math.random()}`} + sx={{ + overflowAnchor: 'none', + height: "100%", + display: "flex", + flexDirection: "column", + "& .MuiDataGrid-root": { + display: "flex", + flexDirection: "column", + height: "100%", + flex: 1, + overflow: "hidden", + }, + "& .MuiDataGrid-main": { + overflow: "hidden !important", + position: "relative", + height: "100%", + display: "flex", + flexDirection: "column", + flex: 1, + }, + // Container dos headers - DEVE FICAR FIXO (não rola) + "& .MuiDataGrid-container--top": { + overflow: "hidden !important", + position: "relative", + zIndex: 100, + backgroundColor: "#f9fafb", + flexShrink: 0, + flexGrow: 0, + }, + "& .MuiDataGrid-columnHeaders": { + position: "relative !important", + backgroundColor: "#f9fafb !important", + zIndex: 100, + borderBottom: "1px solid #e5e7eb", + }, + "& .MuiDataGrid-columnHeader": { + backgroundColor: "#f9fafb !important", + }, + "& .MuiDataGrid-columnHeaderRow": { + backgroundColor: "#f9fafb !important", + }, + "& .MuiDataGrid-columnHeadersInner": { + backgroundColor: "#f9fafb !important", + }, + "& .MuiDataGrid-cell": { + borderBottom: "1px solid #f0f0f0", + fontSize: "0.875rem", + }, + // Container do virtualScroller - deve ter scroll e ocupar espaço restante + "& .MuiDataGrid-container--bottom": { + flex: 1, + overflow: "hidden !important", + position: "relative", + minHeight: 0, + display: "flex", + flexDirection: "column", + }, + // Apenas o virtualScroller deve ter scroll + "& .MuiDataGrid-virtualScroller": { + overflowY: "auto !important", + overflowX: "auto !important", + height: "100% !important", + flex: 1, + overflowAnchor: 'none', + // Garantir que a barra de scroll seja visível + scrollbarWidth: "thin", + "&::-webkit-scrollbar": { + height: "8px", + width: "8px", + }, + "&::-webkit-scrollbar-track": { + background: "#f1f1f1", + }, + "&::-webkit-scrollbar-thumb": { + background: "#888", + borderRadius: "4px", + }, + "&::-webkit-scrollbar-thumb:hover": { + background: "#555", + }, + }, + "& .MuiDataGrid-virtualScrollerContent": { + minWidth: "max-content", + }, + "& .MuiDataGrid-row": { + minWidth: "max-content", + }, + "& .MuiDataGrid-toolbarContainer": { + backgroundColor: "#f8fafc", + borderBottom: "1px solid #e5e7eb", + padding: "8px 16px", + }, + "& .MuiDataGrid-scrollbar": { + display: "none", + }, + // Ocultar todos os ícones nativos das colunas + "& .MuiDataGrid-columnHeaderMenuContainer": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderMenuButton": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderSortIcon": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderSortIconContainer": { + display: "none !important", + }, + "& .MuiDataGrid-iconButtonContainer": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderSeparator": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderSortButton": { + display: "none !important", + }, + // Ocultar qualquer ícone de menu adicional + "& .MuiDataGrid-menuIcon": { + display: "none !important", + }, + "& .MuiDataGrid-menuIconButton": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderMenuIcon": { + display: "none !important", + }, + "& .MuiDataGrid-columnHeaderMenuIconButton": { + display: "none !important", + }, + "& .MuiDataGrid-menuContainer": { + display: "none !important", + }, + "& .MuiDataGrid-menu": { + display: "none !important", + }, + // Ocultar footer de paginação + "& .MuiDataGrid-footerContainer": { + display: "none !important", + }, + "& .MuiDataGrid-pagination": { + display: "none !important", + }, + "& .MuiTablePagination-root": { + display: "none !important", + }, + // Garantir que nosso botão customizado apareça + "& .MuiDataGrid-columnHeaderTitleContainer": { + width: "100%", + display: "flex", + alignItems: "center", + justifyContent: "space-between", + }, + }} + /> + + {/* Card de Agregação Customizado */} + {sortedAndFilteredData.length > 0 && ( +
+
+
+
+
+ Vl.Realizado: + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(columnTotals.valor)} + +
+ +
+ Vl.Previsto: + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(columnTotals.valor_previsto)} + +
+ +
+ Vl.Confirmado: + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(columnTotals.valor_confirmado)} + +
+ +
+ Vl.Pago: + + {new Intl.NumberFormat("pt-BR", { + style: "currency", + currency: "BRL", + }).format(columnTotals.valor_pago)} + +
+
+ +
+ Total de Registros: {sortedAndFilteredData.length} +
+
+
+
+ )} +
+
+
+ + ); + }; + return (
{/* Header Section */} @@ -914,263 +1243,53 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) { Exportar XLSX -
- - - - - - {/* DataGridPro */} - - -
-
-

- Total de Registros: {sortedAndFilteredData.length} -

-
- Valor Total: - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(valorTotal)} - -
-
-
- -
- row.id || `row-${row.recnum || Math.random()}`} - sx={{ - overflowAnchor: 'none', - height: "100%", - display: "flex", - flexDirection: "column", - "& .MuiDataGrid-root": { - display: "flex", - flexDirection: "column", - height: "100%", - flex: 1, - overflow: "hidden", - }, - "& .MuiDataGrid-main": { - overflow: "hidden !important", - position: "relative", - height: "100%", - display: "flex", - flexDirection: "column", - flex: 1, - }, - // Container dos headers - DEVE FICAR FIXO (não rola) - "& .MuiDataGrid-container--top": { - overflow: "hidden !important", - position: "relative", - zIndex: 100, - backgroundColor: "#f9fafb", - flexShrink: 0, - flexGrow: 0, - }, - "& .MuiDataGrid-columnHeaders": { - position: "relative !important", - backgroundColor: "#f9fafb !important", - zIndex: 100, - borderBottom: "1px solid #e5e7eb", - }, - "& .MuiDataGrid-columnHeader": { - backgroundColor: "#f9fafb !important", - }, - "& .MuiDataGrid-columnHeaderRow": { - backgroundColor: "#f9fafb !important", - }, - "& .MuiDataGrid-columnHeadersInner": { - backgroundColor: "#f9fafb !important", - }, - "& .MuiDataGrid-cell": { - borderBottom: "1px solid #f0f0f0", - fontSize: "0.875rem", - }, - // Container do virtualScroller - deve ter scroll e ocupar espaço restante - "& .MuiDataGrid-container--bottom": { - flex: 1, - overflow: "hidden !important", - position: "relative", - minHeight: 0, - display: "flex", - flexDirection: "column", - }, - // Apenas o virtualScroller deve ter scroll - "& .MuiDataGrid-virtualScroller": { - overflowY: "auto !important", - overflowX: "auto !important", - height: "100% !important", - flex: 1, - overflowAnchor: 'none', - // Garantir que a barra de scroll seja visível - scrollbarWidth: "thin", - "&::-webkit-scrollbar": { - height: "8px", - width: "8px", - }, - "&::-webkit-scrollbar-track": { - background: "#f1f1f1", - }, - "&::-webkit-scrollbar-thumb": { - background: "#888", - borderRadius: "4px", - }, - "&::-webkit-scrollbar-thumb:hover": { - background: "#555", - }, - }, - "& .MuiDataGrid-virtualScrollerContent": { - minWidth: "max-content", - }, - "& .MuiDataGrid-row": { - minWidth: "max-content", - }, - "& .MuiDataGrid-toolbarContainer": { - backgroundColor: "#f8fafc", - borderBottom: "1px solid #e5e7eb", - padding: "8px 16px", - }, - "& .MuiDataGrid-scrollbar": { - display: "none", - }, - // Ocultar todos os ícones nativos das colunas - "& .MuiDataGrid-columnHeaderMenuContainer": { - display: "none !important", - }, - "& .MuiDataGrid-columnHeaderMenuButton": { - display: "none !important", - }, - "& .MuiDataGrid-columnHeaderSortIcon": { - display: "none !important", - }, - "& .MuiDataGrid-columnHeaderSortIconContainer": { - display: "none !important", - }, - "& .MuiDataGrid-iconButtonContainer": { - display: "none !important", - }, - "& .MuiDataGrid-columnHeaderSeparator": { - display: "none !important", - }, - "& .MuiDataGrid-columnHeaderSortButton": { - display: "none !important", - }, - // Ocultar qualquer ícone de menu adicional - "& .MuiDataGrid-menuIcon": { - display: "none !important", - }, - "& .MuiDataGrid-menuIconButton": { - display: "none !important", - }, - "& .MuiDataGrid-columnHeaderMenuIcon": { - display: "none !important", - }, - "& .MuiDataGrid-columnHeaderMenuIconButton": { - display: "none !important", - }, - "& .MuiDataGrid-menuContainer": { - display: "none !important", - }, - "& .MuiDataGrid-menu": { - display: "none !important", - }, - // Ocultar footer de paginação - "& .MuiDataGrid-footerContainer": { - display: "none !important", - }, - "& .MuiDataGrid-pagination": { - display: "none !important", - }, - "& .MuiTablePagination-root": { - display: "none !important", - }, - // Garantir que nosso botão customizado apareça - "& .MuiDataGrid-columnHeaderTitleContainer": { - width: "100%", - display: "flex", - alignItems: "center", - justifyContent: "space-between", - }, - }} - /> - - {/* Card de Agregação Customizado - Usando position: sticky */} - {sortedAndFilteredData.length > 0 && ( -
+ + + + + +
+
+ + Análise Analítica{filtros.linhaSelecionada ? ` - ${filtros.linhaSelecionada}` : ""} + + + Relatório detalhado de transações - Versão Maximizada + +
+ + + +
+
+
+ {renderAnaliticoContent(true)} +
+
+ +
- -
- Vl.Previsto: - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(columnTotals.valor_previsto)} -
- -
- Vl.Confirmado: - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(columnTotals.valor_confirmado)} - -
- -
- Vl.Pago: - - {new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(columnTotals.valor_pago)} - -
- - -
- Total de Registros: {sortedAndFilteredData.length} -
- - - - )} - - -
-
+ + + + {/* Conteúdo Principal - Versão Normal */} + {renderAnaliticoContent(false)} {/* Advanced Filters Dialog */} diff --git a/src/components/ui/drawer.tsx b/src/components/ui/drawer.tsx new file mode 100644 index 0000000..e61f344 --- /dev/null +++ b/src/components/ui/drawer.tsx @@ -0,0 +1,110 @@ +"use client" + +import * as React from "react" +import { Drawer as DrawerPrimitive } from "vaul" + +import { cn } from "@/lib/utils" + +const Drawer = DrawerPrimitive.Root + +const DrawerTrigger = DrawerPrimitive.Trigger + +const DrawerClose = DrawerPrimitive.Close + +const DrawerPortal = DrawerPrimitive.Portal + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName + +const DrawerContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + +
+ {children} + + +)) +DrawerContent.displayName = "DrawerContent" + +const DrawerHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DrawerHeader.displayName = "DrawerHeader" + +const DrawerFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DrawerFooter.displayName = "DrawerFooter" + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerTitle.displayName = DrawerPrimitive.Title.displayName + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerDescription.displayName = DrawerPrimitive.Description.displayName + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +} +