aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components/panels/SearchPane.jsx
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/panels/SearchPane.jsx')
-rw-r--r--frontend/src/components/panels/SearchPane.jsx309
1 files changed, 0 insertions, 309 deletions
diff --git a/frontend/src/components/panels/SearchPane.jsx b/frontend/src/components/panels/SearchPane.jsx
deleted file mode 100644
index 6fe9dc7..0000000
--- a/frontend/src/components/panels/SearchPane.jsx
+++ /dev/null
@@ -1,309 +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 React, {Component} from "react";
-import Table from "react-bootstrap/Table";
-import backend from "../../backend";
-import dispatcher from "../../dispatcher";
-import {createCurlCommand, dateTimeToTime, durationBetween} from "../../utils";
-import ButtonField from "../fields/ButtonField";
-import CheckField from "../fields/CheckField";
-import InputField from "../fields/InputField";
-import TagField from "../fields/TagField";
-import TextField from "../fields/TextField";
-import LinkPopover from "../objects/LinkPopover";
-import "./common.scss";
-import "./SearchPane.scss";
-
-import _ from 'lodash';
-
-class SearchPane extends Component {
-
- searchOptions = {
- "text_search": {
- "terms": null,
- "excluded_terms": null,
- "exact_phrase": "",
- "case_sensitive": false
- },
- "regex_search": {
- "pattern": "",
- "not_pattern": "",
- "case_insensitive": false,
- "multi_line": false,
- "ignore_whitespaces": false,
- "dot_character": false
- },
- "timeout": 10
- };
-
- state = {
- searches: [],
- currentSearchOptions: this.searchOptions,
- };
-
- componentDidMount() {
- this.reset();
- this.loadSearches();
-
- dispatcher.register("notifications", this.handleNotification);
- document.title = "caronte:~/searches$";
- }
-
- componentWillUnmount() {
- dispatcher.unregister(this.handleNotification);
- }
-
- loadSearches = () => {
- backend.get("/api/searches")
- .then((res) => this.setState({searches: res.json, searchesStatusCode: res.status}))
- .catch((res) => this.setState({searchesStatusCode: res.status, searchesResponse: JSON.stringify(res.json)}));
- };
-
- performSearch = () => {
- const options = this.state.currentSearchOptions;
- this.setState({loading: true});
- if (this.validateSearch(options)) {
- backend.post("/api/searches/perform", options).then((res) => {
- this.reset();
- this.setState({searchStatusCode: res.status, loading: false});
- this.loadSearches();
- this.viewSearch(res.json.id);
- }).catch((res) => {
- this.setState({
- searchStatusCode: res.status, searchResponse: JSON.stringify(res.json),
- loading: false
- });
- });
- }
- };
-
- reset = () => {
- this.setState({
- currentSearchOptions: _.cloneDeep(this.searchOptions),
- exactPhraseError: null,
- patternError: null,
- notPatternError: null,
- searchStatusCode: null,
- searchesStatusCode: null,
- searchResponse: null,
- searchesResponse: null
- });
- };
-
- validateSearch = (options) => {
- let valid = true;
- if (options["text_search"]["exact_phrase"] && options["text_search"]["exact_phrase"].length < 3) {
- this.setState({exactPhraseError: "text_search.exact_phrase.length < 3"});
- valid = false;
- }
- if (options["regex_search"].pattern && options["regex_search"].pattern.length < 3) {
- this.setState({patternError: "regex_search.pattern.length < 3"});
- valid = false;
- }
- if (options["regex_search"]["not_pattern"] && options["regex_search"]["not_pattern"].length < 3) {
- this.setState({exactPhraseError: "regex_search.not_pattern.length < 3"});
- valid = false;
- }
-
- return valid;
- };
-
- updateParam = (callback) => {
- callback(this.state.currentSearchOptions);
- this.setState({currentSearchOptions: this.state.currentSearchOptions});
- };
-
- extractPattern = (options) => {
- let pattern = "";
- if (_.isEqual(options.regex_search, this.searchOptions.regex_search)) { // is text search
- if (options["text_search"]["exact_phrase"]) {
- pattern += `"${options["text_search"]["exact_phrase"]}"`;
- } else {
- pattern += options["text_search"].terms.join(" ");
- if (options["text_search"]["excluded_terms"]) {
- pattern += " -" + options["text_search"]["excluded_terms"].join(" -");
- }
- }
- options["text_search"]["case_sensitive"] && (pattern += "/s");
- } else { // is regex search
- if (options["regex_search"].pattern) {
- pattern += "/" + options["regex_search"].pattern + "/";
- } else {
- pattern += "!/" + options["regex_search"]["not_pattern"] + "/";
- }
- options["regex_search"]["case_insensitive"] && (pattern += "i");
- options["regex_search"]["multi_line"] && (pattern += "m");
- options["regex_search"]["ignore_whitespaces"] && (pattern += "x");
- options["regex_search"]["dot_character"] && (pattern += "s");
- }
-
- return pattern;
- };
-
- viewSearch = (searchId) => {
- dispatcher.dispatch("connections_filters", {"performed_search": searchId});
- };
-
- handleNotification = (payload) => {
- if (payload.event === "searches.new") {
- this.loadSearches();
- }
- };
-
- render() {
- const options = this.state.currentSearchOptions;
-
- let searches = this.state.searches.map((s) =>
- <tr key={s.id} className="row-small row-clickable">
- <td>{s.id.substring(0, 8)}</td>
- <td>{this.extractPattern(s["search_options"])}</td>
- <td>{s["affected_connections_count"]}</td>
- <td>{dateTimeToTime(s["started_at"])}</td>
- <td>{durationBetween(s["started_at"], s["finished_at"])}</td>
- <td><ButtonField name="view" variant="green" small onClick={() => this.viewSearch(s.id)}/></td>
- </tr>
- );
-
- const textOptionsModified = !_.isEqual(this.searchOptions.text_search, options.text_search);
- const regexOptionsModified = !_.isEqual(this.searchOptions.regex_search, options.regex_search);
-
- const curlCommand = createCurlCommand("/searches/perform", "POST", options);
-
- return (
- <div className="pane-container search-pane">
- <div className="pane-section searches-list">
- <div className="section-header">
- <span className="api-request">GET /api/searches</span>
- {this.state.searchesStatusCode &&
- <span className="api-response"><LinkPopover text={this.state.searchesStatusCode}
- content={this.state.searchesResponse}
- placement="left"/></span>}
- </div>
-
- <div className="section-content">
- <div className="section-table">
- <Table borderless size="sm">
- <thead>
- <tr>
- <th>id</th>
- <th>pattern</th>
- <th>occurrences</th>
- <th>started_at</th>
- <th>duration</th>
- <th>actions</th>
- </tr>
- </thead>
- <tbody>
- {searches}
- </tbody>
- </Table>
- </div>
- </div>
- </div>
-
- <div className="pane-section search-new">
- <div className="section-header">
- <span className="api-request">POST /api/searches/perform</span>
- <span className="api-response"><LinkPopover text={this.state.searchStatusCode}
- content={this.state.searchResponse}
- placement="left"/></span>
- </div>
-
- <div className="section-content">
- <span className="notes">
- NOTE: it is recommended to use the rules for recurring themes. Give preference to textual search over that with regex.
- </span>
-
- <div className="content-row">
- <div className="text-search">
- <TagField tags={(options["text_search"].terms || []).map((t) => {
- return {name: t};
- })}
- name="terms" min={3} inline allowNew={true}
- readonly={regexOptionsModified || options["text_search"]["exact_phrase"]}
- onChange={(tags) => this.updateParam((s) => s["text_search"].terms = tags.map((t) => t.name))}/>
- <TagField tags={(options["text_search"]["excluded_terms"] || []).map((t) => {
- return {name: t};
- })}
- name="excluded_terms" min={3} inline allowNew={true}
- readonly={regexOptionsModified || options["text_search"]["exact_phrase"]}
- onChange={(tags) => this.updateParam((s) => s["text_search"]["excluded_terms"] = tags.map((t) => t.name))}/>
-
- <span className="exclusive-separator">or</span>
-
- <InputField name="exact_phrase" value={options["text_search"]["exact_phrase"]} inline
- error={this.state.exactPhraseError}
- onChange={(v) => this.updateParam((s) => s["text_search"]["exact_phrase"] = v)}
- readonly={regexOptionsModified || (Array.isArray(options["text_search"].terms) && options["text_search"].terms.length > 0)}/>
-
- <CheckField checked={options["text_search"]["case_sensitive"]} name="case_sensitive"
- readonly={regexOptionsModified} small
- onChange={(v) => this.updateParam((s) => s["text_search"]["case_sensitive"] = v)}/>
- </div>
-
- <div className="separator">
- <span>or</span>
- </div>
-
- <div className="regex-search">
- <InputField name="pattern" value={options["regex_search"].pattern} inline
- error={this.state.patternError}
- readonly={textOptionsModified || options["regex_search"]["not_pattern"]}
- onChange={(v) => this.updateParam((s) => s["regex_search"].pattern = v)}/>
- <span className="exclusive-separator">or</span>
- <InputField name="not_pattern" value={options["regex_search"]["not_pattern"]} inline
- error={this.state.notPatternError}
- readonly={textOptionsModified || options["regex_search"].pattern}
- onChange={(v) => this.updateParam((s) => s["regex_search"]["not_pattern"] = v)}/>
-
- <div className="checkbox-line">
- <CheckField checked={options["regex_search"]["case_insensitive"]}
- name="case_insensitive"
- readonly={textOptionsModified} small
- onChange={(v) => this.updateParam((s) => s["regex_search"]["case_insensitive"] = v)}/>
- <CheckField checked={options["regex_search"]["multi_line"]} name="multi_line"
- readonly={textOptionsModified} small
- onChange={(v) => this.updateParam((s) => s["regex_search"]["multi_line"] = v)}/>
- <CheckField checked={options["regex_search"]["ignore_whitespaces"]}
- name="ignore_whitespaces"
- readonly={textOptionsModified} small
- onChange={(v) => this.updateParam((s) => s["regex_search"]["ignore_whitespaces"] = v)}/>
- <CheckField checked={options["regex_search"]["dot_character"]} name="dot_character"
- readonly={textOptionsModified} small
- onChange={(v) => this.updateParam((s) => s["regex_search"]["dot_character"] = v)}/>
- </div>
- </div>
- </div>
-
- <TextField value={curlCommand} rows={3} readonly small={true}/>
- </div>
-
- <div className="section-footer">
- <ButtonField variant="red" name="cancel" bordered disabled={this.state.loading}
- onClick={this.reset}/>
- <ButtonField variant="green" name="perform_search" bordered
- disabled={this.state.loading} onClick={this.performSearch}/>
- </div>
- </div>
- </div>
- );
- }
-
-}
-
-export default SearchPane;