diff --git a/ui/src/components/EntryDetailed.tsx b/ui/src/components/EntryDetailed.tsx index f8854a293..9a331a6ba 100644 --- a/ui/src/components/EntryDetailed.tsx +++ b/ui/src/components/EntryDetailed.tsx @@ -3,6 +3,7 @@ import EntryViewer from "./EntryDetailed/EntryViewer"; import {EntryItem} from "./EntryListItem/EntryListItem"; import {makeStyles} from "@material-ui/core"; import Protocol from "./UI/Protocol" +import Queryable from "./UI/Queryable"; const useStyles = makeStyles(() => ({ entryTitle: { @@ -37,28 +38,33 @@ const EntryTitle: React.FC = ({protocol, data, bodySize, elapsedTime, updat const classes = useStyles(); const response = data.response; - return
- +
- {response &&
{ - updateQuery(`response.bodySize == ${bodySize}`) - }} + {response && - {formatSize(bodySize)} -
} - {response &&
{ - updateQuery(`elapsedTime >= ${elapsedTime}`) - }} +
+ {formatSize(bodySize)} +
+ } + {response && = ${elapsedTime}`} + updateQuery={updateQuery} + style={{marginRight: 18}} + displayIconOnMouseOver={true} > - {Math.round(elapsedTime)}ms -
} +
+ {Math.round(elapsedTime)}ms +
+ }
; }; diff --git a/ui/src/components/EntryDetailed/EntrySections.module.sass b/ui/src/components/EntryDetailed/EntrySections.module.sass index a7ec762ca..b1b8471f6 100644 --- a/ui/src/components/EntryDetailed/EntrySections.module.sass +++ b/ui/src/components/EntryDetailed/EntrySections.module.sass @@ -27,7 +27,7 @@ font-weight: 600 font-size: .75rem line-height: 1.2 - margin: .3rem 0 + margin-bottom: -2px .dataKey color: $blue-gray diff --git a/ui/src/components/EntryDetailed/EntrySections.tsx b/ui/src/components/EntryDetailed/EntrySections.tsx index 88db7b310..1d183d279 100644 --- a/ui/src/components/EntryDetailed/EntrySections.tsx +++ b/ui/src/components/EntryDetailed/EntrySections.tsx @@ -3,6 +3,7 @@ import React, {useState} from "react"; import {SyntaxHighlighter} from "../UI/SyntaxHighlighter/index"; import CollapsibleContainer from "../UI/CollapsibleContainer"; import FancyTextDisplay from "../UI/FancyTextDisplay"; +import Queryable from "../UI/Queryable"; import Checkbox from "../UI/Checkbox"; import ProtobufDecoder from "protobuf-decoder"; @@ -15,23 +16,29 @@ interface EntryViewLineProps { } const EntryViewLine: React.FC = ({label, value, updateQuery, selector, overrideQueryValue}) => { + let query: string; + if (!selector) { + query = ""; + } else if (overrideQueryValue) { + query = `${selector} == ${overrideQueryValue}`; + } else if (typeof(value) == "string") { + query = `${selector} == "${JSON.stringify(value).slice(1, -1)}"`; + } else { + query = `${selector} == ${value}`; + } return (label && - { - if (!selector) { - return - } else if (overrideQueryValue) { - updateQuery(`${selector} == ${overrideQueryValue}`) - } else if (typeof(value) === "string") { - updateQuery(`${selector} == "${JSON.stringify(value).slice(1, -1)}"`) - } else { - updateQuery(`${selector} == ${value}`) - } - }} - > - {label} - + + + {label} + + = ({title, color, isExpanded}) => { return
- +
{isExpanded ? '-' : '+'} - +
{title}
} diff --git a/ui/src/components/EntryListItem/EntryListItem.module.sass b/ui/src/components/EntryListItem/EntryListItem.module.sass index 5cf3ed227..296404727 100644 --- a/ui/src/components/EntryListItem/EntryListItem.module.sass +++ b/ui/src/components/EntryListItem/EntryListItem.module.sass @@ -19,7 +19,6 @@ .rowSelected border: 1px $blue-color solid - margin-right: 3px .ruleSuccessRow background: #E8FFF1 @@ -52,7 +51,6 @@ white-space: nowrap color: $secondary-font-color padding-left: 4px - padding-top: 3px padding-right: 10px display: flex font-size: 12px @@ -70,8 +68,9 @@ flex-direction: column overflow: hidden padding-right: 10px - padding-left: 10px flex-grow: 1 + padding-top: 5px + margin-left: -6px .separatorRight display: flex diff --git a/ui/src/components/EntryListItem/EntryListItem.tsx b/ui/src/components/EntryListItem/EntryListItem.tsx index 4461b4f70..b18e2e2f9 100644 --- a/ui/src/components/EntryListItem/EntryListItem.tsx +++ b/ui/src/components/EntryListItem/EntryListItem.tsx @@ -3,6 +3,7 @@ import styles from './EntryListItem.module.sass'; import StatusCode, {getClassification, StatusCodeClassification} from "../UI/StatusCode"; import Protocol, {ProtocolInterface} from "../UI/Protocol" import {Summary} from "../UI/Summary"; +import Queryable from "../UI/Queryable"; import ingoingIconSuccess from "../assets/ingoing-traffic-success.svg" import ingoingIconFailure from "../assets/ingoing-traffic-failure.svg" import ingoingIconNeutral from "../assets/ingoing-traffic-neutral.svg" @@ -131,7 +132,7 @@ export const EntryItem: React.FC = ({entry, focusedEntryId, setFocus border: isSelected ? `1px ${entry.protocol.backgroundColor} solid` : "1px transparent solid", position: !headingMode ? "absolute" : "unset", top: style['top'], - marginTop: style['marginTop'], + marginTop: !headingMode ? style['marginTop'] : "10px", width: !headingMode ? "calc(100% - 25px)" : "calc(100% - 18px)", }} > @@ -146,15 +147,18 @@ export const EntryItem: React.FC = ({entry, focusedEntryId, setFocus
- { - updateQuery(`service == "${entry.service}"`) - }} + - {entry.service} - + + {entry.service} + +
{ @@ -172,74 +176,109 @@ export const EntryItem: React.FC = ({entry, focusedEntryId, setFocus : "" }
- { - updateQuery(`src.ip == "${entry.sourceIp}"`) - }} + - {entry.sourceIp} - - : - { - updateQuery(`src.port == "${entry.sourcePort}"`) - }} + + {entry.sourceIp} + + + : + - {entry.sourcePort} - + + {entry.sourcePort} + + {entry.isOutgoing ? - Ingoing traffic { - updateQuery(`outgoing == true`) - }} - /> + + Ingoing traffic + : - Outgoing traffic { - updateQuery(`outgoing == false`) - }} - /> + + Outgoing traffic { + updateQuery(`outgoing == false`) + }} + /> + } - { - updateQuery(`dst.ip == "${entry.destinationIp}"`) - }} + - {entry.destinationIp} - - : - { - updateQuery(`dst.port == "${entry.destinationPort}"`) - }} + + {entry.destinationIp} + + + : + - {entry.destinationPort} - + + {entry.destinationPort} + +
- { - updateQuery(`timestamp >= datetime("${new Date(+entry.timestamp)?.toLocaleString("en-US", {timeZone: 'UTC' })}")`) - }} + = datetime("${new Date(+entry.timestamp)?.toLocaleString("en-US", {timeZone: 'UTC' })}")`} + updateQuery={updateQuery} + displayIconOnMouseOver={true} + flipped={false} > - {new Date(+entry.timestamp)?.toLocaleString("en-US")} - + + {new Date(+entry.timestamp)?.toLocaleString("en-US")} + +
diff --git a/ui/src/components/Filters.tsx b/ui/src/components/Filters.tsx index 87463085a..9168b3472 100644 --- a/ui/src/components/Filters.tsx +++ b/ui/src/components/Filters.tsx @@ -226,7 +226,7 @@ export const QueryForm: React.FC = ({query, setQuery, background language="python" /> - By clicking the UI elements in both left-pane and right-pane, you can automatically select a field and update the query: + By clicking the plus icon that appears beside the queryable UI elements on hovering in both left-pane and right-pane, you can automatically select a field and update the query: = ({query, setQuery, background title="Clicking to UI elements (left-pane)" /> - Such that; clicking this in left-pane, would append the query below: + Such that; clicking this icon in left-pane, would append the query below: diff --git a/ui/src/components/TrafficPage.tsx b/ui/src/components/TrafficPage.tsx index 13175f236..0887ef0e0 100644 --- a/ui/src/components/TrafficPage.tsx +++ b/ui/src/components/TrafficPage.tsx @@ -21,7 +21,7 @@ const useLayoutStyles = makeStyles(() => ({ padding: "12px 24px", borderRadius: 4, marginTop: 15, - background: variables.headerBackgoundColor, + background: variables.headerBackgroundColor, }, viewer: { diff --git a/ui/src/components/UI/Protocol.tsx b/ui/src/components/UI/Protocol.tsx index 0befffce0..5369847fb 100644 --- a/ui/src/components/UI/Protocol.tsx +++ b/ui/src/components/UI/Protocol.tsx @@ -1,5 +1,6 @@ import React from "react"; import styles from './style/Protocol.module.sass'; +import Queryable from "./Queryable"; export interface ProtocolInterface { name: string @@ -22,34 +23,45 @@ interface ProtocolProps { const Protocol: React.FC = ({protocol, horizontal, updateQuery}) => { if (horizontal) { - return + return + + + {protocol.longName} + + + + } else { + return - {protocol.longName} + {protocol.abbr} - - } else { - return { - updateQuery(protocol.macro) - }} - > - {protocol.abbr} - + } }; diff --git a/ui/src/components/UI/Queryable.tsx b/ui/src/components/UI/Queryable.tsx new file mode 100644 index 000000000..e08d36cd9 --- /dev/null +++ b/ui/src/components/UI/Queryable.tsx @@ -0,0 +1,62 @@ +import React, { useEffect, useState } from 'react'; +import { CopyToClipboard } from 'react-copy-to-clipboard'; +import AddCircleIcon from '@material-ui/icons/AddCircle'; +import './style/Queryable.sass'; + +interface Props { + query: string, + updateQuery: any, + style?: object, + iconStyle?: object, + className?: string, + useTooltip?: boolean, + displayIconOnMouseOver?: boolean, + flipped?: boolean, +} + +const Queryable: React.FC = ({query, updateQuery, style, iconStyle, className, useTooltip= true, displayIconOnMouseOver = false, flipped = false, children}) => { + const [showAddedNotification, setAdded] = useState(false); + const [showTooltip, setShowTooltip] = useState(false); + + const onCopy = () => { + setAdded(true) + }; + + useEffect(() => { + let timer; + if (showAddedNotification) { + updateQuery(query); + timer = setTimeout(() => { + setAdded(false); + }, 1000); + } + return () => clearTimeout(timer); + }, [showAddedNotification, query, updateQuery]); + + const addButton = query ? + + + {showAddedNotification && Added} + + : null; + + return ( +
setShowTooltip(true)} + onMouseLeave={ e => setShowTooltip(false)} + > + {flipped && addButton} + {children} + {!flipped && addButton} + {useTooltip && showTooltip && {query}} +
+ ); +}; + +export default Queryable; diff --git a/ui/src/components/UI/StatusCode.tsx b/ui/src/components/UI/StatusCode.tsx index b743aec02..d9180680a 100644 --- a/ui/src/components/UI/StatusCode.tsx +++ b/ui/src/components/UI/StatusCode.tsx @@ -1,5 +1,6 @@ import React from "react"; import styles from './style/StatusCode.module.sass'; +import Queryable from "./Queryable"; export enum StatusCodeClassification { SUCCESS = "success", @@ -16,15 +17,19 @@ const StatusCode: React.FC = ({statusCode, updateQuery}) => { const classification = getClassification(statusCode) - return { - updateQuery(`response.status == ${statusCode}`) - }} + return - {statusCode} - + + {statusCode} + + }; export function getClassification(statusCode: number): string { diff --git a/ui/src/components/UI/Summary.tsx b/ui/src/components/UI/Summary.tsx index 3c05a89a8..7303e11e5 100644 --- a/ui/src/components/UI/Summary.tsx +++ b/ui/src/components/UI/Summary.tsx @@ -1,6 +1,7 @@ import miscStyles from "./style/misc.module.sass"; import React from "react"; import styles from './style/Summary.module.sass'; +import Queryable from "./Queryable"; interface SummaryProps { method: string @@ -9,24 +10,28 @@ interface SummaryProps { } export const Summary: React.FC = ({method, summary, updateQuery}) => { + return
- {method && { - updateQuery(`method == "${method}"`) - }} + {method && - {method} - } - {summary &&
{ - updateQuery(`summary == "${summary}"`) - }} + + {method} + + } + {summary && - {summary} -
} +
+ {summary} +
+ }
}; diff --git a/ui/src/components/UI/style/Queryable.sass b/ui/src/components/UI/style/Queryable.sass new file mode 100644 index 000000000..f045f2183 --- /dev/null +++ b/ui/src/components/UI/style/Queryable.sass @@ -0,0 +1,48 @@ +.Queryable-Container + display: flex + align-items: center + + &.displayIconOnMouseOver + .Queryable-Icon + opacity: 0 + width: 0px + pointer-events: none + &:hover + .Queryable-Icon + opacity: 1 + pointer-events: all + + + .Queryable-Icon + height: 22px + width: 22px + cursor: pointer + color: #27AE60 + + &:hover + background-color: rgba(255, 255, 255, 0.06) + border-radius: 4px + color: #1E884B + + .Queryable-AddNotifier + background-color: #1E884B + font-weight: normal + padding: 2px 5px + border-radius: 4px + position: absolute + transform: translate(0, 10%) + color: white + z-index: 1000 + font-size: 11px + + .Queryable-Tooltip + background-color: #1E884B + font-weight: normal + padding: 2px 5px + border-radius: 4px + position: absolute + transform: translate(0, -80%) + color: white + z-index: 1000 + font-size: 11px + diff --git a/ui/src/components/UI/style/Summary.module.sass b/ui/src/components/UI/style/Summary.module.sass index 59bc34893..2bce9af67 100644 --- a/ui/src/components/UI/style/Summary.module.sass +++ b/ui/src/components/UI/style/Summary.module.sass @@ -3,6 +3,4 @@ align-items: center .summary - text-overflow: ellipsis - overflow: hidden white-space: nowrap diff --git a/ui/src/components/UI/style/misc.module.sass b/ui/src/components/UI/style/misc.module.sass index 8c3a1cbac..bef317cd3 100644 --- a/ui/src/components/UI/style/misc.module.sass +++ b/ui/src/components/UI/style/misc.module.sass @@ -11,13 +11,14 @@ &.method margin-right: 10px + height: 12px &.filterPlate border-color: #bcc6dd20 color: #a0b2ff font-size: 10px -.noSelect +.noSelect -webkit-touch-callout: none -webkit-user-select: none -khtml-user-select: none diff --git a/ui/src/components/assets/filter-ui-example-1.png b/ui/src/components/assets/filter-ui-example-1.png index 244c1b511..f8f76b85a 100644 Binary files a/ui/src/components/assets/filter-ui-example-1.png and b/ui/src/components/assets/filter-ui-example-1.png differ diff --git a/ui/src/components/assets/filter-ui-example-2.png b/ui/src/components/assets/filter-ui-example-2.png index 97424b2a5..e8ae1a5ec 100644 Binary files a/ui/src/components/assets/filter-ui-example-2.png and b/ui/src/components/assets/filter-ui-example-2.png differ diff --git a/ui/src/components/style/TrafficPage.sass b/ui/src/components/style/TrafficPage.sass index 3a3c558f5..68d4c3d08 100644 --- a/ui/src/components/style/TrafficPage.sass +++ b/ui/src/components/style/TrafficPage.sass @@ -114,4 +114,4 @@ .playPauseIcon cursor: pointer margin-right: 15px - height: 30px \ No newline at end of file + height: 30px diff --git a/ui/src/index.sass b/ui/src/index.sass index b8f707d22..2cb79f24b 100644 --- a/ui/src/index.sass +++ b/ui/src/index.sass @@ -22,11 +22,6 @@ code .uppercase text-transform: uppercase -.queryable - cursor: pointer - &:hover - text-decoration: underline - /**** * Button ***/ diff --git a/ui/src/variables.module.scss b/ui/src/variables.module.scss index d2f3d89bd..9e25bb3c3 100644 --- a/ui/src/variables.module.scss +++ b/ui/src/variables.module.scss @@ -11,7 +11,7 @@ $blue-gray: #494677; :export { mainBackgroundColor: $main-background-color; - headerBackgoundColor: $header-background-color; + headerBackgroundColor: $header-background-color; fontColor: $font-color; secondaryFontColor: $secondary-font-color; blueColor: $blue-color;