Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend - Add form for natural language query #85

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkg_client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"react": "^18.2.0",
"react-bootstrap": "^2.9.1",
"react-dom": "^18.2.0",
"react-icons": "^4.12.0",
"react-router-dom": "^6.21.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5",
"web-vitals": "^2.1.4"
Expand Down
4 changes: 0 additions & 4 deletions pkg_client/src/App.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
.App {
text-align: center;
}

.App-logo {
height: 40vmin;
pointer-events: none;
Expand Down
6 changes: 2 additions & 4 deletions pkg_client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useContext } from "react";
import "./App.css";
import APIHandler from "./components/APIHandler";
import LoginForm from "./components/LoginForm";
import Container from 'react-bootstrap/Container'
import Container from "react-bootstrap/Container";
import { UserContext } from "./contexts/UserContext";

function App() {
Expand All @@ -13,9 +13,7 @@ function App() {
return (
<Container className="p-3">
<Container className="p-3 mb-4 bg-light rounded-3">
<div className="App">
{content}
</div>
<div className="App">{content}</div>
</Container>
</Container>
);
Expand Down
67 changes: 28 additions & 39 deletions pkg_client/src/components/APIHandler.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,39 @@
import React, { useContext, useEffect, useState } from "react";
import axios from "axios";
import React, { useContext } from "react";
import { UserContext } from "../contexts/UserContext";
import Container from "react-bootstrap/Container";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Layout from "./Layout";
import NLtoPKG from "./NLtoPKG";

const APIHandler: React.FC = () => {
const { user } = useContext(UserContext);
// State tracker for service management data.
const [serviceData, setServiceData] = useState(null);
// State tracker for personal facts data.
const [factsData, setFactsData] = useState(null);
// State tracker for PKG exploration data. Data presentation, graphs, etc.
const [exploreData, setExploreData] = useState(null);

useEffect(() => {
const baseURL =
(window as any)["PKG_API_BASE_URL"] || "http://localhost:5000";

axios
.get(`${baseURL}/service`)
.then((response) => setServiceData(response.data));
axios
.get(`${baseURL}/facts`)
.then((response) => setFactsData(response.data));
axios
.get(`${baseURL}/explore`)
.then((response) => setExploreData(response.data));
}, []);

return (
<Container>
<h1>Personal Knowledge Graph API</h1>
<div>
<p>Welcome {JSON.stringify(user, null, 2)}</p>
</div>
<div>
<h2>Service Management Data</h2>
<pre>{JSON.stringify(serviceData, null, 2)}</pre>
</div>
<div>
<h2>Personal Facts Data</h2>
<pre>{JSON.stringify(factsData, null, 2)}</pre>
</div>
<div>
<h2>PKG Exploration Data</h2>
<pre>{JSON.stringify(exploreData, null, 2)}</pre>
</div>
<h1>Personal Knowledge Graph</h1>

<BrowserRouter>
<Routes>
<Route path="/" element={<Layout />}>
<Route
index
element={
<div>
<div>Welcome {user?.username}.</div>
<NLtoPKG />
</div>
}
/>
<Route path="service" element={<div>Service Management</div>} />
<Route path="population" element={<div>Population form</div>} />
<Route
path="preferences"
element={<div>Personal Preferences</div>}
/>
<Route path="explore" element={<div>PKG Exploration</div>} />
</Route>
</Routes>
</BrowserRouter>
</Container>
);
};
Expand Down
47 changes: 47 additions & 0 deletions pkg_client/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Layout component for the application includes a navigation bar and content
// of the current tab.
import { Outlet, Link } from "react-router-dom";
import Nav from "react-bootstrap/Nav";
import { useState } from "react";

const Layout = () => {
const [activeKey, setActiveKey] = useState("/");

const handleSelect = (eventKey: string | null) => {
if (eventKey !== null) {
setActiveKey(eventKey);
}
};

return (
<>
<Nav variant="underline" activeKey={activeKey} onSelect={handleSelect}>
<Nav.Item>
<Nav.Link eventKey="/" as={Link} to="/">
Home
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="/service" as={Link} to="/service">
Service Management
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="/population" as={Link} to="/population">
Populate PKG
</Nav.Link>
</Nav.Item>
<Nav.Item>
<Nav.Link eventKey="/explore" as={Link} to="/explore">
Explore PKG
</Nav.Link>
</Nav.Item>
</Nav>

<br />
<Outlet />
</>
);
};

export default Layout;
44 changes: 44 additions & 0 deletions pkg_client/src/components/NLtoPKG.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Natural language to PKG component

import { Container } from "react-bootstrap";
import { UserContext } from "../contexts/UserContext";
import { useContext, useState } from "react";
import axios from "axios";
import QueryForm from "./QueryForm";

const NLtoPKG = () => {
const { user } = useContext(UserContext);
const [error, setError] = useState("");
const baseURL =
(window as any)["PKG_API_BASE_URL"] || "http://127.0.0.1:5000";

const handleSubmit = (query: string) => {
return axios
.post(`${baseURL}/nl`, {
query: query,
username: user?.username,
user_uri: user?.uri,
})
.then((response) => {
// TODO: Handle response.
// Output can be displayed in information-container.
setError("");
})
.catch((error) => {
setError(error.message);
throw error;
});
};

return (
<Container>
<div>
<b>Manage your PKG with natural language queries.</b>
</div>
<QueryForm handleSubmit={handleSubmit} error={error} />
<Container id="information-container"></Container>
</Container>
);
};

export default NLtoPKG;
66 changes: 66 additions & 0 deletions pkg_client/src/components/QueryForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Form comprising a single text input and a submit button.

import { useState } from "react";
import { Alert, Button, Spinner } from "react-bootstrap";
import Form from "react-bootstrap/Form";

interface QueryFormProps {
handleSubmit: (query: string) => Promise<void>;
error: string;
}

const QueryForm: React.FC<QueryFormProps> = ({ handleSubmit, error = "" }) => {
const [query, setQuery] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);

const handleClick = async (query: string) => {
try {
setIsSubmitting(true);
await handleSubmit(query);
setQuery("");
} catch (error) {
} finally {
setIsSubmitting(false);
}
};

return (
<div>
{error && <Alert variant="danger">{error}</Alert>}
<Form>
<fieldset disabled={isSubmitting}>
<Form.Control
required
id="query-input"
type="text"
placeholder="Enter query"
value={query}
onChange={(e) => setQuery(e.target.value)}
/>
<Button
id="submit-button"
variant="primary"
type="button"
onClick={() => handleClick(query)}
style={{ marginTop: "10px" }}
disabled={query === ""}
>
{isSubmitting ? (
<Spinner
as="span"
animation="border"
size="sm"
role="status"
aria-hidden="true"
style={{ marginRight: "5px" }}
/>
) : null}
{isSubmitting ? "Submitting..." : "Submit"}
</Button>
</fieldset>
</Form>
</div>
);
};

export default QueryForm;
33 changes: 20 additions & 13 deletions pkg_client/src/contexts/UserContext.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
import React, { useState } from "react";

export type User = {
username: string;
uri: string;
}
username: string;
uri: string;
};

type UserProviderProps = {
children: React.ReactNode;
children: React.ReactNode;
};

export const UserContext = React.createContext<{ user: User | null; setUser: React.Dispatch<React.SetStateAction<User | null>>; }>({ user: null, setUser: () => { } });
export const UserContext = React.createContext<{
user: User | null;
setUser: React.Dispatch<React.SetStateAction<User | null>>;
}>({ user: null, setUser: () => {} });

export const UserProvider: React.FC<UserProviderProps> = ({ children, }: { children: React.ReactNode }) => {
const [user, setUser] = useState<User | null>(null);
export const UserProvider: React.FC<UserProviderProps> = ({
children,
}: {
children: React.ReactNode;
}) => {
const [user, setUser] = useState<User | null>(null);

return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
}
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
Loading