Merge Complete

This commit is contained in:
Luis Fernando Molina committed 2020-11-20 20:28:04 +01:00
1 parent 482ef7a81a
commit dabe23d505
6483 files changed

No files matched your search

@@ -0,0 +1,18 @@
import React from 'react'
import Typography from '@material-ui/core/Typography'
const BannerTopBar = ({
size,
backgroundUrl,
title,
text
}) => (
<div className={`bannerTopBar ${size}`} style={backgroundUrl ? { backgroundImage: `url(${backgroundUrl})` } : {}}>
<div className='caption'>
<Typography className='title' variant='h1' align='center'>{title}</Typography>
<Typography className='text' variant='h2' align='center'>{text}</Typography>
</div>
</div>
)
export default BannerTopBar
@@ -0,0 +1,24 @@
import React, { Component } from 'react'
import UserList from './UserList'
import BannerTopBar from './BannerTopBar'
class Browse extends Component {
render () {
return (
<React.Fragment>
<BannerTopBar
size='medium'
title='Users Directory'
text='Everyone involved in Advanced Algos is here.'
backgroundUrl='https://superalgos.org/img/photos/users.jpg'
/>
<div className='container'>
<UserList />
</div>
</React.Fragment>
)
}
}
export default Browse
@@ -0,0 +1,258 @@
import React, { Component } from 'react'
import {graphql, compose} from 'react-apollo'
import {getDescendentsQuery} from '../queries/queries'
// Materia UI
import { withStyles } from '@material-ui/core/styles'
import Card from '@material-ui/core/Card'
import CardActionArea from '@material-ui/core/CardActionArea'
import CardActions from '@material-ui/core/CardActions'
import CardContent from '@material-ui/core/CardContent'
import CardMedia from '@material-ui/core/CardMedia'
import Typography from '@material-ui/core/Typography'
import Grid from '@material-ui/core/Grid'
// Material UI Nestled Lists
import ListSubheader from '@material-ui/core/ListSubheader';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import Collapse from '@material-ui/core/Collapse';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import StarBorder from '@material-ui/icons/StarBorder';
// Images
import UserDefaultPicture from '../img/user-default-pic.jpg'
const styles = theme => ({
card: {
maxWidth: 300,
minWidth: 300,
paddingTop: '30'
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9,
marginTop: '30',
minWidth: 300
},
typography: {
maxWidth: 345,
minWidth: 300,
paddingTop: '30'
},
appBar: {
position: 'relative'
},
flex: {
flex: 1
},
grid: {
paddingTop: '30',
marginTop: 30
},
list: {
width: '80%',
marginLeft: '10%',
marginTop: 25
},
button: {
margin: theme.spacing.unit,
marginTop: theme.spacing.unit * 3
},
nested: {
paddingLeft: theme.spacing.unit * 4,
},
})
class DescendentsTree extends Component {
constructor (props) {
super(props)
this.state = {
id: '',
open: true
}
}
handleClick = () => {
this.setState(state => ({ open: !state.open }));
};
displayIcons(amount) {
switch (amount) {
case 1: return (
<div>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
</div>
)
case 2: return (
<div>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
</div>
)
case 3: return (
<div>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
<ListItemIcon>
<StarBorder />
</ListItemIcon>
</div>
)
default: return(<div/>)
}
}
displayNestedListItems(node, level) {
const { classes } = this.props
return node.descendents.map(descendent => {
return (
<div key={descendent.alias}>
<ListItem button className={classes.nested}>
{this.displayIcons(level)}
<Card className={classes.card}>
<CardActionArea>
<CardMedia
className={classes.media}
image={UserDefaultPicture}
title='User Profile'
/>
<CardContent>
<Typography gutterBottom variant='headline' component='h2'>
{descendent.alias}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Grid container justify='center' spacing={8}>
<Grid item />
</Grid>
</CardActions>
</Card>
</ListItem>
{this.displayNestedList(descendent, level)}
</div>
)
}
)
}
displayNestedList(descendent, level) {
const primaryText = "Children of " + descendent.alias
if (descendent.descendents !== undefined) {
if (descendent.descendents.length > 0) {
return(
<List component="div" disablePadding>
<ListItem button onClick={ () => {
this.setState(state => ({ [descendent.alias]: !state[descendent.alias] }));
}
}>
<ListItemText inset primary={primaryText} />
{this.state[descendent.alias] ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Collapse in={this.state[descendent.alias]} timeout="auto" unmountOnExit>
{this.displayNestedListItems(descendent, level + 1)}
</Collapse>
</List>
)
} else {
return (<div/>)
}
}
}
displayTopList () {
let data = this.props.data
const { classes } = this.props
if (data.loading) {
return (<div> Loading Descendents... </div>)
} else {
if (data.users_Descendents === undefined) {
return (<div> No Descendents to Display </div>)
} else {
return data.users_Descendents.map(descendent => {
return (
<div key={descendent.alias}>
<ListItem button>
{this.displayIcons(1)}
<Card className={classes.card}>
<CardActionArea>
<CardMedia
className={classes.media}
image={UserDefaultPicture}
title='User Profile'
/>
<CardContent>
<Typography gutterBottom variant='headline' component='h2'>
{descendent.alias}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Grid container justify='center' spacing={8}>
<Grid item />
</Grid>
</CardActions>
</Card>
</ListItem>
{this.displayNestedList(descendent, 1)}
</div>
)
})
}
}
}
render () {
const { classes } = this.props
return (
<div>
<List
component="nav"
subheader={<ListSubheader component="div"></ListSubheader>}
className={classes.list}
>
{this.displayTopList()}
</List>
</div>
)
}
}
export default compose(
graphql(getDescendentsQuery, { // What follows is the way to pass a parameter to a query.
options: (props) => {
return {
variables: {
name: 'getDescendentsQuery',
id: props.userId
}
}
}}),
withStyles(styles)
)(DescendentsTree) // This technique binds more than one query to a single component.
@@ -0,0 +1,41 @@
import React, { Component } from 'react'
import BannerTopBar from './BannerTopBar'
import { Typography } from '@material-ui/core'
class Home extends Component {
render () {
return (
<React.Fragment>
<BannerTopBar
size='big'
title='Users Module'
text='Responsible for all human users of the Advanced Algos Platform.'
backgroundUrl='https://superalgos.org/img/photos/users.jpg'
/>
<div className='homePage container'>
<Typography variant='h1' align='center' className='title'>Welcome to the Users Module!</Typography>
<Typography variant='h2' align='center' className='subtitle'>The human component of the Advanced Algos Ecosystem.</Typography>
<div className='column'>
<Typography align='justify'>
The Advanced Algos Ecosystem is made out of humans, financial beings and other entities such as teams.
</Typography>
<Typography align='justify'>
Humans are either <strong>users</strong> or <strong>guests</strong>. If you haven’t signed up yet or you are logged off, then you are acting as a guest, with limited features. Please sign-up or log-in for a richer experience.
</Typography>
</div>
<div className='column'>
<Typography align='justify'>
Users may have different roles, such as developer, trader, analyst, etc. Each of these roles usually have personalized features throughout the system.
</Typography>
<Typography align='justify'>
The Users Module allows you to handle all of your details as a user of the system as well as finding other people in the ecosystem.
</Typography>
</div>
</div>
</React.Fragment>
)
}
}
export default Home
@@ -0,0 +1,196 @@
import React, { Component } from 'react'
import {graphql, compose} from 'react-apollo'
import {getUserByAuthIdQuery} from '../queries/queries'
import { withStyles } from '@material-ui/core/styles'
import classNames from 'classnames'
import Button from '@material-ui/core/Button'
import IconButton from '@material-ui/core/IconButton'
// icons
import UserIcon from '@material-ui/icons/Person'
import { Link } from 'react-router-dom'
import ProfileSheetIcon from '@material-ui/icons/Create'
import ProfileImagesIcon from '@material-ui/icons/Wallpaper'
import YourReferrerIcon from '@material-ui/icons/AccessibilityNew'
import DescendentsIcon from '@material-ui/icons/DeviceHub'
const ProfileSheetLink = props => <Link to='/users/profile-sheet' {...props} />
const ProfileImagesLink = props => <Link to='/users/profile-images' {...props} />
const YourReferrerLink = props => <Link to='/users/referrer' {...props} />
const YourDescendentsLink = props => <Link to='/users/descendents' {...props} />
const UserLink = props => <Link to='/users/user' {...props} />
const styles = theme => ({
root: {
flexGrow: 1
},
flex: {
flexGrow: 1,
marginLeft: 30
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
cssRoot: {
color: '#FFFFFF',
backgroundColor: theme.palette.secondary,
'&:hover': {
backgroundColor: theme.palette.dark
},
whiteSpace: 'nowrap',
paddingRight: 2 * theme.spacing.unit,
paddingLeft: 2 * theme.spacing.unit
},
button: {
margin: theme.spacing.unit
},
leftIcon: {
marginRight: theme.spacing.unit
},
rightIcon: {
marginLeft: theme.spacing.unit
},
iconSmall: {
fontSize: 20
}
})
class LoggedInUser extends Component {
displayLoggedInUser () {
const user = this.props.data.users_UserByAuthId
const { classes, match } = this.props
if (user) {
let displayName = 'No Display Name'
if (
user.alias !== null &&
user.alias !== ''
) { displayName = user.alias }
if (
user.firstName !== null &&
user.firstName !== ''
) { displayName = user.firstName }
if (
user.firstName !== null &&
user.firstName !== '' &&
user.lastName !== null &&
user.lastName !== ''
) { displayName = user.firstName + ' ' + user.lastName }
let authArray = this.props.authId.split('|')
let identityProvider = authArray[0]
return (
<li className='menu-item-has-children'>
<Button
variant='text'
size='small'
className={classNames(classes.button, classes.cssRoot)}
title='Manage Your Profile'
component={UserLink}
to={`${match.url}/user`}>
<UserIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
Your Profile
</Button>
<ul>
<li>
<Button
variant='text'
size='small'
className={classNames(classes.button, classes.cssRoot)}
title='Profile Sheet'
component={ProfileSheetLink}
to={`${match.url}/profile-sheet`}>
<ProfileSheetIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
Profile Sheet
</Button>
</li>
<li>
<Button
variant='text'
size='small'
className={classNames(classes.button, classes.cssRoot)}
title='Profile Images'
component={ProfileImagesLink}
to={`${match.url}/profile-images`}>
<ProfileImagesIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
Profile Images
</Button>
</li>
<li>
<Button
variant='text'
size='small'
className={classNames(classes.button, classes.cssRoot)}
title='Your Referrer'
component={YourReferrerLink}
to={`${match.url}/referrer`}>
<YourReferrerIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
Your Referrer
</Button>
</li>
<li>
<Button
variant='text'
size='small'
className={classNames(classes.button, classes.cssRoot)}
title='Your Descendents'
component={YourDescendentsLink}
to={`${match.url}/descendents`}>
<DescendentsIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
Your Descendents
</Button>
</li>
</ul>
</li>
)
} else {
return (
<div />
)
}
}
componentDidMount () {
}
render () {
if (this.loggedInUserStored !== true) {
let user = this.props.data.users_UserByAuthId
if (user !== undefined) {
localStorage.setItem('loggedInUser', JSON.stringify(this.props.data.users_UserByAuthId))
this.loggedInUserStored = true
}
}
return (
<React.Fragment>
{this.displayLoggedInUser()}
</React.Fragment>
)
}
}
export default compose(
graphql(getUserByAuthIdQuery, { // What follows is the way to pass a parameter to a query.
options: (props) => {
return {
variables: {
authId: props.authId
}
}
}
}),
withStyles(styles)
)(LoggedInUser)
@@ -0,0 +1,42 @@
import React, { Component } from 'react'
import { compose } from 'recompose'
import { withStyles } from '@material-ui/core/styles'
import Paper from '@material-ui/core/Paper'
import Typography from '@material-ui/core/Typography'
const styles = theme => ({
root: {
width: '50%',
flexGrow: 1,
padding: 10,
marginLeft: '25%',
marginTop: '5%',
marginBottom: '10%'
},
typography: {
width: '80%',
marginLeft: '10%',
marginTop: 40,
marginBottom: 40
}
})
class ProfileImages extends Component {
render () {
const { classes } = this.props
return (
<React.Fragment>
<Paper className={classes.root}>
<Typography className={classes.typography} variant='h5' gutterBottom>
Comming Soon
</Typography>
</Paper>
</React.Fragment>
)
}
}
export default compose(
withStyles(styles)
)(ProfileImages)
@@ -0,0 +1,525 @@
import React, { Component } from 'react'
import {graphql, compose} from 'react-apollo'
import {getRolesQuery, updateUserMutation, getUsersQuery} from '../queries/queries'
// Material-ui
import { withStyles } from '@material-ui/core/styles'
import MenuItem from '@material-ui/core/MenuItem'
import TextField from '@material-ui/core/TextField'
import FormControl from '@material-ui/core/FormControl'
import InputLabel from '@material-ui/core/InputLabel'
import Input from '@material-ui/core/Input'
import Button from '@material-ui/core/Button'
import FormControlLabel from '@material-ui/core/FormControlLabel'
import Checkbox from '@material-ui/core/Checkbox'
import FormGroup from '@material-ui/core/FormGroup'
import Select from '@material-ui/core/Select'
import FormHelperText from '@material-ui/core/FormHelperText'
import Paper from '@material-ui/core/Paper'
import Grid from '@material-ui/core/Grid'
import Typography from '@material-ui/core/Typography'
// Images
import GithubLogo from '../img/github-logo.png'
import Auth0Logo from '../img/auth0-logo.png'
import AALogo from '../img/aa-logo.png'
const styles = theme => ({
root: {
width: '100%',
flexGrow: 1,
padding: 10,
marginTop: '5%',
marginBottom: '10%'
},
inputField: {
width: '80%',
marginLeft: '10%',
marginTop: 25
},
typography: {
width: '80%',
marginLeft: '10%',
marginTop: 40
},
checkbox: {
width: 150,
marginLeft: '0%',
marginTop: 20
},
selectEmpty: {
marginTop: theme.spacing.unit * 2
},
button: {
margin: theme.spacing.unit,
marginTop: theme.spacing.unit * 3
},
img: {
display: 'block',
marginTop: 20,
maxWidth: 120,
maxHeight: 24
}
})
class ProfileSheet extends Component {
constructor (props) {
super(props)
this.defaultValuesSet = false
this.state = {
id: '',
firstName: '',
middleName: '',
lastName: '',
bio: '',
email: '',
emailVerified: 0,
isDeveloper: 0,
isTrader: 0,
isDataAnalyst: 0,
roleId: '1',
firstNameError: false,
middleNameError: false,
lastNameError: false,
bioError: false,
updated: false
}
}
displayRoles () {
var data = this.props.getRolesQuery // When there is more than one query binded to a single componente 'data' is replaced by thename of the query given below at the binding operation.
if (data.loading) {
return (
<MenuItem><em>Loading roles</em></MenuItem>
)
} else {
return data.users_Roles.map(role => {
return (
<MenuItem key={role.id} value={role.id}>{ role.name }</MenuItem>
)
})
}
}
submitForm (e) {
e.preventDefault()
this.props.updateUserMutation({
variables: {
firstName: this.state.firstName,
middleName: this.state.middleName,
lastName: this.state.lastName,
bio: this.state.bio,
isDeveloper: this.state.isDeveloper,
isTrader: this.state.isTrader,
isDataAnalyst: this.state.isDataAnalyst,
roleId: this.state.roleId
},
refetchQueries: [{ query: getUsersQuery}] // This allow us to re run whatever queries are necesary after the mutation.
})
/* Before we are done, we need to update the state of the local storage. */
let user = JSON.parse(localStorage.getItem('loggedInUser'))
user.firstName = this.state.firstName
user.middleName = this.state.middleName
user.lastName = this.state.lastName
user.bio = this.state.bio
user.isDeveloper = this.state.isDeveloper
user.isTrader = this.state.isTrader
user.isDataAnalyst = this.state.isDataAnalyst
user.role.id = this.state.roleId
localStorage.setItem('loggedInUser', JSON.stringify(user))
this.setState({updated: true})
}
handleCheckBoxes (e) {
let fieldValue
if (e.target.checked === true) {
fieldValue = 1
} else {
fieldValue = 0
/* When the user unchecks one of the checkboxes, we need to make sure that that Role is not the one selected at the SELECT,
and if it is, then we need to automatically select another one */
switch (e.target.id) {
case 'isDeveloper':
if (this.state.roleId === '2') {
this.setState({ roleId: '1'})
}
break
case 'isTrader':
if (this.state.roleId === '3') {
this.setState({ roleId: '1'})
}
break
case 'isDataAnalyst':
if (this.state.roleId === '4') {
this.setState({ roleId: '1'})
}
break
default:
}
}
this.setState({ [e.target.id]: fieldValue})
}
handleSelect (e) {
/*
Whenever the select component changes its value, we need to activate the type of role selected.
To do so, we change the state of the corresponding type of role here.
*/
switch (e.target.value) {
case '2':
this.setState({ isDeveloper: 1})
break
case '3':
this.setState({ isTrader: 1})
break
case '4':
this.setState({ isDataAnalyst: 1})
break
default:
}
this.setState({ roleId: e.target.value })
}
handleTextField (e) {
/*
We will do some basic input validations here. If something is wrong we will turn on the error sign on the textfield.
*/
let value = e.target.value
switch (e.target.id) {
case 'firstName':
if (value.length > 30) {
this.setState({firstNameError: true})
} else {
this.setState({firstNameError: false, firstName: value})
}
break
case 'middleName':
if (value.length > 30) {
this.setState({middleNameError: true})
} else {
this.setState({middleNameError: false, middleName: value})
}
break
case 'lastName':
if (value.length > 50) {
this.setState({lastNameError: true})
} else {
this.setState({lastNameError: false, lastName: value})
}
break
case 'bio':
if (value.length > 250) {
this.setState({bioError: true})
} else {
this.setState({bioError: false, bio: value})
}
break
default:
}
}
rightCheckboxValue (stateValue) {
if (stateValue === 1) {
return true
} else { return false }
}
componentWillMount () {
if (this.defaultValuesSet === false) {
let authUser = JSON.parse(localStorage.getItem('user'))
let authId = authUser.authId
let userData = localStorage.getItem('loggedInUser')
if (userData === 'undefined' || userData === null) { return }
let user = JSON.parse(userData)
this.defaultValuesSet = true
/* To avoid console warning, we need to take care of the fields that are null. */
if (user.firstName === null) { user.firstName = '' }
if (user.middleName === null) { user.middleName = '' }
if (user.lastName === null) { user.lastName = '' }
if (user.bio === null) { user.bio = '' }
/* Now we are ready to set the initial state. */
this.setState({
id: user.id,
alias: user.alias,
email: user.email,
emailVerified: user.emailVerified,
firstName: user.firstName,
middleName: user.middleName,
lastName: user.lastName,
bio: user.bio,
isDeveloper: user.isDeveloper,
isTrader: user.isTrader,
isDataAnalyst: user.isDataAnalyst,
roleId: user.role.id,
authId: authId
})
}
}
displayIdentityProvider () {
const { classes } = this.props
let authId = this.state.authId
if (authId !== undefined && authId !== null) {
let authArray = this.state.authId.split('|')
let identityProvider = authArray[0]
switch (identityProvider) {
case 'github':
return (
<img className={classes.img} src={GithubLogo} alt='Github' />
)
break
case 'auth0':
return (
<img className={classes.img} src={Auth0Logo} alt='Auth0' />
)
break
default:
}
}
}
displayButtonTitle () {
if (this.state.updated === true) {
return 'Updated'
} else {
return 'Update'
}
}
addForm () {
const { classes } = this.props
return (
<Paper className={classes.root}>
<form onSubmit={this.submitForm.bind(this)}>
<Typography className={classes.typography} variant='h5' gutterBottom>
Profile Sheet
</Typography>
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
Use this form to control the information you keep at the Advanced Algos system about yourself.
</Typography>
<Grid container justify='center' >
<Grid item>
{this.displayIdentityProvider()}
</Grid>
</Grid>
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
This is your basic information we have gotten from the identity provider you used to sign up. This information can not be changed.
</Typography>
<TextField
id='alias'
type='text'
value={this.state.alias}
label='Alias'
className={classes.inputField}
disabled />
<TextField
id='email'
type='text'
value={this.state.email}
label='Email'
className={classes.inputField}
disabled />
<FormControlLabel
disabled
className={classes.inputField}
control={
<Checkbox
id='emailVerified'
checked={this.rightCheckboxValue(this.state.emailVerified)}
color='primary'
/>
}
label='Email Verified'
/>
<Grid container justify='center' >
<Grid item>
<img className={classes.img} src={AALogo} alt='Advanced Algos' />
</Grid>
</Grid>
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
Complete your profile with the following optional information. Providing your real name might help other users trust you more.
</Typography>
<TextField
error={this.state.firstNameError}
id='firstName'
type='text'
value={this.state.firstName}
label='First Name'
className={classes.inputField}
onChange={this.handleTextField.bind(this)}
/>
<TextField
error={this.state.middleNameError}
id='middleName'
type='text'
value={this.state.middleName}
label='Middle Name'
className={classes.inputField}
onChange={this.handleTextField.bind(this)}
/>
<TextField
error={this.state.lastNameError}
id='lastName'
type='text'
value={this.state.lastName}
label='Last Name'
className={classes.inputField}
onChange={this.handleTextField.bind(this)}
/>
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
If you wish, you can add a short paragraph with your Bio or anything you would like to communicate to whoever finds your user profile.
</Typography>
<TextField
error={this.state.bioError}
id='bio'
type='text'
value={this.state.bio}
label='Bio'
className={classes.inputField}
onChange={this.handleTextField.bind(this)}
/>
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
Check the following options to enable specialized tools designed for each role. You can allways come back and change these settings later.
</Typography>
<FormGroup row className={classes.inputField}>
<Grid container justify='center' >
<Grid item>
<FormControlLabel
className={classes.checkbox}
control={
<Checkbox
id='isDeveloper'
onChange={this.handleCheckBoxes.bind(this)}
checked={this.rightCheckboxValue(this.state.isDeveloper)}
color='secondary'
/>
}
label='Developer'
/>
</Grid>
<Grid item>
<FormControlLabel
className={classes.checkbox}
control={
<Checkbox
id='isTrader'
onChange={this.handleCheckBoxes.bind(this)}
checked={this.rightCheckboxValue(this.state.isTrader)}
color='secondary'
/>
}
label='Trader'
/>
</Grid>
<Grid item>
<FormControlLabel
className={classes.checkbox}
control={
<Checkbox
id='isDataAnalyst'
onChange={this.handleCheckBoxes.bind(this)}
checked={this.rightCheckboxValue(this.state.isDataAnalyst)}
color='secondary'
/>
}
label='Data Analyst'
/>
</Grid>
</Grid>
</FormGroup>
<p className={classes.typography}>Your current role determines how the system is going to optimize its user interface to best serves your current needs.</p>
<FormControl className={classes.inputField}>
<InputLabel shrink htmlFor='select'>
Current Role
</InputLabel>
<Select
id='select'
ref='select'
value={this.state.roleId}
onChange={this.handleSelect.bind(this)}
input={<Input name='Role' id='role-label-placeholder' />}
displayEmpty
name='select'
className={classes.selectEmpty}
>
{ this.displayRoles() }
</Select>
<FormHelperText>Select from the list your current role</FormHelperText>
</FormControl>
<Grid container justify='center' >
<Grid item>
<Button
variant='contained'
color='secondary'
className={classes.button}
onClick={this.submitForm.bind(this)}
disabled={this.state.updated}
>{this.displayButtonTitle()}</Button>
</Grid>
</Grid>
</form>
</Paper>
)
}
render () {
return (
<React.Fragment>
<div className='container'>
{this.addForm()}
</div>
</React.Fragment>
)
}
}
export default compose(
graphql(getRolesQuery, {name: 'getRolesQuery'}),
graphql(updateUserMutation, {name: 'updateUserMutation'}),
withStyles(styles)
)(ProfileSheet) // This technique binds more than one query to a single component.
@@ -0,0 +1,45 @@
import React, { Component } from 'react'
import UserSearch from './UserSearch'
import {compose} from 'react-apollo'
import Paper from '@material-ui/core/Paper'
import BannerTopBar from './BannerTopBar'
// Materia UI
import { withStyles } from '@material-ui/core/styles'
const styles = theme => ({
root: {
width: '100%',
flexGrow: 1,
padding: 10,
marginTop: '5%',
marginBottom: '10%'
}
})
class Search extends Component {
render () {
const { classes } = this.props
return (
<React.Fragment>
<BannerTopBar
size='medium'
title='Users Search'
text='Find anyone involved in Advanced Algos here.'
backgroundUrl='https://superalgos.org/img/photos/users.jpg'
/>
<div className='container'>
<Paper className={classes.root}>
<UserSearch />
</Paper>
</div>
</React.Fragment>
)
}
}
export default compose(
withStyles(styles)
)(Search)
@@ -0,0 +1,19 @@
import React from 'react'
import UserTabs from './UserTabs'
import BannerTopBar from './BannerTopBar'
const User = () => {
return (
<React.Fragment>
<BannerTopBar
size='medium'
title='Your Profile'
text='Manage your profile info, pictures and more.'
backgroundUrl='https://superalgos.org/img/photos/users.jpg'
/>
<UserTabs />
</React.Fragment>
)
}
export default User
@@ -0,0 +1,185 @@
import React, { Component } from 'react';
import {graphql, compose} from 'react-apollo';
import {getUsersQuery} from '../queries/queries';
// Materia UI
import { withStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import CardMedia from '@material-ui/core/CardMedia';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import Grid from '@material-ui/core/Grid';
import Paper from '@material-ui/core/Paper';
// Full screen dialog imports (still Material UI)
import PropTypes from 'prop-types';
import Dialog from '@material-ui/core/Dialog';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import IconButton from '@material-ui/core/IconButton';
import CloseIcon from '@material-ui/icons/Close';
import Slide from '@material-ui/core/Slide';
// components
import UserProfile from './UserProfile';
// Images
import UserDefaultPicture from '../img/user-default-pic.jpg'
const styles = theme => ({
root: {
paddingTop:30,
paddingBottom:30,
flexGrow: 1
},
card: {
maxWidth: 345,
minWidth: 300,
paddingTop:'30'
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9,
marginTop:'30',
minWidth: 300
},
typography: {
maxWidth: 345,
minWidth: 300,
paddingTop:'30'
},
appBar: {
position: 'relative',
},
flex: {
flex: 1,
},
grid: {
paddingTop: 30,
marginTop:'30'
},
});
function Transition(props) {
return <Slide direction="up" {...props} />;
}
class UserList extends Component {
constructor(props){
super(props);
this.state = {
selected: null,
open: false
}
}
handleClickOpen = () => {
this.setState({ open: true });
};
handleClose = () => {
this.setState({ open: false });
};
displayUsers(){
let data = this.props.getUsersQuery;
const { classes } = this.props;
if(data.loading){
return ( <div> Loading Users... </div>);
} else {
return data.users_Users.map(user => {
return (
<Grid key={user.id} item>
<Card className={classes.card} onClick={ (e) => {
this.setState({ selected: user.id});
}
}>
<CardActionArea>
<CardMedia
className={classes.media}
image={UserDefaultPicture}
title="User Profile"
/>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
{user.alias}
</Typography>
<Typography className={classes.typography} gutterBottom>
{user.bio}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Grid container justify="center" spacing={8}>
<Grid item>
<Button onClick={this.handleClickOpen}>User Profile</Button>
<Dialog
fullScreen
open={this.state.open}
onClose={this.handleClose}
TransitionComponent={Transition}
>
<AppBar className={classes.appBar}>
<Toolbar>
<IconButton color="inherit" onClick={this.handleClose} aria-label="Close">
<CloseIcon />
</IconButton>
<Typography variant="h6" color="inherit" className={classes.flex}>
User Profile
</Typography>
<Button color="inherit" onClick={this.handleClose}>
Close
</Button>
</Toolbar>
</AppBar>
<Paper className={classes.root}>
<UserProfile userId={this.state.selected}/>
</Paper>
</Dialog>
</Grid>
<Grid item>
<Button disabled color="primary">
Extended Profile
</Button>
</Grid>
</Grid>
</CardActions>
</Card>
</Grid>
)
});
}
}
render() {
const { classes } = this.props;
return (
<React.Fragment>
<Grid container justify="center" spacing={40} className={classes.root}>
{this.displayUsers()}
</Grid>
</React.Fragment>
);
}
}
UserList.propTypes = {
classes: PropTypes.object.isRequired,
};
export default compose(
graphql(getUsersQuery, {name: "getUsersQuery"}),
withStyles(styles)
) (UserList); // This technique binds more than one query to a single component.
@@ -0,0 +1,155 @@
import React, { Component } from 'react'
import {graphql, compose} from 'react-apollo'
import {getUserProfileQuery} from '../queries/queries'
// Materia UI
import { withStyles } from '@material-ui/core/styles'
import Grid from '@material-ui/core/Grid'
import Card from '@material-ui/core/Card'
import CardActionArea from '@material-ui/core/CardActionArea'
import CardContent from '@material-ui/core/CardContent'
import CardMedia from '@material-ui/core/CardMedia'
import Typography from '@material-ui/core/Typography'
import Divider from '@material-ui/core/Divider'
// Images
import UserDefaultPicture from '../img/user-default-pic.jpg'
const styles = theme => ({
root: {
flexGrow: 1,
padding: 60,
margin: 2
},
card: {
maxWidth: 500,
minWidth: 500,
paddingTop: '30'
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9,
marginTop: '30',
minWidth: '80%'
},
grid: {
paddingTop: '30',
marginTop: '30'
},
typography: {
width: '100%',
marginTop: 20,
marginBottom: 20
}
})
class UserProfile extends Component {
displayNames () {
const user = this.props.data.users_User
const { classes } = this.props
if (user.firstName !== null && user.lastName !== null) {
return (
<Typography className={classes.typography} gutterBottom variant='h5' component='h2'>
{ user.firstName + ' ' + user.lastName}
</Typography>
)
}
}
displayRole () {
const user = this.props.data.users_User
const { classes } = this.props
if (user.role.name !== null && user.role.name !== 'Not Defined') {
return (
<div>
<Divider />
<Typography className={classes.typography} align='left' gutterBottom variant='subtitle1' component='h2'>
{ user.role.name }
</Typography>
</div>
)
}
}
displayBio () {
const user = this.props.data.users_User
const { classes } = this.props
if (user.bio !== null && user.bio !== '') {
return (
<div>
<Divider />
<Typography className={classes.typography} component='p'>
{ user.bio }
</Typography>
</div>
)
}
}
displayUserProfile () {
const user = this.props.data.users_User
const { classes } = this.props
if (user) {
return (
<Grid key={user.id} item>
<Card className={classes.card} onClick={(e) => {
this.setState({ selected: user.id})
}
}>
<CardActionArea>
<CardMedia
className={classes.media}
image={UserDefaultPicture}
title='User Profile'
/>
<CardContent>
{this.displayNames()}
<Typography className={classes.typography} gutterBottom variant='h6' component='h3'>
{user.alias}
</Typography>
{this.displayRole()}
{this.displayBio()}
</CardContent>
</CardActionArea>
</Card>
</Grid>
)
} else {
return (<div>No User selected...</div>)
}
}
render () {
return (
<Grid container justify='center' spacing={24}>
{this.displayUserProfile()}
</Grid>
)
}
}
export default compose(
graphql(getUserProfileQuery, { // What follows is the way to pass a parameter to a query.
options: (props) => {
return {
variables: {
name: 'getUserProfileQuery',
id: props.userId
}
}
}}),
withStyles(styles)
)(UserProfile) // This technique binds more than one query to a single component.
@@ -0,0 +1,275 @@
import React, { Component } from 'react'
import {graphql, compose} from 'react-apollo'
import {getUsersBySearchFields} from '../queries/queries'
// Materia UI
import { withStyles } from '@material-ui/core/styles'
import Card from '@material-ui/core/Card'
import CardActionArea from '@material-ui/core/CardActionArea'
import CardActions from '@material-ui/core/CardActions'
import CardContent from '@material-ui/core/CardContent'
import CardMedia from '@material-ui/core/CardMedia'
import Button from '@material-ui/core/Button'
import Typography from '@material-ui/core/Typography'
import Grid from '@material-ui/core/Grid'
import TextField from '@material-ui/core/TextField'
// Images
import UserDefaultPicture from '../img/user-default-pic.jpg'
const styles = theme => ({
card: {
maxWidth: 150,
minWidth: 150,
paddingTop: '30',
marginBottom: 40
},
media: {
height: 0,
paddingTop: '56.25%', // 16:9,
marginTop: '30',
minWidth: 300
},
typography: {
maxWidth: 345,
minWidth: 300,
paddingTop: '30'
},
appBar: {
position: 'relative'
},
flex: {
flex: 1
},
grid: {
width: '80%',
marginLeft: '10%',
paddingTop: '30',
marginTop: 30
},
inputField: {
width: '80%',
marginLeft: '10%',
marginTop: 25
},
formTypography: {
width: '80%',
marginLeft: '10%',
marginTop: 40
},
button: {
margin: theme.spacing.unit,
marginTop: theme.spacing.unit * 3
}
})
class UserSearch extends Component {
constructor (props) {
super(props)
this.state = {
alias: '',
firstName: '',
middleName: '',
lastName: ''
}
}
submitForm (e) {
e.preventDefault()
this.props.getUsersBySearchFields.variables.alias = this.state.alias
this.props.getUsersBySearchFields.variables.firstName = this.state.firstName
this.props.getUsersBySearchFields.variables.middleName = this.state.middleName
this.props.getUsersBySearchFields.variables.lastName = this.state.lastName
this.props.getUsersBySearchFields.refetch({})
}
handleTextField (e) {
/*
We will do some basic input validations here. If something is wrong we will turn on the error sign on the textfield.
*/
let value = e.target.value
switch (e.target.id) {
case 'alias':
if (value.length > 30) {
this.setState({aliasError: true})
} else {
this.setState({aliasError: false, alias: value})
}
break
case 'firstName':
if (value.length > 30) {
this.setState({firstNameError: true})
} else {
this.setState({firstNameError: false, firstName: value})
}
break
case 'middleName':
if (value.length > 30) {
this.setState({middleNameError: true})
} else {
this.setState({middleNameError: false, middleName: value})
}
break
case 'lastName':
if (value.length > 50) {
this.setState({lastNameError: true})
} else {
this.setState({lastNameError: false, lastName: value})
}
break
default:
}
}
selectText () {
if (this.props.selectButton === true) {
return (<span>{this.props.selectText}</span>)
} else {
return (<span />)
}
}
selectButton (user) {
if (this.props.selectButton === true) {
return (
<Button id={user.id} ref={user.id} color='primary' onClick={() => {
this.props.onSelect(user) // This sends the user to the parent component
}}>
Select
</Button>
)
} else {
return (<div />)
}
}
displayUsers () {
let data = this.props.getUsersBySearchFields
const { classes } = this.props
if (data.loading) {
return (<div> Loading Users... </div>)
} else {
if (data.users_UsersSearch === undefined) {
return (<div> No Users to Display </div>)
} else {
return data.users_UsersSearch.map(user => {
return (
<Grid key={user.id} item>
<Card className={classes.card} onClick={(e) => {
this.setState({ selected: user.id})
}
}>
<CardActionArea>
<CardMedia
className={classes.media}
image={UserDefaultPicture}
title='User Profile'
/>
<CardContent>
<Typography gutterBottom component='h2'>
{user.alias}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Grid container justify='center' spacing={8}>
<Grid item>
{this.selectButton(user)}
</Grid>
</Grid>
</CardActions>
</Card>
</Grid>
)
})
}
}
}
componentDidMount () {
window.dispatchEvent(new Event('load')) // This is a workaround to solve the problem that the slider does not show up
}
render () {
const { classes } = this.props
return (
<div>
<form onSubmit={this.submitForm.bind(this)}>
<Typography className={classes.formTypography} variant='body1' gutterBottom align='left'>
Use any of these fileds to search for users. {this.selectText()}
</Typography>
<TextField
error={this.state.aliasError}
id='alias'
type='text'
value={this.state.alias}
label='Alias'
className={classes.inputField}
onChange={this.handleTextField.bind(this)}
/>
<TextField
error={this.state.firstNameError}
id='firstName'
type='text'
value={this.state.firstName}
label='First Name'
className={classes.inputField}
onChange={this.handleTextField.bind(this)}
/>
<TextField
error={this.state.middleNameError}
id='middleName'
type='text'
value={this.state.middleName}
label='Middle Name'
className={classes.inputField}
onChange={this.handleTextField.bind(this)}
/>
<TextField
error={this.state.lastNameError}
id='lastName'
type='text'
value={this.state.lastName}
label='Last Name'
className={classes.inputField}
onChange={this.handleTextField.bind(this)}
/>
<Grid container justify='center' >
<Grid item>
<Button variant='contained' color='secondary' className={classes.button} onClick={this.submitForm.bind(this)}>Search</Button>
</Grid>
</Grid>
</form>
<Grid container className={classes.grid} justify='center' spacing={24}>
{this.displayUsers()}
</Grid>
</div>
)
}
}
export default compose(
graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}),
withStyles(styles)
)(UserSearch) // This technique binds more than one query to a single component.
@@ -0,0 +1,86 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Tabs from '@material-ui/core/Tabs';
import Tab from '@material-ui/core/Tab';
import ProfileSheetIcon from '@material-ui/icons/Create';
import ProfileImagesIcon from '@material-ui/icons/Wallpaper';
import YourReferrerIcon from '@material-ui/icons/AccessibilityNew';
import DescendentsIcon from '@material-ui/icons/DeviceHub';
import Typography from '@material-ui/core/Typography';
// Components
import ProfileSheet from './ProfileSheet';
import ProfileImages from './ProfileImages';
import YourReferrer from './YourReferrer';
import YourDescendents from './YourDescendents';
function TabContainer(props) {
return (
<Typography component="div" style={{ padding: 8 * 3 }}>
{props.children}
</Typography>
);
}
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
};
const styles = theme => ({
root: {
flexGrow: 1,
width: '100%',
backgroundColor: theme.palette.background.paper,
padding: 0,
margin: 0,
},
});
class UserTabs extends React.Component {
state = {
value: 0
};
handleChange = (event, value) => {
this.setState({ value });
};
render() {
const { classes } = this.props;
const { value } = this.state;
return (
<div className={classes.root}>
<AppBar position="static" color="default">
<Tabs
value={value}
onChange={this.handleChange}
scrollable
scrollButtons="off"
indicatorColor="primary"
textColor="primary"
fullWidth
>
<Tab label="Profile Sheet" icon={<ProfileSheetIcon />} />
<Tab disabled label="Profile Images" icon={<ProfileImagesIcon />} />
<Tab label="Your Referrer" icon={<YourReferrerIcon />} />
<Tab label="Your Descendents" icon={<DescendentsIcon />} />
</Tabs>
</AppBar>
{value === 0 && <TabContainer><ProfileSheet/></TabContainer>}
{value === 1 && <TabContainer><ProfileImages/></TabContainer>}
{value === 2 && <TabContainer><YourReferrer/></TabContainer>}
{value === 3 && <TabContainer><YourDescendents/></TabContainer>}
</div>
);
}
}
UserTabs.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(UserTabs);
@@ -0,0 +1,96 @@
import React, { Component } from 'react'
import {compose} from 'react-apollo'
// Materia UI
import { withStyles } from '@material-ui/core/styles'
import Typography from '@material-ui/core/Typography'
import Grid from '@material-ui/core/Grid'
import Paper from '@material-ui/core/Paper'
// components
import DescendentsTree from './DescendentsTree'
const styles = theme => ({
root: {
width: '100%',
flexGrow: 1,
padding: 10,
marginTop: '5%',
marginBottom: '10%'
},
typography: {
width: '80%',
marginLeft: '10%',
marginTop: 40,
marginBottom: 40
},
grid: {
paddingTop: '30',
marginTop: 30
}
})
class YourDescendents extends Component {
constructor (props) {
super(props)
this.defaultValuesSet = false
this.state = {
id: ''
}
}
componentWillMount () {
if (this.defaultValuesSet === false) {
let userData = localStorage.getItem('loggedInUser')
if (userData === 'undefined') { return }
let user = JSON.parse(userData)
this.defaultValuesSet = true
/* Now we are ready to set the initial state. */
this.setState({
id: user.id
})
}
}
addForm () {
const { classes } = this.props
return (
<Paper className={classes.root}>
<Typography className={classes.typography} variant='h5' gutterBottom>
Your Decendents
</Typography>
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
These are your decendents within the project. Your children referred you as the one who brought them to the project, while
your grandchildren referred your children and so on. In order to be able to see your descendents, the users who you referred
must first point to you as your referrer.
</Typography>
<DescendentsTree userId={this.state.id} />
<Grid container className={classes.grid} justify='center' spacing={24} />
</Paper>
)
}
render () {
return (
<React.Fragment>
<div className='container'>
{this.addForm()}
</div>
</React.Fragment>
)
}
}
export default compose(
withStyles(styles)
)(YourDescendents) // This technique binds more than one query to a single component.
@@ -0,0 +1,154 @@
import React, { Component } from 'react'
import {graphql, compose} from 'react-apollo'
import {updateReferrerMutation} from '../queries/queries'
// components
import UserSearch from './UserSearch'
import UserProfile from './UserProfile'
// Material-ui
import { withStyles } from '@material-ui/core/styles'
import Paper from '@material-ui/core/Paper'
import Typography from '@material-ui/core/Typography'
const styles = theme => ({
root: {
width: '100%',
flexGrow: 1,
padding: 10,
marginTop: '5%',
marginBottom: '10%'
},
typography: {
width: '80%',
marginLeft: '10%',
marginTop: 40,
marginBottom: 40
},
button: {
margin: theme.spacing.unit,
marginTop: theme.spacing.unit * 3
},
inputField: {
width: '80%',
marginLeft: '10%',
marginTop: 25
}
})
class YourReferrer extends Component {
constructor (props) {
super(props)
this.defaultValuesSet = false
this.state = {
id: '',
referrerId: '',
firstNameError: false,
middleNameError: false,
lastNameError: false,
aliasError: false
}
}
userSelected (referrerUser) {
this.setState({referrerId: referrerUser.id})
this.props.updateReferrerMutation({
variables: {
referrerId: referrerUser.id
}
})
/* Before we are done, we need to update the state of the local storage. */
let user = JSON.parse(localStorage.getItem('loggedInUser'))
user.referrerId = referrerUser.id
localStorage.setItem('loggedInUser', JSON.stringify(user))
}
renderMode () {
const { classes } = this.props
if (this.state.referrerId !== null) {
return (
<div>
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
This is the user who introduced you to the project.
</Typography>
<UserProfile userId={this.state.referrerId} />
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
This information can not be changed.
</Typography>
</div>
)
} else {
return (
<div>
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
We know that a group often becomes more intelligent when the number of people in the group increases.
To incentivize members of the community to invite more people to join we have developed this referral
program in which you can specify who referred you to the project, and see the members who pointed at
you as their referrer. Later this information will be available at your user profile and add to your
overall reputation within the project.
</Typography>
<UserSearch selectButton onSelect={this.userSelected.bind(this)} selectText="After that press the 'Select' button on the desired user to set it as your referrer. Bear in mind that this action can not be undone." />
</div>
)
}
}
componentWillMount () {
if (this.defaultValuesSet === false) {
let userData = localStorage.getItem('loggedInUser')
if (userData === 'undefined') { return }
let user = JSON.parse(userData)
this.defaultValuesSet = true
/* Now we are ready to set the initial state. */
this.setState({
id: user.id,
referrerId: user.referrerId
})
}
}
addForm () {
const { classes } = this.props
return (
<Paper className={classes.root}>
<Typography className={classes.typography} variant='h5' gutterBottom>
Your Referrer
</Typography>
{this.renderMode()}
</Paper>
)
}
render () {
return (
<React.Fragment>
<div className='container'>
{this.addForm()}
</div>
</React.Fragment>
)
}
}
export default compose(
graphql(updateReferrerMutation, {name: 'updateReferrerMutation'}),
withStyles(styles)
)(YourReferrer) // This technique binds more than one query to a single component.
Binary file not shown.

