From a30815021e61023f996b1450ddcd9164a6e18bef Mon Sep 17 00:00:00 2001
From: Emiliano Ciavatta
Date: Thu, 8 Oct 2020 17:07:07 +0200
Subject: Add header license to all files
---
frontend/src/components/fields/CheckField.js | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
(limited to 'frontend/src/components/fields/CheckField.js')
diff --git a/frontend/src/components/fields/CheckField.js b/frontend/src/components/fields/CheckField.js
index 33f4f83..dd44970 100644
--- a/frontend/src/components/fields/CheckField.js
+++ b/frontend/src/components/fields/CheckField.js
@@ -1,3 +1,20 @@
+/*
+ * 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 .
+ */
+
import React, {Component} from 'react';
import './CheckField.scss';
import './common.scss';
--
cgit v1.2.3-70-g09d2
From a44b70943ea4fce61261fc5fadf84a2a98fd2435 Mon Sep 17 00:00:00 2001
From: Emiliano Ciavatta
Date: Mon, 12 Oct 2020 20:08:45 +0200
Subject: Add search pane on frontend
---
connections_controller.go | 2 +-
frontend/src/components/Header.js | 9 +-
frontend/src/components/fields/CheckField.js | 2 +-
frontend/src/components/fields/TagField.js | 78 ++++++
frontend/src/components/fields/TagField.scss | 120 +++++++++
.../src/components/filters/ExitSearchFilter.js | 52 ++++
.../src/components/filters/FiltersDispatcher.js | 57 ++++
.../components/filters/RulesConnectionsFilter.js | 2 +-
.../components/filters/RulesConnectionsFilter.scss | 193 +++++++-------
frontend/src/components/pages/MainPage.js | 5 +
frontend/src/components/panels/SearchPane.js | 293 +++++++++++++++++++++
frontend/src/components/panels/SearchPane.scss | 51 ++++
12 files changed, 764 insertions(+), 100 deletions(-)
create mode 100644 frontend/src/components/fields/TagField.js
create mode 100644 frontend/src/components/fields/TagField.scss
create mode 100644 frontend/src/components/filters/ExitSearchFilter.js
create mode 100644 frontend/src/components/filters/FiltersDispatcher.js
create mode 100644 frontend/src/components/panels/SearchPane.js
create mode 100644 frontend/src/components/panels/SearchPane.scss
(limited to 'frontend/src/components/fields/CheckField.js')
diff --git a/connections_controller.go b/connections_controller.go
index a293a80..924cb53 100644
--- a/connections_controller.go
+++ b/connections_controller.go
@@ -151,7 +151,7 @@ func (cc ConnectionsController) GetConnections(c context.Context, filter Connect
performedSearchID, _ := RowIDFromHex(filter.PerformedSearch)
if !performedSearchID.IsZero() {
performedSearch := cc.searchController.GetPerformedSearch(performedSearchID)
- if !performedSearch.ID.IsZero() {
+ if !performedSearch.ID.IsZero() && len(performedSearch.AffectedConnections) > 0 {
query = query.Filter(OrderedDocument{{"_id", UnorderedDocument{"$in": performedSearch.AffectedConnections}}})
}
}
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index 4d29364..b72b532 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -21,6 +21,7 @@ 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";
class Header extends Component {
@@ -50,7 +51,7 @@ class Header extends Component {
componentWillUnmount() {
this.typed.destroy();
- if (typeof window !== "undefined") {
+ if (typeof window) {
window.removeEventListener("quick-filters", this.fetchStateFromLocalStorage);
}
}
@@ -82,12 +83,16 @@ class Header extends Component {
{quickFilters}
+
-
+ {/**/}
+
+
+
diff --git a/frontend/src/components/fields/CheckField.js b/frontend/src/components/fields/CheckField.js
index dd44970..a0e2706 100644
--- a/frontend/src/components/fields/CheckField.js
+++ b/frontend/src/components/fields/CheckField.js
@@ -35,7 +35,7 @@ class CheckField extends Component {
const small = this.props.small || false;
const name = this.props.name || null;
const handler = () => {
- if (this.props.onChange) {
+ if (!this.props.readonly && this.props.onChange) {
this.props.onChange(!checked);
}
};
diff --git a/frontend/src/components/fields/TagField.js b/frontend/src/components/fields/TagField.js
new file mode 100644
index 0000000..f1a48bd
--- /dev/null
+++ b/frontend/src/components/fields/TagField.js
@@ -0,0 +1,78 @@
+/*
+ * 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 .
+ */
+
+import React, {Component} from 'react';
+import './TagField.scss';
+import './common.scss';
+import {randomClassName} from "../../utils";
+import ReactTags from "react-tag-autocomplete";
+
+const classNames = require('classnames');
+const _ = require('lodash');
+
+class TagField extends Component {
+
+ constructor(props) {
+ super(props);
+
+ this.id = `field-${this.props.name || "noname"}-${randomClassName()}`;
+ }
+
+ state = {
+
+ };
+
+ onAddition = (tag) => {
+ if (typeof this.props.onChange === "function") {
+ const tags = [].concat(this.wrappedTags(), tag);
+ this.props.onChange(tags.map(t => t.name), true, tag); // true == addition
+ }
+ };
+
+ onDelete = (i) => {
+ if (typeof this.props.onChange === "function") {
+ const tags = this.wrappedTags();
+ const tag = tags[i];
+ tags.splice(i, 1);
+ this.props.onChange(tags.map(t => t.name), true, tag); // false == delete
+ }
+ };
+
+ wrappedTags = () => this.props.tags.map(t => new Object({"name": t}));
+
+ render() {
+ const small = this.props.small || false;
+ const name = this.props.name || null;
+
+ return (
+
+ { name &&
+
+
+
+ }
+
+
+ );
+ }
+}
+
+export default TagField;
diff --git a/frontend/src/components/fields/TagField.scss b/frontend/src/components/fields/TagField.scss
new file mode 100644
index 0000000..e77db97
--- /dev/null
+++ b/frontend/src/components/fields/TagField.scss
@@ -0,0 +1,120 @@
+@import "../../colors.scss";
+
+.tag-field {
+ .react-tags {
+ font-size: 12px;
+ position: relative;
+ z-index: 10;
+ padding: 0 6px;
+ cursor: text;
+ border-radius: 4px;
+ background-color: $color-primary-2;
+ }
+
+ .react-tags.is-focused {
+ border-color: #b1b1b1;
+ }
+
+ .react-tags__selected {
+ display: inline;
+ }
+
+ .react-tags__selected-tag {
+ font-size: 11px;
+ display: inline-block;
+ margin: 0 6px 6px 0;
+ padding: 2px 4px;
+ color: $color-primary-3;
+ border: none;
+ border-radius: 2px;
+ background: $color-primary-4;
+ }
+
+ .react-tags__selected-tag::after {
+ margin-left: 8px;
+ content: "\2715";
+ color: $color-primary-3;
+ }
+
+ .react-tags__selected-tag:hover,
+ .react-tags__selected-tag:focus {
+ border-color: #b1b1b1;
+ }
+
+ .react-tags__search {
+ display: inline-block;
+ max-width: 100%;
+ padding: 7px 10px;
+ }
+
+ @media screen and (min-width: 30em) {
+ .react-tags__search {
+ position: relative;
+ }
+ }
+
+ .react-tags__search-input {
+ font-size: inherit;
+ line-height: inherit;
+ max-width: 100%;
+ margin: 0;
+ padding: 0;
+ color: $color-primary-4;
+ border: 0;
+ outline: none;
+ background-color: $color-primary-2;
+ }
+
+ .react-tags__search-input::-ms-clear {
+ display: none;
+ }
+
+ .react-tags__suggestions {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ width: 100%;
+ }
+
+ @media screen and (min-width: 30em) {
+ .react-tags__suggestions {
+ width: 240px;
+ }
+ }
+
+ .react-tags__suggestions ul {
+ font-size: 12px;
+ margin: 4px -1px;
+ padding: 0;
+ list-style: none;
+ color: $color-primary-1;
+ border-radius: 2px;
+ background: $color-primary-4;
+ }
+
+ .react-tags__suggestions li {
+ padding: 3px 5px;
+ border-bottom: 1px solid #ddd;
+ }
+
+ .react-tags__suggestions li mark {
+ font-weight: 600;
+ text-decoration: underline;
+ background: none;
+ }
+
+ .react-tags__suggestions li:hover {
+ cursor: pointer;
+ color: $color-primary-4;
+ background: $color-primary-0;
+ }
+
+ .react-tags__suggestions li.is-active {
+ background: #b7cfe0;
+ }
+
+ .react-tags__suggestions li.is-disabled {
+ cursor: auto;
+ opacity: 0.5;
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/components/filters/ExitSearchFilter.js b/frontend/src/components/filters/ExitSearchFilter.js
new file mode 100644
index 0000000..cfee298
--- /dev/null
+++ b/frontend/src/components/filters/ExitSearchFilter.js
@@ -0,0 +1,52 @@
+/*
+ * 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 .
+ */
+
+import React, {Component} from 'react';
+import {withRouter} from "react-router-dom";
+import CheckField from "../fields/CheckField";
+import dispatcher from "../../dispatcher";
+
+class ExitSearchFilter extends Component {
+
+ state = {};
+
+ componentDidMount() {
+ let params = new URLSearchParams(this.props.location.search);
+ this.setState({performedSearch: params.get("performed_search")});
+
+ dispatcher.register("connections_filters", payload => {
+ if (this.state.performedSearch !== payload["performed_search"]) {
+ this.setState({performedSearch: payload["performed_search"]});
+ }
+ });
+ }
+
+ render() {
+ return (
+ <>
+ {this.state.performedSearch &&
+
}
+ >
+ );
+ }
+
+}
+
+export default withRouter(ExitSearchFilter);
diff --git a/frontend/src/components/filters/FiltersDispatcher.js b/frontend/src/components/filters/FiltersDispatcher.js
new file mode 100644
index 0000000..3769055
--- /dev/null
+++ b/frontend/src/components/filters/FiltersDispatcher.js
@@ -0,0 +1,57 @@
+/*
+ * 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 .
+ */
+
+import React, {Component} from 'react';
+import {withRouter} from "react-router-dom";
+import {Redirect} from "react-router";
+import dispatcher from "../../dispatcher";
+
+class FiltersDispatcher extends Component {
+
+ state = {};
+
+ componentDidMount() {
+ let params = new URLSearchParams(this.props.location.search);
+ this.setState({params});
+
+ dispatcher.register("connections_filters", payload => {
+ const params = this.state.params;
+
+ Object.entries(payload).forEach(([key, value]) => {
+ if (value == null) {
+ params.delete(key);
+ } else {
+ params.set(key, value);
+ }
+ });
+
+ this.needRedirect = true;
+ this.setState({params});
+ });
+ }
+
+ render() {
+ if (this.needRedirect) {
+ this.needRedirect = false;
+ return ;
+ }
+
+ return null;
+ }
+}
+
+export default withRouter(FiltersDispatcher);
diff --git a/frontend/src/components/filters/RulesConnectionsFilter.js b/frontend/src/components/filters/RulesConnectionsFilter.js
index 48affb0..fc0ad4d 100644
--- a/frontend/src/components/filters/RulesConnectionsFilter.js
+++ b/frontend/src/components/filters/RulesConnectionsFilter.js
@@ -89,7 +89,7 @@ class RulesConnectionsFilter extends Component {
return (