Merge pull request #44 from thermokarst/species-refactor
Species refactor
This commit is contained in:
commit
6881d57786
47 changed files with 528 additions and 256 deletions
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"predef": [
|
||||
"server",
|
||||
"document",
|
||||
"window",
|
||||
"-Promise",
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
---
|
||||
language: node_js
|
||||
node_js:
|
||||
- "0.12"
|
||||
|
||||
sudo: false
|
||||
|
||||
|
@ -10,7 +8,12 @@ cache:
|
|||
- node_modules
|
||||
|
||||
before_install:
|
||||
- export PATH=/usr/local/phantomjs-2.0.0/bin:$PATH
|
||||
- mkdir -p ~/.nvm/versions
|
||||
- source ~/.nvm/nvm.sh && nvm install 0.12.2
|
||||
- mkdir travis-phantomjs
|
||||
- wget https://s3.amazonaws.com/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -O $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2
|
||||
- tar -xvf $PWD/travis-phantomjs/phantomjs-2.0.0-ubuntu-12.04.tar.bz2 -C $PWD/travis-phantomjs
|
||||
- export PATH=$PWD/travis-phantomjs:$PATH
|
||||
- "npm config set spin false"
|
||||
- "npm install -g npm@^2"
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ export default BaseAuthenticator.extend({
|
|||
return RSVP.resolve(data);
|
||||
},
|
||||
|
||||
invalidate: function(data) {
|
||||
invalidate: function(/* data */) {
|
||||
return RSVP.resolve();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Ember from 'ember';
|
||||
import { formatDate } from '../utils/date-helpers';
|
||||
import formatDate from '../utils/date-helpers';
|
||||
|
||||
export default Ember.Helper.helper(function(params) {
|
||||
let value = params[0],
|
||||
|
|
19
app/mirage/config.js
Normal file
19
app/mirage/config.js
Normal file
|
@ -0,0 +1,19 @@
|
|||
export default function() {
|
||||
// Don't use mirage for development (for now)
|
||||
this.urlPrefix = 'http://127.0.0.1:8901';
|
||||
this.namespace = '/api';
|
||||
this.passthrough();
|
||||
}
|
||||
|
||||
export function testConfig() {
|
||||
this.urlPrefix = 'https://bactdb-test.herokuapp.com';
|
||||
this.namespace = '/api/hymenobacter';
|
||||
this.timing = 0;
|
||||
|
||||
this.get('/users/:id');
|
||||
|
||||
this.get('/species');
|
||||
this.post('/species');
|
||||
this.get('/species/:id');
|
||||
this.put('/species/:id');
|
||||
}
|
12
app/mirage/factories/species.js
Normal file
12
app/mirage/factories/species.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Mirage, { faker } from 'ember-cli-mirage';
|
||||
|
||||
export default Mirage.Factory.extend({
|
||||
speciesName() { return faker.lorem.words().join(' '); },
|
||||
typeSpecies: faker.random.boolean(),
|
||||
etymology: faker.lorem.sentences(),
|
||||
genusName: 'hymenobacter',
|
||||
strains: [],
|
||||
totalStrains: 0,
|
||||
sortOrder: faker.random.number(),
|
||||
canEdit: faker.random.boolean(),
|
||||
});
|
8
app/mirage/factories/users.js
Normal file
8
app/mirage/factories/users.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import Mirage, { faker } from 'ember-cli-mirage';
|
||||
|
||||
export default Mirage.Factory.extend({
|
||||
name() { return faker.name.firstName() + ' ' + faker.name.lastName(); },
|
||||
email: faker.internet.email,
|
||||
role: 'R',
|
||||
canEdit: false,
|
||||
});
|
7
app/mirage/scenarios/default.js
Normal file
7
app/mirage/scenarios/default.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
export default function(/* server */) {
|
||||
|
||||
// Seed your development database using your factories. This
|
||||
// data will not be loaded in your tests.
|
||||
|
||||
// server.createList('contact', 10);
|
||||
}
|
15
app/mixins/delete-model.js
Normal file
15
app/mixins/delete-model.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Mixin } = Ember;
|
||||
|
||||
export default Mixin.create({
|
||||
transitionRoute: null,
|
||||
|
||||
actions: {
|
||||
delete: function() {
|
||||
this.get('model').destroyRecord().then(() => {
|
||||
this.transitionToRoute(this.get('transitionRoute'));
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
36
app/mixins/elevated-access.js
Normal file
36
app/mixins/elevated-access.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Mixin , inject: { service } } = Ember;
|
||||
|
||||
export default Mixin.create({
|
||||
currentUser: service('session-account'),
|
||||
|
||||
fallbackRouteBefore: null,
|
||||
fallbackRouteAfter: null,
|
||||
|
||||
beforeModel: function(transition) {
|
||||
this._super(transition);
|
||||
this.get('currentUser.account').then((user) => {
|
||||
if (user.get('isReader')) {
|
||||
this.transitionTo(this.get('fallbackRouteBefore'));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
afterModel: function(model) {
|
||||
if (!model.get('isNew') && !model.get('canEdit')) {
|
||||
this.transitionTo(this.get('fallbackRouteAfter'), model.get('id'));
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
willTransition: function(/*transition*/) {
|
||||
const controller = this.get('controller');
|
||||
const model = controller.get('model');
|
||||
|
||||
if (model.get('isNew')) {
|
||||
model.destroyRecord();
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
36
app/mixins/save-model.js
Normal file
36
app/mixins/save-model.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
import Ember from 'ember';
|
||||
import ajaxError from '../utils/ajax-error';
|
||||
|
||||
const { Mixin } = Ember;
|
||||
|
||||
export default Mixin.create({
|
||||
fallbackRoute: null,
|
||||
|
||||
actions: {
|
||||
save: function(properties) {
|
||||
const model = this.get('model');
|
||||
const fallbackRoute = this.get('fallbackRoute');
|
||||
|
||||
model.setProperties(properties);
|
||||
|
||||
if (model.get('hasDirtyAttributes')) {
|
||||
model.save().then((model) => {
|
||||
this.transitionToRoute(fallbackRoute, model);
|
||||
}, () => {
|
||||
ajaxError(model.get('errors'), this.get('flashMessages'));
|
||||
});
|
||||
} else {
|
||||
this.transitionToRoute(fallbackRoute, model);
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
const model = this.get('model');
|
||||
|
||||
model.get('errors').clear();
|
||||
model.rollbackAttributes();
|
||||
|
||||
this.transitionToRoute(this.get('fallbackRoute'), model);
|
||||
},
|
||||
},
|
||||
});
|
|
@ -3,7 +3,8 @@ import Ember from 'ember';
|
|||
|
||||
export default Ember.Component.extend({
|
||||
quill: null,
|
||||
value: null, // passed in
|
||||
value: null,
|
||||
update: null,
|
||||
|
||||
didInsertElement: function() {
|
||||
let quill = new Quill(`#${this.get('elementId')} .editor`, {
|
||||
|
@ -22,11 +23,10 @@ export default Ember.Component.extend({
|
|||
|
||||
quill.on('text-change', (delta, source) => {
|
||||
if (source === 'user') {
|
||||
this.set('value', Ember.$(quill.getHTML()).html());
|
||||
this.attrs['update'](Ember.$(quill.getHTML()).html());
|
||||
}
|
||||
});
|
||||
|
||||
this.set('quill', quill);
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -1,30 +1,9 @@
|
|||
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 species = this.get('model');
|
||||
const { Controller } = Ember;
|
||||
|
||||
if (species.get('hasDirtyAttributes')) {
|
||||
species.save().then((species) => {
|
||||
this.transitionToRoute('protected.species.show', species);
|
||||
}, () => {
|
||||
ajaxError(species.get('errors'), this.get('flashMessages'));
|
||||
});
|
||||
} else {
|
||||
this.transitionToRoute('protected.species.show', species);
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
let species = this.get('model');
|
||||
|
||||
species.get('errors').clear();
|
||||
species.rollbackAttributes();
|
||||
|
||||
this.transitionToRoute('protected.species.show', species);
|
||||
},
|
||||
|
||||
},
|
||||
export default Controller.extend(SaveModel, {
|
||||
// Required for SaveModel mixin
|
||||
fallbackRoute: 'protected.species.show',
|
||||
});
|
||||
|
|
|
@ -1,28 +1,15 @@
|
|||
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.species.index');
|
||||
}
|
||||
});
|
||||
},
|
||||
export default Route.extend(ElevatedAccess, {
|
||||
// Required for ElevatedAccess mixin
|
||||
fallbackRouteBefore: 'protected.species.index',
|
||||
fallbackRouteAfter: 'protected.species.show',
|
||||
|
||||
afterModel: function(species) {
|
||||
if (!species.get('canEdit')) {
|
||||
this.transitionTo('species.show', species.get('id'));
|
||||
}
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.set('model', model);
|
||||
this.get('currentUser.account').then((user) => {
|
||||
controller.set('metaData', user.get('metaData'));
|
||||
});
|
||||
model: function(params) {
|
||||
return this.store.findRecord('species', params.species_id);
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{{
|
||||
protected/species/species-form
|
||||
species=model
|
||||
metaData=metaData
|
||||
save="save"
|
||||
cancel="cancel"
|
||||
on-save=(action "save")
|
||||
on-cancel=(action "cancel")
|
||||
}}
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
sortParams: ['speciesName', 'strainCount'],
|
||||
sortedSpecies: 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('species');
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.set('model', model);
|
||||
this.get('currentUser.account').then((user) => {
|
||||
controller.set('metaData', user.get('metaData'));
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
|
20
app/pods/protected/species/index/species-table/component.js
Normal file
20
app/pods/protected/species/index/species-table/component.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component, inject: { service }} = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
currentUser: service('session-account'),
|
||||
|
||||
metaData: null,
|
||||
species: null,
|
||||
|
||||
setupMetaDataOnInit: Ember.on('init', function() {
|
||||
this.get('currentUser.account').then((user) => {
|
||||
this.set('metaData', user.get('metaData'));
|
||||
});
|
||||
}),
|
||||
|
||||
sortParams: ['speciesName', 'strainCount'],
|
||||
sortedSpecies: Ember.computed.sort('species', 'sortParams'),
|
||||
|
||||
});
|
33
app/pods/protected/species/index/species-table/template.hbs
Normal file
33
app/pods/protected/species/index/species-table/template.hbs
Normal file
|
@ -0,0 +1,33 @@
|
|||
<h3 id="total-species">Total species: {{species.length}}</h3>
|
||||
|
||||
{{add-button label="Add Species" link="protected.species.new" canAdd=metaData.canAdd}}
|
||||
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Strains</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each sortedSpecies as |species|}}
|
||||
<tr>
|
||||
<td>
|
||||
<em>
|
||||
{{#link-to 'protected.species.show' species}}
|
||||
{{species.speciesName}}
|
||||
{{/link-to}}
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
{{#each species.strains as |strain index|}}
|
||||
{{if index ","}}
|
||||
{{#link-to 'protected.strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
|
@ -1,34 +1,6 @@
|
|||
<h2>{{genus-name}} Species</h2>
|
||||
<h3>Total species: {{model.length}}</h3>
|
||||
|
||||
{{add-button label="Add Species" link="protected.species.new" canAdd=metaData.canAdd}}
|
||||
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Strains</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each sortedSpecies as |species|}}
|
||||
<tr>
|
||||
<td>
|
||||
<em>
|
||||
{{#link-to 'protected.species.show' species}}
|
||||
{{species.speciesName}}
|
||||
{{/link-to}}
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
{{#each species.strains as |strain index|}}
|
||||
{{if index ","}}
|
||||
{{#link-to 'protected.strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{
|
||||
protected/species/index/species-table
|
||||
species=model
|
||||
}}
|
||||
|
|
|
@ -1,29 +1,9 @@
|
|||
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 species = this.get('model');
|
||||
const { Controller } = Ember;
|
||||
|
||||
if (species.get('hasDirtyAttributes')) {
|
||||
species.save().then((species) => {
|
||||
this.transitionToRoute('protected.species.show', species.get('id'));
|
||||
}, () => {
|
||||
ajaxError(species.get('errors'), this.get('flashMessages'));
|
||||
});
|
||||
} else {
|
||||
species.destroyRecord().then(() => {
|
||||
this.transitionToRoute('protected.species.index');
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
this.get('model').destroyRecord().then(() => {
|
||||
this.transitionToRoute('protected.species.index');
|
||||
});
|
||||
},
|
||||
|
||||
},
|
||||
export default Controller.extend(SaveModel, {
|
||||
// Required for SaveModel mixin
|
||||
fallbackRoute: 'protected.species.show',
|
||||
});
|
||||
|
|
|
@ -1,30 +1,14 @@
|
|||
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.species.index');
|
||||
}
|
||||
});
|
||||
},
|
||||
export default Route.extend(ElevatedAccess, {
|
||||
// Required for ElevatedAccess mixin
|
||||
fallbackRouteBefore: 'protected.species.index',
|
||||
fallbackRouteAfter: 'protected.species.show',
|
||||
|
||||
model: function() {
|
||||
return this.store.createRecord('species');
|
||||
},
|
||||
|
||||
actions: {
|
||||
willTransition: function(/*transition*/) {
|
||||
const controller = this.get('controller');
|
||||
const species = controller.get('model');
|
||||
|
||||
if (species.get('isNew')) {
|
||||
species.destroyRecord();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{
|
||||
protected/species/species-form
|
||||
species=model
|
||||
save="save"
|
||||
cancel="cancel"
|
||||
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.species.index');
|
||||
});
|
||||
},
|
||||
},
|
||||
const { Controller } = Ember;
|
||||
|
||||
export default Controller.extend(DeleteModel, {
|
||||
// Required for DeleteModel mixin
|
||||
transitionRoute: 'protected.species.index',
|
||||
});
|
||||
|
|
|
@ -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('species', params.species_id, { reload: true });
|
||||
return this.store.findRecord('species', params.species_id);
|
||||
},
|
||||
|
||||
});
|
||||
|
|
14
app/pods/protected/species/show/species-card/component.js
Normal file
14
app/pods/protected/species/show/species-card/component.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
const { Component } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
species: null,
|
||||
"on-delete": null,
|
||||
|
||||
actions: {
|
||||
deleteSpecies: function() {
|
||||
return this.attrs['on-delete']();
|
||||
},
|
||||
},
|
||||
});
|
62
app/pods/protected/species/show/species-card/template.hbs
Normal file
62
app/pods/protected/species/show/species-card/template.hbs
Normal file
|
@ -0,0 +1,62 @@
|
|||
<div class="grid-1">
|
||||
<div class="span-1">
|
||||
<fieldset class="flakes-information-box">
|
||||
<legend>
|
||||
Species <em>{{species.speciesName}}</em>
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Strains</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
{{#each species.strains as |strain index|}}
|
||||
<li>
|
||||
{{#link-to 'protected.strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Type Species?</dt>
|
||||
<dd>
|
||||
{{if species.typeSpecies 'Yes' 'No'}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-1 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Etymology</dt>
|
||||
<dd>
|
||||
{{{species.etymology}}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 3 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Record Created</dt>
|
||||
<dd>{{null-time species.createdAt 'LL'}}</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Record Updated</dt>
|
||||
<dd>{{null-time species.updatedAt 'LL'}}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{{#if species.canEdit}}
|
||||
<br>
|
||||
{{#link-to 'protected.species.edit' species class="button-gray smaller"}}
|
||||
Edit
|
||||
{{/link-to}}
|
||||
{{delete-button delete=(action 'deleteSpecies')}}
|
||||
{{/if}}
|
|
@ -1,62 +1,5 @@
|
|||
<div class="grid-1">
|
||||
<div class="span-1">
|
||||
<fieldset class="flakes-information-box">
|
||||
<legend>
|
||||
Species <em>{{model.speciesName}}</em>
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-2 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Strains</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
{{#each model.strains as |strain index|}}
|
||||
<li>
|
||||
{{#link-to 'protected.strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-1">
|
||||
<dt>Type Species?</dt>
|
||||
<dd>
|
||||
{{if model.typeSpecies 'Yes' 'No'}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-1 gutter-20">
|
||||
<dl class="span-1">
|
||||
<dt>Etymology</dt>
|
||||
<dd>
|
||||
{{{model.etymology}}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 3 }}
|
||||
<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>
|
||||
</div>
|
||||
{{#if model.canEdit}}
|
||||
<br>
|
||||
{{#link-to 'protected.species.edit' model class="button-gray smaller"}}
|
||||
Edit
|
||||
{{/link-to}}
|
||||
{{delete-button delete=(action 'delete')}}
|
||||
{{/if}}
|
||||
{{
|
||||
protected/species/show/species-card
|
||||
species=model
|
||||
on-delete=(action 'delete')
|
||||
}}
|
||||
|
|
|
@ -1,13 +1,71 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
const { Component, inject: { service } } = Ember;
|
||||
|
||||
export default Component.extend({
|
||||
currentUser: service('session-account'),
|
||||
|
||||
// Read-only attributes
|
||||
species: null,
|
||||
isNew: null,
|
||||
isDirty: false,
|
||||
|
||||
// Actions
|
||||
"on-save": null,
|
||||
"on-cancel": null,
|
||||
"on-update": null,
|
||||
|
||||
// Property mapping
|
||||
propertiesList: ['speciesName', 'typeSpecies', 'strains', 'etymology'],
|
||||
speciesName: null,
|
||||
typeSpecies: null,
|
||||
strains: null,
|
||||
etymology: null,
|
||||
|
||||
resetOnInit: Ember.on('init', function() {
|
||||
this.get('propertiesList').forEach((field) => {
|
||||
const valueInSpecies = this.get('species').get(field);
|
||||
this.set(field, valueInSpecies);
|
||||
});
|
||||
// Read-only attributes
|
||||
this.set('isNew', this.get('species.isNew'));
|
||||
}),
|
||||
|
||||
setupMetaDataOnInit: Ember.on('init', function() {
|
||||
this.get('currentUser.account').then((user) => {
|
||||
this.set('metaData', user.get('metaData'));
|
||||
});
|
||||
}),
|
||||
|
||||
updateField: function(property, value) {
|
||||
this.set(property, value);
|
||||
// Manually compare against passed in value
|
||||
if (this.get('species').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']();
|
||||
},
|
||||
|
||||
speciesNameDidChange: function(value) {
|
||||
this.updateField('speciesName', value);
|
||||
},
|
||||
|
||||
typeSpeciesDidChange: function() {
|
||||
this.updateField('typeSpecies', !this.get('typeSpecies'));
|
||||
},
|
||||
|
||||
etymologyDidChange: function(value) {
|
||||
this.updateField('etymology', value);
|
||||
},
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
<form class="grid-form" {{action 'save' on='submit'}}>
|
||||
<fieldset>
|
||||
<legend><em>{{species.speciesName}}</em></legend>
|
||||
<legend><em>{{speciesName}}</em></legend>
|
||||
<div data-row-span="2">
|
||||
<div data-field-span="1">
|
||||
<label>Species Name</label>
|
||||
{{input value=species.speciesName}}
|
||||
{{one-way-input type="text" class="species-name" value=speciesName update=(action "speciesNameDidChange")}}
|
||||
</div>
|
||||
<div data-field-span="1">
|
||||
<label>Type Species?</label>
|
||||
{{input type="checkbox" checked=species.typeSpecies}} {{if species.typeSpecies 'Yes' 'No'}}
|
||||
<input type="checkbox" checked={{typeSpecies}} value="{{typeSpecies}}" onchange={{action "typeSpeciesDidChange"}}>
|
||||
{{if typeSpecies 'Yes' 'No'}}
|
||||
</div>
|
||||
</div>
|
||||
{{#unless species.isNew}}
|
||||
{{#unless isNew}}
|
||||
<div data-row-span="1">
|
||||
<div data-field-span="1">
|
||||
<label>Strains</label>
|
||||
{{#each species.strains as |strain index|}}
|
||||
{{#each strains as |strain index|}}
|
||||
{{if index ","}}
|
||||
{{#link-to 'protected.strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
|
@ -29,7 +30,7 @@
|
|||
<div data-row-span="1">
|
||||
<div data-field-span="1">
|
||||
<label>Etymology</label>
|
||||
{{text-editor value=species.etymology}}
|
||||
{{text-editor value=etymology update=(action "etymologyDidChange")}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
@ -37,8 +38,8 @@
|
|||
<a class="button-red smaller" {{action 'cancel'}}>
|
||||
Cancel
|
||||
</a>
|
||||
{{#if species.hasDirtyAttributes}}
|
||||
<button type="submit" class="button-green smaller">
|
||||
{{# if isDirty}}
|
||||
<button type="submit" class="button-green smaller save-species">
|
||||
Save
|
||||
</button>
|
||||
{{/if}}
|
||||
|
|
|
@ -9,5 +9,13 @@ export default Ember.Component.extend({
|
|||
cancel: function() {
|
||||
this.sendAction('cancel');
|
||||
},
|
||||
}
|
||||
|
||||
isolatedFromDidChange: function(value) {
|
||||
this.set('strain.isolatedFrom', value);
|
||||
},
|
||||
|
||||
notesDidChange: function(value) {
|
||||
this.set('strain.notes', value);
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
<div data-row-span="2">
|
||||
<div data-field-span="2">
|
||||
<label>Isolated From</label>
|
||||
{{text-editor value=strain.isolatedFrom}}
|
||||
{{text-editor value=strain.isolatedFrom update=(action "isolatedFromDidChange")}}
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="3">
|
||||
|
@ -45,7 +45,7 @@
|
|||
<div data-row-span="2">
|
||||
<div data-field-span="2">
|
||||
<label>Notes</label>
|
||||
{{text-editor value=strain.notes}}
|
||||
{{text-editor value=strain.notes update=(action "notesDidChange")}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -12,7 +12,7 @@ export default Ember.Route.extend(UnauthenticatedRouteMixin, {
|
|||
this.get('session').authenticate('authenticator:jwt-resolved', token).then(() => {
|
||||
this.get('currentUser.account').then((account) => {
|
||||
this.transitionTo('protected.users.changepassword', account.get('id'));
|
||||
})
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
function formatDate(date, format) {
|
||||
export default function formatDate(date, format) {
|
||||
return window.moment(date).format(format);
|
||||
}
|
||||
|
||||
export {
|
||||
formatDate
|
||||
};
|
||||
|
|
|
@ -4,6 +4,6 @@ export default function parseBase64(token) {
|
|||
tokenData = atob(token.split('.')[1]);
|
||||
return JSON.parse(tokenData);
|
||||
} catch (e) {
|
||||
return tokenData;
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
"antiscroll": "git://github.com/azirbel/antiscroll.git#90391fb371c7be769bc32e7287c5271981428356",
|
||||
"jquery-mousewheel": "~3.1.4",
|
||||
"jquery-ui": "~1.11.4",
|
||||
"quill": "~0.19.14"
|
||||
"quill": "~0.19.14",
|
||||
"pretender": "~0.10.1",
|
||||
"lodash": "~3.7.0",
|
||||
"Faker": "~3.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ module.exports = function(environment) {
|
|||
apiURL = 'https://bactdb-test.herokuapp.com';
|
||||
ENV.APP.LOG_ACTIVE_GENERATION = false;
|
||||
ENV.APP.LOG_VIEW_LOOKUPS = false;
|
||||
ENV.locationType = 'none';
|
||||
}
|
||||
|
||||
if (environment === 'production') {
|
||||
|
|
|
@ -30,14 +30,16 @@
|
|||
"ember-cli-htmlbars-inline-precompile": "^0.2.0",
|
||||
"ember-cli-ic-ajax": "0.2.1",
|
||||
"ember-cli-inject-live-reload": "^1.3.1",
|
||||
"ember-cli-mirage": "0.1.11",
|
||||
"ember-cli-qunit": "^1.0.0",
|
||||
"ember-simple-auth": "1.0.0",
|
||||
"ember-cli-release": "0.2.3",
|
||||
"ember-cli-sri": "^1.0.3",
|
||||
"ember-cli-uglify": "^1.2.0",
|
||||
"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"
|
||||
"ember-one-way-input": "0.1.3",
|
||||
"ember-select-2": "1.3.0",
|
||||
"ember-simple-auth": "1.0.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"predef": [
|
||||
"server",
|
||||
"document",
|
||||
"window",
|
||||
"location",
|
||||
|
|
70
tests/acceptance/species-test.js
Normal file
70
tests/acceptance/species-test.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
import Ember from 'ember';
|
||||
import { module, test } from 'qunit';
|
||||
import startApp from '../helpers/start-app';
|
||||
import { authenticateSession } from '../helpers/ember-simple-auth';
|
||||
|
||||
module('Acceptance | species', {
|
||||
beforeEach: function() {
|
||||
this.application = startApp();
|
||||
authenticateSession(this.application, {
|
||||
access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg"
|
||||
});
|
||||
server.create('users', { role: 'A', canEdit: true });
|
||||
},
|
||||
|
||||
afterEach: function() {
|
||||
Ember.run(this.application, 'destroy');
|
||||
}
|
||||
});
|
||||
|
||||
test('visiting /species', function(assert) {
|
||||
const species = server.createList('species', 20);
|
||||
visit('/species');
|
||||
|
||||
andThen(function() {
|
||||
assert.equal(currentURL(), '/species');
|
||||
assert.equal(find(".flakes-table > tbody > tr").length, species.length);
|
||||
assert.equal(find("#total-species").text(), "Total species: 20");
|
||||
});
|
||||
});
|
||||
|
||||
test('visiting /species/:id', function(assert) {
|
||||
const species = server.create('species');
|
||||
visit(`/species/${species.id}`);
|
||||
|
||||
andThen(function() {
|
||||
assert.equal(currentURL(), `/species/${species.id}`);
|
||||
assert.equal(find(".flakes-information-box > legend > em").text().trim(), species.speciesName);
|
||||
});
|
||||
});
|
||||
|
||||
test('editing /species/:id/edit', function(assert) {
|
||||
const species = server.create('species', { 'canEdit': true });
|
||||
visit(`/species/${species.id}/edit`);
|
||||
|
||||
andThen(function() {
|
||||
assert.equal(currentURL(), `/species/${species.id}/edit`);
|
||||
|
||||
fillIn('.species-name', 'Revised Species Name');
|
||||
click('.save-species');
|
||||
|
||||
andThen(function() {
|
||||
assert.equal(currentURL(), `/species/${species.id}`);
|
||||
assert.equal(find(".flakes-information-box > legend > em").text().trim(), 'Revised Species Name');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('creating /species/new', function(assert) {
|
||||
visit(`/species/new`);
|
||||
|
||||
andThen(function() {
|
||||
assert.equal(currentURL(), `/species/new`);
|
||||
fillIn('.species-name', 'New Species Name');
|
||||
click('.save-species');
|
||||
|
||||
andThen(function() {
|
||||
assert.equal(find(".flakes-information-box > legend > em").text().trim(), 'New Species Name');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
import resolver from './helpers/resolver';
|
||||
import flashMessageHelper from './helpers/flash-message';
|
||||
// import flashMessageHelper from './helpers/flash-message';
|
||||
|
||||
import {
|
||||
setResolver
|
||||
|
|
12
tests/unit/mixins/delete-model-test.js
Normal file
12
tests/unit/mixins/delete-model-test.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import DeleteModelMixin from '../../../mixins/delete-model';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Mixin | delete model');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
var DeleteModelObject = Ember.Object.extend(DeleteModelMixin);
|
||||
var subject = DeleteModelObject.create();
|
||||
assert.ok(subject);
|
||||
});
|
12
tests/unit/mixins/elevated-access-test.js
Normal file
12
tests/unit/mixins/elevated-access-test.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import ElevatedAccessMixin from '../../../mixins/elevated-access';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Mixin | elevated access');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
var ElevatedAccessObject = Ember.Object.extend(ElevatedAccessMixin);
|
||||
var subject = ElevatedAccessObject.create();
|
||||
assert.ok(subject);
|
||||
});
|
12
tests/unit/mixins/save-model-test.js
Normal file
12
tests/unit/mixins/save-model-test.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import SaveModelMixin from '../../../mixins/save-model';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Mixin | save model');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
var SaveModelObject = Ember.Object.extend(SaveModelMixin);
|
||||
var subject = SaveModelObject.create();
|
||||
assert.ok(subject);
|
||||
});
|
|
@ -5,7 +5,7 @@ import {
|
|||
|
||||
moduleForModel('measurement', {
|
||||
// Specify the other units that are required for this test.
|
||||
needs: ['model:strain']
|
||||
needs: ['model:strain', 'model:characteristic']
|
||||
});
|
||||
|
||||
test('it exists', function(assert) {
|
||||
|
|
|
@ -5,7 +5,7 @@ import {
|
|||
|
||||
moduleForModel('strain', {
|
||||
// Specify the other units that are required for this test.
|
||||
needs: ['model:measurement']
|
||||
needs: ['model:measurement', 'model:characteristic', 'model:species']
|
||||
});
|
||||
|
||||
test('it exists', function(assert) {
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import parseBase64 from '../../../utils/parse-base64';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | parse base64');
|
||||
module('parseBase64');
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it works', function(assert) {
|
||||
var result = parseBase64();
|
||||
var result = parseBase64('123');
|
||||
assert.ok(result);
|
||||
});
|
||||
|
|
Reference in a new issue