Skip to content
This repository was archived by the owner on Jul 15, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6ba5142
added source-maps and update yarn.lock
Jul 11, 2017
8dd0803
fixed conflicts
Jul 11, 2017
2ec863b
added user dashboard and forms
Jul 11, 2017
66864a5
interim commit: user management UI
Jul 20, 2017
a13b39c
Create users with a password and allow auth for them
dsnipe Aug 1, 2017
ef2f449
Properly authorize user controller actions and handle an empty passwo…
dsnipe Aug 2, 2017
aa37fb0
Merge branch 'master' into cms-frontend
Aug 9, 2017
a966934
fixed conflicts
Aug 9, 2017
5099114
added signin form
Aug 11, 2017
31f5d67
restructured and added features
Jul 11, 2017
3edffbd
added break line
Jul 11, 2017
8d66a16
improved and introduces platform specific installation steps
Jul 11, 2017
450ce7f
updated support to github profiles
Jul 11, 2017
16a1d3d
Update a license
dsnipe Jul 14, 2017
7708a17
Update installation steps
dsnipe Jul 14, 2017
9b4f4ab
Fix a terrible typo
dsnipe Jul 31, 2017
cef1809
Move to Hammer rate limiter with ETS backend
dsnipe Aug 4, 2017
b91668e
Create users with a password and allow auth for them
dsnipe Aug 1, 2017
3dfe688
User can be created with or without a password
dsnipe Aug 11, 2017
0d02feb
WIP: move login page to React
dsnipe Aug 11, 2017
e57ee44
fixed conflicts
Aug 11, 2017
dac559a
Return a user object when a session is created
dsnipe Aug 11, 2017
89c9f53
Merge branch 'gh-126-email-pwd-login' of github.com:WeTransfer/Xperim…
Aug 11, 2017
48fc955
wired up sessions api
Aug 11, 2017
019a9ea
Merge branch 'master' into gh-126-email-pwd-login
dsnipe Oct 1, 2017
ae60879
Delete a log file
dsnipe Oct 1, 2017
8a27134
A new mix.lock file with updated mix
dsnipe Oct 1, 2017
4b75b60
Merge branch 'master' into gh-126-email-pwd-login
dsnipe Oct 26, 2017
09e78ad
Merge branch 'gh-126-email-pwd-login' of github.com:WeTransfer/Xperim…
dsnipe Nov 4, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions assets/js/action/auth.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import ActionHelper from 'modules/redux-actions';

import API from 'modules/api';

import config from 'config';

export const actions = ActionHelper.types([
'AUTH_CREATE',
'AUTH_CREATE_SUCCESS',
'SET_NEW_AUTH_VALUES'
]);

export default ActionHelper.generate({
create(data) {
return async (dispatch) => {
dispatch({type: actions.AUTH_CREATE});

try {
const response = await API.post(config.api.resources.auth.POST, data);
response.json().then(json => {
dispatch({
type: actions.AUTH_CREATE_SUCCESS,
data: json.user
});
});
} catch (e) {
throw 'APIPostFailed';
}
};
},

setValues(data) {
return (dispatch) => {
dispatch({
type: actions.SET_NEW_AUTH_VALUES,
data
});
};
}
});
7 changes: 1 addition & 6 deletions assets/js/action/experiment.es6
Original file line number Diff line number Diff line change
Expand Up @@ -121,11 +121,6 @@ export default ActionHelper.generate({
data: json.experiment
});

// dispatch({
// type: AppActions.SET_APP_REDIRECT,
// path: '/experiments/'
// });

dispatch({
type: AppActions.SET_APP_NOTIFICATION,
notificationData: {
Expand Down Expand Up @@ -166,4 +161,4 @@ export default ActionHelper.generate({
}
};
}
});
});
10 changes: 8 additions & 2 deletions assets/js/action/index.es6
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@ import ExcludableExperiments from './excludableexperiments';
import NewVariant from './newvariant';
import NewRule from './newrule';
import ValidationErrors from './validationerrors';
import Users from './users';
import NewUser from './newuser';
import Auth from './auth';

export default {
Auth,
App,
User,
Applications,
Expand All @@ -19,5 +23,7 @@ export default {
ExcludableExperiments,
NewVariant,
NewRule,
ValidationErrors
};
ValidationErrors,
Users,
NewUser
};
101 changes: 101 additions & 0 deletions assets/js/action/newuser.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import ActionHelper from 'modules/redux-actions';
import {actions as AppActions} from 'action/app';
import {actions as ValidationErrorsActions} from 'action/validationerrors';
import {actions as UsersActions} from 'action/users';
import API from 'modules/api';
import config from 'config';

import validators from 'helper/validators';

const validate = data => {
let errors = {};

if (!data.name) {
errors.name = ['This field is required'];
}

if (!data.email) {
errors.email = ['This field is required'];
} else if (!validators.isEmail(data.email)) {
errors.email = ['Provide a valid email'];
}

if (!data.role) {
errors.role = ['This field is required'];
}

return errors;
};

export const actions = ActionHelper.types([
'SET_NEW_USER_VALUES',
'RESET_NEW_USER'
]);

