Merge pull request #20 from JurunenseDevInterno/dev

Dev
This commit is contained in:
Alessandro Gonçalves 2025-10-24 14:17:00 -03:00 committed by GitHub
commit 716af28a10
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 116 additions and 53 deletions

View File

@ -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>

View File

@ -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>
);
}