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'), password : DS.attr('string'),
name : DS.attr('string'), name : DS.attr('string'),
role : DS.attr('string'), role : DS.attr('string'),
canEdit : DS.attr('boolean'),
createdAt: DS.attr('date'), createdAt: DS.attr('date'),
updatedAt: DS.attr('date'), updatedAt: DS.attr('date'),
deletedAt: DS.attr('date'), deletedAt: DS.attr('date'),

View file

@ -1,5 +1,4 @@
import DS from 'ember-data'; import DS from 'ember-data';
import Ember from 'ember';
export default DS.RESTAdapter.extend({ export default DS.RESTAdapter.extend({
namespace: function() { namespace: function() {
@ -12,24 +11,6 @@ export default DS.RESTAdapter.extend({
coalesceFindRequests: true, 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() { shouldReloadAll: function() {
return true; 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"> <dl class="span-2">
<dt>Measurements</dt> <dt>Measurements</dt>
<dd> <dd>
<p>To add/edit/remove a measurement, please visit the strain's page (links below)</p>
{{protected/characteristics/show/measurements-table model=model}} {{protected/characteristics/show/measurements-table model=model}}
</dd> </dd>
</dl> </dl>
@ -54,4 +55,5 @@
{{#link-to 'protected.characteristics.edit' model.id class="button-gray smaller"}} {{#link-to 'protected.characteristics.edit' model.id class="button-gray smaller"}}
Edit Edit
{{/link-to}} {{/link-to}}
{{delete-button delete=(action 'delete')}}
{{/if}} {{/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"}} {{#link-to 'protected.species.edit' model class="button-gray smaller"}}
Edit Edit
{{/link-to}} {{/link-to}}
{{delete-button delete=(action 'delete')}}
{{/if}} {{/if}}

View file

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

View file

@ -2,6 +2,7 @@
protected/strains/strain-form protected/strains/strain-form
strain=strain strain=strain
species=species species=species
canAdd=metaData.canAdd
save="save" save="save"
cancel="cancel" 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', tagName: 'tr',
isEditing: false, 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: { actions: {
edit: function() { edit: function() {
// The parent table fetches all of the characteristics ahead of time // The parent table fetches all of the characteristics ahead of time
@ -13,8 +23,14 @@ export default Ember.Component.extend({
save: function() { save: function() {
this.toggleProperty('isEditing'); 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> </td>
{{#if canEdit}} {{#if canEdit}}
<td> <td>
<button class="button-red smaller" {{action 'save'}}> {{#if rowChanged}}
<button class="button-green smaller" {{action 'save'}}>
Save Save
</button> </button>
{{else}}
<button class="button-gray smaller" {{action 'save'}}>
Cancel
</button>
{{/if}}
</td> </td>
{{/if}} {{/if}}
{{else}} {{else}}
@ -38,6 +44,7 @@
<button class="button-gray smaller" {{action 'edit'}}> <button class="button-gray smaller" {{action 'edit'}}>
Edit Edit
</button> </button>
{{delete-button delete=(action 'delete')}}
</td> </td>
{{/if}} {{/if}}
{{/if}} {{/if}}

View file

@ -11,7 +11,19 @@ export default Ember.Component.extend({
} }
}.on('didInsertElement'), }.on('didInsertElement'),
sortParams: ['characteristicTypeName', 'sortOrder', 'characteristicName'], sortParams: ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName'],
sortedMeasurements: Ember.computed.sort('model.measurements', 'sortParams'), 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}} {{#if measurementsPresent}}
<table class="flakes-table"> <table class="flakes-table">
<colgroup> <colgroup>
{{#if canEdit}} {{#if canEdit}}
<col span="1" style="width:40%"> <col span="1" style="width:40%">
<col span="1" style="width:20%"> <col span="1" style="width:20%">
<col span="1" style="width:30%"> <col span="1" style="width:20%">
<col span="1" style="width:10%"> <col span="1" style="width:20%">
{{else}} {{else}}
<col span="1" style="width:40%"> <col span="1" style="width:40%">
<col span="1" style="width:30%"> <col span="1" style="width:30%">

View file

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

View file

@ -54,6 +54,7 @@
protected/strains/show/measurements-table protected/strains/show/measurements-table
model=strain model=strain
canEdit=strain.canEdit canEdit=strain.canEdit
canAdd=canAdd
}} }}
</div> </div>
<br> <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> </fieldset>
</div> </div>
<br> <br>
{{#link-to 'protected.users.show' model.id class="button-gray smaller"}} <div class="grid-2 gutter-20">
Change Password (Does nothing at the moment) {{#if isUser}}
{{/link-to}} <div class="span-1">
{{#if model.canEdit}} {{#link-to 'protected.users.changepassword' model.id class="button-gray smaller"}}
<br> Change Password
{{#link-to 'protected.user.edit' model.id class="button-gray smaller"}} {{/link-to}}
Edit </div>
{{/link-to}} {{/if}}
{{/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('users', function() {
this.route('show', { path: ':user_id' }); this.route('show', { path: ':user_id' });
this.route('edit', { path: ':user_id/edit' }); this.route('edit', { path: ':user_id/edit' });
this.route('changepassword', { path: ':user_id/changepassword' });
}); });
this.route('compare', function() { this.route('compare', function() {

View file

@ -2,10 +2,10 @@
"name": "clostridiumdotinfo", "name": "clostridiumdotinfo",
"dependencies": { "dependencies": {
"jquery": "~2.1.1", "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-shims": "ember-cli/ember-cli-shims#0.0.3",
"ember-cli-test-loader": "ember-cli-test-loader#0.1.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-load-initializers": "ember-cli/ember-load-initializers#0.1.5",
"ember-qunit": "0.4.9", "ember-qunit": "0.4.9",
"ember-qunit-notifications": "0.0.7", "ember-qunit-notifications": "0.0.7",

View file

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