Added team page
This commit is contained in:
5
server/package-lock.json
generated
5
server/package-lock.json
generated
@@ -223,6 +223,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/auto-bind2/-/auto-bind2-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/auto-bind2/-/auto-bind2-1.0.3.tgz",
|
||||||
"integrity": "sha512-+br9nya9M8ayHjai7m9rdpRxuEr8xcYRDrIp7HybNe0ixUHbc1kDiWXKMb0ldsfWb9Zi+SqJ9JfjW8nTkYD0QQ=="
|
"integrity": "sha512-+br9nya9M8ayHjai7m9rdpRxuEr8xcYRDrIp7HybNe0ixUHbc1kDiWXKMb0ldsfWb9Zi+SqJ9JfjW8nTkYD0QQ=="
|
||||||
},
|
},
|
||||||
|
"autobind-decorator": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/autobind-decorator/-/autobind-decorator-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-bgyxeRi1R2Q8kWpHsb1c+lXCulbIAHsyZRddaS+agAUX3hFUVZMociwvRgeZi1zWvfqEEjybSv4zxWvFV8ydQQ=="
|
||||||
|
},
|
||||||
"aws-sdk": {
|
"aws-sdk": {
|
||||||
"version": "2.197.0",
|
"version": "2.197.0",
|
||||||
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.197.0.tgz",
|
"resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.197.0.tgz",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"amqplib": "^0.5.1",
|
"amqplib": "^0.5.1",
|
||||||
"app-root-path": "^2.0.1",
|
"app-root-path": "^2.0.1",
|
||||||
"auto-bind2": "^1.0.3",
|
"auto-bind2": "^1.0.3",
|
||||||
|
"autobind-decorator": "^2.1.0",
|
||||||
"aws-sdk": "^2.98.0",
|
"aws-sdk": "^2.98.0",
|
||||||
"body-parser": "^1.17.1",
|
"body-parser": "^1.17.1",
|
||||||
"canvas": "^1.6.7",
|
"canvas": "^1.6.7",
|
||||||
|
|||||||
@@ -237,15 +237,12 @@ class API extends EventEmitter {
|
|||||||
return this.post('/auth/password/reset', passwords)
|
return this.post('/auth/password/reset', passwords)
|
||||||
}
|
}
|
||||||
|
|
||||||
getUser(_id) {
|
getUser(id) {
|
||||||
return this.get('/users/' + _id)
|
return this.get('/users/' + id)
|
||||||
}
|
}
|
||||||
listUsers() {
|
listUsers() {
|
||||||
return this.get('/users')
|
return this.get('/users')
|
||||||
}
|
}
|
||||||
listBrokerUsers() {
|
|
||||||
return this.get('/users/brokers')
|
|
||||||
}
|
|
||||||
createUser(user) {
|
createUser(user) {
|
||||||
return this.post('/users', user)
|
return this.post('/users', user)
|
||||||
}
|
}
|
||||||
@@ -263,11 +260,8 @@ class API extends EventEmitter {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
deleteUser(_id) {
|
deleteUser(id) {
|
||||||
return this.delete('/users/' + _id)
|
return this.delete('/users/' + id)
|
||||||
}
|
|
||||||
setUserImage(details) {
|
|
||||||
return this.put('/users/set-image', details)
|
|
||||||
}
|
}
|
||||||
enterRoom(roomName) {
|
enterRoom(roomName) {
|
||||||
return this.put('/users/enter-room/' + (roomName || ''))
|
return this.put('/users/enter-room/' + (roomName || ''))
|
||||||
@@ -276,6 +270,22 @@ class API extends EventEmitter {
|
|||||||
return this.put('/users/leave-room')
|
return this.put('/users/leave-room')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTeam(id) {
|
||||||
|
return this.get('/teams/' + id)
|
||||||
|
}
|
||||||
|
listTeams() {
|
||||||
|
return this.get('/teams')
|
||||||
|
}
|
||||||
|
createTeam(team) {
|
||||||
|
return this.post('/teams', team)
|
||||||
|
}
|
||||||
|
updateTeam(team) {
|
||||||
|
this.put('/teams', team)
|
||||||
|
}
|
||||||
|
deleteTeam(id) {
|
||||||
|
return this.delete('/teams/' + id)
|
||||||
|
}
|
||||||
|
|
||||||
upload(file, progressCallback) {
|
upload(file, progressCallback) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const chunkSize = 32 * 1024
|
const chunkSize = 32 * 1024
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Login, Logout, ResetPassword, ForgotPassword, ConfirmEmail, ProtectedRo
|
|||||||
import { Home } from './Home'
|
import { Home } from './Home'
|
||||||
import { Profile } from './Profile'
|
import { Profile } from './Profile'
|
||||||
import { Users } from './Users'
|
import { Users } from './Users'
|
||||||
|
import { Teams } from './Teams'
|
||||||
import { HeaderButton, HeaderText, Column, Row, Text, Box } from 'ui'
|
import { HeaderButton, HeaderText, Column, Row, Text, Box } from 'ui'
|
||||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
|
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
|
||||||
import logoImage from 'images/logo.png'
|
import logoImage from 'images/logo.png'
|
||||||
@@ -99,7 +100,7 @@ export class App extends Component {
|
|||||||
<Route exact path='/forgot-password' component={ForgotPassword} />
|
<Route exact path='/forgot-password' component={ForgotPassword} />
|
||||||
<ProtectedRoute exact path='/profile' render={props => (<Profile {...props} changeTitle={this.handleChangeTitle} />)} />
|
<ProtectedRoute exact path='/profile' render={props => (<Profile {...props} changeTitle={this.handleChangeTitle} />)} />
|
||||||
<ProtectedRoute exact admin path='/users' render={props => (<Users {...props} changeTitle={this.handleChangeTitle} />)} />
|
<ProtectedRoute exact admin path='/users' render={props => (<Users {...props} changeTitle={this.handleChangeTitle} />)} />
|
||||||
<ProtectedRoute exact admin path='/teams' component={Users} />
|
<ProtectedRoute exact admin path='/teams' render={props => (<Teams {...props} changeTitle={this.handleChangeTitle} />)} />
|
||||||
<ProtectedRoute exact admin path='/system' component={Users} />
|
<ProtectedRoute exact admin path='/system' component={Users} />
|
||||||
<ProtectedRoute exact admin path='/home' render={props => (<Home {...props} changeTitle={this.handleChangeTitle} />)} />
|
<ProtectedRoute exact admin path='/home' render={props => (<Home {...props} changeTitle={this.handleChangeTitle} />)} />
|
||||||
<DefaultRoute />
|
<DefaultRoute />
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ export class Home extends Component {
|
|||||||
</Row.Item>
|
</Row.Item>
|
||||||
<Row.Item width={sizeInfo.panelButtonSpacing} />
|
<Row.Item width={sizeInfo.panelButtonSpacing} />
|
||||||
<Row.Item>
|
<Row.Item>
|
||||||
<PanelButton icon='teams' text='Teams' />
|
<PanelButton icon='teams' text='Teams' onClick={() => (this.props.history.push('/teams'))} />
|
||||||
</Row.Item>
|
</Row.Item>
|
||||||
<Row.Item width={sizeInfo.panelButtonSpacing} />
|
<Row.Item width={sizeInfo.panelButtonSpacing} />
|
||||||
<Row.Item>
|
<Row.Item>
|
||||||
|
|||||||
214
website/src/Teams/TeamForm.js
Normal file
214
website/src/Teams/TeamForm.js
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import autobind from 'autobind-decorator'
|
||||||
|
import { regExpPattern } from 'regexp-pattern'
|
||||||
|
import { api } from 'src/API'
|
||||||
|
import { Row, Column, BoundInput, BoundButton, BoundCheckbox, BoundEmailIcon, DropdownList } from 'ui'
|
||||||
|
import { FormBinder } from 'react-form-binder'
|
||||||
|
import { sizeInfo } from 'ui/style'
|
||||||
|
|
||||||
|
export class TeamForm extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
user: PropTypes.object,
|
||||||
|
onSave: PropTypes.func,
|
||||||
|
onRemove: PropTypes.func,
|
||||||
|
onModifiedChanged: PropTypes.func,
|
||||||
|
onChangeEmail: PropTypes.func,
|
||||||
|
onResendEmail: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
static bindings = {
|
||||||
|
email: {
|
||||||
|
isValid: (r, v) => (regExpPattern.email.test(v)),
|
||||||
|
isDisabled: (r) => (r._id)
|
||||||
|
},
|
||||||
|
emailValidated: {
|
||||||
|
initValue: false,
|
||||||
|
isDisabled: (r) => (!r._id)
|
||||||
|
},
|
||||||
|
changeEmail: {
|
||||||
|
noValue: true,
|
||||||
|
isDisabled: (r) => (!r._id)
|
||||||
|
},
|
||||||
|
resendEmail: {
|
||||||
|
noValue: true,
|
||||||
|
isDisabled: (r) => (!r._id || !!r.getFieldValue('emailValidated'))
|
||||||
|
},
|
||||||
|
firstName: {
|
||||||
|
isValid: (r, v) => (v !== '')
|
||||||
|
},
|
||||||
|
lastName: {
|
||||||
|
isValid: (r, v) => (v !== '')
|
||||||
|
},
|
||||||
|
administrator: {
|
||||||
|
isValid: (r, v) => true,
|
||||||
|
initValue: false,
|
||||||
|
isDisabled: (r) => (api.loggedInTeam._id === r._id), // Adding a new user
|
||||||
|
alwaysGet: true,
|
||||||
|
},
|
||||||
|
remove: {
|
||||||
|
noValue: true,
|
||||||
|
isVisible: (r) => (r._id),
|
||||||
|
isDisabled: (r) => (api.loggedInTeam._id === r._id)
|
||||||
|
},
|
||||||
|
reset: {
|
||||||
|
noValue: true,
|
||||||
|
isDisabled: (r) => {
|
||||||
|
return !r.anyModified
|
||||||
|
}
|
||||||
|
},
|
||||||
|
submit: {
|
||||||
|
noValue: true,
|
||||||
|
isDisabled: (r) => (!r.anyModified || !r.allValid),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
binder: new FormBinder(this.props.user, TeamForm.bindings, this.props.onModifiedChanged)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.user !== this.props.user) {
|
||||||
|
this.setState({
|
||||||
|
binder: new FormBinder(nextProps.user, TeamForm.bindings, nextProps.onModifiedChanged)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleSubmit(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
let obj = this.state.binder.getModifiedFieldValues()
|
||||||
|
|
||||||
|
if (obj) {
|
||||||
|
this.props.onSave(obj)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleReset() {
|
||||||
|
const { user, onModifiedChanged } = this.props
|
||||||
|
|
||||||
|
this.setState({ binder: new FormBinder(user, TeamForm.bindings, onModifiedChanged) })
|
||||||
|
|
||||||
|
if (onModifiedChanged) {
|
||||||
|
onModifiedChanged(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleChangeEmail() {
|
||||||
|
this.props.onChangeEmail()
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleResendEmail() {
|
||||||
|
this.props.onResendEmail()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { binder } = this.state
|
||||||
|
const teams = [
|
||||||
|
{ id: 1, name: 'Sign of the Times' },
|
||||||
|
{ id: 2, name: 'Trash Monsters' },
|
||||||
|
{ id: 3, name: 'The Bigger Picker Uppers' },
|
||||||
|
{ id: 4, name: 'Carcass Masters' },
|
||||||
|
{ id: 5, name: 'Dust Bunnies' },
|
||||||
|
{ id: 6, name: 'Pavement Busters' },
|
||||||
|
{ id: 7, name: 'Don\'t Hug That Tree' },
|
||||||
|
{ id: 8, name: 'Broken Swingers' },
|
||||||
|
]
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form style={{ width: '100%', height: '100%', overflow: 'scroll' }} id='userForm' onSubmit={this.handleSubmit}>
|
||||||
|
<Column>
|
||||||
|
<Column.Item height={sizeInfo.formColumnSpacing} />
|
||||||
|
<Row>
|
||||||
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
|
<Row.Item grow>
|
||||||
|
<Column.Item>
|
||||||
|
<Column>
|
||||||
|
<Column.Item>
|
||||||
|
<Row>
|
||||||
|
<Row.Item grow>
|
||||||
|
<BoundInput label='First Name' name='firstName' message='Must not be empty' binder={binder} />
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
|
<Row.Item grow>
|
||||||
|
<BoundInput label='Last Name' name='lastName' binder={binder} />
|
||||||
|
</Row.Item>
|
||||||
|
</Row>
|
||||||
|
</Column.Item>
|
||||||
|
<Column.Item>
|
||||||
|
<Row>
|
||||||
|
<Row.Item grow>
|
||||||
|
<BoundInput label='Email' name='email' message='Must be a valid email address. Required.' binder={binder} />
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
|
<Row.Item>
|
||||||
|
<BoundEmailIcon name='emailValidated' binder={binder} />
|
||||||
|
</Row.Item>
|
||||||
|
</Row>
|
||||||
|
</Column.Item>
|
||||||
|
<Column.Item>
|
||||||
|
<DropdownList items={teams} render={(item) => (
|
||||||
|
<DropdownList.Item key={item.id}>
|
||||||
|
<DropdownList.Icon name='team' size={sizeInfo.dropdownIconSize} />
|
||||||
|
<DropdownList.Text>{item.name}</DropdownList.Text>
|
||||||
|
</DropdownList.Item>
|
||||||
|
)} />
|
||||||
|
</Column.Item>
|
||||||
|
<Column.Item height={sizeInfo.formColumnSpacing} />
|
||||||
|
<Column.Item minHeight={sizeInfo.buttonHeight}>
|
||||||
|
<Row>
|
||||||
|
<Row.Item>
|
||||||
|
<BoundButton text='Change Email' name='changeEmail' binder={binder}
|
||||||
|
width={sizeInfo.buttonWideWidth} onClick={this.handleChangeEmail} />
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item grow />
|
||||||
|
<Row.Item>
|
||||||
|
<BoundButton text='Resend Confirmation Email' name='resendEmail' binder={binder}
|
||||||
|
width={sizeInfo.buttonWideWidth} onClick={this.handleResendEmail} />
|
||||||
|
</Row.Item>
|
||||||
|
</Row>
|
||||||
|
</Column.Item>
|
||||||
|
<Column.Item height={sizeInfo.formColumnSpacing} />
|
||||||
|
<Column.Item>
|
||||||
|
<Row>
|
||||||
|
<Row.Item>
|
||||||
|
<BoundCheckbox label={'Administrator'} name='administrator' binder={this.state.binder} />
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item grow />
|
||||||
|
</Row>
|
||||||
|
</Column.Item>
|
||||||
|
<Column.Item height={sizeInfo.formColumnSpacing} />
|
||||||
|
<Column.Item minHeight={sizeInfo.buttonHeight}>
|
||||||
|
<Row>
|
||||||
|
<Row.Item>
|
||||||
|
<BoundButton text='Reset' name='reset' binder={binder} onClick={this.handleReset} />
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item grow />
|
||||||
|
<Row.Item>
|
||||||
|
<BoundButton text='Remove' name='remove' binder={binder} onClick={this.props.onRemove} />
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
|
<Row.Item>
|
||||||
|
<BoundButton submit='userForm' text={binder._id ? 'Save' : 'Add'} name='submit' binder={binder} />
|
||||||
|
</Row.Item>
|
||||||
|
</Row>
|
||||||
|
</Column.Item>
|
||||||
|
</Column>
|
||||||
|
</Column.Item>
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
|
</Row>
|
||||||
|
<Column.Item height={sizeInfo.formColumnSpacing} />
|
||||||
|
</Column>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
14
website/src/Teams/TeamFormPlaceholder.js
Normal file
14
website/src/Teams/TeamFormPlaceholder.js
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { Column, Text } from 'ui'
|
||||||
|
|
||||||
|
export const TeamFormPlaceholder = () => (
|
||||||
|
<Column fillParent>
|
||||||
|
<Column.Item grow />
|
||||||
|
<Column.Item>
|
||||||
|
<Text size='large' align='center' width='100%'>Select a team to view details here</Text>
|
||||||
|
<br />
|
||||||
|
<Text size='small' align='center' width='100%'>Or 'Add New Team'</Text>
|
||||||
|
</Column.Item>
|
||||||
|
<Column.Item grow />
|
||||||
|
</Column>
|
||||||
|
)
|
||||||
55
website/src/Teams/TeamList.js
Normal file
55
website/src/Teams/TeamList.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import { Column, List, Button } from 'ui'
|
||||||
|
import { sizeInfo } from 'ui/style'
|
||||||
|
|
||||||
|
export class TeamList extends React.Component {
|
||||||
|
static propTypes = {
|
||||||
|
users: PropTypes.array,
|
||||||
|
onUserListClick: PropTypes.func,
|
||||||
|
selectedUser: PropTypes.object,
|
||||||
|
selectionModified: PropTypes.bool,
|
||||||
|
onAddNewUser: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
users: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillReceiveProps(nextProps) {
|
||||||
|
if (nextProps.users !== this.props.users) {
|
||||||
|
this.setState({ users: nextProps.users })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { selectedUser, selectionModified } = this.props
|
||||||
|
const { users } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Column fillParent>
|
||||||
|
<Column.Item grow>
|
||||||
|
<List data={users} render={(user, index) => {
|
||||||
|
return (
|
||||||
|
<List.Item key={user._id || '0'} onClick={(e) => (this.props.onUserListClick(e, index))}
|
||||||
|
active={user === this.props.selectedUser}>
|
||||||
|
<List.Icon name={user.administrator ? 'admin' : 'profile'} size={sizeInfo.listIcon} />
|
||||||
|
<List.Text>
|
||||||
|
{ user._id ? user.firstName + ' ' + user.lastName : '[New User]' }
|
||||||
|
</List.Text>
|
||||||
|
{ user === selectedUser && selectionModified ? <List.Icon name='edit' size={sizeInfo.listIcon} /> : null }
|
||||||
|
</List.Item>
|
||||||
|
)
|
||||||
|
}} />
|
||||||
|
</Column.Item>
|
||||||
|
<Column.Item height={sizeInfo.formColumnSpacing} />
|
||||||
|
<Column.Item height={sizeInfo.buttonHeight}>
|
||||||
|
<Button width='100%' color='inverse' onClick={this.props.onAddNewUser} text='Add New User' />
|
||||||
|
</Column.Item>
|
||||||
|
</Column>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
310
website/src/Teams/Teams.js
Normal file
310
website/src/Teams/Teams.js
Normal file
@@ -0,0 +1,310 @@
|
|||||||
|
import React, { Component, Fragment } from 'react'
|
||||||
|
import PropTypes from 'prop-types'
|
||||||
|
import autobind from 'autobind-decorator'
|
||||||
|
import { TeamList } from './TeamList'
|
||||||
|
import { TeamForm } from './TeamForm'
|
||||||
|
import { TeamFormPlaceholder } from './TeamFormPlaceholder'
|
||||||
|
import { api } from 'src/API'
|
||||||
|
import { Row, Column, Box } from 'ui'
|
||||||
|
import { YesNoMessageModal, MessageModal, ChangeEmailModal, WaitModal } from '../Modal'
|
||||||
|
import { sizeInfo, colorInfo } from 'ui/style'
|
||||||
|
|
||||||
|
export class Teams extends Component {
|
||||||
|
static propTypes = {
|
||||||
|
changeTitle: PropTypes.func.isRequired,
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
modified: false,
|
||||||
|
selectedTeam: null,
|
||||||
|
users: [],
|
||||||
|
yesNoModal: null,
|
||||||
|
messageModal: null,
|
||||||
|
waitModal: null,
|
||||||
|
changeEmailModal: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.changeTitle('Teams')
|
||||||
|
|
||||||
|
api.listTeams().then((list) => {
|
||||||
|
list.items.sort((userA, userB) => (userA.lastName.localeCompare(userB.lastName)))
|
||||||
|
this.setState({ users: list.items, selectedTeam: list.items[0] }) // TODO: <- Remove
|
||||||
|
}).catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
messageModal: {
|
||||||
|
icon: 'hand',
|
||||||
|
message: 'Unable to get the list of teams.',
|
||||||
|
detail: error.message,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.changeTitle('')
|
||||||
|
}
|
||||||
|
|
||||||
|
removeUnfinishedNewTeam() {
|
||||||
|
let users = this.state.users
|
||||||
|
|
||||||
|
if (users.length > 0 && !users[0]._id) {
|
||||||
|
this.setState({ users: this.state.users.slice(1) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleTeamListClick(e, index) {
|
||||||
|
let user = this.state.users[index]
|
||||||
|
|
||||||
|
if (this.state.modified) {
|
||||||
|
this.nextSelectedTeam = user
|
||||||
|
this.setState({
|
||||||
|
yesNoModal: {
|
||||||
|
question: 'This user has been modified. Are you sure you would like to navigate away?',
|
||||||
|
onDismiss: this.handleModifiedModalDismiss
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.setState({ selectedTeam: user })
|
||||||
|
this.removeUnfinishedNewTeam()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleSave(user) {
|
||||||
|
if (user._id) {
|
||||||
|
this.setState({ waitModal: { message: 'Updating Team' } })
|
||||||
|
api.updateTeam(user).then((updatedTeam) => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
users: this.state.users.map((user) => (user._id === updatedTeam._id ? updatedTeam : user)),
|
||||||
|
modified: false,
|
||||||
|
selectedTeam: updatedTeam
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: 'hand',
|
||||||
|
message: 'Unable to save the user changes',
|
||||||
|
detail: error.message,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.setState({ waitModal: { message: 'Creating Team' } })
|
||||||
|
api.createTeam(user).then((createdTeam) => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: false,
|
||||||
|
users: this.state.users.map((user) => (!user._id ? createdTeam : user)).sort((a, b) => (
|
||||||
|
a.lastName < b.lastName ? -1 : a.lastName > b.lastName : 0
|
||||||
|
)),
|
||||||
|
modified: false,
|
||||||
|
selectedTeam: createdTeam
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: 'hand',
|
||||||
|
message: 'Unable to create the user.',
|
||||||
|
detail: error.message,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleChangeEmail() {
|
||||||
|
this.setState({ changeEmailModal: { oldEmail: this.state.selectedTeam.email } })
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleResendEmail() {
|
||||||
|
this.setState({
|
||||||
|
waitModal: { message: 'Resending Email...' }
|
||||||
|
})
|
||||||
|
api.sendConfirmEmail({ existingEmail: this.state.selectedTeam.email }).then(() => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: 'thumb',
|
||||||
|
message: `An email has been sent to '${this.state.selectedTeam.email}' with further instructions.`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: 'hand',
|
||||||
|
message: 'Unable to request email change.',
|
||||||
|
detail: error.message,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleChangeEmailDismiss(newEmail) {
|
||||||
|
this.setState({ changeEmailModal: null })
|
||||||
|
if (!newEmail) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
waitModal: { message: 'Requesting Email Change...' }
|
||||||
|
})
|
||||||
|
if (this.state.selectedTeam) {
|
||||||
|
api.sendConfirmEmail({ existingEmail: this.state.selectedTeam.email, newEmail }).then(() => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: 'hand',
|
||||||
|
message: `An email has been sent to '${newEmail}' to confirm this email.`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
error: true,
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: 'hand',
|
||||||
|
message: 'Unable to request email change.',
|
||||||
|
detail: error.message,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleRemove() {
|
||||||
|
this.setState({
|
||||||
|
yesNoModal: {
|
||||||
|
question: 'Are you sure you want to remove this user? This will also remove them from any teams they belong to.',
|
||||||
|
onDismiss: this.handleRemoveModalDismiss
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleRemoveModalDismiss(yes) {
|
||||||
|
if (yes) {
|
||||||
|
const selectedTeamId = this.state.selectedTeam._id
|
||||||
|
const selectedIndex = this.state.users.findIndex((user) => (user._id === selectedTeamId))
|
||||||
|
|
||||||
|
if (selectedIndex >= 0) {
|
||||||
|
this.setState({ waitModal: { message: 'Removing Team' } })
|
||||||
|
api.deleteTeam(selectedTeamId).then(() => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
users: [...this.state.users.slice(0, selectedIndex), ...this.state.users.slice(selectedIndex + 1)],
|
||||||
|
selectedTeam: null
|
||||||
|
})
|
||||||
|
}).catch((error) => {
|
||||||
|
this.setState({
|
||||||
|
waitModal: null,
|
||||||
|
messageModal: {
|
||||||
|
icon: 'hand',
|
||||||
|
message: 'Unable to remove the user.',
|
||||||
|
detail: error.message,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
yesNoModal: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleModifiedModalDismiss(yes) {
|
||||||
|
if (yes) {
|
||||||
|
this.setState({
|
||||||
|
selectedTeam: this.nextSelectedTeam,
|
||||||
|
modified: false
|
||||||
|
})
|
||||||
|
this.removeUnfinishedNewTeam()
|
||||||
|
delete this.nextSelectedTeam
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
yesNoModal: null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleMessageModalDismiss() {
|
||||||
|
this.setState({ messageModal: null })
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleModifiedChanged(modified) {
|
||||||
|
this.setState({ modified: modified })
|
||||||
|
}
|
||||||
|
|
||||||
|
@autobind
|
||||||
|
handleAddNewTeam() {
|
||||||
|
let newTeam = {}
|
||||||
|
let newTeams = [newTeam].concat(this.state.users)
|
||||||
|
this.setState({ users: newTeams, selectedTeam: newTeam })
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { messageModal, yesNoModal, changeEmailModal } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
<Column.Item height={sizeInfo.formColumnSpacing} />
|
||||||
|
<Column.Item grow>
|
||||||
|
<Row fillParent>
|
||||||
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
|
<Row.Item width='25vw'>
|
||||||
|
<TeamList users={this.state.users} selectedTeam={this.state.selectedTeam}
|
||||||
|
selectionModified={this.state.modified} onTeamListClick={this.handleTeamListClick}
|
||||||
|
onAddNewTeam={this.handleAddNewTeam} />
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
|
<Row.Item grow>
|
||||||
|
<Box border={{ width: sizeInfo.headerBorderWidth, color: colorInfo.headerBorder }} radius={sizeInfo.formBoxRadius}>
|
||||||
|
{
|
||||||
|
this.state.selectedTeam
|
||||||
|
? <TeamForm user={this.state.selectedTeam} onSave={this.handleSave}
|
||||||
|
onRemove={this.handleRemove} onModifiedChanged={this.handleModifiedChanged}
|
||||||
|
onChangeEmail={this.handleChangeEmail} onResendEmail={this.handleResendEmail} />
|
||||||
|
: <TeamFormPlaceholder />
|
||||||
|
}
|
||||||
|
</Box>
|
||||||
|
</Row.Item>
|
||||||
|
<Row.Item width={sizeInfo.formRowSpacing} />
|
||||||
|
</Row>
|
||||||
|
</Column.Item>
|
||||||
|
<Column.Item height={sizeInfo.formColumnSpacing}>
|
||||||
|
<ChangeEmailModal open={!!changeEmailModal}
|
||||||
|
oldEmail={changeEmailModal && changeEmailModal.oldEmail}
|
||||||
|
onDismiss={this.handleChangeEmailDismiss} />
|
||||||
|
|
||||||
|
<YesNoMessageModal open={!!yesNoModal}
|
||||||
|
question={yesNoModal ? yesNoModal.question : ''}
|
||||||
|
onDismiss={yesNoModal && yesNoModal.onDismiss} />
|
||||||
|
|
||||||
|
<MessageModal
|
||||||
|
open={!!messageModal}
|
||||||
|
icon={messageModal ? messageModal.icon : ''}
|
||||||
|
message={messageModal ? messageModal.message : ''}
|
||||||
|
detail={messageModal && messageModal.detail}
|
||||||
|
onDismiss={this.handleMessageModalDismiss} />
|
||||||
|
|
||||||
|
<WaitModal active={!!this.state.waitModal} message={this.state.waitModal ? this.state.waitModal.message : ''} />
|
||||||
|
</Column.Item>
|
||||||
|
</Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
1
website/src/Teams/index.js
Normal file
1
website/src/Teams/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { Teams } from './Teams'
|
||||||
Reference in New Issue
Block a user