summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorErlend <erlendniko@hotmail.com>2022-06-22 15:03:43 +0200
committerErlend <erlendniko@hotmail.com>2022-06-22 15:03:43 +0200
commit5c9754bcb24921a518ea2b2f21072508a200be2b (patch)
treebf040b8a4cc55b9f7b91eeaf6d6f6cdd8cbdb931 /client
parent201ad32121bb5eaa86a94c2aa1d670b9e8712b45 (diff)
Moved files to project
Diffstat (limited to 'client')
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.js17
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.js14
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.js21
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.js12
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.js20
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.js24
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.js40
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/Info.js14
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.js18
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.js45
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.js29
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.js19
-rw-r--r--client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.js7
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.pngbin0 -> 29139 bytes
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.pngbin0 -> 19776 bytes
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/copy.svg7
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg1
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/features-help.pngbin0 -> 2554 bytes
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/information.svg10
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/paste.svg6
-rw-r--r--client/js/app/src/app/pages/querybuilder/assets/img/reload.svg6
-rw-r--r--client/js/app/src/app/pages/querybuilder/index.js63
22 files changed, 373 insertions, 0 deletions
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.js b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.js
new file mode 100644
index 00000000000..a07ebadf1f6
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/AddQueryInputButton.js
@@ -0,0 +1,17 @@
+import React, {useContext} from "react";
+import { QueryInputContext } from "Components/Contexts/QueryInputContext";
+import OverlayImageButton from "Components/Buttons/OverlayImageButton";
+
+export default function AddQueryInput() {
+ const {inputs, setInputs, id, setId} = useContext(QueryInputContext);
+
+ const updateInputs = (e) => {
+ e.preventDefault();
+ setId(id+1);
+ setInputs(prevInputs => [...prevInputs, {id: id+1, type: "", input: ""}]);
+ }
+
+ return (
+ <OverlayImageButton onClick={updateInputs} className="addRow" id="addRow" tooltip="Add row" height="0" width="0">+</OverlayImageButton>
+ );
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.js b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.js
new file mode 100644
index 00000000000..d3a024e4eb6
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/ImageButton.js
@@ -0,0 +1,14 @@
+import React from "react";
+
+
+export default function ImageButton({onClick, children, className, id, image, height="15", width="15", style}) {
+ return (
+ <button
+ id={id}
+ className={className}
+ onClick={onClick}>
+ <img src={image} height={height} width={width} style={style} alt="Missing"/>
+ {children}
+ </button>
+ );
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.js b/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.js
new file mode 100644
index 00000000000..f9fc5af145c
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/OverlayImageButton.js
@@ -0,0 +1,21 @@
+import React from "react";
+import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
+import Tooltip from 'react-bootstrap/Tooltip';
+import ImageButton from "./ImageButton";
+
+
+export default function OverlayImageButton({onClick, children, className, id, image, height="15", width="15", style, tooltip}) {
+ return (
+ <OverlayTrigger
+ placement="right"
+ delay={{show:250, hide:400}}
+ overlay={<Tooltip id="button-tooltip">{tooltip}</Tooltip>}>
+ <span>
+ <ImageButton id={id} className={className} image={image}
+ height={height} width={width} style={style} onClick={onClick}>{children}</ImageButton>
+ </span>
+ </OverlayTrigger>
+ );
+}
+
+// \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.js b/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.js
new file mode 100644
index 00000000000..e58a3ac8086
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Buttons/SimpleButton.js
@@ -0,0 +1,12 @@
+import React from "react";
+
+export default function SimpleButton({onClick, children, className, id}) {
+ return (
+ <button
+ id={id}
+ className={className}
+ onClick={onClick}>
+ {children}
+ </button>
+ );
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.js b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.js
new file mode 100644
index 00000000000..03579ac022f
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Contexts/QueryInputContext.js
@@ -0,0 +1,20 @@
+import React, {useState, createContext} from "react";
+
+export const QueryInputContext = createContext();
+
+export const QueryInputProvider = (prop) => {
+ const [inputs, setInputs] = useState([
+ {
+ id:1,
+ type: "",
+ input: ""
+ }
+ ])
+ const [id, setId] = useState(1)
+
+ return (
+ <QueryInputContext.Provider value={{inputs, setInputs, id, setId}}>
+ {prop.children}
+ </QueryInputContext.Provider>
+ )
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.js b/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.js
new file mode 100644
index 00000000000..32e9f4166b1
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Navigation/CustomNavbar.js
@@ -0,0 +1,24 @@
+import React from "react";
+import { Navbar, Container, Nav } from "react-bootstrap";
+
+function CustomNavbar() {
+ return (
+ <Navbar collapseOnSelect variant="default" expand="sm">
+ <Container>
+ <Navbar.Brand href="https://vespa.ai">Vespa. Big data. Real time.</Navbar.Brand>
+ <Navbar.Toggle aria-controls="responsive-navbar-nav" />
+ <Navbar.Collapse id="responsive-navbar-nav">
+ <Nav>
+ <Nav.Link href="https://blog.vespa.ai/">Blog</Nav.Link>
+ <Nav.Link variant="link" href="https://twitter.com/vespaengine">Twitter</Nav.Link>
+ <Nav.Link variant="link" href="https://docs.vespa.ai">Docs</Nav.Link>
+ <Nav.Link variant="link" href="https://github.com/vespa-engine">GitHub</Nav.Link>
+ <Nav.Link variant="link" href="https://docs.vespa.ai/en/getting-started.html">Get Started Now</Nav.Link>
+ </Nav>
+ </Navbar.Collapse>
+ </Container>
+ </Navbar>
+ );
+}
+
+export default CustomNavbar; \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.js b/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.js
new file mode 100644
index 00000000000..0b9ec75939a
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Navigation/Footer.js
@@ -0,0 +1,40 @@
+import React from "react";
+import { Table } from "react-bootstrap";
+
+export default function Footer() {
+ return (
+ <footer>
+ <Table borderless size="sm">
+ <thead className="footer-title">
+ <tr>
+ <th>Resources</th>
+ <th>Contact</th>
+ <th>Community</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <th><a href="https://docs.vespa.ai/en/vespa-quick-start.html">Getting Started</a></th>
+ <th><a href="https://twitter.com/vespaengine">Twitter</a></th>
+ <th><a href="https://github.com/vespa-engine/vespa/blob/master/CONTRIBUTING.md">Contributing</a></th>
+ </tr>
+ <tr>
+ <th><a href="https://docs.vespa.ai">Documentation</a></th>
+ <th><a href="mailto:info@vespa.ai">info@vespa.ai</a></th>
+ <th><a href="https://stackoverflow.com/questions/tagged/vespa">Stack Overflow</a></th>
+ </tr>
+ <tr>
+ <th><a href="https://github.com/vespa-engine/vespa">Open source</a></th>
+ <th><a href="https://github.com/vespa-engine/vespa/issues">Issues</a></th>
+ <th><a href="https://gitter.im/vespa-engine/Lobby">Gitter</a></th>
+ </tr>
+ </tbody>
+ </Table>
+ <div className="credits">
+ <span>Copyright Yahoo</span>
+ Licensed under <a href="https://github.com/vespa-engine/vespa/blob/master/LICENSE">Apache License 2.0</a>
+ , <a href="https://github.com/y7kim/agency-jekyll-theme">Theme</a> by Rick K.
+ </div>
+ </footer>
+ );
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/Info.js b/client/js/app/src/app/pages/querybuilder/Components/Text/Info.js
new file mode 100644
index 00000000000..79420af2a39
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/Info.js
@@ -0,0 +1,14 @@
+import React from "react";
+
+export default function Info({id, className="tip", height=15, width=15}) {
+const image = require("../../assets/img/information.svg").default;
+ return (
+ <>
+ <a href="#" className={className} id={`inf${id}`} style={{visibility: "visible"}}>
+ <img src={image} height={height} width={width} className="information" alt="Missing"/>
+ <span id={`span${id}`}></span>
+ </a>
+ </>
+ //TODO: Swap <a> with a bootstrap Overlay
+ );
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.js b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.js
new file mode 100644
index 00000000000..6fdb27610e3
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryDropDownForm.js
@@ -0,0 +1,18 @@
+import { QueryInputContext } from "Components/Contexts/QueryInputContext";
+import React, { useContext } from "react";
+import SimpleDropDownForm from "./SimpleDropDownForm";
+
+export default function QueryDropdownFormn({choices, id}) {
+ const {inputs, setInputs} = useContext(QueryInputContext);
+
+ const updateInputs = (e) => {
+ e.preventDefault();
+ const index = inputs.findIndex((element => element.id === id));
+ inputs[index].type = e.target.value;
+ setInputs(inputs);
+ }
+
+ return (
+ <SimpleDropDownForm id={id} onChange={updateInputs} choices={choices}></SimpleDropDownForm>
+ )
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.js b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.js
new file mode 100644
index 00000000000..c42bfe79008
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/QueryInput.js
@@ -0,0 +1,45 @@
+import React, { useContext } from "react";
+import SimpleButton from "Components/Buttons/SimpleButton";
+import Info from "./Info";
+import SimpleForm from "./SimpleForm";
+import { OverlayTrigger, Tooltip} from "react-bootstrap";
+import { QueryInputContext } from "Components/Contexts/QueryInputContext";
+import QueryDropdownFormn from "./QueryDropDownForm";
+
+export default function QueryInput({id, onClick}) {
+ const choices = ["yql", "hits", "offset", "tracelevel"]
+
+ const {inputs, setInputs} = useContext(QueryInputContext);
+
+
+ function removeRow(id) {
+ const newList = inputs.filter((item) => item.id !== id);
+ setInputs(newList);
+ }
+
+ const inputList = inputs.map((value, index) => {
+ return (
+ <div key={value.id} id={value.id} className="queryinput">
+ <QueryDropdownFormn choices={choices} id={value.id}></QueryDropdownFormn>
+ {/* <SimpleDropDownForm choices={choices} id={`i${value.id}`}></SimpleDropDownForm> */}
+ <Info id={value.id} height="15" width="15"></Info>
+ <SimpleForm id={`v${value.id}`} size="30"></SimpleForm>
+ <OverlayTrigger
+ placement="right"
+ delay={{show:250, hide:400}}
+ overlay={<Tooltip id="button-tooltip">Remove row</Tooltip>}>
+ <span>
+ <SimpleButton id={`b${value.id}`} className="removeRow" onClick={() => removeRow(value.id)} children="-"></SimpleButton>
+ </span>
+ </OverlayTrigger>
+ <br/>
+ </div>
+ )
+ });
+
+ return (
+ <>
+ {inputList}
+ </>
+ );
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.js b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.js
new file mode 100644
index 00000000000..247b6a37161
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleDropDownForm.js
@@ -0,0 +1,29 @@
+import React from "react";
+import { useState } from "react";
+
+export default function SimpleDropDownForm({choices, id, className="input", onChange}) {
+ SimpleDropDownForm.defaultProps = {
+ onChange: handleChange
+ }
+ const {choice, setChoice} = useState();
+
+ const options = choices.map((value, index) => {
+ return (
+ <option className="options" key={index} value={value}>{value}</option>
+ )
+ });
+
+ function handleChange(e) {
+ setChoice(e.target.value);
+ }
+
+
+ return (
+ <form id={id}>
+ <select className={className} value={choice} onChange={onChange}>
+ {options}
+ </select>
+ </form>
+ );
+}
+
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.js b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.js
new file mode 100644
index 00000000000..f01cb4ed9ff
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/SimpleForm.js
@@ -0,0 +1,19 @@
+import React from "react";
+import { useState } from "react";
+
+export default function SimpleForm({id, className="propvalue", initial, size="20"}) {
+ const [input, setValue] = useState(initial);
+
+ return (
+ <form className={className}>
+ <input
+ size={size}
+ type="text"
+ id={id}
+ className={className}
+ value={input}
+ onChange={(e) => setValue(e.target.value)}
+ />
+ </form>
+ );
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.js b/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.js
new file mode 100644
index 00000000000..1b780f670e4
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/Components/Text/TextBox.js
@@ -0,0 +1,7 @@
+import React from "react";
+
+export default function TextBox({id, className, children}) {
+ return (
+ <p className={className} id={id}>{children}</p>
+ )
+} \ No newline at end of file
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png b/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png
new file mode 100644
index 00000000000..ac87f8e94d0
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/Vespa-V2.png
Binary files differ
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png b/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png
new file mode 100644
index 00000000000..33063432c20
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/VespaIcon.png
Binary files differ
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg b/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg
new file mode 100644
index 00000000000..eada154413a
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/copy.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 488.3 488.3" style="enable-background:new 0 0 488.3 488.3;" xml:space="preserve" width="512px" height="512px" class=""><g><g>
+ <g>
+ <path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z" data-original="#000000" class="active-path" data-old_color="#F8F5F5" fill="#FCFCFC"/>
+ <path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z" data-original="#000000" class="active-path" data-old_color="#F8F5F5" fill="#FCFCFC"/>
+ </g>
+</g></g> </svg>
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg b/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg
new file mode 100644
index 00000000000..d78d2f9c17f
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/down-arrow.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 36 36"><defs><style>.cls-1{clip-path:url(#clip-path);}.cls-2{clip-path:url(#clip-path-2);}.cls-3{clip-path:url(#clip-path-3);}</style><clipPath id="clip-path"><path d="M18,4.09A13.91,13.91,0,1,1,4.09,18,13.91,13.91,0,0,1,18,4.09ZM18,35A17,17,0,1,0,6,30,17,17,0,0,0,18,35Z"/></clipPath><clipPath id="clip-path-2"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/></clipPath><clipPath id="clip-path-3"><path d="M26,13.83a1.55,1.55,0,0,0-2.16,0L18,19.75l-5.88-6A1.54,1.54,0,0,0,9.93,16L18,24.18,26.07,16a1.57,1.57,0,0,0,0-2.19Z"/></clipPath></defs><title>down-arrow</title><path d="M18,4.09A13.91,13.91,0,1,1,4.09,18,13.91,13.91,0,0,1,18,4.09ZM18,35A17,17,0,1,0,6,30,17,17,0,0,0,18,35Z"/><g class="cls-1"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/><g class="cls-2"><rect x="-4.15" y="-4.15" width="44.3" height="44.3" fill="#FFFFFF"/></g></g><path d="M26,13.83a1.55,1.55,0,0,0-2.16,0L18,19.75l-5.88-6A1.54,1.54,0,0,0,9.93,16L18,24.18,26.07,16a1.57,1.57,0,0,0,0-2.19Z"/><g class="cls-3"><rect x="-723.82" y="-837.15" width="1483.64" height="3802.85" fill="#FFFFFF"/><g class="cls-2"><rect x="4.35" y="8.21" width="27.3" height="21.12" fill="#FFFFFF"/></g></g></svg>
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png b/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png
new file mode 100644
index 00000000000..65702f8b91f
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/features-help.png
Binary files differ
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/information.svg b/client/js/app/src/app/pages/querybuilder/assets/img/information.svg
new file mode 100644
index 00000000000..da42cf2caf6
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/information.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512px" height="512px"><g><g>
+ <g>
+ <g>
+ <circle cx="256" cy="378.5" r="25" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ <path d="M256,0C114.516,0,0,114.497,0,256c0,141.484,114.497,256,256,256c141.484,0,256-114.497,256-256 C512,114.516,397.503,0,256,0z M256,472c-119.377,0-216-96.607-216-216c0-119.377,96.607-216,216-216 c119.377,0,216,96.607,216,216C472,375.377,375.393,472,256,472z" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ <path d="M256,128.5c-44.112,0-80,35.888-80,80c0,11.046,8.954,20,20,20s20-8.954,20-20c0-22.056,17.944-40,40-40 c22.056,0,40,17.944,40,40c0,22.056-17.944,40-40,40c-11.046,0-20,8.954-20,20v50c0,11.046,8.954,20,20,20 c11.046,0,20-8.954,20-20v-32.531c34.466-8.903,60-40.26,60-77.469C336,164.388,300.112,128.5,256,128.5z" data-original="#000000" class="active-path" data-old_color="#898989" fill="#767474"/>
+ </g>
+ </g>
+</g></g> </svg>
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg b/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg
new file mode 100644
index 00000000000..b2edac680bf
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/paste.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 561 561" style="enable-background:new 0 0 561 561;" xml:space="preserve" class=""><g><g>
+ <g id="content-paste">
+ <path d="M459,51H351.9c-10.2-30.6-38.25-51-71.4-51c-33.15,0-61.2,20.4-71.4,51H102c-28.05,0-51,22.95-51,51v408 c0,28.05,22.95,51,51,51h357c28.05,0,51-22.95,51-51V102C510,73.95,487.05,51,459,51z M280.5,51c15.3,0,25.5,10.2,25.5,25.5 S295.8,102,280.5,102S255,91.8,255,76.5S265.2,51,280.5,51z M459,510H102V102h51v76.5h255V102h51V510z" data-original="#000000" class="active-path" data-old_color="#F9F6F6" fill="#FBF9F9"/>
+ </g>
+</g></g> </svg>
diff --git a/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg b/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg
new file mode 100644
index 00000000000..c5381f9f232
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/assets/img/reload.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Layer_1" x="0px" y="0px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve" width="512px" height="512px" class=""><g><g>
+ <g>
+ <path d="M482.195,226.196C482.195,101.471,380.725,0,256.001,0S29.805,101.471,29.805,226.196c0,7.409,6.007,13.416,13.416,13.416 s13.416-6.008,13.416-13.416c0-109.93,89.434-199.363,199.363-199.363s199.363,89.434,199.363,199.363 c0,109.928-89.434,199.362-199.363,199.362h-23.276l33.282-37.255c4.937-5.525,4.458-14.007-1.067-18.944 c-5.525-4.937-14.008-4.457-18.944,1.068l-47.576,53.255c-7.788,8.718-7.788,21.866,0,30.584l47.576,53.255 c2.651,2.968,6.322,4.478,10.01,4.478c3.181,0,6.375-1.126,8.934-3.41c5.526-4.937,6.004-13.419,1.067-18.944l-33.282-37.255 h23.276C380.725,452.39,482.195,350.919,482.195,226.196z" data-original="#000000" class="active-path" data-old_color="#F0EDED" fill="#F3F2F2"/>
+ </g>
+</g></g> </svg>
diff --git a/client/js/app/src/app/pages/querybuilder/index.js b/client/js/app/src/app/pages/querybuilder/index.js
new file mode 100644
index 00000000000..de40d0c2174
--- /dev/null
+++ b/client/js/app/src/app/pages/querybuilder/index.js
@@ -0,0 +1,63 @@
+import "./css/agency.css";
+import "./css/vespa.css"
+import "bootstrap/dist/css/bootstrap.min.css";
+import "./font-awesome/css/font-awesome.min.css";
+
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import SimpleButton from 'Components/Buttons/SimpleButton';
+import QueryInput from 'Components/Text/QueryInput';
+import SimpleDropDownForm from 'Components/Text/SimpleDropDownForm';
+import SimpleForm from 'Components/Text/SimpleForm';
+import TextBox from 'Components/Text/TextBox';
+import ImageButton from "Components/Buttons/ImageButton";
+import OverlayImageButton from "Components/Buttons/OverlayImageButton";
+import AddQueryInput from "Components/Buttons/AddQueryInputButton";
+import { QueryInputProvider } from "Components/Contexts/QueryInputContext";
+
+
+
+const root = ReactDOM.createRoot(document.getElementById('root'));
+const messageMethodArray = ["POST", "GET"];
+
+const pasteImage = require("./assets/img/paste.svg").default;
+const copyImage = require("./assets/img/copy.svg").default;
+const refreshImage = require("./assets/img/reload.svg").default;
+
+
+root.render(
+ <>
+ <React.StrictMode>
+ <header>
+ <div className="intro container">
+ <TextBox className={"intro-lead-in"}>Vespa Search Engine</TextBox>
+ <TextBox className={"intro-long"}>Select the method for sending a request and construct a query.</TextBox>
+ <SimpleDropDownForm choices={messageMethodArray} id="method" className='methodselector'></SimpleDropDownForm>
+ <SimpleForm id="url" className='textbox' initial="http://localhost:8080/search/" size="30"></SimpleForm>
+ <SimpleButton id="send" className="button" onClick={handleClick}>Send</SimpleButton>
+ <br/>
+ <QueryInputProvider>
+ <div id="request">
+ <QueryInput></QueryInput>
+ </div>
+ <br/>
+ <AddQueryInput/>
+ </QueryInputProvider>
+ <br/>
+ <ImageButton id="pasteJSON" className="pasteJSON" showImage={true} image={pasteImage} style={{marginTop:"-2px", marginRight: "3px"}}>Paste JSON</ImageButton>
+ <SimpleButton className="showJSON">Show query JSON</SimpleButton>
+ <TextBox className="response">Response</TextBox>
+ <textarea className="responsebox" readOnly cols="70" rows="25"></textarea>
+ <OverlayImageButton className="intro-copy" image={copyImage} height="30" width="30" tooltip="Copy">Copy</OverlayImageButton>
+ <OverlayImageButton className="intro-refresh" image={refreshImage} height="30" width="30" tooltip="Refresh">Refresh</OverlayImageButton>
+ <br/>
+ <br/>
+ </div>
+ </header>
+ </React.StrictMode>
+ </>
+);
+
+function handleClick() {
+ console.log("Click happened");
+}