fixed merge conflict in tests/index
This commit is contained in:
commit
8730b6659f
116 changed files with 1793 additions and 1106 deletions
|
@ -1,3 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({});
|
||||
const { Route } = Ember;
|
||||
|
||||
export default Route.extend({});
|
||||
|
|
|
@ -6,5 +6,5 @@ const { Controller } = Ember;
|
|||
export default Controller.extend(SaveModel, {
|
||||
// Required for SaveModel mixin
|
||||
fallbackRouteSave: 'protected.characteristics.show',
|
||||
fallbackRouteCancel: 'protected.characteristics.index',
|
||||
fallbackRouteCancel: 'protected.characteristics.show',
|
||||
});
|
||||
|
|
|
@ -1,41 +1,22 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
const { Controller } = Ember;
|
||||
|
||||
export default Controller.extend({
|
||||
selectedStrains: null,
|
||||
selectedCharacteristics: null,
|
||||
|
||||
actions: {
|
||||
search: function() {
|
||||
let query = {
|
||||
strain_ids: this.get('selectedStrains'),
|
||||
characteristic_ids: this.get('selectedCharacteristics'),
|
||||
};
|
||||
|
||||
this.transitionToRoute('protected.compare.results', {queryParams: query});
|
||||
search: function(query) {
|
||||
this.transitionToRoute('protected.compare.results', { queryParams: query });
|
||||
},
|
||||
|
||||
selectAllStrains: function() {
|
||||
let strains = this.get('strains');
|
||||
let strain_ids = [];
|
||||
strains.forEach((strain) => {
|
||||
strain_ids.push(strain.id);
|
||||
});
|
||||
this.set('selectedStrains', strain_ids.join(","));
|
||||
updateStrainSelection: function(selection) {
|
||||
this.set('selectedStrains', selection);
|
||||
},
|
||||
|
||||
deselectAllStrains: function() {
|
||||
this.set('selectedStrains', '');
|
||||
updateCharacteristicSelection: function(selection) {
|
||||
this.set('selectedCharacteristics', selection);
|
||||
},
|
||||
|
||||
selectAllCharacteristics: function() {
|
||||
let chars = this.get('characteristics');
|
||||
let char_ids = [];
|
||||
chars.forEach((char) => {
|
||||
char_ids.push(char.id);
|
||||
});
|
||||
this.set('selectedCharacteristics', char_ids.join(","));
|
||||
},
|
||||
|
||||
deselectAllCharacteristics: function() {
|
||||
this.set('selectedCharacteristics', '');
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,31 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
const { Controller } = Ember;
|
||||
|
||||
export default Controller.extend({
|
||||
queryParams: ['strain_ids', 'characteristic_ids'],
|
||||
|
||||
csvLink: function() {
|
||||
let token = encodeURIComponent(this.get('session.secure.token'));
|
||||
return `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/` +
|
||||
`compare?token=${token}&strain_ids=${this.get('strain_ids')}&` +
|
||||
`characteristic_ids=${this.get('characteristic_ids')}&mimeType=csv`;
|
||||
}.property('strain_ids', 'characteristic_ids').readOnly(),
|
||||
|
||||
strains: function() {
|
||||
let strains = [];
|
||||
let strain_ids = this.get('strain_ids').split(',');
|
||||
strain_ids.forEach((id) => {
|
||||
strains.push(this.store.peekRecord('strain', id));
|
||||
});
|
||||
return strains;
|
||||
}.property('strain_ids'),
|
||||
|
||||
characteristics: function() {
|
||||
let characteristics = [];
|
||||
let characteristic_ids = this.get('characteristic_ids').split(',');
|
||||
characteristic_ids.forEach((id) => {
|
||||
characteristics.push(this.store.peekRecord('characteristic', id));
|
||||
});
|
||||
return characteristics;
|
||||
}.property('characteristic_ids'),
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component, computed, inject: { service } } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
session: service(),
|
||||
|
||||
strains: null,
|
||||
characteristics: null,
|
||||
strain_ids: null,
|
||||
characteristic_ids: null,
|
||||
|
||||
csvLink: computed('strain_ids', 'characteristic_ids', function() {
|
||||
const token = encodeURIComponent(this.get('session.data.authenticated.access_token'));
|
||||
return `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/` +
|
||||
`compare?token=${token}&strain_ids=${this.get('strain_ids')}&` +
|
||||
`characteristic_ids=${this.get('characteristic_ids')}&mimeType=csv`;
|
||||
}),
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Characteristic</th>
|
||||
{{#each strains as |strain|}}
|
||||
<th>
|
||||
{{#link-to 'protected.strains.show' strain.id classBinding="data.typeStrain:type-strain"}}
|
||||
{{strain.fullNameMU}}
|
||||
{{/link-to}}
|
||||
</th>
|
||||
{{/each}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each characteristics as |row|}}
|
||||
<tr>
|
||||
{{#each row key="@index" as |col|}}
|
||||
<td>{{col}}</td>
|
||||
{{/each}}
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<a href="{{csvLink}}" download>Download as CSV</a>
|
|
@ -1,8 +1,10 @@
|
|||
import Ember from 'ember';
|
||||
import ajaxRequest from '../../../../utils/ajax-request';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
session: Ember.inject.service('session'),
|
||||
const { Route, $: { isEmptyObject }, inject: { service } } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
session: service(),
|
||||
ajax: service(),
|
||||
|
||||
queryParams: {
|
||||
strain_ids: {
|
||||
|
@ -15,8 +17,9 @@ export default Ember.Route.extend({
|
|||
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
if (Ember.$.isEmptyObject(transition.queryParams.strain_ids) ||
|
||||
Ember.$.isEmptyObject(transition.queryParams.characteristic_ids)) {
|
||||
const strain_ids = transition.queryParams.strain_ids;
|
||||
const characteristic_ids = transition.queryParams.characteristic_ids;
|
||||
if (isEmptyObject(strain_ids) || isEmptyObject(characteristic_ids)) {
|
||||
this.transitionTo('protected.compare');
|
||||
}
|
||||
},
|
||||
|
@ -26,23 +29,35 @@ export default Ember.Route.extend({
|
|||
this.transitionTo('protected.compare');
|
||||
}
|
||||
|
||||
let compare = this.controllerFor('protected.compare');
|
||||
const compare = this.controllerFor('protected.compare');
|
||||
compare.set('selectedStrains', params.strain_ids);
|
||||
compare.set('selectedCharacteristics', params.characteristic_ids);
|
||||
|
||||
let url = `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/compare`;
|
||||
let options = {
|
||||
method: 'GET',
|
||||
data: params,
|
||||
};
|
||||
return ajaxRequest(url, options, this.get('session'));
|
||||
return this.get('ajax').request('/compare', { data: params });
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
model.forEach((m, i) => {
|
||||
let c = this.store.peekRecord('characteristic', m[0]);
|
||||
const c = this.store.peekRecord('characteristic', m[0]);
|
||||
model[i][0] = c.get('characteristicName');
|
||||
});
|
||||
|
||||
const compare = this.controllerFor('protected.compare');
|
||||
|
||||
const strains = [];
|
||||
const strain_ids = compare.get('selectedStrains').split(',');
|
||||
strain_ids.forEach((id) => {
|
||||
strains.push(this.store.peekRecord('strain', id));
|
||||
});
|
||||
controller.set('strains', strains);
|
||||
|
||||
const characteristics = [];
|
||||
const characteristic_ids = compare.get('selectedCharacteristics').split(',');
|
||||
characteristic_ids.forEach((id) => {
|
||||
characteristics.push(this.store.peekRecord('characteristic', id));
|
||||
});
|
||||
controller.set('characteristics', characteristics);
|
||||
|
||||
controller.set('model', model);
|
||||
},
|
||||
|
||||
|
|
|
@ -1,25 +1,7 @@
|
|||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Characteristic</th>
|
||||
{{#each strains as |strain|}}
|
||||
<th>
|
||||
{{#link-to 'protected.strains.show' strain.id classBinding="data.typeStrain:type-strain"}}
|
||||
{{strain.fullNameMU}}
|
||||
{{/link-to}}
|
||||
</th>
|
||||
{{/each}}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each model as |row|}}
|
||||
<tr>
|
||||
{{#each row key="@index" as |col|}}
|
||||
<td>{{col}}</td>
|
||||
{{/each}}
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<a href="{{csvLink}}" download>Download as CSV</a>
|
||||
{{
|
||||
protected/compare/results/results-table
|
||||
strains=strains
|
||||
characteristics=model
|
||||
strain_ids=strain_ids
|
||||
characteristic_ids=characteristic_ids
|
||||
}}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
const { Route } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
model: function() {
|
||||
return this.store.findAll('characteristic');
|
||||
},
|
||||
|
|
61
app/pods/protected/compare/select-form/component.js
Normal file
61
app/pods/protected/compare/select-form/component.js
Normal file
|
@ -0,0 +1,61 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
characteristics: null,
|
||||
strains: null,
|
||||
|
||||
"on-search": null,
|
||||
"update-strains": null,
|
||||
"update-characteristics": null,
|
||||
|
||||
selectedStrains: null,
|
||||
selectedCharacteristics: null,
|
||||
|
||||
updateStrains: function(selection) {
|
||||
this.set('selectedStrains', selection);
|
||||
this.attrs['update-strains'](this.get('selectedStrains'));
|
||||
},
|
||||
|
||||
updateCharacteristics: function(selection) {
|
||||
this.set('selectedCharacteristics', selection);
|
||||
this.attrs['update-characteristics'](this.get('selectedCharacteristics'));
|
||||
},
|
||||
|
||||
actions: {
|
||||
search: function() {
|
||||
const query = {
|
||||
strain_ids: this.get('selectedStrains'),
|
||||
characteristic_ids: this.get('selectedCharacteristics'),
|
||||
};
|
||||
this.attrs['on-search'](query);
|
||||
},
|
||||
|
||||
selectAllStrains: function() {
|
||||
const strains = this.get('strains');
|
||||
const strain_ids = [];
|
||||
strains.forEach((strain) => {
|
||||
strain_ids.push(strain.get('id'));
|
||||
});
|
||||
this.updateStrains(strain_ids.join(","));
|
||||
},
|
||||
|
||||
deselectAllStrains: function() {
|
||||
this.updateStrains("");
|
||||
},
|
||||
|
||||
selectAllCharacteristics: function() {
|
||||
const chars = this.get('characteristics');
|
||||
const char_ids = [];
|
||||
chars.forEach((char) => {
|
||||
char_ids.push(char.get('id'));
|
||||
});
|
||||
this.updateCharacteristics(char_ids.join(","));
|
||||
},
|
||||
|
||||
deselectAllCharacteristics: function() {
|
||||
this.updateCharacteristics("");
|
||||
},
|
||||
},
|
||||
});
|
53
app/pods/protected/compare/select-form/template.hbs
Normal file
53
app/pods/protected/compare/select-form/template.hbs
Normal file
|
@ -0,0 +1,53 @@
|
|||
<div class="span-1">
|
||||
<fieldset>
|
||||
<form {{action 'search' on='submit'}}>
|
||||
<ul>
|
||||
<li>
|
||||
<label>Strains</label>
|
||||
{{
|
||||
select-2
|
||||
multiple=true
|
||||
content=strains
|
||||
value=selectedStrains
|
||||
optionValuePath="id"
|
||||
optionLabelPath="fullNameMU"
|
||||
placeholder="Select one or more strains"
|
||||
}}
|
||||
</li>
|
||||
<li>
|
||||
<button class="action button-green smaller right" {{action 'selectAllStrains'}}>
|
||||
Select All
|
||||
</button>
|
||||
<button class="action button-red smaller right" {{action 'deselectAllStrains'}}>
|
||||
Deselect All
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<label>Characteristics</label>
|
||||
{{
|
||||
select-2
|
||||
multiple=true
|
||||
content=characteristics
|
||||
value=selectedCharacteristics
|
||||
optionValuePath="id"
|
||||
optionLabelPath="characteristicName"
|
||||
placeholder="Select one or more characteristics"
|
||||
}}
|
||||
</li>
|
||||
<li>
|
||||
<button class="action button-green smaller right" {{action 'selectAllCharacteristics'}}>
|
||||
Select All
|
||||
</button>
|
||||
<button class="action button-red smaller right" {{action 'deselectAllCharacteristics'}}>
|
||||
Deselect All
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="submit" class="action button-gray smaller right">
|
||||
Search
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
|
@ -1,57 +1,14 @@
|
|||
<h2>{{genus-name}} - Compare Strains</h2>
|
||||
|
||||
<div class="span-1">
|
||||
<fieldset>
|
||||
<form {{action 'search' on='submit'}}>
|
||||
<ul>
|
||||
<li>
|
||||
<label>Strains</label>
|
||||
{{
|
||||
select-2
|
||||
multiple=true
|
||||
content=strains
|
||||
value=selectedStrains
|
||||
optionValuePath="id"
|
||||
optionLabelPath="fullNameMU"
|
||||
placeholder="Select one or more strains"
|
||||
}}
|
||||
</li>
|
||||
<li>
|
||||
<button class="action button-green smaller right" {{action 'selectAllStrains'}}>
|
||||
Select All
|
||||
</button>
|
||||
<button class="action button-red smaller right" {{action 'deselectAllStrains'}}>
|
||||
Deselect All
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<label>Characteristics</label>
|
||||
{{
|
||||
select-2
|
||||
multiple=true
|
||||
content=characteristics
|
||||
value=selectedCharacteristics
|
||||
optionValuePath="id"
|
||||
optionLabelPath="characteristicName"
|
||||
placeholder="Select one or more characteristics"
|
||||
}}
|
||||
</li>
|
||||
<li>
|
||||
<button class="action button-green smaller right" {{action 'selectAllCharacteristics'}}>
|
||||
Select All
|
||||
</button>
|
||||
<button class="action button-red smaller right" {{action 'deselectAllCharacteristics'}}>
|
||||
Deselect All
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button type="submit" class="action button-gray smaller right">
|
||||
Search
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{
|
||||
protected/compare/select-form
|
||||
characteristics=characteristics
|
||||
strains=strains
|
||||
selectedStrains=selectedStrains
|
||||
selectedCharacteristics=selectedCharacteristics
|
||||
on-search=(action "search")
|
||||
update-strains=(action "updateStrainSelection")
|
||||
update-characteristics=(action "updateCharacteristicSelection")
|
||||
}}
|
||||
|
||||
{{outlet}}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
const { Route } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
this.transitionTo('protected.compare');
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
Welcome
|
|
@ -1,9 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
||||
|
||||
export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
||||
const { Route } = Ember;
|
||||
|
||||
export default Route.extend(AuthenticatedRouteMixin, {
|
||||
actions: {
|
||||
error: function() {
|
||||
error: function(err) {
|
||||
console.log(err);
|
||||
this.transitionTo('/not-found');
|
||||
},
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import SetupMetaData from '../../../../../mixins/setup-metadata';
|
||||
|
||||
const { Component } = Ember;
|
||||
const { Component, computed: { sort } } = Ember;
|
||||
|
||||
export default Component.extend(SetupMetaData, {
|
||||
species: null,
|
||||
|
||||
sortParams: ['speciesName', 'strainCount'],
|
||||
sortedSpecies: Ember.computed.sort('species', 'sortParams'),
|
||||
sortedSpecies: sort('species', 'sortParams'),
|
||||
|
||||
});
|
||||
|
|
|
@ -1,31 +1,36 @@
|
|||
import Ember from 'ember';
|
||||
import SaveModel from '../../../../mixins/save-model';
|
||||
import ajaxError from '../../../../utils/ajax-error';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
actions: {
|
||||
save: function() {
|
||||
let strain = this.get('strain');
|
||||
const { Controller } = Ember;
|
||||
|
||||
if (strain.get('hasDirtyAttributes')) {
|
||||
strain.save().then((strain) => {
|
||||
this.transitionToRoute('protected.strains.show', strain);
|
||||
}, () => {
|
||||
ajaxError(strain.get('errors'), this.get('flashMessages'));
|
||||
});
|
||||
} else {
|
||||
strain.destroyRecord().then(() => {
|
||||
this.transitionToRoute('protected.strains.show', strain);
|
||||
});
|
||||
}
|
||||
export default Controller.extend(SaveModel, {
|
||||
// Required for SaveModel mixin
|
||||
fallbackRouteSave: 'protected.strains.show',
|
||||
fallbackRouteCancel: 'protected.strains.show',
|
||||
|
||||
actions: {
|
||||
addCharacteristic: function() {
|
||||
return this.store.createRecord('measurement', {
|
||||
characteristic: this.store.createRecord('characteristic', { sortOrder: -999 }),
|
||||
});
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
let strain = this.get('strain');
|
||||
saveMeasurement: function(measurement, properties) {
|
||||
measurement.setProperties(properties);
|
||||
measurement.save().then(() => {
|
||||
this.get('flashMessages').clearMessages();
|
||||
}, () => {
|
||||
ajaxError(measurement.get('errors'), this.get('flashMessages'));
|
||||
});
|
||||
},
|
||||
|
||||
strain.get('errors').clear();
|
||||
strain.rollbackAttributes();
|
||||
|
||||
this.transitionToRoute('protected.strains.show', strain);
|
||||
deleteMeasurement: function(measurement) {
|
||||
const characteristic = measurement.get('characteristic');
|
||||
if (characteristic.get('isNew')) {
|
||||
characteristic.destroyRecord();
|
||||
}
|
||||
measurement.destroyRecord();
|
||||
},
|
||||
|
||||
},
|
||||
|
|
|
@ -1,35 +1,44 @@
|
|||
import Ember from 'ember';
|
||||
import ElevatedAccess from '../../../../mixins/elevated-access';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Route } = Ember;
|
||||
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
this.get('currentUser.account').then((user) => {
|
||||
if (user.get('isReader')) {
|
||||
this.transitionTo('protected.strains.index');
|
||||
}
|
||||
});
|
||||
},
|
||||
export default Route.extend(ElevatedAccess, {
|
||||
// Required for ElevatedAccess mixin
|
||||
fallbackRouteBefore: 'protected.strains.index',
|
||||
fallbackRouteAfter: 'protected.strains.show',
|
||||
|
||||
model: function(params) {
|
||||
return Ember.RSVP.hash({
|
||||
strain: this.store.find('strain', params.strain_id),
|
||||
strain: this.store.findRecord('strain', params.strain_id),
|
||||
species: this.store.findAll('species'), // Need for dropdown
|
||||
characteristics: this.store.findAll('characteristic'), // Need for dropdown
|
||||
});
|
||||
},
|
||||
|
||||
// Overriding afterModel because of RSVP hash
|
||||
afterModel: function(models) {
|
||||
if (!models.strain.get('canEdit')) {
|
||||
this.transitionTo('strains.show', models.strain.get('id'));
|
||||
if (!models.strain.get('isNew') && !models.strain.get('canEdit')) {
|
||||
this.transitionTo(this.get('fallbackRouteAfter'), models.strain.get('id'));
|
||||
}
|
||||
},
|
||||
|
||||
// Setting up controller because of RSVP hash
|
||||
setupController: function(controller, models) {
|
||||
controller.setProperties(models);
|
||||
this.get('currentUser.account').then((user) => {
|
||||
controller.set('metaData', user.get('metaData'));
|
||||
});
|
||||
controller.set('model', models.strain);
|
||||
controller.set('speciesList', models.species);
|
||||
controller.set('allCharacteristics', models.characteristics);
|
||||
},
|
||||
|
||||
actions: {
|
||||
// Overriding willTransition because of RSVP hash
|
||||
willTransition: function(/*transition*/) {
|
||||
const controller = this.get('controller');
|
||||
const model = controller.get('model');
|
||||
|
||||
if (model.get('isNew')) {
|
||||
model.destroyRecord();
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
{{
|
||||
protected/strains/strain-form
|
||||
strain=strain
|
||||
species=species
|
||||
canAdd=metaData.canAdd
|
||||
save="save"
|
||||
cancel="cancel"
|
||||
strain=model
|
||||
speciesList=speciesList
|
||||
add-characteristic=(action "addCharacteristic")
|
||||
allCharacteristics=allCharacteristics
|
||||
save-measurement=(action "saveMeasurement")
|
||||
delete-measurement=(action "deleteMeasurement")
|
||||
on-save=(action "save")
|
||||
on-cancel=(action "cancel")
|
||||
}}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
sortParams: ['sortOrder'],
|
||||
sortedStrains: Ember.computed.sort('model', 'sortParams'),
|
||||
});
|
|
@ -1,17 +1,10 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Route } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
model: function() {
|
||||
return this.store.findAll('strain');
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.set('model', model);
|
||||
this.get('currentUser.account').then((user) => {
|
||||
controller.set('metaData', user.get('metaData'));
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
|
12
app/pods/protected/strains/index/strain-table/component.js
Normal file
12
app/pods/protected/strains/index/strain-table/component.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import SetupMetaData from '../../../../../mixins/setup-metadata';
|
||||
|
||||
const { Component, computed: { sort } } = Ember;
|
||||
|
||||
export default Component.extend(SetupMetaData, {
|
||||
strains: null,
|
||||
|
||||
sortParams: ['fullName'],
|
||||
sortedStrains: sort('strains', 'sortParams'),
|
||||
|
||||
});
|
26
app/pods/protected/strains/index/strain-table/template.hbs
Normal file
26
app/pods/protected/strains/index/strain-table/template.hbs
Normal file
|
@ -0,0 +1,26 @@
|
|||
<h3 id="total-strains">Total strains: {{strains.length}}</h3>
|
||||
|
||||
{{add-button label="Add Strain" link="protected.strains.new" canAdd=metaData.canAdd}}
|
||||
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Species</th>
|
||||
<th>Total Measurements</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each sortedStrains as |strain|}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#link-to 'protected.strains.show' strain classBinding="data.typeStrain:type-strain"}}
|
||||
{{strain.fullNameMU}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>
|
||||
{{strain.totalMeasurements}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
|
@ -1,27 +1,6 @@
|
|||
<h2>{{genus-name}} Strains</h2>
|
||||
<h3>Total strains: {{model.length}}</h3>
|
||||
|
||||
{{add-button label="Add Strain" link="protected.strains.new" canAdd=metaData.canAdd}}
|
||||
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Species</th>
|
||||
<th>Total Measurements</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each sortedStrains as |row|}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#link-to 'protected.strains.show' row classBinding="data.typeStrain:type-strain"}}
|
||||
{{row.fullNameMU}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>
|
||||
{{row.totalMeasurements}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{
|
||||
protected/strains/index/strain-table
|
||||
strains=model
|
||||
}}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
tagName: 'tr',
|
||||
|
||||
// Read-only attributes
|
||||
isEditing: false,
|
||||
allCharacteristics: null,
|
||||
measurement: null,
|
||||
isDirty: null,
|
||||
|
||||
// Actions
|
||||
"save-measurement": null,
|
||||
"delete-measurement": null,
|
||||
|
||||
// Property mapping
|
||||
propertiesList: ['characteristic', 'value', 'notes'],
|
||||
characteristic: null,
|
||||
value: null,
|
||||
notes: null,
|
||||
|
||||
resetOnInit: Ember.on('init', function() {
|
||||
this.get('propertiesList').forEach((field) => {
|
||||
const valueInMeasurement = this.get('measurement').get(field);
|
||||
this.set(field, valueInMeasurement);
|
||||
});
|
||||
}),
|
||||
|
||||
updateField: function(property, value) {
|
||||
this.set(property, value);
|
||||
// Manually compare against passed in value
|
||||
if (this.get('measurement').get(property) !== value) {
|
||||
this.set('isDirty', true);
|
||||
} else {
|
||||
this.set('isDirty', false);
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
edit: function() {
|
||||
this.toggleProperty('isEditing');
|
||||
},
|
||||
|
||||
save: function() {
|
||||
this.attrs['save-measurement'](this.get('measurement'), this.getProperties(this.get('propertiesList')));
|
||||
this.toggleProperty('isEditing');
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this.attrs['delete-measurement'](this.get('measurement'));
|
||||
},
|
||||
|
||||
characteristicDidChange: function(value) {
|
||||
const newCharacteristic = this.get('allCharacteristics').findBy('id', value);
|
||||
this.updateField('characteristic', newCharacteristic);
|
||||
},
|
||||
|
||||
valueDidChange: function(value) {
|
||||
this.updateField('value', value);
|
||||
},
|
||||
|
||||
notesDidChange: function(value) {
|
||||
this.updateField('notes', value);
|
||||
},
|
||||
|
||||
},
|
||||
});
|
|
@ -0,0 +1,52 @@
|
|||
{{#if isEditing}}
|
||||
<td></td>
|
||||
<td>
|
||||
<select onchange={{action "characteristicDidChange" value="target.value"}}>
|
||||
{{#each allCharacteristics as |characteristicChoice|}}
|
||||
<option value={{characteristicChoice.id}} selected={{equal characteristic.id characteristicChoice.id}}>{{characteristicChoice.characteristicName}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
{{one-way-input type="text" class="measurement-value" value=value update=(action "valueDidChange")}}
|
||||
</td>
|
||||
<td>
|
||||
{{one-way-input type="text" class="measurement-notes" value=notes update=(action "notesDidChange")}}
|
||||
</td>
|
||||
{{#if canEdit}}
|
||||
<td>
|
||||
{{#if isDirty}}
|
||||
<button class="button-green smaller" {{action 'save'}}>
|
||||
Save
|
||||
</button>
|
||||
{{else}}
|
||||
<button class="button-gray smaller" {{action 'save'}}>
|
||||
Cancel
|
||||
</button>
|
||||
{{/if}}
|
||||
</td>
|
||||
{{/if}}
|
||||
{{else}}
|
||||
<td>
|
||||
{{{measurement.characteristic.characteristicTypeName}}}
|
||||
</td>
|
||||
<td>
|
||||
{{#link-to 'protected.characteristics.show' measurement.characteristic.id}}
|
||||
{{{measurement.characteristic.characteristicName}}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>
|
||||
{{measurement.value}}
|
||||
</td>
|
||||
<td>
|
||||
{{measurement.notes}}
|
||||
</td>
|
||||
{{#if canEdit}}
|
||||
<td>
|
||||
<button class="button-gray smaller" {{action 'edit'}}>
|
||||
Edit
|
||||
</button>
|
||||
{{delete-button delete=(action 'delete')}}
|
||||
</td>
|
||||
{{/if}}
|
||||
{{/if}}
|
55
app/pods/protected/strains/measurements-table/component.js
Normal file
55
app/pods/protected/strains/measurements-table/component.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component, computed } = Ember;
|
||||
const { sort } = computed;
|
||||
|
||||
export default Component.extend({
|
||||
// Passed in
|
||||
strain: null,
|
||||
allCharacteristics: null,
|
||||
canEdit: false,
|
||||
canAdd: false,
|
||||
|
||||
// Actions
|
||||
"add-characteristic": null,
|
||||
"save-measurement": null,
|
||||
"delete-measurement": null,
|
||||
|
||||
// Properties
|
||||
sortParams: ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName'],
|
||||
sortAsc: true,
|
||||
paramsChanged: false,
|
||||
sortedMeasurements: sort('strain.measurements', 'sortParams'),
|
||||
measurementsPresent: computed('strain.measurements', function() {
|
||||
return this.get('strain.measurements.length') > 0;
|
||||
}),
|
||||
|
||||
actions: {
|
||||
addCharacteristic: function() {
|
||||
const newChar = this.attrs['add-characteristic']();
|
||||
this.get('strain.measurements').addObject(newChar);
|
||||
},
|
||||
|
||||
changeSortParam: function(col) {
|
||||
const sort = this.get('sortAsc') ? 'asc' : 'desc';
|
||||
this.set('sortParams', [`${col}:${sort}`]);
|
||||
this.set('paramsChanged', true);
|
||||
this.toggleProperty('sortAsc');
|
||||
},
|
||||
|
||||
resetSortParam: function() {
|
||||
this.set('sortParams', ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName']);
|
||||
this.set('paramsChanged', false);
|
||||
this.set('sortAsc', true);
|
||||
},
|
||||
|
||||
saveMeasurement: function(measurement, properties) {
|
||||
return this.attrs['save-measurement'](measurement, properties);
|
||||
},
|
||||
|
||||
deleteMeasurement: function(measurement) {
|
||||
return this.attrs['delete-measurement'](measurement);
|
||||
},
|
||||
},
|
||||
|
||||
});
|
|
@ -1,17 +1,17 @@
|
|||
{{#if canAdd}}
|
||||
<br>
|
||||
<button class="button-green smaller" {{action "addCharacteristic"}}>
|
||||
Add characteristic
|
||||
</button>
|
||||
<br><br>
|
||||
<br>
|
||||
<button class="button-green smaller" {{action "addCharacteristic"}}>
|
||||
Add characteristic
|
||||
</button>
|
||||
<br><br>
|
||||
{{/if}}
|
||||
|
||||
{{#if measurementsPresent}}
|
||||
{{#if paramsChanged}}
|
||||
<button class="button-gray smaller" {{action 'resetSortParam'}}>
|
||||
Reset sort
|
||||
</button>
|
||||
{{/if}}
|
||||
{{#if paramsChanged}}
|
||||
<button class="button-gray smaller" {{action 'resetSortParam'}}>
|
||||
Reset sort
|
||||
</button>
|
||||
{{/if}}
|
||||
<table class="flakes-table">
|
||||
<colgroup>
|
||||
{{#if canEdit}}
|
||||
|
@ -41,8 +41,11 @@
|
|||
<tbody>
|
||||
{{#each sortedMeasurements as |measurement|}}
|
||||
{{
|
||||
protected/strains/show/measurements-table-row
|
||||
row=measurement
|
||||
protected/strains/measurements-table-row
|
||||
measurement=measurement
|
||||
save-measurement=(action "saveMeasurement")
|
||||
delete-measurement=(action "deleteMeasurement")
|
||||
allCharacteristics=allCharacteristics
|
||||
canEdit=canEdit
|
||||
}}
|
||||
{{/each}}
|
|
@ -1,29 +1,10 @@
|
|||
import Ember from 'ember';
|
||||
import ajaxError from '../../../../utils/ajax-error';
|
||||
import SaveModel from '../../../../mixins/save-model';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
actions: {
|
||||
save: function() {
|
||||
let strain = this.get('strain');
|
||||
const { Controller } = Ember;
|
||||
|
||||
if (strain.get('hasDirtyAttributes')) {
|
||||
strain.save().then((strain) => {
|
||||
this.transitionToRoute('protected.strains.show', strain);
|
||||
}, () => {
|
||||
ajaxError(strain.get('errors'), this.get('flashMessages'));
|
||||
});
|
||||
} else {
|
||||
strain.destroyRecord().then(() => {
|
||||
this.transitionToRoute('protected.strains.index');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.get('strain').destroyRecord().then(() => {
|
||||
this.transitionToRoute('protected.strains.index');
|
||||
});
|
||||
},
|
||||
|
||||
},
|
||||
export default Controller.extend(SaveModel, {
|
||||
// Required for SaveModel mixin
|
||||
fallbackRouteSave: 'protected.strains.show',
|
||||
fallbackRouteCancel: 'protected.strains.index',
|
||||
});
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import ElevatedAccess from '../../../../mixins/elevated-access';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Route } = Ember;
|
||||
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
this.get('currentUser.account').then((user) => {
|
||||
if (user.get('isReader')) {
|
||||
this.transitionTo('protected.strains.index');
|
||||
}
|
||||
});
|
||||
},
|
||||
export default Route.extend(ElevatedAccess, {
|
||||
// Required for ElevatedAccess mixin
|
||||
fallbackRouteBefore: 'protected.strains.index',
|
||||
fallbackRouteAfter: 'protected.strains.show',
|
||||
|
||||
model: function() {
|
||||
return Ember.RSVP.hash({
|
||||
|
@ -19,19 +15,28 @@ export default Ember.Route.extend({
|
|||
});
|
||||
},
|
||||
|
||||
// Overriding afterModel because of RSVP hash
|
||||
afterModel: function(models) {
|
||||
if (!models.strain.get('isNew') && !models.strain.get('canEdit')) {
|
||||
this.transitionTo(this.get('fallbackRouteAfter'), models.strain.get('id'));
|
||||
}
|
||||
},
|
||||
|
||||
// Setting up controller because of RSVP hash
|
||||
setupController: function(controller, models) {
|
||||
controller.setProperties(models);
|
||||
controller.set('model', models.strain);
|
||||
controller.set('speciesList', models.species);
|
||||
},
|
||||
|
||||
actions: {
|
||||
// Overriding willTransition because of RSVP hash
|
||||
willTransition: function(/*transition*/) {
|
||||
const controller = this.get('controller');
|
||||
const strain = controller.get('strain');
|
||||
const model = controller.get('model');
|
||||
|
||||
if (strain.get('isNew')) {
|
||||
strain.destroyRecord();
|
||||
if (model.get('isNew')) {
|
||||
model.destroyRecord();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{
|
||||
protected/strains/strain-form
|
||||
strain=strain
|
||||
species=species
|
||||
save="save"
|
||||
cancel="cancel"
|
||||
strain=model
|
||||
speciesList=speciesList
|
||||
on-save=(action "save")
|
||||
on-cancel=(action "cancel")
|
||||
}}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import Ember from 'ember';
|
||||
import DeleteModel from '../../../../mixins/delete-model';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
actions: {
|
||||
delete: function() {
|
||||
this.get('model').destroyRecord().then(() => {
|
||||
this.transitionToRoute('protected.strains.index');
|
||||
});
|
||||
},
|
||||
},
|
||||
const { Controller } = Ember;
|
||||
|
||||
export default Controller.extend(DeleteModel, {
|
||||
// Required for DeleteModel mixin
|
||||
transitionRoute: 'protected.strains.index',
|
||||
});
|
||||
|
|
|
@ -1,47 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
import ajaxError from '../../../../../utils/ajax-error';
|
||||
|
||||
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
|
||||
this.set('characteristics', this.store.peekAll('characteristic'));
|
||||
this.toggleProperty('isEditing');
|
||||
},
|
||||
|
||||
save: function() {
|
||||
if (this.get('rowChanged')) {
|
||||
this.get('row').save().then(() => {
|
||||
this.get('flashMessages').clearMessages();
|
||||
this.toggleProperty('isEditing');
|
||||
}, () => {
|
||||
ajaxError(this.get('row.errors'), this.get('flashMessages'));
|
||||
});
|
||||
} else {
|
||||
this.toggleProperty('isEditing');
|
||||
}
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
let char = this.get('row.characteristic');
|
||||
if (char.get('isNew')) {
|
||||
char.destroyRecord();
|
||||
}
|
||||
this.get('row').destroyRecord();
|
||||
}
|
||||
|
||||
},
|
||||
});
|
|
@ -1,54 +0,0 @@
|
|||
{{#if isEditing}}
|
||||
<td></td>
|
||||
<td>
|
||||
{{
|
||||
select-2
|
||||
multiple=false
|
||||
content=characteristics
|
||||
value=row.characteristic
|
||||
optionLabelPath="characteristicName"
|
||||
}}
|
||||
</td>
|
||||
<td>
|
||||
{{input value=row.value}}
|
||||
</td>
|
||||
<td>
|
||||
{{input value=row.notes}}
|
||||
</td>
|
||||
{{#if canEdit}}
|
||||
<td>
|
||||
{{#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}}
|
||||
<td>
|
||||
{{{row.characteristic.characteristicTypeName}}}
|
||||
</td>
|
||||
<td>
|
||||
{{#link-to 'protected.characteristics.show' row.characteristic.id}}
|
||||
{{{row.characteristic.characteristicName}}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>
|
||||
{{row.value}}
|
||||
</td>
|
||||
<td>
|
||||
{{row.notes}}
|
||||
</td>
|
||||
{{#if canEdit}}
|
||||
<td>
|
||||
<button class="button-gray smaller" {{action 'edit'}}>
|
||||
Edit
|
||||
</button>
|
||||
{{delete-button delete=(action 'delete')}}
|
||||
</td>
|
||||
{{/if}}
|
||||
{{/if}}
|
|
@ -1,47 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
measurementsPresent: function() {
|
||||
return this.get('model.measurements.length') > 0;
|
||||
}.property('model.measurements'),
|
||||
|
||||
fetchCharacteristics: function() {
|
||||
if (this.get('canEdit')) {
|
||||
this.store.findAll('characteristic');
|
||||
}
|
||||
}.on('didInsertElement'),
|
||||
|
||||
sortParams: ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName'],
|
||||
sortAsc: true,
|
||||
paramsChanged: false,
|
||||
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);
|
||||
},
|
||||
|
||||
changeSortParam: function(col) {
|
||||
let sort = this.get('sortAsc') ? 'asc' : 'desc';
|
||||
let sortCol = `${col}:${sort}`;
|
||||
this.set('sortParams', [sortCol]);
|
||||
this.set('paramsChanged', true);
|
||||
this.toggleProperty('sortAsc');
|
||||
return false;
|
||||
},
|
||||
|
||||
resetSortParam: function() {
|
||||
this.set('sortParams', ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName']);
|
||||
this.set('paramsChanged', false);
|
||||
this.set('sortAsc', true);
|
||||
return false;
|
||||
},
|
||||
},
|
||||
|
||||
});
|
|
@ -1,8 +1,10 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
const { Route } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
model: function(params) {
|
||||
return this.store.findRecord('strain', params.strain_id, { reload: true });
|
||||
return this.store.findRecord('strain', params.strain_id);
|
||||
},
|
||||
|
||||
});
|
||||
|
|
14
app/pods/protected/strains/show/strain-card/component.js
Normal file
14
app/pods/protected/strains/show/strain-card/component.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
strain: null,
|
||||
"on-delete": null,
|
||||
|
||||
actions: {
|
||||
deleteStrain: function() {
|
||||
return this.attrs['on-delete']();
|
||||
},
|
||||
},
|
||||
});
|
105
app/pods/protected/strains/show/strain-card/template.hbs
Normal file
105
app/pods/protected/strains/show/strain-card/template.hbs
Normal file
|
@ -0,0 +1,105 @@
|
|||
<div class="span-1">
|
||||
<fieldset class="flakes-information-box">
|
||||
<legend>
|
||||
{{strain.strainNameMU}}
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Species</dt>
|
||||
<dd>
|
||||
{{#link-to 'protected.species.show' strain.species.id}}
|
||||
<em>{{strain.species.speciesNameMU}}</em>
|
||||
{{/link-to}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Type Strain?</dt>
|
||||
<dd>
|
||||
{{if strain.typeStrain 'Yes' 'No'}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-3 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Accession Numbers</dt>
|
||||
<dd>
|
||||
{{strain.accessionNumbers}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Genbank</dt>
|
||||
<dd>
|
||||
{{genbank-url genbank=strain.genbank}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Whole Genome Sequence</dt>
|
||||
<dd>
|
||||
{{genbank-url genbank=strain.wholeGenomeSequence}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 3 }}
|
||||
<div class="grid-1 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Isolated From</dt>
|
||||
<dd>
|
||||
{{{strain.isolatedFrom}}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 4 }}
|
||||
<div class="grid-1 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Notes</dt>
|
||||
<dd>
|
||||
{{#if strain.notes}}
|
||||
{{{strain.notes}}}
|
||||
{{else}}
|
||||
No notes.
|
||||
{{/if}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 5 }}
|
||||
<div class="grid-1 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Characteristics</dt>
|
||||
<dd>
|
||||
{{
|
||||
protected/strains/measurements-table
|
||||
strain=strain
|
||||
canEdit=false
|
||||
canAdd=false
|
||||
}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 6 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Record Created</dt>
|
||||
<dd>{{null-time strain.createdAt 'LL'}}</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Record Updated</dt>
|
||||
<dd>{{null-time strain.updatedAt 'LL'}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{#if strain.canEdit}}
|
||||
<br>
|
||||
{{#link-to 'protected.strains.edit' strain.id class="button-gray smaller"}}
|
||||
Edit
|
||||
{{/link-to}}
|
||||
{{delete-button delete=(action 'deleteStrain')}}
|
||||
{{/if}}
|
|
@ -1,105 +1,5 @@
|
|||
<div class="span-1">
|
||||
<fieldset class="flakes-information-box">
|
||||
<legend>
|
||||
Strain {{model.strainNameMU}}
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Species</dt>
|
||||
<dd>
|
||||
{{#link-to 'protected.species.show' model.species.id}}
|
||||
<em>{{model.species.speciesNameMU}}</em>
|
||||
{{/link-to}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Type Strain?</dt>
|
||||
<dd>
|
||||
{{if model.typeStrain 'Yes' 'No'}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-3 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Accession Numbers</dt>
|
||||
<dd>
|
||||
{{model.accessionNumbers}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Genbank</dt>
|
||||
<dd>
|
||||
{{genbank-url genbank=model.genbank}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Whole Genome Sequence</dt>
|
||||
<dd>
|
||||
{{genbank-url genbank=model.wholeGenomeSequence}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 3 }}
|
||||
<div class="grid-1 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Isolated From</dt>
|
||||
<dd>
|
||||
{{{model.isolatedFrom}}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 4 }}
|
||||
<div class="grid-1 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Notes</dt>
|
||||
<dd>
|
||||
{{#if model.notes}}
|
||||
{{{model.notes}}}
|
||||
{{else}}
|
||||
No notes.
|
||||
{{/if}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 5 }}
|
||||
<div class="grid-1 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Characteristics</dt>
|
||||
<dd>
|
||||
{{
|
||||
protected/strains/show/measurements-table
|
||||
model=model
|
||||
canEdit=false
|
||||
canAdd=false
|
||||
}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 6 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Record Created</dt>
|
||||
<dd>{{null-time model.createdAt 'LL'}}</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Record Updated</dt>
|
||||
<dd>{{null-time model.updatedAt 'LL'}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
{{#if model.canEdit}}
|
||||
<br>
|
||||
{{#link-to 'protected.strains.edit' model.id class="button-gray smaller"}}
|
||||
Edit
|
||||
{{/link-to}}
|
||||
{{delete-button delete=(action 'delete')}}
|
||||
{{/if}}
|
||||
{{
|
||||
protected/strains/show/strain-card
|
||||
strain=model
|
||||
on-delete=(action 'delete')
|
||||
}}
|
||||
|
|
|
@ -1,21 +1,106 @@
|
|||
import Ember from 'ember';
|
||||
import SetupMetaData from '../../../../mixins/setup-metadata';
|
||||
|
||||
const { Component } = Ember;
|
||||
|
||||
export default Component.extend(SetupMetaData, {
|
||||
// Read-only attributes
|
||||
strain: null,
|
||||
isNew: null,
|
||||
isDirty: false,
|
||||
speciesList: null,
|
||||
allCharacteristics: null,
|
||||
|
||||
// Actions
|
||||
"on-save": null,
|
||||
"on-cancel": null,
|
||||
"on-update": null,
|
||||
"add-characteristic": null,
|
||||
"save-measurement": null,
|
||||
"delete-measurement": null,
|
||||
|
||||
// Property mapping
|
||||
propertiesList: ['strainName', 'typeStrain', 'species', 'isolatedFrom', 'accessionNumbers', 'genbank', 'wholeGenomeSequence', 'notes'],
|
||||
strainName: null,
|
||||
typeStrain: null,
|
||||
species: null,
|
||||
isolatedFrom: null,
|
||||
accessionNumbers: null,
|
||||
genbank: null,
|
||||
wholeGenomeSequence: null,
|
||||
notes: null,
|
||||
|
||||
resetOnInit: Ember.on('init', function() {
|
||||
this.get('propertiesList').forEach((field) => {
|
||||
const valueInStrain = this.get('strain').get(field);
|
||||
this.set(field, valueInStrain);
|
||||
});
|
||||
// Read-only attributes
|
||||
this.set('isNew', this.get('strain.isNew'));
|
||||
}),
|
||||
|
||||
updateField: function(property, value) {
|
||||
this.set(property, value);
|
||||
// Manually compare against passed in value
|
||||
if (this.get('strain').get(property) !== value) {
|
||||
this.set('isDirty', true);
|
||||
} else {
|
||||
this.set('isDirty', false);
|
||||
}
|
||||
},
|
||||
|
||||
export default Ember.Component.extend({
|
||||
actions: {
|
||||
save: function() {
|
||||
this.sendAction('save');
|
||||
return this.attrs['on-save'](this.getProperties(this.get('propertiesList')));
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.sendAction('cancel');
|
||||
return this.attrs['on-cancel']();
|
||||
},
|
||||
|
||||
addCharacteristic: function() {
|
||||
return this.attrs['add-characteristic']();
|
||||
},
|
||||
|
||||
saveMeasurement: function(measurement, properties) {
|
||||
return this.attrs['save-measurement'](measurement, properties);
|
||||
},
|
||||
|
||||
deleteMeasurement: function(measurement) {
|
||||
return this.attrs['delete-measurement'](measurement);
|
||||
},
|
||||
|
||||
strainNameDidChange: function(value) {
|
||||
this.updateField('strainName', value);
|
||||
},
|
||||
|
||||
typeStrainDidChange: function() {
|
||||
this.updateField('typeStrain', !this.get('typeStrain'));
|
||||
},
|
||||
|
||||
speciesDidChange: function(value) {
|
||||
const newSpecies = this.get('speciesList').findBy('id', value);
|
||||
this.updateField('species', newSpecies);
|
||||
},
|
||||
|
||||
isolatedFromDidChange: function(value) {
|
||||
this.set('strain.isolatedFrom', value);
|
||||
this.updateField('isolatedFrom', value);
|
||||
},
|
||||
|
||||
accessionNumbersNameDidChange: function(value) {
|
||||
this.updateField('accessionNumbers', value);
|
||||
},
|
||||
|
||||
genbankDidChange: function(value) {
|
||||
this.updateField('genbank', value);
|
||||
},
|
||||
|
||||
wholeGenomeSequenceDidChange: function(value) {
|
||||
this.updateField('wholeGenomeSequence', value);
|
||||
},
|
||||
|
||||
notesDidChange: function(value) {
|
||||
this.set('strain.notes', value);
|
||||
this.updateField('strain.notes', value);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,68 +1,72 @@
|
|||
<form class="grid-form" {{action 'save' on='submit'}}>
|
||||
<fieldset>
|
||||
<legend><em>{{strain.strainName}}</em></legend>
|
||||
<legend><em>{{strainName}}</em></legend>
|
||||
<div data-row-span="2">
|
||||
<div data-field-span="1">
|
||||
<label>Strain Name</label>
|
||||
{{input value=strain.strainName}}
|
||||
{{one-way-input type="text" class="strain-name" value=strainName update=(action "strainNameDidChange")}}
|
||||
</div>
|
||||
<div data-field-span="1">
|
||||
<label>Type Strain?</label>
|
||||
{{input type="checkbox" checked=strain.typeStrain}} {{if strain.typeStrain 'Yes' 'No'}}
|
||||
<input type="checkbox" checked={{typeStrain}} value="{{typeStrain}}" onchange={{action "typeStrainDidChange"}}>
|
||||
{{if typeStrain 'Yes' 'No'}}
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="2">
|
||||
<div data-field-span="2">
|
||||
<label>Species</label>
|
||||
{{
|
||||
select-2
|
||||
content=species
|
||||
optionLabelPath="speciesName"
|
||||
value=strain.species
|
||||
}}
|
||||
<select onchange={{action "speciesDidChange" value="target.value"}}>
|
||||
{{#each speciesList as |speciesChoice|}}
|
||||
<option value={{speciesChoice.id}} selected={{equal species.id speciesChoice.id}}>{{speciesChoice.speciesName}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="2">
|
||||
<div data-field-span="2">
|
||||
<label>Isolated From</label>
|
||||
{{text-editor value=strain.isolatedFrom update=(action "isolatedFromDidChange")}}
|
||||
{{text-editor value=isolatedFrom update=(action "isolatedFromDidChange")}}
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="3">
|
||||
<div data-field-span="1">
|
||||
<label>Accession Numbers</label>
|
||||
{{input value=strain.accessionNumbers}}
|
||||
{{one-way-input type="text" class="accession-numbers" value=accessionNumbers update=(action "accessionNumbersNameDidChange")}}
|
||||
</div>
|
||||
<div data-field-span="1">
|
||||
<label>GenBank</label>
|
||||
{{input value=strain.genbank}}
|
||||
{{one-way-input type="text" class="genbank" value=genbank update=(action "genbankDidChange")}}
|
||||
</div>
|
||||
<div data-field-span="1">
|
||||
<label>Whole Genome Sequence</label>
|
||||
{{input value=strain.wholeGenomeSequence}}
|
||||
{{one-way-input type="text" class="whole-genome-sequenc" value=wholeGenomeSequence update=(action "wholeGenomeSequenceDidChange")}}
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="2">
|
||||
<div data-field-span="2">
|
||||
<label>Notes</label>
|
||||
{{text-editor value=strain.notes update=(action "notesDidChange")}}
|
||||
{{text-editor value=notes update=(action "notesDidChange")}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<div>
|
||||
{{
|
||||
protected/strains/show/measurements-table
|
||||
model=strain
|
||||
protected/strains/measurements-table
|
||||
strain=strain
|
||||
add-characteristic=(action "addCharacteristic")
|
||||
allCharacteristics=allCharacteristics
|
||||
save-measurement=(action "saveMeasurement")
|
||||
delete-measurement=(action "deleteMeasurement")
|
||||
canEdit=strain.canEdit
|
||||
canAdd=canAdd
|
||||
canAdd=metaData.canAdd
|
||||
}}
|
||||
</div>
|
||||
<br>
|
||||
<a class="button-red smaller" {{action 'cancel'}}>
|
||||
Cancel
|
||||
</a>
|
||||
{{#if strain.hasDirtyAttributes}}
|
||||
<button type="submit" class="button-green smaller">
|
||||
{{#if isDirty}}
|
||||
<button type="submit" class="button-green smaller save-strain">
|
||||
Save
|
||||
</button>
|
||||
{{/if}}
|
||||
|
|
|
@ -1,33 +1,23 @@
|
|||
import Ember from 'ember';
|
||||
import ajaxRequest from '../../../../utils/ajax-request';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
session: Ember.inject.service('session'),
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Controller, inject: { service } } = Ember;
|
||||
|
||||
passwordConfirm: null,
|
||||
export default Controller.extend({
|
||||
session: service(),
|
||||
ajax: service(),
|
||||
currentUser: service('session-account'),
|
||||
|
||||
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: {
|
||||
id: this.get('currentUser.account.id'),
|
||||
password: this.get('password'),
|
||||
},
|
||||
};
|
||||
ajaxRequest(url, options, this.get('session'));
|
||||
this.transitionToRoute('protected.users.index');
|
||||
save: function(password) {
|
||||
const id = this.get('currentUser.account.id');
|
||||
const data = { id: id, password: password };
|
||||
this.get('ajax').post('/users/password', { data: data });
|
||||
this.transitionToRoute('protected.users.show', id);
|
||||
this.get('flashMessages').information('Your password has been changed.');
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.transitionToRoute('protected.users.show', this.get('currentUser.account.id'));
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
password: null,
|
||||
passwordConfirm: null,
|
||||
matches: false,
|
||||
|
||||
// Actions
|
||||
"on-save": null,
|
||||
"on-cancel": null,
|
||||
|
||||
updateField: function(property, value) {
|
||||
this.set(property, value);
|
||||
this.verifyPassword(this.get('password'), this.get('passwordConfirm'));
|
||||
},
|
||||
|
||||
verifyPassword: function(password, passwordConfirm) {
|
||||
if (password && passwordConfirm) {
|
||||
if (password !== passwordConfirm) {
|
||||
this.get('flashMessages').clearMessages();
|
||||
this.get('flashMessages').error("Password fields don't match");
|
||||
this.set('matches', false);
|
||||
} else {
|
||||
this.get('flashMessages').clearMessages();
|
||||
this.set('matches', true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
save: function() {
|
||||
this.verifyPassword(this.get('password'), this.get('passwordConfirm'));
|
||||
if (this.get('matches')) {
|
||||
return this.attrs['on-save'](this.get('password'));
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
return this.attrs['on-cancel']();
|
||||
},
|
||||
|
||||
passwordDidChange: function(value) {
|
||||
this.updateField('password', value);
|
||||
},
|
||||
|
||||
passwordConfirmDidChange: function(value) {
|
||||
this.updateField('passwordConfirm', value);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
<div class="grid-1">
|
||||
<div class="span-1">
|
||||
<fieldset>
|
||||
<legend>Change password</legend>
|
||||
<form {{action 'save' on='submit'}}>
|
||||
<ul>
|
||||
<li>
|
||||
<label>New Password</label>
|
||||
{{one-way-input type="password" class="password" value=password update=(action "passwordDidChange")}}
|
||||
</li>
|
||||
<li>
|
||||
<label>New Password (confirm)</label>
|
||||
{{one-way-input type="password" class="password-confirm" value=passwordConfirm update=(action "passwordConfirmDidChange")}}
|
||||
</li>
|
||||
<li>
|
||||
<a class="button-red smaller" {{action 'cancel'}}>
|
||||
Cancel
|
||||
</a>
|
||||
{{#if matches}}
|
||||
<button type="submit" class="button-green smaller submit-password">
|
||||
Submit
|
||||
</button>
|
||||
{{/if}}
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
|
@ -1,16 +1,19 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Route, inject: { service } } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
currentUser: service('session-account'),
|
||||
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
|
||||
let user_id = transition.params['protected.users.changepassword'].user_id;
|
||||
// Only the logged in user can change their password
|
||||
const user_id = transition.params['protected.users.changepassword'].user_id;
|
||||
|
||||
this.get('currentUser.account').then((user) => {
|
||||
if (user.get('id') !== user_id) {
|
||||
this.transitionTo('protected.users.index');
|
||||
this.transitionTo('protected.users.show', user.get('id'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,24 +1,5 @@
|
|||
<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>
|
||||
{{
|
||||
protected/users/changepassword/password-form
|
||||
on-save=(action "save")
|
||||
on-cancel=(action "cancel")
|
||||
}}
|
||||
|
|
|
@ -1,40 +1,10 @@
|
|||
import Ember from 'ember';
|
||||
import ajaxError from '../../../../utils/ajax-error';
|
||||
import SaveModel from '../../../../mixins/save-model';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
actions: {
|
||||
save: function() {
|
||||
let user = this.get('model');
|
||||
const { Controller } = Ember;
|
||||
|
||||
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);
|
||||
}, () => {
|
||||
ajaxError(user.get('errors'), this.get('flashMessages'));
|
||||
});
|
||||
} 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);
|
||||
},
|
||||
|
||||
},
|
||||
export default Controller.extend(SaveModel, {
|
||||
// Required for SaveModel mixin
|
||||
fallbackRouteSave: 'protected.users.show',
|
||||
fallbackRouteCancel: 'protected.users.show',
|
||||
});
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Route, inject: { service } } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
currentUser: service('session-account'),
|
||||
|
||||
// Not using ElevatedAccess Mixin because the rules for viewing user accounts
|
||||
// is slightly different.
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
|
||||
|
@ -16,7 +20,7 @@ export default Ember.Route.extend({
|
|||
},
|
||||
|
||||
model: function(params) {
|
||||
return this.store.findRecord('user', params.user_id, { reload: true });
|
||||
return this.store.findRecord('user', params.user_id);
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{{
|
||||
protected/users/user-form
|
||||
user=model
|
||||
currentUser=currentUser.account
|
||||
save="save"
|
||||
cancel="cancel"
|
||||
on-save=(action "save")
|
||||
on-cancel=(action "cancel")
|
||||
}}
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Route, inject: { service } } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
currentUser: service('session-account'),
|
||||
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
|
|
|
@ -1,33 +1,6 @@
|
|||
<h2>{{genus-name}} Users</h2>
|
||||
<h3>Total users: {{model.length}}</h3>
|
||||
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Date Registered</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each model as |row|}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#link-to 'protected.users.show' row}}
|
||||
{{row.name}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>
|
||||
{{row.email}}
|
||||
</td>
|
||||
<td>
|
||||
{{row.fullRole}}
|
||||
</td>
|
||||
<td>
|
||||
{{null-time row.createdAt 'LL'}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{
|
||||
protected/users/index/users-table
|
||||
users=model
|
||||
}}
|
||||
|
|
7
app/pods/protected/users/index/users-table/component.js
Normal file
7
app/pods/protected/users/index/users-table/component.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
users: null,
|
||||
});
|
33
app/pods/protected/users/index/users-table/template.hbs
Normal file
33
app/pods/protected/users/index/users-table/template.hbs
Normal file
|
@ -0,0 +1,33 @@
|
|||
|
||||
<h3 id="total-users">Total users: {{users.length}}</h3>
|
||||
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Date Registered</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each users as |user|}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#link-to 'protected.users.show' user}}
|
||||
{{user.name}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>
|
||||
{{user.email}}
|
||||
</td>
|
||||
<td>
|
||||
{{user.fullRole}}
|
||||
</td>
|
||||
<td>
|
||||
{{null-time user.createdAt 'LL'}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
|
@ -1,9 +1,9 @@
|
|||
import Ember from 'ember';
|
||||
import DeleteModel from '../../../../mixins/delete-model';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Controller } = Ember;
|
||||
|
||||
isUser: Ember.computed('model.id', 'currentUser.account.id', function() {
|
||||
return this.get('model.id') === this.get('currentUser.account.id');
|
||||
}),
|
||||
export default Controller.extend(DeleteModel, {
|
||||
// Required for DeleteModel mixin
|
||||
transitionRoute: 'protected.index',
|
||||
});
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
currentUser: Ember.inject.service('session-account'),
|
||||
const { Route, inject: { service } } = Ember;
|
||||
|
||||
export default Route.extend({
|
||||
currentUser: service('session-account'),
|
||||
|
||||
// Not using ElevatedAccess Mixin because the rules for viewing user accounts
|
||||
// is slightly different.
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
|
||||
this.get('currentUser.account').then((currentUser) => {
|
||||
let user_id = transition.params['protected.users.show'].user_id;
|
||||
if (!currentUser.get('isAdmin') && currentUser.get('id') !== user_id) {
|
||||
this.get('currentUser.account').then((user) => {
|
||||
const user_id = transition.params['protected.users.show'].user_id;
|
||||
if (!user.get('isAdmin') && user.get('id') !== user_id) {
|
||||
this.transitionTo('protected.users.index');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
model: function(params) {
|
||||
return this.store.findRecord('user', params.user_id, { reload: true });
|
||||
return this.store.findRecord('user', params.user_id);
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -1,53 +1,5 @@
|
|||
<div class="span-1">
|
||||
<fieldset class="flakes-information-box">
|
||||
<legend>
|
||||
{{model.name}}
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Email</dt>
|
||||
<dd>
|
||||
{{model.email}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Role</dt>
|
||||
<dd>
|
||||
{{model.fullRole}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Record Created</dt>
|
||||
<dd>{{null-time model.createdAt 'LL'}}</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Record Updated</dt>
|
||||
<dd>{{null-time model.updatedAt 'LL'}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<br>
|
||||
<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>
|
||||
{{
|
||||
protected/users/show/user-card
|
||||
user=model
|
||||
on-delete=(action 'delete')
|
||||
}}
|
||||
|
|
13
app/pods/protected/users/show/user-card/component.js
Normal file
13
app/pods/protected/users/show/user-card/component.js
Normal file
|
@ -0,0 +1,13 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component, computed, inject: { service } } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
currentUser: service('session-account'),
|
||||
|
||||
user: null,
|
||||
|
||||
isUser: computed('user.id', 'currentUser.account.id', function() {
|
||||
return this.get('user.id') === this.get('currentUser.account.id');
|
||||
}),
|
||||
});
|
53
app/pods/protected/users/show/user-card/template.hbs
Normal file
53
app/pods/protected/users/show/user-card/template.hbs
Normal file
|
@ -0,0 +1,53 @@
|
|||
<div class="span-1">
|
||||
<fieldset class="flakes-information-box">
|
||||
<legend>
|
||||
{{user.name}}
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Email</dt>
|
||||
<dd>
|
||||
{{user.email}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Role</dt>
|
||||
<dd>
|
||||
{{user.fullRole}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Record Created</dt>
|
||||
<dd>{{null-time user.createdAt 'LL'}}</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Record Updated</dt>
|
||||
<dd>{{null-time user.updatedAt 'LL'}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<br>
|
||||
<div class="grid-2 gutter-20">
|
||||
{{#if isUser}}
|
||||
<div class="span-1">
|
||||
{{#link-to 'protected.users.changepassword' user.id class="button-gray smaller"}}
|
||||
Change Password
|
||||
{{/link-to}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
<div class="span-1">
|
||||
{{#if user.canEdit}}
|
||||
{{#link-to 'protected.users.edit' user.id class="button-gray smaller"}}
|
||||
Edit
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
|
@ -1,15 +1,61 @@
|
|||
import Ember from 'ember';
|
||||
import SetupMetaData from '../../../../mixins/setup-metadata';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const { Component } = Ember;
|
||||
|
||||
export default Component.extend(SetupMetaData, {
|
||||
// Read-only attributes
|
||||
user: null,
|
||||
isDirty: false,
|
||||
roles: Ember.String.w('A R W'),
|
||||
|
||||
// Actions
|
||||
"on-save": null,
|
||||
"on-cancel": null,
|
||||
"on-update": null,
|
||||
|
||||
// Property mapping
|
||||
propertiesList: ['name', 'email', 'role'],
|
||||
name: null,
|
||||
email: null,
|
||||
role: null,
|
||||
|
||||
resetOnInit: Ember.on('init', function() {
|
||||
this.get('propertiesList').forEach((field) => {
|
||||
const valueInUser = this.get('user').get(field);
|
||||
this.set(field, valueInUser);
|
||||
});
|
||||
}),
|
||||
|
||||
updateField: function(property, value) {
|
||||
this.set(property, value);
|
||||
// Manually compare against passed in value
|
||||
if (this.get('user').get(property) !== value) {
|
||||
this.set('isDirty', true);
|
||||
} else {
|
||||
this.set('isDirty', false);
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
save: function() {
|
||||
this.sendAction('save');
|
||||
return this.attrs['on-save'](this.getProperties(this.get('propertiesList')));
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.sendAction('cancel');
|
||||
return this.attrs['on-cancel']();
|
||||
},
|
||||
}
|
||||
|
||||
nameDidChange: function(value) {
|
||||
this.updateField('name', value);
|
||||
},
|
||||
|
||||
emailDidChange: function(value) {
|
||||
this.updateField('email', value);
|
||||
},
|
||||
|
||||
roleDidChange: function(value) {
|
||||
this.updateField('role', value);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
<form class="grid-form" {{action 'save' on='submit'}}>
|
||||
<fieldset>
|
||||
<legend><em>{{user.name}}</em></legend>
|
||||
<legend><em>{{name}}</em></legend>
|
||||
<div data-row-span="1">
|
||||
<div data-field-span="1">
|
||||
<label>Name</label>
|
||||
{{input value=user.name}}
|
||||
{{one-way-input type="text" class="user-name" value=name update=(action "nameDidChange")}}
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="1">
|
||||
<div data-field-span="1">
|
||||
<label>Email</label>
|
||||
{{input value=user.email}}
|
||||
{{one-way-input type="text" class="email" value=email update=(action "emailDidChange")}}
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="1">
|
||||
<div data-field-span="1">
|
||||
<label>Role</label>
|
||||
{{#if session.currentUser.isAdmin}}
|
||||
<select onchange={{action (mut user.role) value="target.value"}}>
|
||||
{{#if isAdmin}}
|
||||
<select onchange={{action "roleDidChange" value="target.value"}}>
|
||||
{{#each roles as |roleChoice|}}
|
||||
<option value={{roleChoice}} selected={{equal user.role roleChoice}}>{{roleChoice}}</option>
|
||||
<option value={{roleChoice}} selected={{equal role roleChoice}}>{{roleChoice}}</option>
|
||||
{{/each}}
|
||||
</select>
|
||||
{{else}}
|
||||
{{user.role}}
|
||||
{{role}} {{!-- Not editable --}}
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,8 +32,8 @@
|
|||
<a class="button-red smaller" {{action 'cancel'}}>
|
||||
Cancel
|
||||
</a>
|
||||
{{#if user.hasDirtyAttributes}}
|
||||
<button type="submit" class="button-green smaller">
|
||||
{{#if isDirty}}
|
||||
<button type="submit" class="button-green smaller save-user">
|
||||
Save
|
||||
</button>
|
||||
{{/if}}
|
||||
|
|
Reference in a new issue