Merge branch 'master' into clostridium

* master:
  Change password
  Clear store on role change
  Roughing in roles selection
  Confirm delete on meas/char
  Delete strains
  Delete species
  Delete characteristic
  minor cleanup
  ember 1.13.7 & ember-data 1.13.13
  Edit user
  Adapter error deprecated.
  Add characteristic/measurement to strain
  Delete measurement
  Track changes
This commit is contained in:
Matthew Dillon 2015-10-12 20:48:27 -07:00
commit 096e2dca20
31 changed files with 320 additions and 37 deletions

7
app/helpers/equal.js Normal file
View file

@ -0,0 +1,7 @@
import Ember from 'ember';
export function equalHelper(params) {
return params[0] === params[1];
}
export default Ember.HTMLBars.makeBoundHelper(equalHelper);

View file

@ -5,6 +5,7 @@ export default DS.Model.extend({
password : DS.attr('string'),
name : DS.attr('string'),
role : DS.attr('string'),
canEdit : DS.attr('boolean'),
createdAt: DS.attr('date'),
updatedAt: DS.attr('date'),
deletedAt: DS.attr('date'),

View file

@ -1,5 +1,4 @@
import DS from 'ember-data';
import Ember from 'ember';
export default DS.RESTAdapter.extend({
namespace: function() {
@ -12,24 +11,6 @@ export default DS.RESTAdapter.extend({
coalesceFindRequests: true,
ajaxError: function(jqXHR) {
// http://stackoverflow.com/a/24027443
var error = this._super(jqXHR);
if (jqXHR && jqXHR.status === 422) {
var response = Ember.$.parseJSON(jqXHR.responseText),
errors = {};
if (response.errors !== undefined) {
var jsonErrors = response.errors;
Ember.EnumerableUtils.forEach(Object.keys(jsonErrors), function(key) {
errors[Ember.String.camelize(key)] = jsonErrors[key];
});
}
return new DS.InvalidError(errors);
} else {
return error;
}
},
shouldReloadAll: function() {
return true;
},

View file

@ -0,0 +1,13 @@
import Ember from 'ember';
export default Ember.Component.extend({
tagName: 'button',
classNames: ["button-red", "smaller"],
click: function() {
if (window.confirm("Do you really want to delete this?")) {
this.attrs.delete();
}
},
});

View file

@ -0,0 +1 @@
Delete

View file

@ -0,0 +1,11 @@
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
delete: function() {
this.get('model').destroyRecord()
this.transitionToRoute('protected.characteristics.index');
},
},
});

View file

@ -26,6 +26,7 @@
<dl class="span-2">
<dt>Measurements</dt>
<dd>
<p>To add/edit/remove a measurement, please visit the strain's page (links below)</p>
{{protected/characteristics/show/measurements-table model=model}}
</dd>
</dl>
@ -54,4 +55,5 @@
{{#link-to 'protected.characteristics.edit' model.id class="button-gray smaller"}}
Edit
{{/link-to}}
{{delete-button delete=(action 'delete')}}
{{/if}}

View file

@ -0,0 +1,11 @@
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
delete: function() {
this.get('model').destroyRecord();
this.transitionToRoute('protected.species.index');
},
},
});

View file

@ -62,4 +62,5 @@
{{#link-to 'protected.species.edit' model class="button-gray smaller"}}
Edit
{{/link-to}}
{{delete-button delete=(action 'delete')}}
{{/if}}

View file

@ -17,6 +17,7 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
setupController: function(controller, models) {
controller.setProperties(models);
controller.set('metaData', this.store.metadataFor('strain'));
},
});

View file

@ -2,6 +2,7 @@
protected/strains/strain-form
strain=strain
species=species
canAdd=metaData.canAdd
save="save"
cancel="cancel"
}}

View file

@ -0,0 +1,11 @@
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
delete: function() {
this.get('model').destroyRecord();
this.transitionToRoute('protected.strains.index');
},
},
});

View file

