diff --git a/src/mqueryfront/src/App.css b/src/mqueryfront/src/App.css
index 1d67e328..617876f8 100644
--- a/src/mqueryfront/src/App.css
+++ b/src/mqueryfront/src/App.css
@@ -87,3 +87,27 @@
.cursor-pointer {
cursor: pointer;
}
+
+.index-form-wrapper {
+ display: grid;
+ grid-auto-flow: row;
+ grid-row-gap: 10px;
+}
+
+.index-links-wrapper {
+ display: grid;
+ grid-auto-flow: row;
+ grid-row-gap: 3px;
+}
+
+.index-navlink {
+ border-color: black;
+ border-style: solid;
+ border-width: thin;
+ font-size: large;
+ display: grid;
+ grid-auto-flow: row;
+ grid-row-gap: 10px;
+ cursor: pointer;
+ width: fit-content;
+}
diff --git a/src/mqueryfront/src/App.js b/src/mqueryfront/src/App.js
index a5e93dd8..7fdfc883 100644
--- a/src/mqueryfront/src/App.js
+++ b/src/mqueryfront/src/App.js
@@ -9,6 +9,7 @@ import AboutPage from "./about/AboutPage";
import AuthPage from "./auth/AuthPage";
import api, { parseJWT } from "./api";
import "./App.css";
+import IndexPage from "./indexFiles/IndexPage";
function getCurrentTokenOrNull() {
// This function handles missing and corrupted token in the same way.
@@ -69,6 +70,7 @@ function App() {
path="/auth"
element={}
/>
+ } />
);
diff --git a/src/mqueryfront/src/indexFiles/IndexClearQueueButton.js b/src/mqueryfront/src/indexFiles/IndexClearQueueButton.js
new file mode 100644
index 00000000..e1b3decb
--- /dev/null
+++ b/src/mqueryfront/src/indexFiles/IndexClearQueueButton.js
@@ -0,0 +1,28 @@
+import React, { Component } from "react";
+import api from "../api";
+
+class IndexClearQueueButton extends Component {
+ onClick() {
+ api.post(`/queue/${this.props.ursa_id}/clear`, {}).catch((_e) => {});
+ }
+
+ render() {
+ return (
+
+
+
+ );
+ }
+}
+
+export default IndexClearQueueButton;
diff --git a/src/mqueryfront/src/indexFiles/IndexMultiSelect.js b/src/mqueryfront/src/indexFiles/IndexMultiSelect.js
new file mode 100644
index 00000000..e2edd6a8
--- /dev/null
+++ b/src/mqueryfront/src/indexFiles/IndexMultiSelect.js
@@ -0,0 +1,27 @@
+import { Component } from "react";
+import Select from "react-select";
+
+class IndexMultiselect extends Component {
+ get optionsList() {
+ return this.props.options.map((obj) => ({
+ label: obj,
+ value: obj,
+ }));
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default IndexMultiselect;
diff --git a/src/mqueryfront/src/indexFiles/IndexPage.js b/src/mqueryfront/src/indexFiles/IndexPage.js
new file mode 100644
index 00000000..e090eb55
--- /dev/null
+++ b/src/mqueryfront/src/indexFiles/IndexPage.js
@@ -0,0 +1,154 @@
+import { Component } from "react";
+import IndexMultiSelect from "./IndexMultiSelect";
+import { useParams } from "react-router-dom";
+import api from "../api";
+import ErrorBoundary from "../components/ErrorBoundary";
+import IndexProgressBar from "./IndexProgressBar";
+import IndexClearQueueButton from "./IndexClearQueueButton";
+
+// function getAvailableTaintsListFromDatasets(datasets) {
+// var taintList = Object.values(datasets)
+// .map((ds) => ds.taints)
+// .flat();
+// return [...new Set(taintList)];
+// }
+
+class IndexPageInner extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ filenames: [],
+ availableNGrams: ["gram3", "text4", "wide8", "hash4"],
+ selectedNGrams: [],
+ availableTaints: [],
+ selectedTaints: [],
+ allQueuedFilesLength: 0,
+ finishedQueuedFilesLength: 0,
+ };
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.handleTextareaInput = this.handleTextareaInput.bind(this);
+ this.handleNGramSelect = this.handleNGramSelect.bind(this);
+ this.handleTaintSelect = this.handleTaintSelect.bind(this);
+ }
+
+ ursa_id = this.props.params.ursa_id;
+
+ componentDidMount() {
+ api.get(`/queue/${this.props.params.ursa_id}`)
+ // .then((response) => {
+ // this.setState({
+ // allQueuedFilesLength: response.data.all_files, // NOTE: dunno whether these params will be provided at all
+ // finishedQueuedFilesLength: response.data.finished_files
+ // })
+ // })
+ // .catch((error) => {
+ // this.setState({ error: error });
+ // });
+ .catch((_e) => {
+ this.setState({
+ allQueuedFilesLength: 10000,
+ finishedQueuedFilesLength: 2137,
+ });
+ }); // TODO: uncomment prev then-catch clause after API implementation
+
+ api.get("/backend/datasets")
+ .then((_response) => {
+ // this.setState({
+ // availableTaints: getAvailableTaintsListFromDatasets(response.data.datasets),
+ // });
+ this.setState({
+ availableTaints: ["test_taint", "some other taint"],
+ }); // TODO: uncomment prev set state after API implementation
+ })
+ .catch((error) => {
+ this.setState({ error: error });
+ });
+ }
+
+ handleTextareaInput(e) {
+ const splitFiles = e.target.value.split("\n").filter((file) => !!file);
+ this.setState({ filenames: splitFiles });
+ }
+
+ handleNGramSelect(selection) {
+ this.setState({ selectedNGrams: selection });
+ }
+
+ handleTaintSelect(selection) {
+ this.setState({ selectedTaints: selection });
+ }
+
+ handleSubmit() {
+ // TODO: process following data accordingly to new endpoint
+ // c_onsole.log(this.state.filenames); // list of strings
+ // c_onsole.log(this.state.selectedNGrams); // list of {value: nGram, label: nGram} objects
+ // c_onsole.log(this.state.selectedTaints); // list of {value: taint, label: taint} objects
+ api.post(`/queue/${this.ursa_id}`, {})
+ .then((_r) => {})
+ .catch((_e) => {});
+ }
+
+ render() {
+ const fileLen = this.state.filenames.length;
+ const percentage = this.state.allQueuedFilesLength
+ ? (this.state.finishedQueuedFilesLength * 100.0) /
+ this.state.allQueuedFilesLength
+ : 0;
+ return (
+
+
+
{`Index ${this.props.params.ursa_id}`}
+
+
+
+ {this.state.availableTaints.length > 0 && (
+
+ )}
+
+
+ {percentage ? (
+ <>
+
+
+ >
+ ) : (
+ <>>
+ )}
+
+
+ );
+ }
+}
+
+function IndexPage() {
+ return ;
+}
+
+export default IndexPage;
diff --git a/src/mqueryfront/src/indexFiles/IndexProgressBar.js b/src/mqueryfront/src/indexFiles/IndexProgressBar.js
new file mode 100644
index 00000000..4d941afc
--- /dev/null
+++ b/src/mqueryfront/src/indexFiles/IndexProgressBar.js
@@ -0,0 +1,25 @@
+import { Component } from "react";
+
+class IndexProgressBar extends Component {
+ render() {
+ return (
+
+
Indexing progress
+
+
+ {`${this.props.percentage}%`}
+
+
+
+ );
+ }
+}
+
+export default IndexProgressBar;
diff --git a/src/mqueryfront/src/status/IndexLink.js b/src/mqueryfront/src/status/IndexLink.js
new file mode 100644
index 00000000..51d731a6
--- /dev/null
+++ b/src/mqueryfront/src/status/IndexLink.js
@@ -0,0 +1,21 @@
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
+import { Link } from "react-router-dom";
+
+const IndexLink = (props) => {
+ return (
+
+
+ Index using UrsaDB id.:{props.ursaID}
+
+
+
+ );
+};
+
+export default IndexLink;
diff --git a/src/mqueryfront/src/status/StatusPage.js b/src/mqueryfront/src/status/StatusPage.js
index 419a6b28..a6bb9cb5 100644
--- a/src/mqueryfront/src/status/StatusPage.js
+++ b/src/mqueryfront/src/status/StatusPage.js
@@ -5,6 +5,7 @@ import DatabaseTopology from "./DatabaseTopology";
import VersionStatus from "./VersionStatus";
import api from "../api";
import WarningPage from "../components/WarningPage";
+import IndexLink from "./IndexLink";
class StatusPage extends Component {
constructor(props) {
@@ -30,6 +31,8 @@ class StatusPage extends Component {
this._ismounted = true;
}
+ usraIDs = [1, 2, "asd"]; // TODO: collect from endpoint
+
getAgentsUrsaURLDuplicatesWarning(agentgroups) {
var ursaURLS = agentgroups.map((agent) => agent.spec.ursadb_url);
var duplicateURLS = ursaURLS.filter(
@@ -82,6 +85,14 @@ class StatusPage extends Component {
+
+
Index files
+
+ {this.usraIDs.map((ursaID) => (
+
+ ))}
+
+
);