-
Notifications
You must be signed in to change notification settings - Fork 8.5k
[UII] Save agent list table state using session storage #228875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b7cc6e3
649df42
ae1f81e
026fc69
f0e5062
d36b7f8
f6252fb
fad10b0
279aee8
ccb2e48
7f354b4
4516458
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,9 +12,8 @@ import { useQuery } from '@kbn/react-query'; | |
|
|
||
| import { agentStatusesToSummary } from '../../../../../../../common/services'; | ||
|
|
||
| import type { Agent, AgentPolicy } from '../../../../types'; | ||
| import type { AgentPolicy } from '../../../../types'; | ||
| import { | ||
| usePagination, | ||
| useGetAgentPolicies, | ||
| sendGetAgentStatus, | ||
| useUrlParams, | ||
|
|
@@ -32,6 +31,8 @@ import { LEGACY_AGENT_POLICY_SAVED_OBJECT_TYPE, SO_SEARCH_LIMIT } from '../../.. | |
|
|
||
| import { getKuery } from '../utils/get_kuery'; | ||
|
|
||
| import { useSessionAgentListState, defaultAgentListState } from './use_session_agent_list_state'; | ||
|
|
||
| const REFRESH_INTERVAL_MS = 30000; | ||
| const MAX_AGENT_ACTIONS = 100; | ||
| /** Allow to fetch full agent policy using a cache */ | ||
|
|
@@ -102,29 +103,91 @@ export function useFetchAgentsData() { | |
| const { showAgentless } = useAgentlessResources(); | ||
| const defaultKuery: string = (urlParams.kuery as string) || ''; | ||
| const urlHasInactive = (urlParams.showInactive as string) === 'true'; | ||
| const isUsingParams = defaultKuery || urlHasInactive; | ||
|
|
||
| // Extract state from session storage hook | ||
| const sessionState = useSessionAgentListState(); | ||
| const { | ||
| search, | ||
| selectedAgentPolicies, | ||
| selectedStatus, | ||
| selectedTags, | ||
| showUpgradeable, | ||
| sort, | ||
| page, | ||
| updateTableState, | ||
| } = sessionState; | ||
|
|
||
| // If URL params are used, reset the table state to defaults with the param options | ||
| useEffect(() => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: There maybe opportunity here to separate out into its own hook the search session state, with url syncing.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed, I started a refactor to pull this out but ran into issues. didn't have confidence I could finish it & test thoroughly before leave 😅 so merged as-is |
||
| if (isUsingParams) { | ||
| updateTableState({ | ||
| ...defaultAgentListState, | ||
| search: defaultKuery, | ||
| selectedStatus: [...new Set([...selectedStatus, ...(urlHasInactive ? ['inactive'] : [])])], | ||
| }); | ||
| } | ||
| // Empty array so that this only runs once on mount | ||
| // eslint-disable-next-line react-hooks/exhaustive-deps | ||
| }, []); | ||
|
|
||
| // Sync URL kuery param with session storage search to maintain shareable state | ||
| useEffect(() => { | ||
| const currentUrlKuery = (urlParams.kuery as string) || ''; | ||
| // If search is empty and URL has kuery, or search differs from URL, update URL | ||
| if ((search === '' && currentUrlKuery !== '') || (search && search !== currentUrlKuery)) { | ||
| const { kuery: _, ...restParams } = urlParams; | ||
| const newParams = search === '' ? restParams : { ...restParams, kuery: search }; | ||
| history.replace({ | ||
| search: toUrlParams(newParams), | ||
| }); | ||
| } | ||
| }, [search, urlParams, history, toUrlParams]); | ||
|
|
||
| // Flag to indicate if filters differ from default state | ||
| const isUsingFilter = useMemo(() => { | ||
| return ( | ||
| search !== defaultAgentListState.search || | ||
| !isEqual(selectedAgentPolicies, defaultAgentListState.selectedAgentPolicies) || | ||
| !isEqual(selectedStatus, defaultAgentListState.selectedStatus) || | ||
| !isEqual(selectedTags, defaultAgentListState.selectedTags) || | ||
| showUpgradeable !== defaultAgentListState.showUpgradeable | ||
| ); | ||
| }, [search, selectedAgentPolicies, selectedStatus, selectedTags, showUpgradeable]); | ||
|
|
||
| // Create individual setters using updateTableState | ||
| const setSearchState = useCallback( | ||
| (value: string) => updateTableState({ search: value }), | ||
| [updateTableState] | ||
| ); | ||
|
|
||
| const setSelectedAgentPolicies = useCallback( | ||
| (value: string[]) => updateTableState({ selectedAgentPolicies: value }), | ||
| [updateTableState] | ||
| ); | ||
|
|
||
| const setSelectedStatus = useCallback( | ||
| (value: string[]) => updateTableState({ selectedStatus: value }), | ||
| [updateTableState] | ||
| ); | ||
|
|
||
| // Table and search states | ||
| const [showUpgradeable, setShowUpgradeable] = useState<boolean>(false); | ||
| const [draftKuery, setDraftKuery] = useState<string>(defaultKuery); | ||
| const [search, setSearchState] = useState<string>(defaultKuery); | ||
| const { pagination, pageSizeOptions, setPagination } = usePagination(); | ||
| const [sortField, setSortField] = useState<keyof Agent>('enrolled_at'); | ||
| const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc'); | ||
|
|
||
| // Policies state for filtering | ||
| const [selectedAgentPolicies, setSelectedAgentPolicies] = useState<string[]>([]); | ||
|
|
||
| // Status for filtering | ||
| const [selectedStatus, setSelectedStatus] = useState<string[]>([ | ||
| 'healthy', | ||
| 'unhealthy', | ||
| 'orphaned', | ||
| 'updating', | ||
| 'offline', | ||
| ...(urlHasInactive ? ['inactive'] : []), | ||
| ]); | ||
|
|
||
| const [selectedTags, setSelectedTags] = useState<string[]>([]); | ||
| const setSelectedTags = useCallback( | ||
| (value: string[]) => updateTableState({ selectedTags: value }), | ||
| [updateTableState] | ||
| ); | ||
|
|
||
| const setShowUpgradeable = useCallback( | ||
| (value: boolean) => updateTableState({ showUpgradeable: value }), | ||
| [updateTableState] | ||
| ); | ||
|
|
||
| const pageSizeOptions = [5, 20, 50]; | ||
|
|
||
| // Sync draftKuery with session storage search | ||
| const [draftKuery, setDraftKuery] = useState<string>(search); | ||
| useEffect(() => { | ||
| setDraftKuery(search); | ||
| }, [search]); | ||
|
|
||
| const showInactive = useMemo(() => { | ||
| return selectedStatus.some((status) => status === 'inactive') || selectedStatus.length === 0; | ||
|
|
@@ -142,13 +205,14 @@ export function useFetchAgentsData() { | |
| } | ||
|
|
||
| if (urlParams.kuery !== newVal) { | ||
| const { kuery: _, ...restParams } = urlParams; | ||
| const newParams = newVal === '' ? restParams : { ...restParams, kuery: newVal }; | ||
| history.replace({ | ||
| // @ts-expect-error - kuery can't be undefined | ||
| search: toUrlParams({ ...urlParams, kuery: newVal === '' ? undefined : newVal }), | ||
| search: toUrlParams(newParams), | ||
| }); | ||
| } | ||
| }, | ||
| [urlParams, history, toUrlParams] | ||
| [setSearchState, urlParams, history, toUrlParams] | ||
| ); | ||
|
|
||
| // filters kuery | ||
|
|
@@ -192,7 +256,12 @@ export function useFetchAgentsData() { | |
| } | ||
| }, [latestAgentActionErrors, actionErrors]); | ||
|
|
||
| const queryKeyPagination = JSON.stringify({ pagination, sortField, sortOrder }); | ||
| // Use session storage state for pagination and sort | ||
| const queryKeyPagination = JSON.stringify({ | ||
| pagination: { currentPage: page.index + 1, pageSize: page.size }, | ||
| sortField: sort.field, | ||
| sortOrder: sort.direction, | ||
| }); | ||
| const queryKeyFilters = JSON.stringify({ | ||
| kuery, | ||
| showAgentless, | ||
|
|
@@ -210,7 +279,7 @@ export function useFetchAgentsData() { | |
| refetch, | ||
| } = useQuery({ | ||
| queryKey: ['get-agents-list', queryKeyFilters, queryKeyPagination], | ||
| keepPreviousData: true, // Keep previous data to avoid flashing when going through pages coulse | ||
| keepPreviousData: true, // Keep previous data to avoid flashing when going through pages | ||
| queryFn: async () => { | ||
| try { | ||
| const [ | ||
|
|
@@ -220,11 +289,11 @@ export function useFetchAgentsData() { | |
| agentTagsResponse, | ||
| ] = await Promise.all([ | ||
| sendGetAgentsForRq({ | ||
| page: pagination.currentPage, | ||
| perPage: pagination.pageSize, | ||
| page: page.index + 1, | ||
| perPage: page.size, | ||
| kuery: kuery && kuery !== '' ? kuery : undefined, | ||
| sortField: getSortFieldForAPI(sortField), | ||
| sortOrder, | ||
| sortField: getSortFieldForAPI(sort.field), | ||
| sortOrder: sort.direction, | ||
| showAgentless, | ||
| showInactive, | ||
| showUpgradeable, | ||
|
|
@@ -377,26 +446,25 @@ export function useFetchAgentsData() { | |
| setSearch, | ||
| selectedAgentPolicies, | ||
| setSelectedAgentPolicies, | ||
| sortField, | ||
| setSortField, | ||
| sortOrder, | ||
| setSortOrder, | ||
| sort, | ||
| selectedStatus, | ||
| setSelectedStatus, | ||
| selectedTags, | ||
| setSelectedTags, | ||
| allAgentPolicies, | ||
| agentPoliciesRequest, | ||
| agentPoliciesIndexedById, | ||
| pagination, | ||
| page, | ||
| pageSizeOptions, | ||
| setPagination, | ||
| kuery, | ||
| draftKuery, | ||
| setDraftKuery, | ||
| fetchData, | ||
| queryHasChanged, | ||
| latestAgentActionErrors, | ||
| setLatestAgentActionErrors, | ||
| isUsingFilter, | ||
| clearFilters: sessionState.clearFilters, | ||
| onTableChange: sessionState.onTableChange, | ||
| }; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
upgradable filter button previously didn't have a clear "active" state