Added design of the API key page
This commit is contained in:
parent
18239eb2e0
commit
3d2a862d9d
7 changed files with 225 additions and 4 deletions
71
client/modules/User/components/APIKeyForm.jsx
Normal file
71
client/modules/User/components/APIKeyForm.jsx
Normal file
|
@ -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 (
|
||||||
|
<div>
|
||||||
|
<h2 className="form__label">Key label</h2>
|
||||||
|
<form className="form" onSubmit={this.addKey}>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
className="form__input"
|
||||||
|
id="keyLabel"
|
||||||
|
onChange={(event) => { this.setState({ keyLabel: event.target.value }); }}
|
||||||
|
/><br />
|
||||||
|
<input
|
||||||
|
type="submit"
|
||||||
|
value="Create new Key"
|
||||||
|
disabled={this.state.keyLabel === ''}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
<table className="form__table">
|
||||||
|
<tbody>
|
||||||
|
{[{
|
||||||
|
id: 1,
|
||||||
|
label: 'MyFirstAPI',
|
||||||
|
createdAt: new Date(),
|
||||||
|
lastUsedAt: new Date()
|
||||||
|
}, {
|
||||||
|
id: 2,
|
||||||
|
label: 'MyOtherAPI',
|
||||||
|
createdAt: new Date(),
|
||||||
|
lastUsedAt: new Date()
|
||||||
|
}].map(v => (
|
||||||
|
<tr key={v.id}>
|
||||||
|
<td><b>{v.label}</b><br />Created on: {v.createdAt.toLocaleDateString()} {v.createdAt.toLocaleTimeString()}</td>
|
||||||
|
<td>Last used on:<br /> {v.lastUsedAt.toLocaleDateString()} {v.lastUsedAt.toLocaleTimeString()}</td>
|
||||||
|
<td><button className="form__button-remove" onClick={() => this.removeKey(v)}>Delete</button></td>
|
||||||
|
</tr>))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
APIKeyForm.propTypes = {
|
||||||
|
updateSettings: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default APIKeyForm;
|
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { reduxForm } from 'redux-form';
|
import { reduxForm } from 'redux-form';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import { browserHistory } from 'react-router';
|
import { Link, browserHistory } from 'react-router';
|
||||||
import InlineSVG from 'react-inlinesvg';
|
import InlineSVG from 'react-inlinesvg';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
|
@ -22,8 +22,12 @@ class AccountView extends React.Component {
|
||||||
this.gotoHomePage = this.gotoHomePage.bind(this);
|
this.gotoHomePage = this.gotoHomePage.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
document.body.className = this.props.theme;
|
||||||
|
}
|
||||||
|
|
||||||
closeAccountPage() {
|
closeAccountPage() {
|
||||||
browserHistory.push(this.props.previousPath);
|
browserHistory.goBack();
|
||||||
}
|
}
|
||||||
|
|
||||||
gotoHomePage() {
|
gotoHomePage() {
|
||||||
|
@ -47,6 +51,7 @@ class AccountView extends React.Component {
|
||||||
<div className="form-container__content">
|
<div className="form-container__content">
|
||||||
<h2 className="form-container__title">My Account</h2>
|
<h2 className="form-container__title">My Account</h2>
|
||||||
<AccountForm {...this.props} />
|
<AccountForm {...this.props} />
|
||||||
|
<Link to="/account/advanced">Advanced Settings</Link>
|
||||||
<h2 className="form-container__divider">Or</h2>
|
<h2 className="form-container__divider">Or</h2>
|
||||||
<GithubButton buttonText="Login with Github" />
|
<GithubButton buttonText="Login with Github" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -59,7 +64,7 @@ function mapStateToProps(state) {
|
||||||
return {
|
return {
|
||||||
initialValues: state.user, // <- initialValues for reduxForm
|
initialValues: state.user, // <- initialValues for reduxForm
|
||||||
user: state.user,
|
user: state.user,
|
||||||
previousPath: state.ide.previousPath
|
theme: state.preferences.theme
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +91,7 @@ function asyncValidate(formProps, dispatch, props) {
|
||||||
}
|
}
|
||||||
|
|
||||||
AccountView.propTypes = {
|
AccountView.propTypes = {
|
||||||
previousPath: PropTypes.string.isRequired,
|
theme: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
export default reduxForm({
|
export default reduxForm({
|
||||||
|
|
104
client/modules/User/pages/AdvancedSettingsView.jsx
Normal file
104
client/modules/User/pages/AdvancedSettingsView.jsx
Normal file
|
@ -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 (
|
||||||
|
<div className="form-container">
|
||||||
|
<Helmet>
|
||||||
|
<title>p5.js Web Editor | Advanced Settings</title>
|
||||||
|
</Helmet>
|
||||||
|
<div className="form-container__header">
|
||||||
|
<button className="form-container__logo-button" onClick={this.gotoHomePage}>
|
||||||
|
<InlineSVG src={logoUrl} alt="p5js Logo" />
|
||||||
|
</button>
|
||||||
|
<button className="form-container__exit-button" onClick={this.closeAccountPage}>
|
||||||
|
<InlineSVG src={exitUrl} alt="Close Account Page" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="form-container__content">
|
||||||
|
<h2 className="form-container__title">Advanced Settings</h2>
|
||||||
|
<APIKeyForm
|
||||||
|
updateSettings={ident}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
|
@ -9,6 +9,7 @@ import ResetPasswordView from './modules/User/pages/ResetPasswordView';
|
||||||
import EmailVerificationView from './modules/User/pages/EmailVerificationView';
|
import EmailVerificationView from './modules/User/pages/EmailVerificationView';
|
||||||
import NewPasswordView from './modules/User/pages/NewPasswordView';
|
import NewPasswordView from './modules/User/pages/NewPasswordView';
|
||||||
import AccountView from './modules/User/pages/AccountView';
|
import AccountView from './modules/User/pages/AccountView';
|
||||||
|
import AdvancedSettingsView from './modules/User/pages/AdvancedSettingsView';
|
||||||
// import SketchListView from './modules/Sketch/pages/SketchListView';
|
// import SketchListView from './modules/Sketch/pages/SketchListView';
|
||||||
import { getUser } from './modules/User/actions';
|
import { getUser } from './modules/User/actions';
|
||||||
import { stopSketch } from './modules/IDE/actions/ide';
|
import { stopSketch } from './modules/IDE/actions/ide';
|
||||||
|
@ -38,6 +39,7 @@ const routes = store => (
|
||||||
<Route path="/sketches" component={IDEView} />
|
<Route path="/sketches" component={IDEView} />
|
||||||
<Route path="/assets" component={IDEView} />
|
<Route path="/assets" component={IDEView} />
|
||||||
<Route path="/account" component={AccountView} />
|
<Route path="/account" component={AccountView} />
|
||||||
|
<Route path="/account/advanced" component={AdvancedSettingsView} />
|
||||||
<Route path="/:username/sketches/:project_id" component={IDEView} />
|
<Route path="/:username/sketches/:project_id" component={IDEView} />
|
||||||
<Route path="/:username/sketches" component={IDEView} />
|
<Route path="/:username/sketches" component={IDEView} />
|
||||||
<Route path="/about" component={IDEView} />
|
<Route path="/about" component={IDEView} />
|
||||||
|
|
|
@ -37,3 +37,17 @@
|
||||||
.form-container__exit-button {
|
.form-container__exit-button {
|
||||||
@extend %none-themify-icon-with-hover;
|
@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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -51,3 +51,20 @@
|
||||||
.form input[type="submit"]:disabled {
|
.form input[type="submit"]:disabled {
|
||||||
cursor: not-allowed;
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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) => {
|
router.get('/about', (req, res) => {
|
||||||
res.send(renderIndex());
|
res.send(renderIndex());
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue