diff --git a/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.tsx b/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.tsx index b4220d6ff..c2177f49c 100644 --- a/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.tsx +++ b/ui-common/src/components/modals/TrafficStatsModal/TimelineBarChart/TimelineBarChart.tsx @@ -1,5 +1,5 @@ import styles from "./TimelineBarChart.module.sass"; -import { StatsMode } from "../TrafficStatsModal" +import { ALL_PROTOCOLS, StatsMode } from "../TrafficStatsModal" import React, { useEffect, useMemo, useState } from "react"; import { BarChart, @@ -7,31 +7,33 @@ import { XAxis, YAxis, Tooltip, - Legend } from "recharts"; import { Utils } from "../../../../helpers/Utils"; interface TimelineBarChartProps { timeLineBarChartMode: string; data: any; + selectedProtocol: string; } -export const TimelineBarChart: React.FC = ({ timeLineBarChartMode, data }) => { +export const TimelineBarChart: React.FC = ({ timeLineBarChartMode, data, selectedProtocol }) => { const [protocolStats, setProtocolStats] = useState([]); const [protocolsNamesAndColors, setProtocolsNamesAndColors] = useState([]); + const [commandStats, setCommandStats] = useState(null); + const [commandNames, setcommandNames] = useState(null); useEffect(() => { if (!data) return; const protocolsBarsData = []; const prtcNames = []; data.forEach(protocolObj => { - let obj: { [k: string]: any } = {}; - obj.timestamp = Utils.getHoursAndMinutes(protocolObj.timestamp); + let newProtocolbj: { [k: string]: any } = {}; + newProtocolbj.timestamp = Utils.getHoursAndMinutes(protocolObj.timestamp); protocolObj.protocols.forEach(protocol => { - obj[`${protocol.name}`] = protocol[StatsMode[timeLineBarChartMode]]; + newProtocolbj[`${protocol.name}`] = protocol[StatsMode[timeLineBarChartMode]]; prtcNames.push({ name: protocol.name, color: protocol.color }); }) - protocolsBarsData.push(obj); + protocolsBarsData.push(newProtocolbj); }) const uniqueObjArray = Utils.creatUniqueObjArrayByProp(prtcNames, "name") protocolsBarsData.sort((a, b) => a.timestamp < b.timestamp ? -1 : 1); @@ -39,16 +41,39 @@ export const TimelineBarChart: React.FC = ({ timeLineBarC setProtocolsNamesAndColors(uniqueObjArray); }, [data, timeLineBarChartMode]) - const bars = useMemo(() => protocolsNamesAndColors.map((protocolToDIsplay) => { - return - }), [protocolsNamesAndColors]) + useEffect(() => { + if (selectedProtocol === ALL_PROTOCOLS) { + setCommandStats(null); + setcommandNames(null); + return; + } + const commandsNames = []; + const protocolsCommands = []; + data.forEach(protocolObj => { + let newCommandlbj: { [k: string]: any } = {}; + newCommandlbj.timestamp = Utils.getHoursAndMinutes(protocolObj.timestamp); + protocolObj.protocols.find(protocol => protocol.name === selectedProtocol)?.methods.forEach(command => { + newCommandlbj[`${command.name}`] = command[StatsMode[timeLineBarChartMode]] + if (commandsNames.indexOf(command.name) === -1) + commandsNames.push(command.name); + }) + protocolsCommands.push(newCommandlbj); + }) + protocolsCommands.sort((a, b) => a.timestamp < b.timestamp ? -1 : 1); + setcommandNames(commandsNames); + setCommandStats(protocolsCommands); + }, [data, timeLineBarChartMode, selectedProtocol]) + + const bars = useMemo(() => (commandNames || protocolsNamesAndColors).map((entry) => { + return + }), [protocolsNamesAndColors, commandNames]) return (
{protocolStats.length > 0 && = ({ timeLineBarC timeLineBarChartMode === "VOLUME" ? Utils.humanFileSize(value) : value} /> timeLineBarChartMode === "VOLUME" ? Utils.humanFileSize(value) : value + " Requests"} /> - {bars} }
diff --git a/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.module.sass b/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.module.sass deleted file mode 100644 index 2a874bf83..000000000 --- a/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.module.sass +++ /dev/null @@ -1,16 +0,0 @@ -.breadCrumbsContainer - margin-top: 15px - height: 15px - - .breadCrumbs - color: #494677 - text-align: left - - .clickableTag - margin-right: 5px - border-bottom: 1px black solid - cursor: pointer - - .nonClickableTag - margin-left: 5px - font-weight: 600 diff --git a/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.tsx b/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.tsx index 37d498240..e01ee03c7 100644 --- a/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.tsx +++ b/ui-common/src/components/modals/TrafficStatsModal/TrafficPieChart/TrafficPieChart.tsx @@ -1,21 +1,18 @@ -import React, {useEffect, useMemo, useState} from "react"; -import styles from "./TrafficPieChart.module.sass"; -import {Cell, Legend, Pie, PieChart, Tooltip} from "recharts"; -import {Utils} from "../../../../helpers/Utils"; -import {StatsMode as PieChartMode} from "../TrafficStatsModal" - -const COLORS = ['#e6194b', '#3cb44b', '#ffe119', '#4363d8', '#f58231', '#911eb4', '#46f0f0', '#f032e6', '#bcf60c', '#fabebe', '#008080', '#e6beff', '#9a6324', '#fffac8', '#800000', '#aaffc3', '#808000', '#ffd8b1', '#000075', '#808080', '#ffffff', '#000000']; +import React, { useEffect, useMemo, useState } from "react"; +import { Cell, Legend, Pie, PieChart, Tooltip } from "recharts"; +import { Utils } from "../../../../helpers/Utils"; +import { ALL_PROTOCOLS, StatsMode as PieChartMode } from "../TrafficStatsModal" const RADIAN = Math.PI / 180; const renderCustomizedLabel = ({ - cx, - cy, - midAngle, - innerRadius, - outerRadius, - percent, - index - }: any) => { + cx, + cy, + midAngle, + innerRadius, + outerRadius, + percent, + index +}: any) => { const radius = innerRadius + (outerRadius - innerRadius) * 0.5; const x = cx + radius * Math.cos(-midAngle * RADIAN); const y = cy + radius * Math.sin(-midAngle * RADIAN); @@ -38,13 +35,13 @@ const renderCustomizedLabel = ({ interface TrafficPieChartProps { pieChartMode: string; data: any; + selectedProtocol: string; } -export const TrafficPieChart: React.FC = ({pieChartMode , data}) => { +export const TrafficPieChart: React.FC = ({ pieChartMode, data, selectedProtocol }) => { const [protocolsStats, setProtocolsStats] = useState([]); const [commandStats, setCommandStats] = useState(null); - const [selectedProtocol, setSelectedProtocol] = useState(null as string); useEffect(() => { if (!data) return; @@ -59,11 +56,11 @@ export const TrafficPieChart: React.FC = ({pieChartMode , }, [data, pieChartMode]) useEffect(() => { - if (!selectedProtocol) { + if (selectedProtocol === ALL_PROTOCOLS) { setCommandStats(null); return; } - const commandsPieData = data.find(protocol => protocol.name === selectedProtocol).methods.map(command => { + const commandsPieData = data.find(protocol => protocol.name === selectedProtocol)?.methods.map(command => { return { name: command.name, value: command[PieChartMode[pieChartMode]] @@ -75,18 +72,18 @@ export const TrafficPieChart: React.FC = ({pieChartMode , const pieLegend = useMemo(() => { if (!data) return; let legend; - if (!selectedProtocol) { - legend = data.map(protocol =>
-
- + if (selectedProtocol === ALL_PROTOCOLS) { + legend = data.map(protocol =>
+
+ {protocol.name}
) } else { - legend = data.find(protocol => protocol.name === selectedProtocol).methods.map((method, index) =>
-
- + legend = data.find(protocol => protocol.name === selectedProtocol)?.methods.map((method) =>
+
+ {method.name}
) @@ -96,15 +93,7 @@ export const TrafficPieChart: React.FC = ({pieChartMode , return (
-
- {selectedProtocol &&
- setSelectedProtocol(null)}>protocols - / - {selectedProtocol} -
} -
- - {protocolsStats?.length > 0 &&
+ {protocolsStats?.length > 0 &&
= ({pieChartMode , labelLine={false} label={renderCustomizedLabel} outerRadius={125} - fill="#8884d8" - onClick={(section) => !commandStats && setSelectedProtocol(section.name)}> + fill="#8884d8"> {(commandStats || protocolsStats).map((entry, index) => ( - ) + ) )} - - pieChartMode === "VOLUME" ? Utils.humanFileSize(value) : value + " Requests"}/> + + pieChartMode === "VOLUME" ? Utils.humanFileSize(value) : value + " Requests"} />
}
diff --git a/ui-common/src/components/modals/TrafficStatsModal/TrafficStatsModal.module.sass b/ui-common/src/components/modals/TrafficStatsModal/TrafficStatsModal.module.sass index 8ea631893..23f9efb90 100644 --- a/ui-common/src/components/modals/TrafficStatsModal/TrafficStatsModal.module.sass +++ b/ui-common/src/components/modals/TrafficStatsModal/TrafficStatsModal.module.sass @@ -1,3 +1,6 @@ +.headlineContainer + display: flex + .title color: #494677 font-family: Source Sans Pro,Lucida Grande,Tahoma,sans-serif @@ -13,6 +16,11 @@ padding: 30px text-align: center +.selectContainer + display: flex + justify-content: space-evenly + margin-bottom: 4% + .select border: none border-bottom: 1px black solid diff --git a/ui-common/src/components/modals/TrafficStatsModal/TrafficStatsModal.tsx b/ui-common/src/components/modals/TrafficStatsModal/TrafficStatsModal.tsx index 8e0fbe9d1..b437849fb 100644 --- a/ui-common/src/components/modals/TrafficStatsModal/TrafficStatsModal.tsx +++ b/ui-common/src/components/modals/TrafficStatsModal/TrafficStatsModal.tsx @@ -1,10 +1,12 @@ -import React, { useEffect, useState } from "react"; -import { Backdrop, Box, Fade, Modal } from "@mui/material"; +import React, { useCallback, useEffect, useState } from "react"; +import { Backdrop, Box, Button, debounce, Fade, Modal } from "@mui/material"; import styles from "./TrafficStatsModal.module.sass"; import closeIcon from "assets/close.svg"; import { TrafficPieChart } from "./TrafficPieChart/TrafficPieChart"; import { TimelineBarChart } from "./TimelineBarChart/TimelineBarChart"; import spinnerImg from "assets/spinner.svg"; +import refreshIcon from "assets/refresh.svg"; +import { useCommonStyles } from "../../../helpers/commonStyle"; const modalStyle = { position: 'absolute', @@ -32,15 +34,20 @@ interface TrafficStatsModalProps { getTimelineStatsDataApi: () => Promise } +export const PROTOCOLS = ["ALL PROTOCOLS","gRPC", "REDIS", "HTTP", "GQL", "AMQP", "KFAKA"]; +export const ALL_PROTOCOLS = PROTOCOLS[0]; + export const TrafficStatsModal: React.FC = ({ isOpen, onClose, getPieStatsDataApi, getTimelineStatsDataApi }) => { const modes = Object.keys(StatsMode).filter(x => !(parseInt(x) >= 0)); const [statsMode, setStatsMode] = useState(modes[0]); + const [selectedProtocol, setSelectedProtocol] = useState("ALL PROTOCOLS"); const [pieStatsData, setPieStatsData] = useState(null); const [timelineStatsData, setTimelineStatsData] = useState(null); const [isLoading, setIsLoading] = useState(false); + const commonClasses = useCommonStyles(); - useEffect(() => { + const getTrafficStats = useCallback(async () => { if (isOpen && getPieStatsDataApi) { (async () => { try { @@ -58,6 +65,14 @@ export const TrafficStatsModal: React.FC = ({ isOpen, on } }, [isOpen, getPieStatsDataApi, getTimelineStatsDataApi, setPieStatsData, setTimelineStatsData]) + useEffect(() => { + getTrafficStats(); + }, [getTrafficStats]) + + const refreshStats = debounce(() => { + getTrafficStats(); + }, 500); + return ( = ({ isOpen, on
close onClose()} style={{ cursor: "pointer", userSelect: "none" }} />
-
Traffic Statistics
+
+
Traffic Statistics
+ +
-
- Breakdown By - +
+
+ Breakdown By + +
+
+ Protocol + +
{isLoading ?
spinner -
: +
:
- - + +
}
diff --git a/ui-common/src/components/modals/TrafficStatsModal/assets/refresh.svg b/ui-common/src/components/modals/TrafficStatsModal/assets/refresh.svg new file mode 100644 index 000000000..de0615856 --- /dev/null +++ b/ui-common/src/components/modals/TrafficStatsModal/assets/refresh.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui-common/src/helpers/Utils.ts b/ui-common/src/helpers/Utils.ts index 65744cb23..ad0273abb 100644 --- a/ui-common/src/helpers/Utils.ts +++ b/ui-common/src/helpers/Utils.ts @@ -42,4 +42,17 @@ export class Utils { return Array.from(map); } + static stringToColor = (str) => { + let colors = ["#e51c23", "#e91e63", "#9c27b0", "#673ab7", "#3f51b5", "#5677fc", "#03a9f4", "#00bcd4", "#009688", "#259b24", "#8bc34a", "#afb42b", "#ff9800", "#ff5722", "#795548", "#607d8b"] + + let hash = 0; + if (str.length === 0) return hash; + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + hash = hash & hash; + } + hash = ((hash % colors.length) + colors.length) % colors.length; + return colors[hash]; +} + }