mirror of
https://github.com/featurebasedb/featurebase.git
synced 2026-08-28 10:54:59 +00:00
Added Featurebase UI code for authentication
This commit is contained in:
parent
3b58e887ed
commit
89a628e91a
21 changed files with 438 additions and 129 deletions
|
|
@ -363,7 +363,7 @@ func (h *Handler) collectStats(next http.Handler) http.Handler {
|
|||
|
||||
// latticeRoutes lists the frontend routes that do not directly correspond to
|
||||
// backend routes, and require special handling.
|
||||
var latticeRoutes = []string{"/tables", "/query", "/querybuilder", "/login"} // TODO somehow pull this from some metadata in the lattice directory
|
||||
var latticeRoutes = []string{"/tables", "/query", "/querybuilder", "/signin"} // TODO somehow pull this from some metadata in the lattice directory
|
||||
|
||||
// newRouter creates a new mux http router.
|
||||
func newRouter(handler *Handler) http.Handler {
|
||||
|
|
|
|||
|
|
@ -1,60 +1,37 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import CssBaseline from '@material-ui/core/CssBaseline';
|
||||
import { Route, Switch } from 'react-router-dom';
|
||||
import { darkTheme, lightTheme } from 'theme/';
|
||||
import { Home } from 'App/Home';
|
||||
import { Header } from 'shared/Header';
|
||||
import Login from 'App/AuthFlow/Login';
|
||||
import Main from 'Main';
|
||||
import { BrowserRouter, Route, Switch } from 'react-router-dom';
|
||||
import { useAuth } from 'services/useAuth';
|
||||
import PrivateRoute from 'shared/PrivateRoute/PrivateRoute';
|
||||
import { lightTheme } from 'theme/';
|
||||
|
||||
import { MuiThemeProvider } from '@material-ui/core/styles';
|
||||
import { Nav } from 'shared/Nav';
|
||||
import { NotFound } from 'App/NotFound';
|
||||
import { MoleculaTablesContainer } from 'App/MoleculaTables';
|
||||
import { QueryContainer } from 'App/Query';
|
||||
import { QueryBuilderContainer } from 'App/QueryBuilder';
|
||||
import css from './App.module.scss';
|
||||
import Login from 'App/Login/Login';
|
||||
|
||||
const App = () => {
|
||||
const [theme, setTheme] = useState<string>(
|
||||
localStorage.getItem('theme') || 'light'
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if(theme === 'dark') {
|
||||
document.documentElement.setAttribute('data-theme', 'dark')
|
||||
} else {
|
||||
document.documentElement.removeAttribute('data-theme');
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
const onToggleTheme = () => {
|
||||
const newTheme = theme === 'dark' ? 'light' : 'dark';
|
||||
setTheme(newTheme);
|
||||
localStorage.setItem('theme', newTheme);
|
||||
};
|
||||
const auth = useAuth();
|
||||
|
||||
return (
|
||||
<MuiThemeProvider theme={theme === 'light' ? lightTheme : darkTheme}>
|
||||
<CssBaseline />
|
||||
<Header onToggleTheme={onToggleTheme} />
|
||||
|
||||
<div className={css.container}>
|
||||
<div className={css.layout}>
|
||||
<Nav />
|
||||
|
||||
<div className={css.mainContent}>
|
||||
<BrowserRouter>
|
||||
{auth.isLoading ? (
|
||||
<div></div>
|
||||
) : (
|
||||
<MuiThemeProvider theme={lightTheme}>
|
||||
{auth.authOn ? (
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route path="/tables/:id?" component={MoleculaTablesContainer} />
|
||||
<Route exact path="/query" component={QueryContainer} />
|
||||
<Route exact path="/querybuilder" component={QueryBuilderContainer} />
|
||||
<Route exact path="/login" component={Login} />
|
||||
<Route component={NotFound} />
|
||||
<Route
|
||||
exact
|
||||
path="/signin"
|
||||
render={(props) => <Login {...props} name="Login"></Login>}
|
||||
/>
|
||||
<PrivateRoute path="/" component={Main} />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MuiThemeProvider>
|
||||
) : (
|
||||
<Route path="/" component={Main} />
|
||||
)}
|
||||
</MuiThemeProvider>
|
||||
)}
|
||||
</BrowserRouter>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
|
|
|||
56
lattice/src/App/AuthFlow/AuthFlow.module.scss
Normal file
56
lattice/src/App/AuthFlow/AuthFlow.module.scss
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
.main {
|
||||
min-height: 100vh;
|
||||
background-repeat: no-repeat;
|
||||
background-image: linear-gradient(
|
||||
to bottom,
|
||||
rgba(250, 250, 250, 1),
|
||||
rgba(250, 250, 250, 0.7)
|
||||
),
|
||||
url(/assets/bg-pattern.png);
|
||||
background-size: cover;
|
||||
padding-bottom: 32px;
|
||||
}
|
||||
|
||||
.logoContainer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 85px;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
.loginForm {
|
||||
width: 500px;
|
||||
margin: 0 auto;
|
||||
padding-top: 75px;
|
||||
}
|
||||
|
||||
.formError {
|
||||
color: #f44336;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.sso {
|
||||
text-align: center;
|
||||
padding: 24px 0 16px;
|
||||
}
|
||||
|
||||
.passwordField {
|
||||
position: relative;
|
||||
|
||||
.forgotPassword {
|
||||
// [syang] Eww yes, I hate this
|
||||
position: absolute;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.backToSignIn {
|
||||
padding: 24px 0 16px;
|
||||
}
|
||||
|
||||
.alert {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
34
lattice/src/App/AuthFlow/Login.tsx
Normal file
34
lattice/src/App/AuthFlow/Login.tsx
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { ReactComponent as MLogo } from 'assets/m-bug-alt.svg';
|
||||
|
||||
import Card from '@material-ui/core/Card';
|
||||
import CardContent from '@material-ui/core/CardContent';
|
||||
import CardHeader from '@material-ui/core/CardHeader';
|
||||
|
||||
import css from './AuthFlow.module.scss';
|
||||
import SignInButton from './SignInButton';
|
||||
|
||||
function Login(props) {
|
||||
const renderLoginForm = () => (
|
||||
<Card>
|
||||
<CardHeader
|
||||
// title={"Sign in"}
|
||||
subheader={"Sign in to continue"}
|
||||
/>
|
||||
<CardContent>
|
||||
<SignInButton />
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={css.main}>
|
||||
<div className={css.loginForm}>
|
||||
<div className={css.logoContainer}>
|
||||
<MLogo className={css.logo} />
|
||||
</div>
|
||||
{renderLoginForm()}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default Login;
|
||||
19
lattice/src/App/AuthFlow/SignInButton.tsx
Normal file
19
lattice/src/App/AuthFlow/SignInButton.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Button } from '@material-ui/core';
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const SignInButton: React.FC<Props> = ({ children }) => {
|
||||
return (
|
||||
<a href="/login">
|
||||
<Button variant="contained" color="primary" size="large" fullWidth>
|
||||
Sign in
|
||||
</Button>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignInButton;
|
||||
19
lattice/src/App/AuthFlow/SignOutButton.tsx
Normal file
19
lattice/src/App/AuthFlow/SignOutButton.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import React from 'react';
|
||||
|
||||
import { Button } from '@material-ui/core';
|
||||
|
||||
interface Props {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const SignOutButton: React.FC<Props> = ({ children }) => {
|
||||
return (
|
||||
<a href="/logout">
|
||||
<Button variant="contained" color="secondary">
|
||||
Signout
|
||||
</Button>
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
export default SignOutButton;
|
||||
1
lattice/src/App/AuthFlow/index.ts
Normal file
1
lattice/src/App/AuthFlow/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from './Login';
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
import LoginButton from './LoginButton';
|
||||
import { pilosa } from 'services/eventServices';
|
||||
|
||||
function login() {
|
||||
pilosa.get.login().then((res) => {
|
||||
console.log(`login result:`, res);
|
||||
});
|
||||
}
|
||||
|
||||
function Login() {
|
||||
return (
|
||||
<>
|
||||
<h6>Login</h6>
|
||||
<LoginButton onClick={login} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Login;
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import React from 'react';
|
||||
|
||||
interface Props {
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
const LoginButton: React.FC<Props> = ({ onClick }) => {
|
||||
return <button onClick={onClick}>Login</button>;
|
||||
};
|
||||
|
||||
export default LoginButton;
|
||||
|
|
@ -1 +0,0 @@
|
|||
export * from './Login';
|
||||
67
lattice/src/Main.tsx
Normal file
67
lattice/src/Main.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
import { Home } from "App/Home";
|
||||
import { MoleculaTablesContainer } from "App/MoleculaTables";
|
||||
import { NotFound } from "App/NotFound";
|
||||
import { QueryContainer } from "App/Query";
|
||||
import { QueryBuilderContainer } from "App/QueryBuilder";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Route, Switch } from "react-router-dom";
|
||||
import { Header } from "shared/Header";
|
||||
import { Nav } from "shared/Nav";
|
||||
import { darkTheme, lightTheme } from "theme/";
|
||||
|
||||
import CssBaseline from "@material-ui/core/CssBaseline";
|
||||
import { MuiThemeProvider } from "@material-ui/core/styles";
|
||||
|
||||
import css from "./App.module.scss";
|
||||
|
||||
const Main = () => {
|
||||
const [theme, setTheme] = useState<string>(
|
||||
localStorage.getItem("theme") || "light"
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (theme === "dark") {
|
||||
document.documentElement.setAttribute("data-theme", "dark");
|
||||
} else {
|
||||
document.documentElement.removeAttribute("data-theme");
|
||||
}
|
||||
}, [theme]);
|
||||
|
||||
const onToggleTheme = () => {
|
||||
const newTheme = theme === "dark" ? "light" : "dark";
|
||||
setTheme(newTheme);
|
||||
localStorage.setItem("theme", newTheme);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MuiThemeProvider theme={theme === "light" ? lightTheme : darkTheme}>
|
||||
<CssBaseline />
|
||||
<Header onToggleTheme={onToggleTheme} />
|
||||
<div className={css.container}>
|
||||
<div className={css.layout}>
|
||||
<Nav />
|
||||
<div className={css.mainContent}>
|
||||
<Switch>
|
||||
<Route exact path="/" component={Home} />
|
||||
<Route
|
||||
path="/tables/:id?"
|
||||
component={MoleculaTablesContainer}
|
||||
/>
|
||||
<Route exact path="/query" component={QueryContainer} />
|
||||
<Route
|
||||
exact
|
||||
path="/querybuilder"
|
||||
component={QueryBuilderContainer}
|
||||
/>
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</MuiThemeProvider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Main;
|
||||
BIN
lattice/src/assets/bg-pattern.png
Normal file
BIN
lattice/src/assets/bg-pattern.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
16
lattice/src/assets/m-bug-alt.svg
Normal file
16
lattice/src/assets/m-bug-alt.svg
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg viewBox="0 0 276 313" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<defs>
|
||||
<polygon id="path-1" points="0 0.35 275.231 0.35 275.231 312.94 0 312.94"></polygon>
|
||||
</defs>
|
||||
<g id="Group-3">
|
||||
<mask id="mask-2" fill="white">
|
||||
<use xlink:href="#path-1"></use>
|
||||
</mask>
|
||||
<g id="Clip-2"></g>
|
||||
<path d="M275.231,86.005 C275.231,81.175 271.809,75.246 267.625,72.831 L145.222,2.162 C141.038,-0.254 134.193,-0.254 130.01,2.162 L7.606,72.831 C3.423,75.246 0,81.175 0,86.005 L0,227.345 C0,232.175 3.423,238.103 7.606,240.518 L130.01,311.188 C134.193,313.603 141.038,313.603 145.222,311.188 L267.625,240.518 C271.809,238.103 275.231,232.175 275.231,227.345 L275.231,86.005" id="Fill-1" fill="#084E83" mask="url(#mask-2)"></path>
|
||||
</g>
|
||||
<path d="M143.926,42.423 C138.489,39.277 131.526,41.13 128.376,46.569 C125.226,52.008 127.082,58.97 132.52,62.119 L216.669,110.849 L216.669,208.127 C216.669,214.412 221.764,219.507 228.049,219.507 C234.334,219.507 239.429,214.412 239.429,208.127 L239.429,97.728 L143.926,42.423" id="Fill-4" fill="#C3D4DD"></path>
|
||||
<path d="M83.364,72.494 C80.213,77.933 82.07,84.895 87.508,88.044 L171.657,136.774 L171.657,234.053 C171.657,240.338 176.751,245.433 183.036,245.433 C189.321,245.433 194.416,240.338 194.416,234.053 L194.416,123.653 L98.913,68.349 C93.477,65.202 86.513,67.055 83.364,72.494" id="Fill-5" fill="#48B5CD"></path>
|
||||
<path d="M38.185,98.634 C37.203,100.33 36.708,102.173 36.656,104.003 C36.633,105.008 36.819,208.157 36.819,208.157 C36.819,214.442 41.913,219.537 48.199,219.537 C54.484,219.537 59.578,214.442 59.578,208.157 L59.578,124.13 L81.632,136.915 L81.632,234.127 C81.632,240.412 86.727,245.507 93.012,245.507 C99.297,245.507 104.392,240.412 104.392,234.127 L104.392,150.11 L126.645,163.011 L126.645,260.017 C126.645,266.301 131.739,271.396 138.024,271.396 C144.309,271.396 149.404,266.301 149.404,260.017 L149.404,149.846 L53.735,94.489 C48.299,91.342 41.334,93.196 38.185,98.634" id="Fill-6" fill="#FFFFFF"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -1,15 +1,19 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
import { BrowserRouter as Router, Route } from 'react-router-dom';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
import './index.scss';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { ProvideAuth } from 'services/useAuth';
|
||||
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
|
||||
ReactDOM.render(
|
||||
<Router>
|
||||
<Route path="/" component={App} />
|
||||
</Router>,
|
||||
document.getElementById('root')
|
||||
<React.StrictMode>
|
||||
<ProvideAuth>
|
||||
<App />
|
||||
</ProvideAuth>
|
||||
</React.StrictMode>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
|
|
|
|||
|
|
@ -1,49 +1,53 @@
|
|||
import axios from 'axios';
|
||||
|
||||
import { baseURL } from './baseURL';
|
||||
|
||||
const api = axios.create({
|
||||
baseURL,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
Accept: 'application/json'
|
||||
}
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
Accept: "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
export const pilosa = {
|
||||
get: {
|
||||
status() {
|
||||
return api.get('/status');
|
||||
return api.get("/status");
|
||||
},
|
||||
login() {
|
||||
return api.get('/login');
|
||||
auth() {
|
||||
return api.get("/auth");
|
||||
},
|
||||
userinfo() {
|
||||
return api.get("/userinfo");
|
||||
},
|
||||
info() {
|
||||
return api.get('/info');
|
||||
return api.get("/info");
|
||||
},
|
||||
version() {
|
||||
return api.get('/version');
|
||||
return api.get("/version");
|
||||
},
|
||||
transactions() {
|
||||
return api.get('/ui/transaction');
|
||||
return api.get("/ui/transaction");
|
||||
},
|
||||
transaction(id) {
|
||||
return api.get(`/transaction/${id}`);
|
||||
},
|
||||
schema() {
|
||||
return api.get('/schema');
|
||||
return api.get("/schema");
|
||||
},
|
||||
schemaDetails() {
|
||||
return api.get('/schema/details');
|
||||
return api.get("/schema/details");
|
||||
},
|
||||
metrics() {
|
||||
return api.get('/metrics.json');
|
||||
return api.get("/metrics.json");
|
||||
},
|
||||
usage() {
|
||||
return api.get('/ui/usage');
|
||||
return api.get("/ui/usage");
|
||||
},
|
||||
queryHistory() {
|
||||
return api.get('/query-history');
|
||||
}
|
||||
return api.get("/query-history");
|
||||
},
|
||||
},
|
||||
post: {
|
||||
finishTransaction(id) {
|
||||
|
|
@ -51,6 +55,6 @@ export const pilosa = {
|
|||
},
|
||||
query(index, query) {
|
||||
return api.post(`/index/${index}/query`, query);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
95
lattice/src/services/useAuth.tsx
Normal file
95
lattice/src/services/useAuth.tsx
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { pilosa } from './eventServices';
|
||||
|
||||
const authContext = createContext<any>({});
|
||||
|
||||
// Provider component that wraps your app and makes auth object ...
|
||||
// ... available to any child component that calls useAuth().
|
||||
export function ProvideAuth({ children }) {
|
||||
const auth = useProvideAuth();
|
||||
return <authContext.Provider value={auth}>{children}</authContext.Provider>;
|
||||
}
|
||||
|
||||
// Hook for child components to get the auth object ...
|
||||
// ... and re-render when it changes.
|
||||
export const useAuth = () => {
|
||||
return useContext(authContext);
|
||||
};
|
||||
|
||||
export interface IUser {
|
||||
userid: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
// Provider hook that creates auth object and handles state
|
||||
function useProvideAuth() {
|
||||
const history = useHistory();
|
||||
const [user, setUser] = useState<IUser | undefined>(undefined);
|
||||
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
|
||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||
const [authOn, setAuthOn] = useState<boolean>(true);
|
||||
|
||||
const userinfo = () => {
|
||||
pilosa.get.userinfo().then((userinfoRes) => {
|
||||
if (userinfoRes.data.userid && userinfoRes.data.username) {
|
||||
setUser(userinfoRes.data);
|
||||
} else {
|
||||
setUser(undefined);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const signin = () => {
|
||||
history.push(`/login`);
|
||||
};
|
||||
|
||||
const signout = () => {
|
||||
history.push("/logout");
|
||||
};
|
||||
// Subscribe to user on mount
|
||||
// Because this sets state in the callback it will cause any ...
|
||||
// ... component that utilizes this hook to re-render with the ...
|
||||
// ... latest auth object.
|
||||
useEffect(() => {
|
||||
pilosa.get
|
||||
.auth()
|
||||
.then((res) => {
|
||||
// User is authenticated
|
||||
if (res.data === "OK") {
|
||||
setAuthOn(true);
|
||||
setIsAuthenticated(true);
|
||||
|
||||
// get userinfo
|
||||
userinfo();
|
||||
}
|
||||
// Auth is off
|
||||
else if (
|
||||
res.data.startsWith(
|
||||
"Trying to authenticate but authentication is off"
|
||||
)
|
||||
) {
|
||||
setAuthOn(false);
|
||||
}
|
||||
// User not authenticated
|
||||
else {
|
||||
setAuthOn(true);
|
||||
setIsAuthenticated(false);
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isAuthenticated,
|
||||
isLoading,
|
||||
user,
|
||||
authOn,
|
||||
userinfo,
|
||||
signin,
|
||||
signout,
|
||||
};
|
||||
}
|
||||
|
|
@ -1,12 +1,17 @@
|
|||
import React, { FC } from 'react';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Toolbar from '@material-ui/core/Toolbar';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ReactComponent as MoleculaLogo } from 'assets/lightTheme/MoleculaLogo.svg';
|
||||
import { ReactComponent as MoleculaLogoDark } from 'assets/darkTheme/MoleculaLogo.svg';
|
||||
import { ThemeToggle } from 'shared/ThemeToggle';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
import css from './Header.module.scss';
|
||||
import SignOutButton from "App/AuthFlow/SignOutButton";
|
||||
import { ReactComponent as MoleculaLogoDark } from "assets/darkTheme/MoleculaLogo.svg";
|
||||
import { ReactComponent as MoleculaLogo } from "assets/lightTheme/MoleculaLogo.svg";
|
||||
import { FC } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useAuth } from "services/useAuth";
|
||||
import { ThemeToggle } from "shared/ThemeToggle";
|
||||
|
||||
import AppBar from "@material-ui/core/AppBar";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import { useTheme } from "@material-ui/core/styles";
|
||||
import Toolbar from "@material-ui/core/Toolbar";
|
||||
|
||||
import css from "./Header.module.scss";
|
||||
|
||||
type HeaderProps = {
|
||||
onToggleTheme: () => void;
|
||||
|
|
@ -14,7 +19,8 @@ type HeaderProps = {
|
|||
|
||||
export const Header: FC<HeaderProps> = ({ onToggleTheme }) => {
|
||||
const theme = useTheme();
|
||||
const isDark = theme.palette.type === 'dark';
|
||||
const isDark = theme.palette.type === "dark";
|
||||
const auth = useAuth();
|
||||
|
||||
return (
|
||||
<AppBar
|
||||
|
|
@ -38,6 +44,22 @@ export const Header: FC<HeaderProps> = ({ onToggleTheme }) => {
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{auth.isAuthenticated ? (
|
||||
<div>
|
||||
{auth.user && (
|
||||
<Button
|
||||
style={{
|
||||
backgroundColor: "transparent",
|
||||
pointerEvents: "none",
|
||||
}}
|
||||
>
|
||||
{auth.user.username}
|
||||
</Button>
|
||||
)}
|
||||
<SignOutButton />
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
|
|
|||
|
|
@ -51,13 +51,6 @@ export const Nav = () => {
|
|||
</Typography>
|
||||
</ListItem>
|
||||
</NavLink>
|
||||
<NavLink to="/login" activeClassName={css.activeNavLink}>
|
||||
<ListItem className={css.navLink} button>
|
||||
<Typography variant="subtitle2" color={textColor}>
|
||||
Login
|
||||
</Typography>
|
||||
</ListItem>
|
||||
</NavLink>
|
||||
</List>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
33
lattice/src/shared/PrivateRoute/PrivateRoute.tsx
Normal file
33
lattice/src/shared/PrivateRoute/PrivateRoute.tsx
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { Redirect, Route } from 'react-router-dom';
|
||||
import { useAuth } from 'services/useAuth';
|
||||
|
||||
function PrivateRoute({ component: Component, ...rest }) {
|
||||
const auth = useAuth();
|
||||
|
||||
return (
|
||||
<Route
|
||||
{...rest}
|
||||
render={(props) => {
|
||||
// If the user is authed render the component
|
||||
if (auth.isAuthenticated) {
|
||||
// if (true) {
|
||||
return <Component {...rest} {...props} />;
|
||||
} else {
|
||||
// If they are not then we need to redirect to a public page
|
||||
return (
|
||||
<Redirect
|
||||
to={{
|
||||
pathname: "/signin",
|
||||
state: {
|
||||
from: props.location,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default PrivateRoute;
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
/* tslint:disable */
|
||||
import { createMuiTheme } from '@material-ui/core/styles';
|
||||
import { createTheme } from '@material-ui/core/styles';
|
||||
import { baseTheme } from 'theme/';
|
||||
|
||||
export const darkTheme = createMuiTheme({
|
||||
export const darkTheme = createTheme({
|
||||
...baseTheme,
|
||||
palette: {
|
||||
background: {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
/* tslint:disable */
|
||||
import { createMuiTheme } from '@material-ui/core/styles';
|
||||
import { createTheme } from '@material-ui/core/styles';
|
||||
import { baseTheme } from 'theme/';
|
||||
|
||||
export const lightTheme = createMuiTheme({
|
||||
export const lightTheme = createTheme({
|
||||
...baseTheme,
|
||||
palette: {
|
||||
background: {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue