Merge branch 'develop'
No files matched your search
@@ -20,7 +20,7 @@ App.js
|
||||
| | |
|
||||
| | |- UserTabs
|
||||
| | | |
|
||||
| | | |- Tab 1 - UserUpdate
|
||||
| | | |- Tab 1 - ProfileSheet
|
||||
| | | |
|
||||
| | | |- Tab 2 - ManageImages
|
||||
| |
|
||||
|
||||
@@ -4628,6 +4628,11 @@
|
||||
"schema-utils": "0.3.0"
|
||||
}
|
||||
},
|
||||
"file-type": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/file-type/-/file-type-8.1.0.tgz",
|
||||
"integrity": "sha512-qyQ0pzAy78gVoJsmYeNgl8uH8yKhr1lVhW7JbzJmnlRi0I4R2eEDEJZVKG8agpDnLpacwNbDhLNG/LMdxHD2YQ=="
|
||||
},
|
||||
"filename-regex": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
|
||||
@@ -4647,6 +4652,44 @@
|
||||
"resolved": "https://registry.npmjs.org/filesize/-/filesize-3.5.11.tgz",
|
||||
"integrity": "sha512-ZH7loueKBoDb7yG9esn1U+fgq7BzlzW6NRi5/rMdxIZ05dj7GFD/Xc5rq2CDt5Yq86CyfSYVyx4242QQNZbx1g=="
|
||||
},
|
||||
"filestack-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/filestack-js/-/filestack-js-1.5.1.tgz",
|
||||
"integrity": "sha512-NXYZhKl0JySNNpQNWtKdyMfQAiMw6VF8Mx+XWHGg+gh3By3ITjR0wTorjM02CVr/6Od/DXQCN1e+6np1Zex9Nw==",
|
||||
"requires": {
|
||||
"abab": "1.0.4",
|
||||
"bowser": "1.9.4",
|
||||
"file-type": "8.1.0",
|
||||
"filestack-loader": "3.0.4",
|
||||
"is-svg": "3.0.0",
|
||||
"isutf8": "2.0.2",
|
||||
"mime-types": "2.1.20",
|
||||
"spark-md5": "3.0.0",
|
||||
"superagent": "3.8.3",
|
||||
"tcomb-validation": "3.4.1",
|
||||
"tslib": "1.9.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"is-svg": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-svg/-/is-svg-3.0.0.tgz",
|
||||
"integrity": "sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ==",
|
||||
"requires": {
|
||||
"html-comment-regex": "1.1.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"filestack-loader": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filestack-loader/-/filestack-loader-3.0.4.tgz",
|
||||
"integrity": "sha512-b6uOCWHd1gM0+5KBA1rA4qfEgTqyTr5umLM4bBWT4z98WUwxa6KzCiq+z0VnR4rN+NCx6kyZ/wLXjGcPU32TxQ=="
|
||||
},
|
||||
"filestack-react": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/filestack-react/-/filestack-react-2.0.0.tgz",
|
||||
"integrity": "sha512-+lOqPx2XYsvm3pJutjlDTyG8fdrJAefN0QLi8crJfwI1dJffOjK84dnQJHQ9aQzxF37A1cHI8ZbPscv7w6bRTA=="
|
||||
},
|
||||
"fill-range": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz",
|
||||
@@ -6516,6 +6559,11 @@
|
||||
"handlebars": "4.0.11"
|
||||
}
|
||||
},
|
||||
"isutf8": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/isutf8/-/isutf8-2.0.2.tgz",
|
||||
"integrity": "sha512-9lNbG6k+PE6vuFnPTHjhtX79/LDMQrXoFx1MOEjXXCyqFCSahWKAzuAb9G5ct+lfS12aQxNmC0tiZuyujvr4Uw=="
|
||||
},
|
||||
"iterall": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz",
|
||||
@@ -10805,6 +10853,11 @@
|
||||
"resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
|
||||
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM="
|
||||
},
|
||||
"spark-md5": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.0.tgz",
|
||||
"integrity": "sha1-NyIifFTi+vJLHcbZM8wUTm9xv+8="
|
||||
},
|
||||
"spdx-correct": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.0.tgz",
|
||||
@@ -11170,6 +11223,19 @@
|
||||
"resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz",
|
||||
"integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI="
|
||||
},
|
||||
"tcomb": {
|
||||
"version": "3.2.27",
|
||||
"resolved": "https://registry.npmjs.org/tcomb/-/tcomb-3.2.27.tgz",
|
||||
"integrity": "sha512-XWdJW7F/M3YzXhDEUP8ycmNWoYymBtsHwCHoda0YF44RthJsls95TqDrmpAlC1sB/KXaCvkdBlcNRq+AaV6klA=="
|
||||
},
|
||||
"tcomb-validation": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/tcomb-validation/-/tcomb-validation-3.4.1.tgz",
|
||||
"integrity": "sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==",
|
||||
"requires": {
|
||||
"tcomb": "3.2.27"
|
||||
}
|
||||
},
|
||||
"term-size": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
|
||||
@@ -11340,6 +11406,11 @@
|
||||
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
||||
"integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM="
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
||||
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
|
||||
},
|
||||
"tty-browserify": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
"apollo-link-error": "^1.1.0",
|
||||
"apollo-link-http": "^1.5.4",
|
||||
"auth0-lock": "^11.9.0",
|
||||
"filestack-js": "^1.5.1",
|
||||
"filestack-react": "^2.0.0",
|
||||
"graphql": "^14.0.0",
|
||||
"graphql-tag": "^2.9.2",
|
||||
"react": "^16.4.2",
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
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 -->
|
||||
@@ -52,15 +53,5 @@
|
||||
</noscript>
|
||||
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -11,7 +11,8 @@ import { Route, BrowserRouter, Switch } from 'react-router-dom'
|
||||
|
||||
// Material UI
|
||||
|
||||
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles'
|
||||
import { MuiThemeProvider } from '@material-ui/core/styles'
|
||||
import theme from './theme'
|
||||
|
||||
// Components
|
||||
|
||||
@@ -26,11 +27,16 @@ import Modules from './components/Modules'
|
||||
import Contact from './components/Contact'
|
||||
import Logout from './components/Logout'
|
||||
import Post from './components/Post'
|
||||
import Footer from './components/Footer'
|
||||
// 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 })
|
||||
|
||||
@@ -70,15 +76,6 @@ export const client = new ApolloClient({
|
||||
|
||||
export const auth = new Auth(result => console.log('auth result', result), client)
|
||||
|
||||
/* Here we change the default Material UI theme for Advanced Algos brand colors. */
|
||||
|
||||
const theme = createMuiTheme({
|
||||
palette: {
|
||||
primary: { main: '#303036' }, // DARK.
|
||||
secondary: { main: '#CC5835' } // RUSTED_RED.
|
||||
}
|
||||
})
|
||||
|
||||
class App extends Component {
|
||||
render () {
|
||||
return (
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<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
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1 @@
|
||||
<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
|
||||
|
After Width: | Height: | Size: 5.3 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
After Width: | Height: | Size: 502 B |
|
After Width: | Height: | Size: 758 B |
|
After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,9 @@
|
||||
<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">
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1 @@
|
||||
<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>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
import React, { Component } from 'react'
|
||||
import {graphql, compose} from 'react-apollo'
|
||||
import {getDescendentsQuery} from '../queries/queries'
|
||||
|
||||
// Materia UI
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Card from '@material-ui/core/Card'
|
||||
import CardActionArea from '@material-ui/core/CardActionArea'
|
||||
import CardActions from '@material-ui/core/CardActions'
|
||||
import CardContent from '@material-ui/core/CardContent'
|
||||
import CardMedia from '@material-ui/core/CardMedia'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
|
||||
// Material UI Nestled Lists
|
||||
import ListSubheader from '@material-ui/core/ListSubheader';
|
||||
import List from '@material-ui/core/List';
|
||||
import ListItem from '@material-ui/core/ListItem';
|
||||
import ListItemIcon from '@material-ui/core/ListItemIcon';
|
||||
import ListItemText from '@material-ui/core/ListItemText';
|
||||
import Collapse from '@material-ui/core/Collapse';
|
||||
import ExpandLess from '@material-ui/icons/ExpandLess';
|
||||
import ExpandMore from '@material-ui/icons/ExpandMore';
|
||||
import StarBorder from '@material-ui/icons/StarBorder';
|
||||
|
||||
// Images
|
||||
import PortraitImage from '../img/portrait.jpg'
|
||||
|
||||
const styles = theme => ({
|
||||
card: {
|
||||
maxWidth: 300,
|
||||
minWidth: 300,
|
||||
paddingTop: '30'
|
||||
},
|
||||
media: {
|
||||
height: 0,
|
||||
paddingTop: '56.25%', // 16:9,
|
||||
marginTop: '30',
|
||||
minWidth: 300
|
||||
},
|
||||
typography: {
|
||||
maxWidth: 345,
|
||||
minWidth: 300,
|
||||
paddingTop: '30'
|
||||
},
|
||||
appBar: {
|
||||
position: 'relative'
|
||||
},
|
||||
flex: {
|
||||
flex: 1
|
||||
},
|
||||
grid: {
|
||||
paddingTop: '30',
|
||||
marginTop: 30
|
||||
},
|
||||
list: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
marginTop: 25
|
||||
},
|
||||
button: {
|
||||
margin: theme.spacing.unit,
|
||||
marginTop: theme.spacing.unit * 3
|
||||
},
|
||||
nested: {
|
||||
paddingLeft: theme.spacing.unit * 4,
|
||||
},
|
||||
})
|
||||
|
||||
class DescendentsTree extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
id: '',
|
||||
open: true
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = () => {
|
||||
this.setState(state => ({ open: !state.open }));
|
||||
};
|
||||
|
||||
displayIcons(amount) {
|
||||
|
||||
switch (amount) {
|
||||
case 1: return (
|
||||
<div>
|
||||
<ListItemIcon>
|
||||
<StarBorder />
|
||||
</ListItemIcon>
|
||||
</div>
|
||||
)
|
||||
case 2: return (
|
||||
<div>
|
||||
<ListItemIcon>
|
||||
<StarBorder />
|
||||
</ListItemIcon>
|
||||
<ListItemIcon>
|
||||
<StarBorder />
|
||||
</ListItemIcon>
|
||||
</div>
|
||||
)
|
||||
case 3: return (
|
||||
<div>
|
||||
<ListItemIcon>
|
||||
<StarBorder />
|
||||
</ListItemIcon>
|
||||
<ListItemIcon>
|
||||
<StarBorder />
|
||||
</ListItemIcon>
|
||||
<ListItemIcon>
|
||||
<StarBorder />
|
||||
</ListItemIcon>
|
||||
</div>
|
||||
)
|
||||
default: return(<div/>)
|
||||
}
|
||||
}
|
||||
|
||||
displayNestedListItems(node, level) {
|
||||
|
||||
const { classes } = this.props
|
||||
return node.descendents.map(descendent => {
|
||||
return (
|
||||
<div key={descendent.alias}>
|
||||
<ListItem button className={classes.nested}>
|
||||
{this.displayIcons(level)}
|
||||
<Card className={classes.card}>
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image={PortraitImage}
|
||||
title='User Profile'
|
||||
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant='headline' component='h2'>
|
||||
{descendent.alias}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<CardActions>
|
||||
<Grid container justify='center' spacing={8}>
|
||||
<Grid item />
|
||||
</Grid>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</ListItem>
|
||||
{this.displayNestedList(descendent, level)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
displayNestedList(descendent, level) {
|
||||
const primaryText = "Children of " + descendent.alias
|
||||
if (descendent.descendents !== undefined) {
|
||||
if (descendent.descendents.length > 0) {
|
||||
return(
|
||||
<List component="div" disablePadding>
|
||||
<ListItem button onClick={ () => {
|
||||
this.setState(state => ({ [descendent.alias]: !state[descendent.alias] }));
|
||||
}
|
||||
}>
|
||||
<ListItemText inset primary={primaryText} />
|
||||
{this.state[descendent.alias] ? <ExpandLess /> : <ExpandMore />}
|
||||
</ListItem>
|
||||
<Collapse in={this.state[descendent.alias]} timeout="auto" unmountOnExit>
|
||||
{this.displayNestedListItems(descendent, level + 1)}
|
||||
</Collapse>
|
||||
</List>
|
||||
)
|
||||
} else {
|
||||
return (<div/>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
displayTopList () {
|
||||
let data = this.props.data
|
||||
const { classes } = this.props
|
||||
|
||||
if (data.loading) {
|
||||
return (<div> Loading Descendents... </div>)
|
||||
} else {
|
||||
if (data.descendents === undefined) {
|
||||
return (<div> No Descendents to Display </div>)
|
||||
} else {
|
||||
return data.descendents.map(descendent => {
|
||||
return (
|
||||
<div key={descendent.alias}>
|
||||
|
||||
<ListItem button>
|
||||
{this.displayIcons(1)}
|
||||
<Card className={classes.card}>
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image={PortraitImage}
|
||||
title='User Profile'
|
||||
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant='headline' component='h2'>
|
||||
{descendent.alias}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<CardActions>
|
||||
<Grid container justify='center' spacing={8}>
|
||||
<Grid item />
|
||||
</Grid>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</ListItem>
|
||||
|
||||
{this.displayNestedList(descendent, 1)}
|
||||
|
||||
</div>
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<div>
|
||||
<List
|
||||
component="nav"
|
||||
subheader={<ListSubheader component="div"></ListSubheader>}
|
||||
className={classes.list}
|
||||
>
|
||||
{this.displayTopList()}
|
||||
</List>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
graphql(getDescendentsQuery, { // What follows is the way to pass a parameter to a query.
|
||||
options: (props) => {
|
||||
return {
|
||||
variables: {
|
||||
name: 'getDescendentsQuery',
|
||||
id: props.userId
|
||||
}
|
||||
}
|
||||
}}),
|
||||
withStyles(styles)
|
||||
)(DescendentsTree) // This technique binds more than one query to a single component.
|
||||
@@ -1,84 +0,0 @@
|
||||
import React, { Component } from 'react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { compose } from 'recompose'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Button from '@material-ui/core/Button'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
|
||||
// Images
|
||||
import AALogo from '../img/aa-logo.png'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
padding: 100
|
||||
},
|
||||
img: {
|
||||
margin: 'auto',
|
||||
display: 'block',
|
||||
maxWidth: 120,
|
||||
maxHeight: 24
|
||||
},
|
||||
button: {
|
||||
margin: theme.spacing.unit
|
||||
}
|
||||
})
|
||||
|
||||
class Footer extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
selected: '',
|
||||
user: null
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
|
||||
<Grid className={classes.button} container justify='center' spacing={24}>
|
||||
<Grid item>
|
||||
<Button color='primary' className={classes.button}>
|
||||
<NavLink color='inherit' exact to='/'>Home</NavLink>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button color='primary' className={classes.button}>
|
||||
<NavLink to='/browse'>Browse</NavLink>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button color='primary' className={classes.button}>
|
||||
<NavLink to='/search'>Search</NavLink>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button color='primary' className={classes.button}>
|
||||
<a href='http://www.advancedalgos.net'><img className={classes.img} alt='complex' src={AALogo} /></a>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button color='primary' className={classes.button}>
|
||||
<NavLink color='inherit' exact to='/contact'>Contact</NavLink>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button color='primary' className={classes.button}>
|
||||
<NavLink to='/about'>About</NavLink>
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<Button color='primary' className={classes.button}>
|
||||
<NavLink to='/modules'>Modules</NavLink>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withStyles(styles)
|
||||
)(Footer)
|
||||
@@ -2,9 +2,9 @@ import React, { Component } from 'react'
|
||||
import { compose } from 'recompose'
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import {LineChart} from 'react-easy-chart'
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import CssBaseline from '@material-ui/core/CssBaseline'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
@@ -19,6 +19,26 @@ const styles = theme => ({
|
||||
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`
|
||||
}
|
||||
})
|
||||
|
||||
@@ -27,41 +47,63 @@ class Home extends Component {
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<React.Fragment>
|
||||
<CssBaseline />
|
||||
<main className={classes.layout}>
|
||||
<div className={classes.heroContent}>
|
||||
<Typography
|
||||
variant='display3'
|
||||
align='center'
|
||||
color='textPrimary'
|
||||
gutterBottom
|
||||
>
|
||||
Collaborate. Innovate. Evolve.
|
||||
</Typography>
|
||||
<Typography
|
||||
variant='title'
|
||||
align='center'
|
||||
color='textSecondary'
|
||||
component='h3'
|
||||
>
|
||||
Welcome to Advanced Algos Users Module
|
||||
</Typography>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Grid container justify='center' spacing={24}>
|
||||
<Grid className={classes.grid} item>
|
||||
<Typography className={classes.typography} gutterBottom variant='headline' component='h2'>
|
||||
Welcome to the Users Module
|
||||
</Typography>
|
||||
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>
|
||||
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>
|
||||
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 1st sprint of 2 weeks of work.
|
||||
</Typography>
|
||||
This module is is currently under heavy development. Implementation started 1st of September 2018 and this is the result of the 1st sprint of 2 weeks of work.
|
||||
</Typography>
|
||||
<Typography className={classes.typography} gutterBottom variant='headline' component='h2'>
|
||||
What can you do?
|
||||
</Typography>
|
||||
What can you do?
|
||||
</Typography>
|
||||
<Typography className={classes.typography} component='p'>
|
||||
Currently you can use the Browse section to list all registered users. 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. You will find all features under develpment with a dialog indicating it is not ready.
|
||||
</Typography>
|
||||
Currently you can use the Browse section to list all registered users. 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. You will find all features under develpment with a dialog indicating it is not ready.
|
||||
</Typography>
|
||||
<Typography className={classes.typography} gutterBottom variant='headline' component='h2'>
|
||||
What is next?
|
||||
</Typography>
|
||||
What is next?
|
||||
</Typography>
|
||||
<Typography className={classes.typography} component='p'>
|
||||
The major features for the next sprint are:
|
||||
</Typography>
|
||||
The major features for the next sprint are:
|
||||
</Typography>
|
||||
<ul>
|
||||
<li>1. Managing profile images.</li>
|
||||
<li>2. Making sure that Auth0 integration server side is working well.</li>
|
||||
<li>3. Making sure that mutations can not be impersonated.</li>
|
||||
</ul>
|
||||
<Typography className={classes.typography} component='p' >
|
||||
Below, you can see some usage statistics of this module, in this case, the total amount of users over time. [data is not real yet.]
|
||||
</Typography>
|
||||
Below, you can see some usage statistics of this module, in this case, the total amount of users over time. [data is not real yet.]
|
||||
</Typography>
|
||||
<LineChart
|
||||
xType={'time'}
|
||||
axes
|
||||
@@ -70,18 +112,18 @@ class Home extends Component {
|
||||
height={250}
|
||||
data={[
|
||||
[
|
||||
{ x: '1-Sep-17', y: 2 },
|
||||
{ x: '2-Sep-17', y: 4 },
|
||||
{ x: '3-Sep-17', y: 5 },
|
||||
{ x: '4-Sep-17', y: 6 },
|
||||
{ x: '5-Sep-17', y: 7 }
|
||||
{ x: '1-Sep-17', y: 2 },
|
||||
{ x: '2-Sep-17', y: 4 },
|
||||
{ x: '3-Sep-17', y: 5 },
|
||||
{ x: '4-Sep-17', y: 6 },
|
||||
{ x: '5-Sep-17', y: 7 }
|
||||
]
|
||||
]}
|
||||
/>
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</Paper>
|
||||
</React.Fragment>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import { Link } from 'react-router-dom'
|
||||
import LoggedInUser from './LoggedInUser'
|
||||
|
||||
// Images
|
||||
import AALogo from '../img/aa-logo-dark-8.png'
|
||||
import AALogo from '../assets/logos/advanced-algos/aa-logo-dark.svg'
|
||||
|
||||
const ModulesLink = props => <Link to='/modules' {...props} />
|
||||
const AboutLink = props => <Link to='/about' {...props} />
|
||||
@@ -43,8 +43,8 @@ const styles = {
|
||||
img: {
|
||||
margin: 20,
|
||||
display: 'block',
|
||||
maxWidth: 120,
|
||||
maxHeight: 24
|
||||
maxWidth: 240,
|
||||
maxHeight: 48
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
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 { 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%'
|
||||
},
|
||||
typography: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
marginTop: 40
|
||||
}
|
||||
})
|
||||
|
||||
class ProfileImages extends React.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() {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<Typography className={classes.typography} variant='headline' gutterBottom>
|
||||
Profile Images
|
||||
</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>
|
||||
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
graphql(updateUserImagesMutation, {name: 'updateUserImagesMutation'}),
|
||||
withStyles(styles)
|
||||
)(ProfileImages) // This technique binds more than one query to a single component.
|
||||
@@ -62,7 +62,7 @@ const styles = theme => ({
|
||||
}
|
||||
})
|
||||
|
||||
class UserUpdate extends Component {
|
||||
class ProfileSheet extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
@@ -467,4 +467,4 @@ export default compose(
|
||||
graphql(getRolesQuery, {name: 'getRolesQuery'}),
|
||||
graphql(updateUserMutation, {name: 'updateUserMutation'}),
|
||||
withStyles(styles)
|
||||
)(UserUpdate) // This technique binds more than one query to a single component.
|
||||
)(ProfileSheet) // This technique binds more than one query to a single component.
|
||||
@@ -1,48 +1,37 @@
|
||||
import React from 'react';
|
||||
import Button from '@material-ui/core/Button';
|
||||
import Dialog from '@material-ui/core/Dialog';
|
||||
import DialogActions from '@material-ui/core/DialogActions';
|
||||
import DialogContent from '@material-ui/core/DialogContent';
|
||||
import DialogContentText from '@material-ui/core/DialogContentText';
|
||||
import DialogTitle from '@material-ui/core/DialogTitle';
|
||||
import React, { Component } from 'react'
|
||||
import UserSearch from './UserSearch'
|
||||
import {compose} from 'react-apollo'
|
||||
|
||||
class Search extends React.Component {
|
||||
state = {
|
||||
open: true,
|
||||
};
|
||||
// Materia UI
|
||||
|
||||
handleClickOpen = () => {
|
||||
this.setState({ open: true });
|
||||
};
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
|
||||
handleClose = () => {
|
||||
this.setState({ open: false });
|
||||
};
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
flexGrow: 1,
|
||||
padding: 60,
|
||||
margin: 2
|
||||
}
|
||||
})
|
||||
|
||||
render() {
|
||||
class Search extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
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>
|
||||
);
|
||||
<Paper className={classes.root}>
|
||||
<UserSearch />
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default Search;
|
||||
export default compose(
|
||||
withStyles(styles)
|
||||
)(Search)
|
||||
@@ -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 UserImages 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 UserImages;
|
||||
@@ -143,7 +143,9 @@ class UserList extends Component {
|
||||
</Button>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<Paper className={classes.root}>
|
||||
<UserProfile userId={this.state.selected}/>
|
||||
</Paper>
|
||||
</Dialog>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
|
||||
@@ -6,7 +6,6 @@ import {getUserProfileQuery} from '../queries/queries'
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
import Card from '@material-ui/core/Card'
|
||||
import CardActionArea from '@material-ui/core/CardActionArea'
|
||||
import CardContent from '@material-ui/core/CardContent'
|
||||
@@ -24,15 +23,14 @@ const styles = theme => ({
|
||||
margin: 2
|
||||
},
|
||||
card: {
|
||||
maxWidth: 800,
|
||||
minWidth: 800,
|
||||
width: '80%',
|
||||
paddingTop: '30'
|
||||
},
|
||||
media: {
|
||||
height: 0,
|
||||
paddingTop: '56.25%', // 16:9,
|
||||
marginTop: '30',
|
||||
minWidth: 800
|
||||
minWidth: '80%'
|
||||
},
|
||||
grid: {
|
||||
paddingTop: '30',
|
||||
@@ -92,13 +90,10 @@ class UserProfile extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<Grid container justify='center' spacing={24}>
|
||||
{this.displayUserProfile()}
|
||||
</Grid>
|
||||
</Paper>
|
||||
<Grid container justify='center' spacing={24}>
|
||||
{this.displayUserProfile()}
|
||||
</Grid>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,267 @@
|
||||
import React, { Component } from 'react'
|
||||
import {graphql, compose} from 'react-apollo'
|
||||
import {getUsersBySearchFields} from '../queries/queries'
|
||||
|
||||
// Materia UI
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Card from '@material-ui/core/Card'
|
||||
import CardActionArea from '@material-ui/core/CardActionArea'
|
||||
import CardActions from '@material-ui/core/CardActions'
|
||||
import CardContent from '@material-ui/core/CardContent'
|
||||
import CardMedia from '@material-ui/core/CardMedia'
|
||||
import Button from '@material-ui/core/Button'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
|
||||
import TextField from '@material-ui/core/TextField'
|
||||
|
||||
// Images
|
||||
import PortraitImage from '../img/portrait.jpg'
|
||||
|
||||
const styles = theme => ({
|
||||
card: {
|
||||
maxWidth: 150,
|
||||
minWidth: 150,
|
||||
paddingTop: '30',
|
||||
marginBottom: 40
|
||||
},
|
||||
media: {
|
||||
height: 0,
|
||||
paddingTop: '56.25%', // 16:9,
|
||||
marginTop: '30',
|
||||
minWidth: 300
|
||||
},
|
||||
typography: {
|
||||
maxWidth: 345,
|
||||
minWidth: 300,
|
||||
paddingTop: '30'
|
||||
},
|
||||
appBar: {
|
||||
position: 'relative'
|
||||
},
|
||||
flex: {
|
||||
flex: 1
|
||||
},
|
||||
grid: {
|
||||
paddingTop: '30',
|
||||
marginTop: 30
|
||||
},
|
||||
inputField: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
marginTop: 25
|
||||
},
|
||||
formTypography: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
marginTop: 40
|
||||
},
|
||||
button: {
|
||||
margin: theme.spacing.unit,
|
||||
marginTop: theme.spacing.unit * 3
|
||||
}
|
||||
})
|
||||
|
||||
class UserSearch extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
alias: '',
|
||||
firstName: '',
|
||||
middleName: '',
|
||||
lastName: ''
|
||||
}
|
||||
}
|
||||
|
||||
submitForm (e) {
|
||||
e.preventDefault()
|
||||
|
||||
this.props.getUsersBySearchFields.variables.alias = this.state.alias
|
||||
this.props.getUsersBySearchFields.variables.firstName = this.state.firstName
|
||||
this.props.getUsersBySearchFields.variables.middleName = this.state.middleName
|
||||
this.props.getUsersBySearchFields.variables.lastName = this.state.lastName
|
||||
|
||||
this.props.getUsersBySearchFields.refetch({})
|
||||
}
|
||||
|
||||
handleTextField (e) {
|
||||
/*
|
||||
We will do some basic input validations here. If something is wrong we will turn on the error sign on the textfield.
|
||||
*/
|
||||
|
||||
let value = e.target.value
|
||||
|
||||
switch (e.target.id) {
|
||||
case 'alias':
|
||||
if (value.length > 30) {
|
||||
this.setState({aliasError: true})
|
||||
} else {
|
||||
this.setState({aliasError: false, alias: value})
|
||||
}
|
||||
break
|
||||
case 'firstName':
|
||||
if (value.length > 30) {
|
||||
this.setState({firstNameError: true})
|
||||
} else {
|
||||
this.setState({firstNameError: false, firstName: value})
|
||||
}
|
||||
break
|
||||
case 'middleName':
|
||||
if (value.length > 30) {
|
||||
this.setState({middleNameError: true})
|
||||
} else {
|
||||
this.setState({middleNameError: false, middleName: value})
|
||||
}
|
||||
break
|
||||
case 'lastName':
|
||||
if (value.length > 50) {
|
||||
this.setState({lastNameError: true})
|
||||
} else {
|
||||
this.setState({lastNameError: false, lastName: value})
|
||||
}
|
||||
break
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
selectText () {
|
||||
if (this.props.selectButton === true) {
|
||||
return (<dummy>{this.props.selectText}</dummy>)
|
||||
} else {
|
||||
return (<div />)
|
||||
}
|
||||
}
|
||||
|
||||
selectButton (user) {
|
||||
if (this.props.selectButton === true) {
|
||||
return (
|
||||
<Button id={user.id} ref={user.id} color='primary' onClick={() => {
|
||||
this.props.onSelect(user) // This sends the user to the parent component
|
||||
}}>
|
||||
Select
|
||||
</Button>
|
||||
)
|
||||
} else {
|
||||
return (<div />)
|
||||
}
|
||||
}
|
||||
|
||||
displayUsers () {
|
||||
let data = this.props.getUsersBySearchFields
|
||||
const { classes } = this.props
|
||||
|
||||
if (data.loading) {
|
||||
return (<div> Loading Users... </div>)
|
||||
} else {
|
||||
if (data.usersSearch === undefined) {
|
||||
return (<div> No Users to Display </div>)
|
||||
} else {
|
||||
return data.usersSearch.map(user => {
|
||||
return (
|
||||
|
||||
<Grid key={user.id} item>
|
||||
<Card className={classes.card} onClick={(e) => {
|
||||
this.setState({ selected: user.id})
|
||||
}
|
||||
}>
|
||||
<CardActionArea>
|
||||
<CardMedia
|
||||
className={classes.media}
|
||||
image={PortraitImage}
|
||||
title='User Profile'
|
||||
|
||||
/>
|
||||
<CardContent>
|
||||
<Typography gutterBottom variant='headline' component='h2'>
|
||||
{user.alias}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<CardActions>
|
||||
<Grid container justify='center' spacing={8}>
|
||||
<Grid item>
|
||||
{this.selectButton(user)}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</CardActions>
|
||||
</Card>
|
||||
</Grid>
|
||||
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={this.submitForm.bind(this)}>
|
||||
|
||||
<Typography className={classes.formTypography} variant='body1' gutterBottom align='left'>
|
||||
Use any of these fileds to search for users. {this.selectText()}
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
error={this.state.aliasError}
|
||||
id='alias'
|
||||
type='text'
|
||||
value={this.state.alias}
|
||||
label='Alias'
|
||||
className={classes.inputField}
|
||||
onChange={this.handleTextField.bind(this)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
error={this.state.firstNameError}
|
||||
id='firstName'
|
||||
type='text'
|
||||
value={this.state.firstName}
|
||||
label='First Name'
|
||||
className={classes.inputField}
|
||||
onChange={this.handleTextField.bind(this)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
error={this.state.middleNameError}
|
||||
id='middleName'
|
||||
type='text'
|
||||
value={this.state.middleName}
|
||||
label='Middle Name'
|
||||
className={classes.inputField}
|
||||
onChange={this.handleTextField.bind(this)}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
error={this.state.lastNameError}
|
||||
id='lastName'
|
||||
type='text'
|
||||
value={this.state.lastName}
|
||||
label='Last Name'
|
||||
className={classes.inputField}
|
||||
onChange={this.handleTextField.bind(this)}
|
||||
/>
|
||||
|
||||
<Grid container justify='center' >
|
||||
<Grid item>
|
||||
<Button variant='contained' color='secondary' className={classes.button} onClick={this.submitForm.bind(this)}>Search</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</form>
|
||||
|
||||
<Grid container className={classes.grid} justify='center' spacing={24}>
|
||||
{this.displayUsers()}
|
||||
</Grid>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
graphql(getUsersBySearchFields, {name: 'getUsersBySearchFields'}),
|
||||
withStyles(styles)
|
||||
)(UserSearch) // This technique binds more than one query to a single component.
|
||||
@@ -4,14 +4,18 @@ import { withStyles } from '@material-ui/core/styles';
|
||||
import AppBar from '@material-ui/core/AppBar';
|
||||
import Tabs from '@material-ui/core/Tabs';
|
||||
import Tab from '@material-ui/core/Tab';
|
||||
import UpdateIcon from '@material-ui/icons/Create';
|
||||
import ImageIcon from '@material-ui/icons/Wallpaper';
|
||||
import ProfileSheetIcon from '@material-ui/icons/Create';
|
||||
import ProfileImagesIcon from '@material-ui/icons/Wallpaper';
|
||||
import YourReferrerIcon from '@material-ui/icons/AccessibilityNew';
|
||||
import DescendentsIcon from '@material-ui/icons/DeviceHub';
|
||||
import Typography from '@material-ui/core/Typography';
|
||||
|
||||
// Components
|
||||
|
||||
import UserUpdate from './UserUpdate';
|
||||
import UserImages from './UserImages';
|
||||
import ProfileSheet from './ProfileSheet';
|
||||
import ProfileImages from './ProfileImages';
|
||||
import YourReferrer from './YourReferrer';
|
||||
import YourDescendents from './YourDescendents';
|
||||
|
||||
function TabContainer(props) {
|
||||
return (
|
||||
@@ -59,12 +63,16 @@ class UserTabs extends React.Component {
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
>
|
||||
<Tab label="Your User Profile" icon={<UpdateIcon />} />
|
||||
<Tab label="Manage Images" icon={<ImageIcon />} />
|
||||
<Tab label="Profile Sheet" icon={<ProfileSheetIcon />} />
|
||||
<Tab label="Profile Images" icon={<ProfileImagesIcon />} />
|
||||
<Tab label="Your Referrer" icon={<YourReferrerIcon />} />
|
||||
<Tab label="Your Descendents" icon={<DescendentsIcon />} />
|
||||
</Tabs>
|
||||
</AppBar>
|
||||
{value === 0 && <TabContainer><UserUpdate/></TabContainer>}
|
||||
{value === 1 && <TabContainer><UserImages/></TabContainer>}
|
||||
{value === 0 && <TabContainer><ProfileSheet/></TabContainer>}
|
||||
{value === 1 && <TabContainer><ProfileImages/></TabContainer>}
|
||||
{value === 2 && <TabContainer><YourReferrer/></TabContainer>}
|
||||
{value === 3 && <TabContainer><YourDescendents/></TabContainer>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import React, { Component } from 'react'
|
||||
import {compose} from 'react-apollo'
|
||||
|
||||
// Materia UI
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import Grid from '@material-ui/core/Grid'
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
|
||||
// components
|
||||
import DescendentsTree from './DescendentsTree'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
width: '50%',
|
||||
flexGrow: 1,
|
||||
padding: 10,
|
||||
marginLeft: '25%',
|
||||
marginTop: '2%'
|
||||
},
|
||||
typography: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
marginTop: 40,
|
||||
marginBottom: 40
|
||||
},
|
||||
grid: {
|
||||
paddingTop: '30',
|
||||
marginTop: 30
|
||||
}
|
||||
})
|
||||
|
||||
class YourDescendents extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.defaultValuesSet = false
|
||||
|
||||
this.state = {
|
||||
id: ''
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
if (this.defaultValuesSet === false) {
|
||||
let userData = localStorage.getItem('loggedInUser')
|
||||
|
||||
if (userData === 'undefined') { return }
|
||||
|
||||
let user = JSON.parse(userData)
|
||||
this.defaultValuesSet = true
|
||||
|
||||
/* Now we are ready to set the initial state. */
|
||||
this.setState({
|
||||
id: user.id
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<Typography className={classes.typography} variant='headline' gutterBottom>
|
||||
Your Descendents
|
||||
</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.
|
||||
</Typography>
|
||||
|
||||
<DescendentsTree userId={this.state.id} />
|
||||
<Grid container className={classes.grid} justify='center' spacing={24} />
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withStyles(styles)
|
||||
)(YourDescendents) // This technique binds more than one query to a single component.
|
||||
@@ -0,0 +1,146 @@
|
||||
import React, { Component } from 'react'
|
||||
import {graphql, compose} from 'react-apollo'
|
||||
import {updateReferrerMutation} from '../queries/queries'
|
||||
|
||||
// components
|
||||
|
||||
import UserSearch from './UserSearch'
|
||||
import UserProfile from './UserProfile'
|
||||
|
||||
// Material-ui
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import Paper from '@material-ui/core/Paper'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
|
||||
const styles = theme => ({
|
||||
root: {
|
||||
width: '50%',
|
||||
flexGrow: 1,
|
||||
padding: 10,
|
||||
marginLeft: '25%',
|
||||
marginTop: '2%'
|
||||
},
|
||||
typography: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
marginTop: 40,
|
||||
marginBottom: 40
|
||||
},
|
||||
button: {
|
||||
margin: theme.spacing.unit,
|
||||
marginTop: theme.spacing.unit * 3
|
||||
},
|
||||
inputField: {
|
||||
width: '80%',
|
||||
marginLeft: '10%',
|
||||
marginTop: 25
|
||||
}
|
||||
})
|
||||
|
||||
class YourReferrer extends Component {
|
||||
|
||||
constructor (props) {
|
||||
super(props)
|
||||
|
||||
this.defaultValuesSet = false
|
||||
|
||||
this.state = {
|
||||
id: '',
|
||||
referrerId: '',
|
||||
firstNameError: false,
|
||||
middleNameError: false,
|
||||
lastNameError: false,
|
||||
aliasError: false
|
||||
}
|
||||
}
|
||||
|
||||
userSelected (referrerUser) {
|
||||
this.setState({referrerId: referrerUser.id})
|
||||
|
||||
this.props.updateReferrerMutation({
|
||||
variables: {
|
||||
id: this.state.id,
|
||||
referrerId: referrerUser.id
|
||||
}
|
||||
})
|
||||
|
||||
/* Before we are done, we need to update the state of the local storage. */
|
||||
|
||||
let user = JSON.parse(localStorage.getItem('loggedInUser'))
|
||||
|
||||
user.referrerId = referrerUser.id
|
||||
|
||||
localStorage.setItem('loggedInUser', JSON.stringify(user))
|
||||
}
|
||||
|
||||
renderMode () {
|
||||
const { classes } = this.props
|
||||
|
||||
if (this.state.referrerId !== null) {
|
||||
return (
|
||||
<div>
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
This is the user who introduced you to the project.
|
||||
</Typography>
|
||||
|
||||
<UserProfile userId={this.state.referrerId} />
|
||||
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
This information can not be changed.
|
||||
</Typography>
|
||||
</div>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<Typography className={classes.typography} variant='body1' gutterBottom align='left'>
|
||||
We know that a group often becomes more intelligent when the number of people in the group increases.
|
||||
To incentivize members of the community to invite more people to join we have developed this referral
|
||||
program in which you can specify who referred you to the project, and see the members who pointed at
|
||||
you as their referrer. Later this information will be available at your user profile and add to your
|
||||
overall reputation within the project.
|
||||
</Typography>
|
||||
|
||||
<UserSearch selectButton onSelect={this.userSelected.bind(this)} selectText="After that press the 'Select' button on the desired user to set it as your referrer. Bear in mind that this action can not be undone." />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
componentWillMount () {
|
||||
if (this.defaultValuesSet === false) {
|
||||
let userData = localStorage.getItem('loggedInUser')
|
||||
|
||||
if (userData === 'undefined') { return }
|
||||
|
||||
let user = JSON.parse(userData)
|
||||
this.defaultValuesSet = true
|
||||
|
||||
/* Now we are ready to set the initial state. */
|
||||
|
||||
this.setState({
|
||||
id: user.id,
|
||||
referrerId: user.referrerId
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { classes } = this.props
|
||||
return (
|
||||
<Paper className={classes.root}>
|
||||
<Typography className={classes.typography} variant='headline' gutterBottom>
|
||||
Your Referrer
|
||||
</Typography>
|
||||
|
||||
{this.renderMode()}
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default compose(
|
||||
graphql(updateReferrerMutation, {name: 'updateReferrerMutation'}),
|
||||
withStyles(styles)
|
||||
)(YourReferrer) // This technique binds more than one query to a single component.
|
||||
@@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
XYPlot,
|
||||
XAxis,
|
||||
YAxis,
|
||||
HorizontalGridLines,
|
||||
VerticalGridLines,
|
||||
LineSeries
|
||||
} from 'index';
|
||||
|
||||
const data = [];
|
||||
|
||||
for (let i = 0; i < 20; i++) {
|
||||
const series = [];
|
||||
for (let j = 0; j < 100; j++) {
|
||||
series.push({x: j, y: (i / 10 + 1) * Math.sin(Math.PI * (i + j) / 50)});
|
||||
}
|
||||
data.push({color: i, key: i, data: series, opacity: 0.8});
|
||||
}
|
||||
|
||||
export default class Example extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<XYPlot
|
||||
width={300}
|
||||
height={300}
|
||||
colorType="linear"
|
||||
colorDomain={[0, 9]}
|
||||
colorRange={['yellow', 'orange']}
|
||||
>
|
||||
<HorizontalGridLines />
|
||||
<VerticalGridLines />
|
||||
<XAxis />
|
||||
<YAxis />
|
||||
{data.map(props => <LineSeries {...props}/>)}
|
||||
</XYPlot>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import NavBar from './navbar'
|
||||
import Footer from './footer'
|
||||
import Callback from './callback'
|
||||
import Home from './home'
|
||||
import About from './about'
|
||||
import Dashboard from './dashboard'
|
||||
import Teams from './teams'
|
||||
|
||||
import { theme, globalStyles } from './theme'
|
||||
|
||||
export {
|
||||
NavBar,
|
||||
Footer,
|
||||
Callback,
|
||||
Home,
|
||||
About,
|
||||
Dashboard,
|
||||
Teams,
|
||||
theme,
|
||||
globalStyles
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import NavBar from './navbar'
|
||||
import Footer from './footer'
|
||||
import Callback from './callback'
|
||||
import Home from './home'
|
||||
import About from './about'
|
||||
import Dashboard from './dashboard'
|
||||
import Teams from './teams'
|
||||
|
||||
import { theme, globalStyles } from './theme'
|
||||
|
||||
export {
|
||||
NavBar,
|
||||
Footer,
|
||||
Callback,
|
||||
Home,
|
||||
About,
|
||||
Dashboard,
|
||||
Teams,
|
||||
theme,
|
||||
globalStyles
|
||||
}
|
||||
@@ -16,7 +16,6 @@ const getUsersQuery = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const getUserProfileQuery = gql`
|
||||
query($id: ID){
|
||||
user (id: $id){
|
||||
@@ -26,8 +25,7 @@ query($id: ID){
|
||||
middleName
|
||||
lastName
|
||||
bio
|
||||
email
|
||||
emailVerified
|
||||
avatarHandle
|
||||
role {
|
||||
id
|
||||
name
|
||||
@@ -35,11 +33,11 @@ query($id: ID){
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const getUserByAuthIdQuery = gql`
|
||||
query($authId: String){
|
||||
userByAuthId (authId: $authId){
|
||||
id
|
||||
referrerId
|
||||
alias
|
||||
firstName
|
||||
middleName
|
||||
@@ -50,13 +48,60 @@ query($authId: String){
|
||||
isDeveloper
|
||||
isDataAnalyst
|
||||
isTrader
|
||||
avatarHandle
|
||||
avatarChangeDate
|
||||
role {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const getUsersBySearchFields = gql`
|
||||
query(
|
||||
$alias: String,
|
||||
$firstName: String,
|
||||
$middleName: String,
|
||||
$lastName: String
|
||||
){
|
||||
usersSearch (alias: $alias, firstName: $firstName, middleName: $middleName, lastName: $lastName){
|
||||
id
|
||||
alias
|
||||
firstName
|
||||
middleName
|
||||
lastName
|
||||
}
|
||||
}
|
||||
`
|
||||
const getDescendentsQuery = gql`
|
||||
query(
|
||||
$id: String
|
||||
){
|
||||
descendents (id: $id){
|
||||
id
|
||||
alias
|
||||
firstName
|
||||
middleName
|
||||
lastName
|
||||
referrerId
|
||||
descendents {
|
||||
id
|
||||
alias
|
||||
firstName
|
||||
middleName
|
||||
lastName
|
||||
referrerId
|
||||
descendents {
|
||||
id
|
||||
alias
|
||||
firstName
|
||||
middleName
|
||||
lastName
|
||||
referrerId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const getRolesQuery = gql`
|
||||
{
|
||||
roles {
|
||||
@@ -65,7 +110,6 @@ const getRolesQuery = gql`
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
const updateUserMutation = gql`
|
||||
mutation(
|
||||
$id: ID!,
|
||||
@@ -96,4 +140,48 @@ mutation(
|
||||
}
|
||||
}
|
||||
`
|
||||
export {getUsersQuery, getUserByAuthIdQuery, getRolesQuery, updateUserMutation, getUserProfileQuery}
|
||||
const updateReferrerMutation = gql`
|
||||
mutation(
|
||||
$id: ID!,
|
||||
$referrerId:String!
|
||||
)
|
||||
{
|
||||
updateUserReferrer (
|
||||
id: $id,
|
||||
referrerId:$referrerId
|
||||
)
|
||||
{
|
||||
id
|
||||
referrerId
|
||||
}
|
||||
}
|
||||
`
|
||||
const updateUserImagesMutation = gql`
|
||||
mutation(
|
||||
$id: ID!,
|
||||
$avatarHandle:String,
|
||||
$avatarChangeDate:String
|
||||
)
|
||||
{
|
||||
updateUserImages (
|
||||
id: $id,
|
||||
avatarHandle:$avatarHandle,
|
||||
avatarChangeDate: $avatarChangeDate
|
||||
)
|
||||
{
|
||||
id
|
||||
alias
|
||||
}
|
||||
}
|
||||
`
|
||||
export {
|
||||
getUsersQuery,
|
||||
getUserByAuthIdQuery,
|
||||
getRolesQuery,
|
||||
getUserProfileQuery,
|
||||
getUsersBySearchFields,
|
||||
getDescendentsQuery,
|
||||
updateUserMutation,
|
||||
updateReferrerMutation,
|
||||
updateUserImagesMutation
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { createMuiTheme } from '@material-ui/core/styles'
|
||||
|
||||
export const theme = createMuiTheme({
|
||||
palette: {
|
||||
primary: { main: '#303036' }, // DARK.
|
||||
secondary: { main: '#CC5835' } // RUSTED_RED.
|
||||
},
|
||||
typography: {
|
||||
fontFamily: '"Saira","Saira Condenses", sans-serif'
|
||||
},
|
||||
overrides: {
|
||||
Paper: {
|
||||
root: {
|
||||
backgroundColor: '#ffffff'
|
||||
},
|
||||
elevation: 0,
|
||||
square: true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const globalStyles = theme => ({
|
||||
'@global': {
|
||||
body: {
|
||||
backgroundColor: theme.palette.common.white
|
||||
}
|
||||
},
|
||||
appBar: {
|
||||
position: 'relative'
|
||||
},
|
||||
toolbarTitle: {
|
||||
flex: 1
|
||||
},
|
||||
layout: {
|
||||
width: 'auto',
|
||||
marginLeft: theme.spacing.unit * 3,
|
||||
marginRight: theme.spacing.unit * 3,
|
||||
[theme.breakpoints.up(900 + theme.spacing.unit * 3 * 2)]: {
|
||||
width: 900,
|
||||
marginLeft: 'auto',
|
||||
marginRight: 'auto'
|
||||
}
|
||||
},
|
||||
heroContent: {
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
padding: `${theme.spacing.unit * 8}px 0 ${theme.spacing.unit * 6}px`
|
||||
},
|
||||
cardHeader: {
|
||||
backgroundColor: theme.palette.grey[200]
|
||||
},
|
||||
cardPricing: {
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'baseline',
|
||||
marginBottom: theme.spacing.unit * 2
|
||||
},
|
||||
cardActions: {
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
paddingBottom: theme.spacing.unit * 2
|
||||
}
|
||||
},
|
||||
footer: {
|
||||
marginTop: theme.spacing.unit * 8,
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
padding: `${theme.spacing.unit * 6}px 0`
|
||||
}
|
||||
})
|
||||
|
||||
export default theme
|
||||
@@ -0,0 +1,129 @@
|
||||
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'>
|
||||
Users
|
||||
</Typography>
|
||||
</NavLink>
|
||||
<NavLink to='/about' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
About
|
||||
</Typography>
|
||||
</NavLink>
|
||||
</Grid>
|
||||
<Grid item xs>
|
||||
<a
|
||||
href='https://t.me/advancedalgoscommunity'
|
||||
target='_blank>'
|
||||
className={classNames(classes.footerLink)}
|
||||
>
|
||||
<Typography variant='title' color='textPrimary' gutterBottom>
|
||||
Support
|
||||
</Typography>
|
||||
</a>
|
||||
<a
|
||||
href='https://advancedalgos.net/documentation-quick-start.shtml'
|
||||
target='_blank>'
|
||||
className={classNames(classes.footerLink)}
|
||||
>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Documentation
|
||||
</Typography>
|
||||
</a>
|
||||
<NavLink to='/' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Telegram
|
||||
</Typography>
|
||||
</NavLink>
|
||||
</Grid>
|
||||
<Grid item xs>
|
||||
<Typography variant='title' color='textPrimary' gutterBottom>
|
||||
Modules
|
||||
</Typography>
|
||||
<NavLink to='/' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Teams
|
||||
</Typography>
|
||||
</NavLink>
|
||||
<NavLink to='/' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
Users
|
||||
</Typography>
|
||||
</NavLink>
|
||||
<NavLink to='/' className={classNames(classes.footerLink)}>
|
||||
<Typography variant='subheading' color='textSecondary'>
|
||||
KeyVault
|
||||
</Typography>
|
||||
</NavLink>
|
||||
</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)
|
||||
@@ -0,0 +1,21 @@
|
||||
// import NavBar from './navbar'
|
||||
import Footer from './footer'
|
||||
// import Callback from './callback'
|
||||
// import Home from './home'
|
||||
// import About from './about'
|
||||
// import Dashboard from './dashboard'
|
||||
// import Teams from './teams'
|
||||
|
||||
// import { theme, globalStyles } from './theme'
|
||||
|
||||
export {
|
||||
// NavBar,
|
||||
Footer
|
||||
// Callback,
|
||||
// Home,
|
||||
// About,
|
||||
// Dashboard,
|
||||
// Teams,
|
||||
// theme,
|
||||
// globalStyles
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
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
|
||||
@@ -0,0 +1,77 @@
|
||||
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
|
||||
@@ -0,0 +1,15 @@
|
||||
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
|
||||
@@ -0,0 +1,141 @@
|
||||
import React, { Component } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { withStyles } from '@material-ui/core/styles'
|
||||
import AppBar from '@material-ui/core/AppBar'
|
||||
import Toolbar from '@material-ui/core/Toolbar'
|
||||
import Typography from '@material-ui/core/Typography'
|
||||
import IconButton from '@material-ui/core/IconButton'
|
||||
import Button from '@material-ui/core/Button'
|
||||
|
||||
// icons
|
||||
import HomeIcon from '@material-ui/icons/Home'
|
||||
import DashboardIcon from '@material-ui/icons/Dashboard'
|
||||
|
||||
import { Link } from 'react-router-dom'
|
||||
|
||||
import { getItem } from '../../utils/local-storage'
|
||||
|
||||
// components
|
||||
import { LoggedIn } from './LoggedIn'
|
||||
import { LoggedOut } from './LoggedOut'
|
||||
|
||||
import AALogo from '../../assets/logos/advanced-algos/aa-logo-dark.svg'
|
||||
|
||||
const AboutLink = props => <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)
|
||||
@@ -6,6 +6,8 @@ const Schema = mongoose.Schema
|
||||
|
||||
const userSchema = new Schema({
|
||||
authId: String,
|
||||
referrerId: String,
|
||||
creationDate: String,
|
||||
alias: String,
|
||||
firstName: String,
|
||||
middleName: String,
|
||||
@@ -16,7 +18,9 @@ const userSchema = new Schema({
|
||||
isDeveloper: Boolean,
|
||||
isTrader: Boolean,
|
||||
isDataAnalyst: Boolean,
|
||||
roleId: String
|
||||
roleId: String,
|
||||
avatarHandle: String,
|
||||
avatarChangeDate: String
|
||||
})
|
||||
|
||||
global.UserSchema = global.UserSchema || mongoose.model('User', userSchema)
|
||||
|
||||
@@ -30,6 +30,7 @@ const UserType = new GraphQLObjectType({
|
||||
fields: () => ({
|
||||
id: { type: GraphQLID},
|
||||
authId: { type: GraphQLString},
|
||||
referrerId: {type: GraphQLString},
|
||||
alias: {type: GraphQLString},
|
||||
firstName: {type: GraphQLString},
|
||||
middleName: {type: GraphQLString},
|
||||
@@ -40,6 +41,8 @@ const UserType = new GraphQLObjectType({
|
||||
isDeveloper: {type: GraphQLInt},
|
||||
isTrader: {type: GraphQLInt},
|
||||
isDataAnalyst: {type: GraphQLInt},
|
||||
avatarHandle: {type: GraphQLString},
|
||||
avatarChangeDate: {type: GraphQLString},
|
||||
role: {
|
||||
type: RoleType,
|
||||
resolve (parent, args) {
|
||||
@@ -49,6 +52,24 @@ const UserType = new GraphQLObjectType({
|
||||
})
|
||||
})
|
||||
|
||||
const DescendentType = new GraphQLObjectType({
|
||||
name: 'Descendent',
|
||||
fields: () => ({
|
||||
id: { type: GraphQLID},
|
||||
referrerId: {type: GraphQLString},
|
||||
alias: {type: GraphQLString},
|
||||
firstName: {type: GraphQLString},
|
||||
middleName: {type: GraphQLString},
|
||||
lastName: {type: GraphQLString},
|
||||
descendents: {
|
||||
type: new GraphQLList(DescendentType),
|
||||
resolve (parent, args) {
|
||||
return User.find({referrerId: parent.id})
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const RoleType = new GraphQLObjectType({
|
||||
name: 'Role',
|
||||
fields: () => ({
|
||||
@@ -127,6 +148,33 @@ const RootQuery = new GraphQLObjectType({
|
||||
resolve (parent, args) {
|
||||
return roles
|
||||
}
|
||||
},
|
||||
usersSearch: {
|
||||
type: new GraphQLList(UserType),
|
||||
args: {alias: {type: GraphQLString}, firstName: {type: GraphQLString}, middleName: {type: GraphQLString}, lastName: {type: GraphQLString}},
|
||||
resolve (parent, args) {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> usersSearch -> resolve -> Entering function.') }
|
||||
|
||||
let mongoQuery = { $or: [] }
|
||||
|
||||
if (args.alias !== null && args.alias !== '') { mongoQuery.$or.push({alias: args.alias}) }
|
||||
if (args.firstName !== null && args.firstName !== '') { mongoQuery.$or.push({firstName: args.firstName}) }
|
||||
if (args.middleName !== null && args.middleName !== '') { mongoQuery.$or.push({middleName: args.middleName}) }
|
||||
if (args.lastName !== null && args.lastName !== '') { mongoQuery.$or.push({lastName: args.lastName}) }
|
||||
|
||||
if (mongoQuery.$or.length === 0) { mongoQuery = {} }
|
||||
|
||||
return User.find(mongoQuery)
|
||||
}
|
||||
},
|
||||
descendents: {
|
||||
type: new GraphQLList(DescendentType),
|
||||
args: {id: {type: GraphQLString}},
|
||||
resolve (parent, args) {
|
||||
if (INFO_LOG === true) { console.log('[INFO] ' + MODULE_NAME + ' -> RootQuery -> descendents -> resolve -> Entering function.') }
|
||||
|
||||
return User.find({referrerId: args.id})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -195,6 +243,45 @@ const Mutation = new GraphQLObjectType({
|
||||
roleId: args.roleId
|
||||
}
|
||||
|
||||
return User.update(key, updatedUser)
|
||||
}
|
||||
},
|
||||
updateUserImages: {
|
||||
type: UserType,
|
||||
args: {
|
||||
id: {type: new GraphQLNonNull(GraphQLID)},
|
||||
avatarHandle: {type: GraphQLString},
|
||||
avatarChangeDate: {type: GraphQLString}
|
||||
},
|
||||
resolve (parent, args) {
|
||||
let key = {
|
||||
_id: args.id
|
||||
}
|
||||
|
||||
let updatedUser = {
|
||||
avatarHandle: args.avatarHandle,
|
||||
avatarChangeDate: args.avatarChangeDate
|
||||
}
|
||||
|
||||
return User.update(key, updatedUser)
|
||||
}
|
||||
},
|
||||
updateUserReferrer: {
|
||||
type: UserType,
|
||||
args: {
|
||||
id: {type: new GraphQLNonNull(GraphQLID)},
|
||||
referrerId: {type: GraphQLString}
|
||||
},
|
||||
resolve (parent, args) {
|
||||
let key = {
|
||||
_id: args.id,
|
||||
referrerId: null
|
||||
}
|
||||
|
||||
let updatedUser = {
|
||||
referrerId: args.referrerId
|
||||
}
|
||||
|
||||
return User.update(key, updatedUser)
|
||||
}
|
||||
}
|
||||
@@ -345,9 +432,21 @@ function authenticate (encodedToken, callBackFunction) {
|
||||
return
|
||||
}
|
||||
|
||||
let localDate = new Date()
|
||||
let creationDate = new Date(Date.UTC(
|
||||
localDate.getUTCFullYear(),
|
||||
localDate.getUTCMonth(),
|
||||
localDate.getUTCDate(),
|
||||
localDate.getUTCHours(),
|
||||
localDate.getUTCMinutes(),
|
||||
localDate.getUTCSeconds(),
|
||||
localDate.getUTCMilliseconds())
|
||||
)
|
||||
|
||||
let newUser = new User({
|
||||
alias: alias,
|
||||
authId: authId,
|
||||
creationDate: creationDate.toISOString(),
|
||||
email: email,
|
||||
emailVerified: emailVerified,
|
||||
roleId: '1'
|
||||
|
||||