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:
Pranshu Chittora 2022-03-22 16:22:02 +05:30 committed by GitHub
parent f1f606844a
commit 3ab0e1395a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 110 additions and 34 deletions

View File

@ -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"

View File

@ -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 = (

View File

@ -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'

View File

@ -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_%"

View File

@ -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"

View File

@ -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>

View File

@ -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');
};

View File

@ -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>
);

View File

@ -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 &&

View File

@ -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',
},

View File

@ -3,6 +3,6 @@ import styled from 'styled-components';
export const SelectComponent = styled(Select)`
&&& {
min-width: 10rem;
min-width: 12rem;
}
`;

View File

@ -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>;
},
},
{

View File

@ -1 +1,2 @@
export * from './sideBarCollapse';
export * from './toggleDarkMode';

View 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,
});
};
};

View File

@ -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;
}

View File

@ -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;

View File

@ -1,4 +1,5 @@
export default interface AppReducer {
isDarkMode: boolean;
isLoggedIn: boolean;
isSideBarCollapsed: boolean;
}