Merge pull request 'core/page-provider' (#38) from core/page-provider into main

Reviewed-on: eigen/fe-monorepo-template#38
This commit is contained in:
2026-07-27 10:47:32 +00:00
9 changed files with 304 additions and 52 deletions
@@ -16,4 +16,7 @@ export const fullPageStore = create<FullPageStoreState>((set) => ({
privileges: [], privileges: [],
setPrivileges: (privileges) => set({ privileges }), setPrivileges: (privileges) => set({ privileges }),
tableConfig: null,
setTableConfig: (config) => set({ tableConfig: config }),
})); }));
@@ -0,0 +1,92 @@
import { create } from 'zustand';
import { appStorage, AppStorageKey } from '../../../../core/storage/local';
interface SidebarState {
openedKeys: Set<string>;
isInitialized: boolean;
isAllExpanded: boolean;
searchQuery: string;
// Actions
initializeStorage: (initialKeysFromActive: string[], allParentKeys: string[]) => Promise<void>;
toggleMenu: (key: string, isOpened: boolean, allParentKeys: string[]) => Promise<void>;
expandAll: (allParentKeys: string[]) => Promise<void>;
collapseAll: () => Promise<void>;
syncActiveKeys: (keys: string[], allParentKeys: string[]) => Promise<void>;
setSearchQuery: (query: string) => void;
}
export const useSidebarStore = create<SidebarState>((set, get) => ({
openedKeys: new Set<string>(),
isInitialized: false,
isAllExpanded: false,
searchQuery: '',
initializeStorage: async (initialKeysFromActive, allParentKeys) => {
if (get().isInitialized) return;
const saved = await appStorage.getItem<string[]>(AppStorageKey.SIDEBAR_OPEN_MENUS);
if (saved) {
const openedSet = new Set(saved);
const isAllExpanded = saved.length > 0 && allParentKeys.every((k) => openedSet.has(k));
set({ openedKeys: openedSet, isInitialized: true, isAllExpanded });
} else {
// First time load: use initial active keys
await appStorage.setItem(AppStorageKey.SIDEBAR_OPEN_MENUS, initialKeysFromActive);
const openedSet = new Set(initialKeysFromActive);
const isAllExpanded = initialKeysFromActive.length > 0 && allParentKeys.every((k) => openedSet.has(k));
set({ openedKeys: openedSet, isInitialized: true, isAllExpanded });
}
},
toggleMenu: async (key, isOpened, allParentKeys) => {
const prevSet = get().openedKeys;
const nextSet = new Set(prevSet);
if (isOpened) {
nextSet.add(key);
} else {
nextSet.delete(key);
}
const nextArr = Array.from(nextSet);
const isAllExpanded = nextArr.length > 0 && allParentKeys.every((k) => nextSet.has(k));
set({ openedKeys: nextSet, isAllExpanded });
await appStorage.setItem(AppStorageKey.SIDEBAR_OPEN_MENUS, nextArr);
},
expandAll: async (allParentKeys) => {
const nextSet = new Set(allParentKeys);
set({ openedKeys: nextSet, isAllExpanded: true });
await appStorage.setItem(AppStorageKey.SIDEBAR_OPEN_MENUS, allParentKeys);
},
collapseAll: async () => {
set({ openedKeys: new Set(), isAllExpanded: false });
await appStorage.setItem(AppStorageKey.SIDEBAR_OPEN_MENUS, []);
},
syncActiveKeys: async (keysToSync, allParentKeys) => {
const prevSet = get().openedKeys;
let hasChanges = false;
const nextSet = new Set(prevSet);
for (const key of keysToSync) {
if (!nextSet.has(key)) {
nextSet.add(key);
hasChanges = true;
}
}
if (hasChanges) {
const nextArr = Array.from(nextSet);
const isAllExpanded = nextArr.length > 0 && allParentKeys.every((k) => nextSet.has(k));
set({ openedKeys: nextSet, isAllExpanded });
await appStorage.setItem(AppStorageKey.SIDEBAR_OPEN_MENUS, nextArr);
}
},
setSearchQuery: (query: string) => set({ searchQuery: query }),
}));
@@ -1,6 +1,18 @@
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Link, useLocation } from 'react-router-dom'; import { Link, useLocation } from 'react-router-dom';
import { Box, NavLink, Stack, Tooltip, ActionIcon, Divider, Menu, TextInput, Button, Group } from '@repo/ui/components'; import {
Box,
NavLink,
Stack,
Tooltip,
ActionIcon,
Divider,
Menu,
TextInput,
Button,
Group,
Text,
} from '@repo/ui/components';
import { useCoreAppShell } from '@repo/ui/components'; import { useCoreAppShell } from '@repo/ui/components';
import { ChevronsLeft, ChevronsRight, PanelTopClose, PanelTopOpen, Search, X, History, Bookmark } from 'lucide-react'; import { ChevronsLeft, ChevronsRight, PanelTopClose, PanelTopOpen, Search, X, History, Bookmark } from 'lucide-react';
import { publish } from '@repo/core-events'; import { publish } from '@repo/core-events';
@@ -9,6 +21,8 @@ import type { SidebarVariant } from '@repo/ui/components';
import { useTranslation } from '@repo/core-i18n'; import { useTranslation } from '@repo/core-i18n';
import { shortcutsData } from '@repo/ui/constants'; import { shortcutsData } from '@repo/ui/constants';
import { LAYOUT_EVENTS } from '../../../../core/constants/events'; import { LAYOUT_EVENTS } from '../../../../core/constants/events';
import { useSidebarStore } from './sidebar.store';
import { useDebouncedValue } from '@repo/ui/hooks';
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Props // Props
@@ -39,17 +53,15 @@ interface SidebarMenuProps {
interface MenuItemExpandedProps { interface MenuItemExpandedProps {
item: MenuItemType; item: MenuItemType;
activeKeys: Set<string>; activeKeys: Set<string>;
isSearching?: boolean; isSearching: boolean;
expandVersion?: number; allParentKeys: string[];
collapseVersion?: number;
} }
const MenuItemExpanded = memo(function MenuItemExpanded({ const MenuItemExpanded = memo(function MenuItemExpanded({
item, item,
activeKeys, activeKeys,
isSearching, isSearching,
expandVersion = 0, allParentKeys,
collapseVersion = 0,
}: MenuItemExpandedProps) { }: MenuItemExpandedProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const Icon = item.icon; const Icon = item.icon;
@@ -59,26 +71,27 @@ const MenuItemExpanded = memo(function MenuItemExpanded({
const isExactActive = isActive && !hasChildren; const isExactActive = isActive && !hasChildren;
const isParentActive = isActive && hasChildren; const isParentActive = isActive && hasChildren;
const [opened, setOpened] = useState(isActive); const isOpened = useSidebarStore((state) => state.openedKeys.has(item.key));
const isOpened = isSearching || opened; const toggleMenu = useSidebarStore((state) => state.toggleMenu);
useEffect(() => { const effectivelyOpened = isSearching || isOpened;
if (expandVersion > 0) setOpened(true);
}, [expandVersion]);
useEffect(() => { const handleChange = useCallback(
if (collapseVersion > 0) setOpened(false); (newOpened: boolean) => {
}, [collapseVersion]); toggleMenu(item.key, newOpened, allParentKeys);
},
[item.key, toggleMenu, allParentKeys],
);
return ( return (
<NavLink <NavLink
component={hasChildren ? 'button' : (Link as any)} component={hasChildren ? 'button' : (Link as any)}
to={hasChildren ? undefined : item.path} to={hasChildren ? undefined : item.path}
label={t(item.label)} label={t(item.label)}
leftSection={<Icon size={18} />} leftSection={<Icon size={18} strokeWidth={1.8} />}
active={isExactActive} active={isExactActive}
opened={isOpened} opened={effectivelyOpened}
onChange={setOpened} onChange={handleChange}
variant="light" variant="light"
styles={{ styles={{
root: { root: {
@@ -89,6 +102,7 @@ const MenuItemExpanded = memo(function MenuItemExpanded({
overflow: 'hidden', overflow: 'hidden',
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
fontWeight: 500,
}, },
}} }}
> >
@@ -99,8 +113,7 @@ const MenuItemExpanded = memo(function MenuItemExpanded({
item={child} item={child}
activeKeys={activeKeys} activeKeys={activeKeys}
isSearching={isSearching} isSearching={isSearching}
expandVersion={expandVersion} allParentKeys={allParentKeys}
collapseVersion={collapseVersion}
/> />
))} ))}
</NavLink> </NavLink>
@@ -136,7 +149,7 @@ const MenuItemFlyout = memo(function MenuItemFlyout({ item, activeKeys, isRoot =
size="lg" size="lg"
aria-label={t(item.label)} aria-label={t(item.label)}
> >
<Icon size={20} /> <Icon size={20} strokeWidth={1.8} />
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
); );
@@ -153,6 +166,7 @@ const MenuItemFlyout = memo(function MenuItemFlyout({ item, activeKeys, isRoot =
textOverflow: 'ellipsis', textOverflow: 'ellipsis',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
maxWidth: 240, maxWidth: 240,
fontWeight: 500,
}, },
}} }}
> >
@@ -170,7 +184,7 @@ const MenuItemFlyout = memo(function MenuItemFlyout({ item, activeKeys, isRoot =
size="lg" size="lg"
aria-label={t(item.label)} aria-label={t(item.label)}
> >
<Icon size={20} /> <Icon size={20} strokeWidth={1.8} />
</ActionIcon> </ActionIcon>
) : ( ) : (
<Menu.Item <Menu.Item
@@ -222,6 +236,17 @@ const MenuItemFlyout = memo(function MenuItemFlyout({ item, activeKeys, isRoot =
// SidebarMenu Component // SidebarMenu Component
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const getAllParentKeys = (items: MenuItemType[]): string[] => {
let keys: string[] = [];
for (const item of items) {
if (item.children && item.children.length > 0) {
keys.push(item.key);
keys = keys.concat(getAllParentKeys(item.children));
}
}
return keys;
};
export const SidebarMenu = memo(function SidebarMenu({ export const SidebarMenu = memo(function SidebarMenu({
items, items,
variantOverride, variantOverride,
@@ -238,23 +263,27 @@ export const SidebarMenu = memo(function SidebarMenu({
const variant = variantOverride ?? contextVariant; const variant = variantOverride ?? contextVariant;
const isMini = variant === 'mini'; const isMini = variant === 'mini';
const [searchQuery, setSearchQuery] = useState(''); const searchQuery = useSidebarStore((state) => state.searchQuery);
const setSearchQuery = useSidebarStore((state) => state.setSearchQuery);
const [debouncedSearchQuery] = useDebouncedValue(searchQuery, 200);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const isSearching = searchQuery.trim().length > 0; const isSearching = debouncedSearchQuery.trim().length > 0;
const [expandVersion, setExpandVersion] = useState(0); const allParentKeys = useMemo(() => getAllParentKeys(items), [items]);
const [collapseVersion, setCollapseVersion] = useState(0); const isInitialized = useSidebarStore((state) => state.isInitialized);
const [isAllExpanded, setIsAllExpanded] = useState(false); const isAllExpanded = useSidebarStore((state) => state.isAllExpanded);
const initializeStorage = useSidebarStore((state) => state.initializeStorage);
const expandAll = useSidebarStore((state) => state.expandAll);
const collapseAll = useSidebarStore((state) => state.collapseAll);
const syncActiveKeys = useSidebarStore((state) => state.syncActiveKeys);
const handleToggleExpandAll = useCallback(() => { const handleToggleExpandAll = useCallback(() => {
if (isAllExpanded) { if (isAllExpanded) {
setCollapseVersion((v) => v + 1); collapseAll();
setIsAllExpanded(false);
} else { } else {
setExpandVersion((v) => v + 1); expandAll(allParentKeys);
setIsAllExpanded(true);
} }
}, [isAllExpanded]); }, [isAllExpanded, collapseAll, expandAll, allParentKeys]);
// Recursive active state calculation // Recursive active state calculation
const activeKeys = useMemo(() => { const activeKeys = useMemo(() => {
@@ -299,11 +328,33 @@ export const SidebarMenu = memo(function SidebarMenu({
return keys; return keys;
}, [pathname, items]); }, [pathname, items]);
// Sync initial state to local storage or auto-expand on navigation
useEffect(() => {
const parentKeysToSync: string[] = [];
const checkActiveParents = (item: MenuItemType) => {
if (item.children && item.children.length > 0) {
if (activeKeys.has(item.key)) {
parentKeysToSync.push(item.key);
}
item.children.forEach(checkActiveParents);
}
};
items.forEach(checkActiveParents);
if (!isInitialized) {
initializeStorage(parentKeysToSync, allParentKeys);
} else {
if (parentKeysToSync.length > 0) {
syncActiveKeys(parentKeysToSync, allParentKeys);
}
}
}, [activeKeys, items, isInitialized, initializeStorage, syncActiveKeys, allParentKeys]);
// Client-side recursive filtering logic // Client-side recursive filtering logic
const filteredItems = useMemo(() => { const filteredItems = useMemo(() => {
if (!isSearching) return items; if (!isSearching) return items;
const query = searchQuery.toLowerCase(); const query = debouncedSearchQuery.toLowerCase();
const filterItem = (item: MenuItemType): MenuItemType | null => { const filterItem = (item: MenuItemType): MenuItemType | null => {
const isMatch = t(item.label).toLowerCase().includes(query); const isMatch = t(item.label).toLowerCase().includes(query);
@@ -327,7 +378,7 @@ export const SidebarMenu = memo(function SidebarMenu({
}; };
return items.map(filterItem).filter((item): item is MenuItemType => item !== null); return items.map(filterItem).filter((item): item is MenuItemType => item !== null);
}, [items, isSearching, searchQuery]); }, [items, isSearching, debouncedSearchQuery, t]);
const handleToggle = useCallback(() => { const handleToggle = useCallback(() => {
setSidebarVariant(isMini ? 'expanded' : 'mini'); setSidebarVariant(isMini ? 'expanded' : 'mini');
@@ -533,10 +584,13 @@ export const SidebarMenu = memo(function SidebarMenu({
onClick={handleToggleExpandAll} onClick={handleToggleExpandAll}
size={30} size={30}
radius="md" radius="md"
fw={400}
aria-label={isAllExpanded ? t('common:collapseAll') : t('common:expandAll')} aria-label={isAllExpanded ? t('common:collapseAll') : t('common:expandAll')}
> >
{isAllExpanded ? <PanelTopOpen size={16} /> : <PanelTopClose size={16} />} {isAllExpanded ? (
<PanelTopOpen size={16} strokeWidth={1.5} />
) : (
<PanelTopClose size={16} strokeWidth={1.5} />
)}
</ActionIcon> </ActionIcon>
</Tooltip> </Tooltip>
</Box> </Box>
@@ -578,16 +632,32 @@ export const SidebarMenu = memo(function SidebarMenu({
)} )}
<Box p="lg" pt="md" style={{ flex: 1, overflowY: 'auto' }}> <Box p="lg" pt="md" style={{ flex: 1, overflowY: 'auto' }}>
{filteredItems.map((item) => ( {filteredItems.length === 0 && isSearching ? (
<MenuItemExpanded <Box
key={item.key} p="md"
item={item} ta="center"
activeKeys={activeKeys} style={{
isSearching={isSearching} border: '1px dashed var(--mantine-color-default-border)',
expandVersion={expandVersion} borderRadius: 'var(--mantine-radius-md)',
collapseVersion={collapseVersion} }}
/> >
))} <Stack gap="xs" align="center">
<Text size="sm" c="dimmed">
{t('common:system_menu.notFound')}
</Text>
</Stack>
</Box>
) : (
filteredItems.map((item) => (
<MenuItemExpanded
key={item.key}
item={item}
activeKeys={activeKeys}
isSearching={isSearching}
allParentKeys={allParentKeys}
/>
))
)}
</Box> </Box>
{withToggle && ( {withToggle && (
+6 -1
View File
@@ -6,6 +6,7 @@ export const AppStorageKey = {
ACCESS_TOKEN: 'access_token', ACCESS_TOKEN: 'access_token',
REFRESH_TOKEN: 'refresh_token', REFRESH_TOKEN: 'refresh_token',
USER_ID: 'u_id', USER_ID: 'u_id',
SIDEBAR_OPEN_MENUS: 'sidebar_open_menus',
} as const; } as const;
export type AppStorageKeyValue = (typeof AppStorageKey)[keyof typeof AppStorageKey]; export type AppStorageKeyValue = (typeof AppStorageKey)[keyof typeof AppStorageKey];
@@ -26,7 +27,11 @@ export const APP_STORAGE_ENCRYPTED_KEYS = new Set<AppStorageKeyValue>([
AppStorageKey.REFRESH_TOKEN, AppStorageKey.REFRESH_TOKEN,
]); ]);
export const APP_STORAGE_PLAIN_KEYS = new Set<AppStorageKeyValue>([AppStorageKey.LANGUAGE, AppStorageKey.THEME]); export const APP_STORAGE_PLAIN_KEYS = new Set<AppStorageKeyValue>([
AppStorageKey.LANGUAGE,
AppStorageKey.THEME,
AppStorageKey.SIDEBAR_OPEN_MENUS,
]);
export const APP_DATABASE_ENCRYPTED_KEYS = new Set<AppDatabaseKeyValue>([]); export const APP_DATABASE_ENCRYPTED_KEYS = new Set<AppDatabaseKeyValue>([]);
@@ -154,7 +154,8 @@
}, },
"system_menu": { "system_menu": {
"history": "History", "history": "History",
"bookmark": "Bookmark" "bookmark": "Bookmark",
"notFound": "Menu not found"
}, },
"fields": { "fields": {
"id": "ID", "id": "ID",
@@ -154,7 +154,8 @@
}, },
"system_menu": { "system_menu": {
"history": "Riwayat", "history": "Riwayat",
"bookmark": "Bookmark" "bookmark": "Bookmark",
"notFound": "Menu tidak ditemukan"
}, },
"fields": { "fields": {
"id": "ID", "id": "ID",
@@ -10,6 +10,10 @@ import {
MenuItemDef, MenuItemDef,
DefaultMenuItem, DefaultMenuItem,
StatusBar, StatusBar,
ColumnMovedEvent,
ColumnResizedEvent,
ColumnVisibleEvent,
SortChangedEvent,
} from 'ag-grid-community'; } from 'ag-grid-community';
import { Box, Group, TextInput, Indicator, CloseButton } from '@mantine/core'; import { Box, Group, TextInput, Indicator, CloseButton } from '@mantine/core';
import { useDisclosure } from '@mantine/hooks'; import { useDisclosure } from '@mantine/hooks';
@@ -181,6 +185,12 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
searchKey = 'search', searchKey = 'search',
filterConfig, filterConfig,
// Grid config props
onColumnMoved,
onColumnResized,
onColumnVisible,
onSortChanged,
// Bulk action props // Bulk action props
customBulkActions, customBulkActions,
batchSize, batchSize,
@@ -200,8 +210,16 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
const { t } = useEnterpriseModuleTranslationContext(); const { t } = useEnterpriseModuleTranslationContext();
const { dataServices } = useEnterpriseModuleDataServiceContext<E>(); const { dataServices } = useEnterpriseModuleDataServiceContext<E>();
const { selectedRows, setSelectedRows, metaData, setMetaData, filterData, setFilterData } = const {
useEnterpriseModuleSelectionContext<E>(); selectedRows,
setSelectedRows,
metaData,
setMetaData,
filterData,
setFilterData,
tableConfig,
setTableConfig,
} = useEnterpriseModuleSelectionContext<E>();
const navigation = useEnterpriseModuleNavigationContext(); const navigation = useEnterpriseModuleNavigationContext();
const { config } = useEnterpriseModuleConfigContext(); const { config } = useEnterpriseModuleConfigContext();
@@ -791,6 +809,11 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
setSelectedRows([]); setSelectedRows([]);
if (params.api) { 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 // Attach the server-side datasource to the grid API
params.api.setGridOption('serverSideDatasource', datasource); 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 // Triggered whenever the row selection in the grid changes
@@ -817,6 +840,50 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
[setSelectedRows], [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 // Configures the context menu items available when right-clicking a cell
const getContextMenuItems = useCallback((): const getContextMenuItems = useCallback(():
| (DefaultMenuItem | MenuItemDef)[] | (DefaultMenuItem | MenuItemDef)[]
@@ -923,6 +990,10 @@ export function EnterpriseDataTable<E extends BaseEntity>(props: EnterpriseDataT
enableCellTextSelection={true} enableCellTextSelection={true}
onSelectionChanged={handleSelectionChanged} onSelectionChanged={handleSelectionChanged}
onGridReady={onGridReady} onGridReady={onGridReady}
onColumnMoved={handleColumnMoved}
onColumnResized={handleColumnResized}
onColumnVisible={handleColumnVisible}
onSortChanged={handleSortChanged}
getContextMenuItems={getContextMenuItems} getContextMenuItems={getContextMenuItems}
statusBar={statusBar} statusBar={statusBar}
domLayout={domLayout} domLayout={domLayout}
@@ -4,6 +4,7 @@ import type { ZodType } from 'zod';
import type { BaseEntity, BaseRemoteDataServices, StandardPaginationMeta } from '@repo/core-api/data-services'; import type { BaseEntity, BaseRemoteDataServices, StandardPaginationMeta } from '@repo/core-api/data-services';
import type { ModulePageHeaderProps } from '../components/module-page-header'; import type { ModulePageHeaderProps } from '../components/module-page-header';
import { PageActionsProps } from '../../../components'; import { PageActionsProps } from '../../../components';
import { ColumnState } from 'ag-grid-community';
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// Module Constants & Base Types // Module Constants & Base Types
@@ -140,6 +141,8 @@ export interface SelectionSlice<
setMetaData: (data: TMeta | null) => void; setMetaData: (data: TMeta | null) => void;
filterData: TFilter | null; filterData: TFilter | null;
setFilterData: (data: TFilter | null) => void; 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; setFilterData: (data: TFilter | null) => void;
selectedRows: E[]; selectedRows: E[];
setSelectedRows: (rows: E[]) => void; setSelectedRows: (rows: E[]) => void;
tableConfig: ColumnState[] | null;
setTableConfig: (config: ColumnState[] | null) => void;
privileges: string[]; privileges: string[];
setPrivileges: (privileges: string[]) => void; setPrivileges: (privileges: string[]) => void;
} }
@@ -82,6 +82,8 @@ export function EnterpriseModuleProvider<
const setMetaData = store((state: S) => state.setMetaData); const setMetaData = store((state: S) => state.setMetaData);
const filterData = store((state: S) => state.filterData); const filterData = store((state: S) => state.filterData);
const setFilterData = store((state: S) => state.setFilterData); const setFilterData = store((state: S) => state.setFilterData);
const tableConfig = store((state: S) => state.tableConfig);
const setTableConfig = store((state: S) => state.setTableConfig);
const selectionSlice = useMemo( const selectionSlice = useMemo(
() => ({ () => ({
@@ -91,8 +93,10 @@ export function EnterpriseModuleProvider<
setMetaData, setMetaData,
filterData, filterData,
setFilterData, setFilterData,
tableConfig,
setTableConfig,
}), }),
[selectedRows, setSelectedRows, metaData, setMetaData, filterData, setFilterData], [selectedRows, setSelectedRows, metaData, setMetaData, filterData, setFilterData, tableConfig, setTableConfig],
); );
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------