new login/logout infrastructure rough-in (#724, #802)

This commit is contained in:
Michael Quigley 2024-12-03 11:23:34 -05:00
parent ceb96b3e4f
commit 40783cdcda
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
6 changed files with 151 additions and 15 deletions

View File

@ -2,10 +2,17 @@ import {useEffect, useState} from "react";
import {Configuration, MetadataApi} from "./api"; import {Configuration, MetadataApi} from "./api";
import buildVisualizerGraph from "./model/visualizer.ts"; import buildVisualizerGraph from "./model/visualizer.ts";
import {GraphCanvas} from "reagraph"; import {GraphCanvas} from "reagraph";
import {Box} from "@mui/material"; import {Box, Button} from "@mui/material";
import NavBar from "./NavBar.tsx"; 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 [version, setVersion] = useState("no version set");
const [nodes, setNodes] = useState([]); const [nodes, setNodes] = useState([]);
const [edges, setEdges] = useState([]); const [edges, setEdges] = useState([]);
@ -26,7 +33,7 @@ const ApiConsole = () => {
let cfg = new Configuration({ let cfg = new Configuration({
headers: { headers: {
// ignorable token, local development environment // ignorable token, local development environment
"X-TOKEN": "8TPUmoBHMdA8" "X-TOKEN": user.token
} }
}); });
let api = new MetadataApi(cfg); let api = new MetadataApi(cfg);
@ -52,8 +59,9 @@ const ApiConsole = () => {
<NavBar version={version} /> <NavBar version={version} />
<Box> <Box>
<div style={{position: "relative", width: "100%", height: "500px"}}> <div style={{position: "relative", width: "100%", height: "500px"}}>
<GraphCanvas nodes={nodes} edges={edges}/> <GraphCanvas nodes={nodes} edges={edges} theme={reagraphTheme} labelFontUrl={"https://fonts.googleapis.com/css?family=Poppins"}/>
</div> </div>
<Button onClick={logout}>Log Out</Button>
</Box> </Box>
</div> </div>
); );

42
ui100/src/App.tsx Normal file
View File

@ -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 ? <ApiConsole user={user} logout={logout} /> : <Login onLogin={login} />
return (
<BrowserRouter>
<Routes>
<Route index element={consoleRoot} />
</Routes>
</BrowserRouter>
);
}
export default App;

View File

@ -1,12 +1,37 @@
import {Box, Button, Container, TextField, Typography} from "@mui/material"; 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 ( return (
<Typography> <Typography>
<Container maxWidth="xs"> <Container maxWidth="xs">
<Box sx={{ marginTop: 8, display: "flex", flexDirection: "column", alignItems: "center"}}> <Box sx={{ marginTop: 8, display: "flex", flexDirection: "column", alignItems: "center"}}>
<h2>welcome to zrok...</h2> <h2>welcome to zrok...</h2>
<Box component="form" noValidate> <Box component="form" noValidate onSubmit={login}>
<TextField <TextField
margin="normal" margin="normal"
required required
@ -16,6 +41,8 @@ const Login = () => {
name="email" name="email"
autoComplete="email" autoComplete="email"
autoFocus autoFocus
value={email}
onChange={v => { setMessage(""); setEmail(v.target.value) }}
/> />
<TextField <TextField
margin="normal" margin="normal"
@ -26,10 +53,13 @@ const Login = () => {
type="password" type="password"
id="password" id="password"
autoComplete="current-password" autoComplete="current-password"
value={password}
onChange={v => { setMessage(""); setPassword(v.target.value) }}
/> />
<Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }}> <Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }}>
Log In Log In
</Button> </Button>
<Box component="h3">{message}</Box>
</Box> </Box>
</Box> </Box>
</Container> </Container>

View File

@ -1,21 +1,14 @@
import "./index.css"; import "./index.css";
import {StrictMode} from "react"; import {StrictMode} from "react";
import {createRoot} from "react-dom/client"; import {createRoot} from "react-dom/client";
import ApiConsole from "./ApiConsole.tsx";
import {ThemeProvider} from "@mui/material"; import {ThemeProvider} from "@mui/material";
import {theme} from "./model/theme.ts"; import {theme} from "./model/theme.ts";
import {BrowserRouter, Route, Routes} from "react-router"; import App from "./App.tsx";
import Login from "./Login.tsx";
createRoot(document.getElementById('root')!).render( createRoot(document.getElementById('root')!).render(
<StrictMode> <StrictMode>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>
<BrowserRouter> <App />
<Routes>
<Route index element={<ApiConsole />} />
<Route path="login" element={<Login />} />
</Routes>
</BrowserRouter>
</ThemeProvider> </ThemeProvider>
</StrictMode> </StrictMode>
); );

View File

@ -1,4 +1,5 @@
import {createTheme} from "@mui/material"; import {createTheme} from "@mui/material";
import {Theme} from "reagraph";
const componentOptions = { const componentOptions = {
MuiCard: { MuiCard: {
@ -45,3 +46,61 @@ export const modalStyle = {
boxShadow: 24, boxShadow: 24,
p: 4, 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'
}
}
};

4
ui100/src/model/user.ts Normal file
View File

@ -0,0 +1,4 @@
export interface User {
email: string;
token: string;
}