Created
September 25, 2023 18:10
-
-
Save minaairsupport/98fd235a189ea11b2223a6d540bbbfc8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useDispatch, useSelector } from 'react-redux'; | |
import { operations } from 'ducks/shared/Multiselect'; | |
import { toggleArrayValue } from 'utils/helpers'; | |
const useDynamicSelectAll = ({ currentPageIds, selectors, context }) => { | |
const dispatch = useDispatch(); | |
const selectedIds = useSelector(selectors.getSelectedIds); | |
const selectionState = useSelector(selectors.getSelectionState); | |
const isAllPagesSelected = useSelector(selectors.getIsAllSelected); | |
const isSelected = id => isAllPagesSelected || selectedIds.includes(id); | |
const countSelectedOnPage = currentPageIds.filter(isSelected).length; | |
const isHalfChecked = countSelectedOnPage > 0 && countSelectedOnPage < currentPageIds.length; | |
const isAllSelected = isAllPagesSelected || countSelectedOnPage === currentPageIds.length; | |
const deselectAll = () => dispatch(operations.selectIds(context)([])); | |
const forceClearSelectionState = () => dispatch(operations.forceClearSelectionState(context)()); | |
const toggleAll = () => { | |
if (isAllPagesSelected) { | |
// Clear entire selection | |
dispatch(operations.selectIds(context)([])); | |
} else if (isHalfChecked || isAllSelected) { | |
// At least one id on this page is selected, deselect all on the page | |
const newIds = selectedIds.filter(id => !currentPageIds.includes(id)); | |
dispatch(operations.selectIds(context)(newIds)); | |
} else { | |
// Nothing on the page is selected, add whole page to selection | |
dispatch( | |
operations.selectIds(context)([...selectedIds, ...currentPageIds]), | |
); | |
} | |
}; | |
const toggleSelection = (id) => { | |
const currentIds = isAllPagesSelected ? currentPageIds : [...selectedIds]; | |
const newIds = toggleArrayValue(currentIds, id); | |
dispatch(operations.selectIds(context)(newIds)); | |
}; | |
const toggleSelectionInventory = (selectAllPage, toggledNodes) => { | |
if (isAllPagesSelected && selectAllPage && toggledNodes.length === 0) return; | |
let ids = []; | |
if (selectAllPage && toggledNodes.length === 0) { | |
const newIds = currentPageIds.filter(id => !selectedIds.includes(id)); | |
ids = [...selectedIds, ...newIds]; | |
} else if (!selectAllPage && toggledNodes.length === 0) { | |
const newIds = selectedIds.filter(id => !currentPageIds.includes(id)); | |
ids = newIds; | |
} else if (selectAllPage && toggledNodes.length > 0) { | |
let newIds = []; | |
if (isAllPagesSelected) { | |
newIds = currentPageIds.filter(id => !toggledNodes.includes(id)); | |
} else { | |
newIds = selectedIds.filter(id => !toggledNodes.includes(id)); | |
} | |
ids = newIds; | |
} else if (!selectAllPage && toggledNodes.length > 0) { | |
const newIds = toggledNodes.filter(id => !selectedIds.includes(id)); | |
ids = [...selectedIds, ...newIds]; | |
} | |
dispatch(operations.selectIds(context)(ids)); | |
}; | |
const saveSelectionState = (pageNumber, currentSelectionState) => { | |
dispatch( | |
operations.setSelectionState(context)(pageNumber, currentSelectionState), | |
); | |
}; | |
return { | |
toggleAll, | |
toggleSelection, | |
toggleSelectionInventory, | |
isSelected, | |
isHalfChecked, | |
isAllSelected, | |
deselectAll, | |
saveSelectionState, | |
selectionState, | |
isAllPagesSelected, | |
forceClearSelectionState, | |
}; | |
}; | |
export default useDynamicSelectAll; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { RefObject, useCallback, useEffect, useMemo, useState } from 'react'; | |
import { useSelector, useDispatch } from 'react-redux'; | |
import { | |
GetContextMenuItemsParams, | |
IServerSideGetRowsRequest, | |
RowClickedEvent, | |
SortModelItem, | |
StatusPanelDef, | |
SideBarDef, | |
ToolPanelVisibleChangedEvent, | |
ColumnMovedEvent, | |
ColumnState, | |
SelectionChangedEvent, | |
PaginationChangedEvent, | |
IServerSideSelectionState, | |
} from 'ag-grid-community'; | |
import { AgGridReact } from 'ag-grid-react'; | |
import { debounce } from 'lodash'; | |
import { useLocation } from 'react-router-dom'; | |
import { selectors as globalSelectors } from 'ducks/global'; | |
import * as duckSelectors from 'ducks/selectors'; | |
import { operations as searchOperations } from 'ducks/shared/Search'; | |
import { operations as modalOperations } from 'ducks/Modal'; | |
import { operations as toolOperations } from 'ducks/Tool'; | |
import { EDIT_TOOL } from 'constants/modalTypes'; | |
import usePermissions from 'shared/hooks/usePermissions'; | |
import useDynamicSelectAll from 'shared/hooks/multiselect/useDynamicSelectAll'; | |
import { loadToolsV2 } from 'ducks/Tool/operations'; | |
import { loadLocationTools } from 'ducks/Location/operations'; | |
import { | |
customColumnSideBar, | |
getCustomeContextMenuItems, | |
getDefaultStatus, | |
} from 'components/Table/utils'; | |
import { defaultExportBody, defaultSort } from 'constants/tool'; | |
import EmptySearchResult from 'shared/EmptySearchResult'; | |
import { maniplateInventoryData } from 'utils/helpers'; | |
import { trackEvent } from '@sbd-ctg/user-behavior-tracking'; | |
const isToolPanelShowingInventoryKey = 'isToolPanelShowingInventoryKey'; | |
interface UseNewToolsListProps { | |
selectors: any; | |
context: any; | |
assetsLocation?: any; | |
} | |
const LOCAL_COLUMN_STATE_KEY = 'LOCAL_COLUMN_STATE_KEY'; | |
export default function useNewToolsList({ | |
selectors, | |
context, | |
assetsLocation, | |
}: UseNewToolsListProps) { | |
const [gridRef, setGridRef] = useState<RefObject<AgGridReact>>(); | |
const location = useLocation(); | |
const keyField = 'thingId'; | |
const tools: any[] = useSelector(selectors.getCurrentPageOfTools); | |
const currentPage: number = useSelector(selectors.getPageNumber); | |
const querySearch = useSelector(selectors.getToolQuery || selectors.getQuery); | |
const setQuery = searchOperations.setSearchQuery(context); | |
const pageSize: number = useSelector(selectors.getPageSize); | |
const inventorySwitch: boolean = useSelector( | |
(state: any) => state.tool.search.inventorySwitch | |
); | |
const IsInventory = context === 'TOOL'; | |
const clearFilters = useSelector( | |
(state: any) => state.tool.search.clearFilters | |
); | |
const deselectSwitch = useSelector( | |
(state: any) => state.tool.search.deselectSwitch | |
); | |
let columnState = useSelector(selectors.getColumnState); | |
const isToolPanelShowing = localStorage.getItem( | |
`${context}_${isToolPanelShowingInventoryKey}` | |
); | |
const companyId = useSelector((state: any) => | |
globalSelectors.getSelectedCompanyId(state) | |
); | |
const columns = useSelector(duckSelectors.getColumnsV2); | |
const setSearchPageSizeWithContext = | |
searchOperations.setSearchPageSize(context); | |
const setColumnStateWithContext = searchOperations.setColumnState(context)( | |
`${context}_${LOCAL_COLUMN_STATE_KEY}` | |
); | |
const dispatch = useDispatch(); | |
const onToolClick = (id: string) => | |
dispatch(modalOperations.showModal(EDIT_TOOL, { id })); | |
const setSearchPageSize = (size: number) => | |
dispatch(setSearchPageSizeWithContext(size)); | |
const setColumnState = (newState: any) => | |
dispatch(setColumnStateWithContext(newState)); | |
const setSearchPage = (page: any) => | |
dispatch(searchOperations.setSearchPage(context)(page)); | |
const exportAll = () => | |
dispatch(toolOperations.exportTools(defaultExportBody)); | |
if ( | |
!columnState || | |
(Array.isArray(columnState) && columnState.length === 0) | |
) { | |
columnState = JSON.parse( | |
localStorage.getItem(`${context}_${LOCAL_COLUMN_STATE_KEY}`) || '[]' | |
); | |
} | |
const { canManageAssets } = usePermissions(); | |
const onRowClick = (id: string) => { | |
trackEvent('edit_tool', { category: 'Inventory' }); | |
onToolClick(id); | |
}; | |
const onPageSizeChange = (size: number) => { | |
trackEvent('page_size_change', { category: 'Inventory' }); | |
setSearchPageSize(size); | |
}; | |
function getFilterConditions(filterModel: any) { | |
const filterKeys = Object.keys(filterModel); | |
return filterKeys.includes('-') | |
? [{ field: 'search', contains: filterModel['-'].filter }] | |
: filterKeys.map((filter: string) => ({ | |
field: filter, | |
contains: filterModel[filter].filter, | |
})); | |
} | |
function getOrderColumns(sortModel: SortModelItem[]) { | |
return sortModel.length > 0 | |
? sortModel.map(({ sort, colId }) => ({ | |
column: colId, | |
direction: sort, | |
})) | |
: [defaultSort]; | |
} | |
const sideBar = useMemo<SideBarDef>(() => customColumnSideBar(), []); | |
useEffect(() => { | |
if (isToolPanelShowing === 'true') { | |
gridRef?.current?.api.openToolPanel('customColumns'); | |
} else { | |
gridRef?.current?.api.closeToolPanel(); | |
} | |
}, [gridRef, isToolPanelShowing]); | |
async function loadToolsWithRetry( | |
searchBody: any, | |
retries = 3, | |
retryDelay = 1000 | |
): Promise<any> { | |
try { | |
let response; | |
const categoryImageListCall = await dispatch( | |
toolOperations.loadCategoryImages() as any | |
); | |
const categoryImageList = categoryImageListCall.payload.data.data; | |
const { locationId } = assetsLocation || {}; | |
if (locationId && context === 'LOCATION') { | |
response = await dispatch( | |
loadLocationTools(searchBody, locationId) as any | |
); | |
return { | |
rowData: maniplateInventoryData( | |
response.payload.data.data.searchResults.things, | |
categoryImageList | |
), | |
rowCount: response.payload.data.data.searchResults.searchTotalTools, | |
}; | |
} | |
response = await dispatch(loadToolsV2(context)(searchBody) as any); | |
return { | |
rowData: maniplateInventoryData( | |
response.payload.data.data.things, | |
categoryImageList | |
), | |
rowCount: response.payload.data.data.searchTotalTools, | |
}; | |
} catch (error) { | |
if (retries > 0) { | |
await new Promise((resolve) => setTimeout(resolve, retryDelay)); | |
return loadToolsWithRetry(searchBody, retries - 1, retryDelay * 2); | |
} | |
throw new Error( | |
`Failed to load data after ${retries} retries with error ${error}` | |
); | |
} | |
} | |
const loadData = ( | |
params: IServerSideGetRowsRequest, | |
localPageSize: number = pageSize | |
) => { | |
const { endRow, filterModel, sortModel } = params; | |
const orderColumns = getOrderColumns(sortModel); | |
const filterConditions = getFilterConditions(filterModel); | |
const pageNumber = Math.round((endRow || pageSize) / localPageSize); | |
const searchBody = { | |
pageSize: localPageSize, | |
orderColumns, | |
filterConditions: filterConditions || [], | |
pageNumber, // used for redux | |
}; | |
if (orderColumns.length && orderColumns[0].column !== 'thing.createdAt') { | |
trackEvent('sort_by_column', { | |
category: 'Inventory', | |
order_by: orderColumns[0].column, | |
direction: orderColumns[0].direction, | |
}); | |
} | |
if (filterConditions.length) { | |
const [first] = filterConditions; | |
trackEvent('filter_by_column', { | |
category: 'Inventory', | |
field: first.field, | |
search_value: first.contains, | |
}); | |
} | |
setSearchPage(pageNumber); | |
dispatch(searchOperations.setSearchFiltersOnly(context)(filterConditions)); | |
return loadToolsWithRetry(searchBody); | |
}; | |
const { | |
toggleSelectionInventory, | |
deselectAll, | |
saveSelectionState, | |
selectionState, | |
isAllPagesSelected, | |
forceClearSelectionState, | |
} = useDynamicSelectAll({ | |
currentPageIds: tools ? tools.map((t) => t.thingId) : [], | |
selectors, | |
context, | |
}); | |
useEffect(() => { | |
if (isAllPagesSelected) { | |
gridRef?.current?.api.selectAll(); | |
} | |
}, [isAllPagesSelected]); | |
const onSelectionChanged = useCallback( | |
(event: SelectionChangedEvent) => { | |
const serverSelectionState: any = event.api.getServerSideSelectionState(); | |
if ( | |
(isAllPagesSelected && | |
serverSelectionState?.selectAll && | |
serverSelectionState?.toggledNodes.length > 0) || | |
(isAllPagesSelected && | |
!serverSelectionState?.selectAll && | |
serverSelectionState?.toggledNodes.length === 0) | |
) { | |
forceClearSelectionState(); | |
} | |
toggleSelectionInventory( | |
serverSelectionState?.selectAll, | |
serverSelectionState?.toggledNodes | |
); | |
saveSelectionState(currentPage, serverSelectionState); | |
}, | |
[gridRef, toggleSelectionInventory] | |
); | |
useEffect(() => { | |
if (gridRef) { | |
const filterModel: any = {}; | |
if (location.state && (location.state as any).categoryName) { | |
filterModel['category.categoryName'] = { | |
filter: (location.state as any).categoryName, | |
type: 'contains', | |
}; | |
} | |
setTimeout(() => { | |
if (filterModel) { | |
gridRef.current?.api?.setFilterModel(filterModel); | |
} | |
}, 0); | |
} | |
// component cleanup | |
return () => { | |
window.history.replaceState(null, '', window.location.pathname); | |
dispatch(setQuery('')); | |
}; | |
}, [gridRef]); | |
const getContextMenuItems = useCallback( | |
(params: GetContextMenuItemsParams) => | |
getCustomeContextMenuItems(params, exportAll), | |
[] | |
); | |
const onPageSizeChanged = useCallback((value: any) => { | |
onPageSizeChange(Number(value)); | |
}, []); | |
const statusBar = useMemo<{ | |
statusPanels: StatusPanelDef[]; | |
}>(() => getDefaultStatus(pageSize, onPageSizeChanged), [pageSize]); | |
useEffect(() => { | |
const debouncedSearch = debounce(() => { | |
gridRef?.current?.api?.setFilterModel({ | |
'-': { filter: querySearch }, | |
}); | |
}, 1000); | |
debouncedSearch(); | |
return () => debouncedSearch.cancel(); | |
}, [querySearch]); | |
useEffect(() => { | |
gridRef?.current?.api?.refreshServerSide({ purge: true }); | |
gridRef?.current?.api?.deselectAll(); | |
}, [inventorySwitch, companyId]); | |
useEffect(() => { | |
gridRef?.current?.api.deselectAll(); | |
deselectAll(); | |
}, [deselectSwitch]); | |
useEffect(() => { | |
gridRef?.current?.api.setFilterModel(null); | |
}, [clearFilters]); | |
const onToolPanelVisibleChanged = useCallback( | |
(event: ToolPanelVisibleChangedEvent) => { | |
const isToolPanelShowingValue = event.api.isToolPanelShowing(); | |
const actionColumnsSelector = isToolPanelShowingValue ? 'show' : 'hide'; | |
trackEvent(`${actionColumnsSelector}_columns_selector`, { | |
category: 'Inventory', | |
}); | |
localStorage.setItem( | |
`${context}_${isToolPanelShowingInventoryKey}`, | |
isToolPanelShowingValue.toString() | |
); | |
}, | |
[] | |
); | |
const onColumnMoved = useCallback((event: ColumnMovedEvent) => { | |
const currentColumnOrder = event.columnApi | |
.getColumnState() | |
.map((col: ColumnState) => col.colId); | |
const selectionColumnIndex = currentColumnOrder.indexOf('selection'); | |
if (selectionColumnIndex !== 0) { | |
event.columnApi.moveColumnByIndex(selectionColumnIndex, 0); | |
} | |
}, []); | |
const defaultSelectionState: IServerSideSelectionState = { | |
selectAll: false, | |
toggledNodes: [], | |
}; | |
const onPaginationChanged = (params: PaginationChangedEvent) => { | |
const page = params.api.paginationGetCurrentPage() + 1; | |
setSearchPage(page); | |
if (!isAllPagesSelected) { | |
const selectionStateForPage = | |
(selectionState as any)?.[page] || defaultSelectionState; | |
params.api.setServerSideSelectionState(selectionStateForPage); | |
} | |
}; | |
const tableProps = { | |
loadData, | |
setColumnState, | |
columnState, | |
keyField, | |
pageSize, | |
onPageSizeChange, | |
...(canManageAssets && { rowSelection: 'multiple' }), | |
exportAll, | |
setGridRef, | |
className: IsInventory ? 'sbd-ag-theme' : '', | |
/* Override table props */ | |
onRowClicked: (event: RowClickedEvent) => onRowClick(event.data[keyField]), | |
columnDefs: columns, | |
onSelectionChanged, | |
getContextMenuItems, | |
statusBar, | |
suppressPaginationPanel: true, | |
noRowsOverlayComponent: EmptySearchResult, | |
sideBar, | |
suppressRowClickSelection: true, | |
onToolPanelVisibleChanged, | |
menuTabs: ['filterMenuTab', 'columnsMenuTab'], | |
rowHeight: 70, | |
onColumnMoved, | |
onPaginationChanged, | |
}; | |
return tableProps; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment