From 47eefc710e2e4ba10f54a474648e5c2e9f22f146 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 15 Sep 2018 16:58:31 +0200 Subject: [PATCH 01/51] Uploading and showing pictures working well. --- client/package-lock.json | 71 ++++++++++++++++++++++ client/package.json | 2 + client/src/components/UserImages.js | 94 +++++++++++++++++++---------- 3 files changed, 135 insertions(+), 32 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 1aca4e3dc..830e87ede 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4628,6 +4628,11 @@ "schema-utils": "0.3.0" } }, + "file-type": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz", + "integrity": "sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ==" + }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -4647,6 +4652,44 @@ "resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz", "integrity": "sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g==" }, + "filestack-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/filestack-js/-/filestack-js-1.5.1.tgz", + "integrity": "sha512-NXYZhKl0JySNNpQNWtKdyMfQAiMw6VF8Mx+XWHGg+gh3By3ITjR0wTorjM02CVr/6Od/DXQCN1e+6np1Zex9Nw==", + "requires": { + "abab": "1.0.4", + "bowser": "1.9.4", + "file-type": "8.1.0", + "filestack-loader": "3.0.4", + "is-svg": "3.0.0", + "isutf8": "2.0.2", + "mime-types": "2.1.20", + "spark-md5": "3.0.0", + "superagent": "3.8.3", + "tcomb-validation": "3.4.1", + "tslib": "1.9.3" + }, + "dependencies": { + "is-svg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz", + "integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==", + "requires": { + "html-comment-regex": "1.1.1" + } + } + } + }, + "filestack-loader": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/filestack-loader/-/filestack-loader-3.0.4.tgz", + "integrity": "sha512-b6uOCWHd1gM0+5KBA1rA4qfEgTqyTr5umLM4bBWT4z98WUwxa6KzCiq+z0VnR4rN+NCx6kyZ/wLXjGcPU32TxQ==" + }, + "filestack-react": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/filestack-react/-/filestack-react-2.0.0.tgz", + "integrity": "sha512-+lOqPx2XYsvm3pJutjlDTyG8fdrJAefN0QLi8crJfwI1dJffOjK84dnQJHQ9aQzxF37A1cHI8ZbPscv7w6bRTA==" + }, "fill-range": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", @@ -6516,6 +6559,11 @@ "handlebars": "4.0.11" } }, + "isutf8": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/isutf8/-/isutf8-2.0.2.tgz", + "integrity": "sha512-9lNbG6k+PE6vuFnPTHjhtX79/LDMQrXoFx1MOEjXXCyqFCSahWKAzuAb9G5ct+lfS12aQxNmC0tiZuyujvr4Uw==" + }, "iterall": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", @@ -10805,6 +10853,11 @@ "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" }, + "spark-md5": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.0.tgz", + "integrity": "sha1-NyIifFTi+vJLHcbZM8wUTm9xv+8=" + }, "spdx-correct": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz", @@ -11170,6 +11223,19 @@ "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz", "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI=" }, + "tcomb": { + "version": "3.2.27", + "resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.27.tgz", + "integrity": "sha512-XWdJW7F/M3YzXhDEUP8ycmNWoYymBtsHwCHoda0YF44RthJsls95TqDrmpAlC1sB/KXaCvkdBlcNRq+AaV6klA==" + }, + "tcomb-validation": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tcomb-validation/-/tcomb-validation-3.4.1.tgz", + "integrity": "sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==", + "requires": { + "tcomb": "3.2.27" + } + }, "term-size": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", @@ -11340,6 +11406,11 @@ "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==" + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", diff --git a/client/package.json b/client/package.json index 5b58271ac..0bd9cf86c 100644 --- a/client/package.json +++ b/client/package.json @@ -12,6 +12,8 @@ "apollo-link-error": "^1.1.0", "apollo-link-http": "^1.5.4", "auth0-lock": "^11.9.0", + "filestack-js": "^1.5.1", + "filestack-react": "^2.0.0", "graphql": "^14.0.0", "graphql-tag": "^2.9.2", "react": "^16.4.2", diff --git a/client/src/components/UserImages.js b/client/src/components/UserImages.js index 559d91787..ba1b2609c 100644 --- a/client/src/components/UserImages.js +++ b/client/src/components/UserImages.js @@ -1,48 +1,78 @@ import React from 'react'; +import {graphql, compose} from 'react-apollo' +import {getRolesQuery, updateUserMutation, getUsersQuery} from '../queries/queries' + +import ReactFilestack from 'filestack-react'; + +// Material-ui + +import { withStyles } from '@material-ui/core/styles' import Button from '@material-ui/core/Button'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogTitle from '@material-ui/core/DialogTitle'; +import Grid from '@material-ui/core/Grid' + +const options = { + accept: 'image/*', + maxFiles: 5, + storeTo: { + location: 's3', + }, +}; + +const styles = theme => ({ + + button: { + margin: theme.spacing.unit, + marginTop: theme.spacing.unit * 3 + }, +}) class UserImages extends React.Component { - state = { - open: true, - }; - handleClickOpen = () => { - this.setState({ open: true }); - }; + constructor (props) { + super(props) + this.state = { + avatarHandle: '' + } + } + + onSuccess = (uploadResults) => { + console.log('onSuccess') + console.log(uploadResults) - handleClose = () => { - this.setState({ open: false }); - }; + const handle = uploadResults.filesUploaded[0].handle + this.setState({avatarHandle: handle}) + } + + onError = () => { + console.log('onError') + } render() { + const { classes } = this.props return (
- - {"Section under development."} - - - The functionality of this section has not been developed yet. We expect to have it for October 2018. Thanks for your understanding. - - - - - - + +
); } } -export default UserImages; +export default compose( + graphql(getRolesQuery, {name: 'getRolesQuery'}), + graphql(updateUserMutation, {name: 'updateUserMutation'}), + withStyles(styles) +)(UserImages) // This technique binds more than one query to a single component. From f2cb82ad51590ab0aee3b0249a7edec44e128c3d Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Sat, 15 Sep 2018 20:43:50 +0200 Subject: [PATCH 02/51] Image Picker Options Configured --- client/src/components/UserImages.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/client/src/components/UserImages.js b/client/src/components/UserImages.js index ba1b2609c..d886c413f 100644 --- a/client/src/components/UserImages.js +++ b/client/src/components/UserImages.js @@ -11,8 +11,10 @@ import Button from '@material-ui/core/Button'; import Grid from '@material-ui/core/Grid' const options = { - accept: 'image/*', - maxFiles: 5, + accept: ['image/jpeg', 'image/jpg', 'image/png'], + maxFiles: 1, + imageDim: [300, 300], + maxSize: 0.25 * 1024 * 1024, // 1/4 of 1 Mb storeTo: { location: 's3', }, @@ -52,8 +54,7 @@ class UserImages extends React.Component { return (
- - 1 + 2 -
+ + + This is your current Avatar. If you want, you can choose a new one by picking an image from your computer, camera or the web, and crop it with the following tool. + + +
+ + + + + + +
+ + + This is how your new avatar will look like. Press save to proceed with the change. + + + + + + + + + + + + + + + + ); } } export default compose( - graphql(updateUserMutation, {name: 'updateUserMutation'}), + graphql(updateUserImagesMutation, {name: 'updateUserImagesMutation'}), withStyles(styles) )(UserImages) // This technique binds more than one query to a single component. diff --git a/client/src/queries/queries.js b/client/src/queries/queries.js index d4571f70c..b3cf128c3 100644 --- a/client/src/queries/queries.js +++ b/client/src/queries/queries.js @@ -97,4 +97,23 @@ mutation( } } ` -export {getUsersQuery, getUserByAuthIdQuery, getRolesQuery, updateUserMutation, getUserProfileQuery} + +const updateUserImagesMutation = gql` +mutation( + $id: ID!, + $avatarHandle:String, + $avatarChangeDate:String +) + { + updateUser ( + id: $id, + avatarHandle:$avatarHandle, + avatarChangeDate: $avatarChangeDate + ) + { + id + alias + } +} +` +export {getUsersQuery, getUserByAuthIdQuery, getRolesQuery, getUserProfileQuery, updateUserMutation, updateUserImagesMutation} diff --git a/server/schema/schema.js b/server/schema/schema.js index a9dab9416..2c37ba96d 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -179,8 +179,6 @@ const Mutation = new GraphQLObjectType({ isDeveloper: {type: GraphQLInt}, isTrader: {type: GraphQLInt}, isDataAnalyst: {type: GraphQLInt}, - avatarHandle: {type: GraphQLString}, - avatarChangeDate: {type: GraphQLString}, roleId: {type: new GraphQLNonNull(GraphQLString)} }, resolve (parent, args) { @@ -196,11 +194,29 @@ const Mutation = new GraphQLObjectType({ isDeveloper: args.isDeveloper, isTrader: args.isTrader, isDataAnalyst: args.isDataAnalyst, - avatarHandle: args.avatarHandle, - avatarChangeDate: args.avatarChangeDate, roleId: args.roleId } + return User.update(key, updatedUser) + } + }, + updateUserImages: { + type: UserType, + args: { + id: {type: new GraphQLNonNull(GraphQLID)}, + avatarHandle: {type: GraphQLString}, + avatarChangeDate: {type: GraphQLString} + }, + resolve (parent, args) { + let key = { + _id: args.id + } + + let updatedUser = { + avatarHandle: args.avatarHandle, + avatarChangeDate: args.avatarChangeDate + } + return User.update(key, updatedUser) } } From 7424770039742493776734d4421ddfa211f2f049 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 17 Sep 2018 13:47:09 +0200 Subject: [PATCH 06/51] Replacing Avatar works well. Missing> 1) check change date, 2) deleting old at file stack --- client/src/components/UserImages.js | 131 ++++++++++++++++++++-------- client/src/components/UserTabs.js | 4 +- client/src/queries/queries.js | 2 +- 3 files changed, 98 insertions(+), 39 deletions(-) diff --git a/client/src/components/UserImages.js b/client/src/components/UserImages.js index 99c81a490..99e78232a 100644 --- a/client/src/components/UserImages.js +++ b/client/src/components/UserImages.js @@ -5,7 +5,6 @@ import Avatar from '@material-ui/core/Avatar'; import classNames from 'classnames'; import ReactFilestack from 'filestack-react' -import Portrait from '../img/portrait.jpg' // Material-ui @@ -53,10 +52,6 @@ const styles = theme => ({ width: '80%', marginLeft: '10%', marginTop: 40 - }, - button: { - margin: theme.spacing.unit, - marginTop: theme.spacing.unit * 3 } }) @@ -69,7 +64,11 @@ class UserImages extends React.Component { this.state = { avatarHandle: '', - avatarChangeDate: '' + avatarChangeDate: '', + newAvatarHandle: '', + saveDisabled: true, + uploadToolDisabled: false, + showNewAvatar: false } } @@ -78,17 +77,19 @@ class UserImages extends React.Component { this.props.updateUserImagesMutation({ variables: { id: this.state.id, - avatarHandle: this.state.avatarHandle, + avatarHandle: this.state.newAvatarHandle, avatarChangeDate: this.state.avatarChangeDate }, 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. */ + this.setState({saveDisabled: true}) + + /* Before we are done, we need to update the state of the local storage. */ let user = JSON.parse(localStorage.getItem('loggedInUser')) - user.avatarHandle = this.state.avatarHandle + user.avatarHandle = this.state.newAvatarHandle user.avatarChangeDate = this.state.avatarChangeDate localStorage.setItem('loggedInUser', JSON.stringify(user)) @@ -123,14 +124,82 @@ class UserImages extends React.Component { console.log(uploadResults) const handle = uploadResults.filesUploaded[0].handle - const currentDate = (new Date()).toString(); - this.setState({avatarHandle: handle, avatarChangeDate: currentDate}) + + let localDate = new Date() + let currentDate = new Date(Date.UTC( + localDate.getUTCFullYear(), + localDate.getUTCMonth(), + localDate.getUTCDate(), + localDate.getUTCHours(), + localDate.getUTCMinutes(), + localDate.getUTCSeconds(), + localDate.getUTCMilliseconds()) + ) + + currentDate.toISOString() + + this.setState( + { + newAvatarHandle: handle, + avatarChangeDate: currentDate, + showNewAvatar: true, + uploadToolDisabled: true, + saveDisabled: false + } + ) } onError = () => { console.log('onError') } + + showCurrentAvatar() { + + const { classes } = this.props + return ( +
+ + This is your current Avatar: + + + + + + + +
+ ) + + } + + showNewAvatar() { + if (this.state.showNewAvatar === true) { + const { classes } = this.props + return ( +
+ + This is how your new avatar will look like. Press save to proceed with the change. + + + + + + + +
+ ) + } + } + render() { const { classes } = this.props return ( @@ -140,25 +209,21 @@ class UserImages extends React.Component {
- - - - - + {this.showCurrentAvatar()} - This is your current Avatar. If you want, you can choose a new one by picking an image from your computer, camera or the web, and crop it with the following tool. + If you want, you can choose a new one by picking an image from your computer, camera or the web, and crop it with the following tool.
-
- - This is how your new avatar will look like. Press save to proceed with the change. - + {this.showNewAvatar()} - - - - - - - +
diff --git a/client/src/components/UserTabs.js b/client/src/components/UserTabs.js index 32a5d258c..3722275a8 100644 --- a/client/src/components/UserTabs.js +++ b/client/src/components/UserTabs.js @@ -59,8 +59,8 @@ class UserTabs extends React.Component { indicatorColor="primary" textColor="primary" > - } /> - } /> + } /> + } /> {value === 0 && } diff --git a/client/src/queries/queries.js b/client/src/queries/queries.js index b3cf128c3..344958d63 100644 --- a/client/src/queries/queries.js +++ b/client/src/queries/queries.js @@ -105,7 +105,7 @@ mutation( $avatarChangeDate:String ) { - updateUser ( + updateUserImages ( id: $id, avatarHandle:$avatarHandle, avatarChangeDate: $avatarChangeDate From 6c66df280bf11b8f31fb9e707cd50557a1a40615 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Mon, 17 Sep 2018 16:28:20 +0200 Subject: [PATCH 07/51] Checking that the Avatar can not be replaced in 24 hs --- client/src/components/UserImages.js | 67 +++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/client/src/components/UserImages.js b/client/src/components/UserImages.js index 99e78232a..57f613cfa 100644 --- a/client/src/components/UserImages.js +++ b/client/src/components/UserImages.js @@ -68,7 +68,8 @@ class UserImages extends React.Component { newAvatarHandle: '', saveDisabled: true, uploadToolDisabled: false, - showNewAvatar: false + showNewAvatar: false, + needToWait: false } } @@ -109,6 +110,25 @@ class UserImages extends React.Component { if (user.avatarHandle === null) { user.avatarHandle = '' } if (user.avatarChangeDate === null) { user.avatarChangeDate = '' } + /* Here we will check that the user does not change the avatar more often than once a day. */ + + let avatarChangeDate = (new Date(user.avatarChangeDate)).valueOf() + let now = (new Date()).valueOf() + let timePassed = now - avatarChangeDate + let oneDay = 1000 * 60 * 60 * 24 + +console.log("dates", avatarChangeDate, now, timePassed, oneDay) + + if (timePassed < oneDay) { + this.setState({ + needToWait: true + }) + } else { + this.setState({ + needToWait: false + }) + } + /* Now we are ready to set the initial state. */ this.setState({ @@ -200,25 +220,24 @@ class UserImages extends React.Component { } } - render() { + showUploadTool() { const { classes } = this.props - return ( - - - Profile Images - -
- - {this.showCurrentAvatar()} - + if (this.state.needToWait === true) { + return ( +
+ + You updated your Avatar in the last 24 hours. As a protection against denial of service attacks, the system does not allow users to update their Avatar in less than 24 hs since their last update. Please come back tomorrow. + +
+ ) + } else { + return( +
If you want, you can choose a new one by picking an image from your computer, camera or the web, and crop it with the following tool. - - -
+ - - +
+ ) + } + } + render() { + const { classes } = this.props + return ( + + + Profile Images + + + + {this.showCurrentAvatar()} + {this.showUploadTool()} {this.showNewAvatar()} - + + + + +
Profile
+
+ + +
Logout
+
+
+ + ) + } + + handleMenu (e) { + this.setState({ menu: e.currentTarget }) + } + + handleClose () { + this.setState({ menu: null }) + } + + handleLogout () { + this.props.auth.logout() + this.setState({ menu: null }) + } +} + +LoggedInUserMenu.propTypes = { + menuLabel: PropTypes.string.isRequired, + auth: PropTypes.object.isRequired +} + +export default LoggedInUserMenu diff --git a/client/src/views/navbar/LoggedOut.js b/client/src/views/navbar/LoggedOut.js new file mode 100644 index 000000000..ceb40ac28 --- /dev/null +++ b/client/src/views/navbar/LoggedOut.js @@ -0,0 +1,15 @@ +import React from 'react' +import PropTypes from 'prop-types' +import Button from '@material-ui/core/Button' + +export const LoggedOut = ({ auth }) => ( +
auth.login()}> + +
+) + +LoggedOut.propTypes = { + auth: PropTypes.object.isRequired +} + +export default LoggedOut // This binds the querty to the component diff --git a/client/src/views/navbar/index.js b/client/src/views/navbar/index.js new file mode 100644 index 000000000..f144156e0 --- /dev/null +++ b/client/src/views/navbar/index.js @@ -0,0 +1,141 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' + +import { withStyles } from '@material-ui/core/styles' +import AppBar from '@material-ui/core/AppBar' +import Toolbar from '@material-ui/core/Toolbar' +import Typography from '@material-ui/core/Typography' +import IconButton from '@material-ui/core/IconButton' +import Button from '@material-ui/core/Button' + +// icons +import HomeIcon from '@material-ui/icons/Home' +import DashboardIcon from '@material-ui/icons/Dashboard' + +import { Link } from 'react-router-dom' + +import { getItem } from '../../utils/local-storage' + +// components +import { LoggedIn } from './LoggedIn' +import { LoggedOut } from './LoggedOut' + +import AALogo from '../../assets/logos/advanced-algos/aa-logo-dark.svg' + +const AboutLink = props => +const TeamsLink = props => +const DashboardLink = props => +const HomeLink = props => + +const styles = theme => ({ + '@global': { + body: { + backgroundColor: theme.palette.common.white + } + }, + root: { + flexGrow: 1 + }, + colorDefault: { + color: '#000' + }, + flex: { + flexGrow: 1 + }, + toolbarTitle: { + flex: 1 + }, + menuButton: { + marginLeft: -12, + marginRight: 20 + }, + appBar: { + position: 'relative' + }, + img: { + margin: 20, + display: 'block', + maxWidth: 240, + maxHeight: 48 + } +}) + +class NavBar extends Component { + constructor (props) { + super(props) + this.state = { + user: null, + open: false + } + } + + async componentDidMount () { + const user = await getItem('user') + console.log('NavBar CDM: ', user, this.state) + this.setState({ user }) + } + + render () { + let { classes, auth } = this.props + + let user = JSON.parse(this.state.user) + + return ( +
+ + + Advanced Algos + + Teams + + + + + + + + + {this.state.user !== undefined && this.state.user !== null ? ( + + + + + + + ) : ( + + )} + + +
+ ) + } +} + +NavBar.propTypes = { + classes: PropTypes.object.isRequired, + auth: PropTypes.object.isRequired +} + +export default withStyles(styles)(NavBar) From 7e6edc8d9013bafdc27f4daa6901022b8fa92e87 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 18 Sep 2018 12:36:37 +0200 Subject: [PATCH 11/51] Showing current avatar again. --- client/src/components/UserImages.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/components/UserImages.js b/client/src/components/UserImages.js index 23651fda6..05ad4e254 100644 --- a/client/src/components/UserImages.js +++ b/client/src/components/UserImages.js @@ -184,7 +184,7 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay) const { classes } = this.props - if (this.state.removeCurrentAvatar === true) { + if (this.state.removeCurrentAvatar === false) { return (
From d1d7fa72b60b2d07576512ca8f075ae13a8e2192 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 19 Sep 2018 12:00:49 +0200 Subject: [PATCH 12/51] Company logo enlarged --- client/src/components/NavBar.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/NavBar.js b/client/src/components/NavBar.js index e53b65521..f3139a725 100644 --- a/client/src/components/NavBar.js +++ b/client/src/components/NavBar.js @@ -20,7 +20,7 @@ import { Link } from 'react-router-dom' import LoggedInUser from './LoggedInUser' // Images -import AALogo from '../img/aa-logo-dark-8.png' +import AALogo from '../assets/logos/advanced-algos/aa-logo-dark.svg' const ModulesLink = props => const AboutLink = props => @@ -43,8 +43,8 @@ const styles = { img: { margin: 20, display: 'block', - maxWidth: 120, - maxHeight: 24 + maxWidth: 240, + maxHeight: 48 } } From 48b930e04bbcee4a6f3265b47e6de7d495f4ed52 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Tue, 25 Sep 2018 16:25:36 +0200 Subject: [PATCH 13/51] User creationDate recorded --- server/models/user.js | 1 + server/schema/schema.js | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/server/models/user.js b/server/models/user.js index 2fd5bcef9..23cdecf24 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -6,6 +6,7 @@ const Schema = mongoose.Schema const userSchema = new Schema({ authId: String, + creationDate: String, alias: String, firstName: String, middleName: String, diff --git a/server/schema/schema.js b/server/schema/schema.js index 2c37ba96d..f0f12fb7f 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -367,9 +367,21 @@ function authenticate (encodedToken, callBackFunction) { return } + let localDate = new Date() + let creationDate = new Date(Date.UTC( + localDate.getUTCFullYear(), + localDate.getUTCMonth(), + localDate.getUTCDate(), + localDate.getUTCHours(), + localDate.getUTCMinutes(), + localDate.getUTCSeconds(), + localDate.getUTCMilliseconds()) + ) + let newUser = new User({ alias: alias, authId: authId, + creationDate: creationDate.toISOString(), email: email, emailVerified: emailVerified, roleId: '1' From 24d2ebe68e4a0a7a7227a97cf783e2466c9ce35f Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 26 Sep 2018 08:30:18 +0200 Subject: [PATCH 14/51] Referrals Tab Added --- client/src/components/UserReferrals.js | 470 +++++++++++++++++++++++++ client/src/components/UserTabs.js | 4 + client/src/components/index.js | 21 ++ 3 files changed, 495 insertions(+) create mode 100644 client/src/components/UserReferrals.js create mode 100644 client/src/components/index.js diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js new file mode 100644 index 000000000..72bafab0d --- /dev/null +++ b/client/src/components/UserReferrals.js @@ -0,0 +1,470 @@ +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 AALogo from '../img/aa-logo.png' + +const styles = theme => ({ + root: { + width: '50%', + flexGrow: 1, + padding: 10, + marginLeft: '25%', + marginTop: '2%' + }, + 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 UserReferrals 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 + } + } + + 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 ( + Loading roles + ) + } else { + return data.roles.map(role => { + return ( + { role.name } + ) + }) + } + } + + submitForm (e) { + e.preventDefault() + this.props.updateUserMutation({ + variables: { + id: this.state.id, + 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)) + } + + 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 userData = localStorage.getItem('loggedInUser') + + if (userData === 'undefined') { 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 + }) + } + } + + render () { + const { classes } = this.props + return ( + + + Profile Sheet + + + + + Use this form to control the information you keep at the Advanced Algos system about yourself. + + + + + Github + + + + + This is your basic information we have gotten from the identity provider you used to sign up. This information can not be changed. + + + + + + + + } + label='Email Verified' + /> + + + + Advanced Algos + + + + + Complete your profile with the following optional information. Providing your real name might help other users trust you more. + + + + + + + + + + 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. + + + + + + Check the following options to enable specialized tools designed for each role. You can allways come back and change these settings later. + + + + + + + } + label='Developer' + /> + + + + } + label='Trader' + /> + + + + } + label='Data Analyst' + /> + + + + +

Your current role determines how the system is going to optimize its user interface to best serves your current needs.

+ + + + Current Role + + + Select from the list your current role + + + + + + + + +
+ + ) + } +} + +export default compose( + graphql(getRolesQuery, {name: 'getRolesQuery'}), + graphql(updateUserMutation, {name: 'updateUserMutation'}), + withStyles(styles) +)(UserReferrals) // This technique binds more than one query to a single component. diff --git a/client/src/components/UserTabs.js b/client/src/components/UserTabs.js index 3722275a8..aed915c0b 100644 --- a/client/src/components/UserTabs.js +++ b/client/src/components/UserTabs.js @@ -6,12 +6,14 @@ import Tabs from '@material-ui/core/Tabs'; import Tab from '@material-ui/core/Tab'; import UpdateIcon from '@material-ui/icons/Create'; import ImageIcon from '@material-ui/icons/Wallpaper'; +import ReferralsIcon from '@material-ui/icons/DeviceHub'; import Typography from '@material-ui/core/Typography'; // Components import UserUpdate from './UserUpdate'; import UserImages from './UserImages'; +import UserReferrals from './UserReferrals'; function TabContainer(props) { return ( @@ -61,10 +63,12 @@ class UserTabs extends React.Component { > } /> } /> + } /> {value === 0 && } {value === 1 && } + {value === 2 && }
); } diff --git a/client/src/components/index.js b/client/src/components/index.js new file mode 100644 index 000000000..195ec74ff --- /dev/null +++ b/client/src/components/index.js @@ -0,0 +1,21 @@ +import NavBar from './navbar' +import Footer from './footer' +import Callback from './callback' +import Home from './home' +import About from './about' +import Dashboard from './dashboard' +import Teams from './teams' + +import { theme, globalStyles } from './theme' + +export { + NavBar, + Footer, + Callback, + Home, + About, + Dashboard, + Teams, + theme, + globalStyles +} From e194bbe7b94560972acfc892f235facec056b76c Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 26 Sep 2018 09:01:17 +0200 Subject: [PATCH 15/51] User Search Page Added --- client/src/components/Search.js | 54 ++------ client/src/components/UserReferrals.js | 39 ++---- client/src/components/UserSearch.js | 182 +++++++++++++++++++++++++ client/src/components/index0.js | 21 +++ 4 files changed, 223 insertions(+), 73 deletions(-) create mode 100644 client/src/components/UserSearch.js create mode 100644 client/src/components/index0.js diff --git a/client/src/components/Search.js b/client/src/components/Search.js index 9f9c9c734..abc26baac 100644 --- a/client/src/components/Search.js +++ b/client/src/components/Search.js @@ -1,48 +1,12 @@ -import React from 'react'; -import Button from '@material-ui/core/Button'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogTitle from '@material-ui/core/DialogTitle'; +import React from 'react' +import UserSearch from './UserSearch' -class Search extends React.Component { - state = { - open: true, - }; - - handleClickOpen = () => { - this.setState({ open: true }); - }; - - handleClose = () => { - this.setState({ open: false }); - }; - - render() { - return ( -
- - {"Section under development."} - - - The functionality of this section has not been developed yet. We expect to have it for October 2018. Thanks for your understanding. - - - - - - -
- ); - } +const Browse = () => { + return ( +
+ +
+ ) } -export default Search; +export default Browse diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index 72bafab0d..815df63d7 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -278,12 +278,16 @@ class UserReferrals extends Component { return ( - Profile Sheet + Referral Program
- Use this form to control the information you keep at the Advanced Algos system about yourself. + 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. @@ -293,37 +297,16 @@ class UserReferrals extends Component { - This is your basic information we have gotten from the identity provider you used to sign up. This information can not be changed. + Use the search field to find the who referred you to the project. - - - - - } - label='Email Verified' - /> + /> diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js new file mode 100644 index 000000000..9335c5145 --- /dev/null +++ b/client/src/components/UserSearch.js @@ -0,0 +1,182 @@ +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 PortraitImage from '../img/portrait.jpg' + +const styles = theme => ({ + root: { + flexGrow: 1, + padding: 60, + margin: 2, + }, + 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 ; +} + +class UserSearch 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 (
Loading Users...
); + } else { + return data.users.map(user => { + + return ( + + + { + this.setState({ selected: user.id}); + } + }> + + + + + {user.alias} + + + {user.bio} + + + + + + + + + + + + + + + User Profile + + + + + + + + + + + + + + + ) + }); + } + } + + render() { + const { classes } = this.props; + return ( + + + {this.displayUsers()} + + + ); + } +} + +UserSearch.propTypes = { + classes: PropTypes.object.isRequired, +}; + +export default compose( + graphql(getUsersQuery, {name: "getUsersQuery"}), + withStyles(styles) +) (UserSearch); // This technique binds more than one query to a single component. diff --git a/client/src/components/index0.js b/client/src/components/index0.js new file mode 100644 index 000000000..195ec74ff --- /dev/null +++ b/client/src/components/index0.js @@ -0,0 +1,21 @@ +import NavBar from './navbar' +import Footer from './footer' +import Callback from './callback' +import Home from './home' +import About from './about' +import Dashboard from './dashboard' +import Teams from './teams' + +import { theme, globalStyles } from './theme' + +export { + NavBar, + Footer, + Callback, + Home, + About, + Dashboard, + Teams, + theme, + globalStyles +} From cfc76bfce5d294e7224ea81a0f88bcc4cf2a8854 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 26 Sep 2018 09:31:36 +0200 Subject: [PATCH 16/51] Search Fields in Place --- client/src/components/UserSearch.js | 157 ++++++++++++++++++---------- 1 file changed, 103 insertions(+), 54 deletions(-) diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 9335c5145..3c193dd20 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -14,17 +14,7 @@ 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'; +import TextField from '@material-ui/core/TextField' // components import UserProfile from './UserProfile'; @@ -39,8 +29,8 @@ const styles = theme => ({ margin: 2, }, card: { - maxWidth: 345, - minWidth: 300, + maxWidth: 145, + minWidth: 100, paddingTop:'30' }, media: { @@ -62,14 +52,15 @@ const styles = theme => ({ }, grid: { paddingTop: '30', - marginTop:'30' + marginTop:30 + }, + inputField: { + width: '80%', + marginLeft: '10%', + marginTop: 25 }, }); -function Transition(props) { - return ; -} - class UserSearch extends Component { constructor(props){ @@ -88,6 +79,50 @@ class UserSearch extends Component { this.setState({ open: false }); }; + submitForm (e) { + e.preventDefault() + } + + 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: + } + } + displayUsers(){ let data = this.props.getUsersQuery; const { classes } = this.props; @@ -98,7 +133,7 @@ class UserSearch extends Component { return data.users.map(user => { return ( - + { this.setState({ selected: user.id}); @@ -115,46 +150,20 @@ class UserSearch extends Component { {user.alias} - - {user.bio} - - - - - - - - - - User Profile - - - - - - - - - + ) }); } @@ -164,18 +173,58 @@ class UserSearch extends Component { const { classes } = this.props; return ( - + + + + + + + + + + + {this.displayUsers()} - + + +
); } } -UserSearch.propTypes = { - classes: PropTypes.object.isRequired, -}; - export default compose( graphql(getUsersQuery, {name: "getUsersQuery"}), withStyles(styles) From 95d43d19fc46ed79082bf7e7fdf9fe3490706868 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 26 Sep 2018 09:51:40 +0200 Subject: [PATCH 17/51] Text added --- client/src/components/UserSearch.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 3c193dd20..1cb851429 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -59,6 +59,11 @@ const styles = theme => ({ marginLeft: '10%', marginTop: 25 }, + formTypography: { + width: '80%', + marginLeft: '10%', + marginTop: 40 + }, }); class UserSearch extends Component { @@ -175,6 +180,10 @@ class UserSearch extends Component {
+ + Use any of these fileds to search for users. + + Date: Wed, 26 Sep 2018 11:08:14 +0200 Subject: [PATCH 18/51] Users Search GraphQl Schema implemented and tested --- server/schema/schema.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/schema/schema.js b/server/schema/schema.js index f0f12fb7f..4d0561156 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -129,6 +129,20 @@ const RootQuery = new GraphQLObjectType({ resolve (parent, args) { return roles } + }, + usersSearch: { + type: new GraphQLList(UserType), + args: {alias: {type: GraphQLString}, firstName: {type: GraphQLString}, middleName: {type: GraphQLString}, lastName: {type: GraphQLString}}, + resolve (parent, args) { + let mongoQuery = { $or: [] } + + if (args.alias !== null && args.alias !== '') { mongoQuery.$or.push({alias: args.alias}) } + if (args.firstName !== null && args.firstName !== '') { mongoQuery.$or.push({firstName: args.firstName}) } + if (args.middleName !== null && args.middleName !== '') { mongoQuery.$or.push({middleName: args.middleName}) } + if (args.lastName !== null && args.lastName !== '') { mongoQuery.$or.push({lastName: args.lastName}) } + + return User.find(mongoQuery) + } } } }) From 942794990233e44180322bc4d0bc62fade55ff4f Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 26 Sep 2018 13:41:09 +0200 Subject: [PATCH 19/51] Search Functionality Working --- client/src/components/Search.js | 4 +-- client/src/components/UserSearch.js | 53 +++++++++++++++++++++-------- client/src/queries/queries.js | 27 ++++++++++++++- server/schema/schema.js | 2 ++ 4 files changed, 69 insertions(+), 17 deletions(-) diff --git a/client/src/components/Search.js b/client/src/components/Search.js index abc26baac..3271649ff 100644 --- a/client/src/components/Search.js +++ b/client/src/components/Search.js @@ -1,7 +1,7 @@ import React from 'react' import UserSearch from './UserSearch' -const Browse = () => { +const Search = () => { return (
@@ -9,4 +9,4 @@ const Browse = () => { ) } -export default Browse +export default Search diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 1cb851429..ae01d1b5e 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -1,6 +1,6 @@ import React, { Component } from 'react'; import {graphql, compose} from 'react-apollo'; -import {getUsersQuery} from '../queries/queries'; +import {getUsersBySearchFields} from '../queries/queries'; // Materia UI @@ -64,15 +64,22 @@ const styles = theme => ({ marginLeft: '10%', marginTop: 40 }, + button: { + margin: theme.spacing.unit, + marginTop: theme.spacing.unit * 3 + } }); class UserSearch extends Component { constructor(props){ super(props); + this.state = { - selected: null, - open: false + alias: '', + firstName: '', + middleName: '', + lastName: '' } } @@ -86,7 +93,14 @@ class UserSearch extends Component { 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) { /* @@ -127,18 +141,22 @@ class UserSearch extends Component { default: } } - + displayUsers(){ - let data = this.props.getUsersQuery; + let data = this.props.getUsersBySearchFields; const { classes } = this.props; if(data.loading){ return (
Loading Users...
); } else { - return data.users.map(user => { - + if(data.usersSearch === undefined) { + return (
No Users to Display
) + } else { + + return data.usersSearch.map(user => { + return ( - + { this.setState({ selected: user.id}); @@ -172,6 +190,7 @@ class UserSearch extends Component { ) }); } + } } render() { @@ -183,7 +202,7 @@ class UserSearch extends Component { Use any of these fileds to search for users. - + - + - + + + + + + + {this.displayUsers()} - + ); @@ -235,6 +260,6 @@ class UserSearch extends Component { } export default compose( - graphql(getUsersQuery, {name: "getUsersQuery"}), + graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}), withStyles(styles) ) (UserSearch); // This technique binds more than one query to a single component. diff --git a/client/src/queries/queries.js b/client/src/queries/queries.js index 344958d63..ecd4feeb6 100644 --- a/client/src/queries/queries.js +++ b/client/src/queries/queries.js @@ -58,6 +58,23 @@ query($authId: String){ } ` +const getUsersBySearchFields = gql` +query( + $alias: String, + $firstName: String, + $middleName: String, + $lastName: String +){ + usersSearch (alias: $alias, firstName: $firstName, middleName: $middleName, lastName: $lastName){ + id + alias + firstName + middleName + lastName + } +} +` + const getRolesQuery = gql` { roles { @@ -116,4 +133,12 @@ mutation( } } ` -export {getUsersQuery, getUserByAuthIdQuery, getRolesQuery, getUserProfileQuery, updateUserMutation, updateUserImagesMutation} +export { + getUsersQuery, + getUserByAuthIdQuery, + getRolesQuery, + getUserProfileQuery, + getUsersBySearchFields, + updateUserMutation, + updateUserImagesMutation +} diff --git a/server/schema/schema.js b/server/schema/schema.js index 4d0561156..f18a4de51 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -141,6 +141,8 @@ const RootQuery = new GraphQLObjectType({ if (args.middleName !== null && args.middleName !== '') { mongoQuery.$or.push({middleName: args.middleName}) } if (args.lastName !== null && args.lastName !== '') { mongoQuery.$or.push({lastName: args.lastName}) } + if (mongoQuery.$or.length === 0) { mongoQuery = {} } + return User.find(mongoQuery) } } From e92b9dfcfde2e08d10f5ff214a73733b4db75b9d Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 26 Sep 2018 13:50:02 +0200 Subject: [PATCH 20/51] Select Button Conditional --- client/src/components/UserSearch.js | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index ae01d1b5e..4786915f0 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -142,6 +142,22 @@ class UserSearch extends Component { } } + selectButton(){ + +if (this.props.selectButton === true) { + + return( + + ) + +} else { + return(
) +} + + } + displayUsers(){ let data = this.props.getUsersBySearchFields; const { classes } = this.props; @@ -178,9 +194,7 @@ class UserSearch extends Component { - + {this.selectButton()} From dfec88b5b28f4f5a26ef8955b21722c7f309a75f Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 26 Sep 2018 14:40:08 +0200 Subject: [PATCH 21/51] Search form inside the Referral Program --- client/src/components/Search.js | 41 +++++++++++++++++++++----- client/src/components/UserReferrals.js | 23 ++++----------- client/src/components/UserSearch.js | 11 ++----- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/client/src/components/Search.js b/client/src/components/Search.js index 3271649ff..a976d5f83 100644 --- a/client/src/components/Search.js +++ b/client/src/components/Search.js @@ -1,12 +1,37 @@ -import React from 'react' +import React, { Component } from 'react' import UserSearch from './UserSearch' +import {graphql, compose} from 'react-apollo' -const Search = () => { - return ( -
- -
- ) +// Materia UI + +import { withStyles } from '@material-ui/core/styles' +import Paper from '@material-ui/core/Paper' + +const styles = theme => ({ + root: { + flexGrow: 1, + padding: 60, + margin: 2 + } +}) + +class Search extends Component { + + constructor (props) { + super(props) + this.state = { + } + } + render () { + const { classes } = this.props + return ( + + + + ) + } } -export default Search +export default compose( + withStyles(styles) +)(Search) diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index 815df63d7..80078007a 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -2,6 +2,10 @@ import React, { Component } from 'react' import {graphql, compose} from 'react-apollo' import {getRolesQuery, updateUserMutation, getUsersQuery} from '../queries/queries' +// components + +import UserSearch from './UserSearch' + // Material-ui import { withStyles } from '@material-ui/core/styles' @@ -21,7 +25,6 @@ import Grid from '@material-ui/core/Grid' import Typography from '@material-ui/core/Typography' // Images -import GithubLogo from '../img/github-logo.png' import AALogo from '../img/aa-logo.png' const styles = theme => ({ @@ -290,23 +293,7 @@ class UserReferrals extends Component { overall reputation within the project. - - - Github - - - - - Use the search field to find the who referred you to the project. - - - + diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 4786915f0..f64cc1fa1 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -13,7 +13,7 @@ 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'; + import TextField from '@material-ui/core/TextField' // components @@ -23,11 +23,6 @@ import UserProfile from './UserProfile'; import PortraitImage from '../img/portrait.jpg' const styles = theme => ({ - root: { - flexGrow: 1, - padding: 60, - margin: 2, - }, card: { maxWidth: 145, minWidth: 100, @@ -210,7 +205,7 @@ if (this.props.selectButton === true) { render() { const { classes } = this.props; return ( - +
@@ -268,7 +263,7 @@ if (this.props.selectButton === true) { -
+ ); } } From 603277713641d360c158e209b6d0f7bd6a9badb9 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Wed, 26 Sep 2018 14:52:55 +0200 Subject: [PATCH 22/51] Select Button and Text Implemented --- client/src/components/UserReferrals.js | 2 +- client/src/components/UserSearch.js | 29 ++++++++++++++++++-------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index 80078007a..47d24cf8b 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -293,7 +293,7 @@ class UserReferrals extends Component { overall reputation within the project. - + diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index f64cc1fa1..4b0185edd 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -139,17 +139,28 @@ class UserSearch extends Component { selectButton(){ -if (this.props.selectButton === true) { + if (this.props.selectButton === true) { - return( - - ) + return( + + ) -} else { - return(
) + } else { + return(
) + } } + + selectText(){ + + if (this.props.selectButton === true) { + + return(After that press the 'Select' button on the desired user to continue.) + + } else { + return(
) + } } @@ -209,7 +220,7 @@ if (this.props.selectButton === true) {
- Use any of these fileds to search for users. + Use any of these fileds to search for users. {this.selectText()} Date: Thu, 27 Sep 2018 08:29:33 +0200 Subject: [PATCH 23/51] Cleaning Code at Referrals --- client/src/components/Footer.js | 5 +- client/src/components/UserReferrals.js | 131 ------------------------- client/src/components/UserSearch.js | 7 +- 3 files changed, 7 insertions(+), 136 deletions(-) diff --git a/client/src/components/Footer.js b/client/src/components/Footer.js index fb7254f07..03ea106d1 100644 --- a/client/src/components/Footer.js +++ b/client/src/components/Footer.js @@ -11,7 +11,8 @@ import AALogo from '../img/aa-logo.png' const styles = theme => ({ root: { flexGrow: 1, - padding: 100 + padding: 100, + marginTop: 40 }, img: { margin: 'auto', @@ -37,7 +38,7 @@ class Footer extends Component { const { classes } = this.props return ( - + - - diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 4b0185edd..217a11e63 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -24,9 +24,10 @@ import PortraitImage from '../img/portrait.jpg' const styles = theme => ({ card: { - maxWidth: 145, - minWidth: 100, - paddingTop:'30' + maxWidth: 150, + minWidth: 150, + paddingTop:'30', + marginBottom:40 }, media: { height: 0, From 8b52f9671617978b0148a46693e549855f33010f Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 08:29:52 +0200 Subject: [PATCH 24/51] Old Footer Deleted --- client/src/components/Footer.js | 85 --------------------------------- 1 file changed, 85 deletions(-) delete mode 100644 client/src/components/Footer.js diff --git a/client/src/components/Footer.js b/client/src/components/Footer.js deleted file mode 100644 index 03ea106d1..000000000 --- a/client/src/components/Footer.js +++ /dev/null @@ -1,85 +0,0 @@ -import React, { Component } from 'react' -import { NavLink } from 'react-router-dom' -import { compose } from 'recompose' -import { withStyles } from '@material-ui/core/styles' -import Button from '@material-ui/core/Button' -import Grid from '@material-ui/core/Grid' - -// Images -import AALogo from '../img/aa-logo.png' - -const styles = theme => ({ - root: { - flexGrow: 1, - padding: 100, - marginTop: 40 - }, - img: { - margin: 'auto', - display: 'block', - maxWidth: 120, - maxHeight: 24 - }, - button: { - margin: theme.spacing.unit - } -}) - -class Footer extends Component { - - constructor (props) { - super(props) - this.state = { - selected: '', - user: null - } - } - render () { - const { classes } = this.props - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - ) - } -} - -export default compose( - withStyles(styles) -)(Footer) From e42d04c273dae94d254f58c537a707505f25c5f9 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 08:34:17 +0200 Subject: [PATCH 25/51] Footer Adjustment --- client/src/views/footer/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/views/footer/index.js b/client/src/views/footer/index.js index d7454ede6..fa136d760 100644 --- a/client/src/views/footer/index.js +++ b/client/src/views/footer/index.js @@ -16,6 +16,7 @@ const styles = theme => ({ }, layout: { width: 'auto', + marginTop: theme.spacing.unit * 7, marginLeft: theme.spacing.unit * 3, marginRight: theme.spacing.unit * 3, [theme.breakpoints.up(1200 + theme.spacing.unit * 3 * 2)]: { From 13e116a1e2095a669c2c515e798fc2aca70947cf Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 08:51:19 +0200 Subject: [PATCH 26/51] More Cleaning Referrals --- client/src/components/UserReferrals.js | 159 +------------------------ 1 file changed, 1 insertion(+), 158 deletions(-) diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index 6f00b0b27..ad7d8fe8c 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -11,22 +11,12 @@ import UserSearch from './UserSearch' 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 AALogo from '../img/aa-logo.png' - const styles = theme => ({ root: { width: '50%', @@ -35,33 +25,14 @@ const styles = theme => ({ marginLeft: '25%', marginTop: '2%' }, - 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 } }) @@ -73,36 +44,10 @@ class UserReferrals extends Component { 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 - } - } - - 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 ( - Loading roles - ) - } else { - return data.roles.map(role => { - return ( - { role.name } - ) - }) + aliasError: false } } @@ -139,108 +84,6 @@ class UserReferrals extends Component { localStorage.setItem('loggedInUser', JSON.stringify(user)) } - 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 userData = localStorage.getItem('loggedInUser') From 6e36ca3f948ed7246a15148baa2910e87fa2e444 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 10:04:08 +0200 Subject: [PATCH 27/51] Users transmitted to parent component --- client/src/components/UserReferrals.js | 8 +- client/src/components/UserSearch.js | 282 ++++++++++++------------- 2 files changed, 140 insertions(+), 150 deletions(-) diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index ad7d8fe8c..28c83a2e7 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -51,6 +51,12 @@ class UserReferrals extends Component { } } + userSelected (user) { + console.log(user) + + this.setState({referrer: user.id}) + } + submitForm (e) { e.preventDefault() this.props.updateUserMutation({ @@ -136,7 +142,7 @@ class UserReferrals extends Component { overall reputation within the project. - + diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 217a11e63..30c38b8b4 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -1,23 +1,23 @@ -import React, { Component } from 'react'; -import {graphql, compose} from 'react-apollo'; -import {getUsersBySearchFields} from '../queries/queries'; +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 { 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' // components -import UserProfile from './UserProfile'; +import UserProfile from './UserProfile' // Images import PortraitImage from '../img/portrait.jpg' @@ -26,29 +26,29 @@ const styles = theme => ({ card: { maxWidth: 150, minWidth: 150, - paddingTop:'30', - marginBottom:40 + paddingTop: '30', + marginBottom: 40 }, media: { height: 0, paddingTop: '56.25%', // 16:9, - marginTop:'30', + marginTop: '30', minWidth: 300 }, typography: { maxWidth: 345, minWidth: 300, - paddingTop:'30' + paddingTop: '30' }, - appBar: { - position: 'relative', + appBar: { + position: 'relative' }, flex: { - flex: 1, + flex: 1 }, grid: { paddingTop: '30', - marginTop:30 + marginTop: 30 }, inputField: { width: '80%', @@ -64,39 +64,31 @@ const styles = theme => ({ margin: theme.spacing.unit, marginTop: theme.spacing.unit * 3 } -}); +}) class UserSearch extends Component { - constructor(props){ - super(props); - + constructor (props) { + super(props) + this.state = { alias: '', firstName: '', middleName: '', - lastName: '' + lastName: '' } } - handleClickOpen = () => { - this.setState({ open: true }); - }; - - handleClose = () => { - this.setState({ open: false }); - }; - 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.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) { /* @@ -138,149 +130,141 @@ class UserSearch extends Component { } } - selectButton(){ - - if (this.props.selectButton === true) { - - return( - - ) - - } else { - return(
) - } -} - - selectText(){ - + selectText () { if (this.props.selectButton === true) { - - return(After that press the 'Select' button on the desired user to continue.) - + return ({this.props.selectText}) } else { - return(
) + return (
) } - } - displayUsers(){ - let data = this.props.getUsersBySearchFields; - const { classes } = this.props; - - if(data.loading){ - return (
Loading Users...
); + selectButton (user) { + if (this.props.selectButton === true) { + return ( + + ) } else { - if(data.usersSearch === undefined) { - return (
No Users to Display
) - } else { - - return data.usersSearch.map(user => { + return (
) + } + } - return ( + displayUsers () { + let data = this.props.getUsersBySearchFields + const { classes } = this.props - - { - this.setState({ selected: user.id}); + if (data.loading) { + return (
Loading Users...
) + } else { + if (data.usersSearch === undefined) { + return (
No Users to Display
) + } else { + return data.usersSearch.map(user => { + return ( + + + { + this.setState({ selected: user.id}) } }> - - + - - - {user.alias} - - - - - - - {this.selectButton()} - - - - - + + + {user.alias} + + + + + + + {this.selectButton(user)} + + + +
+
- ) - }); - } + ) + }) + } } } - render() { - const { classes } = this.props; + render () { + const { classes } = this.props return ( - +
- + Use any of these fileds to search for users. {this.selectText()} - + - - - - - - - - - - - + + + + + + + + {this.displayUsers()} - - - - ); +
+ ) } } export default compose( graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}), withStyles(styles) -) (UserSearch); // This technique binds more than one query to a single component. +)(UserSearch) // This technique binds more than one query to a single component. From 53a3d3736b88b211c17babb993157cf411f8c33a Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 10:56:30 +0200 Subject: [PATCH 28/51] updateUserReferrer mutation ready --- server/models/user.js | 1 + server/schema/schema.js | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/server/models/user.js b/server/models/user.js index 23cdecf24..b00c278e6 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -6,6 +6,7 @@ const Schema = mongoose.Schema const userSchema = new Schema({ authId: String, + referrerId: String, creationDate: String, alias: String, firstName: String, diff --git a/server/schema/schema.js b/server/schema/schema.js index f18a4de51..cfcc7400f 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -30,6 +30,7 @@ const UserType = new GraphQLObjectType({ fields: () => ({ id: { type: GraphQLID}, authId: { type: GraphQLString}, + referrerId: {type: GraphQLString}, alias: {type: GraphQLString}, firstName: {type: GraphQLString}, middleName: {type: GraphQLString}, @@ -233,6 +234,25 @@ const Mutation = new GraphQLObjectType({ avatarChangeDate: args.avatarChangeDate } + return User.update(key, updatedUser) + } + }, + updateUserReferrer: { + type: UserType, + args: { + id: {type: new GraphQLNonNull(GraphQLID)}, + referrerId: {type: GraphQLString} + }, + resolve (parent, args) { + let key = { + _id: args.id, + referrerId: null + } + + let updatedUser = { + referrerId: args.referrerId + } + return User.update(key, updatedUser) } } From 9d5899683b40fd5a70e7b28959bac824a92b7bef Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 11:14:43 +0200 Subject: [PATCH 29/51] Referrer is being recorded at Database --- client/src/components/UserReferrals.js | 62 ++++++-------------------- client/src/queries/queries.js | 24 +++++++--- 2 files changed, 31 insertions(+), 55 deletions(-) diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index 28c83a2e7..6e6fb8a1a 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import {graphql, compose} from 'react-apollo' -import {getRolesQuery, updateUserMutation, getUsersQuery} from '../queries/queries' +import {getRolesQuery, updateReferrerMutation, getUsersQuery} from '../queries/queries' // components @@ -44,6 +44,8 @@ class UserReferrals extends Component { this.defaultValuesSet = false this.state = { + id: '', + referrerId: '', firstNameError: false, middleNameError: false, lastNameError: false, @@ -51,41 +53,23 @@ class UserReferrals extends Component { } } - userSelected (user) { + userSelected (referrerUser) { console.log(user) - this.setState({referrer: user.id}) - } + this.setState({referrer: referrerUser.id}) - submitForm (e) { - e.preventDefault() - this.props.updateUserMutation({ + this.props.updateReferrerMutation({ variables: { id: this.state.id, - 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. + 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.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 + user.referrerId = referrerUser.id localStorage.setItem('loggedInUser', JSON.stringify(user)) } @@ -99,28 +83,11 @@ class UserReferrals extends Component { 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 + referrerId: user.referrerId }) } } @@ -132,9 +99,8 @@ class UserReferrals extends Component { Referral Program -
- + 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 @@ -142,9 +108,8 @@ class UserReferrals extends Component { overall reputation within the project. - + - ) @@ -152,7 +117,6 @@ class UserReferrals extends Component { } export default compose( - graphql(getRolesQuery, {name: 'getRolesQuery'}), - graphql(updateUserMutation, {name: 'updateUserMutation'}), + graphql(updateReferrerMutation, {name: 'updateReferrerMutation'}), withStyles(styles) )(UserReferrals) // This technique binds more than one query to a single component. diff --git a/client/src/queries/queries.js b/client/src/queries/queries.js index ecd4feeb6..fe02e1ce4 100644 --- a/client/src/queries/queries.js +++ b/client/src/queries/queries.js @@ -16,7 +16,6 @@ const getUsersQuery = gql` } } ` - const getUserProfileQuery = gql` query($id: ID){ user (id: $id){ @@ -34,11 +33,11 @@ query($id: ID){ } } ` - const getUserByAuthIdQuery = gql` query($authId: String){ userByAuthId (authId: $authId){ id + referrerId alias firstName middleName @@ -57,7 +56,6 @@ query($authId: String){ } } ` - const getUsersBySearchFields = gql` query( $alias: String, @@ -74,7 +72,6 @@ query( } } ` - const getRolesQuery = gql` { roles { @@ -83,7 +80,6 @@ const getRolesQuery = gql` } } ` - const updateUserMutation = gql` mutation( $id: ID!, @@ -114,7 +110,22 @@ mutation( } } ` - +const updateReferrerMutation = gql` +mutation( + $id: ID!, + $referrerId:String! +) + { + updateUserReferrer ( + id: $id, + referrerId:$referrerId + ) + { + id + referrerId + } +} +` const updateUserImagesMutation = gql` mutation( $id: ID!, @@ -140,5 +151,6 @@ export { getUserProfileQuery, getUsersBySearchFields, updateUserMutation, + updateReferrerMutation, updateUserImagesMutation } From 81a93b92a6ef13dc83d27af01380c165b0a89ce1 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 11:56:57 +0200 Subject: [PATCH 30/51] Your Referrer Form there --- client/src/components/UserDescendents.js | 169 +++++++++++++++++++++++ client/src/components/UserReferrals.js | 92 ++++++++++-- 2 files changed, 247 insertions(+), 14 deletions(-) create mode 100644 client/src/components/UserDescendents.js diff --git a/client/src/components/UserDescendents.js b/client/src/components/UserDescendents.js new file mode 100644 index 000000000..c2860f0e0 --- /dev/null +++ b/client/src/components/UserDescendents.js @@ -0,0 +1,169 @@ +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' + +// components +import UserProfile from './UserProfile' + +// Images +import PortraitImage from '../img/portrait.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: { + 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 UserDescendents 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({}) + } + + displayUsers () { + let data = this.props.getUsersBySearchFields + const { classes } = this.props + + if (data.loading) { + return (
Loading Users...
) + } else { + if (data.usersSearch === undefined) { + return (
No Users to Display
) + } else { + return data.usersSearch.map(user => { + return ( + + + { + this.setState({ selected: user.id}) + } + }> + + + + + {user.alias} + + + + + + + {this.selectButton(user)} + + + + + + + ) + }) + } + } + } + + render () { + const { classes } = this.props + return ( +
+
+ + + 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. + + + + + + + +
+ + + {this.displayUsers()} + +
+ ) + } +} + +export default compose( + graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}), + withStyles(styles) +)(UserDescendents) // This technique binds more than one query to a single component. diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index 6e6fb8a1a..8ff4eff24 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -28,11 +28,17 @@ const styles = theme => ({ typography: { width: '80%', marginLeft: '10%', - marginTop: 40 + marginTop: 40, + marginBottom: 40 }, button: { margin: theme.spacing.unit, marginTop: theme.spacing.unit * 3 + }, + inputField: { + width: '80%', + marginLeft: '10%', + marginTop: 25 } }) @@ -74,6 +80,74 @@ class UserReferrals extends Component { localStorage.setItem('loggedInUser', JSON.stringify(user)) } + renderMode () { + const { classes } = this.props + + if (this.state.referrerId !== '') { + return ( +
+ + This is the user who introduced you to the project. + + + + + + + + + + + + This information can not be changed. + +
+ ) + } else { + return ( +
+ + 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. + + + +
+ ) + } + } + componentWillMount () { if (this.defaultValuesSet === false) { let userData = localStorage.getItem('loggedInUser') @@ -97,21 +171,11 @@ class UserReferrals extends Component { return ( - Referral Program - - - - 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. - - - + Your Referrer +
+ {this.renderMode()} - ) } } From 9ad4fb29ef42cdd5224fd5e5b6069f6e31ed21e0 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 12:24:38 +0200 Subject: [PATCH 31/51] Showing user profile on your referrer --- client/src/components/UserReferrals.js | 46 ++++---------------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index 8ff4eff24..744a80c9d 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -1,19 +1,18 @@ import React, { Component } from 'react' import {graphql, compose} from 'react-apollo' -import {getRolesQuery, updateReferrerMutation, getUsersQuery} from '../queries/queries' +import {updateReferrerMutation} from '../queries/queries' // components import UserSearch from './UserSearch' +import UserProfile from './UserProfile' // Material-ui import { withStyles } from '@material-ui/core/styles' import MenuItem from '@material-ui/core/MenuItem' import TextField from '@material-ui/core/TextField' -import Input from '@material-ui/core/Input' import Button from '@material-ui/core/Button' -import Select from '@material-ui/core/Select' import Paper from '@material-ui/core/Paper' import Typography from '@material-ui/core/Typography' @@ -82,6 +81,7 @@ class UserReferrals extends Component { renderMode () { const { classes } = this.props + const {user} = this.props.data if (this.state.referrerId !== '') { return ( @@ -90,41 +90,7 @@ class UserReferrals extends Component { This is the user who introduced you to the project. - - - - - - - + This information can not be changed. @@ -155,9 +121,9 @@ class UserReferrals extends Component { if (userData === 'undefined') { return } let user = JSON.parse(userData) - this.defaultValuesSet = true + this.defaultValuesSet = true - /* Now we are ready to set the initial state. */ + /* Now we are ready to set the initial state. */ this.setState({ id: user.id, From cb365c2920315683f2a9f378b90fd6ca21452c4e Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 12:39:53 +0200 Subject: [PATCH 32/51] Cleaning the code --- client/src/App.js | 2 +- client/src/components/Home.js | 1 - client/src/components/Search.js | 2 +- client/src/components/UserList.js | 2 ++ client/src/components/UserProfile.js | 13 +++++-------- client/src/components/UserReferrals.js | 10 ++-------- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/client/src/App.js b/client/src/App.js index ded1338cf..fc381cad5 100644 --- a/client/src/App.js +++ b/client/src/App.js @@ -11,7 +11,7 @@ import { Route, BrowserRouter, Switch } from 'react-router-dom' // Material UI -import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles' +import { MuiThemeProvider } from '@material-ui/core/styles' import theme from './theme' // Components diff --git a/client/src/components/Home.js b/client/src/components/Home.js index 6983aa808..154667baf 100644 --- a/client/src/components/Home.js +++ b/client/src/components/Home.js @@ -2,7 +2,6 @@ import React, { Component } from 'react' import { compose } from 'recompose' import { withStyles } from '@material-ui/core/styles' import {LineChart} from 'react-easy-chart' -import Paper from '@material-ui/core/Paper' import Grid from '@material-ui/core/Grid' import Typography from '@material-ui/core/Typography' import CssBaseline from '@material-ui/core/CssBaseline' diff --git a/client/src/components/Search.js b/client/src/components/Search.js index a976d5f83..90f15f758 100644 --- a/client/src/components/Search.js +++ b/client/src/components/Search.js @@ -1,6 +1,6 @@ import React, { Component } from 'react' import UserSearch from './UserSearch' -import {graphql, compose} from 'react-apollo' +import {compose} from 'react-apollo' // Materia UI diff --git a/client/src/components/UserList.js b/client/src/components/UserList.js index a9db8e7a1..9c28d8589 100644 --- a/client/src/components/UserList.js +++ b/client/src/components/UserList.js @@ -143,7 +143,9 @@ class UserList extends Component { + + diff --git a/client/src/components/UserProfile.js b/client/src/components/UserProfile.js index 65bf487af..438e5f61a 100644 --- a/client/src/components/UserProfile.js +++ b/client/src/components/UserProfile.js @@ -24,15 +24,14 @@ const styles = theme => ({ margin: 2 }, card: { - maxWidth: 800, - minWidth: 800, + width: '80%', paddingTop: '30' }, media: { height: 0, paddingTop: '56.25%', // 16:9, marginTop: '30', - minWidth: 800 + minWidth: '80%' }, grid: { paddingTop: '30', @@ -94,11 +93,9 @@ class UserProfile extends Component { render () { const { classes } = this.props return ( - - - {this.displayUserProfile()} - - + + {this.displayUserProfile()} + ) } } diff --git a/client/src/components/UserReferrals.js b/client/src/components/UserReferrals.js index 744a80c9d..d86c4bba1 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/UserReferrals.js @@ -10,9 +10,6 @@ import UserProfile from './UserProfile' // Material-ui import { withStyles } from '@material-ui/core/styles' -import MenuItem from '@material-ui/core/MenuItem' -import TextField from '@material-ui/core/TextField' -import Button from '@material-ui/core/Button' import Paper from '@material-ui/core/Paper' import Typography from '@material-ui/core/Typography' @@ -59,9 +56,7 @@ class UserReferrals extends Component { } userSelected (referrerUser) { - console.log(user) - - this.setState({referrer: referrerUser.id}) + this.setState({referrerId: referrerUser.id}) this.props.updateReferrerMutation({ variables: { @@ -81,9 +76,8 @@ class UserReferrals extends Component { renderMode () { const { classes } = this.props - const {user} = this.props.data - if (this.state.referrerId !== '') { + if (this.state.referrerId !== null) { return (
From 7468c7ccfee131be9d32014d63bfde1733c36c72 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 12:43:35 +0200 Subject: [PATCH 33/51] No more warnings --- client/src/components/UserProfile.js | 2 -- client/src/components/UserSearch.js | 3 --- client/src/theme.js | 2 -- client/src/views/footer/index.js | 2 +- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/client/src/components/UserProfile.js b/client/src/components/UserProfile.js index 438e5f61a..489322eca 100644 --- a/client/src/components/UserProfile.js +++ b/client/src/components/UserProfile.js @@ -6,7 +6,6 @@ import {getUserProfileQuery} from '../queries/queries' import { withStyles } from '@material-ui/core/styles' import Grid from '@material-ui/core/Grid' -import Paper from '@material-ui/core/Paper' import Card from '@material-ui/core/Card' import CardActionArea from '@material-ui/core/CardActionArea' import CardContent from '@material-ui/core/CardContent' @@ -91,7 +90,6 @@ class UserProfile extends Component { } render () { - const { classes } = this.props return ( {this.displayUserProfile()} diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 30c38b8b4..688bef074 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -16,9 +16,6 @@ import Grid from '@material-ui/core/Grid' import TextField from '@material-ui/core/TextField' -// components -import UserProfile from './UserProfile' - // Images import PortraitImage from '../img/portrait.jpg' diff --git a/client/src/theme.js b/client/src/theme.js index 5f9ff7569..1d7900e4a 100644 --- a/client/src/theme.js +++ b/client/src/theme.js @@ -1,6 +1,4 @@ import { createMuiTheme } from '@material-ui/core/styles' -import indigo from '@material-ui/core/colors/indigo' -// import red from '@material-ui/core/colors/red' export const theme = createMuiTheme({ palette: { diff --git a/client/src/views/footer/index.js b/client/src/views/footer/index.js index fa136d760..1ebd618eb 100644 --- a/client/src/views/footer/index.js +++ b/client/src/views/footer/index.js @@ -114,7 +114,7 @@ const Footer = ({ classes }) => { - + From d86029f781a8daf932a90205af82911c3991046b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 14:40:07 +0200 Subject: [PATCH 34/51] Refeactoring for Your Referrer --- client/src/components/UserTabs.js | 6 +++--- client/src/components/{UserReferrals.js => YourReferrer.js} | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) rename client/src/components/{UserReferrals.js => YourReferrer.js} (96%) diff --git a/client/src/components/UserTabs.js b/client/src/components/UserTabs.js index aed915c0b..488642607 100644 --- a/client/src/components/UserTabs.js +++ b/client/src/components/UserTabs.js @@ -13,7 +13,7 @@ import Typography from '@material-ui/core/Typography'; import UserUpdate from './UserUpdate'; import UserImages from './UserImages'; -import UserReferrals from './UserReferrals'; +import YourReferrer from './YourReferrer'; function TabContainer(props) { return ( @@ -63,12 +63,12 @@ class UserTabs extends React.Component { > } /> } /> - } /> + } /> {value === 0 && } {value === 1 && } - {value === 2 && } + {value === 2 && }
); } diff --git a/client/src/components/UserReferrals.js b/client/src/components/YourReferrer.js similarity index 96% rename from client/src/components/UserReferrals.js rename to client/src/components/YourReferrer.js index d86c4bba1..ab30cd0bf 100644 --- a/client/src/components/UserReferrals.js +++ b/client/src/components/YourReferrer.js @@ -38,7 +38,7 @@ const styles = theme => ({ } }) -class UserReferrals extends Component { +class YourReferrer extends Component { constructor (props) { super(props) @@ -143,4 +143,4 @@ class UserReferrals extends Component { export default compose( graphql(updateReferrerMutation, {name: 'updateReferrerMutation'}), withStyles(styles) -)(UserReferrals) // This technique binds more than one query to a single component. +)(YourReferrer) // This technique binds more than one query to a single component. From 330c5cec564d9a30387616414fcf2713c4a77591 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 14:45:13 +0200 Subject: [PATCH 35/51] ProfileSheet Refactoring --- README.md | 2 +- client/src/components/{UserUpdate.js => ProfileSheet.js} | 4 ++-- client/src/components/UserTabs.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename client/src/components/{UserUpdate.js => ProfileSheet.js} (99%) diff --git a/README.md b/README.md index a8790edfb..5650f6db0 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ App.js | | | | | |- UserTabs | | | | -| | | |- Tab 1 - UserUpdate +| | | |- Tab 1 - ProfileSheet | | | | | | | |- Tab 2 - ManageImages | | diff --git a/client/src/components/UserUpdate.js b/client/src/components/ProfileSheet.js similarity index 99% rename from client/src/components/UserUpdate.js rename to client/src/components/ProfileSheet.js index a3c2f2143..20510a91b 100644 --- a/client/src/components/UserUpdate.js +++ b/client/src/components/ProfileSheet.js @@ -62,7 +62,7 @@ const styles = theme => ({ } }) -class UserUpdate extends Component { +class ProfileSheet extends Component { constructor (props) { super(props) @@ -467,4 +467,4 @@ export default compose( graphql(getRolesQuery, {name: 'getRolesQuery'}), graphql(updateUserMutation, {name: 'updateUserMutation'}), withStyles(styles) -)(UserUpdate) // This technique binds more than one query to a single component. +)(ProfileSheet) // This technique binds more than one query to a single component. diff --git a/client/src/components/UserTabs.js b/client/src/components/UserTabs.js index 488642607..09ddf2fc5 100644 --- a/client/src/components/UserTabs.js +++ b/client/src/components/UserTabs.js @@ -11,7 +11,7 @@ import Typography from '@material-ui/core/Typography'; // Components -import UserUpdate from './UserUpdate'; +import ProfileSheet from './ProfileSheet'; import UserImages from './UserImages'; import YourReferrer from './YourReferrer'; @@ -66,7 +66,7 @@ class UserTabs extends React.Component { } /> - {value === 0 && } + {value === 0 && } {value === 1 && } {value === 2 && }
From 80511cab7b1b0fa8a7402126c8f840f6d8c77739 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 14:46:30 +0200 Subject: [PATCH 36/51] Profile Images Refactoring --- .../{UserImages.js => ProfileImages.js} | 54 +++++++++---------- client/src/components/UserTabs.js | 4 +- 2 files changed, 29 insertions(+), 29 deletions(-) rename client/src/components/{UserImages.js => ProfileImages.js} (92%) diff --git a/client/src/components/UserImages.js b/client/src/components/ProfileImages.js similarity index 92% rename from client/src/components/UserImages.js rename to client/src/components/ProfileImages.js index 05ad4e254..f3c769010 100644 --- a/client/src/components/UserImages.js +++ b/client/src/components/ProfileImages.js @@ -55,11 +55,11 @@ const styles = theme => ({ } }) -class UserImages extends React.Component { +class ProfileImages extends React.Component { constructor (props) { super(props) - + this.defaultValuesSet = false this.state = { @@ -86,7 +86,7 @@ class UserImages extends React.Component { }) this.setState({saveDisabled: true, removeCurrentAvatar:true}) - + /* Before we are done, we need to update the state of the local storage. */ let user = JSON.parse(localStorage.getItem('loggedInUser')) @@ -116,10 +116,10 @@ class UserImages extends React.Component { let avatarChangeDate = (new Date(user.avatarChangeDate)).valueOf() let now = (new Date()).valueOf() let timePassed = now - avatarChangeDate - let oneDay = 1000 * 60 * 60 * 24 + let oneDay = 1000 * 60 * 60 * 24 console.log("dates", avatarChangeDate, now, timePassed, oneDay) - + if (timePassed < oneDay) { this.setState({ needToWait: true @@ -139,7 +139,7 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay) }) } } - + onSuccess = (uploadResults) => { console.log('onSuccess') console.log(uploadResults) @@ -156,12 +156,12 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay) localDate.getUTCSeconds(), localDate.getUTCMilliseconds()) ) - + currentDate.toISOString() - + this.setState( { - newAvatarHandle: handle, + newAvatarHandle: handle, avatarChangeDate: currentDate, showNewAvatar: true, uploadToolDisabled: true, @@ -181,7 +181,7 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay) } showCurrentAvatar() { - + const { classes } = this.props if (this.state.removeCurrentAvatar === false) { @@ -191,7 +191,7 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay) This is your current Avatar: - +
) - + } else { return ( @@ -220,10 +220,10 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay)
) } - - + + } - + showNewAvatar() { if (this.state.showNewAvatar === true) { const { classes } = this.props @@ -249,7 +249,7 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay) showUploadTool() { const { classes } = this.props - if (this.state.needToWait === true) { + if (this.state.needToWait === true) { return (
@@ -262,13 +262,13 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay)
If you want, you can choose a new one by picking an image from your computer, camera or the web, and crop it with the following tool. - + - - @@ -319,4 +319,4 @@ console.log("dates", avatarChangeDate, now, timePassed, oneDay) export default compose( graphql(updateUserImagesMutation, {name: 'updateUserImagesMutation'}), withStyles(styles) -)(UserImages) // This technique binds more than one query to a single component. +)(ProfileImages) // This technique binds more than one query to a single component. diff --git a/client/src/components/UserTabs.js b/client/src/components/UserTabs.js index 09ddf2fc5..420e73821 100644 --- a/client/src/components/UserTabs.js +++ b/client/src/components/UserTabs.js @@ -12,7 +12,7 @@ import Typography from '@material-ui/core/Typography'; // Components import ProfileSheet from './ProfileSheet'; -import UserImages from './UserImages'; +import ProfileImages from './ProfileImages'; import YourReferrer from './YourReferrer'; function TabContainer(props) { @@ -67,7 +67,7 @@ class UserTabs extends React.Component { {value === 0 && } - {value === 1 && } + {value === 1 && } {value === 2 && }
); From aa77ea0dbeed15f895c774d72c0a65f8352b906e Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 15:20:51 +0200 Subject: [PATCH 37/51] Descendents tab added --- client/src/components/UserTabs.js | 16 ++++++++++------ .../{UserDescendents.js => YourDescendents.js} | 0 2 files changed, 10 insertions(+), 6 deletions(-) rename client/src/components/{UserDescendents.js => YourDescendents.js} (100%) diff --git a/client/src/components/UserTabs.js b/client/src/components/UserTabs.js index 420e73821..e4df491ae 100644 --- a/client/src/components/UserTabs.js +++ b/client/src/components/UserTabs.js @@ -4,9 +4,10 @@ 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 UpdateIcon from '@material-ui/icons/Create'; -import ImageIcon from '@material-ui/icons/Wallpaper'; -import ReferralsIcon from '@material-ui/icons/DeviceHub'; +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 @@ -14,6 +15,7 @@ import Typography from '@material-ui/core/Typography'; import ProfileSheet from './ProfileSheet'; import ProfileImages from './ProfileImages'; import YourReferrer from './YourReferrer'; +import YourDescendents from './YourDescendents'; function TabContainer(props) { return ( @@ -61,14 +63,16 @@ class UserTabs extends React.Component { indicatorColor="primary" textColor="primary" > - } /> - } /> - } /> + } /> + } /> + } /> + } /> {value === 0 && } {value === 1 && } {value === 2 && } + {value === 2 && }
); } diff --git a/client/src/components/UserDescendents.js b/client/src/components/YourDescendents.js similarity index 100% rename from client/src/components/UserDescendents.js rename to client/src/components/YourDescendents.js From 831acae792e5ba5657990c9bc203e64a52f9b915 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 15:31:52 +0200 Subject: [PATCH 38/51] Minor Corrections --- client/src/components/UserTabs.js | 2 +- client/src/components/YourDescendents.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client/src/components/UserTabs.js b/client/src/components/UserTabs.js index e4df491ae..28b3ae94c 100644 --- a/client/src/components/UserTabs.js +++ b/client/src/components/UserTabs.js @@ -72,7 +72,7 @@ class UserTabs extends React.Component { {value === 0 && } {value === 1 && } {value === 2 && } - {value === 2 && } + {value === 3 && }
); } diff --git a/client/src/components/YourDescendents.js b/client/src/components/YourDescendents.js index c2860f0e0..1fea7dd49 100644 --- a/client/src/components/YourDescendents.js +++ b/client/src/components/YourDescendents.js @@ -66,7 +66,7 @@ const styles = theme => ({ } }) -class UserDescendents extends Component { +class YourDescendents extends Component { constructor (props) { super(props) @@ -166,4 +166,4 @@ class UserDescendents extends Component { export default compose( graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}), withStyles(styles) -)(UserDescendents) // This technique binds more than one query to a single component. +)(YourDescendents) // This technique binds more than one query to a single component. From 74ea9140456fbbd94b8185787b7294efdef35593 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 15:47:14 +0200 Subject: [PATCH 39/51] Descendants on GraphQL --- server/schema/schema.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/server/schema/schema.js b/server/schema/schema.js index cfcc7400f..24ba54086 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -144,7 +144,14 @@ const RootQuery = new GraphQLObjectType({ if (mongoQuery.$or.length === 0) { mongoQuery = {} } - return User.find(mongoQuery) + return User.find([mongoQuery]) + } + }, + descendents: { + type: new GraphQLList(UserType), + args: {id: {type: GraphQLString}}, + resolve (parent, args) { + return User.find({referrerId: args.id}) } } } From 8ca1e2e5927cf6d95945542024c64644643a436b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Thu, 27 Sep 2018 16:27:45 +0200 Subject: [PATCH 40/51] Descendents query working on GraphQL --- server/schema/schema.js | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/server/schema/schema.js b/server/schema/schema.js index 24ba54086..e2d93a6bc 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -52,6 +52,24 @@ const UserType = new GraphQLObjectType({ }) }) +const DescendentType = new GraphQLObjectType({ + name: 'Descendent', + fields: () => ({ + id: { type: GraphQLID}, + referrerId: {type: GraphQLString}, + alias: {type: GraphQLString}, + firstName: {type: GraphQLString}, + middleName: {type: GraphQLString}, + lastName: {type: GraphQLString}, + descendents: { + type: new GraphQLList(DescendentType), + resolve (parent, args) { + return User.find({referrerId: parent.id}) + } + } + }) +}) + const RoleType = new GraphQLObjectType({ name: 'Role', fields: () => ({ @@ -148,7 +166,7 @@ const RootQuery = new GraphQLObjectType({ } }, descendents: { - type: new GraphQLList(UserType), + type: new GraphQLList(DescendentType), args: {id: {type: GraphQLString}}, resolve (parent, args) { return User.find({referrerId: args.id}) From 7b784b7ef2dec14e1d7d8ddd64bd852b0b4e603d Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 08:08:50 +0200 Subject: [PATCH 41/51] User Search Working Again --- client/src/components/UserSearch.js | 1 + server/schema/schema.js | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 688bef074..8284b68f6 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -156,6 +156,7 @@ class UserSearch extends Component { if (data.loading) { return (
Loading Users...
) } else { + console.log(data) if (data.usersSearch === undefined) { return (
No Users to Display
) } else { diff --git a/server/schema/schema.js b/server/schema/schema.js index e2d93a6bc..09264ccf2 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -153,6 +153,8 @@ const RootQuery = new GraphQLObjectType({ type: new GraphQLList(UserType), args: {alias: {type: GraphQLString}, firstName: {type: GraphQLString}, middleName: {type: GraphQLString}, lastName: {type: GraphQLString}}, resolve (parent, args) { + if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> usersSearch -> resolve -> Entering function.') } + let mongoQuery = { $or: [] } if (args.alias !== null && args.alias !== '') { mongoQuery.$or.push({alias: args.alias}) } @@ -161,14 +163,16 @@ const RootQuery = new GraphQLObjectType({ if (args.lastName !== null && args.lastName !== '') { mongoQuery.$or.push({lastName: args.lastName}) } if (mongoQuery.$or.length === 0) { mongoQuery = {} } - - return User.find([mongoQuery]) + console.log('mongoQuery', mongoQuery) + return User.find(mongoQuery) } }, descendents: { type: new GraphQLList(DescendentType), args: {id: {type: GraphQLString}}, resolve (parent, args) { + if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> descendents -> resolve -> Entering function.') } + return User.find({referrerId: args.id}) } } From b68cf6b7b45373c8bab74614abb9b27e6a112bc2 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 08:09:33 +0200 Subject: [PATCH 42/51] Cleaning Console.log --- client/src/components/UserSearch.js | 1 - server/schema/schema.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/src/components/UserSearch.js b/client/src/components/UserSearch.js index 8284b68f6..688bef074 100644 --- a/client/src/components/UserSearch.js +++ b/client/src/components/UserSearch.js @@ -156,7 +156,6 @@ class UserSearch extends Component { if (data.loading) { return (
Loading Users...
) } else { - console.log(data) if (data.usersSearch === undefined) { return (
No Users to Display
) } else { diff --git a/server/schema/schema.js b/server/schema/schema.js index 09264ccf2..99049d0b8 100644 --- a/server/schema/schema.js +++ b/server/schema/schema.js @@ -163,7 +163,7 @@ const RootQuery = new GraphQLObjectType({ if (args.lastName !== null && args.lastName !== '') { mongoQuery.$or.push({lastName: args.lastName}) } if (mongoQuery.$or.length === 0) { mongoQuery = {} } - console.log('mongoQuery', mongoQuery) + return User.find(mongoQuery) } }, From 9021d96985fccad28cfa4137fd89bc1c71157ad3 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 08:40:46 +0200 Subject: [PATCH 43/51] Your Descendatnts Page created --- client/src/components/DescendentsTree.js | 150 +++++++++++++++++++++++ client/src/components/YourDescendents.js | 148 ++++------------------ client/src/components/YourReferrer.js | 2 +- client/src/queries/queries.js | 30 +++++ 4 files changed, 203 insertions(+), 127 deletions(-) create mode 100644 client/src/components/DescendentsTree.js diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js new file mode 100644 index 000000000..3a8ebf1c4 --- /dev/null +++ b/client/src/components/DescendentsTree.js @@ -0,0 +1,150 @@ +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 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' + +// components +import UserProfile from './UserProfile' + +// Images +import PortraitImage from '../img/portrait.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: { + 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 YourDescendents extends Component { + + constructor (props) { + super(props) + + this.state = { + alias: '', + firstName: '', + middleName: '', + lastName: '' + } + } + + displayUsers () { + let data = this.props.getUsersBySearchFields + const { classes } = this.props + + if (data.loading) { + return (
Loading Users...
) + } else { + if (data.usersSearch === undefined) { + return (
No Users to Display
) + } else { + return data.usersSearch.map(user => { + return ( + + + { + this.setState({ selected: user.id}) + } + }> + + + + + {user.alias} + + + + + + + {this.selectButton(user)} + + + + + + + ) + }) + } + } + } + + render () { + const { classes } = this.props + return ( +
+ + + 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. + + + + {this.displayUsers()} + +
+ ) + } +} + +export default compose( + graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}), + withStyles(styles) +)(YourDescendents) // This technique binds more than one query to a single component. diff --git a/client/src/components/YourDescendents.js b/client/src/components/YourDescendents.js index 1fea7dd49..db59b2870 100644 --- a/client/src/components/YourDescendents.js +++ b/client/src/components/YourDescendents.js @@ -1,68 +1,33 @@ 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' +import Paper from '@material-ui/core/Paper' // components import UserProfile from './UserProfile' -// Images -import PortraitImage from '../img/portrait.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 + root: { + width: '50%', + flexGrow: 1, + padding: 10, + marginLeft: '25%', + marginTop: '2%' }, typography: { - maxWidth: 345, - minWidth: 300, - paddingTop: '30' - }, - appBar: { - position: 'relative' - }, - flex: { - flex: 1 + width: '80%', + marginLeft: '10%', + marginTop: 40, + marginBottom: 40 }, grid: { 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 } }) @@ -72,98 +37,29 @@ class YourDescendents extends Component { 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({}) - } - - displayUsers () { - let data = this.props.getUsersBySearchFields - const { classes } = this.props - - if (data.loading) { - return (
Loading Users...
) - } else { - if (data.usersSearch === undefined) { - return (
No Users to Display
) - } else { - return data.usersSearch.map(user => { - return ( - - - { - this.setState({ selected: user.id}) - } - }> - - - - - {user.alias} - - - - - - - {this.selectButton(user)} - - - - - - - ) - }) - } + id: '' } } render () { const { classes } = this.props return ( -
-
+ + + Your Descendents + - - 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. - + + 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. + - - - - - - - - - {this.displayUsers()} - -
+ +
) } } export default compose( - graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}), withStyles(styles) )(YourDescendents) // This technique binds more than one query to a single component. diff --git a/client/src/components/YourReferrer.js b/client/src/components/YourReferrer.js index ab30cd0bf..946f2e4e7 100644 --- a/client/src/components/YourReferrer.js +++ b/client/src/components/YourReferrer.js @@ -132,7 +132,7 @@ class YourReferrer extends Component { Your Referrer - + {this.renderMode()} diff --git a/client/src/queries/queries.js b/client/src/queries/queries.js index fe02e1ce4..a9015b5db 100644 --- a/client/src/queries/queries.js +++ b/client/src/queries/queries.js @@ -72,6 +72,36 @@ query( } } ` +const getDescendentsQuery = gql` +query( + $id: String +){ + 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` { roles { From 47d26b0c72562b0bb945d6514d5711bf67ea224b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 09:24:06 +0200 Subject: [PATCH 44/51] Decendant Tree Component Working --- client/src/components/DescendentsTree.js | 44 ++++++++++++------------ client/src/components/YourDescendents.js | 22 +++++++++++- client/src/queries/queries.js | 1 + 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js index 3a8ebf1c4..74e72fd36 100644 --- a/client/src/components/DescendentsTree.js +++ b/client/src/components/DescendentsTree.js @@ -66,7 +66,7 @@ const styles = theme => ({ } }) -class YourDescendents extends Component { +class DescendentsTree extends Component { constructor (props) { super(props) @@ -79,22 +79,22 @@ class YourDescendents extends Component { } } - displayUsers () { - let data = this.props.getUsersBySearchFields + displayDescendents () { + let data = this.props.data const { classes } = this.props if (data.loading) { - return (
Loading Users...
) + return (
Loading Descendents...
) } else { - if (data.usersSearch === undefined) { - return (
No Users to Display
) + if (data.descendents === undefined) { + return (
No Descendents to Display
) } else { - return data.usersSearch.map(user => { + return data.descendents.map(descendent => { return ( - + { - this.setState({ selected: user.id}) + this.setState({ selected: descendent.id}) } }> @@ -106,15 +106,13 @@ class YourDescendents extends Component { /> - {user.alias} + {descendent.alias} - - {this.selectButton(user)} - + @@ -130,14 +128,8 @@ class YourDescendents extends Component { const { classes } = this.props return (
- - - 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. - - - {this.displayUsers()} + {this.displayDescendents()}
) @@ -145,6 +137,14 @@ class YourDescendents extends Component { } export default compose( - graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}), + 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) -)(YourDescendents) // This technique binds more than one query to a single component. +)(DescendentsTree) // This technique binds more than one query to a single component. diff --git a/client/src/components/YourDescendents.js b/client/src/components/YourDescendents.js index db59b2870..550f1c846 100644 --- a/client/src/components/YourDescendents.js +++ b/client/src/components/YourDescendents.js @@ -9,7 +9,7 @@ import Grid from '@material-ui/core/Grid' import Paper from '@material-ui/core/Paper' // components -import UserProfile from './UserProfile' +import DescendentsTree from './DescendentsTree' const styles = theme => ({ root: { @@ -36,13 +36,32 @@ 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 + }) + } + } + render () { const { classes } = this.props + return ( @@ -54,6 +73,7 @@ class YourDescendents extends Component { your grandchildren referred your children and so on. + ) diff --git a/client/src/queries/queries.js b/client/src/queries/queries.js index a9015b5db..2071246a1 100644 --- a/client/src/queries/queries.js +++ b/client/src/queries/queries.js @@ -180,6 +180,7 @@ export { getRolesQuery, getUserProfileQuery, getUsersBySearchFields, + getDescendentsQuery, updateUserMutation, updateReferrerMutation, updateUserImagesMutation From db88d2a07078e9d27f36857ff754f211d2d79f1a Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 11:39:42 +0200 Subject: [PATCH 45/51] Material UI Nestled List Implemented --- client/src/components/DescendentsTree.js | 129 ++++++++++++++++++----- 1 file changed, 100 insertions(+), 29 deletions(-) diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js index 74e72fd36..54268cc64 100644 --- a/client/src/components/DescendentsTree.js +++ b/client/src/components/DescendentsTree.js @@ -14,7 +14,19 @@ 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' +// 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 InboxIcon from '@material-ui/icons/MoveToInbox'; +import DraftsIcon from '@material-ui/icons/Drafts'; +import SendIcon from '@material-ui/icons/Send'; +import ExpandLess from '@material-ui/icons/ExpandLess'; +import ExpandMore from '@material-ui/icons/ExpandMore'; +import StarBorder from '@material-ui/icons/StarBorder'; // components import UserProfile from './UserProfile' @@ -23,11 +35,12 @@ import UserProfile from './UserProfile' import PortraitImage from '../img/portrait.jpg' const styles = theme => ({ - card: { + childrenCard: { maxWidth: 150, minWidth: 150, paddingTop: '30', - marginBottom: 40 + marginBottom: 40, + marginLeft: 10 }, media: { height: 0, @@ -63,7 +76,10 @@ const styles = theme => ({ button: { margin: theme.spacing.unit, marginTop: theme.spacing.unit * 3 - } + }, + nested: { + paddingLeft: theme.spacing.unit * 4, + }, }) class DescendentsTree extends Component { @@ -72,37 +88,35 @@ class DescendentsTree extends Component { super(props) this.state = { - alias: '', - firstName: '', - middleName: '', - lastName: '' + id: '', + open: true } } - displayDescendents () { - let data = this.props.data + handleClick = () => { + this.setState(state => ({ open: !state.open })); +}; + + displayNodeDescendents(node) { + const { classes } = this.props - - if (data.loading) { - return (
Loading Descendents...
) - } else { - if (data.descendents === undefined) { - return (
No Descendents to Display
) - } else { - return data.descendents.map(descendent => { - return ( - - - { - this.setState({ selected: descendent.id}) - } - }> + return node.descendents.map(descendent => { + return ( + + + + + + + + + @@ -116,8 +130,62 @@ class DescendentsTree extends Component { -
+ + + ) + } + ) + } + + displayDescendents () { + let data = this.props.data + const { classes } = this.props + + if (data.loading) { + return (
Loading Descendents...
) + } else { + if (data.descendents === undefined) { + return (
No Descendents to Display
) + } else { + return data.descendents.map(descendent => { + return ( +
+ + + + + + + + + + + {descendent.alias} + + + + + + + + + + + + + {this.state.open ? : } + + + {this.displayNodeDescendents(descendent)} + + +
) }) } @@ -128,9 +196,12 @@ class DescendentsTree extends Component { const { classes } = this.props return (
- + Nested List Items} + > {this.displayDescendents()} - +
) } From a770e5ec355246cf2bd5696de529a42a30921c7b Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 12:04:29 +0200 Subject: [PATCH 46/51] Successful Refactoring --- client/src/components/DescendentsTree.js | 42 ++++++++++++++++-------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js index 54268cc64..ece5479ff 100644 --- a/client/src/components/DescendentsTree.js +++ b/client/src/components/DescendentsTree.js @@ -63,7 +63,7 @@ const styles = theme => ({ paddingTop: '30', marginTop: 30 }, - inputField: { + list: { width: '80%', marginLeft: '10%', marginTop: 25 @@ -101,8 +101,7 @@ class DescendentsTree extends Component { const { classes } = this.props return node.descendents.map(descendent => { - return ( - + return ( @@ -131,14 +130,33 @@ class DescendentsTree extends Component { - + ) } ) } + displayNestedList(descendent) { + const primaryText = "Children of " + descendent.alias + if (descendent.descendents.length > 0) { + return( + + + + {this.state.open ? : } + + + {this.displayNodeDescendents(descendent)} + + + ) + } else { + return (
) + } + } + - displayDescendents () { + displayTopList () { let data = this.props.data const { classes } = this.props @@ -177,13 +195,8 @@ class DescendentsTree extends Component { - - - {this.state.open ? : } - - - {this.displayNodeDescendents(descendent)} - + + {this.displayNestedList(descendent)}
) @@ -198,9 +211,10 @@ class DescendentsTree extends Component {
Nested List Items} + subheader={Your Children} + className={classes.list} > - {this.displayDescendents()} + {this.displayTopList()}
) From db83a203ec5752c2ddb7a504c84e72e957adf3e0 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 12:12:12 +0200 Subject: [PATCH 47/51] Recursivity Working --- client/src/components/DescendentsTree.js | 55 +++++++++++------------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js index ece5479ff..b51e1e123 100644 --- a/client/src/components/DescendentsTree.js +++ b/client/src/components/DescendentsTree.js @@ -35,12 +35,10 @@ import UserProfile from './UserProfile' import PortraitImage from '../img/portrait.jpg' const styles = theme => ({ - childrenCard: { + card: { maxWidth: 150, minWidth: 150, - paddingTop: '30', - marginBottom: 40, - marginLeft: 10 + paddingTop: '30' }, media: { height: 0, @@ -68,11 +66,6 @@ const styles = theme => ({ marginLeft: '10%', marginTop: 25 }, - formTypography: { - width: '80%', - marginLeft: '10%', - marginTop: 40 - }, button: { margin: theme.spacing.unit, marginTop: theme.spacing.unit * 3 @@ -97,11 +90,12 @@ class DescendentsTree extends Component { this.setState(state => ({ open: !state.open })); }; - displayNodeDescendents(node) { + displayNestedListItems(node) { const { classes } = this.props return node.descendents.map(descendent => { - return ( + return ( +
@@ -109,7 +103,7 @@ class DescendentsTree extends Component { - + - + {this.displayNestedList(descendent)} +
) } ) @@ -138,21 +133,23 @@ class DescendentsTree extends Component { displayNestedList(descendent) { const primaryText = "Children of " + descendent.alias - if (descendent.descendents.length > 0) { - return( - - - - {this.state.open ? : } - - - {this.displayNodeDescendents(descendent)} - - - ) - } else { - return (
) - } + if (descendent.descendents !== undefined) { + if (descendent.descendents.length > 0) { + return( + + + + {this.state.open ? : } + + + {this.displayNestedListItems(descendent)} + + + ) + } else { + return (
) + } + } } @@ -174,7 +171,7 @@ class DescendentsTree extends Component { - + Date: Fri, 28 Sep 2018 12:35:19 +0200 Subject: [PATCH 48/51] Star Icons Working --- client/src/components/DescendentsTree.js | 61 +++++++++++++++++------- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js index b51e1e123..6c4a9bf75 100644 --- a/client/src/components/DescendentsTree.js +++ b/client/src/components/DescendentsTree.js @@ -90,19 +90,50 @@ class DescendentsTree extends Component { this.setState(state => ({ open: !state.open })); }; - displayNestedListItems(node) { + displayIcons(amount) { + + switch (amount) { + case 1: return ( +
+ + + +
+ ) + case 2: return ( +
+ + + + + + +
+ ) + case 3: return ( +
+ + + + + + + + + +
+ ) + } + } + + displayNestedListItems(node, level) { const { classes } = this.props return node.descendents.map(descendent => { return ( -
+
- - - - - - + {this.displayIcons(level)} - {this.displayNestedList(descendent)} + {this.displayNestedList(descendent, level)}
) } ) } - displayNestedList(descendent) { + displayNestedList(descendent, level) { const primaryText = "Children of " + descendent.alias if (descendent.descendents !== undefined) { if (descendent.descendents.length > 0) { @@ -142,7 +173,7 @@ class DescendentsTree extends Component { {this.state.open ? : } - {this.displayNestedListItems(descendent)} + {this.displayNestedListItems(descendent, level + 1)} ) @@ -165,12 +196,10 @@ class DescendentsTree extends Component { } else { return data.descendents.map(descendent => { return ( -
+
- - - + {this.displayIcons(1)} - {this.displayNestedList(descendent)} + {this.displayNestedList(descendent, 1)}
) From 92e5a531385c5da298f89fcd2d66f90d7632a65d Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 16:01:56 +0200 Subject: [PATCH 49/51] No More Warnings --- client/src/components/DescendentsTree.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js index 6c4a9bf75..dccb7b6e7 100644 --- a/client/src/components/DescendentsTree.js +++ b/client/src/components/DescendentsTree.js @@ -10,7 +10,6 @@ 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' @@ -21,16 +20,10 @@ 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 InboxIcon from '@material-ui/icons/MoveToInbox'; -import DraftsIcon from '@material-ui/icons/Drafts'; -import SendIcon from '@material-ui/icons/Send'; import ExpandLess from '@material-ui/icons/ExpandLess'; import ExpandMore from '@material-ui/icons/ExpandMore'; import StarBorder from '@material-ui/icons/StarBorder'; -// components -import UserProfile from './UserProfile' - // Images import PortraitImage from '../img/portrait.jpg' @@ -123,6 +116,7 @@ class DescendentsTree extends Component {
) + default: return(
) } } From 9014496cba5a47342a34cba4d6f4d7ae804fdef0 Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 16:13:47 +0200 Subject: [PATCH 50/51] Descendents Tree Working --- client/src/components/DescendentsTree.js | 13 ++++++++----- client/src/components/YourDescendents.js | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js index dccb7b6e7..0d07207dd 100644 --- a/client/src/components/DescendentsTree.js +++ b/client/src/components/DescendentsTree.js @@ -29,8 +29,8 @@ import PortraitImage from '../img/portrait.jpg' const styles = theme => ({ card: { - maxWidth: 150, - minWidth: 150, + maxWidth: 300, + minWidth: 300, paddingTop: '30' }, media: { @@ -162,11 +162,14 @@ class DescendentsTree extends Component { if (descendent.descendents.length > 0) { return( - + { + this.setState(state => ({ [descendent.alias]: !state[descendent.alias] })); + } + }> - {this.state.open ? : } + {this.state[descendent.alias] ? : } - + {this.displayNestedListItems(descendent, level + 1)} diff --git a/client/src/components/YourDescendents.js b/client/src/components/YourDescendents.js index 550f1c846..d069f9311 100644 --- a/client/src/components/YourDescendents.js +++ b/client/src/components/YourDescendents.js @@ -1,5 +1,5 @@ import React, { Component } from 'react' -import {graphql, compose} from 'react-apollo' +import {compose} from 'react-apollo' // Materia UI From 3b5636d1edc6a76b51a93686cc332035c149b61a Mon Sep 17 00:00:00 2001 From: Luis Fernando Molina Date: Fri, 28 Sep 2018 16:18:25 +0200 Subject: [PATCH 51/51] Final Cleaning of the Day --- client/src/components/DescendentsTree.js | 2 +- client/src/components/ProfileImages.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/components/DescendentsTree.js b/client/src/components/DescendentsTree.js index 0d07207dd..c381bc70c 100644 --- a/client/src/components/DescendentsTree.js +++ b/client/src/components/DescendentsTree.js @@ -234,7 +234,7 @@ class DescendentsTree extends Component {
Your Children} + subheader={} className={classes.list} > {this.displayTopList()} diff --git a/client/src/components/ProfileImages.js b/client/src/components/ProfileImages.js index f3c769010..9df04d93c 100644 --- a/client/src/components/ProfileImages.js +++ b/client/src/components/ProfileImages.js @@ -118,7 +118,7 @@ class ProfileImages extends React.Component { let timePassed = now - avatarChangeDate let oneDay = 1000 * 60 * 60 * 24 -console.log("dates", avatarChangeDate, now, timePassed, oneDay) + if (timePassed < oneDay) { this.setState({