commit
716af28a10
|
|
@ -106,6 +106,11 @@ const ExcelFilter: React.FC<ExcelFilterProps> = ({
|
|||
const [selectedValues, setSelectedValues] = React.useState<string[]>(currentFilter);
|
||||
const [selectAll, setSelectAll] = React.useState(false);
|
||||
|
||||
// Sincronizar selectedValues com currentFilter quando ele mudar
|
||||
React.useEffect(() => {
|
||||
setSelectedValues(currentFilter);
|
||||
}, [currentFilter]);
|
||||
|
||||
// Obter valores únicos da coluna baseado nos dados filtrados
|
||||
const uniqueValues = React.useMemo(() => {
|
||||
const values = filteredData
|
||||
|
|
@ -128,30 +133,25 @@ const ExcelFilter: React.FC<ExcelFilterProps> = ({
|
|||
);
|
||||
}, [uniqueValues, searchTerm]);
|
||||
|
||||
// Sincronizar estado local com filtros atuais
|
||||
React.useEffect(() => {
|
||||
setSelectedValues(currentFilter);
|
||||
}, [currentFilter]);
|
||||
|
||||
// Verificar se todos estão selecionados
|
||||
React.useEffect(() => {
|
||||
setSelectAll(selectedValues.length === filteredValues.length && filteredValues.length > 0);
|
||||
}, [selectedValues, filteredValues]);
|
||||
|
||||
const handleSelectAll = (checked: boolean) => {
|
||||
if (checked) {
|
||||
setSelectedValues(filteredValues);
|
||||
setSelectAll(true);
|
||||
} else {
|
||||
setSelectedValues([]);
|
||||
setSelectAll(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleValueToggle = (value: string, checked: boolean) => {
|
||||
let newValues: string[];
|
||||
if (checked) {
|
||||
setSelectedValues([...selectedValues, value]);
|
||||
newValues = [...selectedValues, value];
|
||||
} else {
|
||||
setSelectedValues(selectedValues.filter((v) => v !== value));
|
||||
newValues = selectedValues.filter((v) => v !== value);
|
||||
}
|
||||
setSelectedValues(newValues);
|
||||
setSelectAll(newValues.length === filteredValues.length && filteredValues.length > 0);
|
||||
};
|
||||
|
||||
const handleApply = () => {
|
||||
|
|
@ -294,6 +294,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
{ column: "", operator: "contains", value: "" },
|
||||
]);
|
||||
|
||||
// Estados para o card de agregação customizado (simplificado)
|
||||
const [aggregationCardRef, setAggregationCardRef] = React.useState<HTMLDivElement | null>(null);
|
||||
|
||||
// Estado para armazenar filtros externos (vindos do teste.tsx)
|
||||
const [filtrosExternos, setFiltrosExternos] = React.useState(filtros);
|
||||
|
||||
|
|
@ -426,6 +429,26 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
}));
|
||||
}, [data, columnFilters]);
|
||||
|
||||
// Função para renderizar header com filtro Excel
|
||||
const renderHeaderWithFilter = React.useCallback((column: GridColDef) => {
|
||||
return (params: any) => (
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<span className="text-sm font-medium">{column.headerName}</span>
|
||||
<div className="flex items-center">
|
||||
<ExcelFilter
|
||||
column={column}
|
||||
data={data}
|
||||
filteredData={filteredData}
|
||||
onFilterChange={handleColumnFilterChange}
|
||||
onSortChange={handleColumnSortChange}
|
||||
currentFilter={columnFilters[column.field] || []}
|
||||
currentSort={columnSorts[column.field] || null}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}, [data, filteredData, columnFilters, columnSorts, handleColumnFilterChange, handleColumnSortChange]);
|
||||
|
||||
// Definir colunas do DataGridPro
|
||||
const columns = React.useMemo(() => {
|
||||
const baseColumns = [
|
||||
|
|
@ -511,7 +534,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
width: 140,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
aggregable: true,
|
||||
renderCell: (params: any) => {
|
||||
const value = params.value;
|
||||
if (value === null || value === undefined || value === "") return "-";
|
||||
|
|
@ -535,7 +557,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
width: 130,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
aggregable: true,
|
||||
renderCell: (params: any) => {
|
||||
const value = params.value;
|
||||
if (value === null || value === undefined || value === "" || value === 0) return "-";
|
||||
|
|
@ -559,7 +580,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
width: 140,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
aggregable: true,
|
||||
renderCell: (params: any) => {
|
||||
const value = params.value;
|
||||
if (value === null || value === undefined || value === "" || value === 0) return "-";
|
||||
|
|
@ -583,7 +603,6 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
width: 130,
|
||||
sortable: true,
|
||||
resizable: true,
|
||||
aggregable: true,
|
||||
renderCell: (params: any) => {
|
||||
const value = params.value;
|
||||
if (value === null || value === undefined || value === "" || value === 0) return "-";
|
||||
|
|
@ -629,24 +648,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
// Adicionar renderHeader com filtro Excel para todas as colunas
|
||||
return baseColumns.map((col) => ({
|
||||
...col,
|
||||
renderHeader: (params: any) => (
|
||||
<div className="flex items-center justify-between w-full">
|
||||
<span className="text-sm font-medium">{col.headerName}</span>
|
||||
<div className="flex items-center">
|
||||
<ExcelFilter
|
||||
column={col}
|
||||
data={data}
|
||||
filteredData={filteredData}
|
||||
onFilterChange={handleColumnFilterChange}
|
||||
onSortChange={handleColumnSortChange}
|
||||
currentFilter={columnFilters[col.field] || []}
|
||||
currentSort={columnSorts[col.field] || null}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
renderHeader: renderHeaderWithFilter(col),
|
||||
}));
|
||||
}, [data, filteredData, columnFilters, columnSorts, handleColumnFilterChange, handleColumnSortChange]);
|
||||
}, [renderHeaderWithFilter]);
|
||||
|
||||
// Ordenar dados baseado na ordenação de coluna
|
||||
const sortedAndFilteredData = React.useMemo(() => {
|
||||
|
|
@ -676,6 +680,16 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
return sortedAndFilteredData.reduce((sum, item) => sum + (Number(item.valor) || 0), 0);
|
||||
}, [sortedAndFilteredData]);
|
||||
|
||||
// Calcular totais das colunas de valor para o card de agregação
|
||||
const columnTotals = React.useMemo(() => {
|
||||
return {
|
||||
valor: sortedAndFilteredData.reduce((sum, item) => sum + (Number(item.valor) || 0), 0),
|
||||
valor_previsto: sortedAndFilteredData.reduce((sum, item) => sum + (Number(item.valor_previsto) || 0), 0),
|
||||
valor_confirmado: sortedAndFilteredData.reduce((sum, item) => sum + (Number(item.valor_confirmado) || 0), 0),
|
||||
valor_pago: sortedAndFilteredData.reduce((sum, item) => sum + (Number(item.valor_pago) || 0), 0),
|
||||
};
|
||||
}, [sortedAndFilteredData]);
|
||||
|
||||
// Limpar filtros de colunas que não têm mais valores disponíveis
|
||||
React.useEffect(() => {
|
||||
const updatedFilters = { ...columnFilters };
|
||||
|
|
@ -771,7 +785,7 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-none mx-auto p-2">
|
||||
<div className="w-full max-w-none mx-auto p-2" style={{ overflowAnchor: 'none' }}>
|
||||
{/* Header Section */}
|
||||
<div className="mb-2">
|
||||
<div className="flex items-center justify-between mb-1">
|
||||
|
|
@ -880,8 +894,8 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
</div>
|
||||
|
||||
{/* DataGridPro */}
|
||||
<Card className="w-full h-[40vh] shadow-lg rounded-2xl">
|
||||
<CardContent className="p-4 h-full">
|
||||
<Card className="w-full h-[40vh] shadow-lg rounded-2xl" style={{ overflowAnchor: 'none' }}>
|
||||
<CardContent className="p-4 h-full" style={{ overflowAnchor: 'none' }}>
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-4">
|
||||
<h2 className="text-lg font-semibold">
|
||||
|
|
@ -911,18 +925,9 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
disableColumnSorting={true}
|
||||
pagination={false}
|
||||
disableVirtualization={false}
|
||||
initialState={{
|
||||
aggregation: {
|
||||
model: {
|
||||
valor: 'sum',
|
||||
valor_previsto: 'sum',
|
||||
valor_confirmado: 'sum',
|
||||
valor_pago: 'sum',
|
||||
},
|
||||
},
|
||||
}}
|
||||
getRowId={(row: any) => row.id || `row-${row.recnum || Math.random()}`}
|
||||
sx={{
|
||||
overflowAnchor: 'none',
|
||||
"& .MuiDataGrid-columnHeaders": {
|
||||
position: "sticky",
|
||||
top: 0,
|
||||
|
|
@ -936,7 +941,8 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
},
|
||||
"& .MuiDataGrid-virtualScroller": {
|
||||
overflowY: "auto",
|
||||
maxHeight: "calc(40vh - 120px)"
|
||||
maxHeight: "calc(40vh - 120px)",
|
||||
overflowAnchor: 'none'
|
||||
},
|
||||
"& .MuiDataGrid-toolbarContainer": {
|
||||
backgroundColor: "#f8fafc",
|
||||
|
|
@ -1004,6 +1010,65 @@ export default function AnaliticoComponent({ filtros }: AnaliticoProps) {
|
|||
}}
|
||||
/>
|
||||
|
||||
{/* Card de Agregação Customizado - Usando position: sticky */}
|
||||
{sortedAndFilteredData.length > 0 && (
|
||||
<div
|
||||
ref={setAggregationCardRef}
|
||||
className="w-full bg-gray-50 border-t border-gray-200 sticky bottom-0 z-50 shadow-lg"
|
||||
style={{ overflowAnchor: 'none' }}
|
||||
>
|
||||
<div className="px-4 py-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-6">
|
||||
<div className="text-sm font-medium text-gray-700">
|
||||
Vl.Realizado:
|
||||
<span className={`ml-2 font-semibold ${columnTotals.valor < 0 ? 'text-red-600' : 'text-gray-900'}`}>
|
||||
{new Intl.NumberFormat("pt-BR", {
|
||||
style: "currency",
|
||||
currency: "BRL",
|
||||
}).format(columnTotals.valor)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="text-sm font-medium text-gray-700">
|
||||
Vl.Previsto:
|
||||
<span className={`ml-2 font-semibold ${columnTotals.valor_previsto < 0 ? 'text-red-600' : 'text-gray-900'}`}>
|
||||
{new Intl.NumberFormat("pt-BR", {
|
||||
style: "currency",
|
||||
currency: "BRL",
|
||||
}).format(columnTotals.valor_previsto)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="text-sm font-medium text-gray-700">
|
||||
Vl.Confirmado:
|
||||
<span className={`ml-2 font-semibold ${columnTotals.valor_confirmado < 0 ? 'text-red-600' : 'text-gray-900'}`}>
|
||||
{new Intl.NumberFormat("pt-BR", {
|
||||
style: "currency",
|
||||
currency: "BRL",
|
||||
}).format(columnTotals.valor_confirmado)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="text-sm font-medium text-gray-700">
|
||||
Vl.Pago:
|
||||
<span className={`ml-2 font-semibold ${columnTotals.valor_pago < 0 ? 'text-red-600' : 'text-gray-900'}`}>
|
||||
{new Intl.NumberFormat("pt-BR", {
|
||||
style: "currency",
|
||||
currency: "BRL",
|
||||
}).format(columnTotals.valor_pago)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-sm text-gray-500">
|
||||
Total de Registros: <span className="font-semibold text-blue-600">{sortedAndFilteredData.length}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
|
|
|||
|
|
@ -2214,10 +2214,8 @@ export default function Teste() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Componente Analítico */}
|
||||
{!loading && data.length > 0 && (
|
||||
{/* Componente Analítico - Sempre renderizado para evitar violação das Rules of Hooks */}
|
||||
<AnaliticoComponent filtros={analiticoFiltros} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue