1
0
Fork 0
mirror of https://github.com/pawelmalak/flame.git synced 2025-07-27 23:09:35 +02:00

Moved auth form. Added auto login and logout functionality

This commit is contained in:
Paweł Malak 2021-11-11 14:45:58 +01:00
parent 1571981252
commit d1c61bb393
18 changed files with 311 additions and 98 deletions

View file

@ -0,0 +1,13 @@
import { useSelector } from 'react-redux';
import { Redirect, Route, RouteProps } from 'react-router';
import { State } from '../../store/reducers';
export const ProtectedRoute = ({ ...rest }: RouteProps) => {
const { isAuthenticated } = useSelector((state: State) => state.auth);
if (isAuthenticated) {
return <Route {...rest} />;
} else {
return <Redirect to="/settings/app" />;
}
};

View file

@ -1,71 +1,14 @@
import { FormEvent, Fragment, useState } from 'react';
// Redux
import { useDispatch, useSelector } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../store';
import { State } from '../../../store/reducers';
// UI
import { Button, InputGroup, SettingsHeadline } from '../../UI';
// CSS
import { Fragment } from 'react';
import { Button, SettingsHeadline } from '../../UI';
import classes from './AppDetails.module.css';
// Utils
import { checkVersion } from '../../../utility';
import { AuthForm } from './AuthForm/AuthForm';
export const AppDetails = (): JSX.Element => {
const { isAuthenticated } = useSelector((state: State) => state.auth);
const dispatch = useDispatch();
const { login, logout } = bindActionCreators(actionCreators, dispatch);
const [password, setPassword] = useState('');
const formHandler = (e: FormEvent) => {
e.preventDefault();
login(password);
setPassword('');
};
return (
<Fragment>
<SettingsHeadline text="Authentication" />
{!isAuthenticated ? (
<form onSubmit={formHandler}>
<InputGroup>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
name="password"
placeholder="••••••"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<span>
See
<a
href="https://github.com/pawelmalak/flame/wiki/Authentication"
target="blank"
>
{` project wiki `}
</a>
to read more about authentication
</span>
</InputGroup>
<Button>Login</Button>
</form>
) : (
<div>
<p className={classes.text}>
You are logged in. Your session will expire <span>@@@@</span>
</p>
<Button click={logout}>Logout</Button>
</div>
)}
<AuthForm />
<hr className={classes.separator} />

View file

@ -0,0 +1,104 @@
import { FormEvent, Fragment, useEffect, useState } from 'react';
// Redux
import { useSelector, useDispatch } from 'react-redux';
import { bindActionCreators } from 'redux';
import { actionCreators } from '../../../../store';
import { State } from '../../../../store/reducers';
import { decodeToken, parseTokenExpire } from '../../../../utility';
// Other
import { InputGroup, Button } from '../../../UI';
import classes from '../AppDetails.module.css';
export const AuthForm = (): JSX.Element => {
const { isAuthenticated, token } = useSelector((state: State) => state.auth);
const dispatch = useDispatch();
const { login, logout } = bindActionCreators(actionCreators, dispatch);
const [tokenExpires, setTokenExpires] = useState('');
const [formData, setFormData] = useState({
password: '',
duration: '14d',
});
useEffect(() => {
if (token) {
const decoded = decodeToken(token);
const expiresIn = parseTokenExpire(decoded.exp);
setTokenExpires(expiresIn);
}
}, [token]);
const formHandler = (e: FormEvent) => {
e.preventDefault();
login(formData);
setFormData({
password: '',
duration: '14d',
});
};
return (
<Fragment>
{!isAuthenticated ? (
<form onSubmit={formHandler}>
<InputGroup>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
name="password"
placeholder="••••••"
value={formData.password}
onChange={(e) =>
setFormData({ ...formData, password: e.target.value })
}
/>
<span>
See
<a
href="https://github.com/pawelmalak/flame/wiki/Authentication"
target="blank"
>
{` project wiki `}
</a>
to read more about authentication
</span>
</InputGroup>
<InputGroup>
<label htmlFor="duration">Session duration</label>
<select
id="duration"
name="duration"
value={formData.duration}
onChange={(e) =>
setFormData({ ...formData, duration: e.target.value })
}
>
<option value="5s">dev: 5 seconds</option>
<option value="10s">dev: 10 seconds</option>
<option value="1h">1 hour</option>
<option value="1d">1 day</option>
<option value="14d">2 weeks</option>
<option value="30d">1 month</option>
<option value="1y">1 year</option>
</select>
</InputGroup>
<Button>Login</Button>
</form>
) : (
<div>
<p className={classes.text}>
You are logged in. Your session will expire{' '}
<span>{tokenExpires}</span>
</p>
<Button click={logout}>Logout</Button>
</div>
)}
</Fragment>
);
};

View file

@ -1,5 +1,9 @@
import { NavLink, Link, Switch, Route } from 'react-router-dom';
// Redux
import { useSelector } from 'react-redux';
import { State } from '../../store/reducers';
// Typescript
import { Route as SettingsRoute } from '../../interfaces';
@ -14,6 +18,7 @@ import { AppDetails } from './AppDetails/AppDetails';
import { StyleSettings } from './StyleSettings/StyleSettings';
import { SearchSettings } from './SearchSettings/SearchSettings';
import { DockerSettings } from './DockerSettings/DockerSettings';
import { ProtectedRoute } from '../Routing/ProtectedRoute';
// UI
import { Container, Headline } from '../UI';
@ -22,13 +27,17 @@ import { Container, Headline } from '../UI';
import { routes } from './settings.json';
export const Settings = (): JSX.Element => {
const { isAuthenticated } = useSelector((state: State) => state.auth);
const tabs = isAuthenticated ? routes : routes.filter((r) => !r.authRequired);
return (
<Container>
<Headline title="Settings" subtitle={<Link to="/">Go back</Link>} />
<div className={classes.Settings}>
{/* NAVIGATION MENU */}
<nav className={classes.SettingsNav}>
{routes.map(({ name, dest }: SettingsRoute, idx) => (
{tabs.map(({ name, dest }: SettingsRoute, idx) => (
<NavLink
className={classes.SettingsNavLink}
activeClassName={classes.SettingsNavLinkActive}
@ -45,11 +54,20 @@ export const Settings = (): JSX.Element => {
<section className={classes.SettingsContent}>
<Switch>
<Route exact path="/settings" component={Themer} />
<Route path="/settings/weather" component={WeatherSettings} />
<Route path="/settings/search" component={SearchSettings} />
<Route path="/settings/interface" component={UISettings} />
<Route path="/settings/docker" component={DockerSettings} />
<Route path="/settings/css" component={StyleSettings} />
<ProtectedRoute
path="/settings/weather"
component={WeatherSettings}
/>
<ProtectedRoute
path="/settings/search"
component={SearchSettings}
/>
<ProtectedRoute path="/settings/interface" component={UISettings} />
<ProtectedRoute
path="/settings/docker"
component={DockerSettings}
/>
<ProtectedRoute path="/settings/css" component={StyleSettings} />
<Route path="/settings/app" component={AppDetails} />
</Switch>
</section>

View file

@ -2,31 +2,38 @@
"routes": [
{
"name": "Theme",
"dest": "/settings"
"dest": "/settings",
"authRequired": false
},
{
"name": "Weather",
"dest": "/settings/weather"
"dest": "/settings/weather",
"authRequired": true
},
{
"name": "Search",
"dest": "/settings/search"
"dest": "/settings/search",
"authRequired": true
},
{
"name": "Interface",
"dest": "/settings/interface"
"dest": "/settings/interface",
"authRequired": true
},
{
"name": "Docker",
"dest": "/settings/docker"
"dest": "/settings/docker",
"authRequired": true
},
{
"name": "CSS",
"dest": "/settings/css"
"dest": "/settings/css",
"authRequired": true
},
{
"name": "App",
"dest": "/settings/app"
"dest": "/settings/app",
"authRequired": false
}
]
}