mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
implemented new login page
This commit is contained in:
parent
9e0fec2596
commit
f10e10d525
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
71
net/web/src/access/login/Login.jsx
Normal file
71
net/web/src/access/login/Login.jsx
Normal 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>
|
||||
);
|
||||
};
|
||||
|
58
net/web/src/access/login/Login.styled.js
Normal file
58
net/web/src/access/login/Login.styled.js
Normal 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;
|
||||
}
|
||||
`;
|
||||
|
||||
|
99
net/web/src/access/login/useLogin.hook.js
Normal file
99
net/web/src/access/login/useLogin.hook.js
Normal 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 };
|
||||
}
|
||||
|
6
net/web/src/admin/Admin.jsx
Normal file
6
net/web/src/admin/Admin.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
|
||||
export function Admin() {
|
||||
return <></>
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
const Colors = {
|
||||
background: '#8fbea7',
|
||||
primary: '#8fbea7',
|
||||
formBackground: '#f4f4f4',
|
||||
grey: '#888888',
|
||||
white: '#f8f8f8',
|
||||
};
|
||||
|
||||
export default Colors;
|
||||
|
@ -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({})
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ export function Root() {
|
||||
useEffect(() => {
|
||||
if (app.state) {
|
||||
if (app.state.access) {
|
||||
navigate('/user');
|
||||
navigate('/session');
|
||||
}
|
||||
else {
|
||||
navigate('/login');
|
||||
|
6
net/web/src/session/Session.jsx
Normal file
6
net/web/src/session/Session.jsx
Normal file
@ -0,0 +1,6 @@
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
|
||||
export function Session() {
|
||||
return <></>
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user