aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components
diff options
context:
space:
mode:
authorEmiliano Ciavatta2020-10-15 07:54:25 +0000
committerEmiliano Ciavatta2020-10-15 07:54:25 +0000
commit97b0ee38fcf1e78e66dfe2a2816c95c3c3b10705 (patch)
treeed36b83d39163d872e55d297caada23092b7fcd4 /frontend/src/components
parent08456e7f2e1c1af6fc8fdbf580c0178a25b93f8b (diff)
Update filters
Diffstat (limited to 'frontend/src/components')
-rw-r--r--frontend/src/components/Header.js47
-rw-r--r--frontend/src/components/Header.scss9
-rw-r--r--frontend/src/components/dialogs/Filters.js108
-rw-r--r--frontend/src/components/dialogs/Filters.scss5
-rw-r--r--frontend/src/components/fields/ButtonField.js3
-rw-r--r--frontend/src/components/fields/ButtonField.scss7
-rw-r--r--frontend/src/components/filters/AdvancedFilters.js54
-rw-r--r--frontend/src/components/filters/BooleanConnectionsFilter.js2
-rw-r--r--frontend/src/components/filters/FiltersDefinitions.js73
-rw-r--r--frontend/src/components/filters/StringConnectionsFilter.js2
-rw-r--r--frontend/src/components/panels/ConnectionsPane.js22
11 files changed, 138 insertions, 194 deletions
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index b4a2177..119958b 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -18,21 +18,17 @@
import React, {Component} from 'react';
import Typed from 'typed.js';
import './Header.scss';
-import {filtersDefinitions, filtersNames} from "./filters/FiltersDefinitions";
import {Link, withRouter} from "react-router-dom";
import ButtonField from "./fields/ButtonField";
import ExitSearchFilter from "./filters/ExitSearchFilter";
+import {cleanNumber, validatePort} from "../utils";
+import StringConnectionsFilter from "./filters/StringConnectionsFilter";
+import RulesConnectionsFilter from "./filters/RulesConnectionsFilter";
+import BooleanConnectionsFilter from "./filters/BooleanConnectionsFilter";
+import AdvancedFilters from "./filters/AdvancedFilters";
class Header extends Component {
- constructor(props) {
- super(props);
- let newState = {};
- filtersNames.forEach(elem => newState[`${elem}_active`] = false);
- this.state = newState;
- this.fetchStateFromLocalStorage = this.fetchStateFromLocalStorage.bind(this);
- }
-
componentDidMount() {
const options = {
strings: ["caronte$ "],
@@ -40,33 +36,13 @@ class Header extends Component {
cursorChar: "❚"
};
this.typed = new Typed(this.el, options);
-
- this.fetchStateFromLocalStorage();
-
- if (typeof window !== "undefined") {
- window.addEventListener("quick-filters", this.fetchStateFromLocalStorage);
- }
}
componentWillUnmount() {
this.typed.destroy();
-
- if (typeof window) {
- window.removeEventListener("quick-filters", this.fetchStateFromLocalStorage);
- }
- }
-
- fetchStateFromLocalStorage() {
- let newState = {};
- filtersNames.forEach(elem => newState[`${elem}_active`] = localStorage.getItem(`filters.${elem}`) === "true");
- this.setState(newState);
}
render() {
- let quickFilters = filtersNames.filter(name => this.state[`${name}_active`])
- .map(name => <React.Fragment key={name}>{filtersDefinitions[name]}</React.Fragment>)
- .slice(0, 5);
-
return (
<header className="header container-fluid">
<div className="row">
@@ -82,14 +58,21 @@ class Header extends Component {
<div className="col-auto">
<div className="filters-bar">
- {quickFilters}
- <ExitSearchFilter />
+ <StringConnectionsFilter filterName="service_port"
+ defaultFilterValue="all_ports"
+ replaceFunc={cleanNumber}
+ validateFunc={validatePort}
+ key="service_port_filter"
+ width={200} small inline/>
+ <RulesConnectionsFilter/>
+ <BooleanConnectionsFilter filterName={"marked"}/>
+ <ExitSearchFilter/>
+ <AdvancedFilters onClick={this.props.onOpenFilters} />
</div>
</div>
<div className="col">
<div className="header-buttons">
- <ButtonField variant="pink" onClick={this.props.onOpenFilters} name="filters" bordered/>
<Link to={"/searches" + this.props.location.search}>
<ButtonField variant="pink" name="searches" bordered/>
</Link>
diff --git a/frontend/src/components/Header.scss b/frontend/src/components/Header.scss
index e2e8e1c..fff28e6 100644
--- a/frontend/src/components/Header.scss
+++ b/frontend/src/components/Header.scss
@@ -26,9 +26,16 @@
.filters-bar {
padding: 3px 0;
- .filter {
+ .filter,
+ .button-field {
display: inline-block;
margin-right: 10px;
}
+
+ .button-field button {
+ font-weight: 400;
+ padding: 7px 10px;
+ border-radius: 5px;
+ }
}
}
diff --git a/frontend/src/components/dialogs/Filters.js b/frontend/src/components/dialogs/Filters.js
index dfd554b..d2cce4f 100644
--- a/frontend/src/components/dialogs/Filters.js
+++ b/frontend/src/components/dialogs/Filters.js
@@ -16,91 +16,63 @@
*/
import React, {Component} from 'react';
-import {Col, Container, Modal, Row, Table} from "react-bootstrap";
-import {filtersDefinitions, filtersNames} from "../filters/FiltersDefinitions";
+import {Col, Container, Modal, Row} from "react-bootstrap";
import ButtonField from "../fields/ButtonField";
+import './Filters.scss';
+import {cleanNumber, validateIpAddress, validateMin, validatePort} from "../../utils";
+import StringConnectionsFilter from "../filters/StringConnectionsFilter";
class Filters extends Component {
- constructor(props) {
- super(props);
- let newState = {};
- filtersNames.forEach(elem => newState[`${elem}_active`] = false);
- this.state = newState;
- }
-
- componentDidMount() {
- let newState = {};
- filtersNames.forEach(elem => newState[`${elem}_active`] = localStorage.getItem(`filters.${elem}`) === "true");
- this.setState(newState);
- }
-
- checkboxChangesHandler(filterName, event) {
- this.setState({[`${filterName}_active`]: event.target.checked});
- localStorage.setItem(`filters.${filterName}`, event.target.checked);
- if (typeof window !== "undefined") {
- window.dispatchEvent(new Event("quick-filters"));
- }
- }
-
- generateRows(filtersNames) {
- return filtersNames.map(name =>
- <tr key={name}>
- <td><input type="checkbox"
- checked={this.state[`${name}_active`]}
- onChange={event => this.checkboxChangesHandler(name, event)}/></td>
- <td>{filtersDefinitions[name]}</td>
- </tr>
- );
- }
-
render() {
return (
<Modal
{...this.props}
- show="true"
+ show={true}
size="lg"
aria-labelledby="filters-dialog"
centered
>
<Modal.Header>
<Modal.Title id="filters-dialog">
- ~/filters
+ ~/advanced_filters
</Modal.Title>
</Modal.Header>
<Modal.Body>
- <Container>
- <Row>
- <Col md={6}>
- <Table borderless size="sm" className="filters-table">
- <thead>
- <tr>
- <th>show</th>
- <th>filter</th>
- </tr>
- </thead>
- <tbody>
- {this.generateRows(["service_port", "client_address", "min_duration",
- "min_bytes"])}
- </tbody>
- </Table>
- </Col>
- <Col md={6}>
- <Table borderless size="sm" className="filters-table">
- <thead>
- <tr>
- <th>show</th>
- <th>filter</th>
- </tr>
- </thead>
- <tbody>
- {this.generateRows(["matched_rules", "client_port", "max_duration",
- "max_bytes", "marked"])}
- </tbody>
- </Table>
- </Col>
- </Row>
- </Container>
+ <div className="advanced-filters d-flex">
+ <div className="flex-fill">
+ <StringConnectionsFilter filterName="client_address"
+ defaultFilterValue="all_addresses"
+ validateFunc={validateIpAddress}
+ key="client_address_filter"/>
+ <StringConnectionsFilter filterName="min_duration"
+ defaultFilterValue="0"
+ replaceFunc={cleanNumber}
+ validateFunc={validateMin(0)}
+ key="min_duration_filter"/>
+ <StringConnectionsFilter filterName="min_bytes"
+ defaultFilterValue="0"
+ replaceFunc={cleanNumber}
+ validateFunc={validateMin(0)}
+ key="min_bytes_filter"/>
+ </div>
+
+ <div className="flex-fill">
+ <StringConnectionsFilter filterName="client_port"
+ defaultFilterValue="all_ports"
+ replaceFunc={cleanNumber}
+ validateFunc={validatePort}
+ key="client_port_filter"/>
+ <StringConnectionsFilter filterName="max_duration"
+ defaultFilterValue="∞"
+ replaceFunc={cleanNumber}
+ key="max_duration_filter"/>
+ <StringConnectionsFilter filterName="max_bytes"
+ defaultFilterValue="∞"
+ replaceFunc={cleanNumber}
+ key="max_bytes_filter"/>
+ </div>
+ </div>
</Modal.Body>
<Modal.Footer className="dialog-footer">
<ButtonField variant="red" bordered onClick={this.props.onHide} name="close"/>
diff --git a/frontend/src/components/dialogs/Filters.scss b/frontend/src/components/dialogs/Filters.scss
new file mode 100644
index 0000000..7d09380
--- /dev/null
+++ b/frontend/src/components/dialogs/Filters.scss
@@ -0,0 +1,5 @@
+.advanced-filters {
+ .filter {
+ margin: 10px;
+ }
+}
diff --git a/frontend/src/components/fields/ButtonField.js b/frontend/src/components/fields/ButtonField.js
index 193339c..850f837 100644
--- a/frontend/src/components/fields/ButtonField.js
+++ b/frontend/src/components/fields/ButtonField.js
@@ -55,7 +55,8 @@ class ButtonField extends Component {
}
return (
- <div className={classNames("field", "button-field", {"field-small": this.props.small})}>
+ <div className={classNames("field", "button-field", {"field-small": this.props.small},
+ {"field-active": this.props.active})}>
<button type="button" className={classNames(buttonClassnames)}
onClick={handler} style={buttonStyle}>{this.props.name}</button>
</div>
diff --git a/frontend/src/components/fields/ButtonField.scss b/frontend/src/components/fields/ButtonField.scss
index 9e46b9f..99afe08 100644
--- a/frontend/src/components/fields/ButtonField.scss
+++ b/frontend/src/components/fields/ButtonField.scss
@@ -15,6 +15,13 @@
}
}
+ &.field-active {
+ button {
+ color: $color-primary-1;
+ background-color: $color-primary-4;
+ }
+ }
+
.button-variant-red {
color: $color-red-light;
background-color: $color-red;
diff --git a/frontend/src/components/filters/AdvancedFilters.js b/frontend/src/components/filters/AdvancedFilters.js
new file mode 100644
index 0000000..f5ba825
--- /dev/null
+++ b/frontend/src/components/filters/AdvancedFilters.js
@@ -0,0 +1,54 @@
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React, {Component} from 'react';
+import {withRouter} from "react-router-dom";
+import dispatcher from "../../dispatcher";
+import ButtonField from "../fields/ButtonField";
+import {updateParams} from "../../utils";
+
+class AdvancedFilters extends Component {
+
+ state = {};
+
+ componentDidMount() {
+ this.urlParams = new URLSearchParams(this.props.location.search);
+
+ this.connectionsFiltersCallback = payload => {
+ this.urlParams = updateParams(this.urlParams, payload);
+ const active = ["client_address", "client_port", "min_duration", "max_duration", "min_bytes", "max_bytes"]
+ .some(f => this.urlParams.has(f));
+ if (this.state.active !== active) {
+ this.setState({active: active});
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
+ }
+
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
+ }
+
+ render() {
+ return (
+ <ButtonField onClick={this.props.onClick} name="advanced_filters" small active={this.state.active}/>
+ );
+ }
+
+}
+
+export default withRouter(AdvancedFilters);
diff --git a/frontend/src/components/filters/BooleanConnectionsFilter.js b/frontend/src/components/filters/BooleanConnectionsFilter.js
index c611a0d..9558323 100644
--- a/frontend/src/components/filters/BooleanConnectionsFilter.js
+++ b/frontend/src/components/filters/BooleanConnectionsFilter.js
@@ -59,7 +59,7 @@ class BooleanConnectionsFilter extends Component {
return (
<div className="filter" style={{"width": `${this.props.width}px`}}>
<CheckField checked={this.toBoolean(this.state.filterActive)} name={this.props.filterName}
- onChange={this.filterChanged}/>
+ onChange={this.filterChanged} small/>
</div>
);
}
diff --git a/frontend/src/components/filters/FiltersDefinitions.js b/frontend/src/components/filters/FiltersDefinitions.js
deleted file mode 100644
index 9fb3b18..0000000
--- a/frontend/src/components/filters/FiltersDefinitions.js
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * This file is part of caronte (https://github.com/eciavatta/caronte).
- * Copyright (c) 2020 Emiliano Ciavatta.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import {cleanNumber, validateIpAddress, validateMin, validatePort} from "../../utils";
-import StringConnectionsFilter from "./StringConnectionsFilter";
-import React from "react";
-import RulesConnectionsFilter from "./RulesConnectionsFilter";
-import BooleanConnectionsFilter from "./BooleanConnectionsFilter";
-
-export const filtersNames = ["service_port", "matched_rules", "client_address", "client_port",
- "min_duration", "max_duration", "min_bytes", "max_bytes", "marked"];
-
-export const filtersDefinitions = {
- service_port: <StringConnectionsFilter filterName="service_port"
- defaultFilterValue="all_ports"
- replaceFunc={cleanNumber}
- validateFunc={validatePort}
- key="service_port_filter"
- width={200}/>,
- matched_rules: <RulesConnectionsFilter/>,
- client_address: <StringConnectionsFilter filterName="client_address"
- defaultFilterValue="all_addresses"
- validateFunc={validateIpAddress}
- key="client_address_filter"
- width={320}/>,
- client_port: <StringConnectionsFilter filterName="client_port"
- defaultFilterValue="all_ports"
- replaceFunc={cleanNumber}
- validateFunc={validatePort}
- key="client_port_filter"
- width={200}/>,
- min_duration: <StringConnectionsFilter filterName="min_duration"
- defaultFilterValue="0"
- replaceFunc={cleanNumber}
- validateFunc={validateMin(0)}
- key="min_duration_filter"
- width={200}/>,
- max_duration: <StringConnectionsFilter filterName="max_duration"
- defaultFilterValue="∞"
- replaceFunc={cleanNumber}
- key="max_duration_filter"
- width={200}/>,
- min_bytes: <StringConnectionsFilter filterName="min_bytes"
- defaultFilterValue="0"
- replaceFunc={cleanNumber}
- validateFunc={validateMin(0)}
- key="min_bytes_filter"
- width={200}/>,
- max_bytes: <StringConnectionsFilter filterName="max_bytes"
- defaultFilterValue="∞"
- replaceFunc={cleanNumber}
- key="max_bytes_filter"
- width={200}/>,
- contains_string: <StringConnectionsFilter filterName="contains_string"
- defaultFilterValue=""
- key="contains_string_filter"
- width={320}/>,
- marked: <BooleanConnectionsFilter filterName={"marked"}/>
-};
diff --git a/frontend/src/components/filters/StringConnectionsFilter.js b/frontend/src/components/filters/StringConnectionsFilter.js
index c833220..18b3784 100644
--- a/frontend/src/components/filters/StringConnectionsFilter.js
+++ b/frontend/src/components/filters/StringConnectionsFilter.js
@@ -127,7 +127,7 @@ class StringConnectionsFilter extends Component {
<div className="filter" style={{"width": `${this.props.width}px`}}>
<InputField active={active} invalid={this.state.invalidValue} name={this.props.filterName}
placeholder={this.props.defaultFilterValue} onChange={this.filterChanged}
- value={this.state.fieldValue} inline={true} small={true}/>
+ value={this.state.fieldValue} inline={this.props.inline} small={this.props.small}/>
</div>
);
}
diff --git a/frontend/src/components/panels/ConnectionsPane.js b/frontend/src/components/panels/ConnectionsPane.js
index 33dd7c1..89859e6 100644
--- a/frontend/src/components/panels/ConnectionsPane.js
+++ b/frontend/src/components/panels/ConnectionsPane.js
@@ -26,6 +26,7 @@ import log from "../../log";
import ButtonField from "../fields/ButtonField";
import dispatcher from "../../dispatcher";
import {Redirect} from "react-router";
+import {updateParams} from "../../utils";
const classNames = require('classnames');
@@ -67,29 +68,16 @@ class ConnectionsPane extends Component {
this.loadConnections(additionalParams, urlParams, true).then(() => log.debug("Connections loaded"));
this.connectionsFiltersCallback = payload => {
- const params = this.state.urlParams;
- const initialParams = params.toString();
-
- Object.entries(payload).forEach(([key, value]) => {
- if (value == null) {
- params.delete(key);
- } else if (Array.isArray(value)) {
- params.delete(key);
- value.forEach(v => params.append(key, v));
- } else {
- params.set(key, value);
- }
- });
-
- if (initialParams === params.toString()) {
+ const newParams = updateParams(this.state.urlParams, payload);
+ if (this.state.urlParams.toString() === newParams.toString()) {
return;
}
log.debug("Update following url params:", payload);
this.queryStringRedirect = true;
- this.setState({urlParams});
+ this.setState({urlParams: newParams});
- this.loadConnections({limit: this.queryLimit}, urlParams)
+ this.loadConnections({limit: this.queryLimit}, newParams)
.then(() => log.info("ConnectionsPane reloaded after query string update"));
};
dispatcher.register("connections_filters", this.connectionsFiltersCallback);