|
|
|
import React, { Component } from 'react';
|
|
|
|
import Categories from './Categories';
|
|
|
|
import Category from './Category';
|
|
|
|
import Tool from './Tool';
|
|
|
|
import { Button, Confirm, Container, Dimmer, Form, Header, Icon, Item, Label, Loader, Menu, Message, Segment, Input } from 'semantic-ui-react';
|
|
|
|
import { Link, Route } from 'react-router-dom';
|
|
|
|
import io from 'socket.io-client';
|
|
|
|
|
|
|
|
// Move to env var
|
|
|
|
const SOCKET_SERVER_URL = 'https://tools-socket.protospace.ca';
|
|
|
|
const AUTH_SERVER_URL = 'https://tools-auth.protospace.ca';
|
|
|
|
|
|
|
|
class App extends Component {
|
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
|
|
|
|
this.socket = io(SOCKET_SERVER_URL);
|
|
|
|
|
|
|
|
try {
|
|
|
|
localStorage.setItem('test', 'test');
|
|
|
|
this.storage = true;
|
|
|
|
} catch (e) {
|
|
|
|
this.storage = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
let token = this.storage ? localStorage.getItem('token') : null;
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
login: {token: token, error: false, username: '', password: '', confirmLogout: false},
|
|
|
|
user: null,
|
|
|
|
toolData: null,
|
|
|
|
toolStatus: null,
|
|
|
|
connected: false,
|
|
|
|
network: true,
|
|
|
|
selectedCourses: null,
|
|
|
|
selectedNone: false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
log = (string) => {
|
|
|
|
if (this.state.connected) {
|
|
|
|
const username = this.state.login.username.replace('.', '') || this.state.user.username || 'unknown';
|
|
|
|
this.socket.emit('log', `${username} - Web client: ${string}`);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
noNetwork = () => {
|
|
|
|
this.setState({ network: false });
|
|
|
|
this.socket.disconnect();
|
|
|
|
}
|
|
|
|
|
|
|
|
getUser = () => {
|
|
|
|
fetch(AUTH_SERVER_URL + '/user/', {
|
|
|
|
headers: {'Authorization': 'Token ' + this.state.login.token},
|
|
|
|
})
|
|
|
|
.then(response => {
|
|
|
|
if (response.ok) {
|
|
|
|
response.json().then(data => this.setState({ user: data[0] }));
|
|
|
|
} else {
|
|
|
|
this.handleLogout();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(error => {
|
|
|
|
console.log(error)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
fetch(AUTH_SERVER_URL + '/tooldata/')
|
|
|
|
.then(response => {
|
|
|
|
if (response.ok) {
|
|
|
|
response.json().then(data => this.setState({
|
|
|
|
toolData: data,
|
|
|
|
selectedCourses: data.courses.map(x => false),
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
this.noNetwork();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(error => {
|
|
|
|
console.log(error)
|
|
|
|
this.noNetwork();
|
|
|
|
});
|
|
|
|
|
|
|
|
if (this.state.login.token) this.getUser();
|
|
|
|
|
|
|
|
this.socket.on('toolStatus', toolStatus =>
|
|
|
|
this.setState({ toolStatus: toolStatus })
|
|
|
|
);
|
|
|
|
|
|
|
|
this.socket.on('connect', () =>
|
|
|
|
this.setState({ connected: true })
|
|
|
|
);
|
|
|
|
|
|
|
|
this.socket.on('disconnect', () => {
|
|
|
|
this.setState({ toolStatus: null, connected: false });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this.socket.removeAllListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
requestInterlock = change => {
|
|
|
|
this.socket.emit('requestInterlock', {
|
|
|
|
username: this.state.user.username,
|
|
|
|
token: this.state.login.token,
|
|
|
|
change: change,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleChange = event => {
|
|
|
|
const name = event.target.name;
|
|
|
|
const value = event.target.value;
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
login: {...this.state.login, [name]: value, error: false}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleSubmit = () => {
|
|
|
|
const login = this.state.login;
|
|
|
|
fetch(AUTH_SERVER_URL + '/login/', {
|
|
|
|
method: 'POST',
|
|
|
|
headers: {'Content-Type': 'application/json; charset=utf-8'},
|
|
|
|
body: JSON.stringify({'username': login.username, 'password': login.password})
|
|
|
|
})
|
|
|
|
.then(response => {
|
|
|
|
if (response.ok) {
|
|
|
|
response.json().then(data => {
|
|
|
|
this.log('Good login');
|
|
|
|
this.setState({ login: {...login, ...data, error: false} });
|
|
|
|
if (this.storage) localStorage.setItem('token', data.token);
|
|
|
|
this.getUser();
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
response.json().then(data => {
|
|
|
|
this.log('Bad login');
|
|
|
|
this.setState({ login: {...login, error: data.error} });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(error => {
|
|
|
|
console.log(error)
|
|
|
|
this.setState({ network: false });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
confirmLogout = () => {
|
|
|
|
if (this.state.login.token) {
|
|
|
|
this.setState({
|
|
|
|
login: {...this.state.login, confirmLogout: true}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cancelLogout = () => {
|
|
|
|
this.setState({
|
|
|
|
login: {...this.state.login, confirmLogout: false}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
handleLogout = () => {
|
|
|
|
this.log('Logout');
|
|
|
|
this.setState({
|
|
|
|
login: {...this.state.login, token: null, confirmLogout: false}
|
|
|
|
});
|
|
|
|
localStorage.removeItem('token');
|
|
|
|
}
|
|
|
|
|
|
|
|
handleRefresh = () => {
|
|
|
|
window.location.reload();
|
|
|
|
}
|
|
|
|
|
|
|
|
toggleCourse = (i, data) => {
|
|
|
|
let selectedCourses = this.state.selectedCourses;
|
|
|
|
selectedCourses[i] = data.checked;
|
|
|
|
this.setState({ selectedCourses: selectedCourses, selectedNone: false });
|
|
|
|
}
|
|
|
|
|
|
|
|
selectNone = (data) => {
|
|
|
|
let selectedCourses = this.state.selectedCourses.map(x => false);
|
|
|
|
this.setState({ selectedCourses: selectedCourses, selectedNone: data.checked });
|
|
|
|
}
|
|
|
|
|
|
|
|
submitCourses = () => {
|
|
|
|
const toolData = this.state.toolData;
|
|
|
|
const selectedCourses = this.state.selectedCourses;
|
|
|
|
const courseList = toolData.courses.map(x => x.slug).filter((x, i) => selectedCourses[i]);
|
|
|
|
|
|
|
|
fetch(AUTH_SERVER_URL + '/select-courses/', {
|
|
|
|
method: 'PUT',
|
|
|
|
headers: {'Authorization': 'Token ' + this.state.login.token, 'Content-Type': 'application/json; charset=utf-8'},
|
|
|
|
body: JSON.stringify({ 'courses': courseList })
|
|
|
|
})
|
|
|
|
.then(response => {
|
|
|
|
if (response.ok) {
|
|
|
|
this.log('Selected courses ' + courseList.join(', '));
|
|
|
|
this.getUser();
|
|
|
|
} else {
|
|
|
|
this.noNetwork();
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(error => {
|
|
|
|
console.log(error)
|
|
|
|
this.handleLogout();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
|
|
|
const login = this.state.login;
|
|
|
|
const user = this.state.user;
|
|
|
|
const toolData = this.state.toolData;
|
|
|
|
const toolStatus = this.state.toolStatus;
|
|
|
|
const connected = this.state.connected;
|
|
|
|
const network = this.state.network;
|
|
|
|
const selectedCourses = this.state.selectedCourses;
|
|
|
|
const selectedNone = this.state.selectedNone;
|
|
|
|
|
|
|
|
console.log(this.state);
|
|
|
|
|
|
|
|
return (
|
|
|
|
network ?
|
|
|
|
<div>
|
|
|
|
<Menu fixed='top' borderless inverted>
|
|
|
|
<Menu.Item as={Link} to='/'>
|
|
|
|
<Icon name='home' size='big' />
|
|
|
|
</Menu.Item>
|
|
|
|
<Menu.Item>
|
|
|
|
<Icon name='circle' color={connected && toolStatus ? 'green' : 'red'} />
|
|
|
|
{connected && toolStatus ? 'Connected' : 'Disconnected'}
|
|
|
|
</Menu.Item>
|
|
|
|
<Menu.Item onClick={this.confirmLogout} position='right'>
|
|
|
|
<Icon name='user close' size='big' />
|
|
|
|
</Menu.Item>
|
|
|
|
</Menu>
|
|
|
|
<div style={{height: '70px', display: 'inline-block'}} />
|
|
|
|
|
|
|
|
<Confirm
|
|
|
|
open={login.confirmLogout}
|
|
|
|
header='Logout'
|
|
|
|
onCancel={this.cancelLogout}
|
|
|
|
onConfirm={this.handleLogout}
|
|
|
|
/>
|
|
|
|
|
|
|
|
{login.token ?
|
|
|
|
<div>
|
|
|
|
{toolData && user ?
|
|
|
|
<Container>
|
|
|
|
{user.profile.selected_courses ?
|
|
|
|
<div>
|
|
|
|
<Route exact path='/' render={props =>
|
|
|
|
<Categories {...props} data={toolData} user={user} />
|
|
|
|
} />
|
|
|
|
|
|
|
|
<Route exact path='/:category' render={props =>
|
|
|
|
<Category {...props} data={toolData} user={user} />
|
|
|
|
} />
|
|
|
|
|
|
|
|
<Route exact path='/:category/:slug' render={props =>
|
|
|
|
<Tool {...props}
|
|
|
|
data={toolData}
|
|
|
|
user={user}
|
|
|
|
toolStatus={toolStatus}
|
|
|
|
requestInterlock={this.requestInterlock}
|
|
|
|
/>
|
|
|
|
} />
|
|
|
|
</div>
|
|
|
|
:
|
|
|
|
<div>
|
|
|
|
<Message visible warning header='Please select which courses you’ve taken:' />
|
|
|
|
<Form size='massive' onSubmit={this.submitCourses}>
|
|
|
|
{toolData.courses.map((x, i) =>
|
|
|
|
<Form.Checkbox
|
|
|
|
checked={selectedCourses[i]}
|
|
|
|
onChange={(e, data) => this.toggleCourse(i, data)}
|
|
|
|
label={x.name}
|
|
|
|
key={i}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
<Form.Checkbox
|
|
|
|
checked={selectedNone}
|
|
|
|
onChange={(e, data) => this.selectNone(data)}
|
|
|
|
label='None'
|
|
|
|
/>
|
|
|
|
<br />
|
|
|
|
<Button type='submit' disabled={!selectedCourses.some(x => x) && !selectedNone}>Submit</Button>
|
|
|
|
</Form>
|
|
|
|
<br />Note: your selection will be reviewed by a lockout admin
|
|
|
|
</div>
|
|
|
|
}
|
|
|
|
</Container>
|
|
|
|
:
|
|
|
|
<Dimmer active>
|
|
|
|
<Loader>Loading</Loader>
|
|
|
|
</Dimmer>
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
:
|
|
|
|
<Container text>
|
|
|
|
<Form size='large' onSubmit={this.handleSubmit}>
|
|
|
|
<Message visible warning header='Please log in to access the tools!' />
|
|
|
|
<Form.Field>
|
|
|
|
<label>Protospace Username</label>
|
|
|
|
<input name='username' placeholder='Username' value={login.username} onChange={this.handleChange} />
|
|
|
|
</Form.Field>
|
|
|
|
<Form.Field>
|
|
|
|
<label>Password</label>
|
|
|
|
<input name='password' type='password' value={login.password} onChange={this.handleChange} />
|
|
|
|
</Form.Field>
|
|
|
|
<Button type='submit'>Submit</Button>
|
|
|
|
{login.error && <Label basic color='red' pointing='left'>
|
|
|
|
{login.error}
|
|
|
|
</Label>}
|
|
|
|
</Form>
|
|
|
|
</Container>
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
:
|
|
|
|
<Dimmer active>
|
|
|
|
<Header inverted icon>
|
|
|
|
<Icon color='red' name='wifi' />
|
|
|
|
Please connect to the "Protospace" wifi
|
|
|
|
<br />
|
|
|
|
<br />
|
|
|
|
Disconnect from any VPNs
|
|
|
|
</Header>
|
|
|
|
<br />
|
|
|
|
<Button size='big' inverted icon labelPosition='left' onClick={this.handleRefresh}>
|
|
|
|
<Icon name='refresh' />
|
|
|
|
Refresh
|
|
|
|
</Button>
|
|
|
|
</Dimmer>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export default App;
|