diff --git a/client/modules/User/components/APIKeyForm.jsx b/client/modules/User/components/APIKeyForm.jsx new file mode 100644 index 00000000..feb81135 --- /dev/null +++ b/client/modules/User/components/APIKeyForm.jsx @@ -0,0 +1,71 @@ +import PropTypes from 'prop-types'; +import React from 'react'; + +class APIKeyForm extends React.Component { + constructor(props) { + super(props); + this.state = { keyLabel: '' }; + + this.addKey = this.addKey.bind(this); + } + + addKey(event) { + // TODO + console.log('addKey'); + this.props.updateSettings(); + event.preventDefault(); + return false; + } + + removeKey(k) { + // TODO + console.log(k); + } + + render() { + return ( +
+

Key label

+
+ { this.setState({ keyLabel: event.target.value }); }} + />
+ +
+ + + {[{ + id: 1, + label: 'MyFirstAPI', + createdAt: new Date(), + lastUsedAt: new Date() + }, { + id: 2, + label: 'MyOtherAPI', + createdAt: new Date(), + lastUsedAt: new Date() + }].map(v => ( + + + + + ))} + +
{v.label}
Created on: {v.createdAt.toLocaleDateString()} {v.createdAt.toLocaleTimeString()}
Last used on:
{v.lastUsedAt.toLocaleDateString()} {v.lastUsedAt.toLocaleTimeString()}
+
+ ); + } +} + +APIKeyForm.propTypes = { + updateSettings: PropTypes.func.isRequired, +}; + +export default APIKeyForm; diff --git a/client/modules/User/pages/AccountView.jsx b/client/modules/User/pages/AccountView.jsx index 2fb03b1f..d1bd6ba9 100644 --- a/client/modules/User/pages/AccountView.jsx +++ b/client/modules/User/pages/AccountView.jsx @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import { reduxForm } from 'redux-form'; import { bindActionCreators } from 'redux'; -import { browserHistory } from 'react-router'; +import { Link, browserHistory } from 'react-router'; import InlineSVG from 'react-inlinesvg'; import axios from 'axios'; import { Helmet } from 'react-helmet'; @@ -22,8 +22,12 @@ class AccountView extends React.Component { this.gotoHomePage = this.gotoHomePage.bind(this); } + componentDidMount() { + document.body.className = this.props.theme; + } + closeAccountPage() { - browserHistory.push(this.props.previousPath); + browserHistory.goBack(); } gotoHomePage() { @@ -47,6 +51,7 @@ class AccountView extends React.Component {

My Account

+ Advanced Settings

Or

@@ -59,7 +64,7 @@ function mapStateToProps(state) { return { initialValues: state.user, // <- initialValues for reduxForm user: state.user, - previousPath: state.ide.previousPath + theme: state.preferences.theme }; } @@ -86,7 +91,7 @@ function asyncValidate(formProps, dispatch, props) { } AccountView.propTypes = { - previousPath: PropTypes.string.isRequired, + theme: PropTypes.string.isRequired }; export default reduxForm({ diff --git a/client/modules/User/pages/AdvancedSettingsView.jsx b/client/modules/User/pages/AdvancedSettingsView.jsx new file mode 100644 index 00000000..e963e5a4 --- /dev/null +++ b/client/modules/User/pages/AdvancedSettingsView.jsx @@ -0,0 +1,104 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { reduxForm } from 'redux-form'; +import { bindActionCreators } from 'redux'; +import { browserHistory } from 'react-router'; +import InlineSVG from 'react-inlinesvg'; +import axios from 'axios'; +import { Helmet } from 'react-helmet'; +import { updateSettings, initiateVerification } from '../actions'; +import { validateSettings } from '../../../utils/reduxFormUtils'; +import APIKeyForm from '../components/APIKeyForm'; + +const exitUrl = require('../../../images/exit.svg'); +const logoUrl = require('../../../images/p5js-logo.svg'); + +// TODO tmp +const ident = () => {}; + +class AccountView extends React.Component { + constructor(props) { + super(props); + this.closeAccountPage = this.closeAccountPage.bind(this); + this.gotoHomePage = this.gotoHomePage.bind(this); + } + + componentDidMount() { + document.body.className = this.props.theme; + } + + closeAccountPage() { + browserHistory.goBack(); + } + + gotoHomePage() { + browserHistory.push('/'); + } + + render() { + return ( +
+ + p5.js Web Editor | Advanced Settings + +
+ + +
+
+

Advanced Settings

+ +
+
+ ); + } +} + +function mapStateToProps(state) { + return { + initialValues: state.user, // <- initialValues for reduxForm + user: state.user, + previousPath: state.ide.previousPath, + theme: state.preferences.theme + }; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators({ updateSettings, initiateVerification }, dispatch); +} + +function asyncValidate(formProps, dispatch, props) { + const fieldToValidate = props.form._active; + if (fieldToValidate) { + const queryParams = {}; + queryParams[fieldToValidate] = formProps[fieldToValidate]; + queryParams.check_type = fieldToValidate; + return axios.get('/api/signup/duplicate_check', { params: queryParams }) + .then((response) => { + if (response.data.exists) { + const error = {}; + error[fieldToValidate] = response.data.message; + throw error; + } + }); + } + return Promise.resolve(true).then(() => {}); +} + +AccountView.propTypes = { + theme: PropTypes.string.isRequired +}; + +export default reduxForm({ + form: 'updateAllSettings', + fields: ['username', 'email', 'currentPassword', 'newPassword'], + validate: validateSettings, + asyncValidate, + asyncBlurFields: ['username', 'email', 'currentPassword'] +}, mapStateToProps, mapDispatchToProps)(AccountView); diff --git a/client/routes.jsx b/client/routes.jsx index baa89884..9389d226 100644 --- a/client/routes.jsx +++ b/client/routes.jsx @@ -9,6 +9,7 @@ import ResetPasswordView from './modules/User/pages/ResetPasswordView'; import EmailVerificationView from './modules/User/pages/EmailVerificationView'; import NewPasswordView from './modules/User/pages/NewPasswordView'; import AccountView from './modules/User/pages/AccountView'; +import AdvancedSettingsView from './modules/User/pages/AdvancedSettingsView'; // import SketchListView from './modules/Sketch/pages/SketchListView'; import { getUser } from './modules/User/actions'; import { stopSketch } from './modules/IDE/actions/ide'; @@ -38,6 +39,7 @@ const routes = store => ( + diff --git a/client/styles/components/_form-container.scss b/client/styles/components/_form-container.scss index 225dc155..4b371547 100644 --- a/client/styles/components/_form-container.scss +++ b/client/styles/components/_form-container.scss @@ -37,3 +37,17 @@ .form-container__exit-button { @extend %none-themify-icon-with-hover; } + +.form__table { + display: block; + max-width: 900px; + border-collapse: collapse; + & td { + max-width: 300px; + border: 1px solid #ddd; + padding: 0 10px 0 10px; + } + & tr:nth-child(even) { + background-color: #f2f2f2; + } +} diff --git a/client/styles/components/_forms.scss b/client/styles/components/_forms.scss index 342a8c80..8fea3f3d 100644 --- a/client/styles/components/_forms.scss +++ b/client/styles/components/_forms.scss @@ -51,3 +51,20 @@ .form input[type="submit"]:disabled { cursor: not-allowed; } + +.form__button-remove { + @extend %forms-button; + margin: 1rem 0 1rem 0; + @include themify() { + color: getThemifyVariable('console-error-background-color'); + border-color: getThemifyVariable('console-error-background-color'); + &:enabled:hover { + border-color: getThemifyVariable('console-error-background-color'); + background-color: getThemifyVariable('console-error-background-color'); + } + &:enabled:active { + border-color: getThemifyVariable('console-error-background-color'); + background-color: getThemifyVariable('console-error-background-color'); + } + } +} diff --git a/server/routes/server.routes.js b/server/routes/server.routes.js index adade10d..2f39e924 100644 --- a/server/routes/server.routes.js +++ b/server/routes/server.routes.js @@ -87,6 +87,14 @@ router.get('/account', (req, res) => { } }); +router.get('/account/advanced', (req, res) => { + if (req.user) { + res.send(renderIndex()); + } else { + res.redirect('/login'); + } +}); + router.get('/about', (req, res) => { res.send(renderIndex()); });