@ -4,6 +4,16 @@ export default Ember.Component.extend({
tagName: 'tr',
isEditing: false,
oldCharacteristicId: function() {
let json = this.get('row').toJSON();
return json.characteristic;
}.property(),
rowChanged: Ember.computed('row.notes', 'row.value', 'row.characteristic.id', function() {
return this.get('row.hasDirtyAttributes') ||
this.get('oldCharacteristicId') !== this.get('row.characteristic.id');
}),
actions: {
edit: function() {
// The parent table fetches all of the characteristics ahead of time
@ -13,8 +23,14 @@ export default Ember.Component.extend({
save: function() {
this.toggleProperty('isEditing');
this.get('row').save();
if (this.get('rowChanged')) {
this.get('row').save();
}
},
delete: function() {
this.get('row').destroyRecord();
}
},
});

View file

@ -16,9 +16,15 @@
</td>
{{#if canEdit}}
<td>
<button class="button-red smaller" {{action 'save'}}>
{{#if rowChanged}}
<button class="button-green smaller" {{action 'save'}}>
Save
</button>
{{else}}
<button class="button-gray smaller" {{action 'save'}}>
Cancel
</button>
{{/if}}
</td>
{{/if}}
{{else}}
@ -38,6 +44,7 @@
<button class="button-gray smaller" {{action 'edit'}}>
Edit
</button>
{{delete-button delete=(action 'delete')}}
</td>
{{/if}}
{{/if}}

View file

@ -11,7 +11,19 @@ export default Ember.Component.extend({
}
}.on('didInsertElement'),
sortParams: ['characteristicTypeName', 'sortOrder', 'characteristicName'],
sortParams: ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName'],
sortedMeasurements: Ember.computed.sort('model.measurements', 'sortParams'),
actions: {
addCharacteristic: function() {
const c = this.store.createRecord('characteristic', {
sortOrder: -999
});
const m = this.store.createRecord('measurement', {
characteristic: c
});
this.get('model.measurements').addObject(m);
},
},
});

View file

@ -1,11 +1,19 @@
{{#if canAdd}}
<br>
<button class="button-green smaller" {{action "addCharacteristic"}}>
Add characteristic
</button>
<br><br>
{{/if}}
{{#if measurementsPresent}}
<table class="flakes-table">
<colgroup>
{{#if canEdit}}
<col span="1" style="width:40%">
<col span="1" style="width:20%">
<col span="1" style="width:30%">
<col span="1" style="width:10%">
<col span="1" style="width:20%">
<col span="1" style="width:20%">
{{else}}
<col span="1" style="width:40%">
<col span="1" style="width:30%">

View file

@ -77,6 +77,7 @@
protected/strains/show/measurements-table
model=model
canEdit=false
canAdd=false
}}
</dd>
</dl>
@ -104,4 +105,5 @@
{{#link-to 'protected.strains.edit' model.id class="button-gray smaller"}}
Edit
{{/link-to}}
{{delete-button delete=(action 'delete')}}
{{/if}}

View file

@ -54,6 +54,7 @@
protected/strains/show/measurements-table
model=strain
canEdit=strain.canEdit
canAdd=canAdd
}}
</div>
<br>

View file

@ -0,0 +1,29 @@
import Ember from 'ember';
import ajaxRequest from '../../../../utils/ajax-request';
export default Ember.Controller.extend({
passwordConfirm: null,
actions: {
save: function() {
if (this.get('password') !== this.get('passwordConfirm')) {
this.get('flashMessages').clearMessages();
this.get('flashMessages').error("Password fields don't match");
return;
}
let url = `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/users/password`;
let options = {
method: 'POST',
data: {
password: this.get('password'),
},
};
ajaxRequest(url, options);
this.transitionTo('protected.users.index');
this.get('flashMessages').information('Your password has been changed.');
},
},
});

View file

@ -0,0 +1,12 @@
import Ember from 'ember';
export default Ember.Route.extend({
beforeModel: function(transition) {
this._super(transition);
let user_id = transition.params['protected.users.changepassword'].user_id;
if (this.get('session.currentUser.id') !== user_id) {
this.transitionTo('protected.users.index');
}
}
});

View file

@ -0,0 +1,24 @@
<div class="grid-1">
<div class="span-1">
<fieldset>
<legend>Change password</legend>
<form {{action 'save' on='submit'}}>
<ul>
<li>
<label>New Password</label>
{{input type="password" value=password}}
</li>
<li>
<label>New Password (confirm)</label>
{{input type="password" value=passwordConfirm}}
</li>
<li>
<button type="submit" class="button-green smaller">
Submit
</button>
</li>
</ul>
</form>
</fieldset>
</div>
</div>

View file

@ -0,0 +1,41 @@
import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
save: function() {
let user = this.get('model');
if (user.get('hasDirtyAttributes')) {
let attrs = user.changedAttributes(), roleChanged = false;
if (attrs.role) {
roleChanged = true;
}
user.save().then((user) => {
this.get('flashMessages').clearMessages();
if (roleChanged) {
// Need to clear the store so that canEdit and canAdd
// attributes reflect the new role.
this.get('store').unloadAll();
}
this.transitionToRoute('protected.users.show', user);
}, (err) => {
err.errors.forEach((error) => {
this.get('flashMessages').error(error.detail);
});
});
} else {
this.transitionToRoute('protected.users.show', user);
}
},
cancel: function() {
let user = this.get('model');
user.get('errors').clear();
user.rollbackAttributes();
this.transitionToRoute('protected.users.show', user);
},
},
});

View file

@ -0,0 +1,8 @@
import Ember from 'ember';
export default Ember.Route.extend({
model: function(params) {
return this.store.findRecord('user', params.user_id, { reload: true });
},
});

View file

@ -0,0 +1,7 @@
{{
protected/users/user-form
user=model
currentUser=session.currentUser
save="save"
cancel="cancel"
}}

View file

@ -0,0 +1,7 @@
import Ember from 'ember';
export default Ember.Controller.extend({
isUser: Ember.computed('model.id', 'session.currentUser.id', function() {
return this.get('model.id') === this.get('session.currentUser.id');
}),
});

View file

@ -38,12 +38,20 @@
</fieldset>
</div>
<br>
{{#link-to 'protected.users.show' model.id class="button-gray smaller"}}
Change Password (Does nothing at the moment)
{{/link-to}}
{{#if model.canEdit}}
<br>
{{#link-to 'protected.user.edit' model.id class="button-gray smaller"}}
Edit
{{/link-to}}
{{/if}}
<div class="grid-2 gutter-20">
{{#if isUser}}
<div class="span-1">
{{#link-to 'protected.users.changepassword' model.id class="button-gray smaller"}}
Change Password
{{/link-to}}
</div>
{{/if}}
<div class="span-1">
{{#if model.canEdit}}
{{#link-to 'protected.users.edit' model.id class="button-gray smaller"}}
Edit
{{/link-to}}
{{/if}}
</div>
</div>

View file

@ -0,0 +1,19 @@
import Ember from 'ember';
export default Ember.Component.extend({
isAdmin: Ember.computed('currentUser', function() {
return this.get('currentUser.role') == 'A';
}),
roles: Ember.String.w('A R W'),
actions: {
save: function() {
this.sendAction('save');
},
cancel: function() {
this.sendAction('cancel');
},
}
});

View file

@ -0,0 +1,40 @@
<form class="grid-form" {{action 'save' on='submit'}}>
<fieldset>
<legend><em>{{user.name}}</em></legend>
<div data-row-span="1">
<div data-field-span="1">
<label>Name</label>
{{input value=user.name}}
</div>
</div>
<div data-row-span="1">
<div data-field-span="1">
<label>Email</label>
{{input value=user.email}}
</div>
</div>
<div data-row-span="1">
<div data-field-span="1">
<label>Role</label>
{{#if isAdmin}}
<select onchange={{action (mut user.role) value="target.value"}}>
{{#each roles as |roleChoice|}}
<option value={{roleChoice}} selected={{equal user.role roleChoice}}>{{roleChoice}}</option>
{{/each}}
</select>
{{else}}
{{user.role}}
{{/if}}
</div>
</div>
</fieldset>
<br>
<a class="button-red smaller" {{action 'cancel'}}>
Cancel
</a>
{{#if user.hasDirtyAttributes}}
<button type="submit" class="button-green smaller">
Save
</button>
{{/if}}
</form>

View file

@ -22,6 +22,7 @@ Router.map(function() {
this.route('users', function() {
this.route('show', { path: ':user_id' });
this.route('edit', { path: ':user_id/edit' });
this.route('changepassword', { path: ':user_id/changepassword' });
});
this.route('compare', function() {

View file

@ -2,10 +2,10 @@
"name": "clostridiumdotinfo",
"dependencies": {
"jquery": "~2.1.1",
"ember": "1.13.7",
"ember": "1.13.10",
"ember-cli-shims": "ember-cli/ember-cli-shims#0.0.3",
"ember-cli-test-loader": "ember-cli-test-loader#0.1.3",
"ember-data": "1.13.8",
"ember-data": "1.13.13",
"ember-load-initializers": "ember-cli/ember-load-initializers#0.1.5",
"ember-qunit": "0.4.9",
"ember-qunit-notifications": "0.0.7",

View file

@ -37,7 +37,7 @@
"ember-cli-release": "0.2.3",
"ember-cli-sri": "^1.0.3",
"ember-cli-uglify": "^1.2.0",
"ember-data": "1.13.8",
"ember-data": "1.13.13",
"ember-disable-proxy-controllers": "^1.0.0",
"ember-export-application-global": "^1.0.3",
"ember-select-2": "1.3.0"