updating access screens

This commit is contained in:
Roland Osborne 2024-02-29 15:16:46 -08:00
parent 4ae70543db
commit 2541417662
17 changed files with 239 additions and 40 deletions

View File

@ -4,13 +4,14 @@ import { Login } from './login/Login';
import { Admin } from './admin/Admin';
import { CreateAccount } from './createAccount/CreateAccount';
import { ThemeProvider } from "styled-components";
import { Select } from 'antd';
import login from 'images/login.png';
import dogin from 'images/dogin.png';
import bogin from 'images/bogin.png';
export function Access({ mode }) {
const { state } = useAccess();
const { state, actions } = useAccess();
return (
<ThemeProvider theme={state.colors}>
@ -22,7 +23,7 @@ export function Access({ mode }) {
<img className="splash" src={dogin} alt="Databag Splash" />
)}
{ state.scheme === 'light' && (
<img className="splash" src={login} alt="Databag Splash" />
<img className="splash" src={bogin} alt="Databag Splash" />
)}
</div>
<div className="right">
@ -35,6 +36,30 @@ export function Access({ mode }) {
{ mode === 'admin' && (
<Admin />
)}
<div className="footer">
<div className="option">
<div className="label">{state.strings.theme}</div>
<Select
defaultValue={null}
size="small"
style={{ width: 128 }}
value={state.theme}
onChange={actions.setTheme}
options={state.themes}
/>
</div>
<div className="option">
<div className="label">{state.strings.language}</div>
<Select
defaultValue={null}
size="small"
style={{ width: 128 }}
value={state.language}
onChange={actions.setLanguage}
options={state.languages}
/>
</div>
</div>
</div>
</div>
)}
@ -50,6 +75,32 @@ export function Access({ mode }) {
{ mode === 'admin' && (
<Admin />
)}
<div className="footer">
<div className="option">
<div className="label">{state.strings.theme}</div>
<Select
defaultValue={null}
size="small"
style={{ width: 128 }}
value={state.theme}
onChange={actions.setTheme}
options={state.themes}
/>
</div>
<div className="option">
<div className="label">{state.strings.language}</div>
<Select
defaultValue={null}
size="small"
style={{ width: 128 }}
value={state.language}
onChange={actions.setLanguage}
options={state.languages}
/>
</div>
</div>
</div>
</div>
)}

View File

@ -1,8 +1,22 @@
import styled from 'styled-components';
import { Colors } from 'constants/Colors';
export const AccessWrapper = styled.div`
height: 100%;
color: ${props => props.theme.hintText};
.footer {
display: flex;
gap: 32px;
.option {
gap: 8px;
.label {
font-size: 12px;
padding: 4px;
}
}
}
.full-layout {
width: 100%;
@ -13,8 +27,9 @@ export const AccessWrapper = styled.div`
width: 100%;
height: 100%;
border-radius: 4px;
background: ${Colors.formBackground};
background: ${props => props.theme.frameArea};
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
@ -41,8 +56,9 @@ export const AccessWrapper = styled.div`
.right {
width: 50%;
height: 100%;
background: ${Colors.formBackground};
background: ${props => props.theme.frameArea};
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}

View File

@ -14,9 +14,9 @@ export function Admin() {
}
catch(err) {
modal.error({
title: 'Login Error',
content: 'Please confirm your password.',
bodyStyle: { padding: 16 },
title: <span style={state.menuStyle}>{state.strings.adminError}</span>,
content: <span style={state.menuStyle}>{state.strings.adminMessage}</span>,
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
});
}
}
@ -36,20 +36,20 @@ export function Admin() {
<UserOutlined />
</div>
</div>
<div className="form-title">Admin</div>
<div className="form-title">{state.strings.admin}</div>
<div className="form-form">
<Form name="basic" wrapperCol={{ span: 24, }}>
<Form.Item name="password">
<Input.Password placeholder={ state.unclaimed ? 'New Password' : 'Password' } spellCheck="false"
<Input.Password placeholder={ state.unclaimed ? state.strings.newPassword : state.strings.password } spellCheck="false"
onChange={(e) => actions.setPassword(e.target.value)} autoComplete="current-password"
onKeyDown={(e) => keyDown(e)} prefix={<LockOutlined />} size="large" />
</Form.Item>
<div className="form-button">
<div className="form-login">
<Button type="primary" block onClick={login} size="middle" loading={state.busy}
disabled={!state.password}>Login</Button>
<Button className={state.password ? 'enabled' : 'disabled'} type="primary" block onClick={login} size="middle" loading={state.busy}
disabled={!state.password}>{state.strings.login}</Button>
</div>
</div>

View File

@ -7,6 +7,22 @@ export const AdminWrapper = styled.div`
height: 90%;
display: flex;
flex-direction: column;
.disabled {
background-color: ${props => props.theme.disabledArea};
button {
color: ${props => props.theme.idleText};
}
}
.enabled {
background-color: ${props => props.theme.enabledArea};
button {
color: ${props => props.theme.activeText};
}
}
.app-title {
font-size: 24px;

View File

@ -4,6 +4,7 @@ import { getNodeStatus } from 'api/getNodeStatus';
import { setNodeStatus } from 'api/setNodeStatus';
import { getNodeConfig } from 'api/getNodeConfig';
import { AppContext } from 'context/AppContext';
import { SettingsContext } from 'context/SettingsContext';
export function useAdmin() {
@ -12,10 +13,13 @@ export function useAdmin() {
placeholder: '',
unclaimed: null,
busy: false,
strings: {},
menuStyle: {},
});
const navigate = useNavigate();
const app = useContext(AppContext);
const settings = useContext(SettingsContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -61,6 +65,11 @@ export function useAdmin() {
},
}
useEffect(() => {
const { strings, menuStyle } = settings.state;
updateState({ strings, menuStyle });
}, [settings.state]);
return { state, actions };
}

