mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-07-19 10:48:59 +00:00
UI/Service-map-GUI-improvements (#1055)
* filters visble on click * servicemap layout * headers added Icon changed * flex removed , we have max size for filter * added mixins * search input caption * PR commemts * changed colors to variables * lint fix small buttom style fix Co-authored-by: Leon <>
This commit is contained in:
parent
eef0ee8023
commit
b2830f133f
22
ui-common/src/components.scss
Normal file
22
ui-common/src/components.scss
Normal file
@ -0,0 +1,22 @@
|
||||
.subSectionHeader{
|
||||
position: relative;
|
||||
font-style: normal;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
color: $font-color;
|
||||
&::after{
|
||||
content: "";
|
||||
border: 1px solid #E9EBF8;
|
||||
transform: rotate(180deg);
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: -100%;
|
||||
top: 100%;
|
||||
bottom: 0%;
|
||||
width: 100%;
|
||||
width: -moz-available;
|
||||
width: -webkit-fill-available;
|
||||
width: strech;
|
||||
}
|
||||
}
|
@ -1,34 +1,67 @@
|
||||
@import "../../variables.module"
|
||||
@import "../../components"
|
||||
|
||||
.closeIcon
|
||||
position: absolute
|
||||
right: 20px
|
||||
top: 20px
|
||||
|
||||
.modalContainer
|
||||
display: flex
|
||||
height: calc(100% - 110px)
|
||||
background: #F0F5FF
|
||||
padding: 0 15px
|
||||
padding-bottom: 25px
|
||||
|
||||
.headerContainer
|
||||
width: 100%
|
||||
height: 100%
|
||||
background: white
|
||||
display: flex
|
||||
align-items: center
|
||||
margin-bottom: 15px
|
||||
|
||||
.headerSection
|
||||
display: flex
|
||||
align-content: center
|
||||
align-items: center
|
||||
margin-left: 35px
|
||||
margin-bottom: 25px
|
||||
margin-top: 25px
|
||||
|
||||
& .title
|
||||
font-size: 28px
|
||||
color: $blue-gray
|
||||
font-weight: 600
|
||||
margin-right: 35px
|
||||
|
||||
& .actions
|
||||
|
||||
.graphSection
|
||||
flex: 85%
|
||||
|
||||
.filterSection
|
||||
flex: 15%
|
||||
height: 100%
|
||||
display: none
|
||||
|
||||
&.show
|
||||
display: inline-block
|
||||
|
||||
.filters table
|
||||
margin-top: 0px
|
||||
|
||||
tr
|
||||
border-style: none
|
||||
|
||||
td
|
||||
color: #8f9bb2
|
||||
color: $light-gray
|
||||
font-size: 11px
|
||||
font-weight: 600
|
||||
padding-top: 2px
|
||||
padding-bottom: 2px
|
||||
padding-top: 5px
|
||||
padding-bottom: 5px
|
||||
th
|
||||
font-size: 12px
|
||||
|
||||
.colorBlock
|
||||
display: inline-block
|
||||
height: 15px
|
||||
width: 50px
|
||||
height: 12px
|
||||
width: 22px
|
||||
|
||||
.filterWrapper
|
||||
height: 100%
|
||||
@ -36,29 +69,35 @@
|
||||
flex-direction: column
|
||||
margin-right: 10px
|
||||
width: 100%
|
||||
border-radius: 4px
|
||||
|
||||
.servicesFilterSearch
|
||||
width: calc(100% - 10px)
|
||||
max-width: 300px
|
||||
width: -moz-available
|
||||
width: -webkit-fill-available
|
||||
width: fill-available
|
||||
max-width: 200px
|
||||
box-shadow: 0px 1px 5px #979797
|
||||
margin-left: 10px
|
||||
margin-bottom: 5px
|
||||
margin-top: 10px
|
||||
margin-right: 10px
|
||||
|
||||
.protocolsFilterList, .servicesFilter
|
||||
background: white
|
||||
padding: 10px
|
||||
border-radius: 4px
|
||||
user-select: none
|
||||
|
||||
.servicesFilter
|
||||
margin-top: 15px
|
||||
margin-top: 10px
|
||||
height: 100%
|
||||
overflow: hidden
|
||||
border-radius: 4px
|
||||
|
||||
& .servicesFilterList
|
||||
overflow-y: auto
|
||||
height: calc(100% - 30px - 5px)
|
||||
height: calc(100% - 30px - 52px)
|
||||
|
||||
.separtorLine
|
||||
margin-top: 10px
|
||||
border: 1px solid #E9EBF8
|
||||
|
||||
.closeIcon
|
||||
cursor: pointer
|
||||
user-select: none
|
||||
margin-top: -15px
|
||||
margin-right: 5px
|
||||
.totalSelected
|
||||
font-size: 12px
|
||||
color: $light-blue-color
|
||||
font-weight: 700
|
||||
|
@ -8,6 +8,8 @@ import debounce from 'lodash/debounce';
|
||||
import ServiceMapOptions from './ServiceMapOptions'
|
||||
import { useCommonStyles } from "../../helpers/commonStyle";
|
||||
import refreshIcon from "assets/refresh.svg";
|
||||
import filterIcon from "assets/filter-icon.svg";
|
||||
import filterIconClicked from "assets/filter-icon-clicked.svg";
|
||||
import closeIcon from "assets/close.svg"
|
||||
import styles from './ServiceMapModal.module.sass'
|
||||
import SelectList from "../UI/SelectList";
|
||||
@ -23,14 +25,14 @@ const modalStyle = {
|
||||
transform: 'translate(-50%, 0%)',
|
||||
width: '89vw',
|
||||
height: '82vh',
|
||||
bgcolor: 'background.paper',
|
||||
bgcolor: '#F0F5FF',
|
||||
borderRadius: '5px',
|
||||
boxShadow: 24,
|
||||
p: 4,
|
||||
color: '#000',
|
||||
padding: "25px 15px"
|
||||
padding: "1px 1px",
|
||||
paddingBottom: "15px"
|
||||
};
|
||||
|
||||
interface LegentLabelProps {
|
||||
color: string,
|
||||
name: string
|
||||
@ -46,13 +48,13 @@ const LegentLabel: React.FC<LegentLabelProps> = ({ color, name }) => {
|
||||
}
|
||||
|
||||
const protocols = [
|
||||
{ key: "HTTP", value: "HTTP", component: <LegentLabel color="#205cf5" name="HTTP" /> },
|
||||
{ key: "HTTP/2", value: "HTTP/2", component: <LegentLabel color='#244c5a' name="HTTP/2" /> },
|
||||
{ key: "gRPC", value: "gRPC", component: <LegentLabel color='#244c5a' name="gRPC" /> },
|
||||
{ key: "HTTP", value: "HTTP", component: <LegentLabel color="#494677" name="HTTP" /> },
|
||||
{ key: "HTTP/2", value: "HTTP/2", component: <LegentLabel color='#F7B202' name="HTTP/2" /> },
|
||||
{ key: "gRPC", value: "gRPC", component: <LegentLabel color='#219653' name="gRPC" /> },
|
||||
{ key: "GQL", value: "GQL", component: <LegentLabel color='#e10098' name="GQL" /> },
|
||||
{ key: "AMQP", value: "AMQP", component: <LegentLabel color='#ff6600' name="AMQP" /> },
|
||||
{ key: "KAFKA", value: "KAFKA", component: <LegentLabel color='#000000' name="KAFKA" /> },
|
||||
{ key: "REDIS", value: "REDIS", component: <LegentLabel color='#a41e11' name="REDIS" /> },]
|
||||
{ key: "AMQP", value: "AMQP", component: <LegentLabel color='#F86818' name="AMQP" /> },
|
||||
{ key: "KAFKA", value: "KAFKA", component: <LegentLabel color='#0C0B1A' name="KAFKA" /> },
|
||||
{ key: "REDIS", value: "REDIS", component: <LegentLabel color='#DB2156' name="REDIS" /> },]
|
||||
|
||||
|
||||
interface ServiceMapModalProps {
|
||||
@ -71,6 +73,7 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
const [serviceMapApiData, setServiceMapApiData] = useState<ServiceMapGraph>({ edges: [], nodes: [] })
|
||||
const [servicesSearchVal, setServicesSearchVal] = useState("")
|
||||
const [graphOptions, setGraphOptions] = useState(ServiceMapOptions);
|
||||
const [isFilterClicked, setIsFilterClicked] = useState(true)
|
||||
|
||||
const getServiceMapData = useCallback(async () => {
|
||||
try {
|
||||
@ -113,15 +116,15 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
},
|
||||
}
|
||||
}
|
||||
const mapToKeyValForFilter = (arr) => arr.map(mapNodesDatatoGraph)
|
||||
const mapToKeyValForFilter = useCallback((arr) => arr.map(mapNodesDatatoGraph)
|
||||
.map((edge) => { return { key: edge.label, value: edge.label } })
|
||||
.sort((a, b) => { return a.key.localeCompare(b.key) });
|
||||
.sort((a, b) => { return a.key.localeCompare(b.key) }), [])
|
||||
|
||||
const getServicesForFilter = useMemo(() => {
|
||||
const resolved = mapToKeyValForFilter(serviceMapApiData.nodes?.filter(x => x.resolved))
|
||||
const unResolved = mapToKeyValForFilter(serviceMapApiData.nodes?.filter(x => !x.resolved))
|
||||
return [...resolved, ...unResolved]
|
||||
}, [serviceMapApiData])
|
||||
}, [mapToKeyValForFilter, serviceMapApiData.nodes])
|
||||
|
||||
useEffect(() => {
|
||||
const newGraphData: GraphData = {
|
||||
@ -142,8 +145,9 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (checkedServices.length == 0)
|
||||
if (checkedServices.length === 0)
|
||||
setCheckedServices(getServicesForFilter.map(x => x.key).filter(serviceName => !Utils.isIpAddress(serviceName)))
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [getServicesForFilter])
|
||||
|
||||
useEffect(() => {
|
||||
@ -173,19 +177,52 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
BackdropProps={{ timeout: 500 }}>
|
||||
<Fade in={isOpen}>
|
||||
<Box sx={modalStyle}>
|
||||
<div className={styles.closeIcon}>
|
||||
<img src={closeIcon} alt="close" onClick={() => onClose()} style={{ cursor: "pointer", userSelect: "none" }}></img>
|
||||
</div>
|
||||
<div className={styles.headerContainer}>
|
||||
<div className={styles.headerSection}>
|
||||
<span className={styles.title}>Services</span>
|
||||
<Button size="medium"
|
||||
variant="contained"
|
||||
startIcon={<img src={isFilterClicked ? filterIconClicked : filterIcon} className="custom" alt="refresh" style={{ height: "26px", width: "26px" }}></img>}
|
||||
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton + ` ${isFilterClicked ? commonClasses.clickedButton : ""}`}
|
||||
onClick={() => setIsFilterClicked(prevState => !prevState)}
|
||||
style={{ textTransform: 'unset' }}>
|
||||
Filter
|
||||
</Button >
|
||||
<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={refreshServiceMap}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={styles.modalContainer}>
|
||||
<div className={styles.filterSection}>
|
||||
<div className={styles.filterSection + ` ${isFilterClicked ? styles.show : ""}`}>
|
||||
<Resizeable minWidth={170} maxWidth={320}>
|
||||
<div className={styles.filterWrapper}>
|
||||
<div className={styles.protocolsFilterList}>
|
||||
<SelectList items={protocols} checkBoxWidth="5%" tableName={"Protocols"} multiSelect={true}
|
||||
<h3 className={styles.subSectionHeader} style={{ marginLeft: "10px" }}>
|
||||
PROTOCOLS
|
||||
<span className={styles.totalSelected}> ({checkedProtocols.length})</span>
|
||||
</h3>
|
||||
<SelectList items={protocols} checkBoxWidth="5%" tableName={"All"} multiSelect={true}
|
||||
checkedValues={checkedProtocols} setCheckedValues={onProtocolsChange} tableClassName={styles.filters} />
|
||||
</div>
|
||||
<div className={styles.separtorLine}></div>
|
||||
<div className={styles.servicesFilter}>
|
||||
<input className={commonClasses.textField + ` ${styles.servicesFilterSearch}`} placeholder="search service" value={servicesSearchVal} onChange={(event) => setServicesSearchVal(event.target.value)} />
|
||||
<h3 className={styles.subSectionHeader} style={{ marginLeft: "10px" }}>
|
||||
SERVICES
|
||||
<span className={styles.totalSelected}> ({checkedServices.length})</span>
|
||||
</h3>
|
||||
<input className={commonClasses.textField + ` ${styles.servicesFilterSearch}`} placeholder="Search" value={servicesSearchVal} onChange={(event) => setServicesSearchVal(event.target.value)} />
|
||||
<div className={styles.servicesFilterList}>
|
||||
<SelectList items={getServicesForFilter} tableName={"Services"} tableClassName={styles.filters} multiSelect={true} searchValue={servicesSearchVal}
|
||||
<SelectList items={getServicesForFilter} tableName={"All"} tableClassName={styles.filters} multiSelect={true} searchValue={servicesSearchVal}
|
||||
checkBoxWidth="5%" checkedValues={checkedServices} setCheckedValues={onServiceChanges} />
|
||||
</div>
|
||||
</div>
|
||||
@ -194,16 +231,6 @@ export const ServiceMapModal: React.FC<ServiceMapModalProps> = ({ isOpen, onClos
|
||||
</div>
|
||||
<div className={styles.graphSection}>
|
||||
<div style={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Button style={{ marginLeft: "3%" }}
|
||||
startIcon={<img src={refreshIcon} className="custom" alt="refresh" style={{ marginRight: "8%" }}></img>}
|
||||
size="medium"
|
||||
variant="contained"
|
||||
className={commonClasses.outlinedButton + " " + commonClasses.imagedButton}
|
||||
onClick={refreshServiceMap}
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
<img src={closeIcon} alt="close" onClick={() => onClose()} className={styles.closeIcon}></img>
|
||||
</div>
|
||||
{isLoading && <div className={spinnerStyle.spinnerContainer}>
|
||||
<img alt="spinner" src={spinnerImg} style={{ height: 50 }} />
|
||||
|
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 8L10.6667 12.6667V18H13.3333V12.6667L18 8V6H6V8Z" stroke="white"/>
|
||||
</svg>
|
After Width: | Height: | Size: 182 B |
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 8L10.6667 12.6667V18H13.3333V12.6667L18 8V6H6V8Z" stroke="#205CF5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 184 B |
@ -72,7 +72,9 @@ const SelectList: React.FC<Props> = ({ items, tableName, checkedValues = [], mul
|
||||
const tableHead = multiSelect ? <tr style={{ borderBottomWidth: "2px" }}>
|
||||
<th style={{ width: checkBoxWidth }}><Checkbox data-cy="checkbox-all" checked={headerChecked}
|
||||
onToggle={(isChecked) => toggleAll(isChecked)} /></th>
|
||||
<th>{tableName}</th>
|
||||
<th>
|
||||
{tableName}
|
||||
</th>
|
||||
</tr> :
|
||||
<tr style={{ borderBottomWidth: "2px" }}>
|
||||
<th>{tableName}</th>
|
||||
|
@ -5,7 +5,6 @@
|
||||
align-items: center
|
||||
display: flex
|
||||
overflow: hidden
|
||||
border-right: 1px solid $blue-color
|
||||
height: 100%
|
||||
width: 100%
|
||||
padding-right: 3px
|
||||
|
@ -1,4 +1,5 @@
|
||||
import {makeStyles} from "@material-ui/core";
|
||||
import { makeStyles } from "@material-ui/core";
|
||||
import variables from "../variables.module.scss"
|
||||
|
||||
// @ts-ignore
|
||||
export const useCommonStyles = makeStyles(() => ({
|
||||
@ -9,11 +10,10 @@ export const useCommonStyles = makeStyles(() => ({
|
||||
fontSize: 12,
|
||||
padding: "9px 12px",
|
||||
borderRadius: "6px ! important",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: "#205cf5",
|
||||
},
|
||||
"&:disabled":{
|
||||
"&:disabled": {
|
||||
backgroundColor: "rgba(0, 0, 0, 0.26)"
|
||||
}
|
||||
},
|
||||
@ -25,12 +25,17 @@ export const useCommonStyles = makeStyles(() => ({
|
||||
padding: "8px 12px",
|
||||
border: "1px #205cf5 solid",
|
||||
borderRadius: "6px ! important",
|
||||
|
||||
"&:hover": {
|
||||
backgroundColor: "transparent",
|
||||
},
|
||||
},
|
||||
|
||||
clickedButton: {
|
||||
color: "white",
|
||||
backgroundColor: "#205cf5",
|
||||
"&:hover": {
|
||||
backgroundColor: "#205cf5",
|
||||
},
|
||||
},
|
||||
imagedButton: {
|
||||
padding: "1px 14px"
|
||||
},
|
||||
@ -46,7 +51,7 @@ export const useCommonStyles = makeStyles(() => ({
|
||||
height: "30px",
|
||||
boxSizing: "border-box"
|
||||
},
|
||||
modal :{
|
||||
modal: {
|
||||
position: 'absolute',
|
||||
top: '40%',
|
||||
left: '50%',
|
||||
|
@ -7,6 +7,10 @@ $blue-color: #205CF5;
|
||||
$light-blue-color: #BCCEFD;
|
||||
$success-color: #27AE60;
|
||||
$failure-color: #EB5757;
|
||||
|
||||
$header-section-color : #fbfcfe;
|
||||
$content-section-color: #f8f9fc;
|
||||
|
||||
$blue-gray: #494677;
|
||||
$light-gray: #8F9BB2;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user