Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Login #71

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ PORT=3001
REACT_APP_QUEUE_API_PREFIX=qs
REACT_APP_PREVIEW_API_PREFIX=preview
REACT_APP_PREVIEW_SERVER=http://localhost:8000
REACT_APP_FACILITY_API_PREFIX=info
REACT_APP_USER_API_PREFIX=http://localhost:9000
REACT_APP_FACILITY_API_PREFIX=info
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,16 @@
"@types/react-dom": "latest",
"axios": "^0.20.0",
"http-proxy-middleware": "^1.0.6",
"jwt-decode": "^3.1.2",
"react": "latest",
"react-dom": "latest",
"react-redux": "^7.2.1",
"react-router-dom": "latest",
"react-scripts": "latest",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"typescript": "latest"
"typescript": "latest",
"react-bootstrap": "latest"
},
"scripts": {
"start": "react-scripts start",
Expand Down
83 changes: 83 additions & 0 deletions src/LoginComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React from 'react';
import { Button, Box, TextField, ListItem, List, Typography, IconButton, InputAdornment } from '@material-ui/core';
import { Visibility, VisibilityOff } from '@material-ui/icons';

type IProps = {
loginActionCreator: (email: string, password: string) => void;
};

interface IState {
email: string,
password: string,
error: any,
showPassword: boolean
};

export class LoginComponent extends React.Component<IProps, IState>{

constructor(props: IProps) {
super(props);
this.state = {
email: "",
password: "",
error: "",
showPassword: false,
};
}

showPassword(){
this.setState({ showPassword: !this.state.showPassword });
};

handleMouseDownPassword(event: React.MouseEvent<HTMLButtonElement>){
event.preventDefault();
};

login(){
if (!(this.state.email.length > 0)) {
throw new Error('Email was not provided');
}
if (!(this.state.password.length > 0)) {
throw new Error('Password was not provided');
}
this.props.loginActionCreator(this.state.email, this.state.password);
};

render(){
return (
<Box>
<ListItem style={{justifyContent:'center'}}>
<Typography variant="h5">
Login
</Typography>
</ListItem>
<Box border={1}>
<List>
<ListItem style={{justifyContent:'center'}}>
<TextField style = {{width: '50%'}} label="email" variant="outlined"
onChange={(e) => this.setState({email: e.currentTarget.value})} />
</ListItem>
<ListItem style={{justifyContent:'center'}}>
<TextField type={this.state.showPassword ? 'text' : 'password'} style = {{width: '50%'}} label="password" variant="outlined" onChange={(p) => this.setState({password: p.currentTarget.value})}
InputProps={{
endAdornment: <InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={this.showPassword.bind(this)}
onMouseDown={this.handleMouseDownPassword.bind(this)}>
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>,
}}/>
</ListItem>
<ListItem style={{justifyContent:'center'}}>
<Button variant="contained" onClick={this.login.bind(this)}>
Log In
</Button>
</ListItem>
</List>
</Box>
</Box>
);
}
};
65 changes: 65 additions & 0 deletions src/LoginPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from 'react';
import { connect } from 'react-redux';
import Container from '@material-ui/core/Container';
import Box from '@material-ui/core/Box';
import { IApplicationState } from './store';
import { getUser, loginActionCreator, registerActionCreator } from './useractions'
import { IUser } from './facility';
import { RouteComponentProps } from "react-router-dom";
import { LoginComponent } from './LoginComponent';
import { RegisterComponent } from './RegisterComponent';

interface IProps extends RouteComponentProps {
getUser: typeof getUser;
loginActionCreator: typeof loginActionCreator;
registerActionCreator: typeof registerActionCreator;
loading: boolean;
user: IUser;
}

interface IState {
userId: number;
}
class LoginPage extends React.Component<IProps, IState> {
public constructor(props: IProps) {
super(props);
}
render() {
return (
<Container maxWidth="sm">
<Box my={4}>
<LoginComponent loginActionCreator={this.props.loginActionCreator}/>
<Box width="80vw" height="2vh"></Box>
<RegisterComponent registerActionCreator={this.props.registerActionCreator}/>
</Box>
</Container>
)
}

componentDidMount() {
this.props.getUser(this.props.user.username);
}
}
const mapStateToProps = (store: IApplicationState) => {
return {
loading: store.user.userLoading,
user: store.user.user,
token: store.user.token,
permissions: store.user.permissions,
};
};

const mapDispatchToProps = (dispatch: any) => {
return {
getUser: (username: string) => dispatch(getUser(username)),
loginActionCreator: (email: string, password: string) => dispatch(loginActionCreator(email, password)),
registerActionCreator: (firstName: string, lastName: string,
email: string, password: string) => dispatch(registerActionCreator(firstName, lastName,
email, password))
};
};

export default connect(
mapStateToProps,
mapDispatchToProps
)(LoginPage);
130 changes: 130 additions & 0 deletions src/RegisterComponent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { Box, ListItem, Typography, List, TextField, Button, InputAdornment, IconButton } from '@material-ui/core';
import { Visibility, VisibilityOff } from '@material-ui/icons';
import React from 'react';
import { registerActionCreator } from './useractions'

type IProps = {
registerActionCreator: typeof registerActionCreator
};

interface IState {
firstName: string,
lastName: string,
email: string,
password: string,
passwordConfirmation: string,
error: string,
showPassword: boolean,
};

export class RegisterComponent extends React.Component<IProps, IState>{

constructor(props: IProps) {
super(props);
this.state = {
firstName: "",
lastName: "",
email: "",
password: "",
passwordConfirmation: "",
error: "",
showPassword: false,
};
}

showPassword(){
this.setState({ showPassword: !this.state.showPassword });
};

handleMouseDownPassword(event: React.MouseEvent<HTMLButtonElement>){
event.preventDefault();
};

register(){
// Assert firstName, lastName and phone not empty
if (!((this.state.firstName.length) > 0)) {
throw new Error('First Name was not provided');
}
// Assert firstName, lastName and phone not empty
if (!((this.state.lastName.length) > 0)) {
throw new Error('Last Name was not provided');
}
// Assert email is not empty
if (!(this.state.email.length > 0)) {
throw new Error('Email was not provided');
}
// Assert password is not empty
if (!(this.state.password.length > 0)) {
throw new Error('Password was not provided');
}
// Assert password confirmation is not empty
if (!(this.state.passwordConfirmation.length > 0)) {
throw new Error('Password confirmation was not provided');
}
// Assert email or password or password confirmation is not empty
if (this.state.password !== this.state.passwordConfirmation) {
throw new Error('Passwords do not match')
}

this.props.registerActionCreator(this.state.firstName, this.state.lastName, this.state.email, this.state.password)
}

render(){
return (
<Box>
<ListItem style={{justifyContent:'center'}}>
<Typography variant="h5">
Register
</Typography>
</ListItem>
<Box border={1}>
<List>
<ListItem style={{justifyContent:'center'}}>
<TextField style = {{width: '50%'}} label="first name" variant="outlined"
onChange={(e) => this.setState({firstName: e.currentTarget.value})} />
</ListItem>
<ListItem style={{justifyContent:'center'}}>
<TextField style = {{width: '50%'}} label="last name" variant="outlined"
onChange={(p) => this.setState({lastName: p.currentTarget.value})} />
</ListItem>
<ListItem style={{justifyContent:'center'}}>
<TextField style = {{width: '50%'}} label="email" variant="outlined"
onChange={(p) => this.setState({email: p.currentTarget.value})} />
</ListItem>
<ListItem style={{justifyContent:'center'}}>
<TextField type={this.state.showPassword ? 'text' : 'password'} style = {{width: '50%'}} label="password" variant="outlined" onChange={(p) => this.setState({password: p.currentTarget.value})}
InputProps={{
endAdornment: <InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={this.showPassword.bind(this)}
onMouseDown={this.handleMouseDownPassword.bind(this)}>
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>,
}}/>
</ListItem>
<ListItem style={{justifyContent:'center'}}>
<TextField type={this.state.showPassword ? 'text' : 'password'} style = {{width: '50%'}} label="confirm password" variant="outlined" onChange={(p) => this.setState({passwordConfirmation: p.currentTarget.value})}
InputProps={{
endAdornment: <InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={this.showPassword.bind(this)}
onMouseDown={this.handleMouseDownPassword.bind(this)}>
{this.state.showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>,
}}/>
</ListItem>
<ListItem style={{justifyContent:'center'}}>
<Button variant="contained" onClick={this.register.bind(this)}>
Register
</Button>
</ListItem>
</List>
</Box>
</Box>
);
}
};
28 changes: 21 additions & 7 deletions src/facility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ var axiosInstance = axios.create({

export enum UserActionTypes {
GETINFO = "USER/GETACTION",
LOADING = "USER/LOADING"
LOADING = "USER/LOADING",
LOGIN = "USER/LOGIN",
REGISTER = "USER/REGISTER"
}

export interface IUser {
Expand Down Expand Up @@ -35,20 +37,32 @@ export interface IExperiment {
}


export interface IUserGetAction {
export interface IUserLoginAction {
type: UserActionTypes.GETINFO,
user: IUser
}
}

export interface IUserRegisterAction {
type: UserActionTypes.GETINFO,
user: IUser
}

export interface IUserLoadingAction {
type: UserActionTypes.LOADING
}
export interface IUserLoadingAction {
type: UserActionTypes.LOADING
}

export interface IUserGetAction {
type: UserActionTypes.GETINFO,
user: IUser
}

export type UserInfo =
| IUser | IProposal | IExperiment

export interface IUserState {
readonly user: IUser;
readonly token: any;
readonly permissions: string;
readonly userLoading: boolean;
}

Expand All @@ -70,4 +84,4 @@ export const getProposalsForUser = async(userid: number): Promise<IProposal[]> =
export const getExperimentsForProposal = async(proposalid: number): Promise<IExperiment[]> => {
const res = await axiosInstance.get(`/experiments/proposal/${proposalid}`);
return res.data;
}
}
Loading