From f10e10d5258c5f0c9f0251d0289eef5942265ce8 Mon Sep 17 00:00:00 2001 From: Roland Osborne Date: Thu, 4 Aug 2022 15:20:48 -0700 Subject: [PATCH] implemented new login page --- net/web/src/App.js | 4 + net/web/src/access/Access.jsx | 18 ++++- net/web/src/access/Access.styled.js | 6 ++ net/web/src/access/login/Login.jsx | 71 ++++++++++++++++ net/web/src/access/login/Login.styled.js | 58 +++++++++++++ net/web/src/access/login/useLogin.hook.js | 99 +++++++++++++++++++++++ net/web/src/admin/Admin.jsx | 6 ++ net/web/src/constants/Colors.js | 3 + net/web/src/context/useAppContext.hook.js | 18 ++--- net/web/src/root/Root.jsx | 2 +- net/web/src/session/Session.jsx | 6 ++ 11 files changed, 278 insertions(+), 13 deletions(-) create mode 100644 net/web/src/access/login/Login.jsx create mode 100644 net/web/src/access/login/Login.styled.js create mode 100644 net/web/src/access/login/useLogin.hook.js create mode 100644 net/web/src/admin/Admin.jsx create mode 100644 net/web/src/session/Session.jsx diff --git a/net/web/src/App.js b/net/web/src/App.js index deb7a11b..55febc39 100644 --- a/net/web/src/App.js +++ b/net/web/src/App.js @@ -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() { } /> } /> } /> + } /> + } /> diff --git a/net/web/src/access/Access.jsx b/net/web/src/access/Access.jsx index bbed5b1f..4ca4ce19 100644 --- a/net/web/src/access/Access.jsx +++ b/net/web/src/access/Access.jsx @@ -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 + } + return <> + } + return ( { (viewport.state.display === 'large' || viewport.state.display === 'xlarge') && ( @@ -27,12 +35,16 @@ export function Access({ mode }) {
{login}
-
{ mode }
+
+ +
)} { (viewport.state.display === 'medium' || viewport.state.display === 'small') && (
-
+
+ +
)}
diff --git a/net/web/src/access/Access.styled.js b/net/web/src/access/Access.styled.js index e07928d9..ad8e5fdb 100644 --- a/net/web/src/access/Access.styled.js +++ b/net/web/src/access/Access.styled.js @@ -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; } } `; diff --git a/net/web/src/access/login/Login.jsx b/net/web/src/access/login/Login.jsx new file mode 100644 index 00000000..b003a2f2 --- /dev/null +++ b/net/web/src/access/login/Login.jsx @@ -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 ( + +
+ Databag +
actions.onSettings()}> + +
+
+
Login
+
+
+ + + actions.setUsername(e.target.value)} + autocapitalize="none" onKeyDown={(e) => keyDown(e)} prefix={} /> + + + + actions.setPassword(e.target.value)} + onKeyDown={(e) => keyDown(e)} prefix={} /> + + +
+ +
+ +
+ +
+ +
+
+
+ ); +}; + diff --git a/net/web/src/access/login/Login.styled.js b/net/web/src/access/login/Login.styled.js new file mode 100644 index 00000000..18ab4e40 --- /dev/null +++ b/net/web/src/access/login/Login.styled.js @@ -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; + } +`; + + diff --git a/net/web/src/access/login/useLogin.hook.js b/net/web/src/access/login/useLogin.hook.js new file mode 100644 index 00000000..75909e6e --- /dev/null +++ b/net/web/src/access/login/useLogin.hook.js @@ -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 }; +} + diff --git a/net/web/src/admin/Admin.jsx b/net/web/src/admin/Admin.jsx new file mode 100644 index 00000000..3f31a7eb --- /dev/null +++ b/net/web/src/admin/Admin.jsx @@ -0,0 +1,6 @@ +import React, { useContext, useEffect } from 'react'; + +export function Admin() { + return <> +} + diff --git a/net/web/src/constants/Colors.js b/net/web/src/constants/Colors.js index 028fb783..a5e3ddf4 100644 --- a/net/web/src/constants/Colors.js +++ b/net/web/src/constants/Colors.js @@ -1,6 +1,9 @@ const Colors = { background: '#8fbea7', + primary: '#8fbea7', formBackground: '#f4f4f4', + grey: '#888888', + white: '#f8f8f8', }; export default Colors; diff --git a/net/web/src/context/useAppContext.hook.js b/net/web/src/context/useAppContext.hook.js index 6a28e6d0..e58b72b3 100644 --- a/net/web/src/context/useAppContext.hook.js +++ b/net/web/src/context/useAppContext.hook.js @@ -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({}) } diff --git a/net/web/src/root/Root.jsx b/net/web/src/root/Root.jsx index ef1e4948..bb71ece2 100644 --- a/net/web/src/root/Root.jsx +++ b/net/web/src/root/Root.jsx @@ -10,7 +10,7 @@ export function Root() { useEffect(() => { if (app.state) { if (app.state.access) { - navigate('/user'); + navigate('/session'); } else { navigate('/login'); diff --git a/net/web/src/session/Session.jsx b/net/web/src/session/Session.jsx new file mode 100644 index 00000000..7cf4c689 --- /dev/null +++ b/net/web/src/session/Session.jsx @@ -0,0 +1,6 @@ +import React, { useContext, useEffect } from 'react'; + +export function Session() { + return <> +} +