diff --git a/frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx b/frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx index 2af30b9943..fa21c83729 100644 --- a/frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx +++ b/frontend/src/components/HostMetricsDetail/HostMetricsDetails.tsx @@ -37,6 +37,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -67,6 +68,7 @@ function HostMetricsDetails({ AppState, GlobalReducer >((state) => state.globalTime); + const [searchParams, setSearchParams] = useSearchParams(); const startMs = useMemo(() => Math.floor(Number(minTime) / 1000000000), [ minTime, @@ -86,7 +88,9 @@ function HostMetricsDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [selectedView, setSelectedView] = useState( + (searchParams.get('view') as VIEWS) || VIEWS.METRICS, + ); const isDarkMode = useIsDarkMode(); const initialFilters = useMemo( @@ -149,6 +153,9 @@ function HostMetricsDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + if (host?.hostName) { + setSearchParams({ hostName: host?.hostName, view: e.target.value }); + } logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.HostEntity, view: e.target.value, @@ -313,6 +320,7 @@ function HostMetricsDetails({ const handleClose = (): void => { setSelectedInterval(selectedTime as Time); + setSearchParams({}); if (selectedTime !== 'custom') { const { maxTime, minTime } = GetMinMax(selectedTime); diff --git a/frontend/src/container/InfraMonitoringHosts/HostsList.tsx b/frontend/src/container/InfraMonitoringHosts/HostsList.tsx index c06f63a4b0..f4dac3f9eb 100644 --- a/frontend/src/container/InfraMonitoringHosts/HostsList.tsx +++ b/frontend/src/container/InfraMonitoringHosts/HostsList.tsx @@ -8,6 +8,11 @@ import HostMetricDetail from 'components/HostMetricsDetail'; import QuickFilters from 'components/QuickFilters/QuickFilters'; import { QuickFiltersSource } from 'components/QuickFilters/types'; import { InfraMonitoringEvents } from 'constants/events'; +import { + getFiltersFromParams, + getOrderByFromParams, +} from 'container/InfraMonitoringK8s/commonUtils'; +import { INFRA_MONITORING_K8S_PARAMS_KEYS } from 'container/InfraMonitoringK8s/constants'; import { usePageSize } from 'container/InfraMonitoringK8s/utils'; import { useGetHostList } from 'hooks/infraMonitoring/useGetHostList'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; @@ -15,6 +20,7 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { Filter } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery, Query } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; @@ -27,20 +33,51 @@ function HostsList(): JSX.Element { const { maxTime, minTime } = useSelector( (state) => state.globalTime, ); + const [searchParams, setSearchParams] = useSearchParams(); const [currentPage, setCurrentPage] = useState(1); - const [filters, setFilters] = useState({ - items: [], - op: 'and', + const [filters, setFilters] = useState(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS, + ); + if (!filters) { + return { + items: [], + op: 'and', + }; + } + return filters; }); const [showFilters, setShowFilters] = useState(true); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>(null); + } | null>(() => getOrderByFromParams(searchParams)); - const [selectedHostName, setSelectedHostName] = useState(null); + const handleOrderByChange = ( + orderBy: { + columnName: string; + order: 'asc' | 'desc'; + } | null, + ): void => { + setOrderBy(orderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(orderBy), + }); + }; + + const [selectedHostName, setSelectedHostName] = useState(() => { + const hostName = searchParams.get('hostName'); + return hostName || null; + }); + + const handleHostClick = (hostName: string): void => { + setSelectedHostName(hostName); + setSearchParams({ ...searchParams, hostName }); + }; const { pageSize, setPageSize } = usePageSize('hosts'); @@ -82,6 +119,10 @@ function HostsList(): JSX.Element { const isNewFilterAdded = value.items.length !== filters.items.length; setFilters(value); handleChangeQueryData('filters', value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS]: JSON.stringify(value), + }); if (isNewFilterAdded) { setCurrentPage(1); @@ -161,7 +202,10 @@ function HostsList(): JSX.Element { )} - + diff --git a/frontend/src/container/InfraMonitoringHosts/HostsListControls.tsx b/frontend/src/container/InfraMonitoringHosts/HostsListControls.tsx index 5c7cad87ff..ca0e85e752 100644 --- a/frontend/src/container/InfraMonitoringHosts/HostsListControls.tsx +++ b/frontend/src/container/InfraMonitoringHosts/HostsListControls.tsx @@ -10,8 +10,10 @@ import { DataSource } from 'types/common/queryBuilder'; function HostsListControls({ handleFiltersChange, + filters, }: { handleFiltersChange: (value: IBuilderQuery['filters']) => void; + filters: IBuilderQuery['filters']; }): JSX.Element { const currentQuery = initialQueriesMap[DataSource.METRICS]; const updatedCurrentQuery = useMemo( @@ -26,11 +28,12 @@ function HostsListControls({ aggregateAttribute: { ...currentQuery.builder.queryData[0].aggregateAttribute, }, + filters, }, ], }, }), - [currentQuery], + [currentQuery, filters], ); const query = updatedCurrentQuery?.builder?.queryData[0] || null; diff --git a/frontend/src/container/InfraMonitoringHosts/HostsListTable.tsx b/frontend/src/container/InfraMonitoringHosts/HostsListTable.tsx index ba9b35143b..5c7d3bbe17 100644 --- a/frontend/src/container/InfraMonitoringHosts/HostsListTable.tsx +++ b/frontend/src/container/InfraMonitoringHosts/HostsListTable.tsx @@ -27,7 +27,7 @@ export default function HostsListTable({ tableData: data, hostMetricsData, filters, - setSelectedHostName, + onHostClick, currentPage, setCurrentPage, pageSize, @@ -77,7 +77,7 @@ export default function HostsListTable({ ); const handleRowClick = (record: HostRowData): void => { - setSelectedHostName(record.hostName); + onHostClick(record.hostName); logEvent(InfraMonitoringEvents.ItemClicked, { entity: InfraMonitoringEvents.HostEntity, page: InfraMonitoringEvents.ListPage, diff --git a/frontend/src/container/InfraMonitoringHosts/utils.tsx b/frontend/src/container/InfraMonitoringHosts/utils.tsx index 743a9135b0..b6778ff22d 100644 --- a/frontend/src/container/InfraMonitoringHosts/utils.tsx +++ b/frontend/src/container/InfraMonitoringHosts/utils.tsx @@ -41,16 +41,13 @@ export interface HostsListTableProps { | undefined; hostMetricsData: HostData[]; filters: TagFilter; - setSelectedHostName: Dispatch>; + onHostClick: (hostName: string) => void; currentPage: number; setCurrentPage: Dispatch>; pageSize: number; - setOrderBy: Dispatch< - SetStateAction<{ - columnName: string; - order: 'asc' | 'desc'; - } | null> - >; + setOrderBy: ( + orderBy: { columnName: string; order: 'asc' | 'desc' } | null, + ) => void; setPageSize: (pageSize: number) => void; } diff --git a/frontend/src/container/InfraMonitoringK8s/Clusters/ClusterDetails/ClusterDetails.tsx b/frontend/src/container/InfraMonitoringK8s/Clusters/ClusterDetails/ClusterDetails.tsx index b6b22f462d..b0712c94e4 100644 --- a/frontend/src/container/InfraMonitoringK8s/Clusters/ClusterDetails/ClusterDetails.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Clusters/ClusterDetails/ClusterDetails.tsx @@ -14,8 +14,14 @@ import { initialQueryState, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; -import { filterDuplicateFilters } from 'container/InfraMonitoringK8s/commonUtils'; -import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; +import { + filterDuplicateFilters, + getFiltersFromParams, +} from 'container/InfraMonitoringK8s/commonUtils'; +import { + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from 'container/InfraMonitoringK8s/constants'; import { QUERY_KEYS } from 'container/InfraMonitoringK8s/EntityDetailsUtils/utils'; import { CustomTimeType, @@ -34,6 +40,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -82,11 +89,27 @@ function ClusterDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedView, setSelectedView] = useState(() => { + const view = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + if (view) { + return view as VIEWS; + } + return VIEWS.METRICS; + }); const isDarkMode = useIsDarkMode(); - const initialFilters = useMemo( - () => ({ + const initialFilters = useMemo(() => { + const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + const queryKey = + urlView === VIEW_TYPES.LOGS + ? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS + : INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS; + const filters = getFiltersFromParams(searchParams, queryKey); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -103,12 +126,18 @@ function ClusterDetails({ value: cluster?.meta.k8s_cluster_name || '', }, ], - }), - [cluster?.meta.k8s_cluster_name], - ); + }; + }, [cluster?.meta.k8s_cluster_name, searchParams]); - const initialEventsFilters = useMemo( - () => ({ + const initialEventsFilters = useMemo(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + ); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -138,9 +167,8 @@ function ClusterDetails({ value: cluster?.meta.k8s_cluster_name || '', }, ], - }), - [cluster?.meta.k8s_cluster_name], - ); + }; + }, [cluster?.meta.k8s_cluster_name, searchParams]); const [logsAndTracesFilters, setLogsAndTracesFilters] = useState< IBuilderQuery['filters'] @@ -181,6 +209,13 @@ function ClusterDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify(null), + }); logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.K8sEntity, page: InfraMonitoringEvents.DetailedPage, @@ -220,7 +255,7 @@ function ClusterDetails({ ); const handleChangeLogFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogsAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_CLUSTER_NAME].includes(item.key?.key ?? ''), @@ -240,7 +275,7 @@ function ClusterDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -250,6 +285,16 @@ function ClusterDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -257,7 +302,7 @@ function ClusterDetails({ ); const handleChangeTracesFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogsAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_CLUSTER_NAME].includes(item.key?.key ?? ''), @@ -272,7 +317,7 @@ function ClusterDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -283,6 +328,16 @@ function ClusterDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -290,7 +345,7 @@ function ClusterDetails({ ); const handleChangeEventsFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setEventsFilters((prevFilters) => { const clusterKindFilter = prevFilters.items.find( (item) => item.key?.key === QUERY_KEYS.K8S_OBJECT_KIND, @@ -308,7 +363,7 @@ function ClusterDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -322,6 +377,16 @@ function ClusterDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/InfraMonitoringK8s/Clusters/K8sClustersList.tsx b/frontend/src/container/InfraMonitoringK8s/Clusters/K8sClustersList.tsx index 19e920ffe9..6732d018dc 100644 --- a/frontend/src/container/InfraMonitoringK8s/Clusters/K8sClustersList.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Clusters/K8sClustersList.tsx @@ -23,11 +23,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -59,19 +62,36 @@ function K8sClustersList({ const [currentPage, setCurrentPage] = useState(1); const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [searchParams, setSearchParams] = useSearchParams(); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>({ columnName: 'cpu', order: 'desc' }); + } | null>(() => getOrderByFromParams(searchParams, false)); const [selectedClusterName, setselectedClusterName] = useState( - null, + () => { + const clusterName = searchParams.get( + INFRA_MONITORING_K8S_PARAMS_KEYS.CLUSTER_NAME, + ); + if (clusterName) { + return clusterName; + } + return null; + }, ); const { pageSize, setPageSize } = usePageSize(K8sCategory.CLUSTERS); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [ selectedRowData, @@ -258,15 +278,26 @@ function K8sClustersList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -322,6 +353,10 @@ function K8sClustersList({ if (groupBy.length === 0) { setSelectedRowData(null); setselectedClusterName(record.clusterUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.CLUSTER_NAME]: record.clusterUID, + }); } else { handleGroupByRowClick(record); } @@ -348,6 +383,11 @@ function K8sClustersList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -372,7 +412,9 @@ function K8sClustersList({ }} showHeader={false} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setselectedClusterName(record.clusterUID), + onClick: (): void => { + setselectedClusterName(record.clusterUID); + }, className: 'expanded-clickable-row', })} /> @@ -436,6 +478,20 @@ function K8sClustersList({ const handleCloseClusterDetail = (): void => { setselectedClusterName(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => + ![ + INFRA_MONITORING_K8S_PARAMS_KEYS.CLUSTER_NAME, + INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW, + INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS, + ].includes(key), + ), + ), + }); }; const handleGroupByChange = useCallback( @@ -457,6 +513,10 @@ function K8sClustersList({ // Reset pagination on switching to groupBy setCurrentPage(1); setGroupBy(groupBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); setExpandedRowKeys([]); logEvent(InfraMonitoringEvents.GroupByChanged, { entity: InfraMonitoringEvents.K8sEntity, @@ -464,7 +524,7 @@ function K8sClustersList({ category: InfraMonitoringEvents.Cluster, }); }, - [groupByFiltersData], + [groupByFiltersData, searchParams, setSearchParams], ); useEffect(() => { diff --git a/frontend/src/container/InfraMonitoringK8s/DaemonSets/DaemonSetDetails/DaemonSetDetails.tsx b/frontend/src/container/InfraMonitoringK8s/DaemonSets/DaemonSetDetails/DaemonSetDetails.tsx index 58ef36949a..7b34783524 100644 --- a/frontend/src/container/InfraMonitoringK8s/DaemonSets/DaemonSetDetails/DaemonSetDetails.tsx +++ b/frontend/src/container/InfraMonitoringK8s/DaemonSets/DaemonSetDetails/DaemonSetDetails.tsx @@ -13,7 +13,11 @@ import { initialQueryState, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; -import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; +import { getFiltersFromParams } from 'container/InfraMonitoringK8s/commonUtils'; +import { + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from 'container/InfraMonitoringK8s/constants'; import { CustomTimeType, Time, @@ -31,6 +35,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -83,11 +88,27 @@ function DaemonSetDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedView, setSelectedView] = useState(() => { + const view = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + if (view) { + return view as VIEWS; + } + return VIEWS.METRICS; + }); const isDarkMode = useIsDarkMode(); - const initialFilters = useMemo( - () => ({ + const initialFilters = useMemo(() => { + const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + const queryKey = + urlView === VIEW_TYPES.LOGS + ? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS + : INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS; + const filters = getFiltersFromParams(searchParams, queryKey); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -117,12 +138,22 @@ function DaemonSetDetails({ value: daemonSet?.meta.k8s_namespace_name || '', }, ], - }), - [daemonSet?.meta.k8s_daemonset_name, daemonSet?.meta.k8s_namespace_name], - ); + }; + }, [ + daemonSet?.meta.k8s_daemonset_name, + daemonSet?.meta.k8s_namespace_name, + searchParams, + ]); - const initialEventsFilters = useMemo( - () => ({ + const initialEventsFilters = useMemo(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + ); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -152,9 +183,8 @@ function DaemonSetDetails({ value: daemonSet?.meta.k8s_daemonset_name || '', }, ], - }), - [daemonSet?.meta.k8s_daemonset_name], - ); + }; + }, [daemonSet?.meta.k8s_daemonset_name, searchParams]); const [logAndTracesFilters, setLogAndTracesFilters] = useState< IBuilderQuery['filters'] @@ -195,6 +225,13 @@ function DaemonSetDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify(null), + }); logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.K8sEntity, page: InfraMonitoringEvents.DetailedPage, @@ -234,7 +271,7 @@ function DaemonSetDetails({ ); const handleChangeLogFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_DAEMON_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes( @@ -257,7 +294,7 @@ function DaemonSetDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ ...primaryFilters, @@ -265,6 +302,15 @@ function DaemonSetDetails({ ...(paginationFilter ? [paginationFilter] : []), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -272,7 +318,7 @@ function DaemonSetDetails({ ); const handleChangeTracesFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_DAEMON_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes( @@ -289,7 +335,7 @@ function DaemonSetDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ ...primaryFilters, @@ -298,6 +344,16 @@ function DaemonSetDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -305,7 +361,7 @@ function DaemonSetDetails({ ); const handleChangeEventsFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setEventsFilters((prevFilters) => { const daemonSetKindFilter = prevFilters.items.find( (item) => item.key?.key === QUERY_KEYS.K8S_OBJECT_KIND, @@ -323,7 +379,7 @@ function DaemonSetDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ daemonSetKindFilter, @@ -335,6 +391,16 @@ function DaemonSetDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/InfraMonitoringK8s/DaemonSets/K8sDaemonSetsList.tsx b/frontend/src/container/InfraMonitoringK8s/DaemonSets/K8sDaemonSetsList.tsx index 8518a470c3..a104144e52 100644 --- a/frontend/src/container/InfraMonitoringK8s/DaemonSets/K8sDaemonSetsList.tsx +++ b/frontend/src/container/InfraMonitoringK8s/DaemonSets/K8sDaemonSetsList.tsx @@ -24,11 +24,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -59,21 +62,38 @@ function K8sDaemonSetsList({ ); const [currentPage, setCurrentPage] = useState(1); + const [searchParams, setSearchParams] = useSearchParams(); const [expandedRowKeys, setExpandedRowKeys] = useState([]); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>(null); + } | null>(() => getOrderByFromParams(searchParams, true)); - const [selectedDaemonSetUID, setselectedDaemonSetUID] = useState< + const [selectedDaemonSetUID, setSelectedDaemonSetUID] = useState< string | null - >(null); + >(() => { + const daemonSetUID = searchParams.get( + INFRA_MONITORING_K8S_PARAMS_KEYS.DAEMONSET_UID, + ); + if (daemonSetUID) { + return daemonSetUID; + } + return null; + }); const { pageSize, setPageSize } = usePageSize(K8sCategory.DAEMONSETS); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [ selectedRowData, @@ -262,15 +282,26 @@ function K8sDaemonSetsList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -329,7 +360,11 @@ function K8sDaemonSetsList({ const handleRowClick = (record: K8sDaemonSetsRowData): void => { if (groupBy.length === 0) { setSelectedRowData(null); - setselectedDaemonSetUID(record.daemonsetUID); + setSelectedDaemonSetUID(record.daemonsetUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.DAEMONSET_UID]: record.daemonsetUID, + }); } else { handleGroupByRowClick(record); } @@ -356,6 +391,11 @@ function K8sDaemonSetsList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -380,7 +420,9 @@ function K8sDaemonSetsList({ }} showHeader={false} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setselectedDaemonSetUID(record.daemonsetUID), + onClick: (): void => { + setSelectedDaemonSetUID(record.daemonsetUID); + }, className: 'expanded-clickable-row', })} /> @@ -443,7 +485,21 @@ function K8sDaemonSetsList({ }; const handleCloseDaemonSetDetail = (): void => { - setselectedDaemonSetUID(null); + setSelectedDaemonSetUID(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => + ![ + INFRA_MONITORING_K8S_PARAMS_KEYS.DAEMONSET_UID, + INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW, + INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS, + ].includes(key), + ), + ), + }); }; const handleGroupByChange = useCallback( @@ -464,6 +520,10 @@ function K8sDaemonSetsList({ setCurrentPage(1); setGroupBy(groupBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); setExpandedRowKeys([]); logEvent(InfraMonitoringEvents.GroupByChanged, { @@ -472,7 +532,7 @@ function K8sDaemonSetsList({ category: InfraMonitoringEvents.DaemonSet, }); }, - [groupByFiltersData], + [groupByFiltersData, searchParams, setSearchParams], ); useEffect(() => { diff --git a/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx b/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx index 2531f47233..951b4cf7dd 100644 --- a/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Deployments/DeploymentDetails/DeploymentDetails.tsx @@ -14,8 +14,14 @@ import { initialQueryState, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; -import { filterDuplicateFilters } from 'container/InfraMonitoringK8s/commonUtils'; -import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; +import { + filterDuplicateFilters, + getFiltersFromParams, +} from 'container/InfraMonitoringK8s/commonUtils'; +import { + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from 'container/InfraMonitoringK8s/constants'; import { QUERY_KEYS } from 'container/InfraMonitoringK8s/EntityDetailsUtils/utils'; import { CustomTimeType, @@ -34,6 +40,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -85,11 +92,27 @@ function DeploymentDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedView, setSelectedView] = useState(() => { + const view = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + if (view) { + return view as VIEWS; + } + return VIEWS.METRICS; + }); const isDarkMode = useIsDarkMode(); - const initialFilters = useMemo( - () => ({ + const initialFilters = useMemo(() => { + const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + const queryKey = + urlView === VIEW_TYPES.LOGS + ? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS + : INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS; + const filters = getFiltersFromParams(searchParams, queryKey); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -119,12 +142,22 @@ function DeploymentDetails({ value: deployment?.meta.k8s_namespace_name || '', }, ], - }), - [deployment?.meta.k8s_deployment_name, deployment?.meta.k8s_namespace_name], - ); + }; + }, [ + deployment?.meta.k8s_deployment_name, + deployment?.meta.k8s_namespace_name, + searchParams, + ]); - const initialEventsFilters = useMemo( - () => ({ + const initialEventsFilters = useMemo(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + ); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -154,9 +187,8 @@ function DeploymentDetails({ value: deployment?.meta.k8s_deployment_name || '', }, ], - }), - [deployment?.meta.k8s_deployment_name], - ); + }; + }, [deployment?.meta.k8s_deployment_name, searchParams]); const [logAndTracesFilters, setLogAndTracesFilters] = useState< IBuilderQuery['filters'] @@ -197,6 +229,13 @@ function DeploymentDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify(null), + }); logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.K8sEntity, page: InfraMonitoringEvents.DetailedPage, @@ -236,7 +275,7 @@ function DeploymentDetails({ ); const handleChangeLogFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_DEPLOYMENT_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes( @@ -259,7 +298,7 @@ function DeploymentDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -269,6 +308,16 @@ function DeploymentDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -276,7 +325,7 @@ function DeploymentDetails({ ); const handleChangeTracesFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_DEPLOYMENT_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes( @@ -293,7 +342,7 @@ function DeploymentDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -304,6 +353,16 @@ function DeploymentDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -311,7 +370,7 @@ function DeploymentDetails({ ); const handleChangeEventsFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setEventsFilters((prevFilters) => { const deploymentKindFilter = prevFilters.items.find( (item) => item.key?.key === QUERY_KEYS.K8S_OBJECT_KIND, @@ -329,7 +388,7 @@ function DeploymentDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -343,6 +402,16 @@ function DeploymentDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/InfraMonitoringK8s/Deployments/K8sDeploymentsList.tsx b/frontend/src/container/InfraMonitoringK8s/Deployments/K8sDeploymentsList.tsx index 7b463bf4af..b4294226bc 100644 --- a/frontend/src/container/InfraMonitoringK8s/Deployments/K8sDeploymentsList.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Deployments/K8sDeploymentsList.tsx @@ -24,11 +24,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -61,19 +64,36 @@ function K8sDeploymentsList({ const [currentPage, setCurrentPage] = useState(1); const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [searchParams, setSearchParams] = useSearchParams(); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>(null); + } | null>(() => getOrderByFromParams(searchParams, true)); const [selectedDeploymentUID, setselectedDeploymentUID] = useState< string | null - >(null); + >(() => { + const deploymentUID = searchParams.get( + INFRA_MONITORING_K8S_PARAMS_KEYS.DEPLOYMENT_UID, + ); + if (deploymentUID) { + return deploymentUID; + } + return null; + }); const { pageSize, setPageSize } = usePageSize(K8sCategory.DEPLOYMENTS); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [ selectedRowData, @@ -264,15 +284,26 @@ function K8sDeploymentsList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -333,6 +364,10 @@ function K8sDeploymentsList({ if (groupBy.length === 0) { setSelectedRowData(null); setselectedDeploymentUID(record.deploymentUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.DEPLOYMENT_UID]: record.deploymentUID, + }); } else { handleGroupByRowClick(record); } @@ -359,6 +394,11 @@ function K8sDeploymentsList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -383,7 +423,9 @@ function K8sDeploymentsList({ }} showHeader={false} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setselectedDeploymentUID(record.deploymentUID), + onClick: (): void => { + setselectedDeploymentUID(record.deploymentUID); + }, className: 'expanded-clickable-row', })} /> @@ -447,6 +489,20 @@ function K8sDeploymentsList({ const handleCloseDeploymentDetail = (): void => { setselectedDeploymentUID(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => + ![ + INFRA_MONITORING_K8S_PARAMS_KEYS.DEPLOYMENT_UID, + INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW, + INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS, + ].includes(key), + ), + ), + }); }; const handleGroupByChange = useCallback( @@ -468,6 +524,10 @@ function K8sDeploymentsList({ // Reset pagination on switching to groupBy setCurrentPage(1); setGroupBy(groupBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); setExpandedRowKeys([]); logEvent(InfraMonitoringEvents.GroupByChanged, { @@ -476,7 +536,7 @@ function K8sDeploymentsList({ category: InfraMonitoringEvents.Deployment, }); }, - [groupByFiltersData], + [groupByFiltersData, searchParams, setSearchParams], ); useEffect(() => { diff --git a/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/EntityEvents.tsx b/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/EntityEvents.tsx index 70e02902c9..97808f2d29 100644 --- a/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/EntityEvents.tsx +++ b/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/EntityEvents.tsx @@ -3,6 +3,7 @@ import './entityEvents.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, Table, TableColumnsType } from 'antd'; +import { VIEWS } from 'components/HostMetricsDetail/constants'; import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { EventContents } from 'container/InfraMonitoringK8s/commonUtils'; import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; @@ -28,6 +29,7 @@ import { DataSource } from 'types/common/queryBuilder'; import { EntityDetailsEmptyContainer, getEntityEventsOrLogsQueryPayload, + QUERY_KEYS, } from '../utils'; interface EventDataType { @@ -55,7 +57,10 @@ interface IEntityEventsProps { startTime: number; endTime: number; }; - handleChangeEventFilters: (filters: IBuilderQuery['filters']) => void; + handleChangeEventFilters: ( + filters: IBuilderQuery['filters'], + view: VIEWS, + ) => void; filters: IBuilderQuery['filters']; isModalTimeSelection: boolean; handleTimeChange: ( @@ -103,14 +108,18 @@ export default function Events({ ...currentQuery.builder.queryData[0].aggregateAttribute, }, filters: { - items: [], + items: filters.items.filter( + (item) => + item.key?.key !== QUERY_KEYS.K8S_OBJECT_KIND && + item.key?.key !== QUERY_KEYS.K8S_OBJECT_NAME, + ), op: 'AND', }, }, ], }, }), - [currentQuery], + [currentQuery, filters], ); const query = updatedCurrentQuery?.builder?.queryData[0] || null; @@ -243,7 +252,7 @@ export default function Events({ {query && ( handleChangeEventFilters(value, VIEWS.EVENTS)} disableNavigationShortcuts /> )} diff --git a/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityLogs/EntityLogsDetailedView.tsx b/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityLogs/EntityLogsDetailedView.tsx index e2f071956d..7b235edfbd 100644 --- a/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityLogs/EntityLogsDetailedView.tsx +++ b/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityLogs/EntityLogsDetailedView.tsx @@ -1,5 +1,6 @@ import './entityLogs.styles.scss'; +import { VIEWS } from 'components/HostMetricsDetail/constants'; import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearch'; import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2'; @@ -25,7 +26,7 @@ interface Props { interval: Time | CustomTimeType, dateTimeRange?: [number, number], ) => void; - handleChangeLogFilters: (value: IBuilderQuery['filters']) => void; + handleChangeLogFilters: (value: IBuilderQuery['filters'], view: VIEWS) => void; logFilters: IBuilderQuery['filters']; selectedInterval: Time; queryKey: string; @@ -78,7 +79,7 @@ function EntityLogsDetailedView({ {query && ( handleChangeLogFilters(value, VIEWS.LOGS)} disableNavigationShortcuts /> )} diff --git a/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityTraces/EntityTraces.tsx b/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityTraces/EntityTraces.tsx index 8a7b0f229a..925fa0e545 100644 --- a/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityTraces/EntityTraces.tsx +++ b/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityTraces/EntityTraces.tsx @@ -1,6 +1,7 @@ import './entityTraces.styles.scss'; import logEvent from 'api/common/logEvent'; +import { VIEWS } from 'components/HostMetricsDetail/constants'; import { getListColumns } from 'components/HostMetricsDetail/HostMetricTraces/utils'; import { ResizeTable } from 'components/ResizeTable'; import { DEFAULT_ENTITY_VERSION } from 'constants/app'; @@ -43,7 +44,10 @@ interface Props { interval: Time | CustomTimeType, dateTimeRange?: [number, number], ) => void; - handleChangeTracesFilters: (value: IBuilderQuery['filters']) => void; + handleChangeTracesFilters: ( + value: IBuilderQuery['filters'], + view: VIEWS, + ) => void; tracesFilters: IBuilderQuery['filters']; selectedInterval: Time; queryKey: string; @@ -164,7 +168,9 @@ function EntityTraces({ {query && ( + handleChangeTracesFilters(value, VIEWS.TRACES) + } disableNavigationShortcuts /> )} diff --git a/frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.tsx b/frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.tsx index a3f520629d..9aa6ab6f33 100644 --- a/frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.tsx +++ b/frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.tsx @@ -23,6 +23,7 @@ import { } from 'lucide-react'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { useState } from 'react'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import K8sClustersList from './Clusters/K8sClustersList'; @@ -30,6 +31,7 @@ import { ClustersQuickFiltersConfig, DaemonSetsQuickFiltersConfig, DeploymentsQuickFiltersConfig, + INFRA_MONITORING_K8S_PARAMS_KEYS, JobsQuickFiltersConfig, K8sCategories, NamespaceQuickFiltersConfig, @@ -50,7 +52,14 @@ import K8sVolumesList from './Volumes/K8sVolumesList'; export default function InfraMonitoringK8s(): JSX.Element { const [showFilters, setShowFilters] = useState(true); - const [selectedCategory, setSelectedCategory] = useState(K8sCategories.PODS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedCategory, setSelectedCategory] = useState(() => { + const category = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.CATEGORY); + if (category) { + return category as keyof typeof K8sCategories; + } + return K8sCategories.PODS; + }); const [quickFiltersLastUpdated, setQuickFiltersLastUpdated] = useState(-1); const { currentQuery } = useQueryBuilder(); @@ -70,6 +79,12 @@ export default function InfraMonitoringK8s(): JSX.Element { // in infra monitoring k8s, we are using only one query, hence updating the 0th index of queryData handleChangeQueryData('filters', query.builder.queryData[0].filters); setQuickFiltersLastUpdated(Date.now()); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS]: JSON.stringify( + query.builder.queryData[0].filters, + ), + }); logEvent(InfraMonitoringEvents.FilterApplied, { entity: InfraMonitoringEvents.K8sEntity, @@ -295,6 +310,9 @@ export default function InfraMonitoringK8s(): JSX.Element { const handleCategoryChange = (key: string | string[]): void => { if (Array.isArray(key) && key.length > 0) { setSelectedCategory(key[0] as string); + setSearchParams({ + [INFRA_MONITORING_K8S_PARAMS_KEYS.CATEGORY]: key[0] as string, + }); // Reset filters handleChangeQueryData('filters', { items: [], op: 'and' }); } diff --git a/frontend/src/container/InfraMonitoringK8s/Jobs/JobDetails/JobDetails.tsx b/frontend/src/container/InfraMonitoringK8s/Jobs/JobDetails/JobDetails.tsx index 63ea8eaae9..47063f487d 100644 --- a/frontend/src/container/InfraMonitoringK8s/Jobs/JobDetails/JobDetails.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Jobs/JobDetails/JobDetails.tsx @@ -13,7 +13,11 @@ import { initialQueryState, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; -import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; +import { getFiltersFromParams } from 'container/InfraMonitoringK8s/commonUtils'; +import { + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from 'container/InfraMonitoringK8s/constants'; import { CustomTimeType, Time, @@ -31,6 +35,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -80,11 +85,27 @@ function JobDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedView, setSelectedView] = useState(() => { + const view = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + if (view) { + return view as VIEWS; + } + return VIEWS.METRICS; + }); const isDarkMode = useIsDarkMode(); - const initialFilters = useMemo( - () => ({ + const initialFilters = useMemo(() => { + const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + const queryKey = + urlView === VIEW_TYPES.LOGS + ? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS + : INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS; + const filters = getFiltersFromParams(searchParams, queryKey); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -114,12 +135,18 @@ function JobDetails({ value: job?.meta.k8s_namespace_name || '', }, ], - }), - [job?.meta.k8s_job_name, job?.meta.k8s_namespace_name], - ); + }; + }, [job?.meta.k8s_job_name, job?.meta.k8s_namespace_name, searchParams]); - const initialEventsFilters = useMemo( - () => ({ + const initialEventsFilters = useMemo(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + ); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -149,9 +176,8 @@ function JobDetails({ value: job?.meta.k8s_job_name || '', }, ], - }), - [job?.meta.k8s_job_name], - ); + }; + }, [job?.meta.k8s_job_name, searchParams]); const [logAndTracesFilters, setLogAndTracesFilters] = useState< IBuilderQuery['filters'] @@ -192,6 +218,13 @@ function JobDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify(null), + }); logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.K8sEntity, page: InfraMonitoringEvents.DetailedPage, @@ -231,7 +264,7 @@ function JobDetails({ ); const handleChangeLogFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_JOB_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes( @@ -253,7 +286,7 @@ function JobDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ ...primaryFilters, @@ -261,6 +294,16 @@ function JobDetails({ ...(paginationFilter ? [paginationFilter] : []), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -268,7 +311,7 @@ function JobDetails({ ); const handleChangeTracesFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_JOB_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes( @@ -285,7 +328,7 @@ function JobDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ ...primaryFilters, @@ -294,6 +337,16 @@ function JobDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -301,7 +354,7 @@ function JobDetails({ ); const handleChangeEventsFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setEventsFilters((prevFilters) => { const jobKindFilter = prevFilters.items.find( (item) => item.key?.key === QUERY_KEYS.K8S_OBJECT_KIND, @@ -319,7 +372,7 @@ function JobDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ jobKindFilter, @@ -331,6 +384,16 @@ function JobDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/InfraMonitoringK8s/Jobs/K8sJobsList.tsx b/frontend/src/container/InfraMonitoringK8s/Jobs/K8sJobsList.tsx index 397d9b0488..85d0505ce2 100644 --- a/frontend/src/container/InfraMonitoringK8s/Jobs/K8sJobsList.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Jobs/K8sJobsList.tsx @@ -24,11 +24,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -61,17 +64,32 @@ function K8sJobsList({ const [currentPage, setCurrentPage] = useState(1); const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [searchParams, setSearchParams] = useSearchParams(); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>(null); + } | null>(() => getOrderByFromParams(searchParams, true)); - const [selectedJobUID, setselectedJobUID] = useState(null); + const [selectedJobUID, setselectedJobUID] = useState(() => { + const jobUID = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.JOB_UID); + if (jobUID) { + return jobUID; + } + return null; + }); const { pageSize, setPageSize } = usePageSize(K8sCategory.JOBS); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [selectedRowData, setSelectedRowData] = useState( null, @@ -251,15 +269,26 @@ function K8sJobsList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -306,6 +335,10 @@ function K8sJobsList({ if (groupBy.length === 0) { setSelectedRowData(null); setselectedJobUID(record.jobUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.JOB_UID]: record.jobUID, + }); } else { handleGroupByRowClick(record); } @@ -332,6 +365,11 @@ function K8sJobsList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -356,7 +394,9 @@ function K8sJobsList({ }} showHeader={false} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setselectedJobUID(record.jobUID), + onClick: (): void => { + setselectedJobUID(record.jobUID); + }, className: 'expanded-clickable-row', })} /> @@ -420,6 +460,20 @@ function K8sJobsList({ const handleCloseJobDetail = (): void => { setselectedJobUID(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => + ![ + INFRA_MONITORING_K8S_PARAMS_KEYS.JOB_UID, + INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW, + INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS, + ].includes(key), + ), + ), + }); }; const handleGroupByChange = useCallback( @@ -441,6 +495,10 @@ function K8sJobsList({ setCurrentPage(1); setGroupBy(groupBy); setExpandedRowKeys([]); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); logEvent(InfraMonitoringEvents.GroupByChanged, { entity: InfraMonitoringEvents.K8sEntity, @@ -448,7 +506,7 @@ function K8sJobsList({ category: InfraMonitoringEvents.Job, }); }, - [groupByFiltersData], + [groupByFiltersData, searchParams, setSearchParams], ); useEffect(() => { diff --git a/frontend/src/container/InfraMonitoringK8s/K8sHeader.tsx b/frontend/src/container/InfraMonitoringK8s/K8sHeader.tsx index 2f583a9651..fa9eaf2d6f 100644 --- a/frontend/src/container/InfraMonitoringK8s/K8sHeader.tsx +++ b/frontend/src/container/InfraMonitoringK8s/K8sHeader.tsx @@ -7,11 +7,12 @@ import QueryBuilderSearch from 'container/QueryBuilder/filters/QueryBuilderSearc import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2'; import { Filter, SlidersHorizontal } from 'lucide-react'; import { useCallback, useMemo, useState } from 'react'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { DataSource } from 'types/common/queryBuilder'; -import { K8sCategory } from './constants'; +import { INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory } from './constants'; import K8sFiltersSidePanel from './K8sFiltersSidePanel/K8sFiltersSidePanel'; import { IEntityColumn } from './utils'; @@ -47,11 +48,19 @@ function K8sHeader({ entity, }: K8sHeaderProps): JSX.Element { const [isFiltersSidePanelOpen, setIsFiltersSidePanelOpen] = useState(false); + const [searchParams, setSearchParams] = useSearchParams(); const currentQuery = initialQueriesMap[DataSource.METRICS]; - const updatedCurrentQuery = useMemo( - () => ({ + const updatedCurrentQuery = useMemo(() => { + const urlFilters = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS); + let { filters } = currentQuery.builder.queryData[0]; + if (urlFilters) { + const decoded = decodeURIComponent(urlFilters); + const parsed = JSON.parse(decoded); + filters = parsed; + } + return { ...currentQuery, builder: { ...currentQuery.builder, @@ -62,20 +71,24 @@ function K8sHeader({ aggregateAttribute: { ...currentQuery.builder.queryData[0].aggregateAttribute, }, + filters, }, ], }, - }), - [currentQuery], - ); + }; + }, [currentQuery, searchParams]); const query = updatedCurrentQuery?.builder?.queryData[0] || null; const handleChangeTagFilters = useCallback( (value: IBuilderQuery['filters']) => { handleFiltersChange(value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.FILTERS]: JSON.stringify(value), + }); }, - [handleFiltersChange], + [handleFiltersChange, searchParams, setSearchParams], ); return ( diff --git a/frontend/src/container/InfraMonitoringK8s/Namespaces/K8sNamespacesList.tsx b/frontend/src/container/InfraMonitoringK8s/Namespaces/K8sNamespacesList.tsx index 5181b3ac80..6f076132cf 100644 --- a/frontend/src/container/InfraMonitoringK8s/Namespaces/K8sNamespacesList.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Namespaces/K8sNamespacesList.tsx @@ -23,11 +23,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -60,19 +63,36 @@ function K8sNamespacesList({ const [currentPage, setCurrentPage] = useState(1); const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [searchParams, setSearchParams] = useSearchParams(); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>(null); + } | null>(() => getOrderByFromParams(searchParams, true)); const [selectedNamespaceUID, setselectedNamespaceUID] = useState< string | null - >(null); + >(() => { + const namespaceUID = searchParams.get( + INFRA_MONITORING_K8S_PARAMS_KEYS.NAMESPACE_UID, + ); + if (namespaceUID) { + return namespaceUID; + } + return null; + }); const { pageSize, setPageSize } = usePageSize(K8sCategory.NAMESPACES); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [ selectedRowData, @@ -261,15 +281,26 @@ function K8sNamespacesList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -330,6 +361,10 @@ function K8sNamespacesList({ if (groupBy.length === 0) { setSelectedRowData(null); setselectedNamespaceUID(record.namespaceUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.NAMESPACE_UID]: record.namespaceUID, + }); } else { handleGroupByRowClick(record); } @@ -356,6 +391,11 @@ function K8sNamespacesList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -380,7 +420,9 @@ function K8sNamespacesList({ }} showHeader={false} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setselectedNamespaceUID(record.namespaceUID), + onClick: (): void => { + setselectedNamespaceUID(record.namespaceUID); + }, className: 'expanded-clickable-row', })} /> @@ -444,6 +486,20 @@ function K8sNamespacesList({ const handleCloseNamespaceDetail = (): void => { setselectedNamespaceUID(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => + ![ + INFRA_MONITORING_K8S_PARAMS_KEYS.NAMESPACE_UID, + INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW, + INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS, + ].includes(key), + ), + ), + }); }; const handleGroupByChange = useCallback( @@ -466,6 +522,10 @@ function K8sNamespacesList({ setCurrentPage(1); setGroupBy(groupBy); setExpandedRowKeys([]); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); logEvent(InfraMonitoringEvents.GroupByChanged, { entity: InfraMonitoringEvents.K8sEntity, @@ -473,7 +533,7 @@ function K8sNamespacesList({ category: InfraMonitoringEvents.Namespace, }); }, - [groupByFiltersData], + [groupByFiltersData, searchParams, setSearchParams], ); useEffect(() => { diff --git a/frontend/src/container/InfraMonitoringK8s/Namespaces/NamespaceDetails/NamespaceDetails.tsx b/frontend/src/container/InfraMonitoringK8s/Namespaces/NamespaceDetails/NamespaceDetails.tsx index f9f1801f93..f64057e4ce 100644 --- a/frontend/src/container/InfraMonitoringK8s/Namespaces/NamespaceDetails/NamespaceDetails.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Namespaces/NamespaceDetails/NamespaceDetails.tsx @@ -14,7 +14,11 @@ import { initialQueryState, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; -import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; +import { getFiltersFromParams } from 'container/InfraMonitoringK8s/commonUtils'; +import { + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from 'container/InfraMonitoringK8s/constants'; import { QUERY_KEYS } from 'container/InfraMonitoringK8s/EntityDetailsUtils/utils'; import { CustomTimeType, @@ -33,6 +37,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -84,11 +89,27 @@ function NamespaceDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedView, setSelectedView] = useState(() => { + const view = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + if (view) { + return view as VIEWS; + } + return VIEWS.METRICS; + }); const isDarkMode = useIsDarkMode(); - const initialFilters = useMemo( - () => ({ + const initialFilters = useMemo(() => { + const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + const queryKey = + urlView === VIEW_TYPES.LOGS + ? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS + : INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS; + const filters = getFiltersFromParams(searchParams, queryKey); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -105,12 +126,18 @@ function NamespaceDetails({ value: namespace?.namespaceName || '', }, ], - }), - [namespace?.namespaceName], - ); + }; + }, [namespace?.namespaceName, searchParams]); - const initialEventsFilters = useMemo( - () => ({ + const initialEventsFilters = useMemo(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + ); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -140,9 +167,8 @@ function NamespaceDetails({ value: namespace?.namespaceName || '', }, ], - }), - [namespace?.namespaceName], - ); + }; + }, [namespace?.namespaceName, searchParams]); const [logAndTracesFilters, setLogAndTracesFilters] = useState< IBuilderQuery['filters'] @@ -183,6 +209,13 @@ function NamespaceDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify(null), + }); logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.K8sEntity, page: InfraMonitoringEvents.DetailedPage, @@ -222,7 +255,7 @@ function NamespaceDetails({ ); const handleChangeLogFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_NAMESPACE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes( @@ -244,7 +277,7 @@ function NamespaceDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ ...primaryFilters, @@ -252,6 +285,17 @@ function NamespaceDetails({ ...(paginationFilter ? [paginationFilter] : []), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -259,7 +303,7 @@ function NamespaceDetails({ ); const handleChangeTracesFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_NAMESPACE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes( @@ -276,7 +320,7 @@ function NamespaceDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ ...primaryFilters, @@ -285,6 +329,16 @@ function NamespaceDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify( + updatedFilters, + ), + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -292,7 +346,7 @@ function NamespaceDetails({ ); const handleChangeEventsFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setEventsFilters((prevFilters) => { const namespaceKindFilter = prevFilters.items.find( (item) => item.key?.key === QUERY_KEYS.K8S_OBJECT_KIND, @@ -310,7 +364,7 @@ function NamespaceDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ namespaceKindFilter, @@ -322,6 +376,16 @@ function NamespaceDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify( + updatedFilters, + ), + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/InfraMonitoringK8s/Nodes/K8sNodesList.tsx b/frontend/src/container/InfraMonitoringK8s/Nodes/K8sNodesList.tsx index d27f5d9196..441346980f 100644 --- a/frontend/src/container/InfraMonitoringK8s/Nodes/K8sNodesList.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Nodes/K8sNodesList.tsx @@ -23,11 +23,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -60,17 +63,32 @@ function K8sNodesList({ const [currentPage, setCurrentPage] = useState(1); const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [searchParams, setSearchParams] = useSearchParams(); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>({ columnName: 'cpu', order: 'desc' }); + } | null>(() => getOrderByFromParams(searchParams, false)); - const [selectedNodeUID, setselectedNodeUID] = useState(null); + const [selectedNodeUID, setSelectedNodeUID] = useState(() => { + const nodeUID = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.NODE_UID); + if (nodeUID) { + return nodeUID; + } + return null; + }); const { pageSize, setPageSize } = usePageSize(K8sCategory.NODES); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [selectedRowData, setSelectedRowData] = useState( null, @@ -250,15 +268,26 @@ function K8sNodesList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -307,7 +336,11 @@ function K8sNodesList({ const handleRowClick = (record: K8sNodesRowData): void => { if (groupBy.length === 0) { setSelectedRowData(null); - setselectedNodeUID(record.nodeUID); + setSelectedNodeUID(record.nodeUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.NODE_UID]: record.nodeUID, + }); } else { handleGroupByRowClick(record); } @@ -334,6 +367,11 @@ function K8sNodesList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -359,7 +397,9 @@ function K8sNodesList({ }} showHeader={false} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setselectedNodeUID(record.nodeUID), + onClick: (): void => { + setSelectedNodeUID(record.nodeUID); + }, className: 'expanded-clickable-row', })} /> @@ -422,7 +462,21 @@ function K8sNodesList({ }; const handleCloseNodeDetail = (): void => { - setselectedNodeUID(null); + setSelectedNodeUID(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => + ![ + INFRA_MONITORING_K8S_PARAMS_KEYS.NODE_UID, + INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW, + INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS, + ].includes(key), + ), + ), + }); }; const handleGroupByChange = useCallback( @@ -444,6 +498,10 @@ function K8sNodesList({ setCurrentPage(1); setGroupBy(groupBy); setExpandedRowKeys([]); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); logEvent(InfraMonitoringEvents.GroupByChanged, { entity: InfraMonitoringEvents.K8sEntity, @@ -451,7 +509,7 @@ function K8sNodesList({ category: InfraMonitoringEvents.Node, }); }, - [groupByFiltersData], + [groupByFiltersData, searchParams, setSearchParams], ); useEffect(() => { diff --git a/frontend/src/container/InfraMonitoringK8s/Nodes/NodeDetails/NodeDetails.tsx b/frontend/src/container/InfraMonitoringK8s/Nodes/NodeDetails/NodeDetails.tsx index a1d54e4a78..a216ea010c 100644 --- a/frontend/src/container/InfraMonitoringK8s/Nodes/NodeDetails/NodeDetails.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Nodes/NodeDetails/NodeDetails.tsx @@ -14,8 +14,14 @@ import { initialQueryState, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; -import { filterDuplicateFilters } from 'container/InfraMonitoringK8s/commonUtils'; -import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; +import { + filterDuplicateFilters, + getFiltersFromParams, +} from 'container/InfraMonitoringK8s/commonUtils'; +import { + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from 'container/InfraMonitoringK8s/constants'; import NodeEvents from 'container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents'; import { CustomTimeType, @@ -34,6 +40,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -82,11 +89,27 @@ function NodeDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedView, setSelectedView] = useState(() => { + const view = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + if (view) { + return view as VIEWS; + } + return VIEWS.METRICS; + }); const isDarkMode = useIsDarkMode(); - const initialFilters = useMemo( - () => ({ + const initialFilters = useMemo(() => { + const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + const queryKey = + urlView === VIEW_TYPES.LOGS + ? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS + : INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS; + const filters = getFiltersFromParams(searchParams, queryKey); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -103,12 +126,18 @@ function NodeDetails({ value: node?.meta.k8s_node_name || '', }, ], - }), - [node?.meta.k8s_node_name], - ); + }; + }, [node?.meta.k8s_node_name, searchParams]); - const initialEventsFilters = useMemo( - () => ({ + const initialEventsFilters = useMemo(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + ); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -138,9 +167,8 @@ function NodeDetails({ value: node?.meta.k8s_node_name || '', }, ], - }), - [node?.meta.k8s_node_name], - ); + }; + }, [node?.meta.k8s_node_name, searchParams]); const [logAndTracesFilters, setLogAndTracesFilters] = useState< IBuilderQuery['filters'] @@ -181,6 +209,13 @@ function NodeDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify(null), + }); logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.K8sEntity, page: InfraMonitoringEvents.DetailedPage, @@ -220,7 +255,7 @@ function NodeDetails({ ); const handleChangeLogFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes( @@ -242,7 +277,7 @@ function NodeDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -252,6 +287,16 @@ function NodeDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -259,7 +304,7 @@ function NodeDetails({ ); const handleChangeTracesFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_NODE_NAME, QUERY_KEYS.K8S_CLUSTER_NAME].includes( @@ -276,7 +321,7 @@ function NodeDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -287,6 +332,16 @@ function NodeDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -294,7 +349,7 @@ function NodeDetails({ ); const handleChangeEventsFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setEventsFilters((prevFilters) => { const nodeKindFilter = prevFilters.items.find( (item) => item.key?.key === QUERY_KEYS.K8S_OBJECT_KIND, @@ -312,7 +367,7 @@ function NodeDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ nodeKindFilter, @@ -324,6 +379,16 @@ function NodeDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/InfraMonitoringK8s/Pods/K8sPodLists.tsx b/frontend/src/container/InfraMonitoringK8s/Pods/K8sPodLists.tsx index 08d8a2ddc4..f6bc79171e 100644 --- a/frontend/src/container/InfraMonitoringK8s/Pods/K8sPodLists.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Pods/K8sPodLists.tsx @@ -24,11 +24,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight, CornerDownRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -59,6 +62,7 @@ function K8sPodsList({ const { maxTime, minTime } = useSelector( (state) => state.globalTime, ); + const [searchParams, setSearchParams] = useSearchParams(); const [currentPage, setCurrentPage] = useState(1); @@ -68,7 +72,15 @@ function K8sPodsList({ defaultAvailableColumns, ); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [selectedRowData, setSelectedRowData] = useState( null, @@ -134,9 +146,15 @@ function K8sPodsList({ const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>({ columnName: 'cpu', order: 'desc' }); + } | null>(() => getOrderByFromParams(searchParams, false)); - const [selectedPodUID, setSelectedPodUID] = useState(null); + const [selectedPodUID, setSelectedPodUID] = useState(() => { + const podUID = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.POD_UID); + if (podUID) { + return podUID; + } + return null; + }); const { pageSize, setPageSize } = usePageSize(K8sCategory.PODS); @@ -265,15 +283,26 @@ function K8sPodsList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -318,6 +347,10 @@ function K8sPodsList({ setCurrentPage(1); setGroupBy(groupBy); setExpandedRowKeys([]); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); logEvent(InfraMonitoringEvents.GroupByChanged, { entity: InfraMonitoringEvents.K8sEntity, @@ -325,7 +358,7 @@ function K8sPodsList({ category: InfraMonitoringEvents.Pod, }); }, - [groupByFiltersData], + [groupByFiltersData, searchParams, setSearchParams], ); useEffect(() => { @@ -366,6 +399,10 @@ function K8sPodsList({ const handleRowClick = (record: K8sPodsRowData): void => { if (groupBy.length === 0) { setSelectedPodUID(record.podUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.POD_UID]: record.podUID, + }); setSelectedRowData(null); } else { handleGroupByRowClick(record); @@ -380,6 +417,20 @@ function K8sPodsList({ const handleClosePodDetail = (): void => { setSelectedPodUID(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => + ![ + INFRA_MONITORING_K8S_PARAMS_KEYS.POD_UID, + INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW, + INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS, + ].includes(key), + ), + ), + }); }; const handleAddColumn = useCallback( @@ -435,6 +486,11 @@ function K8sPodsList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -459,7 +515,9 @@ function K8sPodsList({ indicator: } />, }} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setSelectedPodUID(record.podUID), + onClick: (): void => { + setSelectedPodUID(record.podUID); + }, className: 'expanded-clickable-row', })} /> diff --git a/frontend/src/container/InfraMonitoringK8s/Pods/PodDetails/PodDetails.tsx b/frontend/src/container/InfraMonitoringK8s/Pods/PodDetails/PodDetails.tsx index dec4d7d19d..aefb5a0bb1 100644 --- a/frontend/src/container/InfraMonitoringK8s/Pods/PodDetails/PodDetails.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Pods/PodDetails/PodDetails.tsx @@ -15,8 +15,14 @@ import { initialQueryState, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; -import { filterDuplicateFilters } from 'container/InfraMonitoringK8s/commonUtils'; -import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; +import { + filterDuplicateFilters, + getFiltersFromParams, +} from 'container/InfraMonitoringK8s/commonUtils'; +import { + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from 'container/InfraMonitoringK8s/constants'; import { QUERY_KEYS } from 'container/InfraMonitoringK8s/EntityDetailsUtils/utils'; import { CustomTimeType, @@ -35,6 +41,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -86,11 +93,27 @@ function PodDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedView, setSelectedView] = useState(() => { + const view = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + if (view) { + return view as VIEWS; + } + return VIEWS.METRICS; + }); const isDarkMode = useIsDarkMode(); - const initialFilters = useMemo( - () => ({ + const initialFilters = useMemo(() => { + const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + const queryKey = + urlView === VIEW_TYPES.LOGS + ? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS + : INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS; + const filters = getFiltersFromParams(searchParams, queryKey); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -120,12 +143,18 @@ function PodDetails({ value: pod?.meta.k8s_namespace_name || '', }, ], - }), - [pod?.meta.k8s_namespace_name, pod?.meta.k8s_pod_name], - ); + }; + }, [pod?.meta.k8s_namespace_name, pod?.meta.k8s_pod_name, searchParams]); - const initialEventsFilters = useMemo( - () => ({ + const initialEventsFilters = useMemo(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + ); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -155,9 +184,8 @@ function PodDetails({ value: pod?.meta.k8s_pod_name || '', }, ], - }), - [pod?.meta.k8s_pod_name], - ); + }; + }, [pod?.meta.k8s_pod_name, searchParams]); const [logsAndTracesFilters, setLogsAndTracesFilters] = useState< IBuilderQuery['filters'] @@ -198,6 +226,13 @@ function PodDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify(null), + }); logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.K8sEntity, page: InfraMonitoringEvents.DetailedPage, @@ -237,7 +272,7 @@ function PodDetails({ ); const handleChangeLogFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogsAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [ @@ -261,7 +296,7 @@ function PodDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -271,6 +306,16 @@ function PodDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -278,7 +323,7 @@ function PodDetails({ ); const handleChangeTracesFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogsAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [ @@ -297,7 +342,7 @@ function PodDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: filterDuplicateFilters( [ @@ -308,6 +353,16 @@ function PodDetails({ ].filter((item): item is TagFilterItem => item !== undefined), ), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -315,7 +370,7 @@ function PodDetails({ ); const handleChangeEventsFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setEventsFilters((prevFilters) => { const podKindFilter = prevFilters.items.find( (item) => item.key?.key === QUERY_KEYS.K8S_OBJECT_KIND, @@ -333,7 +388,7 @@ function PodDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ podKindFilter, @@ -345,6 +400,16 @@ function PodDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/InfraMonitoringK8s/StatefulSets/K8sStatefulSetsList.tsx b/frontend/src/container/InfraMonitoringK8s/StatefulSets/K8sStatefulSetsList.tsx index f3bdb46ec5..ba2b14f8de 100644 --- a/frontend/src/container/InfraMonitoringK8s/StatefulSets/K8sStatefulSetsList.tsx +++ b/frontend/src/container/InfraMonitoringK8s/StatefulSets/K8sStatefulSetsList.tsx @@ -24,11 +24,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -60,19 +63,36 @@ function K8sStatefulSetsList({ const [currentPage, setCurrentPage] = useState(1); const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [searchParams, setSearchParams] = useSearchParams(); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>(null); + } | null>(() => getOrderByFromParams(searchParams, true)); const [selectedStatefulSetUID, setselectedStatefulSetUID] = useState< string | null - >(null); + >(() => { + const statefulSetUID = searchParams.get( + INFRA_MONITORING_K8S_PARAMS_KEYS.STATEFULSET_UID, + ); + if (statefulSetUID) { + return statefulSetUID; + } + return null; + }); const { pageSize, setPageSize } = usePageSize(K8sCategory.STATEFULSETS); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [ selectedRowData, @@ -263,15 +283,26 @@ function K8sStatefulSetsList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -330,6 +361,10 @@ function K8sStatefulSetsList({ if (groupBy.length === 0) { setSelectedRowData(null); setselectedStatefulSetUID(record.statefulsetUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.STATEFULSET_UID]: record.statefulsetUID, + }); } else { handleGroupByRowClick(record); } @@ -356,6 +391,11 @@ function K8sStatefulSetsList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -380,7 +420,9 @@ function K8sStatefulSetsList({ }} showHeader={false} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setselectedStatefulSetUID(record.statefulsetUID), + onClick: (): void => { + setselectedStatefulSetUID(record.statefulsetUID); + }, className: 'expanded-clickable-row', })} /> @@ -444,6 +486,20 @@ function K8sStatefulSetsList({ const handleCloseStatefulSetDetail = (): void => { setselectedStatefulSetUID(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => + ![ + INFRA_MONITORING_K8S_PARAMS_KEYS.STATEFULSET_UID, + INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW, + INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS, + ].includes(key), + ), + ), + }); }; const handleGroupByChange = useCallback( @@ -465,6 +521,10 @@ function K8sStatefulSetsList({ setCurrentPage(1); setGroupBy(groupBy); setExpandedRowKeys([]); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); logEvent(InfraMonitoringEvents.GroupByChanged, { entity: InfraMonitoringEvents.K8sEntity, @@ -472,7 +532,7 @@ function K8sStatefulSetsList({ category: InfraMonitoringEvents.StatefulSet, }); }, - [groupByFiltersData], + [groupByFiltersData, searchParams, setSearchParams], ); useEffect(() => { diff --git a/frontend/src/container/InfraMonitoringK8s/StatefulSets/StatefulSetDetails/StatefulSetDetails.tsx b/frontend/src/container/InfraMonitoringK8s/StatefulSets/StatefulSetDetails/StatefulSetDetails.tsx index 4ebcd39847..aac8c1c60f 100644 --- a/frontend/src/container/InfraMonitoringK8s/StatefulSets/StatefulSetDetails/StatefulSetDetails.tsx +++ b/frontend/src/container/InfraMonitoringK8s/StatefulSets/StatefulSetDetails/StatefulSetDetails.tsx @@ -13,7 +13,11 @@ import { initialQueryState, } from 'constants/queryBuilder'; import ROUTES from 'constants/routes'; -import { K8sCategory } from 'container/InfraMonitoringK8s/constants'; +import { getFiltersFromParams } from 'container/InfraMonitoringK8s/commonUtils'; +import { + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from 'container/InfraMonitoringK8s/constants'; import EntityEvents from 'container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents'; import EntityLogs from 'container/InfraMonitoringK8s/EntityDetailsUtils/EntityLogs'; import EntityMetrics from 'container/InfraMonitoringK8s/EntityDetailsUtils/EntityMetrics'; @@ -36,6 +40,7 @@ import { } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { @@ -83,11 +88,27 @@ function StatefulSetDetails({ selectedTime as Time, ); - const [selectedView, setSelectedView] = useState(VIEWS.METRICS); + const [searchParams, setSearchParams] = useSearchParams(); + const [selectedView, setSelectedView] = useState(() => { + const view = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + if (view) { + return view as VIEWS; + } + return VIEWS.METRICS; + }); const isDarkMode = useIsDarkMode(); - const initialFilters = useMemo( - () => ({ + const initialFilters = useMemo(() => { + const urlView = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW); + const queryKey = + urlView === VIEW_TYPES.LOGS + ? INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS + : INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS; + const filters = getFiltersFromParams(searchParams, queryKey); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -117,15 +138,22 @@ function StatefulSetDetails({ value: statefulSet?.meta.k8s_namespace_name || '', }, ], - }), - [ - statefulSet?.meta.k8s_statefulset_name, - statefulSet?.meta.k8s_namespace_name, - ], - ); + }; + }, [ + searchParams, + statefulSet?.meta.k8s_statefulset_name, + statefulSet?.meta.k8s_namespace_name, + ]); - const initialEventsFilters = useMemo( - () => ({ + const initialEventsFilters = useMemo(() => { + const filters = getFiltersFromParams( + searchParams, + INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS, + ); + if (filters) { + return filters; + } + return { op: 'AND', items: [ { @@ -155,9 +183,8 @@ function StatefulSetDetails({ value: statefulSet?.meta.k8s_statefulset_name || '', }, ], - }), - [statefulSet?.meta.k8s_statefulset_name], - ); + }; + }, [searchParams, statefulSet?.meta.k8s_statefulset_name]); const [logAndTracesFilters, setLogAndTracesFilters] = useState< IBuilderQuery['filters'] @@ -198,6 +225,13 @@ function StatefulSetDetails({ const handleTabChange = (e: RadioChangeEvent): void => { setSelectedView(e.target.value); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: e.target.value, + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify(null), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify(null), + }); logEvent(InfraMonitoringEvents.TabChanged, { entity: InfraMonitoringEvents.K8sEntity, page: InfraMonitoringEvents.DetailedPage, @@ -237,7 +271,7 @@ function StatefulSetDetails({ ); const handleChangeLogFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_STATEFUL_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes( @@ -260,7 +294,7 @@ function StatefulSetDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ ...primaryFilters, @@ -268,6 +302,16 @@ function StatefulSetDetails({ ...(paginationFilter ? [paginationFilter] : []), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.LOG_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -275,7 +319,7 @@ function StatefulSetDetails({ ); const handleChangeTracesFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setLogAndTracesFilters((prevFilters) => { const primaryFilters = prevFilters.items.filter((item) => [QUERY_KEYS.K8S_STATEFUL_SET_NAME, QUERY_KEYS.K8S_NAMESPACE_NAME].includes( @@ -292,7 +336,7 @@ function StatefulSetDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ ...primaryFilters, @@ -301,6 +345,16 @@ function StatefulSetDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.TRACES_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps @@ -308,7 +362,7 @@ function StatefulSetDetails({ ); const handleChangeEventsFilters = useCallback( - (value: IBuilderQuery['filters']) => { + (value: IBuilderQuery['filters'], view: VIEWS) => { setEventsFilters((prevFilters) => { const statefulSetKindFilter = prevFilters.items.find( (item) => item.key?.key === QUERY_KEYS.K8S_OBJECT_KIND, @@ -326,7 +380,7 @@ function StatefulSetDetails({ }); } - return { + const updatedFilters = { op: 'AND', items: [ statefulSetKindFilter, @@ -338,6 +392,16 @@ function StatefulSetDetails({ ), ].filter((item): item is TagFilterItem => item !== undefined), }; + + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.EVENTS_FILTERS]: JSON.stringify( + updatedFilters, + ), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VIEW]: view, + }); + + return updatedFilters; }); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/frontend/src/container/InfraMonitoringK8s/Volumes/K8sVolumesList.tsx b/frontend/src/container/InfraMonitoringK8s/Volumes/K8sVolumesList.tsx index 9ade0544ec..9ba4a75448 100644 --- a/frontend/src/container/InfraMonitoringK8s/Volumes/K8sVolumesList.tsx +++ b/frontend/src/container/InfraMonitoringK8s/Volumes/K8sVolumesList.tsx @@ -24,11 +24,14 @@ import { useQueryOperations } from 'hooks/queryBuilder/useQueryBuilderOperations import { ChevronDown, ChevronRight } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom-v5-compat'; import { AppState } from 'store/reducers'; import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getOrderByFromParams } from '../commonUtils'; import { + INFRA_MONITORING_K8S_PARAMS_KEYS, K8sCategory, K8sEntityToAggregateAttributeMapping, } from '../constants'; @@ -60,19 +63,36 @@ function K8sVolumesList({ const [currentPage, setCurrentPage] = useState(1); const [expandedRowKeys, setExpandedRowKeys] = useState([]); + const [searchParams, setSearchParams] = useSearchParams(); const [orderBy, setOrderBy] = useState<{ columnName: string; order: 'asc' | 'desc'; - } | null>(null); + } | null>(() => getOrderByFromParams(searchParams, true)); const [selectedVolumeUID, setselectedVolumeUID] = useState( - null, + () => { + const volumeUID = searchParams.get( + INFRA_MONITORING_K8S_PARAMS_KEYS.VOLUME_UID, + ); + if (volumeUID) { + return volumeUID; + } + return null; + }, ); const { pageSize, setPageSize } = usePageSize(K8sCategory.VOLUMES); - const [groupBy, setGroupBy] = useState([]); + const [groupBy, setGroupBy] = useState(() => { + const groupBy = searchParams.get(INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY); + if (groupBy) { + const decoded = decodeURIComponent(groupBy); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['groupBy']; + } + return []; + }); const [ selectedRowData, @@ -253,15 +273,26 @@ function K8sVolumesList({ } if ('field' in sorter && sorter.order) { - setOrderBy({ + const currentOrderBy = { columnName: sorter.field as string, - order: sorter.order === 'ascend' ? 'asc' : 'desc', + order: (sorter.order === 'ascend' ? 'asc' : 'desc') as 'asc' | 'desc', + }; + setOrderBy(currentOrderBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify( + currentOrderBy, + ), }); } else { setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); } }, - [], + [searchParams, setSearchParams], ); const { handleChangeQueryData } = useQueryOperations({ @@ -315,6 +346,10 @@ function K8sVolumesList({ if (groupBy.length === 0) { setSelectedRowData(null); setselectedVolumeUID(record.volumeUID); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.VOLUME_UID]: record.volumeUID, + }); } else { handleGroupByRowClick(record); } @@ -341,6 +376,11 @@ function K8sVolumesList({ setSelectedRowData(null); setGroupBy([]); setOrderBy(null); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify([]), + [INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY]: JSON.stringify(null), + }); }; const expandedRowRender = (): JSX.Element => ( @@ -365,7 +405,9 @@ function K8sVolumesList({ }} showHeader={false} onRow={(record): { onClick: () => void; className: string } => ({ - onClick: (): void => setselectedVolumeUID(record.volumeUID), + onClick: (): void => { + setselectedVolumeUID(record.volumeUID); + }, className: 'expanded-clickable-row', })} /> @@ -429,6 +471,13 @@ function K8sVolumesList({ const handleCloseVolumeDetail = (): void => { setselectedVolumeUID(null); + setSearchParams({ + ...Object.fromEntries( + Array.from(searchParams.entries()).filter( + ([key]) => key !== INFRA_MONITORING_K8S_PARAMS_KEYS.VOLUME_UID, + ), + ), + }); }; const handleGroupByChange = useCallback( @@ -449,6 +498,10 @@ function K8sVolumesList({ setCurrentPage(1); setGroupBy(groupBy); + setSearchParams({ + ...Object.fromEntries(searchParams.entries()), + [INFRA_MONITORING_K8S_PARAMS_KEYS.GROUP_BY]: JSON.stringify(groupBy), + }); setExpandedRowKeys([]); logEvent(InfraMonitoringEvents.GroupByChanged, { @@ -457,7 +510,7 @@ function K8sVolumesList({ category: InfraMonitoringEvents.Volumes, }); }, - [groupByFiltersData], + [groupByFiltersData?.payload?.attributeKeys, searchParams, setSearchParams], ); useEffect(() => { diff --git a/frontend/src/container/InfraMonitoringK8s/commonUtils.tsx b/frontend/src/container/InfraMonitoringK8s/commonUtils.tsx index d7f9fd9b79..e61f64e5db 100644 --- a/frontend/src/container/InfraMonitoringK8s/commonUtils.tsx +++ b/frontend/src/container/InfraMonitoringK8s/commonUtils.tsx @@ -12,9 +12,16 @@ import { ResizeTable } from 'components/ResizeTable'; import FieldRenderer from 'container/LogDetailedView/FieldRenderer'; import { DataType } from 'container/LogDetailedView/TableView'; import { useMemo } from 'react'; -import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; +import { + IBuilderQuery, + TagFilterItem, +} from 'types/api/queryBuilder/queryBuilderData'; -import { getInvalidValueTooltipText, K8sCategory } from './constants'; +import { + getInvalidValueTooltipText, + INFRA_MONITORING_K8S_PARAMS_KEYS, + K8sCategory, +} from './constants'; /** * Converts size in bytes to a human-readable string with appropriate units @@ -250,3 +257,37 @@ export const filterDuplicateFilters = ( return uniqueFilters; }; + +export const getOrderByFromParams = ( + searchParams: URLSearchParams, + returnNullAsDefault = false, +): { + columnName: string; + order: 'asc' | 'desc'; +} | null => { + const orderByFromParams = searchParams.get( + INFRA_MONITORING_K8S_PARAMS_KEYS.ORDER_BY, + ); + if (orderByFromParams) { + const decoded = decodeURIComponent(orderByFromParams); + const parsed = JSON.parse(decoded); + return parsed as { columnName: string; order: 'asc' | 'desc' }; + } + if (returnNullAsDefault) { + return null; + } + return { columnName: 'cpu', order: 'desc' }; +}; + +export const getFiltersFromParams = ( + searchParams: URLSearchParams, + queryKey: string, +): IBuilderQuery['filters'] | null => { + const filtersFromParams = searchParams.get(queryKey); + if (filtersFromParams) { + const decoded = decodeURIComponent(filtersFromParams); + const parsed = JSON.parse(decoded); + return parsed as IBuilderQuery['filters']; + } + return null; +}; diff --git a/frontend/src/container/InfraMonitoringK8s/constants.ts b/frontend/src/container/InfraMonitoringK8s/constants.ts index 997daeaa77..a8bc29351f 100644 --- a/frontend/src/container/InfraMonitoringK8s/constants.ts +++ b/frontend/src/container/InfraMonitoringK8s/constants.ts @@ -518,3 +518,24 @@ export const getInvalidValueTooltipText = ( entity: K8sCategory, attribute: string, ): string => `Some ${entity} do not have ${attribute}s.`; + +export const INFRA_MONITORING_K8S_PARAMS_KEYS = { + CATEGORY: 'category', + VIEW: 'view', + CLUSTER_NAME: 'clusterName', + DAEMONSET_UID: 'daemonSetUID', + DEPLOYMENT_UID: 'deploymentUID', + JOB_UID: 'jobUID', + NAMESPACE_UID: 'namespaceUID', + NODE_UID: 'nodeUID', + POD_UID: 'podUID', + STATEFULSET_UID: 'statefulsetUID', + VOLUME_UID: 'volumeUID', + FILTERS: 'filters', + GROUP_BY: 'groupBy', + ORDER_BY: 'orderBy', + LOG_FILTERS: 'logFilters', + TRACES_FILTERS: 'tracesFilters', + EVENTS_FILTERS: 'eventsFilters', + HOSTS_FILTERS: 'hostsFilters', +};