Basic UI elements in place
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"expoServerPort": 19000,
|
||||
"packagerPort": 19001,
|
||||
"packagerPid": 76299
|
||||
"packagerPid": 12109
|
||||
}
|
||||
46
website/package-lock.json
generated
46
website/package-lock.json
generated
@@ -2596,6 +2596,16 @@
|
||||
"sha.js": "2.4.9"
|
||||
}
|
||||
},
|
||||
"create-react-class": {
|
||||
"version": "15.6.3",
|
||||
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz",
|
||||
"integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==",
|
||||
"requires": {
|
||||
"fbjs": "0.8.16",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||
@@ -10198,6 +10208,42 @@
|
||||
"prop-types": "15.6.0"
|
||||
}
|
||||
},
|
||||
"react-form-binder": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-form-binder/-/react-form-binder-1.0.5.tgz",
|
||||
"integrity": "sha512-R5oWObsNFnOCviaV5MmFDP0e+RcpPhjzVpMgvDqW3CCUdZp7XYjfyqmv1IXYlvsqavodPmxBLSFhQwxglyzJkg==",
|
||||
"requires": {
|
||||
"eventemitter3": "2.0.3",
|
||||
"prop-types": "15.6.0",
|
||||
"react": "15.6.2",
|
||||
"react-dom": "15.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": {
|
||||
"version": "15.6.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz",
|
||||
"integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=",
|
||||
"requires": {
|
||||
"create-react-class": "15.6.3",
|
||||
"fbjs": "0.8.16",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1",
|
||||
"prop-types": "15.6.0"
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "15.6.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz",
|
||||
"integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=",
|
||||
"requires": {
|
||||
"fbjs": "0.8.16",
|
||||
"loose-envify": "1.3.1",
|
||||
"object-assign": "4.1.1",
|
||||
"prop-types": "15.6.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-router": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.2.0.tgz",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"radium": "^0.22.0",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react-form-binder": "^1.0.5",
|
||||
"react-router-dom": "^4.1.1",
|
||||
"regexp-pattern": "^1.0.4",
|
||||
"socket.io-client": "^2.0.3"
|
||||
|
||||
@@ -3,24 +3,30 @@ import { Login, Logout, ResetPassword, ForgotPassword, ConfirmEmail, ProtectedRo
|
||||
import { Home } from './Home'
|
||||
import { Profile } from './Profile'
|
||||
import { Users } from './Users'
|
||||
import { Column, Row, Image, Text, Icon, Box } from 'ui'
|
||||
import { HeaderButton, Column, Row, Image, Text, Box } from 'ui'
|
||||
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
|
||||
import logoImage from 'images/logo.png'
|
||||
import { versionInfo } from './version'
|
||||
import { sizeInfo } from 'ui/style'
|
||||
|
||||
export class App extends React.Component {
|
||||
render() {
|
||||
const height = sizeInfo.headerHeight - sizeInfo.headerBorderWidth
|
||||
|
||||
return (
|
||||
<Column minHeight='100vh'>
|
||||
<Column.Item>
|
||||
<Box color='#FAFAFA' borderBottom='1px solid #B2B2B2'>
|
||||
<Column.Item height={sizeInfo.headerHeight - sizeInfo.headerBorderWidth}>
|
||||
<Box color='#FAFAFA' borderBottom={`${sizeInfo.headerBorderWidth}px solid #B2B2B2`}>
|
||||
<Row minWidth='100vw'>
|
||||
<Row.Item>
|
||||
<Image source={logoImage} width={50} height={50} margin={5} />
|
||||
<Image source={logoImage}
|
||||
height={height}
|
||||
margin={sizeInfo.headerMargin} />
|
||||
</Row.Item>
|
||||
<Row.Item grow> </Row.Item>
|
||||
<Row.Item>
|
||||
<Icon name='logout' />
|
||||
<HeaderButton icon='profile' size={height} />
|
||||
<HeaderButton icon='logout' size={height} />
|
||||
</Row.Item>
|
||||
</Row>
|
||||
</Box>
|
||||
@@ -43,7 +49,7 @@ export class App extends React.Component {
|
||||
</Column.Item>
|
||||
<Column.Item grow />
|
||||
<Column.Item>
|
||||
<Box color='#FAFAFA' borderTop='1px solid #B2B2B2'>
|
||||
<Box color='#FAFAFA' borderTop={`${sizeInfo.headerBorderWidth}px solid #B2B2B2`}>
|
||||
<Text tone='dimmed' margin={10}>{'v' + versionInfo.version} {versionInfo.copyright}</Text>
|
||||
</Box>
|
||||
</Column.Item>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { api } from '../helpers'
|
||||
import PropTypes from 'prop-types'
|
||||
import { MessageDialog, WaitDialog } from '../Dialog'
|
||||
import { MessageModal, WaitModal } from '../Modal'
|
||||
|
||||
export class ConfirmEmail extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -10,18 +10,18 @@ export class ConfirmEmail extends React.Component {
|
||||
constructor() {
|
||||
super()
|
||||
this.state = {
|
||||
waitDialog: null,
|
||||
messageDialog: null
|
||||
waitModal: null,
|
||||
messageModal: null
|
||||
}
|
||||
this.handleMessageDialogDismiss = this.handleMessageDialogDismiss.bind(this)
|
||||
this.handleMessageModalDismiss = this.handleMessageModalDismiss.bind(this)
|
||||
}
|
||||
|
||||
componentDidMount(props) {
|
||||
let emailToken = new URLSearchParams(decodeURIComponent(window.location.search)).get('email-token')
|
||||
this.setState({ waitDialog: { message: 'Validating Email...' } })
|
||||
this.setState({ waitModal: { message: 'Validating Email...' } })
|
||||
if (emailToken) {
|
||||
api.confirmEmail(emailToken).then((response) => {
|
||||
this.setState({ waitDialog: null })
|
||||
this.setState({ waitModal: null })
|
||||
if (response && response.passwordToken) {
|
||||
// API will send a password reset token if this is the first time loggin on
|
||||
this.props.history.replace(`/reset-password?password-token=${response.passwordToken}`)
|
||||
@@ -35,8 +35,8 @@ export class ConfirmEmail extends React.Component {
|
||||
? 'This email address may have already been confirmed.'
|
||||
: `Please contact ${supportEmail} to request a new user invitation`
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
title: 'Error Verifying Email...',
|
||||
message: `We couldn't complete that request. ${message}`
|
||||
}
|
||||
@@ -47,21 +47,21 @@ export class ConfirmEmail extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleMessageDialogDismiss() {
|
||||
this.setState({ messageDialog: null })
|
||||
handleMessageModalDismiss() {
|
||||
this.setState({ messageModal: null })
|
||||
this.props.history.replace('/login')
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<WaitDialog active={!!this.state.waitDialog}
|
||||
message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
|
||||
<WaitModal active={!!this.state.waitModal}
|
||||
message={this.state.waitModal ? this.state.waitModal.message : ''} />
|
||||
|
||||
<MessageDialog error open={!!this.state.messageDialog}
|
||||
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
|
||||
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
|
||||
onDismiss={this.handleMessageDialogDismiss} />
|
||||
<MessageModal error open={!!this.state.messageModal}
|
||||
title={this.state.messageModal ? this.state.messageModal.title : ''}
|
||||
message={this.state.messageModal ? this.state.messageModal.message : ''}
|
||||
onDismiss={this.handleMessageModalDismiss} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { regExpPattern } from 'regexp-pattern'
|
||||
import { Text, Column } from 'ui'
|
||||
import { MessageDialog, WaitDialog } from '../Dialog'
|
||||
import { Validator, ValidatedInput, ValidatedButton } from '../Validated'
|
||||
import { Text, Column, BoundInput, BoundButton } from 'ui'
|
||||
import { MessageModal, WaitModal } from '../Modal'
|
||||
import { api } from '../helpers'
|
||||
import { FormBinder } from 'react-form-binder'
|
||||
|
||||
export class ForgotPassword extends React.Component {
|
||||
static propTypes = {
|
||||
history: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
|
||||
}
|
||||
|
||||
static validations = {
|
||||
static bindings = {
|
||||
email: {
|
||||
alwaysGet: true,
|
||||
isValid: (r, v) => (regExpPattern.email.test(v))
|
||||
@@ -25,23 +25,23 @@ export class ForgotPassword extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
validator: new Validator({}, ForgotPassword.validations),
|
||||
messageDialog: null,
|
||||
waitDialog: null
|
||||
binder: new FormBinder({}, ForgotPassword.bindings),
|
||||
messageModal: null,
|
||||
waitModal: null
|
||||
}
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
this.handleMessageDialogDismiss = this.handleMessageDialogDismiss.bind(this)
|
||||
this.handleMessageModalDismiss = this.handleMessageModalDismiss.bind(this)
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
const obj = this.state.validator.getValues()
|
||||
const obj = this.state.binder.getValues()
|
||||
|
||||
this.setState({ waitDialog: { message: 'Requesting Reset Email' } })
|
||||
this.setState({ waitModal: { message: 'Requesting Reset Email' } })
|
||||
api.sendResetPassword(obj.email).then((res) => {
|
||||
const email = this.state.validator.getField('email').value
|
||||
const email = this.state.binder.getField('email').value
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: false,
|
||||
title: 'Password Reset Requested',
|
||||
message: `An email will be sent to '${email}' with a reset link. Please click on it to finish resetting the password.`
|
||||
@@ -49,9 +49,9 @@ export class ForgotPassword extends React.Component {
|
||||
})
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
validator: new Validator({}, ForgotPassword.validations), // Reset to avoid rapid retries
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
binder: new FormBinder({}, ForgotPassword.bindings), // Reset to avoid rapid retries
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: true,
|
||||
title: 'Password Reset Failed',
|
||||
message: `There was a problem requesting the password reset. ${error ? error.message : ''}`
|
||||
@@ -60,7 +60,7 @@ export class ForgotPassword extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleMessageDialogDismiss() {
|
||||
handleMessageModalDismiss() {
|
||||
this.props.history.replace('/')
|
||||
}
|
||||
|
||||
@@ -73,29 +73,29 @@ export class ForgotPassword extends React.Component {
|
||||
<Text size='large'>Forgotten Password</Text>
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Email' name='email'
|
||||
placeholder='example@xyz.com' validator={this.state.validator}
|
||||
<BoundInput label='Email' name='email'
|
||||
placeholder='example@xyz.com' binder={this.state.binder}
|
||||
message='A valid email address' />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<Text>The email address of an existing user to send the password reset link to.</Text>
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedButton className='submit' name='submit' content='Submit'
|
||||
primary submit validator={this.state.validator}>Submit</ValidatedButton>
|
||||
<BoundButton className='submit' name='submit' content='Submit'
|
||||
primary submit binder={this.state.binder}>Submit</BoundButton>
|
||||
</Column.Item>
|
||||
</Column>
|
||||
</form>
|
||||
|
||||
<WaitDialog active={!!this.state.waitDialog}
|
||||
message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
|
||||
<WaitModal active={!!this.state.waitModal}
|
||||
message={this.state.waitModal ? this.state.waitModal.message : ''} />
|
||||
|
||||
<MessageDialog
|
||||
open={!!this.state.messageDialog}
|
||||
error={this.state.messageDialog ? this.state.messageDialog.error : true}
|
||||
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
|
||||
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
|
||||
onDismiss={this.handleMessageDialogDismiss} />
|
||||
<MessageModal
|
||||
open={!!this.state.messageModal}
|
||||
error={this.state.messageModal ? this.state.messageModal.error : true}
|
||||
title={this.state.messageModal ? this.state.messageModal.title : ''}
|
||||
message={this.state.messageModal ? this.state.messageModal.message : ''}
|
||||
onDismiss={this.handleMessageModalDismiss} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { regExpPattern } from 'regexp-pattern'
|
||||
import { api } from '../helpers'
|
||||
import { Validator, ValidatedInput, ValidatedCheckbox, ValidatedButton } from '../Validated'
|
||||
import { WaitDialog, MessageDialog } from '../Dialog'
|
||||
import { Image, Link, Text, Row, Column } from 'ui'
|
||||
import { WaitModal, MessageModal } from '../Modal'
|
||||
import { Image, Link, Text, Row, Column, BoundInput, BoundCheckbox, BoundButton } from 'ui'
|
||||
import headerLogo from 'images/deighton.png'
|
||||
import { versionInfo } from '../version'
|
||||
import { FormBinder } from 'react-form-binder'
|
||||
|
||||
export class Login extends React.Component {
|
||||
static propTypes = {
|
||||
history: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
|
||||
}
|
||||
|
||||
static validations = {
|
||||
static bindings = {
|
||||
email: {
|
||||
alwaysGet: true,
|
||||
isValid: (r, v) => (regExpPattern.email.test(v))
|
||||
@@ -35,11 +35,11 @@ export class Login extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
this.handleMessageDialogDismiss = this.handleMessageDialogDismiss.bind(this)
|
||||
this.handleMessageModalDismiss = this.handleMessageModalDismiss.bind(this)
|
||||
this.state = {
|
||||
waitDialog: false,
|
||||
messageDialog: null,
|
||||
validator: new Validator({}, Login.validations)
|
||||
waitModal: false,
|
||||
messageModal: null,
|
||||
binder: new FormBinder({}, Login.bindings)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,19 +47,19 @@ export class Login extends React.Component {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
if (this.state.messageDialog) {
|
||||
this.setState({ messageDialog: null })
|
||||
if (this.state.messageModal) {
|
||||
this.setState({ messageModal: null })
|
||||
return
|
||||
} else if (this.state.waitDialog) {
|
||||
} else if (this.state.waitModal) {
|
||||
return
|
||||
}
|
||||
|
||||
let obj = this.state.validator.getValues()
|
||||
let obj = this.state.binder.getValues()
|
||||
|
||||
if (obj) {
|
||||
this.setState({ waitDialog: true })
|
||||
this.setState({ waitModal: true })
|
||||
api.login(obj.email, obj.password, obj.rememberMe).then((user) => {
|
||||
this.setState({ waitDialog: false })
|
||||
this.setState({ waitModal: false })
|
||||
if (this.props.history) {
|
||||
const landing = user.role === 'broker' ? '/broker-dashboard' : 'dashboard'
|
||||
let url = new URLSearchParams(window.location.search).get('redirect') || landing
|
||||
@@ -75,16 +75,16 @@ export class Login extends React.Component {
|
||||
elems[0].focus()
|
||||
}
|
||||
this.setState({
|
||||
validator: new Validator({ email: this.state.validator.getField('email').value }, Login.validations),
|
||||
waitDialog: false,
|
||||
messageDialog: { title: 'Login Error...', message: `Unable to login. ${error.message}` }
|
||||
binder: new FormBinder({ email: this.state.binder.getField('email').value }, Login.bindings),
|
||||
waitModal: false,
|
||||
messageModal: { title: 'Login Error...', message: `Unable to login. ${error.message}` }
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleMessageDialogDismiss() {
|
||||
this.setState({ messageDialog: null })
|
||||
handleMessageModalDismiss() {
|
||||
this.setState({ messageModal: null })
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -104,13 +104,13 @@ export class Login extends React.Component {
|
||||
</Row>
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Email' name='email'
|
||||
placeholder='example@xyz.com' validator={this.state.validator}
|
||||
<BoundInput label='Email' name='email'
|
||||
placeholder='example@xyz.com' binder={this.state.binder}
|
||||
message='Enter the email address associated with your account.' />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput password label='Password' name='password'
|
||||
validator={this.state.validator} message='Enter your password.' />
|
||||
<BoundInput password label='Password' name='password'
|
||||
binder={this.state.binder} message='Enter your password.' />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<Row>
|
||||
@@ -119,8 +119,8 @@ export class Login extends React.Component {
|
||||
</Row.Item>
|
||||
<Row.Item grow />
|
||||
<Row.Item>
|
||||
<ValidatedCheckbox label='Remember Me'
|
||||
name='rememberMe' onChange={this.handleChange} validator={this.state.validator} />
|
||||
<BoundCheckbox label='Remember Me'
|
||||
name='rememberMe' onChange={this.handleChange} binder={this.state.binder} />
|
||||
</Row.Item>
|
||||
</Row>
|
||||
</Column.Item>
|
||||
@@ -129,7 +129,7 @@ export class Login extends React.Component {
|
||||
<Row.Item grow />
|
||||
<Row.Item>
|
||||
<br />
|
||||
<ValidatedButton name='submit' content='Login' height={60} submit validator={this.state.validator} />
|
||||
<BoundButton name='submit' content='Login' height={60} submit binder={this.state.binder} />
|
||||
</Row.Item>
|
||||
</Row>
|
||||
</Column.Item>
|
||||
@@ -144,12 +144,12 @@ export class Login extends React.Component {
|
||||
</Row.Item>
|
||||
<Row.Item grow> </Row.Item>
|
||||
|
||||
<WaitDialog active={this.state.waitDialog} message='Logging In' />
|
||||
<WaitModal active={this.state.waitModal} message='Logging In' />
|
||||
|
||||
<MessageDialog error open={!!this.state.messageDialog}
|
||||
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
|
||||
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
|
||||
onDismiss={this.handleMessageDialogDismiss} />
|
||||
<MessageModal error open={!!this.state.messageModal}
|
||||
title={this.state.messageModal ? this.state.messageModal.title : ''}
|
||||
message={this.state.messageModal ? this.state.messageModal.message : ''}
|
||||
onDismiss={this.handleMessageModalDismiss} />
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Text, Column } from 'ui'
|
||||
import { Validator, ValidatedInput, ValidatedButton } from '../Validated'
|
||||
import { MessageDialog, WaitDialog } from '../Dialog'
|
||||
import { Text, Column, BoundInput, BoundButton } from 'ui'
|
||||
import { MessageModal, WaitModal } from '../Modal'
|
||||
import { api } from '../helpers'
|
||||
import { FormBinder } from 'react-form-binder'
|
||||
|
||||
export class ResetPassword extends React.Component {
|
||||
static propTypes = {
|
||||
history: PropTypes.oneOfType([PropTypes.array, PropTypes.object])
|
||||
}
|
||||
|
||||
static validations = {
|
||||
static bindings = {
|
||||
newPassword: {
|
||||
alwaysGet: true,
|
||||
isValid: (r, v) => (v.length >= 6)
|
||||
@@ -27,27 +27,27 @@ export class ResetPassword extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
validator: new Validator({}, ResetPassword.validations),
|
||||
messageDialog: null,
|
||||
waitDialog: null
|
||||
binder: new FormBinder({}, ResetPassword.bindings),
|
||||
messageModal: null,
|
||||
waitModal: null
|
||||
}
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
this.handleMessageDialogDismiss = this.handleMessageDialogDismiss.bind(this)
|
||||
this.handleMessageModalDismiss = this.handleMessageModalDismiss.bind(this)
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
const obj = this.state.validator.getValues()
|
||||
const obj = this.state.binder.getValues()
|
||||
const passwordToken = new URLSearchParams(decodeURIComponent(window.location.search)).get('password-token')
|
||||
|
||||
this.setState({ waitDialog: { message: 'Setting Password...' } })
|
||||
this.setState({ waitModal: { message: 'Setting Password...' } })
|
||||
api.resetPassword({ newPassword: obj.newPassword, passwordToken }).then(() => {
|
||||
this.setState({ waitDialog: null })
|
||||
this.setState({ waitModal: null })
|
||||
this.props.history.replace('/login')
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
validator: new Validator({}, ResetPassword.validations), // Reset to avoid accidental rapid retries
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
binder: new FormBinder({}, ResetPassword.bindings), // Reset to avoid accidental rapid retries
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
title: 'We had a problem changing your password',
|
||||
message: error.message
|
||||
}
|
||||
@@ -55,8 +55,8 @@ export class ResetPassword extends React.Component {
|
||||
})
|
||||
}
|
||||
|
||||
handleMessageDialogDismiss() {
|
||||
this.setState({ messageDialog: null })
|
||||
handleMessageModalDismiss() {
|
||||
this.setState({ messageModal: null })
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -68,14 +68,14 @@ export class ResetPassword extends React.Component {
|
||||
<Text size='large'>Reset Password</Text>
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='New Password' password name='newPassword'
|
||||
<BoundInput label='New Password' password name='newPassword'
|
||||
message='A new password, cannot be blank or the same as your old password'
|
||||
width={16} validator={this.state.validator} />
|
||||
width={16} binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Re-entered New Password' password name='reenteredNewPassword'
|
||||
<BoundInput label='Re-entered New Password' password name='reenteredNewPassword'
|
||||
message='The new password again, must match and cannot be blank'
|
||||
width={16} validator={this.state.validator} />
|
||||
width={16} binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<Text>
|
||||
@@ -85,19 +85,19 @@ export class ResetPassword extends React.Component {
|
||||
</Text>
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedButton className='submit' name='submit' content='Submit'
|
||||
submit validator={this.state.validator} />
|
||||
<BoundButton className='submit' name='submit' content='Submit'
|
||||
submit binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
</Column>
|
||||
</form>
|
||||
|
||||
<MessageDialog error open={!!this.state.messageDialog}
|
||||
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
|
||||
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
|
||||
onDismiss={this.handleMessageDialogDismiss} />
|
||||
<MessageModal error open={!!this.state.messageModal}
|
||||
title={this.state.messageModal ? this.state.messageModal.title : ''}
|
||||
message={this.state.messageModal ? this.state.messageModal.message : ''}
|
||||
onDismiss={this.handleMessageModalDismiss} />
|
||||
|
||||
<WaitDialog active={!!this.state.waitDialog}
|
||||
message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
|
||||
<WaitModal active={!!this.state.waitModal}
|
||||
message={this.state.waitModal ? this.state.waitModal.message : ''} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
export { WaitDialog } from './WaitDialog'
|
||||
export { YesNoMessageDialog } from './YesNoMessageDialog'
|
||||
export { MessageDialog } from './MessageDialog'
|
||||
export { ChangePasswordDialog } from './ChangePasswordDialog'
|
||||
export { ChangeEmailDialog } from './ChangeEmailDialog'
|
||||
@@ -1,9 +0,0 @@
|
||||
import React from 'react'
|
||||
import './Footer.scss'
|
||||
import { VersionInfo } from '../version'
|
||||
|
||||
export const Footer = () => (
|
||||
<footer>
|
||||
v{VersionInfo.fullVersion}. © {VersionInfo.startYear} Deighton. All rights reserved.
|
||||
</footer>
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
footer {
|
||||
background-color: #0b0b0b;
|
||||
color: grey;
|
||||
height: 3em;
|
||||
line-height: 3em;
|
||||
padding: 0 2em;
|
||||
font-size: 1em;
|
||||
margin-top: 3em;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
text-align: left;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export { Footer } from './Footer'
|
||||
@@ -1,17 +1,17 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { autoBind } from 'auto-bind2'
|
||||
import { Modal, Button, Icon, Column, Text } from 'ui'
|
||||
import { ValidatedInput, ValidatedButton, Validator } from '../Validated'
|
||||
import { Modal, Button, Icon, Column, Text, BoundInput, BoundButton } from 'ui'
|
||||
import { regExpPattern } from 'regexp-pattern'
|
||||
import { FormBinder } from 'react-form-binder'
|
||||
|
||||
export class ChangeEmailDialog extends React.Component {
|
||||
export class ChangeEmailModal extends React.Component {
|
||||
static propTypes = {
|
||||
open: PropTypes.bool,
|
||||
onDismiss: PropTypes.func
|
||||
}
|
||||
|
||||
static validations = {
|
||||
static bindings = {
|
||||
newEmail: {
|
||||
isValid: (r, v) => (v !== '' && regExpPattern.email.test(v))
|
||||
},
|
||||
@@ -25,12 +25,12 @@ export class ChangeEmailDialog extends React.Component {
|
||||
super(props)
|
||||
autoBind(this, (name) => (name.startsWith('handle')))
|
||||
this.state = {
|
||||
validator: new Validator({}, ChangeEmailDialog.validations)
|
||||
binder: new FormBinder({}, ChangeEmailModal.bindings)
|
||||
}
|
||||
}
|
||||
|
||||
close(newEmail) {
|
||||
this.state.validator = new Validator({}, ChangeEmailDialog.validations)
|
||||
this.state.binder = new FormBinder({}, ChangeEmailModal.bindings)
|
||||
this.props.onDismiss(newEmail)
|
||||
}
|
||||
|
||||
@@ -42,8 +42,8 @@ export class ChangeEmailDialog extends React.Component {
|
||||
e.preventDefault()
|
||||
let newEmail = null
|
||||
|
||||
if (this.state.validator.anyModified && this.state.validator.allValid) {
|
||||
newEmail = this.state.validator.getField('newEmail').value
|
||||
if (this.state.binder.anyModified && this.state.binder.allValid) {
|
||||
newEmail = this.state.binder.getField('newEmail').value
|
||||
}
|
||||
|
||||
this.close(newEmail)
|
||||
@@ -63,14 +63,14 @@ export class ChangeEmailDialog extends React.Component {
|
||||
<Text>Change Email</Text>
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='New Email' name='newEmail' width={16}
|
||||
<BoundInput label='New Email' name='newEmail' width={16}
|
||||
message='Your new email address, e.g. xyz@abc.com, cannot be blank'
|
||||
validator={this.state.validator} />
|
||||
binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedButton primary submit form='emailForm' name='submit' validator={this.state.validator}>
|
||||
<BoundButton primary submit form='emailForm' name='submit' binder={this.state.binder}>
|
||||
<Icon name='checkmark' /> OK
|
||||
</ValidatedButton>
|
||||
</BoundButton>
|
||||
<Button color='red' onClick={this.handleClick}>
|
||||
<Icon name='close' /> Cancel
|
||||
</Button>
|
||||
@@ -1,16 +1,16 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { autoBind } from 'auto-bind2'
|
||||
import { Modal, Button, Icon, Column, Row, Text } from 'ui'
|
||||
import { ValidatedInput, ValidatedActionsButton, Validator } from '../Validated'
|
||||
import { Modal, Button, Icon, Column, Row, Text, BoundInput, BoundButton } from 'ui'
|
||||
import { FormBinder } from 'react-form-binder'
|
||||
|
||||
export class ChangePasswordDialog extends React.Component {
|
||||
export class ChangePasswordModal extends React.Component {
|
||||
static propTypes = {
|
||||
open: PropTypes.bool,
|
||||
onDismiss: PropTypes.func
|
||||
}
|
||||
|
||||
static validations = {
|
||||
static bindings = {
|
||||
oldPassword: {
|
||||
alwaysGet: true,
|
||||
isValid: (r, v) => (v !== '')
|
||||
@@ -33,12 +33,12 @@ export class ChangePasswordDialog extends React.Component {
|
||||
super(props)
|
||||
autoBind(this, (name) => (name.startsWith('handle')))
|
||||
this.state = {
|
||||
validator: new Validator({}, ChangePasswordDialog.validations)
|
||||
binder: new FormBinder({}, ChangePasswordModal.bindings)
|
||||
}
|
||||
}
|
||||
|
||||
close(passwords) {
|
||||
this.state.validator = new Validator({}, ChangePasswordDialog.validations)
|
||||
this.state.binder = new FormBinder({}, ChangePasswordModal.bindings)
|
||||
this.props.onDismiss(passwords)
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ export class ChangePasswordDialog extends React.Component {
|
||||
e.preventDefault()
|
||||
let passwords = null
|
||||
|
||||
if (this.state.validator.allValid) {
|
||||
const oldPassword = this.state.validator.getField('oldPassword').value
|
||||
const newPassword = this.state.validator.getField('newPassword').value
|
||||
if (this.state.binder.allValid) {
|
||||
const oldPassword = this.state.binder.getField('oldPassword').value
|
||||
const newPassword = this.state.binder.getField('newPassword').value
|
||||
passwords = { oldPassword, newPassword }
|
||||
}
|
||||
this.close(passwords)
|
||||
@@ -72,27 +72,27 @@ export class ChangePasswordDialog extends React.Component {
|
||||
<Column.Item>
|
||||
<Column>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Current Password' password name='oldPassword'
|
||||
<BoundInput label='Current Password' password name='oldPassword'
|
||||
message='Your existing password, cannot be blank'
|
||||
width={8} validator={this.state.validator} />
|
||||
width={8} binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='New Password' password name='newPassword'
|
||||
<BoundInput label='New Password' password name='newPassword'
|
||||
message='A new password, cannot be blank or the same as your old password'
|
||||
width={8} validator={this.state.validator} />
|
||||
width={8} binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Re-entered New Password' password name='reenteredNewPassword'
|
||||
<BoundInput label='Re-entered New Password' password name='reenteredNewPassword'
|
||||
message='The new password again, must match and cannot be blank'
|
||||
width={8} validator={this.state.validator} />
|
||||
width={8} binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
</Column>
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<Row>
|
||||
<ValidatedActionsButton primary submit form='passwordForm' name='submit' validator={this.state.validator}>
|
||||
<BoundButton primary submit form='passwordForm' name='submit' binder={this.state.binder}>
|
||||
<Icon name='checkmark' /> OK
|
||||
</ValidatedActionsButton>
|
||||
</BoundButton>
|
||||
<Button color='red' onClick={this.handleClick}>
|
||||
<Icon name='close' /> Cancel
|
||||
</Button>
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Modal, Button, Icon, Column, Text } from 'ui'
|
||||
|
||||
export class MessageDialog extends React.Component {
|
||||
export class MessageModal extends React.Component {
|
||||
static propTypes = {
|
||||
open: PropTypes.bool,
|
||||
title: PropTypes.string.isRequired,
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Dimmer, Loader, Text } from 'ui'
|
||||
|
||||
export class WaitDialog extends React.Component {
|
||||
export class WaitModal extends React.Component {
|
||||
static propTypes = {
|
||||
message: PropTypes.string.isRequired
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Modal, Button, Column, Text, Icon } from 'ui'
|
||||
|
||||
export class YesNoMessageDialog extends React.Component {
|
||||
export class YesNoMessageModal extends React.Component {
|
||||
static propTypes = {
|
||||
open: PropTypes.bool,
|
||||
title: PropTypes.string.isRequired,
|
||||
5
website/src/Modal/index.js
Normal file
5
website/src/Modal/index.js
Normal file
@@ -0,0 +1,5 @@
|
||||
export { WaitModal } from './WaitModal'
|
||||
export { YesNoMessageModal } from './YesNoMessageModal'
|
||||
export { MessageModal } from './MessageModal'
|
||||
export { ChangePasswordModal } from './ChangePasswordModal'
|
||||
export { ChangeEmailModal } from './ChangeEmailModal'
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { ProfileForm } from './ProfileForm'
|
||||
import { Constants, api } from '../helpers'
|
||||
import { WaitDialog, MessageDialog, ChangePasswordDialog, ChangeEmailDialog } from '../Dialog'
|
||||
import { WaitModal, MessageModal, ChangePasswordModal, ChangeEmailModal } from '../Modal'
|
||||
import { autoBind } from 'auto-bind2'
|
||||
|
||||
export class Profile extends React.Component {
|
||||
@@ -11,11 +11,11 @@ export class Profile extends React.Component {
|
||||
|
||||
const user = api.loggedInUser
|
||||
this.state = {
|
||||
messageDialog: null,
|
||||
waitDialog: null,
|
||||
changePasswordDialog: null,
|
||||
changeEmailDialog: null,
|
||||
progressDialog: null,
|
||||
messageModal: null,
|
||||
waitModal: null,
|
||||
changePasswordModal: null,
|
||||
changeEmailModal: null,
|
||||
progressModal: null,
|
||||
uploadPercent: 0,
|
||||
user,
|
||||
userImageUrl: api.makeImageUrl(user.imageId, Constants.bigUserImageSize)
|
||||
@@ -35,32 +35,32 @@ export class Profile extends React.Component {
|
||||
}
|
||||
|
||||
handleSaved(user) {
|
||||
this.setState({ waitDialog: { message: 'Updating Profile' } })
|
||||
this.setState({ waitModal: { message: 'Updating Profile' } })
|
||||
api.updateUser(user).then((updatedUser) => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
waitModal: null,
|
||||
user: updatedUser
|
||||
})
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: { title: 'Update Error...', message: `Unable to save the profile changes. ${error.message}` }
|
||||
waitModal: null,
|
||||
messageModal: { title: 'Update Error...', message: `Unable to save the profile changes. ${error.message}` }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
handleMessageDialogDismiss() {
|
||||
this.setState({ messageDialog: null })
|
||||
handleMessageModalDismiss() {
|
||||
this.setState({ messageModal: null })
|
||||
}
|
||||
|
||||
handleChangePassword() {
|
||||
this.setState({ changePasswordDialog: true })
|
||||
this.setState({ changePasswordModal: true })
|
||||
}
|
||||
|
||||
handleSelectImage(file) {
|
||||
this.setState({ progressDialog: { message: `Uploading image '${file.name}'...`, file }, uploadPercent: 0 })
|
||||
this.setState({ progressModal: { message: `Uploading image '${file.name}'...`, file }, uploadPercent: 0 })
|
||||
api.upload(file, this.handleProgress).then((uploadData) => {
|
||||
this.setState({ progressDialog: null })
|
||||
this.setState({ progressModal: null })
|
||||
return api.setUserImage({
|
||||
_id: api.loggedInUser._id,
|
||||
imageId: uploadData.assetId,
|
||||
@@ -70,14 +70,14 @@ export class Profile extends React.Component {
|
||||
}).catch((error) => {
|
||||
// TODO: if the upload succeeds but the setUserImage fails, delete the uploaded image
|
||||
this.setState({
|
||||
progressDialog: null,
|
||||
messageDialog: { title: 'Upload Error...', message: `Unable to upload the file. ${error.message}` }
|
||||
progressModal: null,
|
||||
messageModal: { title: 'Upload Error...', message: `Unable to upload the file. ${error.message}` }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
handleProgress(uploadData) {
|
||||
if (this.state.progressDialog) {
|
||||
if (this.state.progressModal) {
|
||||
this.setState({ uploadPercent: Math.round(uploadData.uploadedChunks / uploadData.numberOfChunks * 100) })
|
||||
return true
|
||||
} else {
|
||||
@@ -86,22 +86,22 @@ export class Profile extends React.Component {
|
||||
}
|
||||
|
||||
handleUploadCancel(result) {
|
||||
this.setState({ progressDialog: null })
|
||||
this.setState({ progressModal: null })
|
||||
}
|
||||
|
||||
handleChangePasswordDismiss(passwords) {
|
||||
this.setState({ changePasswordDialog: false })
|
||||
this.setState({ changePasswordModal: false })
|
||||
|
||||
if (passwords) {
|
||||
this.setState({
|
||||
waitDialog: { message: 'Changing Password' }
|
||||
waitModal: { message: 'Changing Password' }
|
||||
})
|
||||
api.changePassword(passwords).then(() => {
|
||||
this.setState({ waitDialog: false })
|
||||
this.setState({ waitModal: false })
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
waitDialog: false,
|
||||
messageDialog: {
|
||||
waitModal: false,
|
||||
messageModal: {
|
||||
title: 'Changing Password Error',
|
||||
message: `Unable to change password. ${error.message}.`
|
||||
}
|
||||
@@ -111,21 +111,21 @@ export class Profile extends React.Component {
|
||||
}
|
||||
|
||||
handleChangeEmail() {
|
||||
this.setState({ changeEmailDialog: {} })
|
||||
this.setState({ changeEmailModal: {} })
|
||||
}
|
||||
|
||||
handleChangeEmailDismiss(newEmail) {
|
||||
this.setState({ changeEmailDialog: null })
|
||||
this.setState({ changeEmailModal: null })
|
||||
if (!newEmail) {
|
||||
return
|
||||
}
|
||||
this.setState({
|
||||
waitDialog: { message: 'Requesting Email Change...' }
|
||||
waitModal: { message: 'Requesting Email Change...' }
|
||||
})
|
||||
api.sendConfirmEmail({ newEmail }).then(() => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: false,
|
||||
title: 'Email Change Requested...',
|
||||
message: `An email has been sent to '${newEmail}' with a link that you need to click on to finish changing your email.`
|
||||
@@ -134,8 +134,8 @@ export class Profile extends React.Component {
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
error: true,
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: true,
|
||||
title: 'Email Change Error...',
|
||||
message: `Unable to request email change. ${error ? error.message : ''}`
|
||||
@@ -155,16 +155,16 @@ export class Profile extends React.Component {
|
||||
onChangeEmail={this.handleChangeEmail}
|
||||
userImageUrl={this.state.userImageUrl} />
|
||||
|
||||
<MessageDialog error open={!!this.state.messageDialog}
|
||||
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
|
||||
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
|
||||
onDismiss={this.handleMessageDialogDismiss} />
|
||||
<MessageModal error open={!!this.state.messageModal}
|
||||
title={this.state.messageModal ? this.state.messageModal.title : ''}
|
||||
message={this.state.messageModal ? this.state.messageModal.message : ''}
|
||||
onDismiss={this.handleMessageModalDismiss} />
|
||||
|
||||
<ChangeEmailDialog open={!!this.state.changeEmailDialog} onDismiss={this.handleChangeEmailDismiss} />
|
||||
<ChangeEmailModal open={!!this.state.changeEmailModal} onDismiss={this.handleChangeEmailDismiss} />
|
||||
|
||||
<WaitDialog active={!!this.state.waitDialog} message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
|
||||
<WaitModal active={!!this.state.waitModal} message={this.state.waitModal ? this.state.waitModal.message : ''} />
|
||||
|
||||
<ChangePasswordDialog open={!!this.state.changePasswordDialog} onDismiss={this.handleChangePasswordDismiss} />
|
||||
<ChangePasswordModal open={!!this.state.changePasswordModal} onDismiss={this.handleChangePasswordDismiss} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Column, Button } from 'ui'
|
||||
import { Column, Button, BoundInput, BoundButton } from 'ui'
|
||||
import { regExpPattern } from 'regexp-pattern'
|
||||
import { Validator, ValidatedInput, ValidatedButton } from '../Validated'
|
||||
import { FormBinder } from 'react-form-binder'
|
||||
|
||||
export class ProfileForm extends React.Component {
|
||||
static propTypes = {
|
||||
@@ -13,7 +13,7 @@ export class ProfileForm extends React.Component {
|
||||
onChangeEmail: PropTypes.func
|
||||
}
|
||||
|
||||
static validations = {
|
||||
static bindings = {
|
||||
email: {
|
||||
isValid: (r, v) => (v !== ''),
|
||||
isDisabled: (r) => (!!r._id)
|
||||
@@ -68,16 +68,16 @@ export class ProfileForm extends React.Component {
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this)
|
||||
this.state = {
|
||||
validator: new Validator(
|
||||
this.props.user, ProfileForm.validations, this.props.onModifiedChanged)
|
||||
binder: new FormBinder(
|
||||
this.props.user, ProfileForm.bindings, this.props.onModifiedChanged)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.user !== this.props.user) {
|
||||
this.setState({
|
||||
validator: new Validator(
|
||||
nextProps.user, ProfileForm.validations, nextProps.onModifiedChanged)
|
||||
binder: new FormBinder(
|
||||
nextProps.user, ProfileForm.bindings, nextProps.onModifiedChanged)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -85,7 +85,7 @@ export class ProfileForm extends React.Component {
|
||||
handleSubmit(e) {
|
||||
e.preventDefault()
|
||||
|
||||
let obj = this.state.validator.getValues()
|
||||
let obj = this.state.binder.getValues()
|
||||
|
||||
if (obj && this.props.onSaved) {
|
||||
this.props.onSaved(obj)
|
||||
@@ -97,16 +97,16 @@ export class ProfileForm extends React.Component {
|
||||
<form className='profile-form' onSubmit={this.handleSubmit}>
|
||||
<Column stackable>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='First Name' name='firstName' width={8}
|
||||
validator={this.state.validator} />
|
||||
<BoundInput label='First Name' name='firstName' width={8}
|
||||
binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Last Name' name='lastName' width={8}
|
||||
validator={this.state.validator} />
|
||||
<BoundInput label='Last Name' name='lastName' width={8}
|
||||
binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Email' name='email' width={8} message='Required. Must be a valid email address.'
|
||||
validator={this.state.validator} />
|
||||
<BoundInput label='Email' name='email' width={8} message='Required. Must be a valid email address.'
|
||||
binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<Button fluid content={'Change Email'} label=' '
|
||||
@@ -115,8 +115,8 @@ export class ProfileForm extends React.Component {
|
||||
width={4} onClick={this.props.onChangePassword} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedButton submit primary width={4} size='medium' content='Save' label=' ' name='save'
|
||||
validator={this.state.validator} />
|
||||
<BoundButton submit primary width={4} size='medium' content='Save' label=' ' name='save'
|
||||
binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
</Column>
|
||||
</form>
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { autoBind } from 'auto-bind2'
|
||||
import { reactAutoBind } from 'auto-bind2'
|
||||
import { regExpPattern } from 'regexp-pattern'
|
||||
import { ValidatedEmailIcon } from './ValidatedEmailIcon'
|
||||
import { Constants, api } from '../helpers'
|
||||
import { Validator, ValidatedInput, ValidatedButton, ValidatedCheckbox } from '../Validated'
|
||||
import { Column } from 'ui'
|
||||
import { Column, BoundInput, BoundButton, BoundCheckbox, BoundEmailIcon } from 'ui'
|
||||
import { FormBinder } from 'react-form-binder'
|
||||
|
||||
export class UserForm extends React.Component {
|
||||
static propTypes = {
|
||||
user: PropTypes.object,
|
||||
onSave: PropTypes.func,
|
||||
onRemove: PropTypes.func,
|
||||
onModifiedChanged: PropTypes.func,
|
||||
onAnyModified: PropTypes.func,
|
||||
onChangeEmail: PropTypes.func,
|
||||
onResendEmail: PropTypes.func
|
||||
}
|
||||
|
||||
static validations = {
|
||||
static bindings = {
|
||||
email: {
|
||||
isValid: (r, v) => (regExpPattern.email.test(v)),
|
||||
isDisabled: (r) => (!!r._id)
|
||||
@@ -35,45 +34,6 @@ export class UserForm extends React.Component {
|
||||
lastName: {
|
||||
isValid: (r, v) => (v !== '')
|
||||
},
|
||||
zip: {
|
||||
isValid: (r, v) => (v === '' || regExpPattern.zip.test(v))
|
||||
},
|
||||
state: {
|
||||
isValid: (r, v) => (v === '' || regExpPattern.state.test(v))
|
||||
},
|
||||
city: {
|
||||
isValid: true
|
||||
},
|
||||
address1: {
|
||||
isValid: true
|
||||
},
|
||||
address2: {
|
||||
isValid: true
|
||||
},
|
||||
homePhone: {
|
||||
isValid: (r, v) => (v === '' || regExpPattern.phone.test(v))
|
||||
},
|
||||
cellPhone: {
|
||||
isValid: (r, v) => (v === '' || regExpPattern.phone.test(v))
|
||||
},
|
||||
dateOfBirth: {
|
||||
isValid: true
|
||||
},
|
||||
dateOfHire: {
|
||||
isValid: true
|
||||
},
|
||||
ssn: {
|
||||
isValid: (r, v) => (v === '' || regExpPattern.ssn.test(v))
|
||||
},
|
||||
numHouseholds: {
|
||||
isValid: true
|
||||
},
|
||||
t12: {
|
||||
isValid: true
|
||||
},
|
||||
aum: {
|
||||
isValid: true
|
||||
},
|
||||
role: {
|
||||
isValid: (r, v) => (v !== ''),
|
||||
isDisabled: (r) => (api.loggedInUser._id === r._id)
|
||||
@@ -94,43 +54,40 @@ export class UserForm extends React.Component {
|
||||
nonValue: true,
|
||||
isDisabled: (r) => (!r.anyModified && !r.allValid)
|
||||
},
|
||||
'broker-fields': {
|
||||
nonValue: true,
|
||||
isVisible: (r, v) => (r.getField('role').value === 'broker')
|
||||
},
|
||||
'standard-fields': {
|
||||
nonValue: true,
|
||||
isVisible: (r, v) => (r.getField('role').value !== 'broker')
|
||||
}
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
autoBind(this, (name) => (name.startsWith('handle')))
|
||||
reactAutoBind(this)
|
||||
this.state = {
|
||||
validator: new Validator(this.props.user, UserForm.validations, this.props.onModifiedChanged)
|
||||
binder: new FormBinder(this.props.user, UserForm.bindings, this.props.onAnyModified)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.user !== this.props.user) {
|
||||
this.setState({
|
||||
validator: new Validator(nextProps.user, UserForm.validations, nextProps.onModifiedChanged)
|
||||
binder: new FormBinder(nextProps.user, UserForm.bindings, nextProps.onAnyModified)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
e.preventDefault()
|
||||
let obj = this.state.validator.getValues()
|
||||
let obj = this.state.binder.getValues()
|
||||
if (obj) {
|
||||
this.props.onSave(obj)
|
||||
}
|
||||
}
|
||||
|
||||
handleReset() {
|
||||
this.setState({ validator: new Validator(this.props.user, UserForm.validations, this.props.onModifiedChanged) })
|
||||
this.props.onModifiedChanged(false)
|
||||
const { user, onAnyModified } = this.props
|
||||
|
||||
this.setState({ binder: new FormBinder(user, UserForm.bindings, onAnyModified) })
|
||||
|
||||
if (onAnyModified) {
|
||||
onAnyModified(false)
|
||||
}
|
||||
}
|
||||
|
||||
handleChangeEmail() {
|
||||
@@ -146,37 +103,37 @@ export class UserForm extends React.Component {
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<Column>
|
||||
<Column.Item>
|
||||
<ValidatedCheckbox label={'Deighton Access & Security Level'} width={6} selection
|
||||
<BoundCheckbox label={'Deighton Access & Security Level'} width={6} selection
|
||||
options={Constants.accessLevels} name='role' message='The user role and security level'
|
||||
placeholder='' validator={this.state.validator} />
|
||||
placeholder='' binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='First Name' name='firstName'
|
||||
width={8} validator={this.state.validator} />
|
||||
<BoundInput label='First Name' name='firstName'
|
||||
width={8} binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Last Name' name='lastName'
|
||||
width={8} validator={this.state.validator} />
|
||||
<BoundInput label='Last Name' name='lastName'
|
||||
width={8} binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedInput label='Email' name='email' width={8} message='Must be a valid email address. Required.'
|
||||
validator={this.state.validator} />
|
||||
<ValidatedEmailIcon name='emailValidated' validator={this.state.validator} width={4}
|
||||
<BoundInput label='Email' name='email' width={8} message='Must be a valid email address. Required.'
|
||||
binder={this.state.binder} />
|
||||
<BoundEmailIcon name='emailValidated' binder={this.state.binder} width={4}
|
||||
onClick={this.handleResendEmail} />
|
||||
</Column.Item>
|
||||
<Column.Item>
|
||||
<ValidatedButton width={4} size='medium' content='Change Email' label=' ' name='changeEmail'
|
||||
validator={this.state.validator} onClick={this.handleChangeEmail} />
|
||||
<BoundButton width={4} size='medium' content='Change Email' label=' ' name='changeEmail'
|
||||
binder={this.state.binder} onClick={this.handleChangeEmail} />
|
||||
</Column.Item>
|
||||
|
||||
<Column.Item>
|
||||
<ValidatedButton color='red' width={4} size='medium' content='Remove' label=' ' name='remove'
|
||||
validator={this.state.validator} onClick={this.props.onRemove} />
|
||||
<ValidatedButton width={4} size='medium' content='Reset' label=' ' name='reset'
|
||||
validator={this.state.validator} onClick={this.handleReset} />
|
||||
<ValidatedButton primary submit width={4} size='medium'
|
||||
content={this.state.validator._id ? 'Save' : 'Add'} label=' ' name='submit'
|
||||
validator={this.state.validator} />
|
||||
<BoundButton color='red' width={4} size='medium' content='Remove' label=' ' name='remove'
|
||||
binder={this.state.binder} onClick={this.props.onRemove} />
|
||||
<BoundButton width={4} size='medium' content='Reset' label=' ' name='reset'
|
||||
binder={this.state.binder} onClick={this.handleReset} />
|
||||
<BoundButton primary submit width={4} size='medium'
|
||||
content={this.state.binder._id ? 'Save' : 'Add'} label=' ' name='submit'
|
||||
binder={this.state.binder} />
|
||||
</Column.Item>
|
||||
</Column>
|
||||
</form>
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
.user-form {
|
||||
text-align: left;
|
||||
margin: 3em auto 4em auto;
|
||||
}
|
||||
|
||||
.user-form > .fields {
|
||||
margin-bottom: 1.5em !important;
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Dropdown, List, Icon, Button, Image } from 'ui'
|
||||
import { Constants, api } from '../helpers'
|
||||
import './UserList.scss'
|
||||
|
||||
const selectionOptions = Constants.accessLevels.concat([{ value: 'all', text: 'All Levels' }])
|
||||
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
.user-list {
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0.25em !important;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 0.25em 2px Silver;
|
||||
-webkit-box-shadow: 0 0 0.25em 2px Silver;
|
||||
max-height: 60vh;
|
||||
min-height: 60vh;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.user-list > .item {
|
||||
margin: 5em 0;
|
||||
}
|
||||
|
||||
i.user-update {
|
||||
position: absolute;
|
||||
right: 7.5%;
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { UserForm } from './UserForm'
|
||||
import { UserFormPlaceholder } from './UserFormPlaceholder'
|
||||
import { api } from '../helpers'
|
||||
import { Row } from 'ui'
|
||||
import { YesNoMessageDialog, MessageDialog, ChangeEmailDialog, WaitDialog } from '../Dialog'
|
||||
import { YesNoMessageModal, MessageModal, ChangeEmailModal, WaitModal } from '../Modal'
|
||||
|
||||
export class Users extends React.Component {
|
||||
constructor() {
|
||||
@@ -14,10 +14,10 @@ export class Users extends React.Component {
|
||||
this.state = {
|
||||
selectedUser: null,
|
||||
users: [],
|
||||
yesNoDialog: null,
|
||||
messageDialog: null,
|
||||
waitDialog: null,
|
||||
changeEmailDialog: null
|
||||
yesNoModal: null,
|
||||
messageModal: null,
|
||||
waitModal: null,
|
||||
changeEmailModal: null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export class Users extends React.Component {
|
||||
this.setState({ users: list.items })
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
messageDialog: {
|
||||
messageModal: {
|
||||
error: true,
|
||||
title: 'User List Error',
|
||||
message: `Unable to get the list of users. ${error.message}`
|
||||
@@ -50,10 +50,10 @@ export class Users extends React.Component {
|
||||
if (this.state.modified) {
|
||||
this.nextSelectedUser = user
|
||||
this.setState({
|
||||
yesNoDialog: {
|
||||
yesNoModal: {
|
||||
title: 'User Modified',
|
||||
message: 'This user has been modified. Are you sure you would like to navigate away?',
|
||||
onDismiss: this.handleModifiedDialogDismiss
|
||||
onDismiss: this.handleModifiedModalDismiss
|
||||
}
|
||||
})
|
||||
} else {
|
||||
@@ -64,18 +64,18 @@ export class Users extends React.Component {
|
||||
|
||||
handleSave(user) {
|
||||
if (user._id) {
|
||||
this.setState({ waitDialog: { message: 'Updating User' } })
|
||||
this.setState({ waitModal: { message: 'Updating User' } })
|
||||
api.updateUser(user).then((updatedUser) => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
waitModal: null,
|
||||
users: this.state.users.map((user) => (user._id === updatedUser._id ? updatedUser : user)),
|
||||
modified: false,
|
||||
selectedUser: updatedUser
|
||||
})
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: true,
|
||||
title: 'Update Error',
|
||||
message: `Unable to save the user changes. ${error.message}`
|
||||
@@ -83,18 +83,18 @@ export class Users extends React.Component {
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.setState({ waitDialog: { message: 'Creating User' } })
|
||||
this.setState({ waitModal: { message: 'Creating User' } })
|
||||
api.createUser(user).then((createdUser) => {
|
||||
this.setState({
|
||||
waitDialog: false,
|
||||
waitModal: false,
|
||||
users: this.state.users.map((user) => (!user._id ? createdUser : user)),
|
||||
modified: false,
|
||||
selectedUser: createdUser
|
||||
})
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: true,
|
||||
title: 'Create Error',
|
||||
message: `Unable to create the user. ${error.message}`
|
||||
@@ -105,17 +105,17 @@ export class Users extends React.Component {
|
||||
}
|
||||
|
||||
handleChangeEmail() {
|
||||
this.setState({ changeEmailDialog: {} })
|
||||
this.setState({ changeEmailModal: {} })
|
||||
}
|
||||
|
||||
handleResendEmail() {
|
||||
this.setState({
|
||||
waitDialog: { message: 'Resending Email...' }
|
||||
waitModal: { message: 'Resending Email...' }
|
||||
})
|
||||
api.sendConfirmEmail({ existingEmail: this.state.selectedUser.email }).then(() => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: false,
|
||||
title: "We've Re-Sent the Email...",
|
||||
message: `An email has been sent to '${this.state.selectedUser.email}'. This user will need to follow that email's included link to verify their account.`
|
||||
@@ -124,8 +124,8 @@ export class Users extends React.Component {
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
error: true,
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: true,
|
||||
title: 'Email Change Error...',
|
||||
message: `Unable to request email change. ${error ? error.message : ''}`
|
||||
@@ -135,17 +135,17 @@ export class Users extends React.Component {
|
||||
}
|
||||
|
||||
handleChangeEmailDismiss(newEmail) {
|
||||
this.setState({ changeEmailDialog: null })
|
||||
this.setState({ changeEmailModal: null })
|
||||
if (!newEmail) {
|
||||
return
|
||||
}
|
||||
this.setState({
|
||||
waitDialog: { message: 'Requesting Email Change...' }
|
||||
waitModal: { message: 'Requesting Email Change...' }
|
||||
})
|
||||
api.sendConfirmEmail({ existingEmail: this.state.selectedUser.email, newEmail }).then(() => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: false,
|
||||
title: 'Email Change Requested...',
|
||||
message: `An email has been sent to '${newEmail}'. This user will need to follow that email's included link to finish changing their email.`
|
||||
@@ -154,8 +154,8 @@ export class Users extends React.Component {
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
error: true,
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: true,
|
||||
title: 'Email Change Error...',
|
||||
message: `Unable to request email change. ${error ? error.message : ''}`
|
||||
@@ -166,32 +166,32 @@ export class Users extends React.Component {
|
||||
|
||||
handleRemove() {
|
||||
this.setState({
|
||||
yesNoDialog: {
|
||||
yesNoModal: {
|
||||
title: 'Permanently Delete User?',
|
||||
message: 'You are about to delete this user from the system. This includes references to this user in Projects, Packages, and so on. Are you sure you want to remove this user?',
|
||||
onDismiss: this.handleRemoveDialogDismiss
|
||||
onDismiss: this.handleRemoveModalDismiss
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleRemoveDialogDismiss(yes) {
|
||||
handleRemoveModalDismiss(yes) {
|
||||
if (yes) {
|
||||
// TODO: Pass the _id back from the dialog input data
|
||||
const selectedUserId = this.state.selectedUser._id
|
||||
const selectedIndex = this.state.users.findIndex((user) => (user._id === selectedUserId))
|
||||
|
||||
if (selectedIndex >= 0) {
|
||||
this.setState({ waitDialog: { message: 'Removing User' } })
|
||||
this.setState({ waitModal: { message: 'Removing User' } })
|
||||
api.deleteUser(selectedUserId).then(() => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
waitModal: null,
|
||||
users: [...this.state.users.slice(0, selectedIndex), ...this.state.users.slice(selectedIndex + 1)],
|
||||
selectedUser: null
|
||||
})
|
||||
}).catch((error) => {
|
||||
this.setState({
|
||||
waitDialog: null,
|
||||
messageDialog: {
|
||||
waitModal: null,
|
||||
messageModal: {
|
||||
error: true,
|
||||
title: 'Remove Error',
|
||||
message: `Unable to remove the user. ${error.message}`
|
||||
@@ -202,11 +202,11 @@ export class Users extends React.Component {
|
||||
}
|
||||
|
||||
this.setState({
|
||||
yesNoDialog: null
|
||||
yesNoModal: null
|
||||
})
|
||||
}
|
||||
|
||||
handleModifiedDialogDismiss(yes) {
|
||||
handleModifiedModalDismiss(yes) {
|
||||
if (yes) {
|
||||
this.setState({
|
||||
selectedUser: this.nextSelectedUser,
|
||||
@@ -217,12 +217,12 @@ export class Users extends React.Component {
|
||||
}
|
||||
|
||||
this.setState({
|
||||
yesNoDialog: null
|
||||
yesNoModal: null
|
||||
})
|
||||
}
|
||||
|
||||
handleMessageDialogDismiss() {
|
||||
this.setState({ messageDialog: null })
|
||||
handleMessageModalDismiss() {
|
||||
this.setState({ messageModal: null })
|
||||
}
|
||||
|
||||
handleModifiedChanged(modified) {
|
||||
@@ -260,21 +260,21 @@ export class Users extends React.Component {
|
||||
</Row.Item>
|
||||
</Row>
|
||||
|
||||
<ChangeEmailDialog open={!!this.state.changeEmailDialog} onDismiss={this.handleChangeEmailDismiss} />
|
||||
<ChangeEmailModal open={!!this.state.changeEmailModal} onDismiss={this.handleChangeEmailDismiss} />
|
||||
|
||||
<YesNoMessageDialog open={!!this.state.yesNoDialog}
|
||||
title={this.state.yesNoDialog ? this.state.yesNoDialog.title : ''}
|
||||
message={this.state.yesNoDialog ? this.state.yesNoDialog.message : ''}
|
||||
onDismiss={this.state.yesNoDialog ? this.state.yesNoDialog.onDismiss : null} />
|
||||
<YesNoMessageModal open={!!this.state.yesNoModal}
|
||||
title={this.state.yesNoModal ? this.state.yesNoModal.title : ''}
|
||||
message={this.state.yesNoModal ? this.state.yesNoModal.message : ''}
|
||||
onDismiss={this.state.yesNoModal ? this.state.yesNoModal.onDismiss : null} />
|
||||
|
||||
<MessageDialog
|
||||
open={!!this.state.messageDialog}
|
||||
error={this.state.messageDialog ? this.state.messageDialog.error : false}
|
||||
title={this.state.messageDialog ? this.state.messageDialog.title : ''}
|
||||
message={this.state.messageDialog ? this.state.messageDialog.message : ''}
|
||||
onDismiss={this.handleMessageDialogDismiss} />
|
||||
<MessageModal
|
||||
open={!!this.state.messageModal}
|
||||
error={this.state.messageModal ? this.state.messageModal.error : false}
|
||||
title={this.state.messageModal ? this.state.messageModal.title : ''}
|
||||
message={this.state.messageModal ? this.state.messageModal.message : ''}
|
||||
onDismiss={this.handleMessageModalDismiss} />
|
||||
|
||||
<WaitDialog active={!!this.state.waitDialog} message={this.state.waitDialog ? this.state.waitDialog.message : ''} />
|
||||
<WaitModal active={!!this.state.waitModal} message={this.state.waitModal ? this.state.waitModal.message : ''} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Button } from 'ui'
|
||||
|
||||
export class ValidatedActionsButton extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
size: PropTypes.string,
|
||||
validator: PropTypes.object.isRequired,
|
||||
primary: PropTypes.bool,
|
||||
submit: PropTypes.bool,
|
||||
color: PropTypes.string,
|
||||
form: PropTypes.string,
|
||||
children: PropTypes.node
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.updateValue = this.updateValue.bind(this)
|
||||
|
||||
let { name, validator } = this.props
|
||||
|
||||
validator.addListener(name, this.updateValue)
|
||||
this.state = validator.getField(name)
|
||||
}
|
||||
|
||||
updateValue(name) {
|
||||
this.setState(this.props.validator.getField(name))
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.validator.removeListener(this.props.name, this.updateValue)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.validator !== this.props.validator) {
|
||||
this.props.validator.removeListener(this.props.name, this.updateValue)
|
||||
nextProps.validator.addListener(nextProps.name, this.updateValue)
|
||||
this.setState(nextProps.validator.getField(nextProps.name))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, size, color, primary, submit, name, form } = this.props
|
||||
|
||||
if (this.state.visible) {
|
||||
return (
|
||||
<Button color={color} primary={primary}
|
||||
disabled={this.state.disabled}
|
||||
type={submit ? 'submit' : 'button'}
|
||||
size={size} name={name} form={form}>
|
||||
{children}
|
||||
</Button>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Checkbox } from 'ui'
|
||||
|
||||
// This is an example of a validated component with a value that can change itself, that cannot ever be invalid.
|
||||
|
||||
export class ValidatedCheckbox extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
label: PropTypes.string,
|
||||
validator: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = props.validator.getField(props.name)
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
}
|
||||
|
||||
handleChange(e, data) {
|
||||
const { validator, name } = this.props
|
||||
const state = validator.getField(name)
|
||||
|
||||
if (!state.readOnly && !state.disabled) {
|
||||
this.setState(validator.updateValue(name, data.checked))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.validator !== this.props.validator) {
|
||||
this.setState(nextProps.validator.getField(nextProps.name))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Checkbox value={!!this.state.value} name={this.props.name} label={this.props.label} />
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
export class ValidatedContainer extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
validator: PropTypes.object.isRequired,
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.array,
|
||||
PropTypes.object
|
||||
])
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.updateValue = this.updateValue.bind(this)
|
||||
|
||||
let { name, validator } = this.props
|
||||
|
||||
validator.addListener(name, this.updateValue)
|
||||
this.state = validator.getField(name)
|
||||
}
|
||||
|
||||
updateValue(name) {
|
||||
this.setState(this.props.validator.getField(name))
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.validator.removeListener(this.props.name, this.updateValue)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.validator !== this.props.validator) {
|
||||
this.props.validator.removeListener(this.props.name, this.updateValue)
|
||||
nextProps.validator.addListener(nextProps.name, this.updateValue)
|
||||
this.setState(nextProps.validator.getField(nextProps.name))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.visible) {
|
||||
return (
|
||||
<div>
|
||||
{this.props.children}
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Dropdown, Text } from 'ui'
|
||||
|
||||
export class ValidatedDropdown extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
label: PropTypes.string,
|
||||
width: PropTypes.number,
|
||||
placeholder: PropTypes.string,
|
||||
options: PropTypes.array,
|
||||
message: PropTypes.string.isRequired,
|
||||
validator: PropTypes.object.isRequired,
|
||||
searchable: PropTypes.bool
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = props.validator.getField(props.name)
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
}
|
||||
|
||||
handleChange(e, data) {
|
||||
const { validator, name } = this.props
|
||||
|
||||
this.setState(validator.updateValue(name, data.value))
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.validator !== this.props.validator) {
|
||||
this.setState(nextProps.validator.getField(nextProps.name))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div error={!this.state.valid} width={this.props.width}>
|
||||
<label>{this.props.label}</label>
|
||||
<Dropdown selection disabled={this.state.disabled} readOnly={this.state.readOnly}
|
||||
placeholder={this.props.placeholder} options={this.props.options}
|
||||
value={this.state.value} onChange={this.handleChange} search={this.props.searchable} />
|
||||
<Text>{this.props.message}</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
import EventEmitter from 'eventemitter3'
|
||||
|
||||
const any = function(obj, pred) {
|
||||
for (let name in obj) {
|
||||
if (pred(obj[name])) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
export class Validator extends EventEmitter {
|
||||
constructor(originalObj, validations, onModifiedChanged) {
|
||||
super()
|
||||
this.updateValue = this.updateValue.bind(this)
|
||||
|
||||
this._id = originalObj._id
|
||||
this.onModifiedChanged = onModifiedChanged
|
||||
this.fields = {}
|
||||
this.nonValueFields = {}
|
||||
|
||||
for (let name in validations) {
|
||||
let validation = validations[name]
|
||||
let field = {
|
||||
isDisabled: this.normalize(validation.isDisabled, false),
|
||||
isReadOnly: this.normalize(validation.isReadOnly, false),
|
||||
isVisible: this.normalize(validation.isVisible, true),
|
||||
nonValue: validation.nonValue || false
|
||||
}
|
||||
|
||||
if (field.nonValue) {
|
||||
field.disabled = field.isDisabled(this)
|
||||
field.readOnly = field.isReadOnly(this)
|
||||
field.visible = field.isVisible(this)
|
||||
this.nonValueFields[name] = field
|
||||
} else {
|
||||
field.alwaysGet = validation.alwaysGet
|
||||
field.isValid = this.normalize(validation.isValid, true)
|
||||
field.initValue = (validation.initValue === undefined ? '' : validation.initValue)
|
||||
field.originalValue = Validator.getObjectValue(originalObj, name)
|
||||
this.updateFieldValue(field, field.originalValue || field.initValue)
|
||||
this.fields[name] = field
|
||||
}
|
||||
}
|
||||
|
||||
this.updateNonValueFields()
|
||||
}
|
||||
|
||||
normalize(obj, def) {
|
||||
return obj ? ((obj.constructor === Function) ? obj : () => (!!obj)) : () => (def)
|
||||
}
|
||||
|
||||
static stateSafeField(field) {
|
||||
return {
|
||||
value: field.value,
|
||||
modified: field.modified,
|
||||
valid: field.valid,
|
||||
disabled: field.disabled,
|
||||
readOnly: field.readOnly,
|
||||
visible: field.visible
|
||||
}
|
||||
}
|
||||
|
||||
updateValue(name, newValue) {
|
||||
let lastAnyModified = this.anyModified
|
||||
let field = this.fields[name]
|
||||
|
||||
if (field) {
|
||||
this.updateFieldValue(field, newValue)
|
||||
this.updateNonValueFields()
|
||||
if (lastAnyModified !== this.anyModified && this.onModifiedChanged) {
|
||||
this.onModifiedChanged(this.anyModified)
|
||||
}
|
||||
}
|
||||
|
||||
return Validator.stateSafeField(field)
|
||||
}
|
||||
|
||||
updateFieldValue(field, newValue) {
|
||||
field.value = newValue
|
||||
field.disabled = field.isDisabled(this)
|
||||
field.readOnly = field.isReadOnly(this)
|
||||
field.visible = field.isVisible(this)
|
||||
field.valid = field.isValid(this, newValue)
|
||||
field.modified =
|
||||
field.originalValue !== undefined ? (field.originalValue !== newValue) : (newValue !== field.initValue)
|
||||
|
||||
this.anyModified = field.modified || any(this.fields, (field) => (field.modified))
|
||||
this.allValid = !field.valid ? false : !any(this.fields, (field) => (!field.valid))
|
||||
}
|
||||
|
||||
updateNonValueFields() {
|
||||
for (let name in this.nonValueFields) {
|
||||
let field = this.nonValueFields[name]
|
||||
let disabled = field.isDisabled(this)
|
||||
let readOnly = field.isReadOnly(this)
|
||||
let visible = field.isVisible(this)
|
||||
let b = (disabled !== field.disabled || readOnly !== field.readOnly || visible !== field.visible)
|
||||
|
||||
field.disabled = disabled
|
||||
field.readOnly = readOnly
|
||||
field.visible = visible
|
||||
|
||||
if (b) {
|
||||
this.emit(name, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getField(name) {
|
||||
let field = this.fields[name] || this.nonValueFields[name]
|
||||
|
||||
if (!field) {
|
||||
throw new Error(`Field '${name}' does not have a validation entry`)
|
||||
}
|
||||
|
||||
return Validator.stateSafeField(field)
|
||||
}
|
||||
|
||||
getValues() {
|
||||
// Generate an object that has the modified and alwaysGet fields
|
||||
let obj = {}
|
||||
|
||||
if (!this.anyModified && !this.allValid) {
|
||||
return obj
|
||||
}
|
||||
|
||||
// Will have an _id if updating
|
||||
if (this._id) {
|
||||
obj._id = this._id
|
||||
}
|
||||
|
||||
for (let name in this.fields) {
|
||||
let field = this.fields[name]
|
||||
|
||||
if (field.alwaysGet || (!field.nonValue && field.modified)) {
|
||||
let value = field.value
|
||||
if (value && value.constructor === 'String') {
|
||||
value = value.trim()
|
||||
}
|
||||
Validator.setObjectValue(obj, name, value)
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
getOriginalValues() {
|
||||
// Generate an object that has the original values of all fields
|
||||
let obj = {}
|
||||
|
||||
if (this._id) {
|
||||
obj._id = this._id
|
||||
}
|
||||
|
||||
for (let name in this.fields) {
|
||||
let field = this.fields[name]
|
||||
|
||||
if (field.originalValue !== undefined) {
|
||||
Validator.setObjectValue(obj, name, field.originalValue)
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
static getObjectValue(obj, name) {
|
||||
name.split('.').forEach((namePart) => {
|
||||
if (obj) {
|
||||
obj = obj[namePart]
|
||||
}
|
||||
})
|
||||
return obj
|
||||
}
|
||||
|
||||
static setObjectValue(obj, name, value) {
|
||||
name.split('.').forEach((namePart, i, nameParts) => {
|
||||
if (i < nameParts.length - 1) {
|
||||
if (!obj[namePart]) {
|
||||
obj[namePart] = {}
|
||||
}
|
||||
obj = obj[namePart]
|
||||
} else {
|
||||
obj[namePart] = value
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
export { Validator } from './Validator'
|
||||
export { ValidatedInput } from './ValidatedInput'
|
||||
export { ValidatedButton } from './ValidatedButton'
|
||||
export { ValidatedActionsButton } from './ValidatedActionsButton'
|
||||
export { ValidatedDropdown } from './ValidatedDropdown'
|
||||
export { ValidatedCheckbox } from './ValidatedCheckbox'
|
||||
export { ValidatedContainer } from './ValidatedContainer'
|
||||
@@ -1,54 +1,9 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 320.002 320.002" style="enable-background:new 0 0 320.002 320.002;" xml:space="preserve">
|
||||
<g id="XMLID_6_">
|
||||
<path id="XMLID_7_" d="M51.213,175.001h173.785c8.284,0,15-6.716,15-15c0-8.284-6.716-15-15-15H51.213l19.394-19.394
|
||||
c5.858-5.858,5.858-15.355,0-21.213c-5.857-5.858-15.355-5.858-21.213,0L4.396,149.393c-0.351,0.351-0.683,0.719-0.997,1.103
|
||||
c-0.137,0.167-0.256,0.344-0.385,0.515c-0.165,0.22-0.335,0.435-0.488,0.664c-0.14,0.209-0.261,0.426-0.389,0.64
|
||||
c-0.123,0.206-0.252,0.407-0.365,0.619c-0.118,0.22-0.217,0.446-0.323,0.67c-0.104,0.219-0.213,0.435-0.306,0.659
|
||||
c-0.09,0.219-0.164,0.442-0.243,0.664c-0.087,0.24-0.179,0.477-0.253,0.722c-0.067,0.222-0.116,0.447-0.172,0.672
|
||||
c-0.063,0.249-0.133,0.497-0.183,0.751c-0.051,0.259-0.082,0.521-0.119,0.782c-0.032,0.223-0.075,0.443-0.097,0.669
|
||||
c-0.048,0.484-0.073,0.971-0.074,1.457c0,0.007-0.001,0.015-0.001,0.022c0,0.007,0.001,0.015,0.001,0.022
|
||||
c0.001,0.487,0.026,0.973,0.074,1.458c0.022,0.223,0.064,0.44,0.095,0.661c0.038,0.264,0.069,0.528,0.121,0.79
|
||||
c0.05,0.252,0.119,0.496,0.182,0.743c0.057,0.227,0.107,0.456,0.175,0.681c0.073,0.241,0.164,0.474,0.248,0.71
|
||||
c0.081,0.226,0.155,0.453,0.247,0.675c0.091,0.22,0.198,0.431,0.3,0.646c0.108,0.229,0.21,0.46,0.33,0.685
|
||||
c0.11,0.205,0.235,0.4,0.354,0.599c0.131,0.221,0.256,0.444,0.4,0.659c0.146,0.219,0.309,0.424,0.466,0.635
|
||||
c0.136,0.181,0.262,0.368,0.407,0.544c0.299,0.364,0.616,0.713,0.947,1.048c0.016,0.016,0.029,0.034,0.045,0.05l45,45.001
|
||||
c2.93,2.929,6.768,4.394,10.607,4.394c3.838-0.001,7.678-1.465,10.606-4.393c5.858-5.858,5.858-15.355,0.001-21.213L51.213,175.001
|
||||
z"/>
|
||||
<path id="XMLID_8_" d="M305.002,25h-190c-8.284,0-15,6.716-15,15v60c0,8.284,6.716,15,15,15s15-6.716,15-15V55h160v210.001h-160
|
||||
v-45.001c0-8.284-6.716-15-15-15s-15,6.716-15,15v60.001c0,8.284,6.716,15,15,15h190c8.284,0,15-6.716,15-15V40
|
||||
C320.002,31.716,313.286,25,305.002,25z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 129 129" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 129 129">
|
||||
<g>
|
||||
<g>
|
||||
<path d="m88.6,94.4c0.8,0.8 1.8,1.2 2.9,1.2s2.1-0.4 2.9-1.2l27-27c0.2-0.2 0.4-0.4 0.5-0.6 0,0 0.1-0.1 0.1-0.2 0.1-0.2 0.2-0.4 0.3-0.5 0-0.1 0-0.2 0.1-0.2 0.1-0.2 0.1-0.3 0.2-0.5 0.1-0.3 0.1-0.5 0.1-0.8 0-0.3 0-0.5-0.1-0.8 0-0.2-0.1-0.4-0.2-0.5 0-0.1 0-0.2-0.1-0.2-0.1-0.2-0.2-0.4-0.3-0.6 0,0 0-0.1-0.1-0.1-0.1-0.2-0.3-0.4-0.5-0.6l-27-27c-1.6-1.6-4.2-1.6-5.8,0-1.6,1.6-1.6,4.2 0,5.8l20,20h-71.1c-2.3,0-4.1,1.8-4.1,4.1 0,2.3 1.8,4.1 4.1,4.1h71.1l-20,20c-1.6,1.4-1.6,4 0,5.6z"/>
|
||||
<path d="m10.5,122.5h54c2.3,0 4.1-1.8 4.1-4.1v-40.3c0-2.3-1.8-4.1-4.1-4.1s-4.1,1.8-4.1,4.1v36.2h-45.8v-99.7h45.8v36.2c0,2.3 1.8,4.1 4.1,4.1s4.1-1.8 4.1-4.1v-40.3c0-2.3-1.8-4.1-4.1-4.1h-54c-2.3,0-4.1,1.8-4.1,4.1v107.9c0.1,2.3 1.9,4.1 4.1,4.1z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 965 B |
9
website/src/assets/icons/placeholder.svg
Normal file
9
website/src/assets/icons/placeholder.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 129 129" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 129 129">
|
||||
<g>
|
||||
<g>
|
||||
<path d="m64.5,6.4c-25.5,0-46.3,20.8-46.3,46.4-3.55271e-15,3.1 0.3,6.2 0.9,9.2 0,0.2 0.1,0.4 0.1,0.7 5.1,22.9 41,57.3 42.5,58.7 0.8,0.8 1.8,1.1 2.8,1.1 1,0 2-0.4 2.8-1.1 1.5-1.5 37.2-35.8 42.3-58.2 0-0.2 0.1-0.4 0.1-0.6 0.7-3.2 1-6.5 1-9.8 0.1-25.6-20.7-46.4-46.2-46.4zm37.2,54.7c0,0.2 0,0.1 0,0.3-3.8,16.6-28.7,42.8-37.2,51.3-8.5-8.5-33.5-34.7-37.3-51.7 0-0.2 0-0.3-0.1-0.5-0.5-2.6-0.8-5.2-0.8-7.8 0-21 17.1-38.2 38.1-38.2 21,0 38.2,17.1 38.2,38.2-1.42109e-14,2.9-0.3,5.7-0.9,8.4z"/>
|
||||
<path d="m64.5,32.5c-8.6,0-15.5,7-15.5,15.5s7,15.5 15.5,15.5 15.5-6.9 15.5-15.5-6.9-15.5-15.5-15.5zm0,22.9c-4.1,0-7.4-3.3-7.4-7.4s3.3-7.4 7.4-7.4c4.1,0 7.4,3.3 7.4,7.4s-3.3,7.4-7.4,7.4z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 918 B |
9
website/src/assets/icons/profile.svg
Normal file
9
website/src/assets/icons/profile.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 129 129" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 129 129">
|
||||
<g>
|
||||
<g>
|
||||
<path d="m64.3,71.6c18,0 32.6-14.6 32.6-32.6s-14.6-32.5-32.6-32.5-32.6,14.6-32.6,32.5 14.6,32.6 32.6,32.6zm0-56.6c13.2,0 24,10.8 24,24s-10.8,24-24,24-24-10.8-24-24 10.8-24 24-24z"/>
|
||||
<path d="m7.9,122.5h113.2c2.4,0 4.3-1.9 4.3-4.3 0-22.5-18.3-40.9-40.9-40.9h-40c-22.5,0-40.9,18.3-40.9,40.9-1.33227e-15,2.4 1.9,4.3 4.3,4.3zm36.6-36.6h40c16.4,0 29.9,12.2 32,28h-104c2.1-15.7 15.6-28 32-28z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 634 B |
@@ -1,55 +0,0 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 210 210" style="enable-background:new 0 0 210 210;" xml:space="preserve">
|
||||
<g id="XMLID_12_">
|
||||
<path id="XMLID_13_" d="M195,0h-20c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V15
|
||||
C210,6.716,203.284,0,195,0z"/>
|
||||
<path id="XMLID_14_" d="M115,0H95c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V15
|
||||
C130,6.716,123.284,0,115,0z"/>
|
||||
<path id="XMLID_15_" d="M35,0H15C6.716,0,0,6.716,0,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V15
|
||||
C50,6.716,43.284,0,35,0z"/>
|
||||
<path id="XMLID_16_" d="M195,160h-20c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15v-20
|
||||
C210,166.716,203.284,160,195,160z"/>
|
||||
<path id="XMLID_17_" d="M115,160H95c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15v-20
|
||||
C130,166.716,123.284,160,115,160z"/>
|
||||
<path id="XMLID_18_" d="M35,160H15c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15v-20
|
||||
C50,166.716,43.284,160,35,160z"/>
|
||||
<path id="XMLID_19_" d="M195,80h-20c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V95
|
||||
C210,86.716,203.284,80,195,80z"/>
|
||||
<path id="XMLID_20_" d="M115,80H95c-8.284,0-15,6.716-15,15v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V95
|
||||
C130,86.716,123.284,80,115,80z"/>
|
||||
<path id="XMLID_21_" d="M35,80H15C6.716,80,0,86.716,0,95v20c0,8.284,6.716,15,15,15h20c8.284,0,15-6.716,15-15V95
|
||||
C50,86.716,43.284,80,35,80z"/>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
<g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,15 +1,16 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Button } from 'ui'
|
||||
import { reactAutoBind } from 'auto-bind2'
|
||||
|
||||
export class ValidatedButton extends React.Component {
|
||||
export default class BoundButton extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
label: PropTypes.string,
|
||||
width: PropTypes.number,
|
||||
size: PropTypes.string,
|
||||
content: PropTypes.string,
|
||||
validator: PropTypes.object.isRequired,
|
||||
binder: PropTypes.object.isRequired,
|
||||
submit: PropTypes.bool,
|
||||
color: PropTypes.string,
|
||||
onClick: PropTypes.func
|
||||
@@ -17,27 +18,27 @@ export class ValidatedButton extends React.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.updateValue = this.updateValue.bind(this)
|
||||
reactAutoBind(this)
|
||||
|
||||
let { name, validator } = this.props
|
||||
let { name, binder } = this.props
|
||||
|
||||
validator.addListener(name, this.updateValue)
|
||||
this.state = validator.getField(name)
|
||||
binder.addListener(name, this.updateValue)
|
||||
this.state = binder.getFieldState(name)
|
||||
}
|
||||
|
||||
updateValue(name) {
|
||||
this.setState(this.props.validator.getField(name))
|
||||
this.setState(this.props.binder.getFieldState(name))
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.validator.removeListener(this.props.name, this.updateValue)
|
||||
this.props.binder.removeListener(this.props.name, this.updateValue)
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.validator !== this.props.validator) {
|
||||
this.props.validator.removeListener(this.props.name, this.updateValue)
|
||||
nextProps.validator.addListener(nextProps.name, this.updateValue)
|
||||
this.setState(nextProps.validator.getField(nextProps.name))
|
||||
if (nextProps.binder !== this.props.binder) {
|
||||
this.props.binder.removeListener(this.props.name, this.updateValue)
|
||||
nextProps.binder.addListener(nextProps.name, this.updateValue)
|
||||
this.setState(nextProps.binder.getFieldState(nextProps.name))
|
||||
}
|
||||
}
|
||||
|
||||
39
website/src/ui/BoundCheckbox.js
Normal file
39
website/src/ui/BoundCheckbox.js
Normal file
@@ -0,0 +1,39 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Checkbox } from 'ui'
|
||||
import { reactAutoBind } from 'auto-bind2'
|
||||
|
||||
export default class BoundCheckbox extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
label: PropTypes.string,
|
||||
binder: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
reactAutoBind(this)
|
||||
this.state = props.binder.getFieldState(props.name)
|
||||
}
|
||||
|
||||
handleChange(e, data) {
|
||||
const { binder, name } = this.props
|
||||
const state = binder.getFieldState(name)
|
||||
|
||||
if (!state.readOnly && !state.disabled) {
|
||||
this.setState(binder.updateFieldValue(name, data.checked))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.binder !== this.props.binder) {
|
||||
this.setState(nextProps.binder.getFieldState(nextProps.name))
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Checkbox value={!!this.state.value} name={this.props.name} label={this.props.label} />
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -2,24 +2,22 @@ import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Text, Button, Icon } from 'ui'
|
||||
|
||||
// This is a validated component with a value that cannot change itself and is specialized
|
||||
|
||||
export class ValidatedEmailIcon extends React.Component {
|
||||
export default class BoundEmailIcon extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string,
|
||||
validator: PropTypes.object,
|
||||
binder: PropTypes.object,
|
||||
width: PropTypes.number,
|
||||
onClick: PropTypes.func
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = props.validator.getField('emailValidated')
|
||||
this.state = props.binder.getField('emailValidated')
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.validator !== this.props.validator) {
|
||||
this.setState(nextProps.validator.getField(nextProps.name))
|
||||
if (nextProps.binder !== this.props.binder) {
|
||||
this.setState(nextProps.binder.getField(nextProps.name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,14 +26,14 @@ export class ValidatedEmailIcon extends React.Component {
|
||||
return (
|
||||
<div width={this.props.width}>
|
||||
<Text> </Text>
|
||||
<Icon name='mail' color='green' size='big' className='mail-validated-icon' />
|
||||
<Icon name='mail' color='green' size='big' />
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div width={this.props.width}>
|
||||
<Text> </Text>
|
||||
<Button fluid icon='mail outline' color='red' labelPosition='left'
|
||||
<Button icon='mail outline' color='red' labelPosition='left'
|
||||
content='Resend Email' onClick={this.props.onClick} disabled={this.state.disabled} />
|
||||
</div>
|
||||
)
|
||||
@@ -1,38 +1,36 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { Input, Text } from 'ui'
|
||||
import { reactAutoBind } from 'auto-bind2'
|
||||
|
||||
// This is an example of a validated component with a value that changes itself
|
||||
|
||||
export class ValidatedInput extends React.Component {
|
||||
export default class BoundInput extends React.Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
message: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
validator: PropTypes.object.isRequired,
|
||||
binder: PropTypes.object.isRequired,
|
||||
password: PropTypes.bool,
|
||||
placeholder: PropTypes.string
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = props.validator.getField(props.name)
|
||||
this.handleChange = this.handleChange.bind(this)
|
||||
reactAutoBind(this)
|
||||
this.state = props.binder.getFieldState(props.name)
|
||||
}
|
||||
|
||||
handleChange(e, data) {
|
||||
const { validator, name } = this.props
|
||||
const state = validator.getField(name)
|
||||
const { binder, name } = this.props
|
||||
const state = binder.getFieldState(name)
|
||||
|
||||
if (!state.readOnly && !state.disabled) {
|
||||
this.setState(validator.updateValue(name, data.value))
|
||||
this.setState(binder.updateFieldValue(name, data.value))
|
||||
}
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.validator !== this.props.validator) {
|
||||
this.setState(nextProps.validator.getField(nextProps.name))
|
||||
if (nextProps.binder !== this.props.binder) {
|
||||
this.setState(nextProps.binder.getFieldState(nextProps.name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ class Box extends Component {
|
||||
|
||||
return (
|
||||
<div style={[
|
||||
{ height: '100%', width: '100%' },
|
||||
color && { backgroundColor: color },
|
||||
border ? { border } : { borderTop, borderBottom, borderLeft, borderRight }]}>
|
||||
{children}
|
||||
|
||||
@@ -31,9 +31,9 @@ class Checkbox extends Component {
|
||||
</div>
|
||||
<span style={{
|
||||
verticalAlign: 'top',
|
||||
fontSize: fontInfo.size['medium'],
|
||||
fontSize: fontInfo.size.medium,
|
||||
fontFamily: fontInfo.family,
|
||||
color: fontInfo.color['normal'],
|
||||
color: fontInfo.color.normal,
|
||||
marginLeft: 10
|
||||
}}>{this.props.label}</span>
|
||||
</div>
|
||||
|
||||
@@ -12,7 +12,7 @@ class Column extends Component {
|
||||
const { children, minHeight } = this.props
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', minHeight, flexDirection: 'column' }}>{children}</div>
|
||||
<div style={{ width: '100%', display: 'flex', minHeight, flexDirection: 'column' }}>{children}</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -21,15 +21,21 @@ Column.Item = Radium(class StackLayoutItem extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.node,
|
||||
minHeight: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
|
||||
height: PropTypes.oneOfType([ PropTypes.string, PropTypes.number ]),
|
||||
paddingTop: PropTypes.number,
|
||||
paddingBottom: PropTypes.number,
|
||||
padding: PropTypes.number,
|
||||
grow: PropTypes.bool
|
||||
}
|
||||
|
||||
render() {
|
||||
const { children, grow, minHeight } = this.props
|
||||
const { children, grow, height, minHeight, padding, paddingTop, paddingBottom } = this.props
|
||||
const flexGrow = grow ? 1 : null
|
||||
|
||||
return (
|
||||
<div style={{ minHeight, flexGrow }}>{children}</div>
|
||||
<div style={{ height, minHeight, flexGrow, paddingTap: paddingTop || padding, paddingBottom: paddingBottom || padding }}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
25
website/src/ui/HeaderButton.js
Normal file
25
website/src/ui/HeaderButton.js
Normal file
@@ -0,0 +1,25 @@
|
||||
import Radium from 'radium'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { Component } from 'react'
|
||||
import style from './HeaderButton.style'
|
||||
import { Icon } from 'ui'
|
||||
|
||||
class HeaderButton extends Component {
|
||||
static propTypes = {
|
||||
icon: PropTypes.string,
|
||||
onClick: PropTypes.func,
|
||||
size: PropTypes.number
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onClick, icon, size } = this.props
|
||||
|
||||
return (
|
||||
<button type='button' style={[{ height: size, width: size }, style.base]} onClick={onClick}>
|
||||
<Icon name={icon} size={size} />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Radium(HeaderButton)
|
||||
15
website/src/ui/HeaderButton.style.js
Normal file
15
website/src/ui/HeaderButton.style.js
Normal file
@@ -0,0 +1,15 @@
|
||||
import { colorInfo } from './style'
|
||||
|
||||
export default {
|
||||
base: {
|
||||
background: colorInfo.headerButtonBackground,
|
||||
verticalAlign: 'middle',
|
||||
borderWidth: 0,
|
||||
padding: '0 0 0 0',
|
||||
outline: 'none',
|
||||
|
||||
':hover': {
|
||||
background: colorInfo.headerButtonBackgroundHover,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,32 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { sizeInfo } from './style'
|
||||
|
||||
// See https://www.flaticon.com/packs/web-button-compilation for more icons
|
||||
// See https://www.flaticon.com/packs/free-basic-ui-elements
|
||||
|
||||
export default class Icon extends Component {
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
size: PropTypes.number,
|
||||
margin: PropTypes.number
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
size: 50,
|
||||
margin: 5,
|
||||
name: 'shapes'
|
||||
size: 50
|
||||
}
|
||||
|
||||
static svgs = {
|
||||
logout: require('icons/logout.svg'),
|
||||
shapes: require('icons/shapes.svg')
|
||||
profile: require('icons/profile.svg'),
|
||||
placeholder: require('icons/placeholder.svg'),
|
||||
}
|
||||
|
||||
render() {
|
||||
return <img style={{ width: this.props.size, height: this.props.size, margin: this.props.margin }}
|
||||
src={Icon.svgs[this.props.name]} />
|
||||
let { size, name } = this.props
|
||||
let source = Icon.svgs[name] || Icon.svgs['placeholder']
|
||||
const margin = sizeInfo.iconMargin
|
||||
|
||||
size -= margin * 2
|
||||
|
||||
return <img style={{ width: size, height: size, margin }} src={source} />
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,24 @@
|
||||
import Radium from 'radium'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { Component } from 'react'
|
||||
import { sizeInfo } from './style'
|
||||
|
||||
class Image extends Component {
|
||||
static propTypes = {
|
||||
source: PropTypes.string,
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
margin: PropTypes.number
|
||||
}
|
||||
|
||||
render() {
|
||||
let { source, width, height } = this.props
|
||||
const margin = sizeInfo.imageMargin
|
||||
|
||||
width = width ? (width - margin * 2) : null
|
||||
height = height ? (height - margin * 2) : null
|
||||
|
||||
return (
|
||||
<img src={this.props.source} style={{ width: this.props.width, height: this.props.height, margin: this.props.margin }} />
|
||||
<img src={source} style={{ width, height, margin }} />
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class Row extends Component {
|
||||
const { children, minWidth } = this.props
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', minWidth, flexDirection: 'row' }}>{children}</div>
|
||||
<div style={{ height: '100%', display: 'flex', minWidth, flexDirection: 'row' }}>{children}</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export { default as Box } from './Box'
|
||||
export { default as Button } from './Button'
|
||||
export { default as HeaderButton } from './HeaderButton'
|
||||
export { default as Checkbox } from './Checkbox'
|
||||
export { default as Input } from './Input'
|
||||
export { default as Image } from './Image'
|
||||
@@ -13,3 +14,7 @@ export { default as Dimmer } from './Dimmer'
|
||||
export { default as Loader } from './Loader'
|
||||
export { default as Row } from './Row'
|
||||
export { default as Column } from './Column'
|
||||
export { default as BoundButton } from './BoundButton'
|
||||
export { default as BoundCheckbox } from './BoundCheckbox'
|
||||
export { default as BoundInput } from './BoundInput'
|
||||
export { default as BoundEmailIcon } from './BoundEmailIcon'
|
||||
|
||||
@@ -3,7 +3,16 @@ export const colorInfo = {
|
||||
alertText: '#FF0000',
|
||||
grayText: '#B0B0B0',
|
||||
buttonBackground: '#3498DB',
|
||||
buttonBackgroundHover: '#3CB0FD'
|
||||
buttonBackgroundHover: '#3CB0FD',
|
||||
headerButtonBackground: '#FAFAFA',
|
||||
headerButtonBackgroundHover: '#DADADA',
|
||||
}
|
||||
|
||||
export const sizeInfo = {
|
||||
headerHeight: 60,
|
||||
imageMargin: 5, // The margin around images
|
||||
iconMargin: 10, // The margin around icons
|
||||
headerBorderWidth: 1
|
||||
}
|
||||
|
||||
export const fontInfo = {
|
||||
|
||||
Reference in New Issue
Block a user