mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-13 01:48:59 +08:00
801 dropdown is added in the dashboard page (#1669)
* chore: update the import from constant rather than static string * chore: removed redundant div * feat: added auto refresh component * refactor: top nav is refactored
This commit is contained in:
parent
a3b03ef0ca
commit
65af8c1b98
@ -11,3 +11,5 @@ export const INVITE_MEMBERS_HASH = '#invite-team-members';
|
||||
|
||||
export const SIGNOZ_UPGRADE_PLAN_URL =
|
||||
'https://upgrade.signoz.io/upgrade-from-app';
|
||||
|
||||
export const DASHBOARD_TIME_IN_DURATION = 'refreshInterval';
|
||||
|
63
frontend/src/container/TopNav/AutoRefresh/config.ts
Normal file
63
frontend/src/container/TopNav/AutoRefresh/config.ts
Normal file
@ -0,0 +1,63 @@
|
||||
export const options: IOptions[] = [
|
||||
{
|
||||
label: '',
|
||||
key: 'off',
|
||||
value: 0,
|
||||
},
|
||||
{
|
||||
label: '5s',
|
||||
key: '5s',
|
||||
value: 5000,
|
||||
},
|
||||
{
|
||||
label: '10s',
|
||||
key: '10s',
|
||||
value: 10000,
|
||||
},
|
||||
{
|
||||
label: '30s',
|
||||
key: '30s',
|
||||
value: 30000,
|
||||
},
|
||||
{
|
||||
label: '1m',
|
||||
key: '1m',
|
||||
value: 60000,
|
||||
},
|
||||
{
|
||||
label: '5m',
|
||||
key: '5m',
|
||||
value: 300000,
|
||||
},
|
||||
{
|
||||
label: '10m',
|
||||
key: '10m',
|
||||
value: 600000,
|
||||
},
|
||||
{
|
||||
label: '30m',
|
||||
key: '30m',
|
||||
value: 1800000,
|
||||
},
|
||||
{
|
||||
label: '1h',
|
||||
key: '1h',
|
||||
value: 3600000,
|
||||
},
|
||||
{
|
||||
label: '2h',
|
||||
key: '2h',
|
||||
value: 7200000,
|
||||
},
|
||||
{
|
||||
label: '1d',
|
||||
key: '1d',
|
||||
value: 86400000,
|
||||
},
|
||||
];
|
||||
|
||||
export interface IOptions {
|
||||
label: string;
|
||||
key: string;
|
||||
value: number;
|
||||
}
|
108
frontend/src/container/TopNav/AutoRefresh/index.tsx
Normal file
108
frontend/src/container/TopNav/AutoRefresh/index.tsx
Normal file
@ -0,0 +1,108 @@
|
||||
import { Select } from 'antd';
|
||||
import get from 'api/browser/localstorage/get';
|
||||
import set from 'api/browser/localstorage/set';
|
||||
import { DASHBOARD_TIME_IN_DURATION } from 'constants/app';
|
||||
import dayjs from 'dayjs';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useInterval } from 'react-use';
|
||||
import { Dispatch } from 'redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import { UPDATE_TIME_INTERVAL } from 'types/actions/globalTime';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { options } from './config';
|
||||
import { SelectContainer } from './styles';
|
||||
|
||||
function AutoRefresh({ disabled = false }: AutoRefreshProps): JSX.Element {
|
||||
const { minTime: initialMinTime, selectedTime } = useSelector<
|
||||
AppState,
|
||||
GlobalReducer
|
||||
>((state) => state.globalTime);
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const params = useUrlQuery();
|
||||
|
||||
const localStorageData = JSON.parse(get(DASHBOARD_TIME_IN_DURATION) || '{}');
|
||||
|
||||
const localStorageValue = useMemo(() => localStorageData[pathname], [
|
||||
pathname,
|
||||
localStorageData,
|
||||
]);
|
||||
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
const [selectedOption, setSelectedOption] = useState<string>(
|
||||
localStorageValue || options[0].key,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSelectedOption(localStorageValue || options[0].key);
|
||||
}, [localStorageValue, params]);
|
||||
|
||||
const getOption = useMemo(
|
||||
() => options.find((option) => option.key === selectedOption),
|
||||
[selectedOption],
|
||||
);
|
||||
|
||||
useInterval(() => {
|
||||
const selectedValue = getOption?.value;
|
||||
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedOption !== 'off' && selectedValue) {
|
||||
const min = initialMinTime / 1000000;
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_TIME_INTERVAL,
|
||||
payload: {
|
||||
maxTime: dayjs().valueOf() * 1000000,
|
||||
minTime: dayjs(min).subtract(selectedValue, 'second').valueOf() * 1000000,
|
||||
selectedTime,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, getOption?.value || 0);
|
||||
|
||||
const onChangeHandler = useCallback(
|
||||
(value: unknown) => {
|
||||
if (typeof value === 'string') {
|
||||
setSelectedOption(value);
|
||||
params.set(DASHBOARD_TIME_IN_DURATION, value);
|
||||
set(
|
||||
DASHBOARD_TIME_IN_DURATION,
|
||||
JSON.stringify({ ...localStorageData, [pathname]: value }),
|
||||
);
|
||||
}
|
||||
},
|
||||
[params, pathname, localStorageData],
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectContainer
|
||||
disabled={disabled}
|
||||
onChange={onChangeHandler}
|
||||
value={selectedOption}
|
||||
>
|
||||
{options.map((option) => (
|
||||
<Select.Option key={option.key} value={option.key}>
|
||||
{option.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</SelectContainer>
|
||||
);
|
||||
}
|
||||
|
||||
interface AutoRefreshProps {
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
AutoRefresh.defaultProps = {
|
||||
disabled: false,
|
||||
};
|
||||
|
||||
export default AutoRefresh;
|
9
frontend/src/container/TopNav/AutoRefresh/styles.ts
Normal file
9
frontend/src/container/TopNav/AutoRefresh/styles.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Select } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const SelectContainer = styled(Select)`
|
||||
&&& {
|
||||
width: 100%;
|
||||
min-width: 4rem;
|
||||
}
|
||||
`;
|
@ -2,7 +2,10 @@ import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { RefreshTextContainer, Typography } from './styles';
|
||||
|
||||
function RefreshText({ onLastRefreshHandler }: RefreshTextProps): JSX.Element {
|
||||
function RefreshText({
|
||||
onLastRefreshHandler,
|
||||
refreshButtonHidden,
|
||||
}: RefreshTextProps): JSX.Element {
|
||||
const [refreshText, setRefreshText] = useState<string>('');
|
||||
|
||||
// this is to update the refresh text
|
||||
@ -19,7 +22,7 @@ function RefreshText({ onLastRefreshHandler }: RefreshTextProps): JSX.Element {
|
||||
}, [onLastRefreshHandler, refreshText]);
|
||||
|
||||
return (
|
||||
<RefreshTextContainer>
|
||||
<RefreshTextContainer refreshButtonHidden={refreshButtonHidden}>
|
||||
<Typography>{refreshText}</Typography>
|
||||
</RefreshTextContainer>
|
||||
);
|
||||
@ -27,6 +30,7 @@ function RefreshText({ onLastRefreshHandler }: RefreshTextProps): JSX.Element {
|
||||
|
||||
interface RefreshTextProps {
|
||||
onLastRefreshHandler: () => string;
|
||||
refreshButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export default RefreshText;
|
||||
|
@ -67,3 +67,19 @@ export const getOptions = (routes: string): Option[] => {
|
||||
}
|
||||
return Options;
|
||||
};
|
||||
|
||||
export const routesToSkip = [
|
||||
ROUTES.SETTINGS,
|
||||
ROUTES.LIST_ALL_ALERT,
|
||||
ROUTES.TRACE_DETAIL,
|
||||
ROUTES.ALL_CHANNELS,
|
||||
ROUTES.USAGE_EXPLORER,
|
||||
ROUTES.INSTRUMENTATION,
|
||||
ROUTES.VERSION,
|
||||
ROUTES.ALL_DASHBOARD,
|
||||
ROUTES.ORG_SETTINGS,
|
||||
ROUTES.ERROR_DETAIL,
|
||||
ROUTES.ALERTS_NEW,
|
||||
ROUTES.EDIT_ALERTS,
|
||||
ROUTES.LIST_ALL_ALERT,
|
||||
];
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Button, Select as DefaultSelect } from 'antd';
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||
@ -14,10 +15,11 @@ import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import AutoRefresh from '../AutoRefresh';
|
||||
import CustomDateTimeModal, { DateTimeRangeType } from '../CustomDateTimeModal';
|
||||
import { getDefaultOption, getOptions, Time } from './config';
|
||||
import RefreshText from './Refresh';
|
||||
import { Container, Form, FormItem } from './styles';
|
||||
import { Form, FormItem } from './styles';
|
||||
|
||||
const { Option } = DefaultSelect;
|
||||
|
||||
@ -240,6 +242,8 @@ function DateTimeSelection({
|
||||
setStartTime(dayjs(preStartTime));
|
||||
setEndTime(dayjs(preEndTime));
|
||||
|
||||
setRefreshButtonHidden(updatedTime === 'custom');
|
||||
|
||||
updateTimeInterval(updatedTime, [preStartTime, preEndTime]);
|
||||
}, [
|
||||
location.pathname,
|
||||
@ -253,7 +257,7 @@ function DateTimeSelection({
|
||||
]);
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<>
|
||||
<Form
|
||||
form={formSelector}
|
||||
layout="inline"
|
||||
@ -272,9 +276,15 @@ function DateTimeSelection({
|
||||
</DefaultSelect>
|
||||
|
||||
<FormItem hidden={refreshButtonHidden}>
|
||||
<Button type="primary" onClick={onRefreshHandler}>
|
||||
Refresh
|
||||
</Button>
|
||||
<Button
|
||||
icon={<SyncOutlined />}
|
||||
type="primary"
|
||||
onClick={onRefreshHandler}
|
||||
/>
|
||||
</FormItem>
|
||||
|
||||
<FormItem>
|
||||
<AutoRefresh disabled={refreshButtonHidden} />
|
||||
</FormItem>
|
||||
</Form>
|
||||
|
||||
@ -282,6 +292,7 @@ function DateTimeSelection({
|
||||
{...{
|
||||
onLastRefreshHandler,
|
||||
}}
|
||||
refreshButtonHidden={refreshButtonHidden}
|
||||
/>
|
||||
|
||||
<CustomDateTimeModal
|
||||
@ -291,7 +302,7 @@ function DateTimeSelection({
|
||||
setCustomDTPickerVisible(false);
|
||||
}}
|
||||
/>
|
||||
</Container>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,6 @@
|
||||
import { Form as FormComponent, Typography as TypographyComponent } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Container = styled.div`
|
||||
justify-content: flex-end;
|
||||
`;
|
||||
|
||||
export const Form = styled(FormComponent)`
|
||||
&&& {
|
||||
justify-content: flex-end;
|
||||
@ -23,6 +19,12 @@ export const FormItem = styled(Form.Item)`
|
||||
}
|
||||
`;
|
||||
|
||||
export const RefreshTextContainer = styled.div`
|
||||
interface Props {
|
||||
refreshButtonHidden: boolean;
|
||||
}
|
||||
|
||||
export const RefreshTextContainer = styled.div<Props>`
|
||||
min-height: 2rem;
|
||||
visibility: ${({ refreshButtonHidden }): string =>
|
||||
refreshButtonHidden ? 'hidden' : 'visible'};
|
||||
`;
|
||||
|
@ -1,52 +1,40 @@
|
||||
import { Col } from 'antd';
|
||||
import ROUTES from 'constants/routes';
|
||||
import history from 'lib/history';
|
||||
import React from 'react';
|
||||
import { matchPath } from 'react-router-dom';
|
||||
import React, { useMemo } from 'react';
|
||||
import { matchPath, useHistory } from 'react-router-dom';
|
||||
|
||||
import ShowBreadcrumbs from './Breadcrumbs';
|
||||
import DateTimeSelector from './DateTimeSelection';
|
||||
import { routesToSkip } from './DateTimeSelection/config';
|
||||
import { Container } from './styles';
|
||||
|
||||
const routesToSkip = [
|
||||
ROUTES.SETTINGS,
|
||||
ROUTES.LIST_ALL_ALERT,
|
||||
ROUTES.TRACE_DETAIL,
|
||||
ROUTES.ALL_CHANNELS,
|
||||
ROUTES.USAGE_EXPLORER,
|
||||
ROUTES.INSTRUMENTATION,
|
||||
ROUTES.VERSION,
|
||||
ROUTES.ALL_DASHBOARD,
|
||||
ROUTES.ORG_SETTINGS,
|
||||
ROUTES.ERROR_DETAIL,
|
||||
ROUTES.ALERTS_NEW,
|
||||
ROUTES.EDIT_ALERTS,
|
||||
ROUTES.LIST_ALL_ALERT,
|
||||
];
|
||||
|
||||
function TopNav(): JSX.Element | null {
|
||||
if (history.location.pathname === ROUTES.SIGN_UP) {
|
||||
const { location } = useHistory();
|
||||
|
||||
const isRouteToSkip = useMemo(
|
||||
() =>
|
||||
routesToSkip.some((route) =>
|
||||
matchPath(location.pathname, { path: route, exact: true }),
|
||||
),
|
||||
[location.pathname],
|
||||
);
|
||||
|
||||
const isSignUpPage = useMemo(
|
||||
() => matchPath(location.pathname, { path: ROUTES.SIGN_UP, exact: true }),
|
||||
[location.pathname],
|
||||
);
|
||||
|
||||
if (isSignUpPage) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const checkRouteExists = (currentPath: string): boolean => {
|
||||
for (let i = 0; i < routesToSkip.length; i += 1) {
|
||||
if (
|
||||
matchPath(currentPath, { path: routesToSkip[i], exact: true, strict: true })
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Col span={16}>
|
||||
<ShowBreadcrumbs />
|
||||
</Col>
|
||||
|
||||
{!checkRouteExists(history.location.pathname) && (
|
||||
{!isRouteToSkip && (
|
||||
<Col span={8}>
|
||||
<DateTimeSelector />
|
||||
</Col>
|
||||
|
@ -2,6 +2,7 @@ import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import { Dispatch } from 'redux';
|
||||
import AppActions from 'types/actions';
|
||||
import { UPDATE_TIME_INTERVAL } from 'types/actions/globalTime';
|
||||
|
||||
export const UpdateTimeInterval = (
|
||||
interval: Time,
|
||||
@ -11,7 +12,7 @@ export const UpdateTimeInterval = (
|
||||
const { maxTime, minTime } = GetMinMax(interval, dateTimeRange);
|
||||
|
||||
dispatch({
|
||||
type: 'UPDATE_TIME_INTERVAL',
|
||||
type: UPDATE_TIME_INTERVAL,
|
||||
payload: {
|
||||
maxTime,
|
||||
minTime,
|
||||
|
Loading…
x
Reference in New Issue
Block a user