diff --git a/src/components/Errors/ResponseError/ResponseError.tsx b/src/components/Errors/ResponseError/ResponseError.tsx
index d30cec98ad..ce9be261f9 100644
--- a/src/components/Errors/ResponseError/ResponseError.tsx
+++ b/src/components/Errors/ResponseError/ResponseError.tsx
@@ -1,8 +1,7 @@
-import type {IResponseError} from '../../../types/api/error';
import i18n from '../i18n';
interface ResponseErrorProps {
- error?: IResponseError;
+ error?: unknown;
className?: string;
defaultMessage?: string;
}
@@ -12,5 +11,13 @@ export const ResponseError = ({
className,
defaultMessage = i18n('responseError.defaultMessage'),
}: ResponseErrorProps) => {
- return
{error?.statusText || defaultMessage}
;
+ let statusText = '';
+ if (error && typeof error === 'object') {
+ if ('statusText' in error && typeof error.statusText === 'string') {
+ statusText = error.statusText;
+ } else if ('message' in error && typeof error.message === 'string') {
+ statusText = error.message;
+ }
+ }
+ return {statusText || defaultMessage}
;
};
diff --git a/src/containers/AppWithClusters/useClusterData.ts b/src/containers/AppWithClusters/useClusterData.ts
index e4c80aa09b..a7f994a636 100644
--- a/src/containers/AppWithClusters/useClusterData.ts
+++ b/src/containers/AppWithClusters/useClusterData.ts
@@ -1,28 +1,25 @@
+import React from 'react';
+
import {useLocation} from 'react-router';
import {parseQuery} from '../../routes';
-import {selectClusterInfo} from '../../store/reducers/clusters/selectors';
+import {clustersApi} from '../../store/reducers/clusters/clusters';
import {getAdditionalNodesProps} from '../../utils/additionalProps';
import {USE_CLUSTER_BALANCER_AS_BACKEND_KEY} from '../../utils/constants';
-import {useSetting, useTypedSelector} from '../../utils/hooks';
-import {useClustersList} from '../Clusters/useClustersList';
+import {useSetting} from '../../utils/hooks';
export function useClusterData() {
- useClustersList();
const location = useLocation();
+ const {clusterName} = parseQuery(location);
- const queryParams = parseQuery(location);
+ const {data} = clustersApi.useGetClustersListQuery(undefined);
- const {clusterName} = queryParams;
+ const info = React.useMemo(() => {
+ const clusters = data || [];
+ return clusters.find((cluster) => cluster.name === clusterName);
+ }, [data, clusterName]);
- const {
- solomon: monitoring,
- balancer,
- versions,
- cluster,
- } = useTypedSelector((state) =>
- selectClusterInfo(state, typeof clusterName === 'string' ? clusterName : ''),
- );
+ const {solomon: monitoring, balancer, versions, cluster} = info || {};
const [useClusterBalancerAsBackend] = useSetting(USE_CLUSTER_BALANCER_AS_BACKEND_KEY);
diff --git a/src/containers/Cluster/ClusterInfo/ClusterInfo.tsx b/src/containers/Cluster/ClusterInfo/ClusterInfo.tsx
index 2d9a7795fe..d4ed347270 100644
--- a/src/containers/Cluster/ClusterInfo/ClusterInfo.tsx
+++ b/src/containers/Cluster/ClusterInfo/ClusterInfo.tsx
@@ -123,11 +123,12 @@ const getInfo = (
}
if (cluster.SystemTablets) {
+ const tablets = cluster.SystemTablets.slice(0).sort(compareTablets);
info.push({
label: i18n('tablets'),
value: (
- {cluster.SystemTablets.sort(compareTablets).map((tablet, tabletIndex) => (
+ {tablets.map((tablet, tabletIndex) => (
))}
@@ -226,7 +227,7 @@ export const ClusterInfo = ({
}
if (error) {
- ;
+ return ;
}
return ;
diff --git a/src/containers/Clusters/Clusters.scss b/src/containers/Clusters/Clusters.scss
index 1188219d11..4fdef07530 100644
--- a/src/containers/Clusters/Clusters.scss
+++ b/src/containers/Clusters/Clusters.scss
@@ -1,12 +1,12 @@
-@import '../../styles/mixins.scss';
+@use '../../styles/mixins.scss';
.clusters {
overflow: auto;
padding-top: 15px;
- @include body-2-typography();
- @include flex-container();
+ @include mixins.body-2-typography();
+ @include mixins.flex-container();
&__cluster {
display: flex;
@@ -128,7 +128,7 @@
&__text {
color: var(--g-color-text-primary);
- @include body-2-typography();
+ @include mixins.body-2-typography();
&::first-letter {
color: var(--g-color-text-danger);
@@ -145,13 +145,13 @@
overflow: auto;
padding-left: 5px;
- @include flex-container();
+ @include mixins.flex-container();
}
&__table-content {
overflow: auto;
- @include freeze-nth-column(1);
+ @include mixins.freeze-nth-column(1);
}
&__balancer-cell {
display: flex;
@@ -173,4 +173,9 @@
display: flex;
align-items: center;
}
+
+ &__error {
+ margin-left: 15px;
+ @include mixins.body-2-typography();
+ }
}
diff --git a/src/containers/Clusters/Clusters.tsx b/src/containers/Clusters/Clusters.tsx
index cd9e45376b..6adfc8f860 100644
--- a/src/containers/Clusters/Clusters.tsx
+++ b/src/containers/Clusters/Clusters.tsx
@@ -4,22 +4,21 @@ import DataTable from '@gravity-ui/react-data-table';
import {Select, TableColumnSetup} from '@gravity-ui/uikit';
import {Helmet} from 'react-helmet-async';
+import {ResponseError} from '../../components/Errors/ResponseError';
import {Loader} from '../../components/Loader';
import {Search} from '../../components/Search';
-import {changeClustersFilters, fetchClustersList} from '../../store/reducers/clusters/clusters';
+import {changeClustersFilters, clustersApi} from '../../store/reducers/clusters/clusters';
import {
+ aggregateClustersInfo,
+ filterClusters,
selectClusterNameFilter,
- selectClustersAggregation,
- selectClustersList,
- selectFilteredClusters,
- selectLoadingFlag,
selectServiceFilter,
selectStatusFilter,
selectVersionFilter,
- selectVersions,
} from '../../store/reducers/clusters/selectors';
-import {DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
-import {useAutofetcher, useTypedDispatch, useTypedSelector} from '../../utils/hooks';
+import {DEFAULT_POLLING_INTERVAL, DEFAULT_TABLE_SETTINGS} from '../../utils/constants';
+import {useTypedDispatch, useTypedSelector} from '../../utils/hooks';
+import {getMinorVersion} from '../../utils/versions';
import {ClustersStatistics} from './ClustersStatistics';
import {CLUSTERS_COLUMNS} from './columns';
@@ -37,23 +36,16 @@ import {useSelectedColumns} from './useSelectedColumns';
import './Clusters.scss';
export function Clusters() {
+ const query = clustersApi.useGetClustersListQuery(undefined, {
+ pollingInterval: DEFAULT_POLLING_INTERVAL,
+ });
+
const dispatch = useTypedDispatch();
- const loading = useTypedSelector(selectLoadingFlag);
- const clusters = useTypedSelector(selectClustersList);
- const filteredClusters = useTypedSelector(selectFilteredClusters);
- const aggregation = useTypedSelector(selectClustersAggregation);
const clusterName = useTypedSelector(selectClusterNameFilter);
const status = useTypedSelector(selectStatusFilter);
const service = useTypedSelector(selectServiceFilter);
const version = useTypedSelector(selectVersionFilter);
- const versions = useTypedSelector(selectVersions);
-
- const fetchData = React.useCallback(() => {
- dispatch(fetchClustersList());
- }, [dispatch]);
-
- useAutofetcher(fetchData, [fetchData], true);
const changeStatus = (value: string[]) => {
dispatch(changeClustersFilters({status: value}));
@@ -76,24 +68,41 @@ export function Clusters() {
[COLUMNS_NAMES.TITLE],
);
- const servicesToSelect = React.useMemo(() => {
+ const clusters = query.data;
+
+ const {servicesToSelect, versions} = React.useMemo(() => {
const clustersServices = new Set();
+ const uniqVersions = new Set();
- clusters.forEach((cluster) => {
+ const clusterList = clusters ?? [];
+ clusterList.forEach((cluster) => {
if (cluster.service) {
clustersServices.add(cluster.service);
}
+ cluster.cluster?.Versions?.forEach((v) => {
+ uniqVersions.add(getMinorVersion(v));
+ });
});
- return Array.from(clustersServices).map((clusterService) => {
- return {
- value: clusterService,
- content: clusterService,
- };
- });
+ return {
+ servicesToSelect: Array.from(clustersServices).map((value) => ({
+ value,
+ content: value,
+ })),
+ versions: Array.from(uniqVersions).map((value) => ({value, content: value})),
+ };
}, [clusters]);
- if (loading && !clusters.length) {
+ const filteredClusters = React.useMemo(() => {
+ return filterClusters(clusters ?? [], {clusterName, status, service, version});
+ }, [clusterName, clusters, service, status, version]);
+
+ const aggregation = React.useMemo(
+ () => aggregateClustersInfo(filteredClusters),
+ [filteredClusters],
+ );
+
+ if (query.isLoading) {
return ;
}
@@ -162,6 +171,7 @@ export function Clusters() {
/>
+ {query.isError ? : null}
{
- dispatch(fetchClustersList());
- }, [dispatch]);
-}
diff --git a/src/services/api.ts b/src/services/api.ts
index 90f0e60a35..9a5e6b412d 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -513,8 +513,10 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
}
// used if not single cluster mode
- getClustersList() {
- return this.get(`${META_BACKEND || ''}/meta/clusters`, null);
+ getClustersList(_?: never, {signal}: {signal?: AbortSignal} = {}) {
+ return this.get(`${META_BACKEND || ''}/meta/clusters`, null, {
+ requestConfig: {signal},
+ });
}
}
diff --git a/src/store/configureStore.ts b/src/store/configureStore.ts
index 56188846a8..1efc00cec0 100644
--- a/src/store/configureStore.ts
+++ b/src/store/configureStore.ts
@@ -1,5 +1,5 @@
import {configureStore as configureReduxStore} from '@reduxjs/toolkit';
-import type {Action, Reducer, UnknownAction} from '@reduxjs/toolkit';
+import type {Action, Dispatch, Middleware, Reducer, UnknownAction} from '@reduxjs/toolkit';
import type {History} from 'history';
import {createBrowserHistory} from 'history';
import {listenForHistoryChange} from 'redux-location-state';
@@ -8,16 +8,18 @@ import {createApi} from '../services/api';
import {getUrlData} from './getUrlData';
import rootReducer from './reducers';
+import {api as storeApi} from './reducers/api';
import {UPDATE_REF} from './reducers/tooltip';
import getLocationMiddleware from './state-url-mapping';
export let backend: string | undefined, basename: string, clusterName: string | undefined;
-function _configureStore(
- aRootReducer: Reducer,
- history: History,
- preloadedState: P,
-) {
+function _configureStore<
+ S = any,
+ A extends Action = UnknownAction,
+ P = S,
+ M extends Middleware<{}, S, Dispatch> = any,
+>(aRootReducer: Reducer, history: History, preloadedState: P, middleware: M[]) {
const {locationMiddleware, reducersWithLocation} = getLocationMiddleware(history, aRootReducer);
const store = configureReduxStore({
@@ -30,7 +32,7 @@ function _configureStore(
ignoredPaths: ['tooltip.currentHoveredRef'],
ignoredActions: [UPDATE_REF],
},
- }).concat(locationMiddleware),
+ }).concat(locationMiddleware, ...middleware),
});
return store;
@@ -54,7 +56,9 @@ export function configureStore({
}));
const history = createBrowserHistory({basename});
- const store = _configureStore(aRootReducer, history, {singleClusterMode});
+ const store = _configureStore(aRootReducer, history, {singleClusterMode}, [
+ storeApi.middleware,
+ ]);
listenForHistoryChange(store, history);
// Interceptor to process OIDC auth
diff --git a/src/store/reducers/api.ts b/src/store/reducers/api.ts
new file mode 100644
index 0000000000..f335375e8f
--- /dev/null
+++ b/src/store/reducers/api.ts
@@ -0,0 +1,28 @@
+import type {BaseQueryFn} from '@reduxjs/toolkit/query';
+import {createApi} from '@reduxjs/toolkit/query/react';
+
+export const api = createApi({
+ baseQuery: fakeBaseQuery(),
+ /**
+ * This api has endpoints injected in adjacent files,
+ * which is why no endpoints are shown below.
+ */
+ endpoints: () => ({}),
+});
+
+export const _NEVER = Symbol();
+type NEVER = typeof _NEVER;
+
+/**
+ * Creates a "fake" baseQuery to be used if your api *only* uses the `queryFn` definition syntax.
+ * This also allows you to specify a specific error type to be shared by all your `queryFn` definitions.
+ *
+ * Can't use fakeBaseQuery from @reduxjs/toolkit/query, because of error
+ */
+function fakeBaseQuery(): BaseQueryFn {
+ return function () {
+ throw new Error(
+ 'When using `fakeBaseQuery`, all queries & mutations must use the `queryFn` definition syntax.',
+ );
+ };
+}
diff --git a/src/store/reducers/clusters/clusters.ts b/src/store/reducers/clusters/clusters.ts
index 302e92ddc2..109c7e67c0 100644
--- a/src/store/reducers/clusters/clusters.ts
+++ b/src/store/reducers/clusters/clusters.ts
@@ -1,71 +1,43 @@
-import type {Reducer} from '@reduxjs/toolkit';
+import {createSlice} from '@reduxjs/toolkit';
+import type {PayloadAction} from '@reduxjs/toolkit';
-import {createApiRequest, createRequestActionTypes} from '../../utils';
+import {api} from '../api';
-import type {ClustersAction, ClustersFilters, ClustersState} from './types';
+import type {ClustersFilters} from './types';
import {prepareClustersData} from './utils';
-export const FETCH_CLUSTERS = createRequestActionTypes('clusters', 'FETCH_CLUSTERS');
-const CHANGE_FILTERS = 'clusters/CHANGE_FILTER';
-
-const initialState: ClustersState = {
- loading: false,
- list: [],
+const initialState: ClustersFilters = {
clusterName: '',
status: [],
service: [],
version: [],
};
-const clusters: Reducer = (state = initialState, action) => {
- switch (action.type) {
- case FETCH_CLUSTERS.REQUEST: {
- return {
- ...state,
- loading: true,
- };
- }
- case FETCH_CLUSTERS.SUCCESS: {
- const {data = []} = action;
-
- return {
- ...state,
- loading: false,
- list: data,
- error: undefined,
- };
- }
- case FETCH_CLUSTERS.FAILURE: {
- return {
- ...state,
- error: action.error,
- loading: false,
- };
- }
- case CHANGE_FILTERS: {
- return {
- ...state,
- ...action.data,
- };
- }
- default:
- return state;
- }
-};
-
-export function fetchClustersList() {
- return createApiRequest({
- request: window.api.getClustersList(),
- actions: FETCH_CLUSTERS,
- dataHandler: prepareClustersData,
- });
-}
-
-export function changeClustersFilters(filters: Partial) {
- return {
- type: CHANGE_FILTERS,
- data: filters,
- } as const;
-}
-
-export default clusters;
+const slice = createSlice({
+ name: 'clusters',
+ initialState,
+ reducers: {
+ changeClustersFilters: (state, action: PayloadAction>) => ({
+ ...state,
+ ...action.payload,
+ }),
+ },
+});
+
+export default slice.reducer;
+export const {changeClustersFilters} = slice.actions;
+
+export const clustersApi = api.injectEndpoints({
+ endpoints: (builder) => ({
+ getClustersList: builder.query({
+ queryFn: async (_, {signal}) => {
+ try {
+ const data = await window.api.getClustersList(undefined, {signal});
+ return {data: prepareClustersData(data)};
+ } catch (error) {
+ return {error};
+ }
+ },
+ }),
+ }),
+});
diff --git a/src/store/reducers/clusters/selectors.ts b/src/store/reducers/clusters/selectors.ts
index 92b55daf53..d6d42cee77 100644
--- a/src/store/reducers/clusters/selectors.ts
+++ b/src/store/reducers/clusters/selectors.ts
@@ -1,11 +1,18 @@
-import {createSelector} from '@reduxjs/toolkit';
-import type {Selector} from '@reduxjs/toolkit';
import escapeRegExp from 'lodash/escapeRegExp';
-import type {MetaExtendedClusterInfo} from '../../../types/api/meta';
-import {getMinorVersion} from '../../../utils/versions';
+import type {
+ ClusterDataAggregation,
+ ClustersFilters,
+ ClustersStateSlice,
+ PreparedCluster,
+} from './types';
-import type {ClusterDataAggregation, ClustersStateSlice, PreparedCluster} from './types';
+// ==== Simple selectors ====
+
+export const selectClusterNameFilter = (state: ClustersStateSlice) => state.clusters.clusterName;
+export const selectStatusFilter = (state: ClustersStateSlice) => state.clusters.status;
+export const selectServiceFilter = (state: ClustersStateSlice) => state.clusters.service;
+export const selectVersionFilter = (state: ClustersStateSlice) => state.clusters.version;
// ==== Filters ====
@@ -66,100 +73,48 @@ const isMatchesByTextQuery = (clusterData: PreparedCluster, searchQuery = '') =>
return filteredByName || filteredByVersion || filteredByHost;
};
-// ==== Simple selectors ====
-
-export const selectLoadingFlag = (state: ClustersStateSlice) => state.clusters.loading;
-export const selectClustersList = (state: ClustersStateSlice) => state.clusters.list;
-export const selectClusterNameFilter = (state: ClustersStateSlice) => state.clusters.clusterName;
-export const selectStatusFilter = (state: ClustersStateSlice) => state.clusters.status;
-export const selectServiceFilter = (state: ClustersStateSlice) => state.clusters.service;
-export const selectVersionFilter = (state: ClustersStateSlice) => state.clusters.version;
-
-// ==== Complex selectors ====
-
-export const selectVersions: Selector =
- createSelector(selectClustersList, (clusters) => {
- const uniqVersions = new Set();
-
- clusters
- .map(({cluster}) => cluster?.Versions)
- .forEach((clusterVersions) =>
- clusterVersions?.forEach((version) => {
- uniqVersions.add(getMinorVersion(version));
- }),
- );
-
- return Array.from(uniqVersions).map((version) => ({
- value: version,
- content: version,
- }));
+export function filterClusters(clusters: PreparedCluster[], filters: ClustersFilters) {
+ return clusters.filter((cluster) => {
+ return (
+ isMatchesByStatus(cluster, filters.status) &&
+ isMatchesByService(cluster, filters.service) &&
+ isMatchesByVersion(cluster, filters.version) &&
+ isMatchesByTextQuery(cluster, filters.clusterName)
+ );
});
-
-export const selectFilteredClusters: Selector =
- createSelector(
- [
- selectClustersList,
- selectClusterNameFilter,
- selectStatusFilter,
- selectServiceFilter,
- selectVersionFilter,
- ],
- (clusters, textSearchQuery, selectedStatuses, selectedServices, selectedVersions) => {
- return clusters.filter((cluster) => {
- return (
- isMatchesByStatus(cluster, selectedStatuses) &&
- isMatchesByService(cluster, selectedServices) &&
- isMatchesByVersion(cluster, selectedVersions) &&
- isMatchesByTextQuery(cluster, textSearchQuery)
- );
- });
- },
- );
-
-export const selectClustersAggregation: Selector =
- createSelector(selectFilteredClusters, (clusters) => {
- let NodesTotal = 0,
- NodesAlive = 0,
- LoadAverage = 0,
- NumberOfCpus = 0,
- StorageUsed = 0,
- StorageTotal = 0,
- Tenants = 0;
- const Hosts = new Set();
-
- const filteredClusters = clusters.filter(({cluster}) => !cluster?.error);
-
- filteredClusters.forEach(({cluster, hosts = {}}) => {
- NodesTotal += cluster?.NodesTotal || 0;
- NodesAlive += cluster?.NodesAlive || 0;
- Object.keys(hosts).forEach((host) => Hosts.add(host));
- Tenants += Number(cluster?.Tenants) || 0;
- LoadAverage += Number(cluster?.LoadAverage) || 0;
- NumberOfCpus += cluster?.NumberOfCpus || 0;
- StorageUsed += cluster?.StorageUsed ? Math.floor(parseInt(cluster.StorageUsed, 10)) : 0;
- StorageTotal += cluster?.StorageTotal
- ? Math.floor(parseInt(cluster.StorageTotal, 10))
- : 0;
- });
-
- return {
- NodesTotal,
- NodesAlive,
- Hosts: Hosts.size,
- Tenants,
- LoadAverage,
- NumberOfCpus,
- StorageUsed,
- StorageTotal,
- };
+}
+
+export function aggregateClustersInfo(clusters: PreparedCluster[]): ClusterDataAggregation {
+ let NodesTotal = 0,
+ NodesAlive = 0,
+ LoadAverage = 0,
+ NumberOfCpus = 0,
+ StorageUsed = 0,
+ StorageTotal = 0,
+ Tenants = 0;
+ const Hosts = new Set();
+
+ const filteredClusters = clusters.filter(({cluster}) => !cluster?.error);
+
+ filteredClusters.forEach(({cluster, hosts = {}}) => {
+ NodesTotal += cluster?.NodesTotal || 0;
+ NodesAlive += cluster?.NodesAlive || 0;
+ Object.keys(hosts).forEach((host) => Hosts.add(host));
+ Tenants += Number(cluster?.Tenants) || 0;
+ LoadAverage += Number(cluster?.LoadAverage) || 0;
+ NumberOfCpus += cluster?.NumberOfCpus || 0;
+ StorageUsed += cluster?.StorageUsed ? Math.floor(parseInt(cluster.StorageUsed, 10)) : 0;
+ StorageTotal += cluster?.StorageTotal ? Math.floor(parseInt(cluster.StorageTotal, 10)) : 0;
});
-export const selectClusterInfo = createSelector(
- selectClustersList,
- (_: unknown, clusterName: string) => clusterName,
- (clusters, clusterName) => {
- const info: MetaExtendedClusterInfo =
- clusters.filter((item) => item.name === clusterName)[0] || {};
- return info;
- },
-);
+ return {
+ NodesTotal,
+ NodesAlive,
+ Hosts: Hosts.size,
+ Tenants,
+ LoadAverage,
+ NumberOfCpus,
+ StorageUsed,
+ StorageTotal,
+ };
+}
diff --git a/src/store/reducers/clusters/types.ts b/src/store/reducers/clusters/types.ts
index 0f027e8d3e..058215aef7 100644
--- a/src/store/reducers/clusters/types.ts
+++ b/src/store/reducers/clusters/types.ts
@@ -1,8 +1,5 @@
import type {MetaExtendedClusterInfo} from '../../../types/api/meta';
import type {ExtendedMetaClusterVersion} from '../../../utils/clusterVersionColors';
-import type {ApiRequestAction} from '../../utils';
-
-import type {FETCH_CLUSTERS, changeClustersFilters} from './clusters';
export interface PreparedCluster extends MetaExtendedClusterInfo {
preparedVersions: ExtendedMetaClusterVersion[];
@@ -26,17 +23,6 @@ export interface ClustersFilters {
clusterName: string;
}
-export interface ClustersState extends ClustersFilters {
- loading: boolean;
- error?: unknown;
-
- list: PreparedCluster[];
-}
-
-export type ClustersAction =
- | ApiRequestAction
- | ReturnType;
-
export interface ClustersStateSlice {
- clusters: ClustersState;
+ clusters: ClustersFilters;
}
diff --git a/src/store/reducers/index.ts b/src/store/reducers/index.ts
index 1b9157396b..cccd7677a7 100644
--- a/src/store/reducers/index.ts
+++ b/src/store/reducers/index.ts
@@ -1,5 +1,6 @@
import {combineReducers} from '@reduxjs/toolkit';
+import {api} from './api';
import authentication from './authentication/authentication';
import cluster from './cluster/cluster';
import clusterNodes from './clusterNodes/clusterNodes';
@@ -47,6 +48,7 @@ import topic from './topic';
import vDisk from './vdisk/vdisk';
export const rootReducer = {
+ [api.reducerPath]: api.reducer,
singleClusterMode,
nodes,
topNodesByLoad,
diff --git a/src/types/redux-location-state.d.ts b/src/types/redux-location-state.d.ts
index 29d6fb4151..84d85e6e26 100644
--- a/src/types/redux-location-state.d.ts
+++ b/src/types/redux-location-state.d.ts
@@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-duplicate-imports */
declare module 'redux-location-state' {
- import type {Middleware, Reducer, Store} from '@reduxjs/toolkit';
+ import type {Dispatch, Middleware, PayloadAction, Reducer, Store} from '@reduxjs/toolkit';
import type {History, Location} from 'history';
+ import type {LOCATION_POP, LOCATION_PUSH} from 'redux-location-state/lib/constants';
export function listenForHistoryChange(store: Store, history: History): void;
@@ -37,7 +38,14 @@ declare module 'redux-location-state' {
location: Location,
) => {location: Location; shouldPush: boolean},
): {
- locationMiddleware: Middleware;
+ locationMiddleware: Middleware<
+ {},
+ S,
+ Dispatch<
+ | PayloadAction
+ | PayloadAction
+ >
+ >;
};
}
diff --git a/src/utils/constants.ts b/src/utils/constants.ts
index 64182967ba..7be1ab6899 100644
--- a/src/utils/constants.ts
+++ b/src/utils/constants.ts
@@ -5,6 +5,7 @@ import {EType} from '../types/api/tablet';
const SECOND = 1000;
export const AUTO_RELOAD_INTERVAL = 10 * SECOND;
+export const DEFAULT_POLLING_INTERVAL = 30 * SECOND;
// by agreement, display all byte values in decimal scale
// values in data are always in bytes, never in higher units,
// therefore there is no issue arbitrary converting them in UI