export default ActionHelper.generate({
create(data, formName) {
return async (dispatch, getState) => {
// Alright lets go, reset the validation errors
dispatch({
type: ValidationErrorsActions.RESET_VALIDATION_ERRORS,
form: formName
});

const validationErrors = validate(data);
if (Object.keys(validationErrors).length) {
dispatch({
type: ValidationErrorsActions.SET_VALIDATION_ERRORS,
form: formName,
errors: validationErrors
});
throw 'ValidationErrors';
}

const {user} = getState();
data.user_id = user.id;

const response = await API.post(config.api.resources.users.POST, {user: data});
if (response.status === 201) {
response.json().then(json => {
dispatch({
type: UsersActions.PUSH_TO_USERS,
data: json.user
});
});

dispatch({type: actions.RESET_NEW_USER});

dispatch({
type: AppActions.SET_APP_NOTIFICATION,
notificationData: {
type: 'info',
message: `${data.name} (${data.email}) was added!`
}
});
return;
} else if (response.status === 422) {
response.json().then(json => {
// Additionally show validation errors in the forms
const validationErrors = json.errors;
if (Object.keys(validationErrors).length) {
dispatch({
type: ValidationErrorsActions.SET_VALIDATION_ERRORS,
form: formName,
errors: validationErrors
});
throw 'ValidationErrors';
}
});
}
};
},

setValues(data) {
return (dispatch) => {
dispatch({
type: actions.SET_NEW_USER_VALUES,
data
});
};
}
});
28 changes: 28 additions & 0 deletions assets/js/action/users.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import ActionHelper from 'modules/redux-actions';
import API from 'modules/api';

import config from 'config';

export const actions = ActionHelper.types([
'FETCH_USERS',
'FETCHED_USERS',
'PUSH_TO_USERS'
]);

export default ActionHelper.generate({
list() {
return async dispatch => {
dispatch({type: actions.FETCH_USERS});

API.get(config.api.resources.users.GET)
.then(response => {
response.json().then(json => {
dispatch({
type: actions.FETCHED_USERS,
list: json.users.reverse()
});
});
});
};
}
});
63 changes: 63 additions & 0 deletions assets/js/component/forms/auth/login.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React from 'react';

import Form from 'component/form';

import RaisedButton from 'material-ui/RaisedButton';
import TextField from 'material-ui/TextField';

export default class Login extends Form {
static propTypes = {
auth: React.PropTypes.object,
setEmail: React.PropTypes.func,
setPassword: React.PropTypes.func,
submit: React.PropTypes.func,
validationErrors: React.PropTypes.object,
unsetValidationError: React.PropTypes.func
};

submit() {
this.props.submit(this.props.auth);
}

render() {
return <div className="form__login">
<div className="row">
<div className="col-md-12">
<TextField
name="email"
fullWidth={true}
floatingLabelText="Email"
onChange={(e, value) => {
this.props.setEmail(value);
this.unsetError('email');
}}
errorText={this.getError('username')}
/>
</div>
<div className="col-md-12">
<TextField
name="password"
fullWidth={true}
floatingLabelText="Password"
onChange={(e, value) => {
this.props.setPassword(value);
this.unsetError('password');
}}
errorText={this.getError('password')}
type="password"
/>
</div>
<div className="col-md-12">
<RaisedButton
label="Signin"
primary={true}
disabled={false}
onTouchTap={() => {
this.submit();
}}
/>
</div>
</div>
</div>;
}
}
4 changes: 2 additions & 2 deletions assets/js/component/forms/createexperiment/experimentform.es6
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import TimePicker from 'material-ui/TimePicker';

import Helper from 'helper';

export default class AddExperiment extends Form {
export default class ExperimentForm extends Form {
static propTypes = {
experiment: React.PropTypes.object,
setName: React.PropTypes.func,
Expand Down Expand Up @@ -141,4 +141,4 @@ export default class AddExperiment extends Form {
</div>
</div>;
}
}
}
68 changes: 68 additions & 0 deletions assets/js/component/forms/user/adduser.es6
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';

import RaisedButton from 'material-ui/RaisedButton';
import FlatButton from 'material-ui/FlatButton';
import Dialog from 'material-ui/Dialog';

import UserForm from './userform';

import globalStyling from 'globalstyling';

const styling = {
...globalStyling
};

export default class AddUser extends React.Component {
static propTypes = {
user: React.PropTypes.object,
experiment: React.PropTypes.object,
setName: React.PropTypes.func,
setEmail: React.PropTypes.func,
setRole: React.PropTypes.func,
save: React.PropTypes.func,
cancel: React.PropTypes.func,
isVisible: React.PropTypes.bool,
validationErrors: React.PropTypes.object,
unsetValidationError: React.PropTypes.func
};

save = () => {
this.props.save(this.props.user, 'createUserForm');
}

render() {
const actions = [
<FlatButton
label="Cancel"
primary={false}
disabled={this.props.user.isSaving}
onTouchTap={this.props.cancel}
style={styling.flatButton}
/>,
<RaisedButton
label="Submit"
primary={true}
disabled={this.props.user.isSaving}
onTouchTap={this.save}
/>
];

return <Dialog
modal={true}
actions={actions}
open={this.props.isVisible}
title="Create User"
repositionOnUpdate={true}
autoScrollBodyContent={true}
>
<UserForm
user={this.props.user}
setName={this.props.setName}
setEmail={this.props.setEmail}
setRole={this.props.setRole}
validationErrors={this.props.validationErrors}
unsetValidationError={this.props.unsetValidationError}
/>
</Dialog>;
}
}
Loading