You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
338 lines
8.9 KiB
338 lines
8.9 KiB
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;
|
|
|