View File

@ -14,9 +14,9 @@ export function CreateAccount() {
}
catch(err) {
modal.error({
title: 'Create Account Error',
content: 'Please check with your administrator.',
bodyStyle: { padding: 16 },
title: <span style={state.menuStyle}>{state.strings.createError}</span>,
content: <span style={state.menuStyle}>{state.strings.createMessage}</span>,
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
});
}
}
@ -36,32 +36,32 @@ export function CreateAccount() {
<SettingOutlined />
</div>
</div>
<div className="form-title">Create Account</div>
<div className="form-title">{state.strings.createAccount}</div>
<div className="form-form">
<Form name="basic" wrapperCol={{ span: 24, }}>
<Form.Item name="username" validateStatus={state.validateStatus} help={state.help}>
<Input placeholder="Username" spellCheck="false" onChange={(e) => actions.setUsername(e.target.value)}
<Input placeholder={state.strings.username} spellCheck="false" onChange={(e) => actions.setUsername(e.target.value)}
autoComplete="username" autoCapitalize="none" onKeyDown={(e) => keyDown(e)} prefix={<UserOutlined />} size="large" />
</Form.Item>
<div className="form-space"></div>
<Form.Item name="password">
<Input.Password placeholder="Password" spellCheck="false" onChange={(e) => actions.setPassword(e.target.value)}
<Input.Password placeholder={state.strings.newPassword} spellCheck="false" onChange={(e) => actions.setPassword(e.target.value)}
autoComplete="new-password" onKeyDown={(e) => keyDown(e)} prefix={<LockOutlined />} size="large" />
</Form.Item>
<Form.Item name="confirm">
<Input.Password placeholder="Confirm Password" spellCheck="false" onChange={(e) => actions.setConfirm(e.target.value)}
<Input.Password placeholder={state.strings.confirmPassword} spellCheck="false" onChange={(e) => actions.setConfirm(e.target.value)}
autoComplete="new-password" onKeyDown={(e) => keyDown(e)} prefix={<LockOutlined />} size="large" />
</Form.Item>
<div className="form-button">
<div className="form-create">
<Button type="primary" block onClick={create} disabled={ actions.isDisabled()}
<Button className={actions.isDisabled() ? 'disabled' : 'enabled'} type="primary" block onClick={create} disabled={ actions.isDisabled()}
loading={state.busy} size="middle">
Create Account
{state.strings.create}
</Button>
</div>
</div>
@ -69,7 +69,7 @@ export function CreateAccount() {
<div className="form-button">
<div className="form-login">
<Button type="link" block onClick={(e) => actions.onLogin()}>
Account Login
{state.strings.accountLogin}
</Button>
</div>
</div>

View File

@ -8,6 +8,22 @@ export const CreateAccountWrapper = styled.div`
display: flex;
flex-direction: column;
.disabled {
background-color: ${props => props.theme.disabledArea};
button {
color: ${props => props.theme.idleText};
}
}
.enabled {
background-color: ${props => props.theme.enabledArea};
button {
color: ${props => props.theme.activeText};
}
}
.app-title {
font-size: 24px;
display: flex;

View File

@ -1,5 +1,6 @@
import { useContext, useState, useEffect, useRef } from 'react';
import { AppContext } from 'context/AppContext';
import { SettingsContext } from 'context/SettingsContext';
import { useNavigate, useLocation } from "react-router-dom";
import { getUsername } from 'api/getUsername';
@ -13,11 +14,14 @@ export function useCreateAccount() {
busy: false,
validatetatus: 'success',
help: '',
strings: {},
menuStyle: {},
});
const navigate = useNavigate();
const { search } = useLocation();
const app = useContext(AppContext);
const settings = useContext(SettingsContext);
const debounce = useRef(null);
const updateState = (value) => {
@ -91,6 +95,11 @@ export function useCreateAccount() {
},
};
useEffect(() => {
const { strings, menuStyle } = settings.state;
updateState({ strings, menuStyle });
}, [settings.state]);
useEffect(() => {
let params = new URLSearchParams(search);
let token = params.get("add");

View File

@ -14,9 +14,9 @@ export function Login() {
}
catch(err) {
modal.error({
title: 'Login Error',
content: 'Please confirm your username and password.',
bodyStyle: { padding: 16 },
title: <span style={state.menuStyle}>{state.strings.loginError}</span>,
content: <span style={state.menuStyle}>{state.strings.loginMessage}</span>,
bodyStyle: { borderRadius: 8, padding: 16, ...state.menuStyle },
});
}
}
@ -36,32 +36,38 @@ export function Login() {
<SettingOutlined />
</div>
</div>
<div className="form-title">Login</div>
<div className="form-title">
<div>{ state.strings.login }</div>
{ !state.available && state.availableSet && (
<div className="form-message">{ state.strings.toCreate }</div>
)}
</div>
<div className="form-form">
<Form name="basic" wrapperCol={{ span: 24, }}>
<Form.Item name="username">
<Input placeholder="Username" spellCheck="false" onChange={(e) => actions.setUsername(e.target.value)}
<Input placeholder={state.strings.username} spellCheck="false" onChange={(e) => actions.setUsername(e.target.value)}
autoComplete="username" autoCapitalize="none" onKeyDown={(e) => keyDown(e)} prefix={<UserOutlined />} size="large" />
</Form.Item>
<Form.Item name="password">
<Input.Password placeholder="Password" spellCheck="false" onChange={(e) => actions.setPassword(e.target.value)}
<Input.Password placeholder={state.strings.password} spellCheck="false" onChange={(e) => actions.setPassword(e.target.value)}
autoComplete="current-password" onKeyDown={(e) => keyDown(e)} prefix={<LockOutlined />} size="large" />
</Form.Item>
<div className="form-button">
<div className="form-login">
<Button type="primary" block onClick={login} disabled={ actions.isDisabled()}
<Button className={actions.isDisabled() ? 'disabled' : 'enabled'} type="primary" block onClick={login} disabled={ actions.isDisabled()}
size="middle" loading={state.busy}>
Login
{state.strings.login}
</Button>
</div>
</div>
<div className="form-button">
<Button type="link" block disabled={ !state.available } onClick={(e) => actions.onCreate()}>
Create Account
{state.strings.createAccount}
</Button>
</div>

View File

@ -1,5 +1,4 @@
import styled from 'styled-components';
import { Colors } from 'constants/Colors';
export const LoginWrapper = styled.div`
max-width: 400px;
@ -8,16 +7,32 @@ export const LoginWrapper = styled.div`
display: flex;
flex-direction: column;
.disabled {
background-color: ${props => props.theme.disabledArea};
button {
color: ${props => props.theme.idleText};
}
}
.enabled {
background-color: ${props => props.theme.enabledArea};
button {
color: ${props => props.theme.activeText};
}
}
.app-title {
font-size: 24px;
display: flex;
align-items: flex-start;
justify-content: center;
flex: 1;
color: ${Colors.grey};
color: ${props => props.theme.hintText};
.settings {
color: ${Colors.grey};
color: ${props => props.theme.hintText};
position: absolute;
top: 0px;
right: 0px;
@ -34,6 +49,16 @@ export const LoginWrapper = styled.div`
align-items: center;
justify-content: center;
flex: 1;
flex-direction: column;
}
.form-message {
font-size: 14px;
padding-top: 8px;
align-items: center;
justify-content: center;
font-weight: normal;
text-align: center;
}
.form-form {

View File

@ -1,5 +1,6 @@
import { useContext, useState, useEffect } from 'react';
import { AppContext } from 'context/AppContext';
import { SettingsContext } from 'context/SettingsContext';
import { getAvailable } from 'api/getAvailable';
import { useLocation, useNavigate } from "react-router-dom";
@ -9,13 +10,17 @@ export function useLogin() {
username: '',
password: '',
available: false,
availableSet: false,
disabled: true,
busy: false,
strings: {},
menuStyle: {},
});
const navigate = useNavigate();
const { search } = useLocation();
const app = useContext(AppContext);
const settings = useContext(SettingsContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -60,7 +65,7 @@ export function useLogin() {
const count = async () => {
try {
const available = await getAvailable()
updateState({ available: available !== 0 })
updateState({ availableSet: true, available: available !== 0 })
}
catch(err) {
console.log(err);
@ -70,6 +75,11 @@ export function useLogin() {
// eslint-disable-next-line
}, [])
useEffect(() => {
const { strings, menuStyle } = settings.state;
updateState({ strings, menuStyle });
}, [settings.state]);
const access = async (token) => {
if (!state.busy) {
updateState({ busy: true });

View File

@ -9,6 +9,11 @@ export function useAccess() {
display: null,
scheme: null,
colors: {},
theme: null,
themes: [],
language: null,
languages: [],
strings: {},
});
const navigate = useNavigate();
@ -45,11 +50,18 @@ export function useAccess() {
useEffect(() => {
const { colors, display, scheme } = settings.state;
updateState({ colors, display, scheme });
const { theme, themes, strings, language, languages, colors, display, scheme } = settings.state;
updateState({ theme, themes, language, languages, strings, colors, display, scheme });
}, [settings.state]);
const actions = {};
const actions = {
setTheme: (theme) => {
settings.actions.setTheme(theme);
},
setLanguage: (language) => {
settings.actions.setLanguage(language);
},
};
return { state, actions };
}

View File

@ -42,7 +42,7 @@ export const Colors = {
export const LightTheme = {
splashArea: '#8fbea7',
baseArea: '#8fbea7',
frameArea: '#dddddd',
frameArea: '#e4e4e4',
iconArea: '#ffffff',
headerArea: '#f0f0f0',
footerArea: '#f0f0f0',

View File

@ -94,6 +94,19 @@ export const en = {
requestConnection: 'Request Connection',
cancelRequest: 'Cancel Request',
resyncContact: 'Resync Contact',
login: 'Login',
create: 'Create',
createAccount: 'Create Account',
accountLogin: 'Account Login',
toCreate: 'Accounts are created through a link generated from the admin dashboard.',
admin: 'Admin',
loginError: 'Login Error',
loginMessage: 'Please confirm your username and password.',
createError: 'Create Account Error',
createMessage: 'Please check with your administrator.',
adminError: 'Admin Access Error',
adminMessage: 'Please confirm your password.',
};
export const fr = {
@ -192,5 +205,18 @@ export const fr = {
requestConnection: 'Demander une Connexion',
cancelRequest: 'Annuler la Demande',
resyncContact: 'Resynchroniser le Contact',
login: 'Connecter',
create: 'Créer',
createAccount: 'Créer un Compte',
accountLogin: 'Connexion au Compte',
toCreate: 'Les comptes sont créés via un lien généré depuis le tableau de bord d\'administration.',
admin: 'Administrateur',
loginError: 'Erreur de connexion',
loginMessage: 'Veuillez confirmer votre nom d\'utilisateur et votre mot de passe.',
createError: 'Erreur de création de compte',
createMessage: 'Veuillez vérifier auprès de votre administrateur.',
adminError: 'Erreur d\'Accès',
adminMessage: 'Veuillez confirmer votre mot de passe',
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -24,6 +24,7 @@
body {
margin: 0;
background-color: #888888;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;

View File

@ -77,6 +77,7 @@ export function AccountAccess() {
<Select
defaultValue={null}
style={{ width: 128 }}
size="small"
value={state.theme}
onChange={actions.setTheme}
options={state.themes}
@ -87,6 +88,7 @@ export function AccountAccess() {
<Select
defaultValue={null}
style={{ width: 128 }}
size="small"
value={state.language}
onChange={actions.setLanguage}
options={state.languages}