mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 07:19:00 +08:00
feat: data time, UI and graph label consistency across FE (#878)
* feat: data time and graph label consistency across FE * feat: saved state of sidebar and horizontal scroll fix for trace filter page * feat: add Y-Axis unit for missing metrics graphs * chore: update node version from 12.18 to 12.22 * fix: 24hr time unit on graph
This commit is contained in:
parent
f1f606844a
commit
3ab0e1395a
@ -1,5 +1,5 @@
|
||||
# stage1 as builder
|
||||
FROM node:12.18.0 as builder
|
||||
FROM node:12.22.0 as builder
|
||||
|
||||
# Add Maintainer Info
|
||||
LABEL maintainer="signoz"
|
||||
|
@ -51,6 +51,7 @@ Chart.register(
|
||||
);
|
||||
|
||||
function Graph({
|
||||
animate = true,
|
||||
data,
|
||||
type,
|
||||
title,
|
||||
@ -58,15 +59,14 @@ function Graph({
|
||||
onClickHandler,
|
||||
name,
|
||||
yAxisUnit = 'short',
|
||||
forceReRender,
|
||||
}: GraphProps): JSX.Element {
|
||||
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
const chartRef = useRef<HTMLCanvasElement>(null);
|
||||
const currentTheme = isDarkMode ? 'dark' : 'light';
|
||||
|
||||
const xAxisTimeUnit = useXAxisTimeUnit(data); // Computes the relevant time unit for x axis by analyzing the time stamp data
|
||||
|
||||
const lineChartRef = useRef<Chart>();
|
||||
|
||||
const getGridColor = useCallback(() => {
|
||||
if (currentTheme === undefined) {
|
||||
return 'rgba(231,233,237,0.1)';
|
||||
@ -86,6 +86,9 @@ function Graph({
|
||||
|
||||
if (chartRef.current !== null) {
|
||||
const options: ChartOptions = {
|
||||
animation: {
|
||||
duration: animate ? 200 : 0,
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
@ -117,8 +120,8 @@ function Graph({
|
||||
unit: xAxisTimeUnit?.unitName || 'minute',
|
||||
stepSize: xAxisTimeUnit?.stepSize || 1,
|
||||
displayFormats: {
|
||||
millisecond: 'hh:mm:ss',
|
||||
second: 'hh:mm:ss',
|
||||
millisecond: 'HH:mm:ss',
|
||||
second: 'HH:mm:ss',
|
||||
minute: 'HH:mm',
|
||||
hour: 'MM/dd HH:mm',
|
||||
day: 'MM/dd',
|
||||
@ -166,11 +169,23 @@ function Graph({
|
||||
plugins: [legend(name, data.datasets.length > 3)],
|
||||
});
|
||||
}
|
||||
}, [chartRef, data, type, title, isStacked, getGridColor, onClickHandler]);
|
||||
}, [
|
||||
animate,
|
||||
title,
|
||||
getGridColor,
|
||||
xAxisTimeUnit?.unitName,
|
||||
xAxisTimeUnit?.stepSize,
|
||||
isStacked,
|
||||
type,
|
||||
data,
|
||||
name,
|
||||
yAxisUnit,
|
||||
onClickHandler,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
buildChart();
|
||||
}, [buildChart]);
|
||||
}, [buildChart, forceReRender]);
|
||||
|
||||
return (
|
||||
<div style={{ height: '85%' }}>
|
||||
@ -181,6 +196,7 @@ function Graph({
|
||||
}
|
||||
|
||||
interface GraphProps {
|
||||
animate?: boolean;
|
||||
type: ChartType;
|
||||
data: Chart['data'];
|
||||
title?: string;
|
||||
@ -189,6 +205,7 @@ interface GraphProps {
|
||||
onClickHandler?: graphOnClickHandler;
|
||||
name: string;
|
||||
yAxisUnit?: string;
|
||||
forceReRender?: boolean | null | number;
|
||||
}
|
||||
|
||||
export type graphOnClickHandler = (
|
||||
|
@ -5,3 +5,5 @@ export const WITHOUT_SESSION_PATH = ['/redirect'];
|
||||
export const AUTH0_REDIRECT_PATH = '/redirect';
|
||||
|
||||
export const DEFAULT_AUTH0_APP_REDIRECTION_PATH = ROUTES.APPLICATION;
|
||||
|
||||
export const IS_SIDEBAR_COLLAPSED = 'isSideBarCollapsed'
|
@ -107,7 +107,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
<GraphTitle>Application latency in ms</GraphTitle>
|
||||
<GraphTitle>Application latency</GraphTitle>
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||
@ -175,7 +175,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
||||
View Traces
|
||||
</Button>
|
||||
<Card>
|
||||
<GraphTitle>Request per sec</GraphTitle>
|
||||
<GraphTitle>Requests</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="request_per_sec"
|
||||
@ -190,7 +190,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
||||
legend: 'Request per second',
|
||||
},
|
||||
])}
|
||||
yAxisUnit="short"
|
||||
yAxisUnit="reqps"
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@ -210,7 +210,7 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
||||
</Button>
|
||||
|
||||
<Card>
|
||||
<GraphTitle>Error Percentage (%)</GraphTitle>
|
||||
<GraphTitle>Error Percentage</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="error_percentage_%"
|
||||
|
@ -25,7 +25,7 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
||||
legend: '{{db_system}}',
|
||||
},
|
||||
])}
|
||||
yAxisUnit="short"
|
||||
yAxisUnit="reqps"
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
@ -33,7 +33,7 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
||||
|
||||
<Col span={12}>
|
||||
<Card>
|
||||
<GraphTitle>Database Calls Avg Duration (in ms)</GraphTitle>
|
||||
<GraphTitle>Database Calls Avg Duration</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="database_call_avg_duration"
|
||||
|
@ -14,7 +14,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
||||
<Row gutter={24}>
|
||||
<Col span={12}>
|
||||
<Card>
|
||||
<GraphTitle>External Call Error Percentage (%)</GraphTitle>
|
||||
<GraphTitle>External Call Error Percentage</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="external_call_error_percentage"
|
||||
@ -68,7 +68,7 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
||||
legend: '{{http_url}}',
|
||||
},
|
||||
])}
|
||||
yAxisUnit="short"
|
||||
yAxisUnit="reqps"
|
||||
/>
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
|
@ -1,13 +1,16 @@
|
||||
import { Menu, Typography } from 'antd';
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
||||
import ROUTES from 'constants/routes';
|
||||
import history from 'lib/history';
|
||||
import setTheme from 'lib/theme/setTheme';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||
import { NavLink, useLocation } from 'react-router-dom';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { ToggleDarkMode } from 'store/actions';
|
||||
import { SideBarCollapse } from 'store/actions/app';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
@ -24,7 +27,10 @@ import {
|
||||
} from './styles';
|
||||
|
||||
function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
||||
const [collapsed, setCollapsed] = useState<boolean>(false);
|
||||
const dispatch = useDispatch();
|
||||
const [collapsed, setCollapsed] = useState<boolean>(
|
||||
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||
);
|
||||
const { pathname } = useLocation();
|
||||
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
@ -53,6 +59,10 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
||||
setCollapsed((collapsed) => !collapsed);
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
dispatch(SideBarCollapse(collapsed));
|
||||
}, [collapsed]);
|
||||
|
||||
const onClickHandler = useCallback(
|
||||
(to: string) => {
|
||||
if (pathname !== to) {
|
||||
@ -62,7 +72,7 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
||||
[pathname],
|
||||
);
|
||||
|
||||
const onClickSlackHandler = () => {
|
||||
const onClickSlackHandler = (): void => {
|
||||
window.open('https://signoz.io/slack', '_blank');
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@ import Graph from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useMeasure } from 'react-use';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { TraceReducer } from 'types/reducer/trace';
|
||||
|
||||
@ -10,6 +11,8 @@ import { getChartData, getChartDataforGroupBy } from './config';
|
||||
import { Container } from './styles';
|
||||
|
||||
function TraceGraph(): JSX.Element {
|
||||
const [ref, { width }] = useMeasure();
|
||||
|
||||
const { spansGraph, selectedGroupBy, yAxisUnit } = useSelector<
|
||||
AppState,
|
||||
TraceReducer
|
||||
@ -21,7 +24,7 @@ function TraceGraph(): JSX.Element {
|
||||
return selectedGroupBy.length === 0
|
||||
? getChartData(payload)
|
||||
: getChartDataforGroupBy(payload);
|
||||
}, [payload]);
|
||||
}, [payload, selectedGroupBy.length]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
@ -40,12 +43,14 @@ function TraceGraph(): JSX.Element {
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Container ref={ref}>
|
||||
<Graph
|
||||
animate={false}
|
||||
data={ChartData}
|
||||
name="traceGraph"
|
||||
type="line"
|
||||
yAxisUnit={yAxisUnit}
|
||||
forceReRender={width}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
@ -1,13 +1,17 @@
|
||||
import React from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
|
||||
interface Props {
|
||||
center?: boolean;
|
||||
ref?: React.RefObject<HTMLDivElement> | null; // The ref type provided by react-use is incorrect -> https://github.com/streamich/react-use/issues/1264 Open Issue
|
||||
}
|
||||
|
||||
export const Container = styled.div<Props>`
|
||||
height: 25vh;
|
||||
height: 25vh !important;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
width: 100% !important;
|
||||
|
||||
${({ center }) =>
|
||||
center &&
|
||||
|
@ -67,35 +67,35 @@ export const functions: Dropdown[] = [
|
||||
key: 'ratePerSec',
|
||||
yAxisUnit: 'reqps',
|
||||
},
|
||||
{ displayValue: 'Sum(duration in ns)', key: 'sum', yAxisUnit: 'ns' },
|
||||
{ displayValue: 'Avg(duration in ns)', key: 'avg', yAxisUnit: 'ns' },
|
||||
{ displayValue: 'Sum (duration)', key: 'sum', yAxisUnit: 'ns' },
|
||||
{ displayValue: 'Avg (duration)', key: 'avg', yAxisUnit: 'ns' },
|
||||
{
|
||||
displayValue: 'Max(duration in ns)',
|
||||
displayValue: 'Max (duration)',
|
||||
key: 'max',
|
||||
yAxisUnit: 'ns',
|
||||
},
|
||||
{
|
||||
displayValue: 'Min(duration in ns)',
|
||||
displayValue: 'Min (duration)',
|
||||
key: 'min',
|
||||
yAxisUnit: 'ns',
|
||||
},
|
||||
{
|
||||
displayValue: '50th percentile(duration in ns)',
|
||||
displayValue: '50th percentile (duration)',
|
||||
key: 'p50',
|
||||
yAxisUnit: 'ns',
|
||||
},
|
||||
{
|
||||
displayValue: '90th percentile(duration in ns)',
|
||||
displayValue: '90th percentile (duration)',
|
||||
key: 'p90',
|
||||
yAxisUnit: 'ns',
|
||||
},
|
||||
{
|
||||
displayValue: '95th percentile(duration in ns)',
|
||||
displayValue: '95th percentile (duration)',
|
||||
key: 'p95',
|
||||
yAxisUnit: 'ns',
|
||||
},
|
||||
{
|
||||
displayValue: '99th percentile(duration in ns)',
|
||||
displayValue: '99th percentile (duration)',
|
||||
key: 'p99',
|
||||
yAxisUnit: 'ns',
|
||||
},
|
||||
|
@ -3,6 +3,6 @@ import styled from 'styled-components';
|
||||
|
||||
export const SelectComponent = styled(Select)`
|
||||
&&& {
|
||||
min-width: 10rem;
|
||||
min-width: 12rem;
|
||||
}
|
||||
`;
|
||||
|
@ -43,7 +43,7 @@ function TraceTable({ getSpansAggregate }: TraceProps): JSX.Element {
|
||||
sorter: true,
|
||||
render: (value: TableType['timestamp']): JSX.Element => {
|
||||
const day = dayjs(value);
|
||||
return <div>{day.format('DD/MM/YYYY hh:mm:ss A')}</div>;
|
||||
return <div>{day.format('YYYY/MM/DD HH:mm:ss')}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './sideBarCollapse';
|
||||
export * from './toggleDarkMode';
|
||||
|
16
frontend/src/store/actions/app/sideBarCollapse.ts
Normal file
16
frontend/src/store/actions/app/sideBarCollapse.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
||||
import { Dispatch } from 'redux';
|
||||
import AppActions from 'types/actions';
|
||||
|
||||
export const SideBarCollapse = (
|
||||
collapseState: boolean,
|
||||
): ((dispatch: Dispatch<AppActions>) => void) => {
|
||||
setLocalStorageKey(IS_SIDEBAR_COLLAPSED, `${collapseState}`);
|
||||
return (dispatch: Dispatch<AppActions>): void => {
|
||||
dispatch({
|
||||
type: 'SIDEBAR_COLLAPSE',
|
||||
payload: collapseState,
|
||||
});
|
||||
};
|
||||
};
|
@ -1,12 +1,19 @@
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
||||
import { IS_LOGGED_IN } from 'constants/auth';
|
||||
import getTheme from 'lib/theme/getTheme';
|
||||
import { AppAction, LOGGED_IN, SWITCH_DARK_MODE } from 'types/actions/app';
|
||||
import {
|
||||
AppAction,
|
||||
LOGGED_IN,
|
||||
SIDEBAR_COLLAPSE,
|
||||
SWITCH_DARK_MODE,
|
||||
} from 'types/actions/app';
|
||||
import InitialValueTypes from 'types/reducer/app';
|
||||
|
||||
const InitialValue: InitialValueTypes = {
|
||||
isDarkMode: getTheme() === 'darkMode',
|
||||
isLoggedIn: getLocalStorageKey(IS_LOGGED_IN) === 'yes',
|
||||
isSideBarCollapsed: getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||
};
|
||||
|
||||
const appReducer = (
|
||||
@ -28,6 +35,13 @@ const appReducer = (
|
||||
};
|
||||
}
|
||||
|
||||
case SIDEBAR_COLLAPSE: {
|
||||
return {
|
||||
...state,
|
||||
isSideBarCollapsed: action.payload,
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE';
|
||||
export const LOGGED_IN = 'LOGGED_IN';
|
||||
export const SIDEBAR_COLLAPSE = 'SIDEBAR_COLLAPSE';
|
||||
|
||||
export interface SwitchDarkMode {
|
||||
type: typeof SWITCH_DARK_MODE;
|
||||
@ -9,4 +10,9 @@ export interface LoggedInUser {
|
||||
type: typeof LOGGED_IN;
|
||||
}
|
||||
|
||||
export type AppAction = SwitchDarkMode | LoggedInUser;
|
||||
export interface SideBarCollapse {
|
||||
type: typeof SIDEBAR_COLLAPSE;
|
||||
payload: boolean;
|
||||
}
|
||||
|
||||
export type AppAction = SwitchDarkMode | LoggedInUser | SideBarCollapse;
|
||||
|
@ -1,4 +1,5 @@
|
||||
export default interface AppReducer {
|
||||
isDarkMode: boolean;
|
||||
isLoggedIn: boolean;
|
||||
isSideBarCollapsed: boolean;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user