feat: implement persistent AG Grid column state

This commit is contained in:
Firman Ramdhani
2026-07-27 17:02:58 +07:00
parent a2616f2b2d
commit 75e0424e36
4 changed files with 87 additions and 4 deletions
@@ -10,6 +10,10 @@ import {
MenuItemDef,
DefaultMenuItem,
StatusBar,
ColumnMovedEvent,
ColumnResizedEvent,
ColumnVisibleEvent,
SortChangedEvent,
} from 'ag-grid-community';
import { Box, Group, TextInput, Indicator, CloseButton } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks';
@@ -181,6 +185,12 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
searchKey = 'search',
filterConfig,
// Grid config props
onColumnMoved,
onColumnResized,
onColumnVisible,
onSortChanged,
// Bulk action props
customBulkActions,
batchSize,
@@ -200,8 +210,16 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
// ---------------------------------------------------------------------------
const { t } = useEnterpriseModuleTranslationContext();
const { dataServices } = useEnterpriseModuleDataServiceContext<E>();
const { selectedRows, setSelectedRows, metaData, setMetaData, filterData, setFilterData } =
useEnterpriseModuleSelectionContext<E>();
const {
selectedRows,
setSelectedRows,
metaData,
setMetaData,
filterData,
setFilterData,
tableConfig,
setTableConfig,
} = useEnterpriseModuleSelectionContext<E>();
const navigation = useEnterpriseModuleNavigationContext();
const { config } = useEnterpriseModuleConfigContext();
@@ -791,6 +809,11 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
setSelectedRows([]);
if (params.api) {
// Restore grid configuration on mount
if (tableConfig) {
params.api.applyColumnState({ state: tableConfig, applyOrder: true });
}
// Attach the server-side datasource to the grid API
params.api.setGridOption('serverSideDatasource', datasource);
@@ -803,7 +826,7 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
}
}
},
[datasource, setSelectedRows, isPaginated, metaData],
[datasource, setSelectedRows, isPaginated, metaData, tableConfig],
);
// Triggered whenever the row selection in the grid changes
@@ -817,6 +840,50 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
[setSelectedRows],
);
// ---------------------------------------------------------------------------
// Save Grid Configuration State Handlers
// ---------------------------------------------------------------------------
const saveGridState = useCallback(() => {
if (!gridApiRef.current || !setTableConfig) return;
const state = gridApiRef.current.getColumnState();
setTableConfig(state);
}, [setTableConfig]);
const handleColumnMoved = useCallback(
(e: ColumnMovedEvent<E>) => {
saveGridState();
if (onColumnMoved) onColumnMoved(e);
},
[saveGridState, onColumnMoved],
);
const handleColumnResized = useCallback(
(e: ColumnResizedEvent<E>) => {
// Only save when resizing is finished to prevent excessive re-renders
if (e.finished) {
saveGridState();
}
if (onColumnResized) onColumnResized(e);
},
[saveGridState, onColumnResized],
);
const handleColumnVisible = useCallback(
(e: ColumnVisibleEvent<E>) => {
saveGridState();
if (onColumnVisible) onColumnVisible(e);
},
[saveGridState, onColumnVisible],
);
const handleSortChanged = useCallback(
(e: SortChangedEvent<E>) => {
saveGridState();
if (onSortChanged) onSortChanged(e);
},
[saveGridState, onSortChanged],
);
// Configures the context menu items available when right-clicking a cell
const getContextMenuItems = useCallback(():
| (DefaultMenuItem | MenuItemDef)[]
@@ -923,6 +990,10 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
enableCellTextSelection={true}
onSelectionChanged={handleSelectionChanged}
onGridReady={onGridReady}
onColumnMoved={handleColumnMoved}
onColumnResized={handleColumnResized}
onColumnVisible={handleColumnVisible}
onSortChanged={handleSortChanged}
getContextMenuItems={getContextMenuItems}
statusBar={statusBar}
domLayout={domLayout}
@@ -4,6 +4,7 @@ import type { ZodType } from 'zod';
import type { BaseEntity, BaseRemoteDataServices, StandardPaginationMeta } from '@repo/core-api/data-services';
import type { ModulePageHeaderProps } from '../components/module-page-header';
import { PageActionsProps } from '../../../components';
import { ColumnState } from 'ag-grid-community';
// ---------------------------------------------------------------------------
// Module Constants & Base Types
@@ -140,6 +141,8 @@ export interface SelectionSlice<
setMetaData: (data: TMeta | null) => void;
filterData: TFilter | null;
setFilterData: (data: TFilter | null) => void;
tableConfig: ColumnState[] | null;
setTableConfig: (config: ColumnState[] | null) => void;
}
/**
@@ -156,6 +159,8 @@ export interface EnterpriseModuleState<
setFilterData: (data: TFilter | null) => void;
selectedRows: E[];
setSelectedRows: (rows: E[]) => void;
tableConfig: ColumnState[] | null;
setTableConfig: (config: ColumnState[] | null) => void;
privileges: string[];
setPrivileges: (privileges: string[]) => void;
}
@@ -82,6 +82,8 @@ export function EnterpriseModuleProvider<
const setMetaData = store((state: S) => state.setMetaData);
const filterData = store((state: S) => state.filterData);
const setFilterData = store((state: S) => state.setFilterData);
const tableConfig = store((state: S) => state.tableConfig);
const setTableConfig = store((state: S) => state.setTableConfig);
const selectionSlice = useMemo(
() => ({
@@ -91,8 +93,10 @@ export function EnterpriseModuleProvider<
setMetaData,
filterData,
setFilterData,
tableConfig,
setTableConfig,
}),
[selectedRows, setSelectedRows, metaData, setMetaData, filterData, setFilterData],
[selectedRows, setSelectedRows, metaData, setMetaData, filterData, setFilterData, tableConfig, setTableConfig],
);
// ---------------------------------------------------------------------------