Skip to content

Commit

Permalink
OAB-18 create admin panel
Browse files Browse the repository at this point in the history
  • Loading branch information
polakowski committed Jul 17, 2018
1 parent 4c99ab5 commit f5396ea
Show file tree
Hide file tree
Showing 17 changed files with 263 additions and 17 deletions.
2 changes: 0 additions & 2 deletions .env

This file was deleted.

3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SLACK_WEBHOOK=https://hooks.slack.com/123/456/789
SLACK_LOG_WEBHOOK=https://hooks.slack.com/123/456/789
RASPBERRY=0|1
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ images/*

.DS_Store

.env
14 changes: 10 additions & 4 deletions client/components/Game.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,26 @@ import socketIOClient from 'socket.io-client'
import { logResult, resultResponse } from '../modules/result'
import { ToastContainer, toast } from 'react-toastify'

import withSocket from '@wrappers/withSocket'
import AdminPanel from '@components/admin-panel/AdminPanel'

const ROLLERS = ['left', 'center', 'right']

export default class Game extends React.Component {
class Game extends React.Component {
constructor(props) {
super(props)

this.state = {}
this.socket = socketIOClient('http://localhost:3000')

ROLLERS.forEach(roller => {
this[roller] = React.createRef()
})
}

componentDidMount() {
this.socket.on('SPIN_REQUEST', forcedSpinTo => this._spinMachine(forcedSpinTo))
this.socket.on('NOTIFY', (type, message) => toast[type](message))
const { socket } = this.props;
socket.on('SPIN_REQUEST', forcedSpinTo => this._spinMachine(forcedSpinTo))
socket.on('NOTIFY', (type, message) => toast[type](message))
}

_spinMachine(forcedSpinTo) {
Expand Down Expand Up @@ -51,9 +54,12 @@ export default class Game extends React.Component {
return (
<div className="rollers">
<ToastContainer autoClose={5000} position={toast.POSITION.TOP_CENTER} />
<AdminPanel />
<div className="overlay" />
{this._createRollers()}
</div>
)
}
}

export default withSocket(Game);
28 changes: 28 additions & 0 deletions client/components/admin-panel/ActionButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'

import withSocket from '@wrappers/withSocket'

class ActionButton extends Component {
_onClick(message) {
this.props.socket.emit(message)
}

render() {
const { text, socketAction } = this.props

return (
<a href='#' onClick={() => this._onClick(socketAction)}>{text}</a>
)
}
}

ActionButton.propTypes = {
text: PropTypes.string.isRequired,
socketAction: PropTypes.string.isRequired,
socket: PropTypes.shape({
emit: PropTypes.func.isRequired
}).isRequired
}

export default withSocket(ActionButton)
53 changes: 53 additions & 0 deletions client/components/admin-panel/AdminPanel.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'

import ActionButton from '@components/admin-panel/ActionButton'
import withSocket from '@wrappers/withSocket'
import '@styles/admin_panel'

class AdminPanel extends Component {
constructor(props) {
super(props)
this.state = { active: false }
this._setActive = this._setActive.bind(this)
}

componentDidMount() {
this.props.socket.on('SET_ADMIN_PANEL_ACTIVE', this._setActive)
}

_setActive(value) {
this.setState({ active: value })
}

_getWrapperClass(active) {
const classes = ['admin-panel']
if (active) { classes.push('active') }
return classes.join(' ')
}

render() {
const { active } = this.state

return (
<div className={this._getWrapperClass(active)}>
<div className='admin-panel__actions'>
<ActionButton text='Reboot' socketAction='@admin/REBOOT' />
<ActionButton text='Shutdown' socketAction='@admin/SHUTDOWN' />
<ActionButton text='Trigger servo' socketAction='@admin/TRIGGER_SERVO' />
<a className='close' onClick={() => this._setActive(false)}>
Close
</a>
</div>
</div>
)
}
}

AdminPanel.propTypes = {
socket: PropTypes.shape({
on: PropTypes.func.isRequired
}).isRequired
}

export default withSocket(AdminPanel)
43 changes: 43 additions & 0 deletions client/styles/admin_panel.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.admin-panel {
position: absolute;
width: 100%;
height: 100%;
z-index: 10;
padding: 15px;
box-sizing: border-box;
will-change: top;
transition: top 0.6s cubic-bezier(0.4, 0.2, 0, 1);
top: -100%;
background: #30373f;

&.active {
top: 0%;
}

&__actions {
margin: 0 -10px 20px;
display: flex;

a {
font-family: Helvetica, sans-serif;
margin: 0 10px 20px;
padding: 0 20px;
background: #cccccc;
border: 1px solid #b1b2b3;
color: #6d6972;
border-radius: 3px;
font-size: 0.8em;
text-transform: uppercase;
line-height: 2.25em;
font-weight: bold;
}

a, a:hover, a:active, a:focus, a:visited {
text-decoration: none;
}

a.close {
margin-left: auto;
}
}
}
1 change: 1 addition & 0 deletions client/styles/game.scss
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ body {
display: flex;
align-items: center;
justify-content: center;
position: relative;
}

.roller-wrapper {
Expand Down
26 changes: 26 additions & 0 deletions client/wrappers/withSocket.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { Component } from 'react'
import socketIOClient from 'socket.io-client'

let socket

export default function withSocket(WrappedComponent) {
class WithSocket extends Component {
constructor(props) {
super(props)

if (!socket) {
socket = socketIOClient('http://localhost:3000')
}
}

render() {
return (
<WrappedComponent socket={socket} {...this.props} />
)
}
}

WithSocket.displayName = `WithSocket(${WrappedComponent.name})`

return WithSocket;
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"react-toastify": "^4.1.0",
"redis": "^2.8.0",
"serialport": "^6.2.1",
"shelljs": "^0.8.2",
"slack-webhook": "^1.0.0",
"socket.io": "^2.1.1",
"socket.io-client": "^2.1.1"
Expand Down
23 changes: 14 additions & 9 deletions src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import http from 'http'
import reader, { parseData } from './rfid'
import redis from 'redis'
import moment from 'moment'
import servo from './servo'
import sendPhoto from './queue'
import Slack from './slack'
import socketIO from 'socket.io'
Expand All @@ -27,12 +26,13 @@ let readyForSpin = false
let socketClient = null
let user = null

import handleAdminPanelBroadcasts from './serverModules/handleAdminPanelBroadcasts'
import handleSpinResult from './serverModules/handleSpinResult'

app.use(webpackMiddleware(webpack(webpackConfig)))
server.listen(3000)

function _rollRequest(userId) {
user = getUser(userId)

if (user === undefined) {
slack.log(userId)
socketClient.emit('NOTIFY', 'error', 'please go to @czana')
Expand Down Expand Up @@ -82,15 +82,20 @@ io.on('connection', client => {

client.on('SPIN_ENDED', result => {
readyForSpin = true

if (result.win) {
if (result.cashPrize) servo.move()
slack.post(user.mention, result.icon, result.cashPrize ? '$$$' : '2 Kudos!')
}
handleSpinResult(result, user);
})

handleAdminPanelBroadcasts(client);
})

reader.on('data', data => {
const userId = parseData(data)
_rollRequest(userId)
user = getUser(userId)

if (user && user.admin) {
socketClient.emit('SET_ADMIN_PANEL_ACTIVE', true);
} else {
socketClient.emit('SET_ADMIN_PANEL_ACTIVE', false);
_rollRequest(userId)
}
})
32 changes: 32 additions & 0 deletions src/serverModules/handleAdminPanelBroadcasts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import shell from 'shelljs'

import raspberryOnly from '../utils/raspberryOnly'

let servo

raspberryOnly(() => {
servo = require('../servo')['default']
})

export default (client) => {
client.on('@admin/REBOOT', () => {
raspberryOnly(() => {
shell.exec('sudo reboot')
client.emit('SET_ADMIN_PANEL_ACTIVE', false);
})
})

client.on('@admin/SHUTDOWN', () => {
raspberryOnly(() => {
shell.exec('sudo shutdown -P now')
client.emit('SET_ADMIN_PANEL_ACTIVE', false);
})
})

client.on('@admin/TRIGGER_SERVO', () => {
raspberryOnly(() => {
servo.move()
client.emit('SET_ADMIN_PANEL_ACTIVE', false);
})
})
}
16 changes: 16 additions & 0 deletions src/serverModules/handleSpinResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import raspberryOnly from '../utils/raspberryOnly'

let servo

raspberryOnly(() => {
servo = require('../servo')
})

export default (result, user) => {
if (result.win) {
raspberryOnly(() => {
if (result.cashPrize) { servo.move() }
})
slack.post(user.mention, result.icon, result.cashPrize ? '$$$' : '2 Kudos!')
}
}
4 changes: 3 additions & 1 deletion src/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const USERS = {
5501512322897: {
mention: 'czana',
email: 't.czana@selleo.com',
admin: true
},
4402038631408: {
mention: 'bart',
Expand All @@ -13,7 +14,8 @@ const USERS = {
},
4402038648119: {
mention: 'm.polakowski',
email: 'm.polakowski@selleo.com'
email: 'm.polakowski@selleo.com',
admin: true
},
5501557468263: {
mention: 'apawlicka',
Expand Down
7 changes: 7 additions & 0 deletions src/utils/raspberryOnly.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require('dotenv').config()

export default (theFunction) => {
if (process.env.RASPBERRY === '1') {
theFunction()
}
}
8 changes: 7 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import HtmlWebpackPlugin from 'html-webpack-plugin'
import path from 'path'

export default {
mode: 'development',
entry: './client/index.jsx',
resolve: {
extensions: ['.js', '.jsx', '.scss']
extensions: ['.js', '.jsx', '.scss'],
alias: {
'@components': path.resolve(__dirname, 'client/components'),
'@styles': path.resolve(__dirname, 'client/styles'),
'@wrappers': path.resolve(__dirname, 'client/wrappers')
}
},
output: {
path: '/',
Expand Down
Loading

0 comments on commit f5396ea

Please sign in to comment.