After

Width:  |  Height:  |  Size: 801 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 472 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

@@ -0,0 +1,24 @@
import React from 'react'
import { Route, Switch } from 'react-router-dom'
import { MuiPickersUtilsProvider } from 'material-ui-pickers'
import LuxonUtils from 'material-ui-pickers/utils/luxon-utils'
import Home from './components/Home'
import User from './components/User'
import Browse from './components/Browse'
import Search from './components/Search'
const App = () => (
<MuiPickersUtilsProvider utils={LuxonUtils}>
<Switch>
<Route exact path='/users/' component={Home} />
<Route path='/users/user' component={User} />
<Route path='/users/browse' component={Browse} />
<Route path='/users/search' component={Search} />
</Switch>
</MuiPickersUtilsProvider>
)
export default App
@@ -0,0 +1,164 @@
import gql from 'graphql-tag'
const getUsersQuery = gql`
{
users_Users {
id
alias
firstName
middleName
lastName
bio
role {
id
name
}
}
}
`
const getUserProfileQuery = gql`
query($id: ID){
users_User (id: $id){
id
alias
firstName
middleName
lastName
bio
avatarHandle
role {
id
name
}
}
}
`
const getUserByAuthIdQuery = gql`
query($authId: String){
users_UserByAuthId (authId: $authId){
id
referrerId
alias
firstName
middleName
lastName
bio
email
emailVerified
isDeveloper
isDataAnalyst
isTrader
avatarHandle
avatarChangeDate
role {
id
}
}
}
`
const getUsersBySearchFields = gql`
query(
$alias: String,
$firstName: String,
$middleName: String,
$lastName: String
){
users_UsersSearch (alias: $alias, firstName: $firstName, middleName: $middleName, lastName: $lastName){
id
alias
firstName
middleName
lastName
}
}
`
const getDescendentsQuery = gql`
query(
$id: String
){
users_Descendents (id: $id){
id
alias
firstName
middleName
lastName
referrerId
descendents {
id
alias
firstName
middleName
lastName
referrerId
descendents {
id
alias
firstName
middleName
lastName
referrerId
}
}
}
}
`
const getRolesQuery = gql`
{
users_Roles {
id
name
}
}
`
const updateUserMutation = gql`
mutation(
$firstName:String,
$middleName:String,
$lastName:String,
$bio:String,
$isDeveloper:Int,
$isTrader:Int,
$isDataAnalyst:Int,
$roleId:String!
)
{
users_UpdateUser (
firstName:$firstName,
middleName: $middleName,
lastName: $lastName,
bio: $bio,
isDeveloper: $isDeveloper,
isTrader: $isTrader,
isDataAnalyst: $isDataAnalyst,
roleId: $roleId
)
{
id
alias
}
}
`
const updateReferrerMutation = gql`
mutation(
$referrerId:String!
)
{
users_UpdateUserReferrer (
referrerId:$referrerId
)
{
id
referrerId
}
}
`
export {
getUsersQuery,
getUserByAuthIdQuery,
getRolesQuery,
getUserProfileQuery,
getUsersBySearchFields,
getDescendentsQuery,
updateUserMutation,
updateReferrerMutation
}
@@ -0,0 +1,65 @@
import { createMuiTheme } from '@material-ui/core/styles'
export const theme = createMuiTheme({
palette: {
primary: { main: '#303036' }, // DARK.
secondary: { main: '#CC5835' } // RUSTED_RED.
},
typography: {
fontFamily: '"Saira","Saira Condenses", sans-serif'
},
overrides: {
Paper: {
root: {
backgroundColor: '#ffffff'
},
elevation: 0,
square: true
}
}
})
export const globalStyles = theme => ({
'@global': {
body: {
backgroundColor: theme.palette.common.white
}
},
appBar: {
position: 'relative'
},
toolbarTitle: {
flex: 1
},
layout: {
width: 'auto',
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(900 + theme.spacing.unit * 3 * 2)]: {
width: 900,
marginLeft: 'auto',
marginRight: 'auto'
}
},
heroContent: {
maxWidth: 600,
margin: '0 auto',
padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px`
},
cardHeader: {
backgroundColor: theme.palette.grey[200]
},
cardPricing: {
display: 'flex',
justifyContent: 'center',
alignItems: 'baseline',
marginBottom: theme.spacing.unit * 2
},
cardActions: {
[theme.breakpoints.up('sm')]: {
paddingBottom: theme.spacing.unit * 2
}
}
})
export default theme
@@ -0,0 +1,9 @@
const getItem = async name => window.localStorage.getItem(name)
const setItem = async (name, value) => window.localStorage.setItem(name, value)
const removeItem = async name => window.localStorage.removeItem(name)
export {
getItem,
setItem,
removeItem
}