From 40783cdcda767346d793aed6afa9ae09a803d7ea Mon Sep 17 00:00:00 2001 From: Michael Quigley Date: Tue, 3 Dec 2024 11:23:34 -0500 Subject: [PATCH] new login/logout infrastructure rough-in (#724, #802) --- ui100/src/ApiConsole.tsx | 16 ++++++++--- ui100/src/App.tsx | 42 ++++++++++++++++++++++++++++ ui100/src/Login.tsx | 34 +++++++++++++++++++++-- ui100/src/main.tsx | 11 ++------ ui100/src/model/theme.ts | 59 ++++++++++++++++++++++++++++++++++++++++ ui100/src/model/user.ts | 4 +++ 6 files changed, 151 insertions(+), 15 deletions(-) create mode 100644 ui100/src/App.tsx create mode 100644 ui100/src/model/user.ts diff --git a/ui100/src/ApiConsole.tsx b/ui100/src/ApiConsole.tsx index e45e38f2..ccf844cc 100644 --- a/ui100/src/ApiConsole.tsx +++ b/ui100/src/ApiConsole.tsx @@ -2,10 +2,17 @@ import {useEffect, useState} from "react"; import {Configuration, MetadataApi} from "./api"; import buildVisualizerGraph from "./model/visualizer.ts"; import {GraphCanvas} from "reagraph"; -import {Box} from "@mui/material"; +import {Box, Button} from "@mui/material"; import NavBar from "./NavBar.tsx"; +import {reagraphTheme} from "./model/theme.ts"; +import {User} from "./model/user.ts"; -const ApiConsole = () => { +interface ApiConsoleProps { + user: User; + logout: () => void; +} + +const ApiConsole = ({ user, logout }: ApiConsoleProps) => { const [version, setVersion] = useState("no version set"); const [nodes, setNodes] = useState([]); const [edges, setEdges] = useState([]); @@ -26,7 +33,7 @@ const ApiConsole = () => { let cfg = new Configuration({ headers: { // ignorable token, local development environment - "X-TOKEN": "8TPUmoBHMdA8" + "X-TOKEN": user.token } }); let api = new MetadataApi(cfg); @@ -52,8 +59,9 @@ const ApiConsole = () => {
- +
+
); diff --git a/ui100/src/App.tsx b/ui100/src/App.tsx new file mode 100644 index 00000000..cda68176 --- /dev/null +++ b/ui100/src/App.tsx @@ -0,0 +1,42 @@ +import {BrowserRouter, Route, Routes} from "react-router"; +import ApiConsole from "./ApiConsole.tsx"; +import Login from "./Login.tsx"; +import {useEffect, useState} from "react"; +import {User} from "./model/user.ts"; + +const App = () => { + const [user, setUser] = useState(null as User); + + useEffect(() => { + const checkUser = () => { + const user = localStorage.getItem("user"); + if(user) { + console.log(user); + setUser(JSON.parse(user)); + console.log("reloaded user", user); + } + } + checkUser(); + }, []); + + const login = (user: User) => { + setUser(user); + } + + const logout = () => { + setUser(null); + localStorage.clear(); + } + + const consoleRoot = user ? : + + return ( + + + + + + ); +} + +export default App; \ No newline at end of file diff --git a/ui100/src/Login.tsx b/ui100/src/Login.tsx index 5b9d2054..11b7856c 100644 --- a/ui100/src/Login.tsx +++ b/ui100/src/Login.tsx @@ -1,12 +1,37 @@ import {Box, Button, Container, TextField, Typography} from "@mui/material"; +import {User} from "./model/user.ts"; +import {useState} from "react"; +import {AccountApi} from "./api"; + +interface LoginProps { + onLogin: (user: User) => void; +} + +const Login = ({ onLogin }: LoginProps) => { + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [message, setMessage] = useState(""); + + const login = async e => { + e.preventDefault(); + console.log(email, password); + + let api = new AccountApi(); + api.login({body: {"email": email, "password": password}}) + .then(d => { + onLogin({email: email, token: d.toString()}); + }) + .catch(e => { + setMessage("login failed: " + e.toString()); + }); + } -const Login = () => { return (

welcome to zrok...

- + { name="email" autoComplete="email" autoFocus + value={email} + onChange={v => { setMessage(""); setEmail(v.target.value) }} /> { type="password" id="password" autoComplete="current-password" + value={password} + onChange={v => { setMessage(""); setPassword(v.target.value) }} /> + {message}
diff --git a/ui100/src/main.tsx b/ui100/src/main.tsx index 1b37d21d..4d98fcf1 100644 --- a/ui100/src/main.tsx +++ b/ui100/src/main.tsx @@ -1,21 +1,14 @@ import "./index.css"; import {StrictMode} from "react"; import {createRoot} from "react-dom/client"; -import ApiConsole from "./ApiConsole.tsx"; import {ThemeProvider} from "@mui/material"; import {theme} from "./model/theme.ts"; -import {BrowserRouter, Route, Routes} from "react-router"; -import Login from "./Login.tsx"; +import App from "./App.tsx"; createRoot(document.getElementById('root')!).render( - - - } /> - } /> - - + ); \ No newline at end of file diff --git a/ui100/src/model/theme.ts b/ui100/src/model/theme.ts index 7d10bc70..cbd0ccb8 100644 --- a/ui100/src/model/theme.ts +++ b/ui100/src/model/theme.ts @@ -1,4 +1,5 @@ import {createTheme} from "@mui/material"; +import {Theme} from "reagraph"; const componentOptions = { MuiCard: { @@ -44,4 +45,62 @@ export const modalStyle = { bgcolor: 'background.paper', boxShadow: 24, p: 4, +}; + +export const reagraphTheme: Theme = { + canvas: { + background: '#fff', + fog: '#fff' + }, + node: { + fill: '#241775', + activeFill: '#9bf316', + opacity: 1, + selectedOpacity: 1, + inactiveOpacity: 0.2, + label: { + color: '#241775', + stroke: '#fff', + activeColor: '#9bf316' + }, + subLabel: { + color: '#241775', + stroke: '#eee', + activeColor: '#9bf316' + } + }, + lasso: { + border: '1px solid #55aaff', + background: 'rgba(75, 160, 255, 0.1)' + }, + ring: { + fill: '#D8E6EA', + activeFill: '#1DE9AC' + }, + edge: { + fill: '#D8E6EA', + activeFill: '#1DE9AC', + opacity: 1, + selectedOpacity: 1, + inactiveOpacity: 0.1, + label: { + stroke: '#fff', + color: '#2A6475', + activeColor: '#1DE9AC' + } + }, + arrow: { + fill: '#D8E6EA', + activeFill: '#1DE9AC' + }, + cluster: { + stroke: '#D8E6EA', + opacity: 1, + selectedOpacity: 1, + inactiveOpacity: 0.1, + label: { + stroke: '#fff', + color: '#2A6475' + } + } }; \ No newline at end of file diff --git a/ui100/src/model/user.ts b/ui100/src/model/user.ts new file mode 100644 index 00000000..782b02af --- /dev/null +++ b/ui100/src/model/user.ts @@ -0,0 +1,4 @@ +export interface User { + email: string; + token: string; +} \ No newline at end of file