2019-02-24 21:27:24 +00:00
|
|
|
'use strict'
|
|
|
|
|
|
|
|
import React from 'react'
|
|
|
|
import ReactDOM from 'react-dom'
|
|
|
|
import { BrowserRouter as Router, Route, NavLink, Switch, Redirect } from 'react-router-dom'
|
|
|
|
import Progress from './components/Progress'
|
|
|
|
import appReducer from './reducers'
|
|
|
|
import adminReducer from './reducers/admin'
|
2019-02-26 03:29:18 +00:00
|
|
|
import { fetchAdminData, patchUser, patchPublisher } from './actions/admin'
|
2019-03-19 20:24:57 +00:00
|
|
|
import { toggleMenu } from './actions'
|
2019-02-24 21:27:24 +00:00
|
|
|
import Util from './lib/Util'
|
2019-02-26 23:55:51 +00:00
|
|
|
import Icon from './components/Icon'
|
2019-03-19 20:24:57 +00:00
|
|
|
import IconButton from './components/IconButton'
|
2019-02-24 21:27:24 +00:00
|
|
|
|
|
|
|
import '../styles/admin.scss'
|
2019-02-26 23:55:51 +00:00
|
|
|
import './containers/listitem.scss'
|
2019-02-24 21:27:24 +00:00
|
|
|
|
|
|
|
const reducer = Util.combineReducers(appReducer, adminReducer)
|
|
|
|
|
|
|
|
class App extends React.Component {
|
|
|
|
constructor () {
|
|
|
|
super()
|
|
|
|
this.state = {
|
|
|
|
error: '',
|
|
|
|
user: {
|
|
|
|
id: '',
|
|
|
|
email: '',
|
|
|
|
password: '',
|
|
|
|
currentPassword: ''
|
|
|
|
},
|
|
|
|
users: [],
|
|
|
|
publishers: [],
|
2019-03-19 20:24:57 +00:00
|
|
|
working: false,
|
|
|
|
navMenu: false
|
2019-02-24 21:27:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.dispatch = this.dispatch.bind(this)
|
|
|
|
this.getRegisteredUsers = this.getRegisteredUsers.bind(this)
|
|
|
|
this.getRegisteredPublishers = this.getRegisteredPublishers.bind(this)
|
2019-02-26 23:55:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
this.state.user = {
|
|
|
|
...this.state.user,
|
|
|
|
'created_at': 1551151466802,
|
|
|
|
'updated_at': 1551151520134,
|
|
|
|
'id': 1,
|
|
|
|
'email': 'admin@tkluge.net',
|
|
|
|
'admin': true
|
|
|
|
}
|
|
|
|
*/
|
2019-02-24 21:27:24 +00:00
|
|
|
}
|
|
|
|
dispatch (action) {
|
|
|
|
if (!action) throw new Error('dispatch: missing action')
|
|
|
|
if (action instanceof Function) {
|
|
|
|
action(this.dispatch, () => this.state)
|
|
|
|
} else {
|
|
|
|
const changes = reducer(this.state, action)
|
|
|
|
if (!changes || !Object.keys(changes).length) return
|
|
|
|
this.setState({
|
|
|
|
...changes
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
componentDidMount () {
|
|
|
|
this.dispatch(fetchAdminData())
|
|
|
|
}
|
|
|
|
getRegisteredUsers () {
|
|
|
|
return this.state.users.map(user => {
|
|
|
|
return (
|
2019-03-19 20:24:57 +00:00
|
|
|
<li className='uri-list-item cols flex-container' key={`is-admin-${user.id}`}>
|
2019-02-24 21:27:24 +00:00
|
|
|
<span className='flex'>{user.email}</span>
|
|
|
|
<span className='flex'>
|
2019-03-19 20:24:57 +00:00
|
|
|
<label htmlFor={`is-admin-${user.id}`} className='cb-label'>Admin?</label>
|
2019-02-26 03:29:18 +00:00
|
|
|
<input className='checkbox' type='checkbox' checked={user.admin} onChange={() => this.dispatch(patchUser({ id: user.id, admin: !user.admin }))} id={`is-admin-${user.id}`} />
|
2019-02-26 23:55:51 +00:00
|
|
|
<label htmlFor={`is-admin-${user.id}`} />
|
2019-02-24 21:27:24 +00:00
|
|
|
</span>
|
|
|
|
<div className='stack flex flex-container flex-vertical'>
|
2019-02-26 23:55:51 +00:00
|
|
|
<span className='flex'><span className='key'>Created at:</span><span className='value'>{new Date(user.created_at).toLocaleString()}</span></span>
|
|
|
|
<span className='flex'><span className='key'>Updated at:</span><span className='value'>{new Date(user.updated_at).toLocaleString()}</span></span>
|
2019-02-24 21:27:24 +00:00
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
getRegisteredPublishers () {
|
|
|
|
return this.state.publishers.map(pub => {
|
|
|
|
return (
|
2019-02-26 23:55:51 +00:00
|
|
|
<li className='uri-list-item flex-container flex-vertical' key={`is-whitelisted-${pub.id}`}>
|
|
|
|
<header><h3>{pub.name}</h3></header>
|
2019-03-19 20:24:57 +00:00
|
|
|
<div className='cols flex flex-container'>
|
2019-02-26 23:55:51 +00:00
|
|
|
<div className='flex flex-container flex-vertical key-value'>
|
|
|
|
<span className='flex'><span className='key'>Owner:</span><span className='value'>{pub.user.email}</span></span>
|
|
|
|
<span className='flex'><span className='key'>App ID:</span><span className='value'>{pub.appid}</span></span>
|
|
|
|
<span className='flex contains-icon'>
|
|
|
|
<span className='key'>App domain:</span>
|
|
|
|
<span className='value'>{pub.url}</span>
|
|
|
|
<Icon icon={pub.verified ? 'shield-check' : 'alert-circle'} className={pub.verified ? 'verified' : 'unverified'} />
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
<span className='flex'>
|
|
|
|
<label htmlFor={`is-whitelisted-${pub.id}`} className='cb-label'>Whitelisted?</label>
|
|
|
|
<input className='checkbox' type='checkbox' checked={pub.whitelisted} onChange={() => this.dispatch(patchPublisher({ id: pub.id, whitelisted: !pub.whitelisted }))} id={`is-whitelisted-${pub.id}`} />
|
|
|
|
<label htmlFor={`is-whitelisted-${pub.id}`} />
|
|
|
|
</span>
|
|
|
|
<div className='stack flex flex-container flex-vertical'>
|
|
|
|
<span className='flex'><span className='key'>Created at:</span><span className='value'>{new Date(pub.created_at).toLocaleString()}</span></span>
|
|
|
|
<span className='flex'><span className='key'>Updated at:</span><span className='value'>{new Date(pub.updated_at).toLocaleString()}</span></span>
|
|
|
|
</div>
|
2019-02-24 21:27:24 +00:00
|
|
|
</div>
|
|
|
|
</li>
|
|
|
|
)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
render () {
|
|
|
|
return (
|
|
|
|
<Router basename='/admin'>
|
2019-03-19 20:24:57 +00:00
|
|
|
<div className={'root-container flex-container admin-container two-panels' + (this.state.navMenu ? ' nav-active' : '')}>
|
2019-02-24 21:27:24 +00:00
|
|
|
<aside className='nav nav-left'>
|
|
|
|
<header>
|
2019-03-19 20:24:57 +00:00
|
|
|
<h1 className='flex-container'><IconButton icon='menu' className='menu-small' onClick={() => this.dispatch(toggleMenu())} /><span className='flex'>RoE Admin</span></h1>
|
2019-02-24 21:27:24 +00:00
|
|
|
<h2 className='flex-container'>
|
|
|
|
<span className='flex'>{this.state.user.email}</span>
|
|
|
|
<a href='/logout'>Log out</a>
|
|
|
|
</h2>
|
|
|
|
</header>
|
|
|
|
<ul>
|
2019-03-19 20:24:57 +00:00
|
|
|
<li className='flex-container'><NavLink to='/users'><Icon icon='account' /><span className='flex'>Users</span></NavLink></li>
|
|
|
|
<li className='flex-container'><NavLink to='/publishers'><Icon icon='transfer-right' /><span className='flex'>Publishers</span></NavLink></li>
|
|
|
|
<li className='flex-container'><a href='/keys'><Icon icon='exit' /><span className='flex'>Exit admin</span></a></li>
|
2019-02-24 21:27:24 +00:00
|
|
|
</ul>
|
|
|
|
</aside>
|
|
|
|
<section className={'content flex' + (this.state.working ? ' working' : '')}>
|
|
|
|
<Progress bound />
|
|
|
|
{this.state.error && <div className='error-box'>{this.state.error}</div>}
|
|
|
|
<Switch>
|
|
|
|
<Route path='/users' exact children={props => (
|
|
|
|
<div>
|
|
|
|
<header className='flex-container'>
|
|
|
|
<div className='flex'>
|
|
|
|
<h1>Site users</h1>
|
|
|
|
<h2>Registered users on RoE</h2>
|
|
|
|
</div>
|
|
|
|
</header>
|
|
|
|
<ul className='list'>
|
|
|
|
{this.getRegisteredUsers()}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
)} />
|
|
|
|
|
|
|
|
<Route path='/publishers' exact children={props => (
|
|
|
|
<div>
|
|
|
|
<header className='flex-container'>
|
|
|
|
<div className='flex'>
|
|
|
|
<h1>Publishers</h1>
|
|
|
|
<h2>Whitelist sites who can publish books</h2>
|
|
|
|
</div>
|
|
|
|
</header>
|
|
|
|
<ul className='list'>
|
|
|
|
{this.getRegisteredPublishers()}
|
|
|
|
</ul>
|
|
|
|
</div>
|
|
|
|
)} />
|
|
|
|
|
|
|
|
<Route path='/' render={() => <Redirect to='/users' />} />
|
|
|
|
</Switch>
|
|
|
|
</section>
|
|
|
|
</div>
|
|
|
|
</Router>
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ReactDOM.render(<App />, document.getElementById('root'))
|