Merge pull request #28 from AAMasters/develop
One issue fix on client and code improvements on server
No files matched your search
@@ -1,6 +1,7 @@
|
||||
|
||||
client/node_modules/*
|
||||
server/node_modules/*
|
||||
client/src/auth/Auth0.js
|
||||
server/auth/Auth0.js
|
||||
server/models/MongoDB.js
|
||||
server/.env
|
||||
|
||||
client/package-lock.json
|
||||
server/package-lock.json
|
||||
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at feedback at advancedalgos dot org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -0,0 +1,45 @@
|
||||
# Contributing
|
||||
|
||||
We are thrilled you are interested in contributing to this repository. Your help may be essential to the project's success and we want you in!
|
||||
|
||||
Advanced Algos LTD is a startup, currently at a very early stage. Although this repository is not open source software yet, we do welcome contributions from the community.
|
||||
|
||||
Contributions are deemed _voluntary work_. There might be incentives in place for certain kinds of contributions. Contact us for more information in this regards.
|
||||
|
||||
In any case, contributors agree to provide an express grant of patent rights for their contributions. Submitting Pull Requests to this repository represents an express agreement with the terms of our Balanced Contributor Intellectual Property Agreement. This agreement is a direct implementation of GitHub's [Balanced Employee Intellectual Property Agreement 1.0.1](https://github.com/github/balanced-employee-ip-agreement/blob/master/Balanced_Employee_IP_Agreement.md).
|
||||
|
||||
# Balanced Contributor Intellectual Property Agreement 1.0.1
|
||||
|
||||
This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person or team submitting Pull Requests to this repository and Advanced Algos LTD ("Company").
|
||||
|
||||
**What is this?** This is the Company's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Company.
|
||||
|
||||
**Why is this?** The Company needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Company having the legal rights to the products and services it is providing so that the Company can continue operating and doing business.
|
||||
|
||||
The Company also believes that it's important to be clear on what it doesn't own. The Company doesn't want you looking over your shoulder every time you work on something personal or worrying that the Company will someday seize your open source non-lethal mousetrap simulation software. In other words, the Company isn't interested in appropriating your personal projects.
|
||||
|
||||
**Read this.** Please read this document and be sure you understand it before you submit any Pull Requests. Due to legal risk and corporate obligations, the Company cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Company's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on.
|
||||
|
||||
Cool? Then, by submitting your first Pull Request, and as a condition of your contribution, you agree to the following:
|
||||
|
||||
1. **What the Company owns.** The Company owns any IP ("Company IP") that you create, or help create _as its contributor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Company product or service at the time you developed, invented, or created it, (ii) developed for use by the Company, or (iii) developed or promoted with existing Company IP or with the Company's endorsement. You hereby grant and assign, and will grant and assign, to the Company all rights and interests in all Company IP.
|
||||
|
||||
"Company IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship.
|
||||
|
||||
2. **What the Company doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Company doesn't own it. In other words, the Company doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1.
|
||||
|
||||
3. **Contribution of your IP to Company projects.** If you include your own IP – such as IP you created prior to working for the Company – into a Company product or service, it's still yours, of course, but you grant the Company a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Company would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well.
|
||||
|
||||
4. **Your contributions to non-Company projects.** The Company recognizes that you may be engaged in work that requires you to submit IP to entities other than the Company, such as open source projects used by the Company. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Company and so should be run through Legal first.
|
||||
|
||||
5. **Voluntary Works** All Company IP that you are involved with or create as part of your work for the Company is voluntary work which does not require compensation. There might be incentives in place for certain kinds of contributions. Feel free to contact the Company to learn more.
|
||||
|
||||
6. **No conflicts.** You agree that you don't have any outstanding agreements or obligations that conflict with those in this Agreement, and that you won't enter into conflicting agreements in the future.
|
||||
|
||||
7. **IP protection.** The Company might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Company IP or IP under License (together, "All IP"), and, if the Company asks, to provide those records to the Company. You agree to help the Company secure and defend its rights in All IP. For your help the Company will compensate you at a reasonable rate. If the Company is unable to secure your help, you authorize the Company to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP.
|
||||
|
||||
8. **Confidentiality.** As an contributor or contractor of the Company, you will have access to sensitive confidential information that is important to the Company's business. You are responsible for keeping this information confidential, including after you end your work for the Company.
|
||||
|
||||
"Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Company because it is not publicly known.
|
||||
|
||||
The Company's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Company's Confidential Information for the purpose of performing your contribution and for the benefit of the Company. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise.
|
||||
@@ -0,0 +1,42 @@
|
||||
COPYRIGHT NOTICE
|
||||
|
||||
© Copyright 2018 Advanced Algos LTD.
|
||||
|
||||
|
||||
LICENSE NOTICE
|
||||
|
||||
Advanced Algos LTD. is a startup, currently at a very early stage. Because certain aspects of
|
||||
the business are still not clearly defined, we have chosen to --temporarily-- grant a
|
||||
restrictive license for the use of this repository, until those business aspects are clarified
|
||||
and a proper open source license can be granted.
|
||||
|
||||
Advanced Algos LTD. is publishing the code of this software for several reasons. However,
|
||||
it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company
|
||||
in the crypto space and this module is still proprietary software.
|
||||
|
||||
The reasons for opening up the code of this module at this stage are the following:
|
||||
|
||||
* We believe that transparency is paramount to achieving our goals, so we want everyone
|
||||
to be able to understand what this module does and how. Opening up the code of this module
|
||||
allows anyone to take a close look.
|
||||
|
||||
* We welcome contributions from the community. Contributions are regarded as voluntary work.
|
||||
There may be incentives in place for certain kinds of contributions. Contact us to learn more.
|
||||
Contributors agree to provide an express grant of patent rights for their contributions.
|
||||
|
||||
|
||||
LICENSE WORDING
|
||||
|
||||
Advanced Algos LTD. grants a limited license for the following uses:
|
||||
|
||||
1. CODE REVIEW AND AUDITS. You are granted permission to clone this repository to analyze,
|
||||
review or audit the code.
|
||||
|
||||
2. COLLABORATING WITH ADVANCED ALGOS LTD. You are granted permission to fork this repository
|
||||
with the sole purpose of correcting bugs and developing functionality in agreement with
|
||||
Advanced Algos LTD. By contributing to this repository you explicitely agree to the terms
|
||||
detailed in CONTRIBUTING.MD.
|
||||
|
||||
PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY.
|
||||
|
||||
|
||||
@@ -1,67 +1,2 @@
|
||||
# ModuleUsers
|
||||
|
||||
## Component Hierarchy
|
||||
|
||||
App.js
|
||||
|
|
||||
|- NavBar
|
||||
| |
|
||||
| |- LoggedInUser
|
||||
| | |
|
||||
| | |- LoggedInUserMenu
|
||||
| | | |
|
||||
| | | |- Menu 1 - Link to User
|
||||
| | | |
|
||||
| | | |- Menu 2 - Link to Logout
|
||||
| |
|
||||
|- Switch
|
||||
| |
|
||||
| |- User
|
||||
| | |
|
||||
| | |- UserTabs
|
||||
| | | |
|
||||
| | | |- Tab 1 - ProfileSheet
|
||||
| | | |
|
||||
| | | |- Tab 2 - ManageImages
|
||||
| |
|
||||
| |- Browse
|
||||
| | |
|
||||
| | |- UserLists
|
||||
| |
|
||||
| |- Search
|
||||
| |
|
||||
| |- About
|
||||
| |
|
||||
| |- Contact
|
||||
| |
|
||||
| |- Logout
|
||||
| |
|
||||
| |- Callback
|
||||
| |
|
||||
|- Footer
|
||||
|
||||
|
||||
|
||||
## Known Improvements
|
||||
|
||||
1. Remove the relationship to users, since it is not needed.
|
||||
|
||||
const RoleType = new GraphQLObjectType({
|
||||
name: 'Role',
|
||||
fields: () => ({
|
||||
id: { type: GraphQLID},
|
||||
name: {type: GraphQLString},
|
||||
users: {
|
||||
type: new GraphQLList(UserType),
|
||||
resolve(parent, args) {
|
||||
return User.find({roleId: parent.id});
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
2. Roles should be an Enum Type.
|
||||
|
||||
Since roles just have a name field, I think we should use the ENUM type... https://graphql.org/learn/schema/#enumeration-types
|
||||
They would just be resolved in Mongoose/MongoDB as strings and we would validate specifcially to those strings.
|
||||
Unless there's going to be many types of roles in the future.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
build/
|
||||
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at feedback at advancedalgos dot org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -0,0 +1,45 @@
|
||||
# Contributing
|
||||
|
||||
We are thrilled you are interested in contributing to this repository. Your help may be essential to the project's success and we want you in!
|
||||
|
||||
Advanced Algos LTD is a startup, currently at a very early stage. Although this repository is not open source software yet, we do welcome contributions from the community.
|
||||
|
||||
Contributions are deemed _voluntary work_. There might be incentives in place for certain kinds of contributions. Contact us for more information in this regards.
|
||||
|
||||
In any case, contributors agree to provide an express grant of patent rights for their contributions. Submitting Pull Requests to this repository represents an express agreement with the terms of our Balanced Contributor Intellectual Property Agreement. This agreement is a direct implementation of GitHub's [Balanced Employee Intellectual Property Agreement 1.0.1](https://github.com/github/balanced-employee-ip-agreement/blob/master/Balanced_Employee_IP_Agreement.md).
|
||||
|
||||
# Balanced Contributor Intellectual Property Agreement 1.0.1
|
||||
|
||||
This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person or team submitting Pull Requests to this repository and Advanced Algos LTD ("Company").
|
||||
|
||||
**What is this?** This is the Company's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Company.
|
||||
|
||||
**Why is this?** The Company needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Company having the legal rights to the products and services it is providing so that the Company can continue operating and doing business.
|
||||
|
||||
The Company also believes that it's important to be clear on what it doesn't own. The Company doesn't want you looking over your shoulder every time you work on something personal or worrying that the Company will someday seize your open source non-lethal mousetrap simulation software. In other words, the Company isn't interested in appropriating your personal projects.
|
||||
|
||||
**Read this.** Please read this document and be sure you understand it before you submit any Pull Requests. Due to legal risk and corporate obligations, the Company cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Company's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on.
|
||||
|
||||
Cool? Then, by submitting your first Pull Request, and as a condition of your contribution, you agree to the following:
|
||||
|
||||
1. **What the Company owns.** The Company owns any IP ("Company IP") that you create, or help create _as its contributor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Company product or service at the time you developed, invented, or created it, (ii) developed for use by the Company, or (iii) developed or promoted with existing Company IP or with the Company's endorsement. You hereby grant and assign, and will grant and assign, to the Company all rights and interests in all Company IP.
|
||||
|
||||
"Company IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship.
|
||||
|
||||
2. **What the Company doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Company doesn't own it. In other words, the Company doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1.
|
||||
|
||||
3. **Contribution of your IP to Company projects.** If you include your own IP – such as IP you created prior to working for the Company – into a Company product or service, it's still yours, of course, but you grant the Company a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Company would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well.
|
||||
|
||||
4. **Your contributions to non-Company projects.** The Company recognizes that you may be engaged in work that requires you to submit IP to entities other than the Company, such as open source projects used by the Company. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Company and so should be run through Legal first.
|
||||
|
||||
5. **Voluntary Works** All Company IP that you are involved with or create as part of your work for the Company is voluntary work which does not require compensation. There might be incentives in place for certain kinds of contributions. Feel free to contact the Company to learn more.
|
||||
|
||||
6. **No conflicts.** You agree that you don't have any outstanding agreements or obligations that conflict with those in this Agreement, and that you won't enter into conflicting agreements in the future.
|
||||
|
||||
7. **IP protection.** The Company might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Company IP or IP under License (together, "All IP"), and, if the Company asks, to provide those records to the Company. You agree to help the Company secure and defend its rights in All IP. For your help the Company will compensate you at a reasonable rate. If the Company is unable to secure your help, you authorize the Company to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP.
|
||||
|
||||
8. **Confidentiality.** As an contributor or contractor of the Company, you will have access to sensitive confidential information that is important to the Company's business. You are responsible for keeping this information confidential, including after you end your work for the Company.
|
||||
|
||||
"Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Company because it is not publicly known.
|
||||
|
||||
The Company's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Company's Confidential Information for the purpose of performing your contribution and for the benefit of the Company. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise.
|
||||
@@ -0,0 +1,42 @@
|
||||
COPYRIGHT NOTICE
|
||||
|
||||
© Copyright 2018 Advanced Algos LTD.
|
||||
|
||||
|
||||
LICENSE NOTICE
|
||||
|
||||
Advanced Algos LTD. is a startup, currently at a very early stage. Because certain aspects of
|
||||
the business are still not clearly defined, we have chosen to --temporarily-- grant a
|
||||
restrictive license for the use of this repository, until those business aspects are clarified
|
||||
and a proper open source license can be granted.
|
||||
|
||||
Advanced Algos LTD. is publishing the code of this software for several reasons. However,
|
||||
it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company
|
||||
in the crypto space and this module is still proprietary software.
|
||||
|
||||
The reasons for opening up the code of this module at this stage are the following:
|
||||
|
||||
* We believe that transparency is paramount to achieving our goals, so we want everyone
|
||||
to be able to understand what this module does and how. Opening up the code of this module
|
||||
allows anyone to take a close look.
|
||||
|
||||
* We welcome contributions from the community. Contributions are regarded as voluntary work.
|
||||
There may be incentives in place for certain kinds of contributions. Contact us to learn more.
|
||||
Contributors agree to provide an express grant of patent rights for their contributions.
|
||||
|
||||
|
||||
LICENSE WORDING
|
||||
|
||||
Advanced Algos LTD. grants a limited license for the following uses:
|
||||
|
||||
1. CODE REVIEW AND AUDITS. You are granted permission to clone this repository to analyze,
|
||||
review or audit the code.
|
||||
|
||||
2. COLLABORATING WITH ADVANCED ALGOS LTD. You are granted permission to fork this repository
|
||||
with the sole purpose of correcting bugs and developing functionality in agreement with
|
||||
Advanced Algos LTD. By contributing to this repository you explicitely agree to the terms
|
||||
detailed in CONTRIBUTING.MD.
|
||||
|
||||
PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY.
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createLogger, format, transports } from 'winston'
|
||||
const { combine, timestamp, simple } = format
|
||||
import { inspect } from 'util'
|
||||
|
||||
const options = {
|
||||
file: {
|
||||
level: 'info',
|
||||
filename: '../logs/app.log',
|
||||
handleExceptions: true,
|
||||
json: true,
|
||||
maxsize: 5242880, // 5MB
|
||||
maxFiles: 5,
|
||||
colorize: false,
|
||||
timestamp: true
|
||||
},
|
||||
console: {
|
||||
level: 'debug',
|
||||
handleExceptions: true,
|
||||
json: false,
|
||||
colorize: true,
|
||||
timestamp: true
|
||||
}
|
||||
}
|
||||
|
||||
const logger = createLogger({
|
||||
level: 'info',
|
||||
format: format.combine(
|
||||
format.colorize(),
|
||||
format.timestamp({ format: 'YY/MM/DD HH:mm' }),
|
||||
format.printf(
|
||||
info => `${info.timestamp} - ${info.level}: ${JSON.parse(JSON.stringify(inspect(info.message)))}`
|
||||
)
|
||||
),
|
||||
transports: [
|
||||
new transports.File(options.file),
|
||||
new transports.Console(options.console)
|
||||
],
|
||||
exitOnError: false // do not exit on handled exceptions
|
||||
})
|
||||
|
||||
export default logger
|
||||
@@ -1,34 +1,29 @@
|
||||
{
|
||||
"name": "client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"name": "@advancedalgos/users-client",
|
||||
"description": "Advanced Algos Users client-side web app",
|
||||
"version": "0.0.24",
|
||||
"main": "src/index.js",
|
||||
"dependencies": {
|
||||
"@material-ui/core": "^3.0.2",
|
||||
"@material-ui/core": "^3.2.0",
|
||||
"@material-ui/icons": "^3.0.1",
|
||||
"apollo-cache-inmemory": "^1.2.9",
|
||||
"apollo-client": "^2.4.1",
|
||||
"apollo-link": "^1.2.2",
|
||||
"apollo-link-context": "^1.0.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",
|
||||
"react-apollo": "^2.1.11",
|
||||
"react-dom": "^16.4.2",
|
||||
"react-easy-chart": "^1.0.0",
|
||||
"react-redux": "^5.0.7",
|
||||
"react": "^16.6.1",
|
||||
"react-apollo": "^2.2.4",
|
||||
"react-dom": "^16.6.1",
|
||||
"react-router-dom": "^4.3.1",
|
||||
"react-scripts": "1.1.5",
|
||||
"redux": "^4.0.0"
|
||||
"react-scripts": "^2.0.4",
|
||||
"graphql": "^14.0.2",
|
||||
"graphql-tag": "^2.10.0",
|
||||
"prop-types": "^15.6.2",
|
||||
"recompose": "^0.30.0",
|
||||
"apollo-client": "2.4.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test --env=jsdom",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"devDependencies": {
|
||||
"react-hot-loader": "^4.3.11"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 758 B |
@@ -1,57 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<style>
|
||||
|
||||
body {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
|
||||
</style>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is added to the
|
||||
homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Saira+Condensed:300,600|Saira:300,600&subset=latin-ext,vietnamese" rel="stylesheet">
|
||||
<!-- font awesome -->
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.13/css/all.css" integrity="sha384-DNOHZ68U8hZfKXOrtjWvjxusGo9WQnrNx2sqG0tfsghAvtVlRW3tvkXWZh58N9jp" crossorigin="anonymous">
|
||||
<!-- default font of Material-UI -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500">
|
||||
<!-- In order to use the font Icon component of Material-UI -->
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||
<title>Advanced Algos - Users Module</title>
|
||||
<!-- Material-UI is developed mobile first -->
|
||||
<meta
|
||||
name="viewport"
|
||||
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
|
||||
/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<noscript>
|
||||
You need to enable JavaScript to run this app.
|
||||
</noscript>
|
||||
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": "./index.html",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
@@ -1,109 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import { ApolloClient } from 'apollo-client'
|
||||
import { InMemoryCache } from 'apollo-cache-inmemory'
|
||||
import { ApolloProvider } from 'react-apollo'
|
||||
import { ApolloLink } from 'apollo-link'
|
||||
import { HttpLink } from 'apollo-link-http'
|
||||
import { onError } from 'apollo-link-error'
|
||||
import { setContext } from 'apollo-link-context'
|
||||
|
||||
import { Route, BrowserRouter, Switch } from 'react-router-dom'
|
||||
|
||||
// Material UI
|
||||
|
||||
import { MuiThemeProvider } from '@material-ui/core/styles'
|
||||
import theme from './theme'
|
||||
|
||||
// Components
|
||||
|
||||
import NavBar from './components/NavBar'
|
||||
|
||||
import Home from './components/Home'
|
||||
import User from './components/User'
|
||||
import Browse from './components/Browse'
|
||||
import Search from './components/Search'
|
||||
import About from './components/About'
|
||||
import Contact from './components/Contact'
|
||||
import Logout from './components/Logout'
|
||||
import Post from './components/Post'
|
||||
// import Footer from './components/Footer'
|
||||
import Callback from './components/Callback'
|
||||
|
||||
import Auth from './auth/Auth'
|
||||
|
||||
import {
|
||||
// NavBar,
|
||||
Footer
|
||||
} from './views'
|
||||
|
||||
// Apollo Client Setup
|
||||
const httpLink = new HttpLink({ uri: 'http://localhost:4000/graphql', changeOrigin: true })
|
||||
|
||||
const errorLink = onError(({ graphQLErrors, networkError }) => {
|
||||
if (graphQLErrors) {
|
||||
graphQLErrors.map(({ message, locations, path }) =>
|
||||
console.log(
|
||||
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
|
||||
),
|
||||
)
|
||||
}
|
||||
if (networkError) console.log(`[Network error]: ${networkError}`)
|
||||
})
|
||||
|
||||
const authLink = setContext((_, { headers }) => {
|
||||
// get the authentication token from local storage if it exists
|
||||
const token = localStorage.getItem('access_token')
|
||||
// return the headers to the context so httpLink can read them
|
||||
return {
|
||||
headers: {
|
||||
...headers,
|
||||
Authorization: token ? `Bearer ${token}` : ``
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const client = new ApolloClient({
|
||||
link: ApolloLink.from([errorLink, authLink, httpLink]),
|
||||
cache: new InMemoryCache()
|
||||
})
|
||||
|
||||
/*
|
||||
export const client = new ApolloClient({
|
||||
uri:'http://localhost:4000/graphql'
|
||||
})
|
||||
*/
|
||||
|
||||
export const auth = new Auth(result => console.log('auth result', result), client)
|
||||
|
||||
class App extends Component {
|
||||
render () {
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<ApolloProvider client={client}>
|
||||
<MuiThemeProvider theme={theme}>
|
||||
<div className='App'>
|
||||
<NavBar />
|
||||
<Switch>
|
||||
<Route exact path='/' component={Home} />
|
||||
<Route path='/user' component={User} />
|
||||
<Route path='/browse' component={Browse} />
|
||||
<Route path='/search' component={Search} />
|
||||
<Route path='/about' component={About} />
|
||||
<Route path='/contact' component={Contact} />
|
||||
<Route path='/logout' component={Logout} />
|
||||
<Route path='/:post_id' component={Post} />
|
||||
<Route path='/callback' render={(props) => {
|
||||
auth.handleAuthentication(props)
|
||||
return <Callback {...props} />
|
||||
}} />
|
||||
</Switch>
|
||||
<Footer />
|
||||
</div>
|
||||
</MuiThemeProvider>
|
||||
</ApolloProvider>
|
||||
</BrowserRouter>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default App
|
||||
@@ -1,6 +0,0 @@
|
||||
export const deletePost = (id) => {
|
||||
return {
|
||||
type: 'DELETE_POST',
|
||||
id
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 120 24"><defs><style>.cls-1,.cls-3{fill:none;}.cls-2{clip-path:url(#clip-path);}.cls-3{stroke:#2a96ff;stroke-miterlimit:10;stroke-width:0.5px;}.cls-4{fill:#fff;}.cls-5{fill:#e3493c;}</style><clipPath id="clip-path"><rect class="cls-1" x="272.54" y="79.59" width="600" height="600"/></clipPath></defs><title>aa-logo-dark</title><g id="Layer_1" data-name="Layer 1"><g class="cls-2"><circle class="cls-3" cx="338.14" cy="263.28" r="277.97"/></g></g><g id="Layer_4" data-name="Layer 4"><path class="cls-4" d="M27.67,15.74l-.19-1.58H26.29l-.2,1.58H24.51L25.7,7.79h2.39l1.19,8Zm-.75-6.39h-.08l-.4,3.41h.89Z"/><path class="cls-4" d="M32.15,7.79h1.93a2.39,2.39,0,0,1,1.46.35,1.68,1.68,0,0,1,.54,1.11,21,21,0,0,1,.1,2.52,20.93,20.93,0,0,1-.1,2.51,1.68,1.68,0,0,1-.54,1.11,2.39,2.39,0,0,1-1.46.35H32.15Zm1.93,6.51a.46.46,0,0,0,.37-.12,1.19,1.19,0,0,0,.13-.6c0-.32,0-.93,0-1.81s0-1.5,0-1.82a1.19,1.19,0,0,0-.13-.6.46.46,0,0,0-.37-.12h-.36V14.3Z"/><path class="cls-4" d="M39.94,15.74,38.87,7.79h1.59l.66,6.31h.12l.61-6.31h1.57l-1.07,8Z"/><path class="cls-4" d="M48.8,15.74l-.19-1.58H47.42l-.2,1.58H45.64l1.19-7.95h2.39l1.19,8Zm-.75-6.39H48l-.4,3.41h.89Z"/><path class="cls-4" d="M56.06,15.74l-1.23-4h0v4h-1.5V7.79h1.47l1.13,3.61h.06V7.79h1.51v8Z"/><path class="cls-4" d="M63.14,15.77a3.11,3.11,0,0,1-.59.06,2.06,2.06,0,0,1-1.36-.37,1.94,1.94,0,0,1-.58-1.19,17.1,17.1,0,0,1-.13-2.5,14.22,14.22,0,0,1,.15-2.5,1.94,1.94,0,0,1,.58-1.2,2,2,0,0,1,1.34-.37,4.21,4.21,0,0,1,.62,0,1.86,1.86,0,0,1,.45.12V9.2a5.77,5.77,0,0,0-.78-.06.77.77,0,0,0-.55.15,1.22,1.22,0,0,0-.18.64c0,.33,0,.94,0,1.84s0,1.5,0,1.83a1.22,1.22,0,0,0,.18.64.77.77,0,0,0,.55.15,4.74,4.74,0,0,0,.8-.06v1.29A1.56,1.56,0,0,1,63.14,15.77Z"/><path class="cls-4" d="M66.59,15.74V7.79h3.19V9.23h-1.6V11h1.41v1.45H68.18V14.3h1.6v1.44Z"/><path class="cls-4" d="M72.79,7.79h1.92a2.4,2.4,0,0,1,1.47.35,1.67,1.67,0,0,1,.53,1.11,20.46,20.46,0,0,1,.11,2.52,20.38,20.38,0,0,1-.11,2.51,1.67,1.67,0,0,1-.53,1.11,2.4,2.4,0,0,1-1.47.35H72.79Zm1.92,6.51a.48.48,0,0,0,.38-.12,1.19,1.19,0,0,0,.13-.6c0-.32,0-.93,0-1.81s0-1.5,0-1.82a1.19,1.19,0,0,0-.13-.6.48.48,0,0,0-.38-.12h-.35V14.3Z"/><path class="cls-5" d="M85.6,15.74l-.91-2.31H80.93L80,15.74h-.76l3.11-7.95h.89l3.12,8ZM82.84,8.66h-.06l-1.61,4.08h3.27Z"/><path class="cls-5" d="M88.44,15.74V7.79h.74V15H93v.72Z"/><path class="cls-5" d="M97.86,12.24v-.68h2.7v4c-.37.06-.8.12-1.26.16s-.87.06-1.22.06a5.68,5.68,0,0,1-2.14-.3A1.71,1.71,0,0,1,95,14.4a9.76,9.76,0,0,1-.26-2.63A9.85,9.85,0,0,1,95,9.13,1.71,1.71,0,0,1,96,8a5.91,5.91,0,0,1,2.13-.29c.38,0,.81,0,1.27.06s.86.1,1.18.16v.66a13.61,13.61,0,0,0-2-.19,8.76,8.76,0,0,0-2,.16,1.21,1.21,0,0,0-.79.85,8.45,8.45,0,0,0-.23,2.37A10.48,10.48,0,0,0,95.66,14a1.27,1.27,0,0,0,.7.91,4.41,4.41,0,0,0,1.72.24l.9,0c.35,0,.64,0,.87-.09V12.24Z"/><path class="cls-5" d="M103.84,15.54a1.48,1.48,0,0,1-.83-1.08,13.91,13.91,0,0,1-.17-2.69,14,14,0,0,1,.17-2.7A1.48,1.48,0,0,1,103.84,8a6,6,0,0,1,2.21-.29,5.94,5.94,0,0,1,2.2.29,1.46,1.46,0,0,1,.83,1.08,13.75,13.75,0,0,1,.18,2.7,13.7,13.7,0,0,1-.18,2.69,1.46,1.46,0,0,1-.83,1.08,5.94,5.94,0,0,1-2.2.29A6,6,0,0,1,103.84,15.54Zm3.93-.57a1,1,0,0,0,.61-.81,13.62,13.62,0,0,0,.14-2.39,13.68,13.68,0,0,0-.14-2.4,1,1,0,0,0-.61-.81,8.84,8.84,0,0,0-3.45,0,1,1,0,0,0-.6.81,12.47,12.47,0,0,0-.15,2.4,12.42,12.42,0,0,0,.15,2.39,1,1,0,0,0,.6.81,8.84,8.84,0,0,0,3.45,0Z"/><path class="cls-5" d="M112.94,15.79c-.5,0-.94-.06-1.31-.11V15l1.33.1c.48,0,.88.05,1.2.05a8.75,8.75,0,0,0,1-.05,1.78,1.78,0,0,0,.65-.22.68.68,0,0,0,.35-.47,3.13,3.13,0,0,0,.09-.83,4.54,4.54,0,0,0-.06-.9.57.57,0,0,0-.31-.37,3.72,3.72,0,0,0-.88-.18l-2-.22a2.08,2.08,0,0,1-1.06-.32,1.18,1.18,0,0,1-.44-.64,5,5,0,0,1-.09-1.07,2.57,2.57,0,0,1,.3-1.38,1.56,1.56,0,0,1,.9-.63,6.12,6.12,0,0,1,1.59-.16c.45,0,.9,0,1.35,0s.81.08,1.07.13v.66c-.31,0-.68-.07-1.11-.1s-.83,0-1.19,0a6.52,6.52,0,0,0-1.1.07,1.88,1.88,0,0,0-.66.22.73.73,0,0,0-.33.44,2.52,2.52,0,0,0-.08.75,2.94,2.94,0,0,0,.07.77.61.61,0,0,0,.29.36,2.05,2.05,0,0,0,.69.16l2,.21a4.86,4.86,0,0,1,1.17.24,1,1,0,0,1,.55.63,4.24,4.24,0,0,1,.13,1.27,3,3,0,0,1-.31,1.54,1.36,1.36,0,0,1-.85.64,5.78,5.78,0,0,1-1.47.14C113.93,15.83,113.45,15.82,112.94,15.79Z"/><path class="cls-5" d="M11.36,13.89a7.43,7.43,0,0,1,2.52.4c.82.58,2,.89,2.5.3s0-1.61-.79-2.38a7.64,7.64,0,0,1-.93-2.35c.11-1-.23-2.23-1-2.35s-1.47,1-1.65,1.88a7.38,7.38,0,0,1-1.61,2c-.9.42-1.77,1.3-1.49,2S10.48,14.16,11.36,13.89Z"/><path class="cls-5" d="M14.75,3.42C16,3.47,16.49,6,16.29,7.91a26.16,26.16,0,0,0,1.64,4.69,1.88,1.88,0,0,0,.79.79,10.55,10.55,0,0,1-.84-5c.79-2.19.44-5.15-.75-5.84a2.71,2.71,0,0,0-3.68,1.2A1.8,1.8,0,0,1,14.75,3.42Z"/><path class="cls-5" d="M5,14C4.4,13,6.3,11.26,8.09,10.45,8.66,10.2,11,7.2,11.33,6.7a2,2,0,0,0,.29-1.09A10.62,10.62,0,0,1,7.68,8.86C5.38,9.27,3,11.05,3,12.42A2.71,2.71,0,0,0,5.87,15,1.88,1.88,0,0,1,5,14Z"/><path class="cls-5" d="M19.12,17.42c-.66,1-3.07.23-4.67-.92a25.88,25.88,0,0,0-4.87-.92,1.79,1.79,0,0,0-1.08.29,10.37,10.37,0,0,1,4.Line truncated
|
||||
|
Before Width: | Height: | Size: 5.0 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 98 140"><defs><style>.cls-1,.cls-3{fill:none;}.cls-2{clip-path:url(#clip-path);}.cls-3{stroke:#2a96ff;stroke-miterlimit:10;stroke-width:0.5px;}.cls-4{fill:#4d4d4d;}.cls-5{fill:#e3493c;}</style><clipPath id="clip-path"><rect class="cls-1" x="144.54" y="59.59" width="600" height="600"/></clipPath></defs><title>aa-logo-vert</title><g id="Layer_1" data-name="Layer 1"><g class="cls-2"><circle class="cls-3" cx="210.14" cy="243.28" r="277.97"/></g></g><g id="Layer_4" data-name="Layer 4"><path class="cls-4" d="M11.27,108.29l-.38-3H8.62l-.38,3h-3L7.49,93.1h4.57l2.28,15.19ZM9.83,96.08H9.68L8.9,102.6h1.71Z"/><path class="cls-4" d="M18,93.1h3.69a4.53,4.53,0,0,1,2.79.67,3.22,3.22,0,0,1,1,2.11,41.13,41.13,0,0,1,.19,4.82,41,41,0,0,1-.19,4.81,3.19,3.19,0,0,1-1,2.11,4.46,4.46,0,0,1-2.79.67H18Zm3.69,12.43a.89.89,0,0,0,.71-.22,2.31,2.31,0,0,0,.25-1.15c0-.61,0-1.77,0-3.46s0-2.85,0-3.47a2.31,2.31,0,0,0-.25-1.15.89.89,0,0,0-.71-.22H21v9.67Z"/><path class="cls-4" d="M31.16,108.29,29.11,93.1h3l1.25,12.06h.23L34.8,93.1h3l-2.05,15.19Z"/><path class="cls-4" d="M46.35,108.29l-.38-3H43.7l-.38,3h-3L42.57,93.1h4.57l2.28,15.19ZM44.91,96.08h-.15L44,102.6h1.7Z"/><path class="cls-4" d="M58.44,108.29l-2.36-7.62H56v7.62H53.12V93.1h2.8L58.09,100h.11V93.1h2.89v15.19Z"/><path class="cls-4" d="M70.19,108.36a6.43,6.43,0,0,1-1.12.11,4,4,0,0,1-2.61-.71,3.72,3.72,0,0,1-1.1-2.28,31.28,31.28,0,0,1-.26-4.78,29.46,29.46,0,0,1,.28-4.77,3.82,3.82,0,0,1,1.12-2.3,3.9,3.9,0,0,1,2.57-.71,7.72,7.72,0,0,1,1.18.1,3.21,3.21,0,0,1,.85.24v2.53a11.75,11.75,0,0,0-1.48-.11,1.48,1.48,0,0,0-1.05.29,2.16,2.16,0,0,0-.35,1.23c0,.62-.08,1.79-.08,3.5s0,2.87.08,3.5a2.15,2.15,0,0,0,.35,1.22,1.48,1.48,0,0,0,1.05.29,9.89,9.89,0,0,0,1.53-.11v2.47A4.23,4.23,0,0,1,70.19,108.36Z"/><path class="cls-4" d="M75,108.29V93.1h6.09v2.76H78v3.38h2.7V102H78v3.53h3.07v2.76Z"/><path class="cls-4" d="M85.08,93.1h3.69a4.55,4.55,0,0,1,2.79.67,3.16,3.16,0,0,1,1,2.11,38.45,38.45,0,0,1,.2,4.82,38.37,38.37,0,0,1-.2,4.81,3.14,3.14,0,0,1-1,2.11,4.48,4.48,0,0,1-2.79.67H85.08Zm3.69,12.43a.89.89,0,0,0,.71-.22,2.31,2.31,0,0,0,.25-1.15c0-.61,0-1.77,0-3.46s0-2.85,0-3.47a2.31,2.31,0,0,0-.25-1.15.89.89,0,0,0-.71-.22h-.69v9.67Z"/><path class="cls-5" d="M19.79,134.79l-2.1-5.3H9.08L7,134.79H5.24l7.15-18.23h2l7.16,18.23Zm-6.33-16.24h-.14L9.64,127.9h7.5Z"/><path class="cls-5" d="M26.41,134.79V116.56h1.7v16.58H37v1.65Z"/><path class="cls-5" d="M48.1,126.76V125.2h6.2v9.3c-.87.14-1.84.26-2.9.35s-2,.15-2.8.15a12.88,12.88,0,0,1-4.9-.69,3.89,3.89,0,0,1-2.15-2.6,22.5,22.5,0,0,1-.58-6,22.11,22.11,0,0,1,.6-6A3.87,3.87,0,0,1,43.72,117a13.15,13.15,0,0,1,4.88-.67c.88,0,1.85.05,2.92.14s2,.22,2.7.36v1.51a33.49,33.49,0,0,0-4.66-.42,18.69,18.69,0,0,0-4.55.37,2.7,2.7,0,0,0-1.81,1.93,18.81,18.81,0,0,0-.53,5.43,23.5,23.5,0,0,0,.39,5.13,3,3,0,0,0,1.59,2.09,10.15,10.15,0,0,0,4,.55c.56,0,1.25,0,2.07-.08s1.47-.12,2-.19v-6.41Z"/><path class="cls-5" d="M61.91,134.32A3.33,3.33,0,0,1,60,131.86a31.94,31.94,0,0,1-.41-6.19,31.89,31.89,0,0,1,.41-6.18A3.33,3.33,0,0,1,61.91,117a13.45,13.45,0,0,1,5.07-.67A13.42,13.42,0,0,1,72,117a3.33,3.33,0,0,1,1.89,2.47,31.89,31.89,0,0,1,.41,6.18,31.94,31.94,0,0,1-.41,6.19A3.33,3.33,0,0,1,72,134.32,13.38,13.38,0,0,1,67,135,13.4,13.4,0,0,1,61.91,134.32Zm9-1.29a2.34,2.34,0,0,0,1.37-1.86,29.55,29.55,0,0,0,.34-5.5,29.7,29.7,0,0,0-.34-5.5,2.32,2.32,0,0,0-1.37-1.85,13.69,13.69,0,0,0-4-.38,13.73,13.73,0,0,0-4,.38,2.32,2.32,0,0,0-1.37,1.85,28.82,28.82,0,0,0-.34,5.5,28.68,28.68,0,0,0,.34,5.5A2.34,2.34,0,0,0,63,133a13.73,13.73,0,0,0,4,.38A13.69,13.69,0,0,0,70.94,133Z"/><path class="cls-5" d="M82.89,134.91c-1.17-.07-2.18-.15-3-.25v-1.59c.91.08,1.93.16,3,.23s2,.11,2.76.11a17.72,17.72,0,0,0,2.32-.12,4,4,0,0,0,1.49-.49,1.67,1.67,0,0,0,.81-1.07,7.68,7.68,0,0,0,.2-1.92,9.6,9.6,0,0,0-.15-2.07,1.14,1.14,0,0,0-.71-.83,7.63,7.63,0,0,0-2-.42L83,126a4.91,4.91,0,0,1-2.42-.74,2.61,2.61,0,0,1-1-1.47,10.4,10.4,0,0,1-.23-2.45,5.81,5.81,0,0,1,.71-3.17,3.47,3.47,0,0,1,2-1.44,14.24,14.24,0,0,1,3.66-.37c1,0,2,0,3.09.1a19.49,19.49,0,0,1,2.44.29v1.51c-.7-.08-1.55-.16-2.54-.22s-1.9-.09-2.73-.09a15.54,15.54,0,0,0-2.52.16,4.18,4.18,0,0,0-1.51.5,1.74,1.74,0,0,0-.76,1,6,6,0,0,0-.19,1.72,6.83,6.83,0,0,0,.17,1.76,1.32,1.32,0,0,0,.67.82,4.9,4.9,0,0,0,1.57.39l4.51.47a10.3,10.3,0,0,1,2.69.56,2.23,2.23,0,0,1,1.25,1.43,9.9,9.9,0,0,1,.3,2.91,6.84,6.84,0,0,1-.72,3.54,3.17,3.17,0,0,1-2,1.47,14.17,14.17,0,0,1-3.38.32C85.14,135,84.05,135,82.89,134.91Z"/><path class="cls-5" d="M47.08,49.62c1.17-.36,8.61.83,9.61,1.53,3.13,2.21,7.71,3.41,9.55,1.15s-.08-6.13-3-9.1c-.88-.88-3.7-7.75-3.56-9,.42-3.87-.85-8.5-3.73-9s-5.61,3.66-6.3,7.16c-.24,1.22-5,7.11-6.14,7.62-3.48,1.62-6.77,5-5.73,7.69S43.7,50.65,47.08,49.62Z"/><path class="cls-5" d="M60,9.61c4.68.2,6.65,9.72,5.89,17.18-.24,2.37,5.27,15.71,6.24,17.91a7.29,7.29,0,0,0,3,3C71.63,38.07,71.32,30.32,72,28.51c3-8.4,1.71-19.69-2.85-22.32-3.07-1.77-9.79-2.48-14.07,4.56A6.91,6.91,0,0,1,60,9.61Z"/><path class="cls-5" d="M22.61,50.2C20.Line truncated
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
Before Width: | Height: | Size: 502 B |
|
Before Width: | Height: | Size: 758 B |
|
Before Width: | Height: | Size: 15 KiB |
@@ -1,9 +0,0 @@
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#e3493c">
|
||||
<meta name="apple-mobile-web-app-title" content="Advanced Algos">
|
||||
<meta name="application-name" content="Advanced Algos">
|
||||
<meta name="msapplication-TileColor" content="#da532c">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
Before Width: | Height: | Size: 2.5 KiB |
@@ -1 +0,0 @@
|
||||
<svg version="1" xmlns="http://www.w3.org/2000/svg" width="1365.333" height="1365.333" viewBox="0 0 1024.000000 1024.000000"><path d="M685.9 27.4c-.2.2-3 .7-6.1 1-12.6 1.5-34.6 8.5-45.2 14.5-2 1.2-5.3 3.1-7.3 4.2-14.3 8.1-30.2 22.3-43.5 38.8-5.9 7.4-16.2 22.1-15.5 22.1.2 0 3.6-1.7 7.5-3.9 17.4-9.6 28.8-12.1 52.2-11.7 13.4.3 17.7.8 23.1 2.5 25.1 8 45.2 36.6 58.9 83.7 1.8 6 3.3 11.6 3.5 12.4 2.3 10.8 3.5 17.1 4 20 .3 1.9 1 6 1.5 9 1.2 7.3 1.8 11.3 2.5 18.5.4 3.3.8 7.6 1.1 9.5 1.6 14.3 2.2 51.4 1 69.5-1.4 21.3-1.5 26-.5 31 .5 2.7 1.2 6.3 1.5 8 2.3 12.9 20.6 69 33.4 102.5 1.6 4.1 4.8 12.4 7.1 18.5 17.1 45.2 47.3 118.6 49.1 119.3.4.2.8.9.8 1.5.1 3.8 18.3 22 29.2 29.1 4.7 3.1 8.8 5.6 9 5.6.3 0-3.2-10.9-7.7-24.3-4.5-13.3-8.4-25.3-8.7-26.7-.4-1.4-2-7.2-3.7-13-4.3-14.5-12.2-45.3-13.7-54-.3-1.4-.7-3.3-1-4.2-.3-1-.7-3-1-4.5-.2-1.6-.9-4.8-1.4-7.3-.5-2.5-1.4-7.2-2-10.5-.5-3.3-1.2-7.1-1.5-8.5-.3-1.4-1-5.7-1.5-9.5-.6-3.9-1.2-8.1-1.5-9.5-.3-2.3-.7-5.4-2-16.3-3.1-25.8-3.8-58.6-1.5-73.2.7-4.4 2.2-10.7 3.5-14 2.3-6.1 9-28.9 9.9-33.5.3-1.4 1.2-5.2 2-8.5s1.7-7.4 1.9-9c.3-1.7 1.4-8.2 2.6-14.6 1.1-6.3 2.3-14.7 2.6-18.5.4-3.8.9-8.3 1.1-9.9 2.2-16.2 2.2-69.4 0-83.5-.2-1.4-.7-5-1.1-8-6.7-56.6-26.4-102.4-53.1-123.1-11.1-8.5-31-16.4-49.9-19.7-9.6-1.7-38.4-2.9-39.6-1.8zM465 215.4c-2.3 3-9.7 11.7-14.5 17-1.1 1.2-5.6 6.2-10 11.1-8.3 9.3-43.9 45.2-53.5 54-51.1 46.9-108.8 86-132.5 90-2.2.4-4.4.8-5 .9-.5.2-2.8.6-5 1.1-24.5 4.7-70 20.4-91 31.3-2.7 1.4-5.2 2.6-5.5 2.7-.7.1-12.3 6.2-16.7 8.7-2.1 1.2-5.4 3.1-7.5 4.2C62.4 471.4 16.3 519 4.3 559.8.9 571.3.5 590 3.4 603.5c2.6 12.1 7.2 25.6 11.8 35 19.6 39.1 52.7 65.8 95.3 76.8 5.5 1.4 12 2.9 14.5 3.2 2.5.4 5.4.9 6.5 1.1 2.8.5 11.7 1.4 19.5 1.9l6.4.4-3.4-2.1c-20.8-12.5-29.7-21.3-39-38.3-1.8-3.3-4.3-7.8-5.5-10-6.1-11.2-8.2-22.8-6.4-35.1 7.4-48 72.3-111.2 159.4-155 10.5-5.3 21-11.2 23.5-13.1 8.7-6.7 26.9-25.3 45.4-46.3 1.7-2 4.9-5.6 7.1-8.1 2.2-2.4 6-6.9 8.5-9.8 2.5-3 5-5.9 5.5-6.5.6-.6 3-3.6 5.5-6.6 6.5-8 7.1-8.6 15.2-18.5 12.7-15.4 51.5-64.5 61.3-77.6 1.1-1.5 5.9-7.8 10.6-14 11.5-15.2 13.3-18.1 16.8-26.9 3-7.7 3.5-9.3 5.1-19 1.2-7.1 1.9-21.9 1.1-22.4-.3-.2-1.7 1.1-3.1 2.8zM563.9 315c-21.5 5.4-43.9 28.6-59.4 61.3-5.2 11-10.1 24.9-12.5 35.7-2 8.8-6.1 16.2-21 37.4-1.8 2.6-17.8 23.2-20.7 26.6-14.7 17.6-22.4 26.2-31.2 35.1-8.7 8.6-12.2 11.3-19.6 15.2-16.7 8.7-25.6 14.5-37 23.8-22.2 18.1-36.8 38.2-41 56.4-1.6 6.6-2.2 17.6-1.1 19.3.3.5.7 2.1 1 3.7.8 4.3 5.3 12.7 9.1 16.8 10.1 11.1 27.7 18.5 50.5 21.3 12.2 1.5 36.1.8 49-1.6 11.6-2 12.6-2.3 20.5-4.5s31.7-2.2 43.5 0c.8.2 4.4.6 8 1 3.6.4 7.4.9 8.4 1.1 1.1.2 4.2.6 6.9.9 2.8.3 5.3.8 5.6.9.3.2 2.9.7 5.6 1 9.1 1.3 39 7.5 47.9 10.1 8.9 2.6 18.5 7 20.6 9.6 1.9 2.3 30.1 16.5 39.5 20 9.1 3.3 26.6 7.7 33.7 8.6 9.2 1.1 23.9.7 30-.7 19.5-4.6 33.3-18.5 36.3-36.7.7-4.2.8-19 .1-20.1-.2-.4-.7-2.4-1-4.6-1-6.1-5.5-18.3-10.3-28.1-4.2-8.3-15.4-26.1-20.1-31.6-5.8-7-14.5-17-18.6-21.4-2.6-2.8-6.5-8-8.7-11.7-6.3-10.8-17.6-35.1-25.4-55.3-1.5-3.9-3.3-8.4-4-10-1.4-3.5-4.9-12.8-5-13.5 0-.3-1.2-3.7-2.5-7.5-1.3-3.9-2.5-7.5-2.6-8-.1-.6-.9-3.5-1.8-6.6-2.6-8.9-2.8-10-2.6-17.4.7-25.5.3-39.3-1.3-49.5-.3-1.4-.8-4.5-1.2-7-.3-2.4-.8-4.7-1-5.1-.2-.3-.7-2.1-1-4-.9-5.4-6.3-20.6-9.7-27.3-7.5-14.9-16.6-24.9-27.5-30.3-6.3-3.1-8.3-3.6-16-3.9-4.8-.2-10.4.1-12.4.6zM340 752.1c-12.3 2.1-18.9 4.4-31.5 10.7-10.5 5.3-11.8 4.4 13 9.2 69 13.5 135.5 34.9 186.8 60.2 24.5 12.1 41 22.8 49.7 32.3 24.3 26.6 37.9 39.4 60 56.6 50.3 39.2 116.2 69.8 162.5 75.5 2.8.3 6.8.9 9 1.3 4.8.9 28.3.6 34-.3 23.2-4 37.1-11.8 56.6-31.7 33-33.8 46.8-81.2 37.4-128.4-3.2-16-8-29.9-16.2-46.5l-4.2-8.5-.1 8c-.1 4.4-.3 8.7-.4 9.5-.2.8-.6 3.8-1 6.6-1.8 14.4-15.1 40.8-26.4 52.7-17.8 18.7-53.6 23.1-99.2 12.2-5.8-1.4-11.5-2.7-12.6-3-1.2-.3-7.7-2.3-14.5-4.6-36.3-12-78-32.8-111.4-55.7-6-4.1-13-8.4-15.5-9.4-2.5-1.1-5.4-2.3-6.5-2.8-5.6-2.4-39.7-11.2-52.5-13.5-1.9-.3-4.2-.7-5-.9-.8-.1-4-.8-7-1.4-9.8-2-26.4-5.1-30-5.6-3-.5-29.1-4.8-37-6.1-1.4-.2-4.3-.7-6.5-1-6.9-1.1-9.8-1.5-15.5-2.5-3-.5-8.2-1.2-11.5-1.6-3.3-.3-6.2-.7-6.5-.9-.3-.2-4.4-.9-9-1.5-4.7-.6-9.6-1.3-11-1.5-1.4-.2-6.3-.9-11-1.5s-9.6-1.3-11-1.5c-5.9-.8-10.8-1.5-15-2-5.1-.6-9.4-1.2-16-2.1-7.1-.9-20.1-1.1-25-.3z"/></svg>
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"name": "Advanced Algos",
|
||||
"short_name": "Advanced Algos",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
@@ -1,207 +0,0 @@
|
||||
import Auth0Lock from 'auth0-lock'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
import { getItem, setItem, removeItem } from '../utils/local-storage'
|
||||
import { validObject, deleteCookie } from '../utils/js-helpers'
|
||||
|
||||
import { client } from '../App'
|
||||
|
||||
import { AUTH_CONFIG } from './Auth0' // create by renaming Auth0.sample.js to Auth0.js and setting vars
|
||||
|
||||
const AUTHENTICATE = gql`
|
||||
mutation authenticate($idToken: String!) {
|
||||
authenticate(idToken: $idToken) {
|
||||
alias
|
||||
authId
|
||||
}
|
||||
}
|
||||
`
|
||||
const lock = new Auth0Lock(AUTH_CONFIG.clientId, AUTH_CONFIG.domain, {
|
||||
oidcConformant: true,
|
||||
autoclose: true,
|
||||
auth: {
|
||||
sso: true,
|
||||
redirectUrl: window.location.origin + '/callback',
|
||||
responseType: 'token id_token',
|
||||
audience: `${AUTH_CONFIG.api_audience}`,
|
||||
params: {
|
||||
scope: `openid profile email user_metadata app_metadata picture`
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
class Auth {
|
||||
constructor (cb, apolloClient) {
|
||||
this.handleAuthentication()
|
||||
// binds functions to keep this context
|
||||
this.apolloClient = apolloClient
|
||||
this.cb = cb.bind(this)
|
||||
this.login = this.login.bind(this)
|
||||
this.logout = this.logout.bind(this)
|
||||
this.isAuthenticated = this.isAuthenticated.bind(this)
|
||||
this.checkSession = this.checkSession.bind(this)
|
||||
}
|
||||
|
||||
login () {
|
||||
// Call the show method to display the widget.
|
||||
lock.show()
|
||||
}
|
||||
|
||||
handleAuthentication () {
|
||||
// Add a callback for Lock's `authenticated` event
|
||||
lock.on('authenticated', this.setSession.bind(this))
|
||||
// Add a callback for Lock's `authorization_error` event
|
||||
lock.on('authorization_error', err => {
|
||||
console.log(err)
|
||||
const data = { status: `error`, errMessage: err.error }
|
||||
this.cb(data)
|
||||
})
|
||||
}
|
||||
|
||||
checkSession () {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Add a callback for Lock's `authenticated` event
|
||||
lock.checkSession(
|
||||
{
|
||||
responseType: 'token id_token',
|
||||
audience: AUTH_CONFIG.api_audience,
|
||||
scope:
|
||||
'openid email profile read:teams write:teams read:member write:member',
|
||||
connection: 'github',
|
||||
prompt: 'none'
|
||||
},
|
||||
function (err, authResult) {
|
||||
if (!err) {
|
||||
resolve(authResult)
|
||||
} else {
|
||||
reject(err)
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
setSession (authResult) {
|
||||
console.log('setSession')
|
||||
if (authResult && authResult.accessToken && authResult.idToken) {
|
||||
// Set the time that the access token will expire at
|
||||
let expiresAt = JSON.stringify(
|
||||
authResult.expiresIn * 1000 + new Date().getTime()
|
||||
)
|
||||
setItem('access_token', authResult.accessToken)
|
||||
setItem('id_token', authResult.idToken)
|
||||
setItem('expires_at', expiresAt)
|
||||
const data = {
|
||||
status: `success`,
|
||||
accessToken: authResult.accessToken,
|
||||
idToken: authResult.idToken,
|
||||
expiresAt
|
||||
}
|
||||
console.log('setSession idTokenPayload: ', authResult.idTokenPayload)
|
||||
|
||||
this.signinOrCreateAccount({ ...data })
|
||||
this.cb(data)
|
||||
console.log('idTokenPayload', authResult.idTokenPayload)
|
||||
setItem('authUser', JSON.stringify(authResult.idTokenPayload))
|
||||
setItem('authId', authResult.idTokenPayload.sub)
|
||||
if (window.location.href.includes(`callback`)) {
|
||||
window.location.href = '/'
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
async signinOrCreateAccount ({ accessToken, idToken, expiresAt }) {
|
||||
console.log(
|
||||
'signinOrCreateAccount: ',
|
||||
client,
|
||||
this.apolloClient,
|
||||
accessToken,
|
||||
idToken,
|
||||
expiresAt
|
||||
)
|
||||
try {
|
||||
const data = await client.mutate({
|
||||
mutation: AUTHENTICATE,
|
||||
variables: { idToken }
|
||||
})
|
||||
|
||||
console.log('signinOrCreateAccount auth: ', await data)
|
||||
setItem('authUser', JSON.stringify(data.data.authenticate))
|
||||
setItem('authId', data.data.authenticate.authId)
|
||||
if (window.location.href.includes(`callback`)) {
|
||||
window.location.href = '/'
|
||||
} else {
|
||||
window.location.reload()
|
||||
}
|
||||
return data
|
||||
} catch (err) {
|
||||
return console.log('Sign in or create account error: ', err)
|
||||
}
|
||||
}
|
||||
|
||||
logout () {
|
||||
// Clear access token and ID token from local storage
|
||||
removeItem('access_token')
|
||||
removeItem('id_token')
|
||||
removeItem('expires_at')
|
||||
removeItem('authUser')
|
||||
removeItem('auth0.ssodata')
|
||||
window.localStorage.clear()
|
||||
deleteCookie('ajs_anonymous_id')
|
||||
deleteCookie('ajs_user_id')
|
||||
deleteCookie('current_tenant')
|
||||
|
||||
lock.logout({ returnTo: AUTH_CONFIG.logoutUrl })
|
||||
console.log('logout')
|
||||
}
|
||||
|
||||
async isAuthenticated () {
|
||||
// check session and run Auth0 SS0
|
||||
const getUser = await getItem('authUser')
|
||||
let user = JSON.parse(getUser)
|
||||
|
||||
if (validObject(user, 'exp') && new Date().getTime() < (user.exp * 1000)) {
|
||||
console.log('handleAuth.user exp: ', user, window.location.href)
|
||||
|
||||
return user
|
||||
}
|
||||
|
||||
const checkSSO = await this.checkSession()
|
||||
.then(result => {
|
||||
console.log('handleAuth.checksessions: ', result)
|
||||
setItem('authUser', result.idTokenPayload.sub)
|
||||
setItem('authId', result.idTokenPayload.sub)
|
||||
// user confirmed, log into client
|
||||
this.setSession(result)
|
||||
return result.idTokenPayload
|
||||
})
|
||||
.catch(err => {
|
||||
console.log('handleAuth.checksessions err: ', err)
|
||||
if (err.error === 'login_required') {
|
||||
// return this.login()
|
||||
}
|
||||
return err
|
||||
})
|
||||
if (
|
||||
(window.location.href.includes(`manage`) ||
|
||||
window.location.href.includes(`profile`)) &&
|
||||
!user
|
||||
) {
|
||||
window.location.href = '/'
|
||||
}
|
||||
|
||||
if (/manage|profile|create|dashboard/.test(window.location.href) && !user) {
|
||||
this.login()
|
||||
}
|
||||
|
||||
console.log('handleAuth.isAuthenticated 2: ', checkSSO, user)
|
||||
if (validObject(checkSSO, 'error')) {
|
||||
return false
|
||||
} else {
|
||||
return checkSSO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Auth
|
||||
@@ -1,7 +0,0 @@
|
||||
export const AUTH_CONFIG = {
|
||||
api_audience: '<AUTH0_API_AUDIENCE>',
|
||||
domain: '<AUTH0_API_DOMAIN>',
|
||||
clientId: '<AUTH0_API_CLIENTID>',
|
||||
callbackUrl: '<AUTH0_CALLBACK_URL>',
|
||||
logoutUrl: '<AUTH0_LOGOUT_URL>'
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
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 CssBaseline from '@material-ui/core/CssBaseline'
|
||||
|
||||
class About extends React.Component {
|
||||
state = {
|
||||
open: true
|
||||
}
|
||||
|
||||
handleClickOpen = () => {
|
||||
this.setState({ open: true })
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ open: false })
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<Dialog
|
||||
open={this.state.open}
|
||||
onClose={this.handleClose}
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'>
|
||||
<DialogTitle id='alert-dialog-title'>
|
||||
{"Section under development."}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id='alert-dialog-description'>
|
||||
The functionality of this section has not been developed yet. We expect to have it for October 2018. Thanks for your understanding.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={this.handleClose} color='primary' autoFocus>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default About
|
||||
@@ -1,30 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withStyles } from '@material-ui/core/styles';
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
const styles = theme => ({
|
||||
button: {
|
||||
margin: theme.spacing.unit,
|
||||
},
|
||||
input: {
|
||||
display: 'none',
|
||||
},
|
||||
});
|
||||
|
||||
function AddUserNextButton(props) {
|
||||
const { classes } = props;
|
||||
return (
|
||||
<div>
|
||||
<Button type="submit" variant="contained" color="primary" className={classes.button}>
|
||||
Next
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
AddUserNextButton.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default withStyles(styles)(AddUserNextButton);
|
||||
@@ -0,0 +1,18 @@
|
||||
import React from 'react'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
|
||||
const BannerTopBar = ({
|
||||
size,
|
||||
backgroundUrl,
|
||||
title,
|
||||
text
|
||||
}) => (
|
||||
<div className={`bannerTopBar ${size}`} style={backgroundUrl ? { backgroundImage: `url(${backgroundUrl})` } : {}}>
|
||||
<div className='caption'>
|
||||
<Typography className='title' variant='h1' align='center'>{title}</Typography>
|
||||
<Typography className='text' variant='h2' align='center'>{text}</Typography>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
export default BannerTopBar
|
||||
@@ -1,14 +1,24 @@
|
||||
import React from 'react'
|
||||
import React, { Component } from 'react'
|
||||
import UserList from './UserList'
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
import BannerTopBar from './BannerTopBar'
|
||||
|
||||
const Browse = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<UserList />
|
||||
</React.Fragment>
|
||||
)
|
||||
class Browse extends Component {
|
||||
|
||||
render () {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<BannerTopBar
|
||||
size='medium'
|
||||
title='Users Directory'
|
||||
text='Everyone involved in Advanced Algos is here.'
|
||||
backgroundUrl='https://advancedalgos.net/img/photos/users.jpg'
|
||||
/>
|
||||
<div className='container'>
|
||||
<UserList />
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Browse
|
||||
@@ -1,13 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
|
||||
class Callback extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
Authenticating...
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Callback
|
||||
@@ -1,52 +0,0 @@
|
||||
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 CssBaseline from '@material-ui/core/CssBaseline'
|
||||
|
||||
class Contact extends React.Component {
|
||||
state = {
|
||||
open: true
|
||||
}
|
||||
|
||||
handleClickOpen = () => {
|
||||
this.setState({ open: true })
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ open: false })
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<Dialog
|
||||
open={this.state.open}
|
||||
onClose={this.handleClose}
|
||||
aria-labelledby='alert-dialog-title'
|
||||
aria-describedby='alert-dialog-description'>
|
||||
<DialogTitle id='alert-dialog-title'>
|
||||
{"Section under development."}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id='alert-dialog-description'>
|
||||
The functionality of this section has not been developed yet. We expect to have it for October 2018. Thanks for your understanding.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={this.handleClose} color='primary' autoFocus>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Contact
|
||||
@@ -25,7 +25,7 @@ import ExpandMore from '@material-ui/icons/ExpandMore';
|
||||
import StarBorder from '@material-ui/icons/StarBorder';
|
||||
|
||||
// Images
|
||||
import PortraitImage from '../img/portrait.jpg'
|
||||
import UserDefaultPicture from '../img/user-default-pic.jpg'
|
||||
|
||||
const styles = theme => ({
|
||||
card: {
|
||||
@@ -132,7 +132,7 @@ class DescendentsTree extends Component {
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image={PortraitImage}
|
||||
image={UserDefaultPicture}
|
||||
title='User Profile'
|
||||
|
||||
/>
|
||||
@@ -188,10 +188,10 @@ class DescendentsTree extends Component {
|
||||
if (data.loading) {
|
||||
return (<div> Loading Descendents... </div>)
|
||||
} else {
|
||||
if (data.descendents === undefined) {
|
||||
if (data.users_Descendents === undefined) {
|
||||
return (<div> No Descendents to Display </div>)
|
||||
} else {
|
||||
return data.descendents.map(descendent => {
|
||||
return data.users_Descendents.map(descendent => {
|
||||
return (
|
||||
<div key={descendent.alias}>
|
||||
|
||||
@@ -201,7 +201,7 @@ class DescendentsTree extends Component {
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image={PortraitImage}
|
||||
image={UserDefaultPicture}
|
||||
title='User Profile'
|
||||
|
||||
/>
|
||||
|
||||
@@ -1,105 +1,41 @@
|
||||
import React, { Component } from 'react'
|
||||
import { compose } from 'recompose'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import {LineChart} from 'react-easy-chart'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
padding: 100
|
||||
},
|
||||
grid: {
|
||||
margin: theme.spacing.unit,
|
||||
width: '50%'
|
||||
},
|
||||
typography: {
|
||||
width: '100%',
|
||||
marginTop: 20,
|
||||
marginBottom: 20
|
||||
},
|
||||
'@global': {
|
||||
body: {
|
||||
backgroundColor: theme.palette.common.white
|
||||
}
|
||||
},
|
||||
layout: {
|
||||
width: 'auto',
|
||||
marginLeft: theme.spacing.unit * 3,
|
||||
marginRight: theme.spacing.unit * 3,
|
||||
[theme.breakpoints.up(900 + theme.spacing.unit * 3 * 2)]: {
|
||||
width: 900,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto'
|
||||
}
|
||||
},
|
||||
heroContent: {
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px`
|
||||
}
|
||||
})
|
||||
import BannerTopBar from './BannerTopBar'
|
||||
import { Typography } from '@material-ui/core'
|
||||
|
||||
class Home extends Component {
|
||||
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<main className={classes.layout}>
|
||||
<div className={classes.heroContent}>
|
||||
<Typography
|
||||
variant='display3'
|
||||
align='center'
|
||||
color='textPrimary'
|
||||
gutterBottom
|
||||
>
|
||||
Collaborate. Innovate. Evolve.
|
||||
<BannerTopBar
|
||||
size='big'
|
||||
title='Users Module'
|
||||
text='Responsible for all human users of the Advanced Algos Platform.'
|
||||
backgroundUrl='https://advancedalgos.net/img/photos/users.jpg'
|
||||
/>
|
||||
<div className='homePage container'>
|
||||
<Typography variant='h1' align='center' className='title'>Welcome to the Users Module!</Typography>
|
||||
<Typography variant='h2' align='center' className='subtitle'>The human component of the Advanced Algos Ecosystem.</Typography>
|
||||
<div className='column'>
|
||||
<Typography align='justify'>
|
||||
The Advanced Algos Ecosystem is made out of humans, financial beings and other entities such as teams.
|
||||
</Typography>
|
||||
<Typography
|
||||
variant='title'
|
||||
align='center'
|
||||
color='textSecondary'
|
||||
component='h3'
|
||||
>
|
||||
Welcome to Advanced Algos Users Module
|
||||
<Typography align='justify'>
|
||||
Humans are either <strong>users</strong> or <strong>guests</strong>. If you haven’t signed up yet or you are logged off, then you are acting as a guest, with limited features. Please sign-up or log-in for a richer experience.
|
||||
</Typography>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Grid container justify='center' spacing={24}>
|
||||
<Grid className={classes.grid} item>
|
||||
<Typography className={classes.typography} gutterBottom variant='headline' component='h2'>
|
||||
Intro
|
||||
</Typography>
|
||||
<Typography className={classes.typography} component='p'>
|
||||
This module is responsible for managing users across the Advanced Algos system. Here you can browse the Users Directory, search for users and get to their profiles. You can create your own User profile if you sign up, or manage it if you already have one by logging in.
|
||||
</Typography>
|
||||
<Typography className={classes.typography} gutterBottom variant='headline' component='h2'>
|
||||
Development Status
|
||||
</Typography>
|
||||
<Typography className={classes.typography} component='p'>
|
||||
This module is is currently under heavy development. Implementation started 1st of September 2018 and this is the result of the 2nd sprint of 2 weeks of work each.
|
||||
</Typography>
|
||||
<Typography className={classes.typography} gutterBottom variant='headline' component='h2'>
|
||||
What can you do?
|
||||
</Typography>
|
||||
<Typography className={classes.typography} component='p'>
|
||||
Currently you can use the Browse section to list all registered users. At the Search page you can searh for users by name. Clicking on View Profile you can see some more info about that user. You can signup with your Github account and manage your profile information while logged in. While loggedin, you can edit your profile info, define your referrer and view your descendants. You will find all features under develpment with a dialog indicating it is not ready.
|
||||
</Typography>
|
||||
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<div className='column'>
|
||||
<Typography align='justify'>
|
||||
Users may have different roles, such as developer, trader, analyst, etc. Each of these roles usually have personalized features throughout the system.
|
||||
</Typography>
|
||||
<Typography align='justify'>
|
||||
The Users Module allows you to handle all of your details as a user of the system as well as finding other people in the ecosystem.
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withStyles(styles)
|
||||
)(Home)
|
||||
export default Home
|
||||
@@ -1,14 +1,69 @@
|
||||
import React, { Component } from 'react'
|
||||
import {graphql} from 'react-apollo'
|
||||
import {graphql, compose} from 'react-apollo'
|
||||
import {getUserByAuthIdQuery} from '../queries/queries'
|
||||
import {auth} from '../App'
|
||||
import LoggedInUserMenu from './LoggedInUserMenu'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Button from '@material-ui/core/Button'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
|
||||
// icons
|
||||
import UserIcon from '@material-ui/icons/Person'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import ProfileSheetIcon from '@material-ui/icons/Create'
|
||||
import ProfileImagesIcon from '@material-ui/icons/Wallpaper'
|
||||
import YourReferrerIcon from '@material-ui/icons/AccessibilityNew'
|
||||
import DescendentsIcon from '@material-ui/icons/DeviceHub'
|
||||
|
||||
const ProfileSheetLink = props => <Link to='/users/profile-sheet' {...props} />
|
||||
const ProfileImagesLink = props => <Link to='/users/profile-images' {...props} />
|
||||
const YourReferrerLink = props => <Link to='/users/referrer' {...props} />
|
||||
const YourDescendentsLink = props => <Link to='/users/descendents' {...props} />
|
||||
|
||||
const UserLink = props => <Link to='/users/user' {...props} />
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
flexGrow: 1
|
||||
},
|
||||
flex: {
|
||||
flexGrow: 1,
|
||||
marginLeft: 30
|
||||
},
|
||||
menuButton: {
|
||||
marginLeft: -12,
|
||||
marginRight: 20
|
||||
},
|
||||
cssRoot: {
|
||||
color: '#FFFFFF',
|
||||
backgroundColor: theme.palette.secondary,
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.dark
|
||||
},
|
||||
whiteSpace: 'nowrap',
|
||||
paddingRight: 2 * theme.spacing.unit,
|
||||
paddingLeft: 2 * theme.spacing.unit
|
||||
},
|
||||
button: {
|
||||
margin: theme.spacing.unit
|
||||
},
|
||||
leftIcon: {
|
||||
marginRight: theme.spacing.unit
|
||||
},
|
||||
rightIcon: {
|
||||
marginLeft: theme.spacing.unit
|
||||
},
|
||||
iconSmall: {
|
||||
fontSize: 20
|
||||
}
|
||||
})
|
||||
|
||||
class LoggedInUser extends Component {
|
||||
|
||||
displayLoggedInUser () {
|
||||
const user = this.props.data.userByAuthId
|
||||
const user = this.props.data.users_UserByAuthId
|
||||
const { classes, match } = this.props
|
||||
|
||||
if (user) {
|
||||
let displayName = 'No Display Name'
|
||||
@@ -34,15 +89,73 @@ class LoggedInUser extends Component {
|
||||
let identityProvider = authArray[0]
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p><LoggedInUserMenu menuLabel={displayName} identityProvider={identityProvider} /></p>
|
||||
</div>
|
||||
<li className='menu-item-has-children'>
|
||||
<Button
|
||||
variant='text'
|
||||
size='small'
|
||||
className={classNames(classes.button, classes.cssRoot)}
|
||||
title='Manage Your Profile'
|
||||
component={UserLink}
|
||||
to={`${match.url}/user`}>
|
||||
<UserIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
|
||||
Your Profile
|
||||
</Button>
|
||||
<ul>
|
||||
<li>
|
||||
<Button
|
||||
variant='text'
|
||||
size='small'
|
||||
className={classNames(classes.button, classes.cssRoot)}
|
||||
title='Profile Sheet'
|
||||
component={ProfileSheetLink}
|
||||
to={`${match.url}/profile-sheet`}>
|
||||
<ProfileSheetIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
|
||||
Profile Sheet
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<Button
|
||||
variant='text'
|
||||
size='small'
|
||||
className={classNames(classes.button, classes.cssRoot)}
|
||||
title='Profile Images'
|
||||
component={ProfileImagesLink}
|
||||
to={`${match.url}/profile-images`}>
|
||||
<ProfileImagesIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
|
||||
Profile Images
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<Button
|
||||
variant='text'
|
||||
size='small'
|
||||
className={classNames(classes.button, classes.cssRoot)}
|
||||
title='Your Referrer'
|
||||
component={YourReferrerLink}
|
||||
to={`${match.url}/referrer`}>
|
||||
<YourReferrerIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
|
||||
Your Referrer
|
||||
</Button>
|
||||
</li>
|
||||
<li>
|
||||
<Button
|
||||
variant='text'
|
||||
size='small'
|
||||
className={classNames(classes.button, classes.cssRoot)}
|
||||
title='Your Descendents'
|
||||
component={YourDescendentsLink}
|
||||
to={`${match.url}/descendents`}>
|
||||
<DescendentsIcon className={classNames(classes.leftIcon, classes.iconSmall)} />
|
||||
Your Descendents
|
||||
</Button>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div onClick={() => auth.login()}>
|
||||
<Button color='inherit'>Login / Sign up</Button>
|
||||
</div>
|
||||
<div />
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -53,28 +166,31 @@ class LoggedInUser extends Component {
|
||||
|
||||
render () {
|
||||
if (this.loggedInUserStored !== true) {
|
||||
let user = this.props.data.userByAuthId
|
||||
let user = this.props.data.users_UserByAuthId
|
||||
|
||||
if (user !== undefined) {
|
||||
localStorage.setItem('loggedInUser', JSON.stringify(this.props.data.userByAuthId))
|
||||
localStorage.setItem('loggedInUser', JSON.stringify(this.props.data.users_UserByAuthId))
|
||||
this.loggedInUserStored = true
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<React.Fragment>
|
||||
{this.displayLoggedInUser()}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default graphql(getUserByAuthIdQuery, { // What follows is the way to pass a parameter to a query.
|
||||
options: (props) => {
|
||||
return {
|
||||
variables: {
|
||||
authId: props.authId
|
||||
export default compose(
|
||||
graphql(getUserByAuthIdQuery, { // What follows is the way to pass a parameter to a query.
|
||||
options: (props) => {
|
||||
return {
|
||||
variables: {
|
||||
authId: props.authId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})(LoggedInUser) // This binds the querty to the component
|
||||
}),
|
||||
withStyles(styles)
|
||||
)(LoggedInUser)
|
||||
@@ -1,98 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import toRenderProps from 'recompose/toRenderProps'
|
||||
import withState from 'recompose/withState'
|
||||
import { Link } from 'react-router-dom'
|
||||
import {compose} from 'react-apollo'
|
||||
|
||||
// Materia UI
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Button from '@material-ui/core/Button'
|
||||
import Menu from '@material-ui/core/Menu'
|
||||
import MenuItem from '@material-ui/core/MenuItem'
|
||||
|
||||
// Images
|
||||
import GithubLogo from '../img/github-head-negative.png'
|
||||
import EmailIcon from '../img/email.png'
|
||||
import ProfileIcon from '@material-ui/icons/Person'
|
||||
import LogoutIcon from '@material-ui/icons/DirectionsWalk'
|
||||
|
||||
const UserLink = props => <Link to='/user' {...props} />
|
||||
const LogoutLink = props => <Link to='/logout' {...props} />
|
||||
|
||||
const WithState = toRenderProps(withState('anchorEl', 'updateAnchorEl', null))
|
||||
|
||||
const styles = {
|
||||
img: {
|
||||
margin: 20,
|
||||
display: 'block',
|
||||
maxWidth: 15,
|
||||
maxHeight: 15
|
||||
}
|
||||
}
|
||||
|
||||
class LoggedInUserMenu extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
user: {}
|
||||
}
|
||||
}
|
||||
|
||||
displayIdentityProvider () {
|
||||
const { classes } = this.props
|
||||
switch (this.props.identityProvider) {
|
||||
case 'github':
|
||||
return (
|
||||
<img className={classes.img} src={GithubLogo} alt='Github' />
|
||||
)
|
||||
break
|
||||
case 'auth0':
|
||||
return (
|
||||
<img className={classes.img} src={EmailIcon} alt='Auth0' />
|
||||
)
|
||||
break
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<WithState>
|
||||
{({ anchorEl, updateAnchorEl }) => {
|
||||
const open = Boolean(anchorEl)
|
||||
const handleClose = () => {
|
||||
updateAnchorEl(null)
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Button
|
||||
aria-owns={open ? 'render-props-menu' : null}
|
||||
aria-haspopup='true'
|
||||
onClick={event => {
|
||||
updateAnchorEl(event.currentTarget)
|
||||
}}
|
||||
color='inherit'
|
||||
>
|
||||
{this.displayIdentityProvider()}
|
||||
{this.props.menuLabel}
|
||||
</Button>
|
||||
<Menu id='render-props-menu' anchorEl={anchorEl} open={open} onClose={handleClose}>
|
||||
<MenuItem onClick={handleClose} component={UserLink}><ProfileIcon /><div>Profile</div></MenuItem>
|
||||
<MenuItem onClick={handleClose} component={LogoutLink}><LogoutIcon /><div>Logout</div></MenuItem>
|
||||
</Menu>
|
||||
</React.Fragment>
|
||||
)
|
||||
}}
|
||||
</WithState>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withStyles(styles)
|
||||
)(LoggedInUserMenu)
|
||||
@@ -1,14 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import {Redirect} from 'react-router';
|
||||
import {auth} from '../App';
|
||||
|
||||
class Logout extends Component {
|
||||
render() {
|
||||
//window.localStorage.removeItem("user");
|
||||
auth.logout();
|
||||
|
||||
return <Redirect to='/' />
|
||||
}
|
||||
}
|
||||
|
||||
export default Logout
|
||||
@@ -1,48 +0,0 @@
|
||||
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';
|
||||
|
||||
class Modules extends React.Component {
|
||||
state = {
|
||||
open: true,
|
||||
};
|
||||
|
||||
handleClickOpen = () => {
|
||||
this.setState({ open: true });
|
||||
};
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ open: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Dialog
|
||||
open={this.state.open}
|
||||
onClose={this.handleClose}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">{"Section under development."}</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
The functionality of this section has not been developed yet. We expect to have it for October 2018. Thanks for your understanding.
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={this.handleClose} color="primary" autoFocus>
|
||||
Close
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Modules;
|
||||
@@ -1,122 +0,0 @@
|
||||
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'
|
||||
|
||||
// icons
|
||||
import HomeIcon from '@material-ui/icons/Home'
|
||||
import BrowseIcon from '@material-ui/icons/ImportContacts'
|
||||
import SearchIcon from '@material-ui/icons/Search'
|
||||
import ContactIcon from '@material-ui/icons/ContactMail'
|
||||
import AboutIcon from '@material-ui/icons/FormatShapes'
|
||||
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
// components
|
||||
import LoggedInUser from './LoggedInUser'
|
||||
|
||||
// Images
|
||||
import AALogo from '../assets/logos/advanced-algos/aa-logo-dark.svg'
|
||||
|
||||
const AboutLink = props => <Link to='/about' {...props} />
|
||||
const ContactLink = props => <Link to='/contact' {...props} />
|
||||
const SearchLink = props => <Link to='/search' {...props} />
|
||||
const BrowseLink = props => <Link to='/browse' {...props} />
|
||||
const HomeLink = props => <Link to='/' {...props} />
|
||||
|
||||
const styles = {
|
||||
root: {
|
||||
flexGrow: 1
|
||||
},
|
||||
flex: {
|
||||
flexGrow: 1
|
||||
},
|
||||
menuButton: {
|
||||
marginLeft: -12,
|
||||
marginRight: 20
|
||||
},
|
||||
img: {
|
||||
margin: 20,
|
||||
display: 'block',
|
||||
maxWidth: 240,
|
||||
maxHeight: 48
|
||||
}
|
||||
}
|
||||
|
||||
class NavBar extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
authId: null
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const authId = localStorage.getItem('authId')
|
||||
|
||||
if (authId !== null && authId !== undefined && authId !== 'undefined') {
|
||||
this.setState({ authId: authId })
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<div className={classes.root}>
|
||||
<AppBar position='static'>
|
||||
<Toolbar>
|
||||
<img className={classes.img} src={AALogo} alt='Advanced Algos' />
|
||||
<Typography variant='title' color='inherit' className={classes.flex}>
|
||||
Users
|
||||
</Typography>
|
||||
<IconButton
|
||||
className={classes.menuButton}
|
||||
color='inherit'
|
||||
title='Home'
|
||||
component={HomeLink}>
|
||||
<HomeIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
className={classes.menuButton}
|
||||
color='inherit'
|
||||
title='Browse the Users Directory'
|
||||
component={BrowseLink}>
|
||||
<BrowseIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
className={classes.menuButton}
|
||||
color='inherit'
|
||||
title='Search Users'
|
||||
component={SearchLink}>
|
||||
<SearchIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
className={classes.menuButton}
|
||||
color='inherit'
|
||||
title='Contact Form'
|
||||
component={ContactLink}>
|
||||
<ContactIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
className={classes.menuButton}
|
||||
color='inherit'
|
||||
title='About this Module'
|
||||
component={AboutLink}>
|
||||
<AboutIcon />
|
||||
</IconButton>
|
||||
<LoggedInUser authId={this.state.authId} />
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
NavBar.propTypes = {
|
||||
classes: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default withStyles(styles)(NavBar)
|
||||
@@ -1,47 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import { connect } from 'react-redux'
|
||||
import { deletePost } from '../actions/postActions'
|
||||
|
||||
class Post extends Component {
|
||||
handleClick = () => {
|
||||
this.props.deletePost(this.props.post.id);
|
||||
this.props.history.push('/');
|
||||
}
|
||||
render() {
|
||||
|
||||
const post = this.props.post ? (
|
||||
<div className="post">
|
||||
<h4 className="center">{this.props.post.title}</h4>
|
||||
<p>{this.props.post.body}</p>
|
||||
<div className="center">
|
||||
<button className="btn grey" onClick={this.handleClick}>
|
||||
Delete Post
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="center">Loading post...</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="container">
|
||||
{post}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = (state, ownProps) => {
|
||||
let id = ownProps.match.params.post_id;
|
||||
return {
|
||||
post: state.posts.find(post => post.id === id)
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch) => {
|
||||
return {
|
||||
deletePost: (id) => dispatch(deletePost(id))
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Post)
|
||||
@@ -1,322 +1,42 @@
|
||||
import React from 'react';
|
||||
import {graphql, compose} from 'react-apollo'
|
||||
import {updateUserImagesMutation, getUsersQuery} from '../queries/queries'
|
||||
import Avatar from '@material-ui/core/Avatar';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import ReactFilestack from 'filestack-react'
|
||||
|
||||
// Material-ui
|
||||
|
||||
import React, { Component } from 'react'
|
||||
import { compose } from 'recompose'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
|
||||
const options = {
|
||||
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',
|
||||
},
|
||||
};
|
||||
|
||||
const styles = theme => ({
|
||||
|
||||
button: {
|
||||
margin: theme.spacing.unit,
|
||||
marginTop: theme.spacing.unit * 3
|
||||
},
|
||||
row: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
avatar: {
|
||||
margin: 10,
|
||||
},
|
||||
bigAvatar: {
|
||||
width: 60,
|
||||
height: 60,
|
||||
},
|
||||
root: {
|
||||
width: '50%',
|
||||
flexGrow: 1,
|
||||
padding: 10,
|
||||
marginLeft: '25%',
|
||||
marginTop: '2%'
|
||||
marginTop: '5%',
|
||||
marginBottom: '10%'
|
||||
},
|
||||
typography: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
marginTop: 40
|
||||
marginTop: 40,
|
||||
marginBottom: 40
|
||||
}
|
||||
})
|
||||
|
||||
class ProfileImages extends React.Component {
|
||||
class ProfileImages extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.defaultValuesSet = false
|
||||
|
||||
this.state = {
|
||||
avatarHandle: '',
|
||||
avatarChangeDate: '',
|
||||
newAvatarHandle: '',
|
||||
saveDisabled: true,
|
||||
uploadToolDisabled: false,
|
||||
showNewAvatar: false,
|
||||
needToWait: false,
|
||||
removeCurrentAvatar: false
|
||||
}
|
||||
}
|
||||
|
||||
submitForm (e) {
|
||||
e.preventDefault()
|
||||
this.props.updateUserImagesMutation({
|
||||
variables: {
|
||||
id: this.state.id,
|
||||
avatarHandle: this.state.newAvatarHandle,
|
||||
avatarChangeDate: this.state.avatarChangeDate
|
||||
},
|
||||
refetchQueries: [{ query: getUsersQuery}] // This allow us to re run whatever queries are necesary after the mutation.
|
||||
})
|
||||
|
||||
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'))
|
||||
|
||||
user.avatarHandle = this.state.newAvatarHandle
|
||||
user.avatarChangeDate = this.state.avatarChangeDate
|
||||
|
||||
localStorage.setItem('loggedInUser', JSON.stringify(user))
|
||||
}
|
||||
|
||||
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.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
|
||||
|
||||
|
||||
|
||||
if (timePassed < oneDay) {
|
||||
this.setState({
|
||||
needToWait: true
|
||||
})
|
||||
} else {
|
||||
this.setState({
|
||||
needToWait: false
|
||||
})
|
||||
}
|
||||
|
||||
/* Now we are ready to set the initial state. */
|
||||
|
||||
this.setState({
|
||||
id: user.id,
|
||||
avatarHandle: user.avatarHandle,
|
||||
avatarChangeDate: user.avatarChangeDate
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
onSuccess = (uploadResults) => {
|
||||
console.log('onSuccess')
|
||||
console.log(uploadResults)
|
||||
|
||||
const handle = uploadResults.filesUploaded[0].handle
|
||||
|
||||
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 = (err) => {
|
||||
console.log('onError')
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
onRemoveSuccess = (err) => {
|
||||
console.log('onRemoveSuccess')
|
||||
console.log(err)
|
||||
}
|
||||
|
||||
showCurrentAvatar() {
|
||||
|
||||
const { classes } = this.props
|
||||
|
||||
if (this.state.removeCurrentAvatar === false) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
This is your current Avatar:
|
||||
</Typography>
|
||||
|
||||
<Grid container justify='center' >
|
||||
<Grid item>
|
||||
<Avatar
|
||||
alt="Avatar"
|
||||
src={"https://cdn.filestackcontent.com/" + this.state.avatarHandle}
|
||||
className={classNames(classes.bigAvatar)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
|
||||
} else {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ReactFilestack
|
||||
apikey={'AH97QJOXTHwdBXjydQgABz'}
|
||||
mode="remove"
|
||||
options={options}
|
||||
fileHandle={this.state.avatarHandle}
|
||||
onSuccess={this.onRemoveSuccess}
|
||||
onError={this.onError}
|
||||
link
|
||||
></ReactFilestack>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
showNewAvatar() {
|
||||
if (this.state.showNewAvatar === true) {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<div>
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
This is how your new avatar will look like. Press save to proceed with the change.
|
||||
</Typography>
|
||||
|
||||
<Grid container justify='center' >
|
||||
<Grid item>
|
||||
<Avatar
|
||||
alt="Avatar"
|
||||
src={"https://cdn.filestackcontent.com/" + this.state.newAvatarHandle}
|
||||
className={classNames(classes.bigAvatar)}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
showUploadTool() {
|
||||
const { classes } = this.props
|
||||
if (this.state.needToWait === true) {
|
||||
return (
|
||||
<div>
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
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.
|
||||
</Typography>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return(
|
||||
<div>
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
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.
|
||||
</Typography>
|
||||
<Grid container justify='center' >
|
||||
<Grid item>
|
||||
<Button
|
||||
disabled={this.state.uploadToolDisabled}
|
||||
variant='contained'
|
||||
color='secondary'
|
||||
className={classes.button}>
|
||||
<ReactFilestack
|
||||
apikey={'AH97QJOXTHwdBXjydQgABz'}
|
||||
options={options}
|
||||
onSuccess={this.onSuccess}
|
||||
onError={this.onError}
|
||||
link
|
||||
>File Upload Tool</ReactFilestack>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<Typography className={classes.typography} variant='headline' gutterBottom>
|
||||
Profile Images
|
||||
<React.Fragment>
|
||||
<Paper className={classes.root}>
|
||||
<Typography className={classes.typography} variant='h5' gutterBottom>
|
||||
Comming Soon
|
||||
</Typography>
|
||||
<form onSubmit={this.submitForm.bind(this)}>
|
||||
|
||||
{this.showCurrentAvatar()}
|
||||
{this.showUploadTool()}
|
||||
{this.showNewAvatar()}
|
||||
|
||||
<Grid container justify='center' >
|
||||
<Grid item>
|
||||
<Button
|
||||
disabled={this.state.saveDisabled}
|
||||
variant='contained'
|
||||
color='secondary'
|
||||
className={classes.button}
|
||||
onClick={this.submitForm.bind(this)}
|
||||
>Save</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</Paper>
|
||||
|
||||
);
|
||||
</Paper>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
graphql(updateUserImagesMutation, {name: 'updateUserImagesMutation'}),
|
||||
withStyles(styles)
|
||||
)(ProfileImages) // This technique binds more than one query to a single component.
|
||||
)(ProfileImages)
|
||||
@@ -27,11 +27,11 @@ import AALogo from '../img/aa-logo.png'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
width: '50%',
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
padding: 10,
|
||||
marginLeft: '25%',
|
||||
marginTop: '2%'
|
||||
marginTop: '5%',
|
||||
marginBottom: '10%'
|
||||
},
|
||||
inputField: {
|
||||
width: '80%',
|
||||
@@ -85,7 +85,8 @@ class ProfileSheet extends Component {
|
||||
firstNameError: false,
|
||||
middleNameError: false,
|
||||
lastNameError: false,
|
||||
bioError: false
|
||||
bioError: false,
|
||||
updated: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +97,7 @@ class ProfileSheet extends Component {
|
||||
<MenuItem><em>Loading roles</em></MenuItem>
|
||||
)
|
||||
} else {
|
||||
return data.roles.map(role => {
|
||||
return data.users_Roles.map(role => {
|
||||
return (
|
||||
<MenuItem key={role.id} value={role.id}>{ role.name }</MenuItem>
|
||||
)
|
||||
@@ -108,7 +109,6 @@ class ProfileSheet extends Component {
|
||||
e.preventDefault()
|
||||
this.props.updateUserMutation({
|
||||
variables: {
|
||||
id: this.state.id,
|
||||
firstName: this.state.firstName,
|
||||
middleName: this.state.middleName,
|
||||
lastName: this.state.lastName,
|
||||
@@ -135,6 +135,8 @@ class ProfileSheet extends Component {
|
||||
user.role.id = this.state.roleId
|
||||
|
||||
localStorage.setItem('loggedInUser', JSON.stringify(user))
|
||||
|
||||
this.setState({updated: true})
|
||||
}
|
||||
|
||||
handleCheckBoxes (e) {
|
||||
@@ -241,10 +243,11 @@ class ProfileSheet extends Component {
|
||||
|
||||
componentWillMount () {
|
||||
if (this.defaultValuesSet === false) {
|
||||
let authId = localStorage.getItem('authId')
|
||||
let authUser = JSON.parse(localStorage.getItem('user'))
|
||||
let authId = authUser.authId
|
||||
let userData = localStorage.getItem('loggedInUser')
|
||||
|
||||
if (userData === 'undefined') { return }
|
||||
if (userData === 'undefined' || userData === null) { return }
|
||||
|
||||
let user = JSON.parse(userData)
|
||||
this.defaultValuesSet = true
|
||||
@@ -279,8 +282,8 @@ class ProfileSheet extends Component {
|
||||
displayIdentityProvider () {
|
||||
const { classes } = this.props
|
||||
let authId = this.state.authId
|
||||
console.log('state', this.state)
|
||||
if (authId !== undefined) {
|
||||
|
||||
if (authId !== undefined && authId !== null) {
|
||||
let authArray = this.state.authId.split('|')
|
||||
let identityProvider = authArray[0]
|
||||
|
||||
@@ -301,19 +304,29 @@ class ProfileSheet extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
displayButtonTitle () {
|
||||
if (this.state.updated === true) {
|
||||
return 'Updated'
|
||||
} else {
|
||||
return 'Update'
|
||||
}
|
||||
}
|
||||
|
||||
addForm () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<Typography className={classes.typography} variant='headline' gutterBottom>
|
||||
Profile Sheet
|
||||
</Typography>
|
||||
|
||||
<form onSubmit={this.submitForm.bind(this)}>
|
||||
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
Use this form to control the information you keep at the Advanced Algos system about yourself.
|
||||
<Typography className={classes.typography} variant='h5' gutterBottom>
|
||||
Profile Sheet
|
||||
</Typography>
|
||||
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
Use this form to control the information you keep at the Advanced Algos system about yourself.
|
||||
</Typography>
|
||||
|
||||
<Grid container justify='center' >
|
||||
<Grid item>
|
||||
{this.displayIdentityProvider()}
|
||||
@@ -321,8 +334,8 @@ class ProfileSheet extends Component {
|
||||
</Grid>
|
||||
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
This is your basic information we have gotten from the identity provider you used to sign up. This information can not be changed.
|
||||
</Typography>
|
||||
This is your basic information we have gotten from the identity provider you used to sign up. This information can not be changed.
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
id='alias'
|
||||
@@ -348,10 +361,10 @@ class ProfileSheet extends Component {
|
||||
id='emailVerified'
|
||||
checked={this.rightCheckboxValue(this.state.emailVerified)}
|
||||
color='primary'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
label='Email Verified'
|
||||
/>
|
||||
/>
|
||||
|
||||
<Grid container justify='center' >
|
||||
<Grid item>
|
||||
@@ -360,8 +373,8 @@ class ProfileSheet extends Component {
|
||||
</Grid>
|
||||
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
Complete your profile with the following optional information. Providing your real name might help other users trust you more.
|
||||
</Typography>
|
||||
Complete your profile with the following optional information. Providing your real name might help other users trust you more.
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
error={this.state.firstNameError}
|
||||
@@ -371,7 +384,7 @@ class ProfileSheet extends Component {
|
||||
label='First Name'
|
||||
className={classes.inputField}
|
||||
onChange={this.handleTextField.bind(this)}
|
||||
/>
|
||||
/>
|
||||
|
||||
<TextField
|
||||
error={this.state.middleNameError}
|
||||
@@ -381,7 +394,7 @@ class ProfileSheet extends Component {
|
||||
label='Middle Name'
|
||||
className={classes.inputField}
|
||||
onChange={this.handleTextField.bind(this)}
|
||||
/>
|
||||
/>
|
||||
|
||||
<TextField
|
||||
error={this.state.lastNameError}
|
||||
@@ -391,11 +404,11 @@ class ProfileSheet extends Component {
|
||||
label='Last Name'
|
||||
className={classes.inputField}
|
||||
onChange={this.handleTextField.bind(this)}
|
||||
/>
|
||||
/>
|
||||
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
If you wish, you can add a short paragraph with your Bio or anything you would like to communicate to whoever finds your user profile.
|
||||
</Typography>
|
||||
If you wish, you can add a short paragraph with your Bio or anything you would like to communicate to whoever finds your user profile.
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
error={this.state.bioError}
|
||||
@@ -405,11 +418,11 @@ class ProfileSheet extends Component {
|
||||
label='Bio'
|
||||
className={classes.inputField}
|
||||
onChange={this.handleTextField.bind(this)}
|
||||
/>
|
||||
/>
|
||||
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
Check the following options to enable specialized tools designed for each role. You can allways come back and change these settings later.
|
||||
</Typography>
|
||||
Check the following options to enable specialized tools designed for each role. You can allways come back and change these settings later.
|
||||
</Typography>
|
||||
|
||||
<FormGroup row className={classes.inputField}>
|
||||
<Grid container justify='center' >
|
||||
@@ -422,10 +435,10 @@ class ProfileSheet extends Component {
|
||||
onChange={this.handleCheckBoxes.bind(this)}
|
||||
checked={this.rightCheckboxValue(this.state.isDeveloper)}
|
||||
color='secondary'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
label='Developer'
|
||||
/>
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<FormControlLabel
|
||||
@@ -436,10 +449,10 @@ class ProfileSheet extends Component {
|
||||
onChange={this.handleCheckBoxes.bind(this)}
|
||||
checked={this.rightCheckboxValue(this.state.isTrader)}
|
||||
color='secondary'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
label='Trader'
|
||||
/>
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<FormControlLabel
|
||||
@@ -450,10 +463,10 @@ class ProfileSheet extends Component {
|
||||
onChange={this.handleCheckBoxes.bind(this)}
|
||||
checked={this.rightCheckboxValue(this.state.isDataAnalyst)}
|
||||
color='secondary'
|
||||
/>
|
||||
}
|
||||
/>
|
||||
}
|
||||
label='Data Analyst'
|
||||
/>
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</FormGroup>
|
||||
@@ -462,8 +475,8 @@ class ProfileSheet extends Component {
|
||||
|
||||
<FormControl className={classes.inputField}>
|
||||
<InputLabel shrink htmlFor='select'>
|
||||
Current Role
|
||||
</InputLabel>
|
||||
Current Role
|
||||
</InputLabel>
|
||||
<Select
|
||||
id='select'
|
||||
ref='select'
|
||||
@@ -473,7 +486,7 @@ class ProfileSheet extends Component {
|
||||
displayEmpty
|
||||
name='select'
|
||||
className={classes.selectEmpty}
|
||||
>
|
||||
>
|
||||
{ this.displayRoles() }
|
||||
</Select>
|
||||
<FormHelperText>Select from the list your current role</FormHelperText>
|
||||
@@ -481,12 +494,26 @@ class ProfileSheet extends Component {
|
||||
|
||||
<Grid container justify='center' >
|
||||
<Grid item>
|
||||
<Button variant='contained' color='secondary' className={classes.button} onClick={this.submitForm.bind(this)}>Update</Button>
|
||||
<Button
|
||||
variant='contained'
|
||||
color='secondary'
|
||||
className={classes.button}
|
||||
onClick={this.submitForm.bind(this)}
|
||||
disabled={this.state.updated}
|
||||
>{this.displayButtonTitle()}</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
</Paper>
|
||||
|
||||
)
|
||||
}
|
||||
render () {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className='container'>
|
||||
{this.addForm()}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { Component } from 'react'
|
||||
import UserSearch from './UserSearch'
|
||||
import {compose} from 'react-apollo'
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
import BannerTopBar from './BannerTopBar'
|
||||
|
||||
// Materia UI
|
||||
|
||||
@@ -9,24 +10,31 @@ import { withStyles } from '@material-ui/core/styles'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
padding: 60,
|
||||
margin: 2
|
||||
padding: 10,
|
||||
marginTop: '5%',
|
||||
marginBottom: '10%'
|
||||
}
|
||||
})
|
||||
|
||||
class Search extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<UserSearch />
|
||||
<BannerTopBar
|
||||
size='medium'
|
||||
title='Users Search'
|
||||
text='Find anyone involved in Advanced Algos here.'
|
||||
backgroundUrl='https://advancedalgos.net/img/photos/users.jpg'
|
||||
/>
|
||||
<div className='container'>
|
||||
<Paper className={classes.root}>
|
||||
<UserSearch />
|
||||
</Paper>
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import React from 'react'
|
||||
import UserTabs from './UserTabs'
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
import BannerTopBar from './BannerTopBar'
|
||||
|
||||
const User = () => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<BannerTopBar
|
||||
size='medium'
|
||||
title='Your Profile'
|
||||
text='Manage your profile info, pictures and more.'
|
||||
backgroundUrl='https://advancedalgos.net/img/photos/users.jpg'
|
||||
/>
|
||||
<UserTabs />
|
||||
</React.Fragment>
|
||||
)
|
||||
|
||||
@@ -30,13 +30,13 @@ import Slide from '@material-ui/core/Slide';
|
||||
import UserProfile from './UserProfile';
|
||||
|
||||
// Images
|
||||
import PortraitImage from '../img/portrait.jpg'
|
||||
import UserDefaultPicture from '../img/user-default-pic.jpg'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
padding: 60,
|
||||
margin: 2,
|
||||
paddingTop:30,
|
||||
paddingBottom:30,
|
||||
flexGrow: 1
|
||||
},
|
||||
card: {
|
||||
maxWidth: 345,
|
||||
@@ -89,13 +89,14 @@ class UserList extends Component {
|
||||
};
|
||||
|
||||
displayUsers(){
|
||||
|
||||
let data = this.props.getUsersQuery;
|
||||
const { classes } = this.props;
|
||||
|
||||
if(data.loading){
|
||||
return ( <div> Loading Users... </div>);
|
||||
} else {
|
||||
return data.users.map(user => {
|
||||
return data.users_Users.map(user => {
|
||||
|
||||
return (
|
||||
|
||||
@@ -107,12 +108,12 @@ class UserList extends Component {
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image={PortraitImage}
|
||||
image={UserDefaultPicture}
|
||||
title="User Profile"
|
||||
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant="headline" component="h2">
|
||||
<Typography gutterBottom variant="h5" component="h2">
|
||||
{user.alias}
|
||||
</Typography>
|
||||
<Typography className={classes.typography} gutterBottom>
|
||||
@@ -135,7 +136,7 @@ class UserList extends Component {
|
||||
<IconButton color="inherit" onClick={this.handleClose} aria-label="Close">
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
<Typography variant="title" color="inherit" className={classes.flex}>
|
||||
<Typography variant="h6" color="inherit" className={classes.flex}>
|
||||
User Profile
|
||||
</Typography>
|
||||
<Button color="inherit" onClick={this.handleClose}>
|
||||
@@ -166,7 +167,7 @@ class UserList extends Component {
|
||||
const { classes } = this.props;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid container justify="center" spacing={24} className={classes.root}>
|
||||
<Grid container justify="center" spacing={40} className={classes.root}>
|
||||
{this.displayUsers()}
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
|
||||
@@ -14,7 +14,7 @@ import Typography from '@material-ui/core/Typography'
|
||||
import Divider from '@material-ui/core/Divider'
|
||||
|
||||
// Images
|
||||
import PortraitImage from '../img/portrait.jpg'
|
||||
import UserDefaultPicture from '../img/user-default-pic.jpg'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
@@ -23,7 +23,8 @@ const styles = theme => ({
|
||||
margin: 2
|
||||
},
|
||||
card: {
|
||||
width: '80%',
|
||||
maxWidth: 500,
|
||||
minWidth: 500,
|
||||
paddingTop: '30'
|
||||
},
|
||||
media: {
|
||||
@@ -46,8 +47,53 @@ const styles = theme => ({
|
||||
|
||||
class UserProfile extends Component {
|
||||
|
||||
displayNames () {
|
||||
const user = this.props.data.users_User
|
||||
const { classes } = this.props
|
||||
|
||||
if (user.firstName !== null && user.lastName !== null) {
|
||||
return (
|
||||
<Typography className={classes.typography} gutterBottom variant='h5' component='h2'>
|
||||
{ user.firstName + ' ' + user.lastName}
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
displayRole () {
|
||||
const user = this.props.data.users_User
|
||||
const { classes } = this.props
|
||||
|
||||
if (user.role.name !== null && user.role.name !== 'Not Defined') {
|
||||
return (
|
||||
<div>
|
||||
<Divider />
|
||||
<Typography className={classes.typography} align='left' gutterBottom variant='subtitle1' component='h2'>
|
||||
{ user.role.name }
|
||||
</Typography>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
displayBio () {
|
||||
const user = this.props.data.users_User
|
||||
const { classes } = this.props
|
||||
|
||||
if (user.bio !== null && user.bio !== '') {
|
||||
return (
|
||||
<div>
|
||||
<Divider />
|
||||
<Typography className={classes.typography} component='p'>
|
||||
{ user.bio }
|
||||
</Typography>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
displayUserProfile () {
|
||||
const {user} = this.props.data
|
||||
const user = this.props.data.users_User
|
||||
const { classes } = this.props
|
||||
|
||||
if (user) {
|
||||
@@ -61,24 +107,21 @@ class UserProfile extends Component {
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image={PortraitImage}
|
||||
image={UserDefaultPicture}
|
||||
title='User Profile'
|
||||
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography className={classes.typography} gutterBottom variant='headline' component='h2'>
|
||||
{ user.firstName } { user.lastName } ( {user.alias} )
|
||||
</Typography>
|
||||
<Divider />
|
||||
|
||||
<Typography className={classes.typography} align='left' gutterBottom variant='subheading' component='h2'>
|
||||
{ user.role.name }
|
||||
</Typography>
|
||||
<Divider />
|
||||
{this.displayNames()}
|
||||
|
||||
<Typography className={classes.typography} component='p'>
|
||||
{ user.bio }
|
||||
<Typography className={classes.typography} gutterBottom variant='h6' component='h3'>
|
||||
{user.alias}
|
||||
</Typography>
|
||||
|
||||
{this.displayRole()}
|
||||
{this.displayBio()}
|
||||
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
</Card>
|
||||
|
||||
@@ -17,7 +17,7 @@ import Grid from '@material-ui/core/Grid'
|
||||
import TextField from '@material-ui/core/TextField'
|
||||
|
||||
// Images
|
||||
import PortraitImage from '../img/portrait.jpg'
|
||||
import UserDefaultPicture from '../img/user-default-pic.jpg'
|
||||
|
||||
const styles = theme => ({
|
||||
card: {
|
||||
@@ -44,6 +44,8 @@ const styles = theme => ({
|
||||
flex: 1
|
||||
},
|
||||
grid: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
paddingTop: '30',
|
||||
marginTop: 30
|
||||
},
|
||||
@@ -156,10 +158,10 @@ class UserSearch extends Component {
|
||||
if (data.loading) {
|
||||
return (<div> Loading Users... </div>)
|
||||
} else {
|
||||
if (data.usersSearch === undefined) {
|
||||
if (data.users_UsersSearch === undefined) {
|
||||
return (<div> No Users to Display </div>)
|
||||
} else {
|
||||
return data.usersSearch.map(user => {
|
||||
return data.users_UsersSearch.map(user => {
|
||||
return (
|
||||
|
||||
<Grid key={user.id} item>
|
||||
@@ -170,12 +172,12 @@ class UserSearch extends Component {
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image={PortraitImage}
|
||||
image={UserDefaultPicture}
|
||||
title='User Profile'
|
||||
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant='headline' component='h2'>
|
||||
<Typography gutterBottom component='h2'>
|
||||
{user.alias}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
@@ -196,9 +198,14 @@ class UserSearch extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
window.dispatchEvent(new Event('load')) // This is a workaround to solve the problem that the slider does not show up
|
||||
}
|
||||
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
|
||||
<div>
|
||||
<form onSubmit={this.submitForm.bind(this)}>
|
||||
|
||||
@@ -257,6 +264,7 @@ class UserSearch extends Component {
|
||||
{this.displayUsers()}
|
||||
</Grid>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,9 +62,10 @@ class UserTabs extends React.Component {
|
||||
scrollButtons="off"
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
fullWidth
|
||||
>
|
||||
<Tab label="Profile Sheet" icon={<ProfileSheetIcon />} />
|
||||
<Tab label="Profile Images" icon={<ProfileImagesIcon />} />
|
||||
<Tab disabled label="Profile Images" icon={<ProfileImagesIcon />} />
|
||||
<Tab label="Your Referrer" icon={<YourReferrerIcon />} />
|
||||
<Tab label="Your Descendents" icon={<DescendentsIcon />} />
|
||||
</Tabs>
|
||||
|
||||
@@ -13,11 +13,11 @@ import DescendentsTree from './DescendentsTree'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
width: '50%',
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
padding: 10,
|
||||
marginLeft: '25%',
|
||||
marginTop: '2%'
|
||||
marginTop: '5%',
|
||||
marginBottom: '10%'
|
||||
},
|
||||
typography: {
|
||||
width: '80%',
|
||||
@@ -59,18 +59,19 @@ class YourDescendents extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
addForm () {
|
||||
const { classes } = this.props
|
||||
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<Typography className={classes.typography} variant='headline' gutterBottom>
|
||||
Your Descendents
|
||||
</Typography>
|
||||
|
||||
<Typography className={classes.typography} variant='h5' gutterBottom>
|
||||
Your Decendents
|
||||
</Typography>
|
||||
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
These are your decendents within the project. Your children referred you as the one who brought them to the project, while
|
||||
your grandchildren referred your children and so on.
|
||||
your grandchildren referred your children and so on. In order to be able to see your descendents, the users who you referred
|
||||
must first point to you as your referrer.
|
||||
</Typography>
|
||||
|
||||
<DescendentsTree userId={this.state.id} />
|
||||
@@ -78,6 +79,16 @@ class YourDescendents extends Component {
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className='container'>
|
||||
{this.addForm()}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
|
||||
@@ -15,11 +15,11 @@ import Typography from '@material-ui/core/Typography'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
width: '50%',
|
||||
width: '100%',
|
||||
flexGrow: 1,
|
||||
padding: 10,
|
||||
marginLeft: '25%',
|
||||
marginTop: '2%'
|
||||
marginTop: '5%',
|
||||
marginBottom: '10%'
|
||||
},
|
||||
typography: {
|
||||
width: '80%',
|
||||
@@ -60,7 +60,6 @@ class YourReferrer extends Component {
|
||||
|
||||
this.props.updateReferrerMutation({
|
||||
variables: {
|
||||
id: this.state.id,
|
||||
referrerId: referrerUser.id
|
||||
}
|
||||
})
|
||||
@@ -126,18 +125,27 @@ class YourReferrer extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
addForm () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<Typography className={classes.typography} variant='headline' gutterBottom>
|
||||
Your Referrer
|
||||
</Typography>
|
||||
|
||||
<Typography className={classes.typography} variant='h5' gutterBottom>
|
||||
Your Referrer
|
||||
</Typography>
|
||||
{this.renderMode()}
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<div className='container'>
|
||||
{this.addForm()}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
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
|
||||
}
|
||||
|
Before Width: | Height: | Size: 689 KiB |
|
After Width: | Height: | Size: 66 KiB |
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,6 +1,24 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import React from 'react'
|
||||
import { Route, Switch } from 'react-router-dom'
|
||||
|
||||
import { MuiPickersUtilsProvider } from 'material-ui-pickers'
|
||||
import LuxonUtils from 'material-ui-pickers/utils/luxon-utils'
|
||||
|
||||
import Home from './components/Home'
|
||||
import User from './components/User'
|
||||
import Browse from './components/Browse'
|
||||
import Search from './components/Search'
|
||||
|
||||
const App = () => (
|
||||
<MuiPickersUtilsProvider utils={LuxonUtils}>
|
||||
<Switch>
|
||||
<Route exact path='/users/' component={Home} />
|
||||
<Route path='/users/user' component={User} />
|
||||
<Route path='/users/browse' component={Browse} />
|
||||
<Route path='/users/search' component={Search} />
|
||||
</Switch>
|
||||
</MuiPickersUtilsProvider>
|
||||
)
|
||||
|
||||
export default App
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
@@ -1,36 +0,0 @@
|
||||
mutation {
|
||||
addUser (alias: "aaa", firstName:"fff", lastName: "lll", roleId: "1", isTrader: 1, isDeveloper: 0) {
|
||||
alias
|
||||
isTrader
|
||||
isDeveloper
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mutation {
|
||||
updateUser (
|
||||
id: "5b8d152f6f4d5b3db44ee103" ,
|
||||
alias: "luis",
|
||||
firstName:"Luis",
|
||||
lastName: "Molina",
|
||||
isDeveloper: 1,
|
||||
isTrader: 1,
|
||||
isDataAnalyst: 1,
|
||||
roleId: "2"
|
||||
) {
|
||||
alias
|
||||
}
|
||||
}
|
||||
|
||||
query {
|
||||
|
||||
users{
|
||||
alias
|
||||
firstName
|
||||
isDeveloper
|
||||
role{
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import gql from 'graphql-tag'
|
||||
|
||||
const getUsersQuery = gql`
|
||||
{
|
||||
users {
|
||||
users_Users {
|
||||
id
|
||||
alias
|
||||
firstName
|
||||
@@ -18,7 +18,7 @@ const getUsersQuery = gql`
|
||||
`
|
||||
const getUserProfileQuery = gql`
|
||||
query($id: ID){
|
||||
user (id: $id){
|
||||
users_User (id: $id){
|
||||
id
|
||||
alias
|
||||
firstName
|
||||
@@ -35,7 +35,7 @@ query($id: ID){
|
||||
`
|
||||
const getUserByAuthIdQuery = gql`
|
||||
query($authId: String){
|
||||
userByAuthId (authId: $authId){
|
||||
users_UserByAuthId (authId: $authId){
|
||||
id
|
||||
referrerId
|
||||
alias
|
||||
@@ -63,7 +63,7 @@ query(
|
||||
$middleName: String,
|
||||
$lastName: String
|
||||
){
|
||||
usersSearch (alias: $alias, firstName: $firstName, middleName: $middleName, lastName: $lastName){
|
||||
users_UsersSearch (alias: $alias, firstName: $firstName, middleName: $middleName, lastName: $lastName){
|
||||
id
|
||||
alias
|
||||
firstName
|
||||
@@ -76,7 +76,7 @@ const getDescendentsQuery = gql`
|
||||
query(
|
||||
$id: String
|
||||
){
|
||||
descendents (id: $id){
|
||||
users_Descendents (id: $id){
|
||||
id
|
||||
alias
|
||||
firstName
|
||||
@@ -104,7 +104,7 @@ query(
|
||||
`
|
||||
const getRolesQuery = gql`
|
||||
{
|
||||
roles {
|
||||
users_Roles {
|
||||
id
|
||||
name
|
||||
}
|
||||
@@ -112,7 +112,6 @@ const getRolesQuery = gql`
|
||||
`
|
||||
const updateUserMutation = gql`
|
||||
mutation(
|
||||
$id: ID!,
|
||||
$firstName:String,
|
||||
$middleName:String,
|
||||
$lastName:String,
|
||||
@@ -123,8 +122,7 @@ mutation(
|
||||
$roleId:String!
|
||||
)
|
||||
{
|
||||
updateUser (
|
||||
id: $id,
|
||||
users_UpdateUser (
|
||||
firstName:$firstName,
|
||||
middleName: $middleName,
|
||||
lastName: $lastName,
|
||||
@@ -142,12 +140,10 @@ mutation(
|
||||
`
|
||||
const updateReferrerMutation = gql`
|
||||
mutation(
|
||||
$id: ID!,
|
||||
$referrerId:String!
|
||||
)
|
||||
{
|
||||
updateUserReferrer (
|
||||
id: $id,
|
||||
users_UpdateUserReferrer (
|
||||
referrerId:$referrerId
|
||||
)
|
||||
{
|
||||
@@ -156,24 +152,6 @@ mutation(
|
||||
}
|
||||
}
|
||||
`
|
||||
const updateUserImagesMutation = gql`
|
||||
mutation(
|
||||
$id: ID!,
|
||||
$avatarHandle:String,
|
||||
$avatarChangeDate:String
|
||||
)
|
||||
{
|
||||
updateUserImages (
|
||||
id: $id,
|
||||
avatarHandle:$avatarHandle,
|
||||
avatarChangeDate: $avatarChangeDate
|
||||
)
|
||||
{
|
||||
id
|
||||
alias
|
||||
}
|
||||
}
|
||||
`
|
||||
export {
|
||||
getUsersQuery,
|
||||
getUserByAuthIdQuery,
|
||||
@@ -182,6 +160,5 @@ export {
|
||||
getUsersBySearchFields,
|
||||
getDescendentsQuery,
|
||||
updateUserMutation,
|
||||
updateReferrerMutation,
|
||||
updateUserImagesMutation
|
||||
updateReferrerMutation
|
||||
}
|
||||
@@ -1,117 +0,0 @@
|
||||
// In production, we register a service worker to serve assets from local cache.
|
||||
|
||||
// This lets the app load faster on subsequent visits in production, and gives
|
||||
// it offline capabilities. However, it also means that developers (and users)
|
||||
// will only see deployed updates on the "N+1" visit to a page, since previously
|
||||
// cached resources are updated in the background.
|
||||
|
||||
// To learn more about the benefits of this model, read https://goo.gl/KwvDNy.
|
||||
// This link also includes instructions on opting out of this behavior.
|
||||
|
||||
const isLocalhost = Boolean(
|
||||
window.location.hostname === 'localhost' ||
|
||||
// [::1] is the IPv6 localhost address.
|
||||
window.location.hostname === '[::1]' ||
|
||||
// 127.0.0.1/8 is considered localhost for IPv4.
|
||||
window.location.hostname.match(
|
||||
/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/
|
||||
)
|
||||
);
|
||||
|
||||
export default function register() {
|
||||
if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
|
||||
// The URL constructor is available in all browsers that support SW.
|
||||
const publicUrl = new URL(process.env.PUBLIC_URL, window.location);
|
||||
if (publicUrl.origin !== window.location.origin) {
|
||||
// Our service worker won't work if PUBLIC_URL is on a different origin
|
||||
// from what our page is served on. This might happen if a CDN is used to
|
||||
// serve assets; see https://github.com/facebookincubator/create-react-app/issues/2374
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
|
||||
|
||||
if (isLocalhost) {
|
||||
// This is running on localhost. Lets check if a service worker still exists or not.
|
||||
checkValidServiceWorker(swUrl);
|
||||
|
||||
// Add some additional logging to localhost, pointing developers to the
|
||||
// service worker/PWA documentation.
|
||||
navigator.serviceWorker.ready.then(() => {
|
||||
console.log(
|
||||
'This web app is being served cache-first by a service ' +
|
||||
'worker. To learn more, visit https://goo.gl/SC7cgQ'
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// Is not local host. Just register service worker
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function registerValidSW(swUrl) {
|
||||
navigator.serviceWorker
|
||||
.register(swUrl)
|
||||
.then(registration => {
|
||||
registration.onupdatefound = () => {
|
||||
const installingWorker = registration.installing;
|
||||
installingWorker.onstatechange = () => {
|
||||
if (installingWorker.state === 'installed') {
|
||||
if (navigator.serviceWorker.controller) {
|
||||
// At this point, the old content will have been purged and
|
||||
// the fresh content will have been added to the cache.
|
||||
// It's the perfect time to display a "New content is
|
||||
// available; please refresh." message in your web app.
|
||||
console.log('New content is available; please refresh.');
|
||||
} else {
|
||||
// At this point, everything has been precached.
|
||||
// It's the perfect time to display a
|
||||
// "Content is cached for offline use." message.
|
||||
console.log('Content is cached for offline use.');
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error during service worker registration:', error);
|
||||
});
|
||||
}
|
||||
|
||||
function checkValidServiceWorker(swUrl) {
|
||||
// Check if the service worker can be found. If it can't reload the page.
|
||||
fetch(swUrl)
|
||||
.then(response => {
|
||||
// Ensure service worker exists, and that we really are getting a JS file.
|
||||
if (
|
||||
response.status === 404 ||
|
||||
response.headers.get('content-type').indexOf('javascript') === -1
|
||||
) {
|
||||
// No service worker found. Probably a different app. Reload the page.
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister().then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Service worker found. Proceed as normal.
|
||||
registerValidSW(swUrl);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
console.log(
|
||||
'No internet connection found. App is running in offline mode.'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function unregister() {
|
||||
if ('serviceWorker' in navigator) {
|
||||
navigator.serviceWorker.ready.then(registration => {
|
||||
registration.unregister();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -59,11 +59,6 @@ export const globalStyles = theme => ({
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
paddingBottom: theme.spacing.unit * 2
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
marginTop: theme.spacing.unit * 8,
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
padding: `${theme.spacing.unit * 6}px 0`
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
export function isDefined (d) {
|
||||
return d !== null && typeof d !== 'undefined'
|
||||
}
|
||||
|
||||
export function isNotDefined (d) {
|
||||
return !isDefined(d)
|
||||
}
|
||||
|
||||
export const isArray = Array.isArray
|
||||
|
||||
export function isObject (d) {
|
||||
return isDefined(d) && typeof d === 'object' && !Array.isArray(d)
|
||||
}
|
||||
|
||||
export function isEmpty (obj) {
|
||||
for (var key in obj) {
|
||||
if (obj.hasOwnProperty(key)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
export function validObject (object, ...keys) {
|
||||
return keys.reduce((a, b) => (a || { })[b], object) !== undefined
|
||||
}
|
||||
|
||||
export function mergeDeep (target, ...sources) {
|
||||
if (!sources.length) return target
|
||||
const source = sources.shift()
|
||||
|
||||
if (isObject(target) && isObject(source)) {
|
||||
for (const key in source) {
|
||||
if (isObject(source[key])) {
|
||||
if (!target[key]) Object.assign(target, { [key]: {} })
|
||||
mergeDeep(target[key], source[key])
|
||||
} else {
|
||||
Object.assign(target, { [key]: source[key] })
|
||||
}
|
||||
}
|
||||
}
|
||||
return mergeDeep(target, ...sources)
|
||||
}
|
||||
|
||||
export function deleteCookie (c) {
|
||||
document.cookie.split(';').forEach(function (c) { document.cookie = c.replace(/^ +/, '').replace(/=.*/, '=;expires=' + new Date().toUTCString() + ';path=/') })
|
||||
}
|
||||
|
||||
export function slugify (string) {
|
||||
const a = 'àáäâãåèéëêìíïîòóöôùúüûñçßÿœæŕśńṕẃǵǹḿǘẍźḧ·/_,:;'
|
||||
const b = 'aaaaaaeeeeiiiioooouuuuncsyoarsnpwgnmuxzh------'
|
||||
const p = new RegExp(a.split('').join('|'), 'g')
|
||||
|
||||
return string.toString().toLowerCase()
|
||||
.replace(/\s+/g, '-') // Replace spaces with -
|
||||
.replace(p, c => b.charAt(a.indexOf(c))) // Replace special characters
|
||||
.replace(/&/g, '-and-') // Replace & with 'and'
|
||||
.replace(/[^\w-]+/g, '') // Remove all non-word characters
|
||||
.replace(/--+/g, '-') // Replace multiple - with single -
|
||||
.replace(/^-+/, '') // Trim - from start of text
|
||||
.replace(/-+$/, '') // Trim - from end of text
|
||||
}
|
||||
@@ -1,34 +1,8 @@
|
||||
const getStateFromStorage = version => {
|
||||
const localState = JSON.parse(window.localStorage.getItem('aa-team'))
|
||||
|
||||
try {
|
||||
if (localState !== undefined && localState !== null) {
|
||||
if (
|
||||
localState.appVersion !== undefined &&
|
||||
localState.appVersion !== null
|
||||
) {
|
||||
if (localState.appVersion === version) {
|
||||
return localState
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Error()
|
||||
}
|
||||
} catch (error) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
const storeStateInStorage = state =>
|
||||
window.localStorage.setItem('aa-team', JSON.stringify(state))
|
||||
|
||||
const getItem = async name => window.localStorage.getItem(name)
|
||||
const setItem = async (name, value) => window.localStorage.setItem(name, value)
|
||||
const removeItem = async name => window.localStorage.removeItem(name)
|
||||
|
||||
export {
|
||||
getStateFromStorage,
|
||||
storeStateInStorage,
|
||||
getItem,
|
||||
setItem,
|
||||
removeItem
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import classNames from 'classnames'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
|
||||
import aalogo from '../../assets/logos/advanced-algos/aa-logo-vert.svg'
|
||||
|
||||
const styles = theme => ({
|
||||
'@global': {
|
||||
body: {
|
||||
backgroundColor: theme.palette.common.white
|
||||
}
|
||||
},
|
||||
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)]: {
|
||||
width: 1200,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto'
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
padding: `${theme.spacing.unit * 6}px 0`
|
||||
},
|
||||
footerContainer: {
|
||||
marginRight: theme.spacing.unit * 8,
|
||||
marginLeft: theme.spacing.unit * 8
|
||||
},
|
||||
footerLink: {
|
||||
textDecoration: 'none'
|
||||
}
|
||||
})
|
||||
|
||||
const Footer = ({ classes }) => {
|
||||
return (
|
||||
<footer className={classNames(classes.footer, classes.layout)}>
|
||||
<Grid
|
||||
container
|
||||
spacing={32}
|
||||
justify='space-evenly'
|
||||
className={classNames(classes.footerContainer)}
|
||||
>
|
||||
<Grid item xs>
|
||||
<Typography variant='title' color='textPrimary' gutterBottom>
|
||||
Pages
|
||||
</Typography>
|
||||
<NavLink to='/' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Home
|
||||
</Typography>
|
||||
</NavLink>
|
||||
<NavLink to='/browse' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Browse
|
||||
</Typography>
|
||||
</NavLink>
|
||||
<NavLink to='/search' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Search
|
||||
</Typography>
|
||||
</NavLink>
|
||||
<NavLink to='/contact' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Contact
|
||||
</Typography>
|
||||
</NavLink>
|
||||
<NavLink to='/about' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
About
|
||||
</Typography>
|
||||
</NavLink>
|
||||
</Grid>
|
||||
<Grid item xs>
|
||||
|
||||
<Typography variant='title' color='textPrimary' gutterBottom>
|
||||
Support
|
||||
</Typography>
|
||||
|
||||
<a
|
||||
href='https://advancedalgos.net/documentation-quick-start.shtml'
|
||||
target='_blank>'
|
||||
className={classNames(classes.footerLink)}
|
||||
>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Documentation
|
||||
</Typography>
|
||||
</a>
|
||||
<a
|
||||
href='https://t.me/advancedalgoscommunity'
|
||||
target='_blank>'
|
||||
className={classNames(classes.footerLink)}
|
||||
>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Telegram
|
||||
</Typography>
|
||||
</a>
|
||||
</Grid>
|
||||
<Grid item xs>
|
||||
<Typography variant='title' color='textPrimary' gutterBottom>
|
||||
Modules
|
||||
</Typography>
|
||||
<a
|
||||
href='https://develop.advancedalgos.net'
|
||||
target='_blank>'
|
||||
className={classNames(classes.footerLink)}
|
||||
>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Platform
|
||||
</Typography>
|
||||
</a>
|
||||
<a
|
||||
href='https://users.advancedalgos.net'
|
||||
target='_blank>'
|
||||
className={classNames(classes.footerLink)}
|
||||
>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Users
|
||||
</Typography>
|
||||
</a>
|
||||
<a
|
||||
href='https://teams.advancedalgos.net'
|
||||
target='_blank>'
|
||||
className={classNames(classes.footerLink)}
|
||||
>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Teams
|
||||
</Typography>
|
||||
</a>
|
||||
<a
|
||||
href='https://keyvault.advancedalgos.net'
|
||||
target='_blank>'
|
||||
className={classNames(classes.footerLink)}
|
||||
>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Key Vault
|
||||
</Typography>
|
||||
</a>
|
||||
</Grid>
|
||||
<Grid item xs>
|
||||
<NavLink to='/'>
|
||||
<img alt='' src={aalogo} width={98} height={140} />
|
||||
</NavLink>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
Footer.propTypes = {
|
||||
classes: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default withStyles(styles)(Footer)
|
||||
@@ -1,21 +0,0 @@
|
||||
// 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
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { graphql } from 'react-apollo'
|
||||
|
||||
import LoggedInMenu from './LoggedInMenu'
|
||||
|
||||
import { GetCurrentMember } from '../../graphql/members'
|
||||
|
||||
import { isDefined } from '../../utils/js-helpers'
|
||||
|
||||
export const LoggedIn = props => {
|
||||
let { data, user, auth } = props
|
||||
console.log('LoggedIn :', data, user)
|
||||
let displayName = 'No Display Name'
|
||||
|
||||
if (isDefined(user.alias)) {
|
||||
displayName = user.alias
|
||||
}
|
||||
|
||||
if (isDefined(user.nickname)) {
|
||||
displayName = user.nickname
|
||||
}
|
||||
|
||||
if (isDefined(user.firstName)) {
|
||||
displayName = user.firstName
|
||||
}
|
||||
|
||||
if (isDefined(user.firstName) && isDefined(user.lastName)) {
|
||||
displayName = user.firstName + ' ' + user.lastName
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<p>
|
||||
<LoggedInMenu menuLabel={displayName} auth={auth} />
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
LoggedIn.propTypes = {
|
||||
data: PropTypes.object,
|
||||
user: PropTypes.object.isRequired,
|
||||
auth: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default graphql(GetCurrentMember)(LoggedIn) // This binds the querty to the component
|
||||
@@ -1,77 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Button from '@material-ui/core/Button'
|
||||
import Menu from '@material-ui/core/Menu'
|
||||
import MenuItem from '@material-ui/core/MenuItem'
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import ProfileIcon from '@material-ui/icons/Person'
|
||||
import LogoutIcon from '@material-ui/icons/DirectionsWalk'
|
||||
|
||||
const UserLink = props => <Link to='/user' {...props} />
|
||||
const LogoutLink = props => <Link to='/' {...props} />
|
||||
|
||||
class LoggedInUserMenu extends Component {
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.handleMenu = this.handleMenu.bind(this)
|
||||
this.handleClose = this.handleClose.bind(this)
|
||||
this.handleLogout = this.handleLogout.bind(this)
|
||||
|
||||
this.state = {
|
||||
menu: null
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { menuLabel } = this.props
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Button
|
||||
aria-owns={this.state.open ? 'render-props-menu' : null}
|
||||
aria-haspopup='true'
|
||||
onClick={e => this.handleMenu(e)}
|
||||
color='inherit'
|
||||
>
|
||||
{menuLabel}
|
||||
</Button>
|
||||
<Menu
|
||||
id='render-props-menu'
|
||||
anchorEl={this.state.menu}
|
||||
open={Boolean(this.state.menu)}
|
||||
onClose={this.handleClose}
|
||||
>
|
||||
<MenuItem onClick={this.handleClose} component={UserLink}>
|
||||
<ProfileIcon />
|
||||
<div>Profile</div>
|
||||
</MenuItem>
|
||||
<MenuItem onClick={this.handleLogout} component={LogoutLink}>
|
||||
<LogoutIcon />
|
||||
<div>Logout</div>
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,15 +0,0 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import Button from '@material-ui/core/Button'
|
||||
|
||||
export const LoggedOut = ({ auth }) => (
|
||||
<div onClick={() => auth.login()}>
|
||||
<Button color='inherit'>Login / Sign up</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
LoggedOut.propTypes = {
|
||||
auth: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default LoggedOut // This binds the querty to the component
|
||||
@@ -1,141 +0,0 @@
|
||||
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 => <Link to='/about' {...props} />
|
||||
const TeamsLink = props => <Link to='/teams' {...props} />
|
||||
const DashboardLink = props => <Link to='/dashboard' {...props} />
|
||||
const HomeLink = props => <Link to='/' {...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 (
|
||||
<div className={classes.root}>
|
||||
<AppBar
|
||||
position='static'
|
||||
classes={{ root: classes.appBar, colorDefault: classes.colorDefault }}
|
||||
>
|
||||
<Toolbar>
|
||||
<img className={classes.img} src={AALogo} alt='Advanced Algos' />
|
||||
<Typography
|
||||
variant='title'
|
||||
color='inherit'
|
||||
className={classes.toolbarTitle}
|
||||
>
|
||||
Teams
|
||||
</Typography>
|
||||
|
||||
<IconButton
|
||||
className={classes.menuButton}
|
||||
color='inherit'
|
||||
title='Home'
|
||||
component={HomeLink}
|
||||
>
|
||||
<HomeIcon />
|
||||
</IconButton>
|
||||
<Button component={TeamsLink} color='inherit'>
|
||||
Teams
|
||||
</Button>
|
||||
<Button component={AboutLink} color='inherit'>
|
||||
About
|
||||
</Button>
|
||||
|
||||
{this.state.user !== undefined && this.state.user !== null ? (
|
||||
<React.Fragment>
|
||||
<IconButton
|
||||
className={classes.menuButton}
|
||||
color='inherit'
|
||||
title='Dashboard'
|
||||
component={DashboardLink}
|
||||
>
|
||||
<DashboardIcon />
|
||||
</IconButton>
|
||||
<LoggedIn user={user} auth={auth} styles={styles} />
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<LoggedOut auth={auth} styles={styles} />
|
||||
)}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
NavBar.propTypes = {
|
||||
classes: PropTypes.object.isRequired,
|
||||
auth: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
export default withStyles(styles)(NavBar)
|
||||
@@ -0,0 +1,20 @@
|
||||
NODE_ENV=development
|
||||
|
||||
VIRTUAL_HOST=users-api.advancedalgos.net
|
||||
VIRTUAL_PORT=4000
|
||||
|
||||
PORT=4000
|
||||
|
||||
DB_USERNAME=
|
||||
DB_PASSWORD=
|
||||
DB_DOMAIN=
|
||||
|
||||
GATEWAY_ENDPOINT=http://localhost:4100/graphql
|
||||
USERS_API_PRESHARED=
|
||||
|
||||
AUTH0_DOMAIN=
|
||||
AUTH0_AUDIENCE=
|
||||
AUTH0_ISSUER=
|
||||
AUTH0_CLIENTID=
|
||||
AUTH0_CALLBACK_URL=
|
||||
AUTH0_LOGOUT_URL=
|
||||
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at feedback at advancedalgos dot org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -0,0 +1,45 @@
|
||||
# Contributing
|
||||
|
||||
We are thrilled you are interested in contributing to this repository. Your help may be essential to the project's success and we want you in!
|
||||
|
||||
Advanced Algos LTD is a startup, currently at a very early stage. Although this repository is not open source software yet, we do welcome contributions from the community.
|
||||
|
||||
Contributions are deemed _voluntary work_. There might be incentives in place for certain kinds of contributions. Contact us for more information in this regards.
|
||||
|
||||
In any case, contributors agree to provide an express grant of patent rights for their contributions. Submitting Pull Requests to this repository represents an express agreement with the terms of our Balanced Contributor Intellectual Property Agreement. This agreement is a direct implementation of GitHub's [Balanced Employee Intellectual Property Agreement 1.0.1](https://github.com/github/balanced-employee-ip-agreement/blob/master/Balanced_Employee_IP_Agreement.md).
|
||||
|
||||
# Balanced Contributor Intellectual Property Agreement 1.0.1
|
||||
|
||||
This BALANCED CONTRIBUTOR INTELLECTUAL PROPERTY AGREEMENT is between person or team submitting Pull Requests to this repository and Advanced Algos LTD ("Company").
|
||||
|
||||
**What is this?** This is the Company's Intellectual Property Agreement ("Agreement"). If you've worked in the technology space before, there's a good chance that you've run across one or more of these in the past. This document is the official, entire and exclusive agreement on what intellectual property ("IP") is yours, and what belongs to the Company.
|
||||
|
||||
**Why is this?** The Company needs to be clear on what IP it owns and has rights to. Its customers, contributors, and investors depend on the Company having the legal rights to the products and services it is providing so that the Company can continue operating and doing business.
|
||||
|
||||
The Company also believes that it's important to be clear on what it doesn't own. The Company doesn't want you looking over your shoulder every time you work on something personal or worrying that the Company will someday seize your open source non-lethal mousetrap simulation software. In other words, the Company isn't interested in appropriating your personal projects.
|
||||
|
||||
**Read this.** Please read this document and be sure you understand it before you submit any Pull Requests. Due to legal risk and corporate obligations, the Company cannot, by and large, negotiate its terms. If you feel you have a particular circumstance that keeps you from signing, please let the Company's legal department ("Legal") know. And, of course, you're always free and encouraged to get your own legal counsel to explain anything you're not clear on.
|
||||
|
||||
Cool? Then, by submitting your first Pull Request, and as a condition of your contribution, you agree to the following:
|
||||
|
||||
1. **What the Company owns.** The Company owns any IP ("Company IP") that you create, or help create _as its contributor_, but only if it meets one or more of these additional conditions: The IP was (i) related to an existing or prospective Company product or service at the time you developed, invented, or created it, (ii) developed for use by the Company, or (iii) developed or promoted with existing Company IP or with the Company's endorsement. You hereby grant and assign, and will grant and assign, to the Company all rights and interests in all Company IP.
|
||||
|
||||
"Company IP" includes concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, and works of authorship.
|
||||
|
||||
2. **What the Company doesn't own.** If you create IP that doesn't fit into one of the categories listed above, the Company doesn't own it. In other words, the Company doesn't own concepts, designs, developments, discoveries, ideas, improvements, inventions, trade secrets, trademarks, or works of authorship that you develop, invent, or create that do not meet any of the conditions in Section 1.
|
||||
|
||||
3. **Contribution of your IP to Company projects.** If you include your own IP – such as IP you created prior to working for the Company – into a Company product or service, it's still yours, of course, but you grant the Company a non-exclusive, irrevocable, fully paid-up, royalty-free, perpetual, sublicensable, transferable, worldwide license to use it without restriction in any way or implementation, in modified form, or as is, by itself, or incorporated into another product or service ("License"). If you include your name in any project over which the Company would have rights under this Agreement, such as in a copyright notice or a comment in code or documentation, then the License covers the rights associated with that use of your name as well.
|
||||
|
||||
4. **Your contributions to non-Company projects.** The Company recognizes that you may be engaged in work that requires you to submit IP to entities other than the Company, such as open source projects used by the Company. Please make sure that Legal is aware of what you're working on so that Legal can help with any licensing issues. If any project asks you to sign a contribution agreement, you should check with Legal before doing so. These agreements may be legally binding on the Company and so should be run through Legal first.
|
||||
|
||||
5. **Voluntary Works** All Company IP that you are involved with or create as part of your work for the Company is voluntary work which does not require compensation. There might be incentives in place for certain kinds of contributions. Feel free to contact the Company to learn more.
|
||||
|
||||
6. **No conflicts.** You agree that you don't have any outstanding agreements or obligations that conflict with those in this Agreement, and that you won't enter into conflicting agreements in the future.
|
||||
|
||||
7. **IP protection.** The Company might someday need to show the work that went into the development of IP that it uses or has used, or to make government filings to establish that it owns the IP or has rights over it. To help in those situations, you agree not to destroy any records you maintain relating to the development of any Company IP or IP under License (together, "All IP"), and, if the Company asks, to provide those records to the Company. You agree to help the Company secure and defend its rights in All IP. For your help the Company will compensate you at a reasonable rate. If the Company is unable to secure your help, you authorize the Company to act on your behalf (as your agent and attorney-in-fact) in securing all rights related to All IP.
|
||||
|
||||
8. **Confidentiality.** As an contributor or contractor of the Company, you will have access to sensitive confidential information that is important to the Company's business. You are responsible for keeping this information confidential, including after you end your work for the Company.
|
||||
|
||||
"Confidential Information" includes non-public technical details about our products or services, financial information, business strategies and forecasts, customer data, or any other information, data or know-how that is valuable to the Company because it is not publicly known.
|
||||
|
||||
The Company's Confidential Information is sensitive and we expect that you'll treat it as such. You agree to only use the Company's Confidential Information for the purpose of performing your contribution and for the benefit of the Company. You will do your best to keep Confidential Information secret. If you are not sure if something is Confidential Information, you should assume that it is, until you can confirm otherwise.
|
||||
@@ -0,0 +1,42 @@
|
||||
COPYRIGHT NOTICE
|
||||
|
||||
© Copyright 2018 Advanced Algos LTD.
|
||||
|
||||
|
||||
LICENSE NOTICE
|
||||
|
||||
Advanced Algos LTD. is a startup, currently at a very early stage. Because certain aspects of
|
||||
the business are still not clearly defined, we have chosen to --temporarily-- grant a
|
||||
restrictive license for the use of this repository, until those business aspects are clarified
|
||||
and a proper open source license can be granted.
|
||||
|
||||
Advanced Algos LTD. is publishing the code of this software for several reasons. However,
|
||||
it shall not be considered free software yet. Advanced Algos LTD. is a for-profit company
|
||||
in the crypto space and this module is still proprietary software.
|
||||
|
||||
The reasons for opening up the code of this module at this stage are the following:
|
||||
|
||||
* We believe that transparency is paramount to achieving our goals, so we want everyone
|
||||
to be able to understand what this module does and how. Opening up the code of this module
|
||||
allows anyone to take a close look.
|
||||
|
||||
* We welcome contributions from the community. Contributions are regarded as voluntary work.
|
||||
There may be incentives in place for certain kinds of contributions. Contact us to learn more.
|
||||
Contributors agree to provide an express grant of patent rights for their contributions.
|
||||
|
||||
|
||||
LICENSE WORDING
|
||||
|
||||
Advanced Algos LTD. grants a limited license for the following uses:
|
||||
|
||||
1. CODE REVIEW AND AUDITS. You are granted permission to clone this repository to analyze,
|
||||
review or audit the code.
|
||||
|
||||
2. COLLABORATING WITH ADVANCED ALGOS LTD. You are granted permission to fork this repository
|
||||
with the sole purpose of correcting bugs and developing functionality in agreement with
|
||||
Advanced Algos LTD. By contributing to this repository you explicitely agree to the terms
|
||||
detailed in CONTRIBUTING.MD.
|
||||
|
||||
PLEASE SEE CONTRIBUTING.MD FOR MORE DETAILS ON THE IMPLICATIONS SURROUNDING YOUR CONTRIBUTIONS TO THIS REPOSITORY.
|
||||
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
/* This module imports */
|
||||
|
||||
const globals = require('./globals')
|
||||
const sessions = require('./sessions')
|
||||
const express = require('express')
|
||||
const graphqlHTTP = require('express-graphql')
|
||||
const schema = require('./schema/schema')
|
||||
const mongoose = require('mongoose')
|
||||
const mongodbConfig = require('./models/MongoDB')
|
||||
const checkJwt = require('./auth/middleware/jwt')
|
||||
|
||||
const cors = require('cors')
|
||||
|
||||
const app = express()
|
||||
|
||||
// Allow crosss-origin requests
|
||||
app.use(cors())
|
||||
|
||||
// Connect to the database
|
||||
mongoose.connect(mongodbConfig.connectionString, { useNewUrlParser: true })
|
||||
mongoose.connection.once('open', () => {
|
||||
console.log('Connected to Mongo')
|
||||
})
|
||||
|
||||
/* Here we bind all requests to this endpoint to be procecced by the GraphQL Library. */
|
||||
|
||||
app.get('/graphql', graphqlHTTP({
|
||||
schema,
|
||||
graphiql: true
|
||||
}))
|
||||
|
||||
app.post('/graphql', graphqlHTTP({
|
||||
schema,
|
||||
graphiql: false,
|
||||
context: req => ({ ...req })
|
||||
}))
|
||||
|
||||
app.post('/graphql', checkJwt, (err, req, res, next) => {
|
||||
console.log(req)
|
||||
if (err) return res.status(401).send(`[Authenticate Token Error] ${err.message}`)
|
||||
next()
|
||||
})
|
||||
|
||||
app.listen(4000, () => {
|
||||
console.log('Now listening for requests on port 4000')
|
||||
})
|
||||
@@ -1,91 +0,0 @@
|
||||
const MODULE_NAME = "token-decoder";
|
||||
|
||||
/*
|
||||
JSON Web Token (JWT) is a compact, URL-safe means of representing
|
||||
claims to be transferred between two parties. The claims in a JWT
|
||||
are encoded as a JSON object that is used as the payload of a JSON
|
||||
Web Signature (JWS) structure or as the plaintext of a JSON Web
|
||||
Encryption (JWE) structure, enabling the claims to be digitally
|
||||
signed or integrity protected with a Message Authentication Code
|
||||
(MAC) and/or encrypted.
|
||||
*/
|
||||
|
||||
const JSONWebToken = require('jsonwebtoken'); // A library to deal with JSON Web Tokens.
|
||||
const webTokenLibrary = require('jwks-rsa'); // A library to retrieve RSA signing keys from a JWKS (JSON Web Key Set) endpoint.
|
||||
|
||||
const AUTH_CONFIG = require('./Auth0'); // Config where we have our secrets.
|
||||
|
||||
const decoder = webTokenLibrary({
|
||||
cache: true,
|
||||
rateLimit: true,
|
||||
jwksRequestsPerMinute: 1,
|
||||
jwksUri: `https://${AUTH_CONFIG.domain}/.well-known/jwks.json`
|
||||
})
|
||||
|
||||
function decodeToken (token, callBackFunction) {
|
||||
|
||||
try{
|
||||
|
||||
if (global.INFO_LOG === true) { console.log("[INFO] " + MODULE_NAME + " -> decodeToken -> Entering function."); }
|
||||
|
||||
const decoded = JSONWebToken.decode(token, { complete: true });
|
||||
const header = decoded.header;
|
||||
const payload = decoded.payload;
|
||||
|
||||
if (
|
||||
header === null ||
|
||||
header.kid === null ||
|
||||
payload === null
|
||||
) {
|
||||
if (global.ERROR_LOG === true) { console.log ("[ERROR] " + MODULE_NAME + " -> decodeToken -> Invalid Token. "); }
|
||||
if (global.ERROR_LOG === true) { console.log ("[ERROR] " + MODULE_NAME + " -> decodeToken -> token = " + token); }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
decoder.getSigningKey(header.kid, onKeyReady);
|
||||
|
||||
function onKeyReady(err, key) {
|
||||
|
||||
if (global.INFO_LOG === true) { console.log("[INFO] " + MODULE_NAME + " -> decodeToken -> onKeyReady -> Entering function."); }
|
||||
|
||||
if (err) {
|
||||
|
||||
if (global.ERROR_LOG === true) { console.log ("[ERROR] " + MODULE_NAME + " -> decodeToken -> onKeyReady -> Error getting the key. "); }
|
||||
if (global.ERROR_LOG === true) { console.log ("[ERROR] " + MODULE_NAME + " -> decodeToken -> onKeyReady -> key = " + key); }
|
||||
if (global.ERROR_LOG === true) { console.log ("[ERROR] " + MODULE_NAME + " -> decodeToken -> onKeyReady -> err = " + err); }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
JSONWebToken.verify(
|
||||
token,
|
||||
key.publicKey,
|
||||
{ algorithms: ['RS256'] },
|
||||
onVerified);
|
||||
|
||||
function onVerified(err, decoded) {
|
||||
|
||||
if (global.INFO_LOG === true) { console.log("[INFO] " + MODULE_NAME + " -> decodeToken -> onKeyReady -> onVerified -> Entering function."); }
|
||||
|
||||
if (err) {
|
||||
|
||||
if (global.ERROR_LOG === true) { console.log ("[ERROR] " + MODULE_NAME + " -> decodeToken -> onKeyReady -> onVerified -> Error verifying. "); }
|
||||
if (global.ERROR_LOG === true) { console.log ("[ERROR] " + MODULE_NAME + " -> decodeToken -> onKeyReady -> onVerified -> decoded = " + decoded); }
|
||||
if (global.ERROR_LOG === true) { console.log ("[ERROR] " + MODULE_NAME + " -> decodeToken -> onKeyReady -> onVerified -> err = " + err); }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
return;
|
||||
}
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, decoded);
|
||||
}
|
||||
}
|
||||
} catch(err) {
|
||||
|
||||
if (global.ERROR_LOG === true) { console.log("[ERROR] " + MODULE_NAME + " -> decodeToken -> err = " + err); }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = decodeToken;
|
||||
@@ -1,15 +1,23 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"description": "Advanced Algos Users backend API",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"start": "nodemon --exec babel-node --presets env,stage-2 src/index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-preset-env": "^1.7.0",
|
||||
"babel-preset-stage-2": "^6.24.1",
|
||||
"nodemon": "^1.18.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"cors": "^2.8.4",
|
||||
"dotenv": "^6.0.0",
|
||||
"express": "^4.16.3",
|
||||
"express-graphql": "^0.6.12",
|
||||
"express-jwt": "^5.3.1",
|
||||
@@ -17,6 +25,7 @@
|
||||
"jsonwebtoken": "^8.3.0",
|
||||
"jwks-rsa": "^1.3.0",
|
||||
"lodash": "^4.17.10",
|
||||
"mongoose": "^5.2.12"
|
||||
"mongoose": "^5.2.12",
|
||||
"winston": "^3.1.0"
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
const jwt = require('express-jwt');
|
||||
const jwksRsa = require('jwks-rsa');
|
||||
const AUTH_CONFIG = require('../Auth0');
|
||||
|
||||
const jwt = require('express-jwt')
|
||||
const jwksRsa = require('jwks-rsa')
|
||||
|
||||
// Authentication middleware. When used, the
|
||||
// if the access token exists, it be verified against
|
||||
// the Auth0 JSON Web Key Set
|
||||
@@ -12,14 +13,14 @@ const checkJwt = jwt({
|
||||
cache: true,
|
||||
rateLimit: true,
|
||||
jwksRequestsPerMinute: 1,
|
||||
jwksUri: `https://${AUTH_CONFIG.domain}/.well-known/jwks.json`
|
||||
jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`
|
||||
}),
|
||||
|
||||
// Validate the audience and the issuer.
|
||||
credentialsRequired: false,
|
||||
audience: AUTH_CONFIG.api_audience,
|
||||
issuer: AUTH_CONFIG.issuer,
|
||||
audience: process.env.AUTH0_AUDIENCE,
|
||||
issuer: process.env.AUTH0_ISSUER,
|
||||
algorithms: [`RS256`]
|
||||
});
|
||||
})
|
||||
|
||||
module.exports = checkJwt ;
|
||||
module.exports = checkJwt
|
||||
@@ -0,0 +1,81 @@
|
||||
const MODULE_NAME = 'token-decoder'
|
||||
|
||||
/*
|
||||
JSON Web Token (JWT) is a compact, URL-safe means of representing
|
||||
claims to be transferred between two parties. The claims in a JWT
|
||||
are encoded as a JSON object that is used as the payload of a JSON
|
||||
Web Signature (JWS) structure or as the plaintext of a JSON Web
|
||||
Encryption (JWE) structure, enabling the claims to be digitally
|
||||
signed or integrity protected with a Message Authentication Code
|
||||
(MAC) and/or encrypted.
|
||||
*/
|
||||
|
||||
const JSONWebToken = require('jsonwebtoken') // A library to deal with JSON Web Tokens.
|
||||
const webTokenLibrary = require('jwks-rsa') // A library to retrieve RSA signing keys from a JWKS (JSON Web Key Set) endpoint.
|
||||
|
||||
const decoder = webTokenLibrary({
|
||||
cache: true,
|
||||
rateLimit: true,
|
||||
jwksRequestsPerMinute: 1,
|
||||
jwksUri: `https://${process.env.AUTH0_DOMAIN}/.well-known/jwks.json`
|
||||
})
|
||||
|
||||
function decodeToken (token, callBackFunction) {
|
||||
try {
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> decodeToken -> Entering function.') }
|
||||
|
||||
const decoded = JSONWebToken.decode(token, { complete: true })
|
||||
const header = decoded.header
|
||||
const payload = decoded.payload
|
||||
|
||||
if (
|
||||
header === null ||
|
||||
header.kid === null ||
|
||||
payload === null
|
||||
) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> Invalid Token. ') }
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> token = ' + token) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
decoder.getSigningKey(header.kid, onKeyReady)
|
||||
|
||||
function onKeyReady (err, key) {
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> decodeToken -> onKeyReady -> Entering function.') }
|
||||
|
||||
if (err) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> onKeyReady -> Error getting the key. ') }
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> onKeyReady -> key = ' + key) }
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> onKeyReady -> err = ' + err) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
JSONWebToken.verify(
|
||||
token,
|
||||
key.publicKey,
|
||||
{ algorithms: ['RS256'] },
|
||||
onVerified)
|
||||
|
||||
function onVerified (err, decoded) {
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> decodeToken -> onKeyReady -> onVerified -> Entering function.') }
|
||||
|
||||
if (err) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> onKeyReady -> onVerified -> Error verifying. ') }
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> onKeyReady -> onVerified -> decoded = ' + decoded) }
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> onKeyReady -> onVerified -> err = ' + err) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, decoded)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> decodeToken -> err = ' + err) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = decodeToken
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"errors": [
|
||||
{
|
||||
"code": 401,
|
||||
"message": "Wait, you're not supposed to be here, go through the gate as everyone else"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
/* This module imports */
|
||||
|
||||
import 'dotenv/config'
|
||||
import './globals'
|
||||
import logger from './logger'
|
||||
import express from 'express'
|
||||
import graphqlHTTP from 'express-graphql'
|
||||
import schema from './schema/schema'
|
||||
import mongoose from 'mongoose'
|
||||
import checkJwt from './auth/jwt'
|
||||
import wrongPreshared from './errors/notAllowed.json'
|
||||
import cors from 'cors'
|
||||
|
||||
require('dotenv').config()
|
||||
|
||||
const app = express()
|
||||
|
||||
// Checking preshared key
|
||||
app.post('/graphql', (req, res, next) => {
|
||||
if (req.headers.preshared === process.env.USERS_API_PRESHARED) {
|
||||
next()
|
||||
} else {
|
||||
logger.error('Wrong Pre-Shared Received', req.headers.preshared)
|
||||
res.send(wrongPreshared)
|
||||
}
|
||||
})
|
||||
|
||||
// Allow crosss-origin requests
|
||||
app.use(cors())
|
||||
|
||||
// Database Connection
|
||||
mongoose.connect(`mongodb://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@${process.env.DB_DOMAIN}`, { useNewUrlParser: true })
|
||||
|
||||
mongoose.connection.once('open', () => {
|
||||
logger.info('Connected to the DB.')
|
||||
})
|
||||
|
||||
/* Here we bind all requests to this endpoint to be procecced by the GraphQL Library. */
|
||||
|
||||
app.get('/graphql', graphqlHTTP({
|
||||
schema,
|
||||
graphiql: true
|
||||
}))
|
||||
|
||||
app.post('/graphql', graphqlHTTP(req => {
|
||||
return {
|
||||
schema,
|
||||
graphiql: false,
|
||||
context: {
|
||||
user: req.user,
|
||||
userId: req.headers.userid
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
app.post('/graphql', checkJwt, (err, req, res, next) => {
|
||||
if (err) return res.status(401).send(`[Authenticate Token Error] ${err.message}`)
|
||||
next()
|
||||
})
|
||||
|
||||
app.listen(4000, () => {
|
||||
logger.info('Now listening for requests on port 4000')
|
||||
})
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import { createLogger, format, transports } from 'winston'
|
||||
const { combine, timestamp, simple } = format
|
||||
import { inspect } from 'util'
|
||||
|
||||
const options = {
|
||||
file: {
|
||||
level: 'info',
|
||||
filename: '../logs/app.log',
|
||||
handleExceptions: true,
|
||||
json: true,
|
||||
maxsize: 5242880, // 5MB
|
||||
maxFiles: 5,
|
||||
colorize: false,
|
||||
timestamp: true
|
||||
},
|
||||
console: {
|
||||
level: 'debug',
|
||||
handleExceptions: true,
|
||||
json: false,
|
||||
colorize: true,
|
||||
timestamp: true
|
||||
}
|
||||
}
|
||||
|
||||
const logger = createLogger({
|
||||
level: 'info',
|
||||
format: format.combine(
|
||||
format.colorize(),
|
||||
format.timestamp({ format: 'YY/MM/DD HH:mm' }),
|
||||
format.printf(
|
||||
info => `${info.timestamp} - ${info.level}: ${JSON.parse(JSON.stringify(inspect(info.message)))}`
|
||||
)
|
||||
),
|
||||
transports: [
|
||||
new transports.File(options.file),
|
||||
new transports.Console(options.console)
|
||||
],
|
||||
exitOnError: false // do not exit on handled exceptions
|
||||
})
|
||||
|
||||
export default logger
|
||||
@@ -7,7 +7,7 @@ const Schema = mongoose.Schema
|
||||
const userSchema = new Schema({
|
||||
authId: String,
|
||||
referrerId: String,
|
||||
creationDate: String,
|
||||
creationDate: Number,
|
||||
alias: String,
|
||||
firstName: String,
|
||||
middleName: String,
|
||||
@@ -1,10 +1,11 @@
|
||||
const MODULE_NAME = 'schema'
|
||||
|
||||
const graphql = require('graphql')
|
||||
const _ = require('lodash')
|
||||
const User = require('../models/user')
|
||||
import logger from '../logger'
|
||||
import _ from 'lodash'
|
||||
import User from '../models/user'
|
||||
import tokenDecoder from '../auth/token-decoder'
|
||||
|
||||
const tokenDecoder = require('../auth/token-decoder')
|
||||
const graphql = require('graphql')
|
||||
|
||||
const {
|
||||
GraphQLObjectType,
|
||||
@@ -104,22 +105,22 @@ const RootQuery = new GraphQLObjectType({
|
||||
resolve (parent, args) {
|
||||
// Code to get data from data source.
|
||||
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Entering function.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Entering function.') }
|
||||
|
||||
/* In order to be able to wait for asyc calls to the database, we need to return a promise to GraphQL. */
|
||||
|
||||
const promiseToGraphQL = new Promise((resolve, reject) => {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> Entering function.') }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> args.authId = ' + args.authId) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> Entering function.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> args.authId = ' + args.authId) }
|
||||
|
||||
findUserByAuthId(args.authId, onUserFound)
|
||||
|
||||
function onUserFound (err, responseToGraphQL) {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> onUserFound -> Entering function.') }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> onUserFound -> responseToGraphQL = ' + JSON.stringify(responseToGraphQL)) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> onUserFound -> Entering function.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> onUserFound -> responseToGraphQL = ' + JSON.stringify(responseToGraphQL)) }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> onUserFound -> err.message = ' + err.message) }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> RootQuery -> userByAuthId -> resolve -> Promise -> onUserFound -> err.message = ' + err.message) }
|
||||
reject(responseToGraphQL)
|
||||
} else {
|
||||
resolve(responseToGraphQL)
|
||||
@@ -154,7 +155,7 @@ 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.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> RootQuery -> usersSearch -> resolve -> Entering function.') }
|
||||
|
||||
let mongoQuery = { $or: [] }
|
||||
|
||||
@@ -172,7 +173,7 @@ const RootQuery = new GraphQLObjectType({
|
||||
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.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> RootQuery -> descendents -> resolve -> Entering function.') }
|
||||
|
||||
return User.find({referrerId: args.id})
|
||||
}
|
||||
@@ -191,20 +192,20 @@ const Mutation = new GraphQLObjectType({
|
||||
idToken: {type: new GraphQLNonNull(GraphQLString)}
|
||||
},
|
||||
resolve (parent, args) {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Entering function.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Entering function.') }
|
||||
|
||||
/* In order to be able to wait for asyc calls to the database, and authorization authority, we need to return a promise to GraphQL. */
|
||||
|
||||
const promiseToGraphQL = new Promise((resolve, reject) => {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Promise -> Entering function.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Promise -> Entering function.') }
|
||||
authenticate(args.idToken, onAuthenticated)
|
||||
|
||||
function onAuthenticated (err, responseToGraphQL) {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Promise -> onAuthenticated -> Entering function.') }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Promise -> onAuthenticated -> responseToGraphQL = ' + JSON.stringify(responseToGraphQL)) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Promise -> onAuthenticated -> Entering function.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Promise -> onAuthenticated -> responseToGraphQL = ' + JSON.stringify(responseToGraphQL)) }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Promise -> onAuthenticated -> err.message = ' + err.message) }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> Mutation -> authenticate -> resolve -> Promise -> onAuthenticated -> err.message = ' + err.message) }
|
||||
reject(responseToGraphQL)
|
||||
} else {
|
||||
resolve(responseToGraphQL)
|
||||
@@ -218,7 +219,6 @@ const Mutation = new GraphQLObjectType({
|
||||
updateUser: {
|
||||
type: UserType,
|
||||
args: {
|
||||
id: {type: new GraphQLNonNull(GraphQLID)},
|
||||
firstName: {type: GraphQLString},
|
||||
middleName: {type: GraphQLString},
|
||||
lastName: {type: GraphQLString},
|
||||
@@ -228,9 +228,9 @@ const Mutation = new GraphQLObjectType({
|
||||
isDataAnalyst: {type: GraphQLInt},
|
||||
roleId: {type: new GraphQLNonNull(GraphQLString)}
|
||||
},
|
||||
resolve (parent, args) {
|
||||
resolve (parent, args, context) {
|
||||
let key = {
|
||||
_id: args.id
|
||||
_id: context.userId
|
||||
}
|
||||
|
||||
let updatedUser = {
|
||||
@@ -247,35 +247,14 @@ const Mutation = new GraphQLObjectType({
|
||||
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)
|
||||
}
|
||||
},
|
||||
updateUserReferrer: {
|
||||
type: UserType,
|
||||
args: {
|
||||
id: {type: new GraphQLNonNull(GraphQLID)},
|
||||
referrerId: {type: GraphQLString}
|
||||
},
|
||||
resolve (parent, args) {
|
||||
resolve (parent, args, context) {
|
||||
let key = {
|
||||
_id: args.id,
|
||||
_id: context.userId,
|
||||
referrerId: null
|
||||
}
|
||||
|
||||
@@ -283,6 +262,24 @@ const Mutation = new GraphQLObjectType({
|
||||
referrerId: args.referrerId
|
||||
}
|
||||
|
||||
return User.update(key, updatedUser)
|
||||
}
|
||||
},
|
||||
updateSessionToken: {
|
||||
type: UserType,
|
||||
args: {
|
||||
userId: {type: GraphQLString},
|
||||
sessionToken: {type: GraphQLString}
|
||||
},
|
||||
resolve (parent, args) {
|
||||
let key = {
|
||||
_id: decodeURI(args.userId)
|
||||
}
|
||||
|
||||
let updatedUser = {
|
||||
sessionToken: args.sessionToken
|
||||
}
|
||||
|
||||
return User.update(key, updatedUser)
|
||||
}
|
||||
}
|
||||
@@ -291,12 +288,12 @@ const Mutation = new GraphQLObjectType({
|
||||
|
||||
function findUserByAuthId (authId, callBackFunction) {
|
||||
try {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> Entering function.') }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> authId = ' + authId) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> Entering function.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> authId = ' + authId) }
|
||||
|
||||
if (authId === null || authId === undefined) {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> User requested not specified.') }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> args.authId = ' + authId) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> User requested not specified.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> args.authId = ' + authId) }
|
||||
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: 'Bad Request' })
|
||||
return
|
||||
@@ -306,13 +303,13 @@ function findUserByAuthId (authId, callBackFunction) {
|
||||
|
||||
function onUserReceived (err, user) {
|
||||
if (err) {
|
||||
if (ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> Database Error.') }
|
||||
if (ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> err = ' + err) }
|
||||
if (ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> Database Error.') }
|
||||
if (ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> err = ' + err) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: err })
|
||||
} else {
|
||||
if (user === null) {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> User not found at Database.') }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> args.authId = ' + authId) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> User not found at Database.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> args.authId = ' + authId) }
|
||||
|
||||
let customResponse = {
|
||||
result: global.CUSTOM_OK_RESPONSE.result,
|
||||
@@ -324,15 +321,15 @@ function findUserByAuthId (authId, callBackFunction) {
|
||||
}
|
||||
|
||||
if (user.authId === authId && user.authId !== undefined) {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> User found at Database.') }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> args.authId = ' + authId) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> User found at Database.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> args.authId = ' + authId) }
|
||||
|
||||
callBackFunction(global.DEFAULT_OK_RESPONSE, user)
|
||||
return
|
||||
} else {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> User found at Database is not the user requested.') }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> args.authId = ' + authId) }
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> user.authId = ' + user.authId) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> User found at Database is not the user requested.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> args.authId = ' + authId) }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> findUserByAuthId -> onUserReceived -> user.authId = ' + user.authId) }
|
||||
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: err })
|
||||
return
|
||||
@@ -340,14 +337,14 @@ function findUserByAuthId (authId, callBackFunction) {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> findUserByAuthId -> err.message = ' + err.message) }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> findUserByAuthId -> err.message = ' + err.message) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: err })
|
||||
}
|
||||
}
|
||||
|
||||
function authenticate (encodedToken, callBackFunction) {
|
||||
try {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> Entering function.') }
|
||||
if (INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> Entering function.') }
|
||||
|
||||
let authId = ''
|
||||
let alias = ''
|
||||
@@ -357,10 +354,10 @@ function authenticate (encodedToken, callBackFunction) {
|
||||
tokenDecoder(encodedToken, onValidated)
|
||||
|
||||
function onValidated (err, decodedToken) {
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> Entering function.') }
|
||||
if (global.INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> Entering function.') }
|
||||
|
||||
if (err.result !== global.DEFAULT_OK_RESPONSE.result) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> err.message = ' + err.message) }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> err.message = ' + err.message) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: err })
|
||||
return
|
||||
}
|
||||
@@ -380,18 +377,18 @@ function authenticate (encodedToken, callBackFunction) {
|
||||
findUserByAuthId(authId, onUserFound)
|
||||
|
||||
function onUserFound (err, user) {
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> Entering function.') }
|
||||
if (global.INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> Entering function.') }
|
||||
|
||||
if (err.result === global.DEFAULT_FAIL_RESPONSE.result) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> err.message = ' + err.message) }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> err.message = ' + err.message) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: err })
|
||||
return
|
||||
}
|
||||
|
||||
if (err.result === global.DEFAULT_OK_RESPONSE.result) {
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> User already exists at database') }
|
||||
if (global.INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> User already exists at database') }
|
||||
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> Existing User: ' + JSON.stringify(user)) }
|
||||
if (global.INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> Existing User: ' + JSON.stringify(user)) }
|
||||
|
||||
/*
|
||||
|
||||
@@ -410,7 +407,7 @@ function authenticate (encodedToken, callBackFunction) {
|
||||
err.result === global.CUSTOM_OK_RESPONSE.result &&
|
||||
err.message === 'User Not Found'
|
||||
) {
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> User does not exist at database') }
|
||||
if (global.INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> User does not exist at database') }
|
||||
|
||||
/*
|
||||
|
||||
@@ -429,47 +426,39 @@ function authenticate (encodedToken, callBackFunction) {
|
||||
socialAccountProvider !== 'github' &&
|
||||
socialAccountProvider !== 'auth0'
|
||||
) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> Social Account Provider not Supoorted. ') }
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> socialAccountProvider = ' + socialAccountProvider) }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> Social Account Provider not Supoorted. ') }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> socialAccountProvider = ' + socialAccountProvider) }
|
||||
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: err })
|
||||
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 creationDate = localDate.valueOf()
|
||||
|
||||
let newUser = new User({
|
||||
alias: alias,
|
||||
authId: authId,
|
||||
creationDate: creationDate.toISOString(),
|
||||
creationDate: creationDate,
|
||||
email: email,
|
||||
emailVerified: emailVerified,
|
||||
roleId: '1'
|
||||
})
|
||||
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> ' + alias + ' being added to the database') }
|
||||
if (global.INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> ' + alias + ' being added to the database') }
|
||||
|
||||
newUser.save(onSaved)
|
||||
|
||||
function onSaved (err, savedUser) {
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> onSaved -> Entering function.') }
|
||||
if (global.INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> onSaved -> Entering function.') }
|
||||
|
||||
if (err) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> onSaved -> err = ' + err) }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> onSaved -> err = ' + err) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: err })
|
||||
return
|
||||
}
|
||||
|
||||
if (global.INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> onSaved -> Saved User: ' + JSON.stringify(savedUser)) }
|
||||
if (global.INFO_LOG === true) { logger.debug('[INFO] ' + MODULE_NAME + ' -> authenticate -> onValidated -> onUserFound -> onSaved -> Saved User: ' + JSON.stringify(savedUser)) }
|
||||
|
||||
/*
|
||||
|
||||
@@ -486,7 +475,7 @@ function authenticate (encodedToken, callBackFunction) {
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (global.ERROR_LOG === true) { console.log('[ERROR] ' + MODULE_NAME + ' -> authenticate -> err.message = ' + err.message) }
|
||||
if (global.ERROR_LOG === true) { logger.error('[ERROR] ' + MODULE_NAME + ' -> authenticate -> err.message = ' + err.message) }
|
||||
callBackFunction(global.DEFAULT_FAIL_RESPONSE, { error: err })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
const getItem = async name => window.localStorage.getItem(name)
|
||||
const setItem = async (name, value) => window.localStorage.setItem(name, value)
|
||||
const removeItem = async name => window.localStorage.removeItem(name)
|
||||
|
||||
export {
|
||||
getItem,
|
||||
setItem,
|
||||
removeItem
|
||||
}
|
||||