implemented new login page

This commit is contained in:
Roland Osborne 2022-08-04 15:20:48 -07:00
parent 9e0fec2596
commit f10e10d525
11 changed files with 278 additions and 13 deletions

View File

@ -16,6 +16,8 @@ import { ViewportContextProvider } from 'context/ViewportContext';
import { AppWrapper } from 'App.styled';
import { Root } from './root/Root';
import { Access } from './access/Access';
import { Session } from './session/Session';
import { Admin } from './admin/Admin';
function App() {
@ -36,6 +38,8 @@ function App() {
<Route path="/" element={ <Root /> } />
<Route path="/login" element={ <Access mode="login" /> } />
<Route path="/create" element={ <Access mode="create" /> } />
<Route path="/session" element={ <Session /> } />
<Route path="/admin" element={ <Admin /> } />
</Routes>
</Router>
</AppWrapper>

View File

@ -3,6 +3,7 @@ import { useNavigate } from "react-router-dom";
import { AppContext } from 'context/AppContext';
import { ViewportContext } from 'context/ViewportContext';
import { AccessWrapper } from './Access.styled';
import { Login } from './login/Login';
import login from 'images/login.png'
@ -15,11 +16,18 @@ export function Access({ mode }) {
useEffect(() => {
if (app.state) {
if (app.state.access) {
navigate('/user');
navigate('/session');
}
}
}, [app]);
const Prompt = () => {
if (mode === 'login') {
return <Login />
}
return <></>
}
return (
<AccessWrapper>
{ (viewport.state.display === 'large' || viewport.state.display === 'xlarge') && (
@ -27,12 +35,16 @@ export function Access({ mode }) {
<div class="left">
<img class="splash" src={login} alt={login} />
</div>
<div class="right">{ mode }</div>
<div class="right">
<Prompt />
</div>
</div>
)}
{ (viewport.state.display === 'medium' || viewport.state.display === 'small') && (
<div class="full-layout">
<div class="center"></div>
<div class="center">
<Prompt />
</div>
</div>
)}
</AccessWrapper>

View File

@ -14,6 +14,9 @@ export const AccessWrapper = styled.div`
height: 100%;
border-radius: 4px;
background: ${Colors.formBackground};
display: flex;
align-items: center;
justify-content: center;
}
}
@ -38,6 +41,9 @@ export const AccessWrapper = styled.div`
width: 50%;
height: 100%;
background: ${Colors.formBackground};
display: flex;
align-items: center;
justify-content: center;
}
}
`;

View File

@ -0,0 +1,71 @@
import React, { useState } from 'react';
import { Button, Modal, Form, Input } from 'antd';
import { SettingOutlined, LockOutlined, UserOutlined } from '@ant-design/icons';
import { LoginWrapper, SubmitButton } from './Login.styled';
import { useLogin } from './useLogin.hook';
export function Login() {
const { state, actions } = useLogin();
const login = async () => {
try {
await actions.onLogin();
}
catch(err) {
Modal.error({
title: 'Login Error',
content: 'Please confirm your username and password.',
});
}
}
const keyDown = (e) => {
if (e.key === 'Enter') {
login()
}
}
return (
<LoginWrapper>
<div class="app-title">
<span>Databag</span>
<div class="settings" onClick={() => actions.onSettings()}>
<SettingOutlined />
</div>
</div>
<div class="form-title">Login</div>
<div class="form-form">
<Form name="basic" wrapperCol={{ span: 24, }}>
<Form.Item name="username">
<Input placeholder="Username" spellCheck="false" onChange={(e) => actions.setUsername(e.target.value)}
autocapitalize="none" onKeyDown={(e) => keyDown(e)} prefix={<UserOutlined />} />
</Form.Item>
<Form.Item name="password">
<Input.Password placeholder="Password" spellCheck="false" onChange={(e) => actions.setPassword(e.target.value)}
onKeyDown={(e) => keyDown(e)} prefix={<LockOutlined />} />
</Form.Item>
<div class="form-button">
<div class="form-login">
<Button type="primary" block onClick={login} disabled={ actions.isDisabled()}
loading={state.busy}>
Login
</Button>
</div>
</div>
<div class="form-button">
<Button type="link" block disabled={ !state.available } onClick={(e) => actions.onCreate()}>
Create Account
</Button>
</div>
</Form>
</div>
</LoginWrapper>
);
};

View File

@ -0,0 +1,58 @@
import styled from 'styled-components';
import Colors from 'constants/Colors';
export const LoginWrapper = styled.div`
max-width: 400px;
width: 90%;
height: 90%;
display: flex;
flex-direction: column;
.app-title {
font-size: 24px;
display: flex;
align-items: flex-start;
justify-content: center;
flex: 1;
color: ${Colors.grey};
.settings {
color: ${Colors.grey};
position: absolute;
top: 0px;
right: 0px;
font-size: 20px;
cursor: pointer;
margin: 16px;
}
}
.form-title {
font-size: 32px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
flex: 1;
}
.form-form {
flex: 2;
.form-button {
display: flex;
align-items: center;
justify-content: center;
.form-login {
width: 50%;
}
}
}
.form-submit {
background-color: #444444;
}
`;

View File

@ -0,0 +1,99 @@
import { useContext, useState, useEffect } from 'react';
import { AppContext } from 'context/AppContext';
import { useNavigate, useLocation, useParams } from "react-router-dom";
export function useLogin() {
const [state, setState] = useState({
username: '',
password: '',
available: false,
disabled: true,
busy: false,
});
const navigate = useNavigate();
const { search } = useLocation();
const app = useContext(AppContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
}
const actions = {
setUsername: (username) => {
updateState({ username });
},
setPassword: (password) => {
updateState({ password });
},
isDisabled: () => {
if (state.username === '' || state.password === '') {
return true
}
return false
},
onSettings: () => {
navigate('/admin');
},
onLogin: async () => {
if (!state.busy && state.username != '' && state.password != '') {
updateState({ busy: true })
try {
await app.actions.login(state.username, state.password)
}
catch (err) {
console.log(err);
updateState({ busy: false })
throw 'login failed: check your username and password';
}
updateState({ busy: false })
}
},
onCreate: () => {
navigate('/create');
},
};
useEffect(() => {
if (app) {
if (app.state) {
if (app.state.access) {
navigate('/session')
}
else {
let params = new URLSearchParams(search);
let token = params.get("access");
if (token) {
const access = async () => {
updateState({ busy: true })
try {
await app.actions.access(token)
}
catch (err) {
console.log(err);
}
updateState({ busy: false })
}
access();
}
}
}
if (app.actions && app.actions.available) {
const count = async () => {
try {
const available = await app.actions.available()
updateState({ available: available !== 0 })
}
catch(err) {
console.log(err);
}
}
count();
}
}
}, [app])
return { state, actions };
}

View File

@ -0,0 +1,6 @@
import React, { useContext, useEffect } from 'react';
export function Admin() {
return <></>
}

View File

@ -1,6 +1,9 @@
const Colors = {
background: '#8fbea7',
primary: '#8fbea7',
formBackground: '#f4f4f4',
grey: '#888888',
white: '#f8f8f8',
};
export default Colors;

View File

@ -47,19 +47,19 @@ export function useAppContext() {
const actions = {
logout: () => {
appLogout(updateState, clearWebsocket);
appLogout();
storeContext.actions.clear();
uploadContext.actions.clear();
resetData();
},
access: async (token) => {
await appAccess(token, updateState, setWebsocket)
await appAccess(token)
},
login: async (username, password) => {
await appLogin(username, password, updateState, setWebsocket)
await appLogin(username, password)
},
create: async (username, password, token) => {
await appCreate(username, password, token, updateState, setWebsocket)
await appCreate(username, password, token)
},
username: getUsername,
available: getAvailable,
@ -72,7 +72,7 @@ export function useAppContext() {
storeContext.actions.setValue('login:timestamp', access.created);
setWebsocket(access.appToken)
localStorage.setItem("session", JSON.stringify({
token: access.appToken,
access: access.appToken,
timestamp: access.created,
}));
return access.created;
@ -84,7 +84,7 @@ export function useAppContext() {
storeContext.actions.setValue('login:timestamp', access.created);
setWebsocket(access.appToken)
localStorage.setItem("session", JSON.stringify({
token: access.appToken,
access: access.appToken,
timestamp: access.created,
}));
return access.created;
@ -170,9 +170,9 @@ export function useAppContext() {
if (storage != null) {
try {
const session = JSON.parse(storage)
if (session?.token) {
setState({ token: session.token })
setWebsocket(session.token);
if (session?.access) {
setState({ access: session.access })
setWebsocket(session.access);
} else {
setState({})
}

View File

@ -10,7 +10,7 @@ export function Root() {
useEffect(() => {
if (app.state) {
if (app.state.access) {
navigate('/user');
navigate('/session');
}
else {
navigate('/login');

View File

@ -0,0 +1,6 @@
import React, { useContext, useEffect } from 'react';
export function Session() {
return <></>
}