mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-08-28 05:13:46 +00:00
time picker added
This commit is contained in:
parent
c010d336bb
commit
20d3d54b4b
2759
ui-common/package-lock.json
generated
2759
ui-common/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -26,14 +26,15 @@
|
||||
"@craco/craco": "^6.4.3",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/node": "^12.20.54",
|
||||
"sass": "^1.52.3",
|
||||
"react": "^17.0.2",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^17.0.2",
|
||||
"recoil": "^0.7.2"
|
||||
"recoil": "^0.7.2",
|
||||
"sass": "^1.52.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@craco/craco": "^6.4.3",
|
||||
"@elastic/eui": "^60.2.0",
|
||||
"@emotion/react": "^11.9.0",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/icons-material": "^5.8.2",
|
||||
|
@ -0,0 +1,91 @@
|
||||
import React, { useState, Fragment } from 'react';
|
||||
|
||||
import {
|
||||
EuiSuperDatePicker,
|
||||
EuiSpacer,
|
||||
} from '@elastic/eui';
|
||||
import dateMath from '@elastic/datemath';
|
||||
|
||||
interface TimeRangePickerProps {
|
||||
refreshStats: (startTime, endTime) => void;
|
||||
}
|
||||
|
||||
export const TimeRangePicker: React.FC<TimeRangePickerProps> = ({ refreshStats }) => {
|
||||
const [recentlyUsedRanges, setRecentlyUsedRanges] = useState([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [start, setStart] = useState('now-30m');
|
||||
const [end, setEnd] = useState('now');
|
||||
const [isPaused, setIsPaused] = useState(true);
|
||||
const [refreshInterval, setRefreshInterval] = useState();
|
||||
|
||||
const dateConvertor = (inputStart, inputEnd) => {
|
||||
const startMoment = dateMath.parse(inputStart);
|
||||
if (!startMoment || !startMoment.isValid()) {
|
||||
throw new Error('Unable to parse start string');
|
||||
}
|
||||
const endMoment = dateMath.parse(inputEnd, { roundUp: true });
|
||||
if (!endMoment || !endMoment.isValid()) {
|
||||
throw new Error('Unable to parse end string');
|
||||
}
|
||||
return { startMoment: startMoment.format("x"), endMoment: endMoment.format("x") }
|
||||
}
|
||||
|
||||
const onTimeChange = ({ start, end }) => {
|
||||
const recentlyUsedRange = recentlyUsedRanges.filter(recentlyUsedRange => {
|
||||
const isDuplicate =
|
||||
recentlyUsedRange.start === start && recentlyUsedRange.end === end;
|
||||
return !isDuplicate;
|
||||
});
|
||||
recentlyUsedRange.unshift({ start, end });
|
||||
setStart(start);
|
||||
setEnd(end);
|
||||
setRecentlyUsedRanges(
|
||||
recentlyUsedRange.length > 10
|
||||
? recentlyUsedRange.slice(0, 9)
|
||||
: recentlyUsedRange
|
||||
);
|
||||
const { startMoment, endMoment } = dateConvertor(start, end)
|
||||
refreshStats(startMoment, endMoment)
|
||||
setIsLoading(true);
|
||||
startLoading();
|
||||
};
|
||||
|
||||
const onRefresh = ({ start, end, refreshInterval }) => {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(resolve, 100);
|
||||
}).then(() => {
|
||||
const { startMoment, endMoment } = dateConvertor(start,end)
|
||||
refreshStats(startMoment, endMoment)
|
||||
});
|
||||
};
|
||||
|
||||
const startLoading = () => {
|
||||
setTimeout(stopLoading, 1000);
|
||||
};
|
||||
const stopLoading = () => {
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const onRefreshChange = ({ isPaused, refreshInterval }) => {
|
||||
setIsPaused(isPaused);
|
||||
setRefreshInterval(refreshInterval);
|
||||
};
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiSpacer />
|
||||
<EuiSuperDatePicker
|
||||
isLoading={isLoading}
|
||||
start={start}
|
||||
end={end}
|
||||
onTimeChange={onTimeChange}
|
||||
onRefresh={onRefresh}
|
||||
isPaused={isPaused}
|
||||
refreshInterval={refreshInterval}
|
||||
onRefreshChange={onRefreshChange}
|
||||
recentlyUsedRanges={recentlyUsedRanges}
|
||||
/>
|
||||
<EuiSpacer />
|
||||
</Fragment>
|
||||
);
|
||||
};
|
@ -19,6 +19,7 @@
|
||||
.selectContainer
|
||||
display: flex
|
||||
justify-content: space-evenly
|
||||
align-items: center
|
||||
margin-bottom: 4%
|
||||
|
||||
.select
|
||||
|
@ -1,13 +1,12 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { Backdrop, Box, Button, debounce, Fade, Modal } from "@mui/material";
|
||||
import { Backdrop, Box, 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 refreshIcon from "assets/refresh.svg";
|
||||
import { useCommonStyles } from "../../../helpers/commonStyle";
|
||||
import { LoadingWrapper } from "../../UI/withLoading/withLoading";
|
||||
import { ALL_PROTOCOLS, StatsMode } from "./consts";
|
||||
import { TimeRangePicker } from "./TimelineBarChart/TimeRangePicker/TimeTangePicker";
|
||||
|
||||
const modalStyle = {
|
||||
position: 'absolute',
|
||||
@ -15,7 +14,7 @@ const modalStyle = {
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, 0%)',
|
||||
width: '60vw',
|
||||
height: '82vh',
|
||||
height: '90vh',
|
||||
bgcolor: 'background.paper',
|
||||
borderRadius: '5px',
|
||||
boxShadow: 24,
|
||||
@ -26,11 +25,10 @@ const modalStyle = {
|
||||
interface TrafficStatsModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
getTrafficStatsDataApi: () => Promise<any>
|
||||
getTrafficStatsDataApi: (start?, end?) => Promise<any>
|
||||
}
|
||||
|
||||
export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, onClose, getTrafficStatsDataApi }) => {
|
||||
|
||||
const modes = Object.keys(StatsMode).filter(x => !(parseInt(x) >= 0));
|
||||
const [statsMode, setStatsMode] = useState(modes[0]);
|
||||
const [selectedProtocol, setSelectedProtocol] = useState(ALL_PROTOCOLS);
|
||||
@ -38,14 +36,16 @@ export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, on
|
||||
const [timelineStatsData, setTimelineStatsData] = useState(null);
|
||||
const [protocols, setProtocols] = useState([])
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const commonClasses = useCommonStyles();
|
||||
|
||||
const getTrafficStats = useCallback(async () => {
|
||||
const now = new Date().getTime();
|
||||
const halfAnHourAgo = new Date().getTime() - (30 * 60 * 1000);
|
||||
|
||||
const getTrafficStats = useCallback(async (startTime, endTime) => {
|
||||
if (isOpen && getTrafficStatsDataApi) {
|
||||
(async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const statsData = await getTrafficStatsDataApi();
|
||||
const statsData = await getTrafficStatsDataApi(startTime, endTime);
|
||||
setPieStatsData(statsData.pie);
|
||||
setTimelineStatsData(statsData.timeline);
|
||||
setProtocols(statsData.protocols)
|
||||
@ -59,11 +59,11 @@ export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, on
|
||||
}, [isOpen, getTrafficStatsDataApi, setPieStatsData, setTimelineStatsData])
|
||||
|
||||
useEffect(() => {
|
||||
getTrafficStats();
|
||||
getTrafficStats(halfAnHourAgo,now);
|
||||
}, [getTrafficStats])
|
||||
|
||||
const refreshStats = debounce(() => {
|
||||
getTrafficStats();
|
||||
const refreshStats = debounce((newStartTime, newEndTime) => {
|
||||
getTrafficStats(newStartTime,newEndTime);
|
||||
}, 500);
|
||||
|
||||
return (
|
||||
@ -82,18 +82,12 @@ export const TrafficStatsModal: React.FC<TrafficStatsModalProps> = ({ isOpen, on
|
||||
</div>
|
||||
<div className={styles.headlineContainer}>
|
||||
<div className={styles.title}>Traffic Statistics</div>
|
||||
<Button style={{ marginLeft: "2%", textTransform: 'unset' }}
|
||||
startIcon={<img src={refreshIcon} className="custom" alt="refresh"></img>}
|
||||
size="medium"
|
||||
variant="contained"
|
||||
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
|
||||
onClick={refreshStats}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.mainContainer}>
|
||||
<div className={styles.selectContainer}>
|
||||
<div>
|
||||
<TimeRangePicker refreshStats={refreshStats} />
|
||||
</div>
|
||||
<div>
|
||||
<span style={{ marginRight: 15 }}>Breakdown By</span>
|
||||
<select className={styles.select} value={statsMode} onChange={(e) => setStatsMode(e.target.value)}>
|
||||
|
2280
ui/package-lock.json
generated
2280
ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,8 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@craco/craco": "^6.4.3",
|
||||
"@elastic/datemath": "^5.0.3",
|
||||
"@elastic/eui": "^60.2.0",
|
||||
"@emotion/react": "^11.9.0",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"@mui/material": "^5.8.2",
|
||||
@ -23,7 +25,6 @@
|
||||
"mobx": "^6.6.0",
|
||||
"moment": "^2.29.3",
|
||||
"node-fetch": "^3.2.4",
|
||||
"sass": "^1.52.3",
|
||||
"numeral": "^2.0.6",
|
||||
"react": "^17.0.2",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
@ -35,6 +36,7 @@
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-toastify": "^8.2.0",
|
||||
"redoc": "^2.0.0-rc.71",
|
||||
"sass": "^1.52.3",
|
||||
"styled-components": "^5.3.5",
|
||||
"typescript": "^4.7.2",
|
||||
"web-vitals": "^2.1.4",
|
||||
@ -49,7 +51,7 @@
|
||||
"recoil": "^0.7.2"
|
||||
},
|
||||
"scripts": {
|
||||
"prestart": "../devops/ui-common-pack.sh $PWD",
|
||||
"_prestart": "../devops/ui-common-pack.sh $PWD",
|
||||
"start": "craco start",
|
||||
"start-dev": "./node_modules/.bin/env-cmd -f .env.dev.basic craco start",
|
||||
"build": "./node_modules/.bin/env-cmd -f .env.basic craco build",
|
||||
|
@ -9,6 +9,7 @@
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<meta name="eui-style-insert">
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
|
@ -10,6 +10,8 @@ import { OasModal } from '@up9/mizu-common';
|
||||
import Api from './helpers/api';
|
||||
import {ThemeProvider, StyledEngineProvider, createTheme} from '@mui/material';
|
||||
import { TrafficStatsModal } from '@up9/mizu-common';
|
||||
import { EuiProvider } from '@elastic/eui';
|
||||
import '@elastic/eui/dist/eui_theme_light.css';
|
||||
|
||||
const api = Api.getInstance()
|
||||
|
||||
@ -20,6 +22,7 @@ const App = () => {
|
||||
const [trafficStatsModalOpen, setTrafficStatsModalOpen] = useRecoilState(trafficStatsModalOpenAtom);
|
||||
|
||||
return (
|
||||
<EuiProvider>
|
||||
<StyledEngineProvider injectFirst>
|
||||
<ThemeProvider theme={createTheme(({}))}>
|
||||
<div className="mizuApp">
|
||||
@ -40,6 +43,7 @@ const App = () => {
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</StyledEngineProvider>
|
||||
</EuiProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -116,8 +116,8 @@ export default class Api {
|
||||
});
|
||||
}
|
||||
|
||||
getTrafficStats = async () => {
|
||||
const response = await client.get("/status/trafficStats");
|
||||
getTrafficStats = async (startTimeMs, endTimeMs) => {
|
||||
const response = await client.get("/status/trafficStats", {params: {startTimeMs, endTimeMs}});
|
||||
return response.data;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user