diff --git a/pkg_client/package.json b/pkg_client/package.json index bd4b27d..c08d18d 100644 --- a/pkg_client/package.json +++ b/pkg_client/package.json @@ -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" diff --git a/pkg_client/src/App.css b/pkg_client/src/App.css index 74b5e05..2d26ac5 100644 --- a/pkg_client/src/App.css +++ b/pkg_client/src/App.css @@ -1,7 +1,3 @@ -.App { - text-align: center; -} - .App-logo { height: 40vmin; pointer-events: none; diff --git a/pkg_client/src/App.tsx b/pkg_client/src/App.tsx index baae969..405122f 100644 --- a/pkg_client/src/App.tsx +++ b/pkg_client/src/App.tsx @@ -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() { @@ -13,9 +13,7 @@ function App() { return ( -
- {content} -
+
{content}
); diff --git a/pkg_client/src/components/APIHandler.tsx b/pkg_client/src/components/APIHandler.tsx index 4f2f070..6d46369 100644 --- a/pkg_client/src/components/APIHandler.tsx +++ b/pkg_client/src/components/APIHandler.tsx @@ -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 ( -

Personal Knowledge Graph API

-
-

Welcome {JSON.stringify(user, null, 2)}

-
-
-

Service Management Data

-
{JSON.stringify(serviceData, null, 2)}
-
-
-

Personal Facts Data

-
{JSON.stringify(factsData, null, 2)}
-
-
-

PKG Exploration Data

-
{JSON.stringify(exploreData, null, 2)}
-
+

Personal Knowledge Graph

+ + + + }> + +
Welcome {user?.username}.
+ + + } + /> + Service Management} /> + Population form} /> + Personal Preferences} + /> + PKG Exploration} /> + +
+
); }; diff --git a/pkg_client/src/components/Layout.tsx b/pkg_client/src/components/Layout.tsx new file mode 100644 index 0000000..1f0ccb5 --- /dev/null +++ b/pkg_client/src/components/Layout.tsx @@ -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 ( + <> + + +
+ + + ); +}; + +export default Layout; diff --git a/pkg_client/src/components/NLtoPKG.tsx b/pkg_client/src/components/NLtoPKG.tsx new file mode 100644 index 0000000..b6db7b6 --- /dev/null +++ b/pkg_client/src/components/NLtoPKG.tsx @@ -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 ( + +
+ Manage your PKG with natural language queries. +
+ + +
+ ); +}; + +export default NLtoPKG; diff --git a/pkg_client/src/components/QueryForm.tsx b/pkg_client/src/components/QueryForm.tsx new file mode 100644 index 0000000..ba4cb7a --- /dev/null +++ b/pkg_client/src/components/QueryForm.tsx @@ -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; + error: string; +} + +const QueryForm: React.FC = ({ 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 ( +
+ {error && {error}} +
+
+ setQuery(e.target.value)} + /> + +
+
+
+ ); +}; + +export default QueryForm; diff --git a/pkg_client/src/contexts/UserContext.tsx b/pkg_client/src/contexts/UserContext.tsx index 0e00073..5684d10 100644 --- a/pkg_client/src/contexts/UserContext.tsx +++ b/pkg_client/src/contexts/UserContext.tsx @@ -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>; }>({ user: null, setUser: () => { } }); +export const UserContext = React.createContext<{ + user: User | null; + setUser: React.Dispatch>; +}>({ user: null, setUser: () => {} }); -export const UserProvider: React.FC = ({ children, }: { children: React.ReactNode }) => { - const [user, setUser] = useState(null); +export const UserProvider: React.FC = ({ + children, +}: { + children: React.ReactNode; +}) => { + const [user, setUser] = useState(null); - return ( - - {children} - - ); -} \ No newline at end of file + return ( + + {children} + + ); +};