diff --git a/frontend/src/constants/localStorage.ts b/frontend/src/constants/localStorage.ts index 26512b627d..5dd89c624c 100644 --- a/frontend/src/constants/localStorage.ts +++ b/frontend/src/constants/localStorage.ts @@ -1,3 +1,3 @@ export enum LOCAL_STORAGE { - METRICS_TIME_IN_DURATION = "metricsTimeDuration", + METRICS_TIME_IN_DURATION = "metricsTimeDurations", } diff --git a/frontend/src/modules/AppWrapper.tsx b/frontend/src/modules/AppWrapper.tsx index 716345594b..b261ad3e32 100644 --- a/frontend/src/modules/AppWrapper.tsx +++ b/frontend/src/modules/AppWrapper.tsx @@ -1,17 +1,11 @@ import React, { Suspense } from "react"; -import { Layout, Spin } from "antd"; +import { Spin } from "antd"; import { useThemeSwitcher } from "react-css-theme-switcher"; import ROUTES from "Src/constants/routes"; import { IS_LOGGED_IN } from "Src/constants/auth"; -import { - BrowserRouter as Router, - Route, - Switch, - Redirect, -} from "react-router-dom"; +import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom"; -import SideNav from "./Nav/SideNav"; -import TopNav from "./Nav/TopNav"; +import BaseLayout from "./BaseLayout"; import { ServiceMetrics, ServiceMap, @@ -24,8 +18,6 @@ import { IntstrumentationPage, } from "Src/pages"; -const { Content, Footer } = Layout; - const App = () => { const { status } = useThemeSwitcher(); @@ -34,47 +26,44 @@ const App = () => { } return ( - - - - - - - }> - - - - - - - - - - - { - return localStorage.getItem(IS_LOGGED_IN) === "yes" ? ( - - ) : ( - - ); - }} - /> - - - - - - - + + }> + + + + + + + + + + + + + { + return localStorage.getItem(IS_LOGGED_IN) === "yes" ? ( + + ) : ( + + ); + }} + /> + + + + + ); }; diff --git a/frontend/src/modules/BaseLayout.tsx b/frontend/src/modules/BaseLayout.tsx new file mode 100644 index 0000000000..a36ac96e66 --- /dev/null +++ b/frontend/src/modules/BaseLayout.tsx @@ -0,0 +1,29 @@ +import React, { ReactNode } from "react"; + +import { Layout } from "antd"; +import SideNav from "./Nav/SideNav"; +import TopNav from "./Nav/TopNav"; +const { Content, Footer } = Layout; + +interface BaseLayoutProps { + children: ReactNode; +} + +const BaseLayout: React.FC = ({ children }) => { + return ( + + + + + + {children} + +
+ SigNoz Inc. ©2020{" "} +
+
+
+ ); +}; + +export default BaseLayout; diff --git a/frontend/src/modules/Nav/TopNav/DateTimeSelector.tsx b/frontend/src/modules/Nav/TopNav/DateTimeSelector.tsx index 7852638be3..03758461d4 100644 --- a/frontend/src/modules/Nav/TopNav/DateTimeSelector.tsx +++ b/frontend/src/modules/Nav/TopNav/DateTimeSelector.tsx @@ -1,11 +1,12 @@ import React, { useEffect, useState } from "react"; +import { cloneDeep } from "lodash"; import { Select as DefaultSelect, Button, Space, Form } from "antd"; import styled from "styled-components"; import { withRouter } from "react-router"; +import { getLocalStorageRouteKey } from "./utils"; import { RouteComponentProps, useLocation } from "react-router-dom"; import { connect } from "react-redux"; import ROUTES from "Src/constants/routes"; -import { findIndex } from "lodash"; import CustomDateTimeModal from "./CustomDateTimeModal"; import { GlobalTime, updateTimeInterval } from "../../../store/actions"; import { StoreState } from "../../../store/reducers"; @@ -25,9 +26,7 @@ const DateTimeWrapper = styled.div` margin-top: 20px; justify-content: flex-end !important; `; -const Select = styled(DefaultSelect)` - width: 150px; -`; +const Select = styled(DefaultSelect)``; interface DateTimeSelectorProps extends RouteComponentProps { currentpath?: string; updateTimeInterval: Function; @@ -39,14 +38,23 @@ This components is mounted all the time. Use event listener to track changes. */ const _DateTimeSelector = (props: DateTimeSelectorProps) => { const location = useLocation(); + const LocalStorageRouteKey: string = getLocalStorageRouteKey( + location.pathname, + ); + const timeDurationInLocalStorage = + JSON.parse(localStorage.getItem(LOCAL_STORAGE.METRICS_TIME_IN_DURATION)) || + {}; const options = location.pathname === ROUTES.SERVICE_MAP ? ServiceMapOptions : Options; - const defaultTime = - location.pathname === ROUTES.SERVICE_MAP || - location.pathname === ROUTES.APPLICATION - ? DefaultOptionsBasedOnRoute[location.pathname] - : DefaultOptionsBasedOnRoute.default; - + let defaultTime = DefaultOptionsBasedOnRoute[LocalStorageRouteKey] + ? DefaultOptionsBasedOnRoute[LocalStorageRouteKey] + : DefaultOptionsBasedOnRoute.default; + if (timeDurationInLocalStorage[LocalStorageRouteKey]) { + defaultTime = timeDurationInLocalStorage[LocalStorageRouteKey]; + } + const [currentLocalStorageRouteKey, setCurrentLocalStorageRouteKey] = useState( + LocalStorageRouteKey, + ); const [customDTPickerVisible, setCustomDTPickerVisible] = useState(false); const [timeInterval, setTimeInterval] = useState(defaultTime); const [startTime, setStartTime] = useState(null); @@ -57,9 +65,6 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => { const [form_dtselector] = Form.useForm(); const updateTimeOnQueryParamChange = () => { - const timeDurationInLocalStorage = localStorage.getItem( - LOCAL_STORAGE.METRICS_TIME_IN_DURATION, - ); const urlParams = new URLSearchParams(location.search); const intervalInQueryParam = urlParams.get(METRICS_PAGE_QUERY_PARAM.interval); const startTimeString = urlParams.get(METRICS_PAGE_QUERY_PARAM.startTime); @@ -75,25 +80,38 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => { const startTime = moment(Number(startTimeString)); const endTime = moment(Number(endTimeString)); setCustomTime(startTime, endTime, true); + } else if (currentLocalStorageRouteKey !== LocalStorageRouteKey) { + setMetricsTimeInterval(defaultTime); + setCurrentLocalStorageRouteKey(LocalStorageRouteKey); } // first pref: handle intervalInQueryParam else if (intervalInQueryParam) { - window.localStorage.setItem( - LOCAL_STORAGE.METRICS_TIME_IN_DURATION, - intervalInQueryParam, - ); setMetricsTimeInterval(intervalInQueryParam); - } else if (timeDurationInLocalStorage) { - setMetricsTimeInterval(timeDurationInLocalStorage); } }; + const setToLocalStorage = (val: string) => { + let timeDurationInLocalStorageObj = cloneDeep(timeDurationInLocalStorage); + if (timeDurationInLocalStorageObj) { + timeDurationInLocalStorageObj[LocalStorageRouteKey] = val; + } else { + timeDurationInLocalStorageObj = { + [LocalStorageRouteKey]: val, + }; + } + window.localStorage.setItem( + LOCAL_STORAGE.METRICS_TIME_IN_DURATION, + JSON.stringify(timeDurationInLocalStorageObj), + ); + }; + + useEffect(() => { + setMetricsTimeInterval(defaultTime); + }, []); + // On URL Change useEffect(() => { updateTimeOnQueryParamChange(); - if (findIndex(options, (option) => option.value === timeInterval) === -1) { - setTimeInterval(defaultTime); - } }, [location]); const setMetricsTimeInterval = (value: string) => { @@ -101,8 +119,7 @@ const _DateTimeSelector = (props: DateTimeSelectorProps) => { setTimeInterval(value); setEndTime(null); setStartTime(null); - - window.localStorage.setItem(LOCAL_STORAGE.METRICS_TIME_IN_DURATION, value); + setToLocalStorage(value); }; const setCustomTime = ( startTime: moment.Moment, @@ -259,8 +276,10 @@ const mapStateToProps = (state: StoreState): { globalTime: GlobalTime } => { return { globalTime: state.globalTime }; }; -export const DateTimeSelector = connect(mapStateToProps, { - updateTimeInterval: updateTimeInterval, -})(_DateTimeSelector); +export const DateTimeSelector = withRouter( + connect(mapStateToProps, { + updateTimeInterval: updateTimeInterval, + })(_DateTimeSelector), +); -export default withRouter(DateTimeSelector); +export default DateTimeSelector; diff --git a/frontend/src/modules/Nav/TopNav/config.ts b/frontend/src/modules/Nav/TopNav/config.ts index b4a436c25e..1cad032461 100644 --- a/frontend/src/modules/Nav/TopNav/config.ts +++ b/frontend/src/modules/Nav/TopNav/config.ts @@ -19,5 +19,6 @@ export const ServiceMapOptions = [ export const DefaultOptionsBasedOnRoute = { [ROUTES.SERVICE_MAP]: ServiceMapOptions[0].value, [ROUTES.APPLICATION]: Options[0].value, + [ROUTES.SERVICE_METRICS]: Options[2].value, default: Options[2].value, }; diff --git a/frontend/src/modules/Nav/TopNav/utils.ts b/frontend/src/modules/Nav/TopNav/utils.ts new file mode 100644 index 0000000000..e46503312e --- /dev/null +++ b/frontend/src/modules/Nav/TopNav/utils.ts @@ -0,0 +1,18 @@ +import ROUTES from "Src/constants/routes"; + +export const getLocalStorageRouteKey = (pathName: string) => { + let localStorageKey = ""; + const pathNameSplit = pathName.split("/"); + if (!pathNameSplit[2]) { + localStorageKey = pathName; + } else { + Object.keys(ROUTES).forEach((key) => { + if (ROUTES[key].indexOf(":") > -1) { + if (ROUTES[key].indexOf(pathNameSplit[1]) > -1) { + localStorageKey = ROUTES[key]; + } + } + }); + } + return localStorageKey; +}; diff --git a/frontend/src/modules/Servicemap/SelectService.tsx b/frontend/src/modules/Servicemap/SelectService.tsx index 8317e87212..016a504fe6 100644 --- a/frontend/src/modules/Servicemap/SelectService.tsx +++ b/frontend/src/modules/Servicemap/SelectService.tsx @@ -4,6 +4,7 @@ import { InfoCircleOutlined } from "@ant-design/icons"; import { Select } from "antd"; import styled from "styled-components"; const { Option } = Select; +import { cloneDeep } from "lodash"; const Container = styled.div` margin-top: 12px; @@ -25,14 +26,25 @@ const Container = styled.div` interface SelectServiceProps { services: servicesItem[]; zoomToService: (arg0: string) => void; + zoomToDefault: () => void; } +const defaultOption = { + serviceName: "Default" +}; + const SelectService = (props: SelectServiceProps) => { - const [selectedVal, setSelectedVal] = useState(); - const { services, zoomToService } = props; + const [selectedVal, setSelectedVal] = useState(defaultOption.serviceName); + const { zoomToService, zoomToDefault } = props; + const services = cloneDeep(props.services); + services.unshift(defaultOption) const handleSelect = (value: string) => { + if(value === defaultOption.serviceName){ + zoomToDefault() + } else { + zoomToService(value); + } setSelectedVal(value); - zoomToService(value); }; return ( diff --git a/frontend/src/modules/Servicemap/ServiceMap.tsx b/frontend/src/modules/Servicemap/ServiceMap.tsx index facc121e35..048c6980b9 100644 --- a/frontend/src/modules/Servicemap/ServiceMap.tsx +++ b/frontend/src/modules/Servicemap/ServiceMap.tsx @@ -10,7 +10,8 @@ import { import { Spin } from "antd"; import styled from "styled-components"; import { StoreState } from "../../store/reducers"; -import { getGraphData, getTooltip, transformLabel } from "./utils"; + +import { getZoomPx, getGraphData, getTooltip, transformLabel } from "./utils"; import SelectService from "./SelectService"; import { ForceGraph2D } from "react-force-graph"; @@ -72,7 +73,11 @@ const ServiceMap = (props: ServiceMapProps) => { } const zoomToService = (value: string) => { - fgRef && fgRef.current.zoomToFit(700, 380, (e) => e.id === value); + fgRef && fgRef.current.zoomToFit(700, getZoomPx(), (e) => e.id === value); + }; + + const zoomToDefault = () => { + fgRef && fgRef.current.zoomToFit(100, 120); }; const { nodes, links } = getGraphData(serviceMap); @@ -82,13 +87,11 @@ const ServiceMap = (props: ServiceMapProps) => { { - fgRef.current.zoomToFit(100, 120); - }} graphData={graphData} nodeLabel={getTooltip} linkAutoColorBy={(d) => d.target} @@ -106,7 +109,7 @@ const ServiceMap = (props: ServiceMapProps) => { ctx.fill(); ctx.textAlign = "center"; ctx.textBaseline = "middle"; - ctx.fillStyle = "#333333"; + ctx.fillStyle = "#646464"; ctx.fillText(label, node.x, node.y); }} onNodeClick={(node) => { diff --git a/frontend/src/modules/Servicemap/utils.ts b/frontend/src/modules/Servicemap/utils.ts index 8aaa5047ad..fb7abfb57a 100644 --- a/frontend/src/modules/Servicemap/utils.ts +++ b/frontend/src/modules/Servicemap/utils.ts @@ -74,6 +74,17 @@ export const getGraphData = (serviceMap: serviceMapStore): graphDataType => { }; }; +export const getZoomPx = (): number => { + const width = window.screen.width; + if (width < 1400) { + return 190; + } else if (width > 1400 && width < 1700) { + return 380; + } else if (width > 1700) { + return 470; + } +}; + export const getTooltip = (node: { p99: number; errorRate: number; diff --git a/frontend/src/store/actions/serviceMap.ts b/frontend/src/store/actions/serviceMap.ts index a8a660d423..fbad4148c3 100644 --- a/frontend/src/store/actions/serviceMap.ts +++ b/frontend/src/store/actions/serviceMap.ts @@ -38,6 +38,11 @@ export interface servicesAction { export const getServiceMapItems = (globalTime: GlobalTime) => { return async (dispatch: Dispatch) => { + dispatch({ + type: ActionTypes.getServiceMapItems, + payload: [], + }); + let request_string = "/serviceMapDependencies?start=" + globalTime.minTime + @@ -45,7 +50,7 @@ export const getServiceMapItems = (globalTime: GlobalTime) => { globalTime.maxTime; const response = await api.get(apiV1 + request_string); - + dispatch({ type: ActionTypes.getServiceMapItems, payload: response.data, @@ -55,11 +60,16 @@ export const getServiceMapItems = (globalTime: GlobalTime) => { export const getDetailedServiceMapItems = (globalTime: GlobalTime) => { return async (dispatch: Dispatch) => { + dispatch({ + type: ActionTypes.getServices, + payload: [], + }); + let request_string = "/services?start=" + globalTime.minTime + "&end=" + globalTime.maxTime; const response = await api.get(apiV1 + request_string); - + dispatch({ type: ActionTypes.getServices, payload: response.data,