mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-06-22 01:31:40 +08:00
fix: url params should not propagate across pages (#5417)
* fix: dashboards list url query params isolation * feat: order query param old logs explorer isolation * feat: added extra checks in place * fix: refactor the dashboards list page for better performance * chore: add test cases for the dashboards list page * fix: added test cases for dashboards list page * fix: added code comments * fix: added empty state for dashboards and no search state
This commit is contained in:
parent
d3b83f5a41
commit
adfe20e88a
@ -73,7 +73,6 @@ import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { isCloudUser } from 'utils/app';
|
||||
|
||||
import useUrlQuery from '../../hooks/useUrlQuery';
|
||||
import DashboardTemplatesModal from './DashboardTemplates/DashboardTemplatesModal';
|
||||
import ImportJSON from './ImportJSON';
|
||||
import { DeleteButton } from './TableComponents/DeleteButton';
|
||||
@ -86,7 +85,7 @@ import {
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function DashboardsList(): JSX.Element {
|
||||
const {
|
||||
data: dashboardListResponse = [],
|
||||
data: dashboardListResponse,
|
||||
isLoading: isDashboardListLoading,
|
||||
error: dashboardFetchError,
|
||||
refetch: refetchDashboardList,
|
||||
@ -99,12 +98,14 @@ function DashboardsList(): JSX.Element {
|
||||
setListSortOrder: setSortOrder,
|
||||
} = useDashboard();
|
||||
|
||||
const [searchString, setSearchString] = useState<string>(
|
||||
sortOrder.search || '',
|
||||
);
|
||||
const [action, createNewDashboard] = useComponentPermission(
|
||||
['action', 'create_new_dashboards'],
|
||||
role,
|
||||
);
|
||||
|
||||
const [searchValue, setSearchValue] = useState<string>('');
|
||||
const [
|
||||
showNewDashboardTemplatesModal,
|
||||
setShowNewDashboardTemplatesModal,
|
||||
@ -123,10 +124,6 @@ function DashboardsList(): JSX.Element {
|
||||
false,
|
||||
);
|
||||
|
||||
const params = useUrlQuery();
|
||||
const searchParams = params.get('search');
|
||||
const [searchString, setSearchString] = useState<string>(searchParams || '');
|
||||
|
||||
const getLocalStorageDynamicColumns = (): DashboardDynamicColumns => {
|
||||
const dashboardDynamicColumnsString = localStorage.getItem('dashboard');
|
||||
let dashboardDynamicColumns: DashboardDynamicColumns = {
|
||||
@ -188,14 +185,6 @@ function DashboardsList(): JSX.Element {
|
||||
setDashboards(sortedDashboards);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
params.set('columnKey', sortOrder.columnKey as string);
|
||||
params.set('order', sortOrder.order as string);
|
||||
params.set('page', sortOrder.pagination || '1');
|
||||
history.replace({ search: params.toString() });
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [sortOrder]);
|
||||
|
||||
const sortHandle = (key: string): void => {
|
||||
if (!dashboards) return;
|
||||
if (key === 'createdAt') {
|
||||
@ -204,6 +193,7 @@ function DashboardsList(): JSX.Element {
|
||||
columnKey: 'createdAt',
|
||||
order: 'descend',
|
||||
pagination: sortOrder.pagination || '1',
|
||||
search: sortOrder.search || '',
|
||||
});
|
||||
} else if (key === 'updatedAt') {
|
||||
sortDashboardsByUpdatedAt(dashboards);
|
||||
@ -211,21 +201,19 @@ function DashboardsList(): JSX.Element {
|
||||
columnKey: 'updatedAt',
|
||||
order: 'descend',
|
||||
pagination: sortOrder.pagination || '1',
|
||||
search: sortOrder.search || '',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function handlePageSizeUpdate(page: number): void {
|
||||
setSortOrder((order) => ({
|
||||
...order,
|
||||
pagination: String(page),
|
||||
}));
|
||||
setSortOrder({ ...sortOrder, pagination: String(page) });
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const filteredDashboards = filterDashboard(
|
||||
searchString,
|
||||
dashboardListResponse,
|
||||
dashboardListResponse || [],
|
||||
);
|
||||
if (sortOrder.columnKey === 'updatedAt') {
|
||||
sortDashboardsByUpdatedAt(filteredDashboards || []);
|
||||
@ -236,6 +224,7 @@ function DashboardsList(): JSX.Element {
|
||||
columnKey: 'updatedAt',
|
||||
order: 'descend',
|
||||
pagination: sortOrder.pagination || '1',
|
||||
search: sortOrder.search || '',
|
||||
});
|
||||
sortDashboardsByUpdatedAt(filteredDashboards || []);
|
||||
}
|
||||
@ -245,6 +234,7 @@ function DashboardsList(): JSX.Element {
|
||||
setSortOrder,
|
||||
sortOrder.columnKey,
|
||||
sortOrder.pagination,
|
||||
sortOrder.search,
|
||||
]);
|
||||
|
||||
const [newDashboardState, setNewDashboardState] = useState({
|
||||
@ -316,12 +306,15 @@ function DashboardsList(): JSX.Element {
|
||||
|
||||
const handleSearch = (event: ChangeEvent<HTMLInputElement>): void => {
|
||||
setIsFilteringDashboards(true);
|
||||
setSearchValue(event.target.value);
|
||||
const searchText = (event as React.BaseSyntheticEvent)?.target?.value || '';
|
||||
const filteredDashboards = filterDashboard(searchText, dashboardListResponse);
|
||||
const filteredDashboards = filterDashboard(
|
||||
searchText,
|
||||
dashboardListResponse || [],
|
||||
);
|
||||
setDashboards(filteredDashboards);
|
||||
setIsFilteringDashboards(false);
|
||||
setSearchString(searchText);
|
||||
setSortOrder({ ...sortOrder, search: searchText });
|
||||
};
|
||||
|
||||
const [state, setCopy] = useCopyToClipboard();
|
||||
@ -412,7 +405,7 @@ function DashboardsList(): JSX.Element {
|
||||
{
|
||||
title: 'Dashboards',
|
||||
key: 'dashboard',
|
||||
render: (dashboard: Data): JSX.Element => {
|
||||
render: (dashboard: Data, _, index): JSX.Element => {
|
||||
const timeOptions: Intl.DateTimeFormatOptions = {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
@ -461,7 +454,9 @@ function DashboardsList(): JSX.Element {
|
||||
style={{ height: '14px', width: '14px' }}
|
||||
alt="dashboard-image"
|
||||
/>
|
||||
<Typography.Text>{dashboard.name}</Typography.Text>
|
||||
<Typography.Text data-testid={`dashboard-title-${index}`}>
|
||||
{dashboard.name}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
|
||||
<div className="tags-with-actions">
|
||||
@ -701,7 +696,7 @@ function DashboardsList(): JSX.Element {
|
||||
<ArrowUpRight size={16} className="learn-more-arrow" />
|
||||
</section>
|
||||
</div>
|
||||
) : dashboards?.length === 0 && !searchValue ? (
|
||||
) : dashboards?.length === 0 && !searchString ? (
|
||||
<div className="dashboard-empty-state">
|
||||
<img
|
||||
src="/Icons/dashboards.svg"
|
||||
@ -739,6 +734,7 @@ function DashboardsList(): JSX.Element {
|
||||
<Button
|
||||
type="text"
|
||||
className="learn-more"
|
||||
data-testid="learn-more"
|
||||
onClick={(): void => {
|
||||
window.open(
|
||||
'https://signoz.io/docs/userguide/manage-dashboards?utm_source=product&utm_medium=dashboard-list-empty-state',
|
||||
@ -758,7 +754,7 @@ function DashboardsList(): JSX.Element {
|
||||
<Input
|
||||
placeholder="Search by name, description, or tags..."
|
||||
prefix={<Search size={12} color={Color.BG_VANILLA_400} />}
|
||||
value={searchValue}
|
||||
value={searchString}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
{createNewDashboard && (
|
||||
@ -786,7 +782,7 @@ function DashboardsList(): JSX.Element {
|
||||
<div className="no-search">
|
||||
<img src="/Icons/emptyState.svg" alt="img" className="img" />
|
||||
<Typography.Text className="text">
|
||||
No dashboards found for {searchValue}. Create a new dashboard?
|
||||
No dashboards found for {searchString}. Create a new dashboard?
|
||||
</Typography.Text>
|
||||
</div>
|
||||
) : (
|
||||
@ -808,6 +804,7 @@ function DashboardsList(): JSX.Element {
|
||||
type="text"
|
||||
className={cx('sort-btns')}
|
||||
onClick={(): void => sortHandle('createdAt')}
|
||||
data-testid="sort-by-last-created"
|
||||
>
|
||||
Last created
|
||||
{sortOrder.columnKey === 'createdAt' && <Check size={14} />}
|
||||
@ -816,6 +813,7 @@ function DashboardsList(): JSX.Element {
|
||||
type="text"
|
||||
className={cx('sort-btns')}
|
||||
onClick={(): void => sortHandle('updatedAt')}
|
||||
data-testid="sort-by-last-updated"
|
||||
>
|
||||
Last updated
|
||||
{sortOrder.columnKey === 'updatedAt' && <Check size={14} />}
|
||||
@ -826,7 +824,7 @@ function DashboardsList(): JSX.Element {
|
||||
placement="bottomRight"
|
||||
arrow={false}
|
||||
>
|
||||
<ArrowDownWideNarrow size={14} />
|
||||
<ArrowDownWideNarrow size={14} data-testid="sort-by" />
|
||||
</Popover>
|
||||
</Tooltip>
|
||||
<Popover
|
||||
|
@ -266,6 +266,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
||||
urlQuery.set('columnKey', listSortOrder.columnKey as string);
|
||||
urlQuery.set('order', listSortOrder.order as string);
|
||||
urlQuery.set('page', listSortOrder.pagination as string);
|
||||
urlQuery.set('search', listSortOrder.search as string);
|
||||
urlQuery.delete(QueryParams.relativeTime);
|
||||
|
||||
const generatedUrl = `${ROUTES.ALL_DASHBOARD}?${urlQuery.toString()}`;
|
||||
|
50
frontend/src/mocks-server/__mockdata__/dashboards.ts
Normal file
50
frontend/src/mocks-server/__mockdata__/dashboards.ts
Normal file
@ -0,0 +1,50 @@
|
||||
export const dashboardSuccessResponse = {
|
||||
status: 'success',
|
||||
data: [
|
||||
{
|
||||
id: 1,
|
||||
uuid: '1',
|
||||
created_at: '2022-11-16T13:29:47.064874419Z',
|
||||
created_by: null,
|
||||
updated_at: '2024-05-21T06:41:30.546630961Z',
|
||||
updated_by: 'thor@avengers.io',
|
||||
isLocked: 0,
|
||||
data: {
|
||||
collapsableRowsMigrated: true,
|
||||
description: '',
|
||||
name: '',
|
||||
panelMap: {},
|
||||
tags: ['linux'],
|
||||
title: 'thor',
|
||||
uploadedGrafana: false,
|
||||
uuid: '',
|
||||
version: '',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
uuid: '2',
|
||||
created_at: '2022-11-16T13:20:47.064874419Z',
|
||||
created_by: null,
|
||||
updated_at: '2024-05-21T06:42:30.546630961Z',
|
||||
updated_by: 'captain-america@avengers.io',
|
||||
isLocked: 0,
|
||||
data: {
|
||||
collapsableRowsMigrated: true,
|
||||
description: '',
|
||||
name: '',
|
||||
panelMap: {},
|
||||
tags: ['linux'],
|
||||
title: 'captain america',
|
||||
uploadedGrafana: false,
|
||||
uuid: '',
|
||||
version: '',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const dashboardEmptyState = {
|
||||
status: 'sucsess',
|
||||
data: [],
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
import { rest } from 'msw';
|
||||
|
||||
import { billingSuccessResponse } from './__mockdata__/billing';
|
||||
import { dashboardSuccessResponse } from './__mockdata__/dashboards';
|
||||
import { inviteUser } from './__mockdata__/invite_user';
|
||||
import { licensesSuccessResponse } from './__mockdata__/licenses';
|
||||
import { membersResponse } from './__mockdata__/members';
|
||||
@ -91,6 +92,10 @@ export const handlers = [
|
||||
res(ctx.status(200), ctx.json(billingSuccessResponse)),
|
||||
),
|
||||
|
||||
rest.get('http://localhost/api/v1/dashboards', (_, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(dashboardSuccessResponse)),
|
||||
),
|
||||
|
||||
rest.get('http://localhost/api/v1/invite', (_, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(inviteUser)),
|
||||
),
|
||||
|
@ -0,0 +1,207 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import ROUTES from 'constants/routes';
|
||||
import DashboardsList from 'container/ListOfDashboard';
|
||||
import { dashboardEmptyState } from 'mocks-server/__mockdata__/dashboards';
|
||||
import { server } from 'mocks-server/server';
|
||||
import { rest } from 'msw';
|
||||
import { DashboardProvider } from 'providers/Dashboard/Dashboard';
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||
import { fireEvent, render, waitFor } from 'tests/test-utils';
|
||||
|
||||
jest.mock('react-router-dom', () => ({
|
||||
...jest.requireActual('react-router-dom'),
|
||||
useLocation: jest.fn(),
|
||||
useRouteMatch: jest.fn().mockReturnValue({
|
||||
params: {
|
||||
dashboardId: 4,
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
const mockWindowOpen = jest.fn();
|
||||
window.open = mockWindowOpen;
|
||||
|
||||
describe('dashboard list page', () => {
|
||||
// should render on updatedAt and descend when the column key and order is messed up
|
||||
it('should render the list even when the columnKey or the order is mismatched', async () => {
|
||||
const mockLocation = {
|
||||
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`,
|
||||
search: `columnKey=asgard&order=stones&page=1`,
|
||||
};
|
||||
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
||||
const { getByText, getByTestId } = render(
|
||||
<MemoryRouter
|
||||
initialEntries={['/dashbords?columnKey=asgard&order=stones&page=1']}
|
||||
>
|
||||
<DashboardProvider>
|
||||
<DashboardsList />
|
||||
</DashboardProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(getByText('All Dashboards')).toBeInTheDocument());
|
||||
const firstElement = getByTestId('dashboard-title-0');
|
||||
expect(firstElement.textContent).toBe('captain america');
|
||||
const secondElement = getByTestId('dashboard-title-1');
|
||||
expect(secondElement.textContent).toBe('thor');
|
||||
});
|
||||
|
||||
// should render correctly when the column key is createdAt and order is descend
|
||||
it('should render the list even when the columnKey and the order are given', async () => {
|
||||
const mockLocation = {
|
||||
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`,
|
||||
search: `columnKey=createdAt&order=descend&page=1`,
|
||||
};
|
||||
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
||||
const { getByText, getByTestId } = render(
|
||||
<MemoryRouter
|
||||
initialEntries={['/dashbords?columnKey=createdAt&order=descend&page=1']}
|
||||
>
|
||||
<DashboardProvider>
|
||||
<DashboardsList />
|
||||
</DashboardProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(getByText('All Dashboards')).toBeInTheDocument());
|
||||
const firstElement = getByTestId('dashboard-title-0');
|
||||
expect(firstElement.textContent).toBe('thor');
|
||||
const secondElement = getByTestId('dashboard-title-1');
|
||||
expect(secondElement.textContent).toBe('captain america');
|
||||
});
|
||||
|
||||
// change the sort by order and dashboards list ot be updated accordingly
|
||||
it('dashboards list should be correctly updated on choosing the different sortBy from dropdown values', async () => {
|
||||
const { getByText, getByTestId } = render(
|
||||
<MemoryRouter
|
||||
initialEntries={[
|
||||
'/dashbords?columnKey=createdAt&order=descend&page=1&search=tho',
|
||||
]}
|
||||
>
|
||||
<DashboardProvider>
|
||||
<DashboardsList />
|
||||
</DashboardProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(getByText('All Dashboards')).toBeInTheDocument());
|
||||
|
||||
const firstElement = getByTestId('dashboard-title-0');
|
||||
expect(firstElement.textContent).toBe('thor');
|
||||
const secondElement = getByTestId('dashboard-title-1');
|
||||
expect(secondElement.textContent).toBe('captain america');
|
||||
|
||||
// click on the sort button
|
||||
const sortByButton = getByTestId('sort-by');
|
||||
expect(sortByButton).toBeInTheDocument();
|
||||
fireEvent.click(sortByButton!);
|
||||
|
||||
// change the sort order
|
||||
const sortByUpdatedBy = getByTestId('sort-by-last-updated');
|
||||
await waitFor(() => expect(sortByUpdatedBy).toBeInTheDocument());
|
||||
fireEvent.click(sortByUpdatedBy!);
|
||||
|
||||
// expect the new order
|
||||
const updatedFirstElement = getByTestId('dashboard-title-0');
|
||||
expect(updatedFirstElement.textContent).toBe('captain america');
|
||||
const updatedSecondElement = getByTestId('dashboard-title-1');
|
||||
expect(updatedSecondElement.textContent).toBe('thor');
|
||||
});
|
||||
|
||||
// should filter correctly on search string
|
||||
it('should filter dashboards based on search string', async () => {
|
||||
const mockLocation = {
|
||||
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`,
|
||||
search: `columnKey=createdAt&order=descend&page=1&search=tho`,
|
||||
};
|
||||
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
||||
const { getByText, getByTestId, queryByText } = render(
|
||||
<MemoryRouter
|
||||
initialEntries={[
|
||||
'/dashbords?columnKey=createdAt&order=descend&page=1&search=tho',
|
||||
]}
|
||||
>
|
||||
<DashboardProvider>
|
||||
<DashboardsList />
|
||||
</DashboardProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
await waitFor(() => expect(getByText('All Dashboards')).toBeInTheDocument());
|
||||
const firstElement = getByTestId('dashboard-title-0');
|
||||
expect(firstElement.textContent).toBe('thor');
|
||||
expect(queryByText('captain america')).not.toBeInTheDocument();
|
||||
|
||||
// the pagination item should not be present in the list when number of items are less than one page size
|
||||
expect(
|
||||
document.querySelector('.ant-table-pagination'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('dashboard empty search state', async () => {
|
||||
const mockLocation = {
|
||||
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`,
|
||||
search: `columnKey=createdAt&order=descend&page=1&search=someRandomString`,
|
||||
};
|
||||
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
||||
const { getByText } = render(
|
||||
<MemoryRouter
|
||||
initialEntries={[
|
||||
'/dashbords?columnKey=createdAt&order=descend&page=1&search=tho',
|
||||
]}
|
||||
>
|
||||
<DashboardProvider>
|
||||
<DashboardsList />
|
||||
</DashboardProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
getByText(
|
||||
'No dashboards found for someRandomString. Create a new dashboard?',
|
||||
),
|
||||
).toBeInTheDocument(),
|
||||
);
|
||||
});
|
||||
|
||||
it('dashboard empty state', async () => {
|
||||
const mockLocation = {
|
||||
pathname: `${process.env.FRONTEND_API_ENDPOINT}/${ROUTES.ALL_DASHBOARD}/`,
|
||||
search: `columnKey=createdAt&order=descend&page=1`,
|
||||
};
|
||||
(useLocation as jest.Mock).mockReturnValue(mockLocation);
|
||||
server.use(
|
||||
rest.get('http://localhost/api/v1/dashboards', (_, res, ctx) =>
|
||||
res(ctx.status(200), ctx.json(dashboardEmptyState)),
|
||||
),
|
||||
);
|
||||
const { getByText, getByTestId } = render(
|
||||
<MemoryRouter
|
||||
initialEntries={[
|
||||
'/dashbords?columnKey=createdAt&order=descend&page=1&search=tho',
|
||||
]}
|
||||
>
|
||||
<DashboardProvider>
|
||||
<DashboardsList />
|
||||
</DashboardProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(getByText('No dashboards yet.')).toBeInTheDocument(),
|
||||
);
|
||||
|
||||
const learnMoreButton = getByTestId('learn-more');
|
||||
expect(learnMoreButton).toBeInTheDocument();
|
||||
fireEvent.click(learnMoreButton);
|
||||
|
||||
// test the correct link to be added for the dashboards empty state
|
||||
await waitFor(() =>
|
||||
expect(mockWindowOpen).toHaveBeenCalledWith(
|
||||
'https://signoz.io/docs/userguide/manage-dashboards?utm_source=product&utm_medium=dashboard-list-empty-state',
|
||||
'_blank',
|
||||
),
|
||||
);
|
||||
});
|
||||
});
|
@ -1,3 +1,4 @@
|
||||
/* eslint-disable no-nested-ternary */
|
||||
import { Modal } from 'antd';
|
||||
import getDashboard from 'api/dashboard/get';
|
||||
import lockDashboardApi from 'api/dashboard/lockDashboard';
|
||||
@ -11,6 +12,7 @@ import useAxiosError from 'hooks/useAxiosError';
|
||||
import useTabVisibility from 'hooks/useTabFocus';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { getUpdatedLayout } from 'lib/dashboard/getUpdatedLayout';
|
||||
import history from 'lib/history';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import isUndefined from 'lodash-es/isUndefined';
|
||||
@ -38,7 +40,7 @@ import AppReducer from 'types/reducer/app';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
import { IDashboardContext } from './types';
|
||||
import { DashboardSortOrder, IDashboardContext } from './types';
|
||||
import { sortLayout } from './util';
|
||||
|
||||
const DashboardContext = createContext<IDashboardContext>({
|
||||
@ -52,7 +54,12 @@ const DashboardContext = createContext<IDashboardContext>({
|
||||
layouts: [],
|
||||
panelMap: {},
|
||||
setPanelMap: () => {},
|
||||
listSortOrder: { columnKey: 'createdAt', order: 'descend', pagination: '1' },
|
||||
listSortOrder: {
|
||||
columnKey: 'createdAt',
|
||||
order: 'descend',
|
||||
pagination: '1',
|
||||
search: '',
|
||||
},
|
||||
setListSortOrder: () => {},
|
||||
setLayouts: () => {},
|
||||
setSelectedDashboard: () => {},
|
||||
@ -68,6 +75,7 @@ interface Props {
|
||||
dashboardId: string;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
export function DashboardProvider({
|
||||
children,
|
||||
}: PropsWithChildren): JSX.Element {
|
||||
@ -82,17 +90,50 @@ export function DashboardProvider({
|
||||
exact: true,
|
||||
});
|
||||
|
||||
const params = useUrlQuery();
|
||||
const orderColumnParam = params.get('columnKey');
|
||||
const orderQueryParam = params.get('order');
|
||||
const paginationParam = params.get('page');
|
||||
|
||||
const [listSortOrder, setListSortOrder] = useState({
|
||||
columnKey: orderColumnParam || 'updatedAt',
|
||||
order: orderQueryParam || 'descend',
|
||||
pagination: paginationParam || '1',
|
||||
const isDashboardListPage = useRouteMatch<Props>({
|
||||
path: ROUTES.ALL_DASHBOARD,
|
||||
exact: true,
|
||||
});
|
||||
|
||||
// added extra checks here in case wrong values appear use the default values rather than empty dashboards
|
||||
const supportedOrderColumnKeys = ['createdAt', 'updatedAt'];
|
||||
|
||||
const supportedOrderKeys = ['ascend', 'descend'];
|
||||
|
||||
const params = useUrlQuery();
|
||||
// since the dashboard provider is wrapped at the very top of the application hence it initialises these values from other pages as well.
|
||||
// pick the below params from URL only if the user is on the dashboards list page.
|
||||
const orderColumnParam = isDashboardListPage && params.get('columnKey');
|
||||
const orderQueryParam = isDashboardListPage && params.get('order');
|
||||
const paginationParam = isDashboardListPage && params.get('page');
|
||||
const searchParam = isDashboardListPage && params.get('search');
|
||||
|
||||
const [listSortOrder, setListOrder] = useState({
|
||||
columnKey: orderColumnParam
|
||||
? supportedOrderColumnKeys.includes(orderColumnParam)
|
||||
? orderColumnParam
|
||||
: 'updatedAt'
|
||||
: 'updatedAt',
|
||||
order: orderQueryParam
|
||||
? supportedOrderKeys.includes(orderQueryParam)
|
||||
? orderQueryParam
|
||||
: 'descend'
|
||||
: 'descend',
|
||||
pagination: paginationParam || '1',
|
||||
search: searchParam || '',
|
||||
});
|
||||
|
||||
function setListSortOrder(sortOrder: DashboardSortOrder): void {
|
||||
if (!isEqual(sortOrder, listSortOrder)) {
|
||||
setListOrder(sortOrder);
|
||||
}
|
||||
params.set('columnKey', sortOrder.columnKey as string);
|
||||
params.set('order', sortOrder.order as string);
|
||||
params.set('page', sortOrder.pagination || '1');
|
||||
params.set('search', sortOrder.search || '');
|
||||
history.replace({ search: params.toString() });
|
||||
}
|
||||
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
|
||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||
|
@ -1,9 +1,15 @@
|
||||
import dayjs from 'dayjs';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
|
||||
export interface DashboardSortOrder {
|
||||
columnKey: string;
|
||||
order: string;
|
||||
pagination: string;
|
||||
search: string;
|
||||
}
|
||||
|
||||
export interface IDashboardContext {
|
||||
isDashboardSliderOpen: boolean;
|
||||
isDashboardLocked: boolean;
|
||||
@ -15,18 +21,8 @@ export interface IDashboardContext {
|
||||
layouts: Layout[];
|
||||
panelMap: Record<string, { widgets: Layout[]; collapsed: boolean }>;
|
||||
setPanelMap: React.Dispatch<React.SetStateAction<Record<string, any>>>;
|
||||
listSortOrder: {
|
||||
columnKey: string;
|
||||
order: string;
|
||||
pagination: string;
|
||||
};
|
||||
setListSortOrder: Dispatch<
|
||||
SetStateAction<{
|
||||
columnKey: string;
|
||||
order: string;
|
||||
pagination: string;
|
||||
}>
|
||||
>;
|
||||
listSortOrder: DashboardSortOrder;
|
||||
setListSortOrder: (sortOrder: DashboardSortOrder) => void;
|
||||
setLayouts: React.Dispatch<React.SetStateAction<Layout[]>>;
|
||||
setSelectedDashboard: React.Dispatch<
|
||||
React.SetStateAction<Dashboard | undefined>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import ROUTES from 'constants/routes';
|
||||
import { parseQuery } from 'lib/logql';
|
||||
import { OrderPreferenceItems } from 'pages/Logs/config';
|
||||
import {
|
||||
@ -29,6 +30,30 @@ import {
|
||||
} from 'types/actions/logs';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
const supportedLogsOrder = [
|
||||
OrderPreferenceItems.ASC,
|
||||
OrderPreferenceItems.DESC,
|
||||
];
|
||||
|
||||
function getLogsOrder(): OrderPreferenceItems {
|
||||
// set the value of order from the URL only when order query param is present and the user is landing on the old logs explorer page
|
||||
if (window.location.pathname === ROUTES.OLD_LOGS_EXPLORER) {
|
||||
const orderParam = new URLSearchParams(window.location.search).get('order');
|
||||
|
||||
if (orderParam) {
|
||||
// check if the order passed is supported else pass the default order
|
||||
if (supportedLogsOrder.includes(orderParam as OrderPreferenceItems)) {
|
||||
return orderParam as OrderPreferenceItems;
|
||||
}
|
||||
|
||||
return OrderPreferenceItems.DESC;
|
||||
}
|
||||
return OrderPreferenceItems.DESC;
|
||||
}
|
||||
|
||||
return OrderPreferenceItems.DESC;
|
||||
}
|
||||
|
||||
const initialState: ILogsReducer = {
|
||||
fields: {
|
||||
interesting: [],
|
||||
@ -51,10 +76,7 @@ const initialState: ILogsReducer = {
|
||||
liveTailStartRange: 15,
|
||||
selectedLogId: null,
|
||||
detailedLog: null,
|
||||
order:
|
||||
(new URLSearchParams(window.location.search).get(
|
||||
'order',
|
||||
) as ILogsReducer['order']) ?? OrderPreferenceItems.DESC,
|
||||
order: getLogsOrder(),
|
||||
};
|
||||
|
||||
export const LogsReducer = (
|
||||
|
Loading…
x
Reference in New Issue
Block a user