Updated merge from master
This commit is contained in:
commit
8ab97c00e1
127 changed files with 578 additions and 1802 deletions
|
@ -1,34 +0,0 @@
|
|||
import Ember from "ember";
|
||||
import DS from 'ember-data';
|
||||
import Session from "simple-auth/session";
|
||||
|
||||
// This is pulled straight from ember-cli-simple-auth-token
|
||||
function getTokenData(token) {
|
||||
var tokenData = atob(token.split('.')[1]);
|
||||
try {
|
||||
return JSON.parse(tokenData);
|
||||
} catch (e) {
|
||||
return tokenData;
|
||||
}
|
||||
}
|
||||
|
||||
var CustomSession = Session.extend({
|
||||
currentUser: function() {
|
||||
let token = this.get('secure.token');
|
||||
if (!Ember.isEmpty(token)) {
|
||||
let t = getTokenData(token);
|
||||
return DS.PromiseObject.create({
|
||||
promise: this.container.lookup('store:main').find('user', t['sub'])
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}.property('token')
|
||||
});
|
||||
|
||||
export default {
|
||||
name: "custom-session",
|
||||
before: "simple-auth",
|
||||
initialize: function(container, application) {
|
||||
application.register('session:custom', CustomSession);
|
||||
}
|
||||
};
|
|
@ -8,5 +8,6 @@ export default DS.Model.extend({
|
|||
deletedAt: DS.attr('date'),
|
||||
createdBy: DS.attr('number'),
|
||||
updatedBy: DS.attr('number'),
|
||||
deletedBy: DS.attr('number')
|
||||
deletedBy: DS.attr('number'),
|
||||
sortOrder: DS.attr('number'),
|
||||
});
|
||||
|
|
|
@ -10,5 +10,6 @@ export default DS.Model.extend({
|
|||
deletedAt : DS.attr('date'),
|
||||
createdBy : DS.attr('number'),
|
||||
updatedBy : DS.attr('number'),
|
||||
deletedBy : DS.attr('number')
|
||||
deletedBy : DS.attr('number'),
|
||||
sortOrder : DS.attr('number'),
|
||||
});
|
||||
|
|
|
@ -7,7 +7,7 @@ export default DS.Model.extend({
|
|||
typeSpecies : DS.attr('boolean'),
|
||||
etymology : DS.attr('string'),
|
||||
genusName : DS.attr('string', { defaultValue: config.APP.genus }),
|
||||
strains : DS.hasMany('strain', { async: true }),
|
||||
strains : DS.hasMany('strain', { async: false }),
|
||||
totalStrains: DS.attr('number'),
|
||||
createdAt : DS.attr('date'),
|
||||
updatedAt : DS.attr('date'),
|
||||
|
@ -15,6 +15,7 @@ export default DS.Model.extend({
|
|||
createdBy : DS.attr('number'),
|
||||
updatedBy : DS.attr('number'),
|
||||
deletedBy : DS.attr('number'),
|
||||
sortOrder : DS.attr('number'),
|
||||
|
||||
speciesNameMU: function() {
|
||||
return Ember.String.htmlSafe(`<em>${this.get('speciesName')}</em>`);
|
||||
|
|
|
@ -2,21 +2,23 @@ import DS from 'ember-data';
|
|||
import Ember from 'ember';
|
||||
|
||||
export default DS.Model.extend({
|
||||
measurements : DS.hasMany('measurements', { async: true }),
|
||||
species : DS.belongsTo('species', { async: true }),
|
||||
strainName : DS.attr('string'),
|
||||
typeStrain : DS.attr('boolean'),
|
||||
accessionNumbers : DS.attr('string'),
|
||||
genbank : DS.attr('string'),
|
||||
isolatedFrom : DS.attr('string'),
|
||||
notes : DS.attr('string'),
|
||||
createdAt : DS.attr('date'),
|
||||
updatedAt : DS.attr('date'),
|
||||
deletedAt : DS.attr('date'),
|
||||
createdBy : DS.attr('number'),
|
||||
updatedBy : DS.attr('number'),
|
||||
deletedBy : DS.attr('number'),
|
||||
totalMeasurements: DS.attr('number'),
|
||||
measurements : DS.hasMany('measurements', { async: true }),
|
||||
species : DS.belongsTo('species', { async: false }),
|
||||
strainName : DS.attr('string'),
|
||||
typeStrain : DS.attr('boolean'),
|
||||
accessionNumbers : DS.attr('string'),
|
||||
genbank : DS.attr('string'),
|
||||
wholeGenomeSequence: DS.attr('string'),
|
||||
isolatedFrom : DS.attr('string'),
|
||||
notes : DS.attr('string'),
|
||||
createdAt : DS.attr('date'),
|
||||
updatedAt : DS.attr('date'),
|
||||
deletedAt : DS.attr('date'),
|
||||
createdBy : DS.attr('number'),
|
||||
updatedBy : DS.attr('number'),
|
||||
deletedBy : DS.attr('number'),
|
||||
totalMeasurements : DS.attr('number'),
|
||||
sortOrder : DS.attr('number'),
|
||||
|
||||
strainNameMU: function() {
|
||||
let type = this.get('typeStrain') ? '<sup>T</sup>' : '';
|
||||
|
|
|
@ -2,6 +2,7 @@ import DS from 'ember-data';
|
|||
|
||||
export default DS.Model.extend({
|
||||
email : DS.attr('string'),
|
||||
password : DS.attr('string'),
|
||||
name : DS.attr('string'),
|
||||
role : DS.attr('string'),
|
||||
createdAt: DS.attr('date'),
|
||||
|
|
|
@ -5,10 +5,13 @@ export default DS.RESTAdapter.extend({
|
|||
namespace: function() {
|
||||
return 'api/' + this.get('globals.genus');
|
||||
}.property(),
|
||||
|
||||
host: function() {
|
||||
return this.get('globals.apiURL');
|
||||
}.property(),
|
||||
|
||||
coalesceFindRequests: true,
|
||||
|
||||
ajaxError: function(jqXHR) {
|
||||
// http://stackoverflow.com/a/24027443
|
||||
var error = this._super(jqXHR);
|
||||
|
@ -25,5 +28,5 @@ export default DS.RESTAdapter.extend({
|
|||
} else {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
|
|
@ -4,7 +4,16 @@ import ApplicationRouteMixin from 'simple-auth/mixins/application-route-mixin';
|
|||
export default Ember.Route.extend(ApplicationRouteMixin, {
|
||||
actions: {
|
||||
invalidateSession: function() {
|
||||
this.get('session').invalidate();
|
||||
this.get('session').invalidate().then(() => {
|
||||
// Wait until promise is resolved
|
||||
return true;
|
||||
});
|
||||
},
|
||||
|
||||
didTransition: function() {
|
||||
this.get('flashMessages').clearMessages();
|
||||
return true;
|
||||
},
|
||||
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
<div class="flakes-navigation">
|
||||
{{#link-to 'index' class='logo'}}
|
||||
{{globals.genus}}.info
|
||||
{{/link-to}}
|
||||
{{site-logo}}
|
||||
{{#if session.isAuthenticated}}
|
||||
<ul>
|
||||
{{#link-to 'compare' tagName='li' href=false}}
|
||||
|
@ -29,24 +27,25 @@
|
|||
</p>
|
||||
{{else}}
|
||||
<p class="foot">
|
||||
{{#link-to 'login' Login}}Login{{/link-to}}
|
||||
{{link-to 'Login' 'login'}}
|
||||
<br>
|
||||
Sign Up
|
||||
{{link-to 'Sign Up' 'users.new'}}
|
||||
</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
<div class="flakes-content">
|
||||
<div class="flakes-mobile-top-bar">
|
||||
<a href="" class="logo-wrap">
|
||||
{{globals.genus}}.info
|
||||
</a>
|
||||
{{site-logo}}
|
||||
<a href="" class="navigation-expand-target">
|
||||
<img src="img/navigation-expand-target.png" height="26px">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="view-wrap">
|
||||
{{#each flashMessages.queue as |flash|}}
|
||||
{{custom-flash-message flash=flash}}
|
||||
{{/each}}
|
||||
{{outlet}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
6
app/pods/characteristics/controller.js
Normal file
6
app/pods/characteristics/controller.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
sortParams: ['characteristicType.characteristicTypeName', 'characteristicName'],
|
||||
sortedCharacteristics: Ember.computed.sort('characteristics', 'sortParams'),
|
||||
});
|
|
@ -8,14 +8,8 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
|||
characteristics: this.store.findAll('characteristic'),
|
||||
});
|
||||
},
|
||||
|
||||
setupController: function(controller, models) {
|
||||
var tableAttrs = [
|
||||
{ name: 'Name', attr: 'characteristicName' },
|
||||
{ name: 'Type', attr: 'characteristicType.characteristicTypeName' }
|
||||
];
|
||||
controller.set('model', models.characteristics);
|
||||
controller.set('tableAttrs', tableAttrs);
|
||||
controller.set('row', 'characteristic-index-row');
|
||||
controller.set('sort', ['characteristicName']);
|
||||
controller.setProperties(models);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,10 +1,19 @@
|
|||
<h2>{{genus-name}} Characteristics</h2>
|
||||
<h3>Total characteristics: {{model.length}}</h3>
|
||||
<h3>Total characteristics: {{characteristics.length}}</h3>
|
||||
|
||||
{{
|
||||
sortable-table
|
||||
content=model
|
||||
tableAttrs=tableAttrs
|
||||
row=row
|
||||
sortProperties=sort
|
||||
}}
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each sortedCharacteristics as |row|}}
|
||||
<tr>
|
||||
<td>{{row.characteristicName}}</td>
|
||||
<td>{{row.characteristicType.characteristicTypeName}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,4 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
|
||||
|
||||
export default Ember.Route.extend(AuthenticatedRouteMixin);
|
||||
export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
||||
resetController: function(controller, isExiting /*, transition*/) {
|
||||
if (isExiting) {
|
||||
controller.set('data', null);
|
||||
controller.set('strains', null);
|
||||
controller.set('dataEmpty', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,19 +1,28 @@
|
|||
<h2>{{genus-name}} - Compare Strains</h2>
|
||||
|
||||
{{measurement-search-panel search='search'}}
|
||||
{{
|
||||
measurement-search-panel
|
||||
search='search'
|
||||
strainLabel='Select one or more strains'
|
||||
charLabel='Select one or more characteristics'
|
||||
}}
|
||||
|
||||
{{#if dataEmpty}}
|
||||
<div class="flakes-message information">
|
||||
Please select one or more strains and one or more characteristics.
|
||||
</div>
|
||||
{{else}}
|
||||
<div>
|
||||
<div class="overflow-div">
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Characteristic</th>
|
||||
{{#each strains as |strain|}}
|
||||
<th>{{strain.fullNameMU}}</th>
|
||||
<th>
|
||||
{{#link-to 'strains.show' strain.id classBinding="data.typeStrain:type-strain"}}
|
||||
{{strain.fullNameMU}}
|
||||
{{/link-to}}
|
||||
</th>
|
||||
{{/each}}
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
canAdd: function() {
|
||||
let role = this.get('session.currentUser.role');
|
||||
return (role === 'W') || (role === 'A');
|
||||
}.property('session.currentUser.role')
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr',
|
||||
});
|
|
@ -1,6 +0,0 @@
|
|||
<td>
|
||||
{{data.characteristicName}}
|
||||
</td>
|
||||
<td>
|
||||
{{data.characteristicType.characteristicTypeName}}
|
||||
</td>
|
7
app/pods/components/custom-flash-message/component.js
Normal file
7
app/pods/components/custom-flash-message/component.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['flakes-message'],
|
||||
classNameBindings: ['type'],
|
||||
type: Ember.computed.readOnly('flash.type'),
|
||||
});
|
1
app/pods/components/custom-flash-message/template.hbs
Normal file
1
app/pods/components/custom-flash-message/template.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{flash.message}}
|
|
@ -1,3 +1,3 @@
|
|||
{{#each error in a}}
|
||||
{{#each a as |error|}}
|
||||
<div class="flakes-message error">{{error.message}}</div>
|
||||
{{/each}}
|
||||
|
|
12
app/pods/components/forms/species-form/component.js
Normal file
12
app/pods/components/forms/species-form/component.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
actions: {
|
||||
save: function() {
|
||||
this.sendAction('save');
|
||||
},
|
||||
cancel: function() {
|
||||
this.sendAction('cancel');
|
||||
},
|
||||
}
|
||||
});
|
42
app/pods/components/forms/species-form/template.hbs
Normal file
42
app/pods/components/forms/species-form/template.hbs
Normal file
|
@ -0,0 +1,42 @@
|
|||
<form class="grid-form">
|
||||
<fieldset>
|
||||
<legend><em>{{species.speciesName}}</em></legend>
|
||||
<div data-row-span="2">
|
||||
<div data-field-span="1">
|
||||
<label>Species Name</label>
|
||||
{{input value=species.speciesName}}
|
||||
</div>
|
||||
<div data-field-span="1">
|
||||
<label>Type Species?</label>
|
||||
{{input type="checkbox" checked=species.typeSpecies}} {{if species.typeSpecies 'Yes' 'No'}}
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="2">
|
||||
<div data-field-span="2">
|
||||
<label>Strains</label>
|
||||
{{#each species.strains as |strain index|}}
|
||||
{{if index ","}}
|
||||
{{#link-to 'strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
{{add-button label="Add Strain" link="strains.new"}}
|
||||
</div>
|
||||
</div>
|
||||
<div data-row-span="2">
|
||||
<div data-field-span="2">
|
||||
<label>Etymology</label>
|
||||
{{textarea value=species.etymology cols="70" rows="5"}}
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<br>
|
||||
{{#if species.isDirty}}
|
||||
<a class="button-green smaller" {{action 'save'}}>
|
||||
Save
|
||||
</a>
|
||||
{{/if}}
|
||||
<a class="button-red smaller" {{action 'cancel'}}>
|
||||
Cancel
|
||||
</a>
|
|
@ -23,10 +23,10 @@ export default Ember.Component.extend({
|
|||
models[item.model] = models[item.model].filter((i) => {
|
||||
if (!Ember.isEmpty(i.get(item.children))) { return true; }
|
||||
});
|
||||
models[item.model] = models[item.model].sortBy(item.text);
|
||||
models[item.model] = models[item.model].sortBy('sortOrder');
|
||||
let temp = models[item.model].map((data) => {
|
||||
let temp_children = [];
|
||||
let sorted_children = data.get(item.children).sortBy(item.ctext);
|
||||
let sorted_children = data.get(item.children).sortBy('sortOrder');
|
||||
sorted_children.forEach((child) => {
|
||||
temp_children.push({id: child.get(item.cid), text: child.get(item.ctext)});
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
content=species
|
||||
value=selectedStrains
|
||||
optionValuePath="id"
|
||||
placeholder="All strains"
|
||||
placeholder=strainLabel
|
||||
}}
|
||||
</li>
|
||||
<li>
|
||||
|
@ -21,7 +21,7 @@
|
|||
content=characteristicTypes
|
||||
value=selectedCharacteristics
|
||||
optionValuePath="id"
|
||||
placeholder="All characteristics"
|
||||
placeholder=charLabel
|
||||
}}
|
||||
</li>
|
||||
<li>
|
||||
|
|
3
app/pods/components/site-logo/template.hbs
Normal file
3
app/pods/components/site-logo/template.hbs
Normal file
|
@ -0,0 +1,3 @@
|
|||
{{#link-to 'index' class='logo'}}
|
||||
<h2>{{globals.genus}}.info</h2>
|
||||
{{/link-to}}
|
|
@ -1,13 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'th',
|
||||
upArrow: '▲',
|
||||
downArrow: '▼',
|
||||
|
||||
actions: {
|
||||
sortBy: function(sortProperty, ascending) {
|
||||
this.sendAction('action', sortProperty, ascending);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,3 +0,0 @@
|
|||
{{title}}
|
||||
<span {{action 'sortBy' sortProperty true}}>{{{upArrow}}}</span>
|
||||
<span {{action 'sortBy' sortProperty false}}>{{{downArrow}}}</span>
|
|
@ -1,14 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend(Ember.SortableMixin, {
|
||||
tagName: 'table',
|
||||
classNames: ['flakes-table'],
|
||||
sortProperties: [],
|
||||
|
||||
actions: {
|
||||
sortBy: function(property, ascending) {
|
||||
this.set('sortAscending', ascending);
|
||||
this.set('sortProperties', [property]);
|
||||
}
|
||||
},
|
||||
});
|
|
@ -1,13 +0,0 @@
|
|||
<thead>
|
||||
<tr>
|
||||
{{#each a in tableAttrs}}
|
||||
{{sortable-table-header title=a.name sortProperty=a.attr action="sortBy"}}
|
||||
{{/each}}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{{#each item in arrangedContent}}
|
||||
{{component row data=item}}
|
||||
{{/each}}
|
||||
</tbody>
|
|
@ -1,22 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['grid-1'],
|
||||
isEditing: false,
|
||||
|
||||
canEdit: function() {
|
||||
let role = this.get('session.currentUser.role');
|
||||
let id = this.get('session.currentUser.id');
|
||||
let author = this.get('species.createdBy');
|
||||
return (role === 'W' && (+id === author)) || (role === 'A');
|
||||
}.property('session.currentUser.role', 'session.currentUser.id', 'species.createdBy'),
|
||||
|
||||
actions: {
|
||||
save: function() {
|
||||
this.sendAction('save');
|
||||
},
|
||||
cancel: function() {
|
||||
this.sendAction('cancel');
|
||||
},
|
||||
}
|
||||
});
|
|
@ -1,89 +0,0 @@
|
|||
<div class="span-1">
|
||||
<fieldset class="flakes-information-box {{if isEditing 'is-editing'}}">
|
||||
<legend>
|
||||
Species
|
||||
{{#if isEditing}}
|
||||
{{input value=species.speciesName}}
|
||||
{{else}}
|
||||
<em>{{species.speciesName}}</em>
|
||||
{{/if}}
|
||||
{{display-errors a=species.errors.speciesName}}
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-4">
|
||||
<dl class="span-2">
|
||||
<dt>Strains</dt>
|
||||
<dd>
|
||||
{{#each species.strains as |strain index|}}
|
||||
{{if index ","}}
|
||||
{{#link-to 'strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
{{#unless species.isNew}}
|
||||
{{add-button label="Add Strain" link="strains.new"}}
|
||||
{{/unless}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-2">
|
||||
<dt>Type Species?</dt>
|
||||
<dd>
|
||||
{{#if isEditing}}
|
||||
{{input type="checkbox" checked=species.typeSpecies}}
|
||||
{{/if}}
|
||||
{{if species.typeSpecies 'Yes' 'No'}}
|
||||
{{display-errors a=species.errors.typeSpecies}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-4">
|
||||
<dl class="span-4">
|
||||
<dt>Etymology</dt>
|
||||
<dd>
|
||||
{{#if isEditing}}
|
||||
{{textarea value=species.etymology cols="70" rows="3"}}
|
||||
{{else}}
|
||||
{{species.etymology}}
|
||||
{{/if}}
|
||||
{{display-errors a=species.errors.etymology}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 3 }}
|
||||
<div class="grid-4">
|
||||
<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>
|
||||
<dl class="span-1">
|
||||
<dt>Record Deleted</dt>
|
||||
<dd>{{null-time species.deletedAt 'LL'}}</dd>
|
||||
</dl>
|
||||
<dl class="span-1"></dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 4 }}
|
||||
{{#if canEdit}}
|
||||
<div class="grid-4">
|
||||
<div class="span-1">
|
||||
<a class="smaller {{if isEditing 'button-red' 'button-gray'}}" {{action 'cancel'}}>
|
||||
{{#if isEditing}}Cancel{{else}}Edit{{/if}}
|
||||
</a>
|
||||
{{#if isEditing}}
|
||||
<a class="button-green smaller" {{action 'save'}}>
|
||||
Save
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr',
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
<td>
|
||||
<em>
|
||||
{{#link-to 'species.show' data}}
|
||||
{{data.speciesName}}
|
||||
{{/link-to}}
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
{{#each data.strains as |strain index|}}
|
||||
{{if index ","}}
|
||||
{{#link-to 'strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
</td>
|
|
@ -1,15 +1,13 @@
|
|||
import Ember from 'ember';
|
||||
import userCanEdit from '../../../utils/user-can-edit';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['grid-1'],
|
||||
isEditing: false,
|
||||
|
||||
canEdit: function() {
|
||||
let role = this.get('session.currentUser.role');
|
||||
let id = this.get('session.currentUser.id');
|
||||
let author = this.get('strain.createdBy');
|
||||
return (role === 'W' && (+id === author)) || (role === 'A');
|
||||
}.property('session.currentUser.role', 'session.currentUser.id', 'strain.createdBy'),
|
||||
return userCanEdit(this.get('session.currentUser'), this.get('strain.createdBy'));
|
||||
}.property('session.currentUser', 'strain.createdBy').readOnly(),
|
||||
|
||||
actions: {
|
||||
save: function() {
|
||||
|
|
|
@ -5,23 +5,22 @@
|
|||
{{#if isEditing}}
|
||||
{{input value=strain.strainName}}
|
||||
{{else}}
|
||||
<em>{{strain.strainName}}</em>
|
||||
{{strain.strainNameMU}}
|
||||
{{/if}}
|
||||
{{display-errors a=strain.errors.strainName}}
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-4">
|
||||
<div class="grid-4 gutter-50">
|
||||
<dl class="span-2">
|
||||
<dt>Species</dt>
|
||||
<dd>
|
||||
{{#if isEditing}}
|
||||
{{
|
||||
view "select"
|
||||
select-2
|
||||
content=species
|
||||
optionValuePath="content.id"
|
||||
optionLabelPath="content.speciesName"
|
||||
selection=strain.species
|
||||
optionLabelPath="speciesName"
|
||||
value=strain.species
|
||||
}}
|
||||
{{else}}
|
||||
{{#link-to 'species.show' strain.species}}
|
||||
|
@ -43,7 +42,7 @@
|
|||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-4">
|
||||
<div class="grid-6">
|
||||
<dl class="span-2">
|
||||
<dt>Accession Numbers</dt>
|
||||
<dd>
|
||||
|
@ -66,6 +65,17 @@
|
|||
{{display-errors a=strain.errors.genbank}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-2">
|
||||
<dt>Whole Genome Sequence</dt>
|
||||
<dd>
|
||||
{{#if isEditing}}
|
||||
{{input value=strain.wholeGenomeSequence}}
|
||||
{{else}}
|
||||
{{strain.wholeGenomeSequence}}
|
||||
{{/if}}
|
||||
{{display-errors a=strain.errors.wholeGenomeSequence}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 3 }}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
tagName: 'tr',
|
||||
});
|
|
@ -1,8 +0,0 @@
|
|||
<td>
|
||||
{{#link-to 'strains.show' data.id classBinding="data.typeStrain:type-strain"}}
|
||||
{{{data.fullNameMU}}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>
|
||||
{{data.totalMeasurements}}
|
||||
</td>
|
|
@ -1,18 +1,25 @@
|
|||
import Ember from 'ember';
|
||||
import parseBase64 from '../../utils/parse-base64';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
actions: {
|
||||
authenticate: function() {
|
||||
this.set('errorMessage', null);
|
||||
let credentials = this.getProperties('identification', 'password');
|
||||
let session = this.get('session');
|
||||
let authenticator = 'simple-auth-authenticator:token';
|
||||
|
||||
this.set('loading', true);
|
||||
this.get('session').authenticate(authenticator, credentials).then(()=>{
|
||||
// Manually clean up because there might not be a transition
|
||||
this.get('flashMessages').clearMessages();
|
||||
session.authenticate(authenticator, credentials).then(()=>{
|
||||
this.set('loading', false);
|
||||
let t = parseBase64(session.get('secure.token'));
|
||||
this.store.find('user', t['sub']).then((user) => {
|
||||
session.set('currentUser', user);
|
||||
});
|
||||
}, (error)=> {
|
||||
this.set('loading', false);
|
||||
this.set('errorMessage', error.error);
|
||||
this.get('flashMessages').error(error.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
import UnauthenticatedRouteMixin from 'simple-auth/mixins/unauthenticated-route-mixin';
|
||||
|
||||
export default Ember.Route.extend(UnauthenticatedRouteMixin, {
|
||||
setupController: function(controller) {
|
||||
controller.set('errorMessage', null);
|
||||
}
|
||||
});
|
||||
export default Ember.Route.extend(UnauthenticatedRouteMixin, {});
|
||||
|
|
|
@ -2,10 +2,7 @@
|
|||
<p>You are already logged in!</p>
|
||||
{{else}}
|
||||
{{#if loading}}
|
||||
<div class="spinner">
|
||||
<div class="double-bounce1"></div>
|
||||
<div class="double-bounce2"></div>
|
||||
</div>
|
||||
{{loading-panel}}
|
||||
{{else}}
|
||||
<form {{action "authenticate" on="submit"}}>
|
||||
<h2>Log In</h2>
|
||||
|
@ -14,7 +11,4 @@
|
|||
{{input class="button-gray" type="submit" value="Log In"}}
|
||||
</form>
|
||||
{{/if}}
|
||||
{{#if errorMessage}}
|
||||
<div class="flakes-message error">{{errorMessage}}</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<h2>{{genus-name}} Measurements</h2>
|
||||
|
||||
{{measurement-search-panel search='search'}}
|
||||
{{measurement-search-panel search='search' strainLabel='All strains' charLabel='All characteristics'}}
|
||||
|
||||
<div class="grid-12 gutter-50">
|
||||
<div class="span-12">
|
||||
|
|
29
app/pods/species/edit/controller.js
Normal file
29
app/pods/species/edit/controller.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
actions: {
|
||||
save: function() {
|
||||
let species = this.get('model');
|
||||
|
||||
if (species.get('isDirty')) {
|
||||
species.save().then((species) => {
|
||||
this.transitionToRoute('species.show', species.get('id'));
|
||||
}, (err) => {
|
||||
this.get('flashMessages').error(err.responseJSON.error);
|
||||
});
|
||||
} else {
|
||||
this.transitionToRoute('species.show', species.get('id'));
|
||||
}
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
let species = this.get('model');
|
||||
|
||||
species.get('errors').clear();
|
||||
species.rollback();
|
||||
|
||||
this.transitionToRoute('species.show', species.get('id'));
|
||||
},
|
||||
|
||||
},
|
||||
});
|
4
app/pods/species/edit/route.js
Normal file
4
app/pods/species/edit/route.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixin';
|
||||
|
||||
export default Ember.Route.extend(AuthenticatedRouteMixin, {});
|
6
app/pods/species/edit/template.hbs
Normal file
6
app/pods/species/edit/template.hbs
Normal file
|
@ -0,0 +1,6 @@
|
|||
{{
|
||||
forms/species-form
|
||||
species=model
|
||||
save="save"
|
||||
cancel="cancel"
|
||||
}}
|
11
app/pods/species/index/controller.js
Normal file
11
app/pods/species/index/controller.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
sortParams: ['speciesName', 'strainCount'],
|
||||
sortedSpecies: Ember.computed.sort('model', 'sortParams'),
|
||||
|
||||
metaData: function() {
|
||||
return this.store.metadataFor('species');
|
||||
}.property('model.isLoaded').readOnly(),
|
||||
|
||||
});
|
|
@ -4,15 +4,5 @@ import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixi
|
|||
export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
||||
model: function() {
|
||||
return this.store.findAll('species');
|
||||
},
|
||||
setupController: function(controller, model) {
|
||||
var tableAttrs = [
|
||||
{ name: 'Name', attr: 'speciesName' },
|
||||
{ name: 'Strains', attr: 'totalStrains' }
|
||||
];
|
||||
controller.set('model', model);
|
||||
controller.set('tableAttrs', tableAttrs);
|
||||
controller.set('row', 'species-index-row');
|
||||
controller.set('sort', ['speciesName']);
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,12 +1,34 @@
|
|||
<h2>{{genus-name}} Species</h2>
|
||||
<h3>Total species: {{controller.length}}</h3>
|
||||
<h3>Total species: {{model.length}}</h3>
|
||||
|
||||
{{add-button label="Add Species" link="species.new"}}
|
||||
{{add-button label="Add Species" link="species.new" canAdd=metaData.canAdd}}
|
||||
|
||||
{{
|
||||
sortable-table
|
||||
content=model
|
||||
tableAttrs=tableAttrs
|
||||
row=row
|
||||
sortProperties=sort
|
||||
}}
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Strains</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each sortedSpecies as |species|}}
|
||||
<tr>
|
||||
<td>
|
||||
<em>
|
||||
{{#link-to 'species.show' species}}
|
||||
{{species.speciesName}}
|
||||
{{/link-to}}
|
||||
</em>
|
||||
</td>
|
||||
<td>
|
||||
{{#each species.strains as |strain index|}}
|
||||
{{if index ","}}
|
||||
{{#link-to 'strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,21 +1,30 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
isEditing: true,
|
||||
actions: {
|
||||
save: function() {
|
||||
var species = this.get('model');
|
||||
let species = this.get('model');
|
||||
|
||||
if (species.get('isDirty')) {
|
||||
species.save();
|
||||
species.save().then((species) => {
|
||||
this.transitionToRoute('species.show', species.get('id'));
|
||||
}, (err) => {
|
||||
this.get('flashMessages').error(err.responseJSON.error);
|
||||
});
|
||||
} else {
|
||||
this.transitionToRoute('species.index');
|
||||
}
|
||||
this.transitionToRoute('species.index');
|
||||
},
|
||||
|
||||
cancel: function() {
|
||||
var species = this.get('model');
|
||||
let species = this.get('model');
|
||||
|
||||
if (species.get('isNew')) {
|
||||
species.deleteRecord();
|
||||
}
|
||||
|
||||
this.transitionToRoute('species.index');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
});
|
||||
|
|
|
@ -5,9 +5,5 @@ export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
|||
model: function() {
|
||||
return this.store.createRecord('species');
|
||||
},
|
||||
actions: {
|
||||
cancelSpecies: function() {
|
||||
this.transitionTo('species.index');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
{{
|
||||
species-details
|
||||
forms/species-form
|
||||
species=model
|
||||
isEditing=true
|
||||
save="save"
|
||||
cancel="cancel"
|
||||
}}
|
||||
|
|
|
@ -1,22 +1,14 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
isEditing: false,
|
||||
actions: {
|
||||
save: function() {
|
||||
var species = this.get('model');
|
||||
if (species.get('isDirty')) {
|
||||
species.save();
|
||||
}
|
||||
this.toggleProperty('isEditing');
|
||||
},
|
||||
cancel: function() {
|
||||
if (this.get('isEditing')) {
|
||||
var species = this.get('model');
|
||||
species.get('errors').clear();
|
||||
species.rollback();
|
||||
}
|
||||
this.toggleProperty('isEditing');
|
||||
userCanEdit: function() {
|
||||
let meta = this.store.metadataFor('species');
|
||||
let id = this.get('model.id');
|
||||
|
||||
if (meta.canEdit.indexOf( +id ) === -1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}.property('model.isLoaded').readOnly(),
|
||||
|
||||
});
|
||||
|
|
|
@ -1,7 +1,63 @@
|
|||
{{
|
||||
species-details
|
||||
species=model
|
||||
isEditing=isEditing
|
||||
save="save"
|
||||
cancel="cancel"
|
||||
}}
|
||||
<div class="grid-1">
|
||||
<div class="span-1">
|
||||
<fieldset class="flakes-information-box {{if isEditing 'is-editing'}}">
|
||||
<legend>
|
||||
Species <em>{{model.speciesName}}</em>
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-4">
|
||||
<dl class="span-2">
|
||||
<dt>Strains</dt>
|
||||
<dd>
|
||||
{{#each model.strains as |strain index|}}
|
||||
{{if index ","}}
|
||||
{{#link-to 'strains.show' strain.id}}
|
||||
{{{strain.strainNameMU}}}
|
||||
{{/link-to}}
|
||||
{{/each}}
|
||||
</dd>
|
||||
</dl>
|
||||
<dl class="span-2">
|
||||
<dt>Type Species?</dt>
|
||||
<dd>
|
||||
{{if model.typeSpecies 'Yes' 'No'}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 2 }}
|
||||
<div class="grid-4">
|
||||
<dl class="span-4">
|
||||
<dt>Etymology</dt>
|
||||
<dd>
|
||||
{{model.etymology}}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
{{! ROW 3 }}
|
||||
<div class="grid-4">
|
||||
<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>
|
||||
<dl class="span-1">
|
||||
<dt>Record Deleted</dt>
|
||||
<dd>{{null-time model.deletedAt 'LL'}}</dd>
|
||||
</dl>
|
||||
<dl class="span-1"></dl>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
{{#if userCanEdit}}
|
||||
<br>
|
||||
{{#link-to 'species.edit' model class="button-gray smaller"}}
|
||||
Edit
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
|
|
6
app/pods/strains/index/controller.js
Normal file
6
app/pods/strains/index/controller.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
sortParams: ['fullNameMU', 'totalMeasurements'],
|
||||
sortedStrains: Ember.computed.sort('strains', 'sortParams'),
|
||||
});
|
|
@ -3,16 +3,12 @@ import AuthenticatedRouteMixin from 'simple-auth/mixins/authenticated-route-mixi
|
|||
|
||||
export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
||||
model: function() {
|
||||
return this.store.findAll('strain');
|
||||
return Ember.RSVP.hash({
|
||||
strains: this.store.findAll('strain'),
|
||||
});
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
var tableAttrs = [
|
||||
{ name: 'Name', attr: 'fullNameMU' },
|
||||
{ name: 'Total Measurements', attr: 'totalMeasurements' }
|
||||
];
|
||||
controller.set('model', model);
|
||||
controller.set('tableAttrs', tableAttrs);
|
||||
controller.set('row', 'strain-index-row');
|
||||
controller.set('sort', ['fullNameMU']);
|
||||
controller.setProperties(model);
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,12 +1,27 @@
|
|||
<h2>{{genus-name}} Strains</h2>
|
||||
<h3>Total strains: {{model.length}}</h3>
|
||||
<h3>Total strains: {{strains.length}}</h3>
|
||||
|
||||
{{add-button label="Add Strain" link="strains.new"}}
|
||||
|
||||
{{
|
||||
sortable-table
|
||||
content=model
|
||||
tableAttrs=tableAttrs
|
||||
row=row
|
||||
sortProperties=sort
|
||||
}}
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Species</th>
|
||||
<th>Total Measurements</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each sortedStrains as |row|}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#link-to 'strains.show' row.id classBinding="data.typeStrain:type-strain"}}
|
||||
{{row.fullNameMU}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>
|
||||
{{row.totalMeasurements}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.RESTAdapter.extend({
|
||||
namespace: 'api',
|
||||
host: function() {
|
||||
return this.get('globals.apiURL');
|
||||
}.property(),
|
||||
coalesceFindRequests: true,
|
||||
});
|
|
@ -1,3 +1,3 @@
|
|||
{{#each user in model}}
|
||||
{{#each model as |user|}}
|
||||
{{user.email}}<br>
|
||||
{{/each}}
|
4
app/pods/users/new/fail/route.js
Normal file
4
app/pods/users/new/fail/route.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
});
|
1
app/pods/users/new/fail/template.hbs
Normal file
1
app/pods/users/new/fail/template.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
<h3>Failure</h3>
|
31
app/pods/users/new/new-user-form/component.js
Normal file
31
app/pods/users/new/new-user-form/component.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['grid-1'],
|
||||
passwordConfirm: null,
|
||||
|
||||
actions: {
|
||||
save: function() {
|
||||
let user = this.get('user');
|
||||
|
||||
// All validation is server-side, except for password verification matching
|
||||
if (user.get('password') !== this.get('passwordConfirm')) {
|
||||
this.get('flashMessages').clearMessages();
|
||||
this.get('flashMessages').error("Password fields don't match");
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.get('isDirty')) {
|
||||
user.save().then(() => {
|
||||
this.sendAction();
|
||||
}).catch(() => {
|
||||
// Manually clean up messages because there is no transition
|
||||
this.get('flashMessages').clearMessages();
|
||||
user.get('errors').forEach((error) => {
|
||||
this.get('flashMessages').error(`${error.attribute.capitalize()} - ${error.message}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
30
app/pods/users/new/new-user-form/template.hbs
Normal file
30
app/pods/users/new/new-user-form/template.hbs
Normal file
|
@ -0,0 +1,30 @@
|
|||
<div class="span-1">
|
||||
<fieldset>
|
||||
<legend>New User Signup</legend>
|
||||
<form>
|
||||
<ul>
|
||||
<li>
|
||||
<label>Name</label>
|
||||
{{input value=user.name}}
|
||||
</li>
|
||||
<li>
|
||||
<label>Email</label>
|
||||
{{input value=user.email}}
|
||||
</li>
|
||||
<li>
|
||||
<label>Password</label>
|
||||
{{input type="password" value=user.password}}
|
||||
</li>
|
||||
<li>
|
||||
<label>Password (confirm)</label>
|
||||
{{input type="password" value=passwordConfirm}}
|
||||
</li>
|
||||
<li>
|
||||
<a class="button-green smaller" {{action 'save'}}>
|
||||
Submit
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
</fieldset>
|
||||
</div>
|
23
app/pods/users/new/route.js
Normal file
23
app/pods/users/new/route.js
Normal file
|
@ -0,0 +1,23 @@
|
|||
import Ember from 'ember';
|
||||
import UnauthenticatedRouteMixin from 'simple-auth/mixins/unauthenticated-route-mixin';
|
||||
|
||||
export default Ember.Route.extend(UnauthenticatedRouteMixin, {
|
||||
model: function() {
|
||||
return Ember.RSVP.hash({
|
||||
user: this.store.createRecord('user'),
|
||||
});
|
||||
},
|
||||
|
||||
setupController: function(controller, model) {
|
||||
controller.setProperties(model);
|
||||
},
|
||||
|
||||
actions: {
|
||||
success: function() {
|
||||
this.transitionTo('login').then(() => {
|
||||
this.get('flashMessages').information(`You have successfully signed up.
|
||||
Please check your email for further instructions.`);
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
4
app/pods/users/new/success/route.js
Normal file
4
app/pods/users/new/success/route.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
});
|
1
app/pods/users/new/success/template.hbs
Normal file
1
app/pods/users/new/success/template.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
<h3>Success</h3>
|
1
app/pods/users/new/template.hbs
Normal file
1
app/pods/users/new/template.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{users/new/new-user-form user=user action="success"}}
|
4
app/pods/users/new/verify/route.js
Normal file
4
app/pods/users/new/verify/route.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
});
|
1
app/pods/users/new/verify/template.hbs
Normal file
1
app/pods/users/new/verify/template.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{outlet}}
|
|
@ -9,18 +9,25 @@ Router.map(function() {
|
|||
this.route('login');
|
||||
this.route('about');
|
||||
this.route('characteristics');
|
||||
this.route('users');
|
||||
this.route('measurements');
|
||||
this.route('compare');
|
||||
|
||||
this.route('species', function() {
|
||||
this.route('new');
|
||||
this.route('show', { path: ':species_id' });
|
||||
this.route('edit', { path: ':species_id/edit' });
|
||||
});
|
||||
this.route('strains', function() {
|
||||
this.route('new');
|
||||
this.route('show', { path: ':strain_id' });
|
||||
});
|
||||
this.route('users', function() {
|
||||
this.route('new', function() {
|
||||
this.route('fail');
|
||||
this.route('success');
|
||||
this.route('verify', { path: ':nonce' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
export default Router;
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
.overflow-div {
|
||||
white-space: nowrap;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.measurements-container {
|
||||
padding: 2em 0em 0em 0em;
|
||||
}
|
||||
|
|
8
app/utils/parse-base64.js
Normal file
8
app/utils/parse-base64.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
export default function parseBase64(token) {
|
||||
let tokenData = atob(token.split('.')[1]);
|
||||
try {
|
||||
return JSON.parse(tokenData);
|
||||
} catch (e) {
|
||||
return tokenData;
|
||||
}
|
||||
}
|
3
app/utils/user-can-add.js
Normal file
3
app/utils/user-can-add.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function userCanAdd(role) {
|
||||
return (role === 'W') || (role === 'A');
|
||||
}
|
5
app/utils/user-can-edit.js
Normal file
5
app/utils/user-can-edit.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
export default function userCanEdit(currentUser, author) {
|
||||
let id = currentUser.id;
|
||||
let role = currentUser.role;
|
||||
return (role === 'W' && (+id === author)) || (role === 'A');
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
"loader.js": "ember-cli/loader.js#3.2.0",
|
||||
"qunit": "~1.17.1",
|
||||
"flakes": "~1.0.0",
|
||||
"ember-simple-auth": "~0.8.0-beta.3",
|
||||
"ember-simple-auth": "~0.8.0",
|
||||
"moment": "~2.9.0",
|
||||
"select2": "3.5.2",
|
||||
"antiscroll": "git://github.com/azirbel/antiscroll.git#90391fb371c7be769bc32e7287c5271981428356",
|
||||
|
|
|
@ -16,8 +16,8 @@ module.exports = function(environment) {
|
|||
},
|
||||
podModulePrefix: 'clostridiumdotinfo/pods',
|
||||
'simple-auth': {
|
||||
session: 'session:custom',
|
||||
authorizer: 'simple-auth-authorizer:token',
|
||||
store: 'simple-auth-session-store:local-storage',
|
||||
},
|
||||
'simple-auth-token': {
|
||||
identificationField: 'email',
|
||||
|
@ -34,32 +34,33 @@ module.exports = function(environment) {
|
|||
'style-src': "'self' 'unsafe-inline'",
|
||||
'media-src': "'self'"
|
||||
},
|
||||
flashMessageDefaults: {
|
||||
sticky: true,
|
||||
type: 'error',
|
||||
types: ['error', 'warning', 'success', 'information', 'tip', 'message'],
|
||||
},
|
||||
};
|
||||
|
||||
var apiURL;
|
||||
|
||||
if (environment === 'development') {
|
||||
ENV['simple-auth']['crossOriginWhitelist'] = ['http://127.0.0.1:4200'];
|
||||
ENV['simple-auth-token']['serverTokenEndpoint'] = '/api/authenticate';
|
||||
ENV.apiURL = 'http://127.0.0.1:4200';
|
||||
ENV.contentSecurityPolicy['connect-src'] = "'self' http://127.0.0.1:4200";
|
||||
apiURL = 'http://127.0.0.1:8901';
|
||||
}
|
||||
|
||||
if (environment === 'test') {
|
||||
ENV['simple-auth']['crossOriginWhitelist'] = ['https://bactdb-test.herokuapp.com'];
|
||||
ENV['simple-auth-token']['serverTokenEndpoint'] = 'https://bactdb-test.herokuapp.com/api/authenticate';
|
||||
ENV.apiURL = 'https://bactdb-test.herokuapp.com';
|
||||
ENV.contentSecurityPolicy['connect-src'] = "'self' https://bactdb-test.herokuapp.com";
|
||||
|
||||
// keep test console output quieter
|
||||
apiURL = 'https://bactdb-test.herokuapp.com';
|
||||
ENV.APP.LOG_ACTIVE_GENERATION = false;
|
||||
ENV.APP.LOG_VIEW_LOOKUPS = false;
|
||||
}
|
||||
|
||||
if (environment === 'production') {
|
||||
ENV['simple-auth']['crossOriginWhitelist'] = ['https://bactdb.herokuapp.com'];
|
||||
ENV['simple-auth-token']['serverTokenEndpoint'] = 'https://bactdb.herokuapp.com/api/authenticate';
|
||||
ENV.apiURL = 'https://bactdb.herokuapp.com';
|
||||
ENV.contentSecurityPolicy['connect-src'] = "'self' https://bactdb.herokuapp.com";
|
||||
apiURL = 'https://bactdb.herokuapp.com';
|
||||
}
|
||||
|
||||
ENV['simple-auth']['crossOriginWhitelist'] = [apiURL];
|
||||
ENV['simple-auth-token']['serverTokenEndpoint'] = apiURL + '/api/authenticate';
|
||||
ENV.apiURL = apiURL;
|
||||
ENV.contentSecurityPolicy['connect-src'] = "'self' " + apiURL;
|
||||
|
||||
return ENV;
|
||||
};
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"body-parser": "^1.12.2",
|
||||
"broccoli-asset-rev": "^2.0.2",
|
||||
"ember-cli": "0.2.7",
|
||||
"ember-cli-app-version": "0.3.3",
|
||||
|
@ -27,11 +26,12 @@
|
|||
"ember-cli-content-security-policy": "0.4.0",
|
||||
"ember-cli-dependency-checker": "^1.0.0",
|
||||
"ember-cli-divshot": "^0.1.7",
|
||||
"ember-cli-flash": "1.1.4",
|
||||
"ember-cli-htmlbars": "0.7.6",
|
||||
"ember-cli-ic-ajax": "0.1.1",
|
||||
"ember-cli-inject-live-reload": "^1.3.0",
|
||||
"ember-cli-qunit": "0.3.13",
|
||||
"ember-cli-simple-auth": "^0.8.0-beta.3",
|
||||
"ember-cli-simple-auth": "^0.8.0",
|
||||
"ember-cli-simple-auth-token": "^0.7.2",
|
||||
"ember-cli-uglify": "^1.0.1",
|
||||
"ember-data": "1.0.0-beta.18",
|
||||
|
@ -39,9 +39,6 @@
|
|||
"ember-export-application-global": "^1.0.2",
|
||||
"ember-select-2": "1.3.0",
|
||||
"ember-table": "0.5.0",
|
||||
"express": "^4.12.4",
|
||||
"glob": "^4.5.3",
|
||||
"jsonwebtoken": "^5.0.0",
|
||||
"morgan": "^1.6.0"
|
||||
"glob": "^4.5.3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"node": true
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
// To use it create some files under `mocks/`
|
||||
// e.g. `server/mocks/ember-hamsters.js`
|
||||
//
|
||||
// module.exports = function(app) {
|
||||
// app.get('/ember-hamsters', function(req, res) {
|
||||
// res.send('hello');
|
||||
// });
|
||||
// };
|
||||
|
||||
// http://stackoverflow.com/q/11001817
|
||||
var allowCrossDomain = function(req, res, next) {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
|
||||
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
|
||||
|
||||
// intercept OPTIONS method
|
||||
if ('OPTIONS' == req.method) {
|
||||
res.sendStatus(200);
|
||||
}
|
||||
else {
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = function(app) {
|
||||
var globSync = require('glob').sync;
|
||||
var mocks = globSync('./mocks/**/*.js', { cwd: __dirname }).map(require);
|
||||
var proxies = globSync('./proxies/**/*.js', { cwd: __dirname }).map(require);
|
||||
|
||||
// Log proxy requests
|
||||
var morgan = require('morgan');
|
||||
app.use(morgan('dev'));
|
||||
app.use(allowCrossDomain);
|
||||
|
||||
// Parse json
|
||||
var bodyParser = require('body-parser');
|
||||
app.use(bodyParser.json());
|
||||
|
||||
mocks.forEach(function(route) { route(app); });
|
||||
proxies.forEach(function(route) { route(app); });
|
||||
|
||||
};
|
|
@ -1,66 +0,0 @@
|
|||
module.exports = function(app) {
|
||||
var express = require('express');
|
||||
var jwt = require('jsonwebtoken');
|
||||
var authenticateRouter = express.Router();
|
||||
|
||||
var USERS = [
|
||||
{
|
||||
id: 1,
|
||||
email: 'testA',
|
||||
name: 'Test Admin User',
|
||||
role: 'A',
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
email: 'testR',
|
||||
name: 'Test Read User',
|
||||
role: 'R',
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
email: 'testW',
|
||||
name: 'Test Write User',
|
||||
role: 'W',
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null
|
||||
}
|
||||
]
|
||||
|
||||
authenticateRouter.post('/', function(req, res) {
|
||||
// wait for a bit to simulate cold boot of heroku api
|
||||
var ms = 3000 + new Date().getTime();
|
||||
while (new Date() < ms){}
|
||||
|
||||
if ((req.body.email === 'testA' || req.body.email === 'testR' || req.body.email === 'testW' )
|
||||
&& req.body.password === 'test') {
|
||||
var user = USERS.filter(function(u) {
|
||||
if (u.email == req.body.email) {
|
||||
return u;
|
||||
}
|
||||
})[0];
|
||||
var token = jwt.sign({
|
||||
'name': user.name,
|
||||
'role': user.role
|
||||
}, 'secret',
|
||||
{
|
||||
expiresInMinutes: 60,
|
||||
issuer: 'bactdb',
|
||||
subject: user.id,
|
||||
});
|
||||
res.send({
|
||||
'token': token
|
||||
});
|
||||
} else {
|
||||
res.status(401).send({'error':'Invalid username or password'});
|
||||
}
|
||||
});
|
||||
|
||||
app.use('/api/authenticate', authenticateRouter);
|
||||
};
|
|
@ -1,81 +0,0 @@
|
|||
module.exports = function(app) {
|
||||
var express = require('express');
|
||||
var characteristicTypesRouter = express.Router();
|
||||
|
||||
var CHARACTERISTIC_TYPES = [
|
||||
{
|
||||
id: 1,
|
||||
characteristicTypeName: 'Type 1',
|
||||
characteristics: [1,4],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
characteristicTypeName: 'Type 2',
|
||||
characteristics: [2,5],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
characteristicTypeName: 'Type 3',
|
||||
characteristics: [3],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null
|
||||
},
|
||||
]
|
||||
|
||||
characteristicTypesRouter.get('/', function(req, res) {
|
||||
var characteristics;
|
||||
if (req.query.ids) {
|
||||
characteristic_types = CHARACTERISTIC_TYPES.filter(function(c) {
|
||||
return req.query.ids.indexOf(c.id.toString()) > -1;
|
||||
});
|
||||
} else {
|
||||
characteristic_types = CHARACTERISTIC_TYPES;
|
||||
}
|
||||
res.send({
|
||||
'characteristicTypes': characteristic_types
|
||||
});
|
||||
});
|
||||
|
||||
characteristicTypesRouter.post('/', function(req, res) {
|
||||
res.status(201).end();
|
||||
});
|
||||
|
||||
characteristicTypesRouter.get('/:id', function(req, res) {
|
||||
var characteristic_type = CHARACTERISTIC_TYPES.filter(function(c) {
|
||||
return c.id == req.params.id;
|
||||
});
|
||||
res.send({
|
||||
'characteristicType': characteristic_type
|
||||
});
|
||||
});
|
||||
|
||||
characteristicTypesRouter.put('/:id', function(req, res) {
|
||||
res.send({
|
||||
'characteristicTypes': {
|
||||
id: req.params.id
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
characteristicTypesRouter.delete('/:id', function(req, res) {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.use('/api/clostridium/characteristicTypes', characteristicTypesRouter);
|
||||
};
|
|
@ -1,113 +0,0 @@
|
|||
module.exports = function(app) {
|
||||
var express = require('express');
|
||||
var characteristicsRouter = express.Router();
|
||||
|
||||
var CHARACTERISTICS = [
|
||||
{
|
||||
id: 1,
|
||||
characteristicName: 'α-fucosidase (API ZYM)',
|
||||
characteristicType: 1,
|
||||
strains: [1,2],
|
||||
measurements: [1,6],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
characteristicName: 'α-glucosidase',
|
||||
characteristicType: 2,
|
||||
strains: [1,2],
|
||||
measurements: [2,7],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
characteristicName: 'Chloramphenicol',
|
||||
characteristicType: 3,
|
||||
strains: [1,2],
|
||||
measurements: [3,8],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
characteristicName: 'Bacitracin',
|
||||
characteristicType: 1,
|
||||
strains: [1,2],
|
||||
measurements: [4,9],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
characteristicName: 'Indole',
|
||||
characteristicType: 2,
|
||||
strains: [1,2],
|
||||
measurements: [5,10],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null
|
||||
}
|
||||
]
|
||||
|
||||
characteristicsRouter.get('/', function(req, res) {
|
||||
var characteristics;
|
||||
if (req.query.ids) {
|
||||
characteristics = CHARACTERISTICS.filter(function(c) {
|
||||
return req.query.ids.indexOf(c.id.toString()) > -1;
|
||||
});
|
||||
} else {
|
||||
characteristics = CHARACTERISTICS;
|
||||
}
|
||||
res.send({
|
||||
'characteristics': characteristics
|
||||
});
|
||||
});
|
||||
|
||||
characteristicsRouter.post('/', function(req, res) {
|
||||
res.status(201).end();
|
||||
});
|
||||
|
||||
characteristicsRouter.get('/:id', function(req, res) {
|
||||
var characteristic = CHARACTERISTICS.filter(function(c) {
|
||||
return c.id == req.params.id;
|
||||
});
|
||||
res.send({
|
||||
'characteristic': characteristic
|
||||
});
|
||||
});
|
||||
|
||||
characteristicsRouter.put('/:id', function(req, res) {
|
||||
res.send({
|
||||
'characteristics': {
|
||||
id: req.params.id
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
characteristicsRouter.delete('/:id', function(req, res) {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.use('/api/clostridium/characteristics', characteristicsRouter);
|
||||
};
|
|
@ -1,209 +0,0 @@
|
|||
module.exports = function(app) {
|
||||
var express = require('express');
|
||||
var measurementsRouter = express.Router();
|
||||
|
||||
var MEASUREMENTS = [
|
||||
{
|
||||
id: 1,
|
||||
strain: 1,
|
||||
characteristic: 1,
|
||||
textMeasurementType: 'Meas. Type 1',
|
||||
txtValue: null,
|
||||
numValue: null,
|
||||
confidenceInterval: null,
|
||||
unitType: null,
|
||||
notes: null,
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
strain: 1,
|
||||
characteristic: 2,
|
||||
textMeasurementType: 'Meas. Type 1',
|
||||
txtValue: null,
|
||||
numValue: null,
|
||||
confidenceInterval: null,
|
||||
unitType: null,
|
||||
notes: null,
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
strain: 1,
|
||||
characteristic: 3,
|
||||
textMeasurementType: null,
|
||||
txtValue: "text value",
|
||||
numValue: null,
|
||||
confidenceInterval: null,
|
||||
unitType: null,
|
||||
notes: "some notes",
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
strain: 1,
|
||||
characteristic: 4,
|
||||
textMeasurementType: null,
|
||||
txtValue: null,
|
||||
numValue: 123.4,
|
||||
confidenceInterval: null,
|
||||
unitType: 'Unit 1',
|
||||
notes: null,
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
strain: 1,
|
||||
characteristic: 5,
|
||||
textMeasurementType: null,
|
||||
txtValue: null,
|
||||
numValue: 567.8,
|
||||
confidenceInterval: 0.2,
|
||||
unitType: 'Unit 1',
|
||||
notes: null,
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
strain: 2,
|
||||
characteristic: 1,
|
||||
textMeasurementType: 'Meas. Type 1',
|
||||
txtValue: null,
|
||||
numValue: null,
|
||||
confidenceInterval: null,
|
||||
unitType: null,
|
||||
notes: null,
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
strain: 2,
|
||||
characteristic: 2,
|
||||
textMeasurementType: 'Meas. Type 1',
|
||||
txtValue: null,
|
||||
numValue: null,
|
||||
confidenceInterval: null,
|
||||
unitType: null,
|
||||
notes: null,
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
strain: 2,
|
||||
characteristic: 3,
|
||||
textMeasurementType: null,
|
||||
txtValue: "text value",
|
||||
numValue: null,
|
||||
confidenceInterval: null,
|
||||
unitType: null,
|
||||
notes: "some notes",
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
strain: 2,
|
||||
characteristic: 4,
|
||||
textMeasurementType: null,
|
||||
txtValue: null,
|
||||
numValue: 123.4,
|
||||
confidenceInterval: null,
|
||||
unitType: 'Unit 1',
|
||||
notes: null,
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
strain: 2,
|
||||
characteristic: 5,
|
||||
textMeasurementType: null,
|
||||
txtValue: null,
|
||||
numValue: 567.8,
|
||||
confidenceInterval: 0.2,
|
||||
unitType: 'Unit 1',
|
||||
notes: null,
|
||||
testMethod: null,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
createdBy: 1,
|
||||
updatedBy: 1
|
||||
}
|
||||
]
|
||||
|
||||
measurementsRouter.get('/', function(req, res) {
|
||||
var measurements;
|
||||
if (req.query.ids) {
|
||||
measurements = MEASUREMENTS.filter(function(m) {
|
||||
return req.query.ids.indexOf(m.id.toString()) > -1;
|
||||
});
|
||||
} else {
|
||||
measurements = MEASUREMENTS;
|
||||
}
|
||||
res.send({
|
||||
'measurements': measurements
|
||||
});
|
||||
});
|
||||
|
||||
measurementsRouter.post('/', function(req, res) {
|
||||
res.status(201).end();
|
||||
});
|
||||
|
||||
measurementsRouter.get('/:id', function(req, res) {
|
||||
var measurements = MEASUREMENTS.filter(function(m) {
|
||||
return m.id == req.params.id;
|
||||
});
|
||||
res.send({
|
||||
'measurement': measurements
|
||||
});
|
||||
});
|
||||
|
||||
measurementsRouter.put('/:id', function(req, res) {
|
||||
var measurements = MEASUREMENTS.filter(function(m) {
|
||||
return m.id == req.params.id;
|
||||
});
|
||||
res.send({
|
||||
'measurement': measurements[0]
|
||||
});
|
||||
});
|
||||
|
||||
measurementsRouter.delete('/:id', function(req, res) {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.use('/api/clostridium/measurements', measurementsRouter);
|
||||
};
|
|
@ -1,105 +0,0 @@
|
|||
module.exports = function(app) {
|
||||
var express = require('express');
|
||||
var speciesRouter = express.Router();
|
||||
|
||||
var SPECIES = [
|
||||
{
|
||||
id: 1,
|
||||
genusName: "Clostridium",
|
||||
speciesName: "One",
|
||||
typeSpecies: true,
|
||||
etymology: "Test Etymology",
|
||||
strains: [1,2],
|
||||
totalStrains: 1,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
genusName: "Clostridium",
|
||||
speciesName: "Two",
|
||||
typeSpecies: true,
|
||||
etymology: "Test Etymology",
|
||||
strains: [3],
|
||||
totalStrains: 1,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
genusName: "Clostridium",
|
||||
speciesName: "Three",
|
||||
typeSpecies: true,
|
||||
etymology: "Test Etymology",
|
||||
strains: [4],
|
||||
totalStrains: 1,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
genusName: "Clostridium",
|
||||
speciesName: "Four",
|
||||
typeSpecies: true,
|
||||
etymology: "Test Etymology",
|
||||
strains: [4],
|
||||
totalStrains: 1,
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null,
|
||||
}
|
||||
];
|
||||
|
||||
speciesRouter.get('/', function(req, res) {
|
||||
var species;
|
||||
if (req.query.ids) {
|
||||
species = SPECIES.filter(function(s) {
|
||||
return req.query.ids.indexOf(s.id.toString()) > -1;
|
||||
});
|
||||
} else {
|
||||
species = SPECIES;
|
||||
}
|
||||
res.send({
|
||||
'species': species
|
||||
});
|
||||
});
|
||||
|
||||
speciesRouter.post('/', function(req, res) {
|
||||
req.body.species.id = Math.max.apply(Math, SPECIES.map(function(o){return o.id;})) + 1;
|
||||
res.status(201).send(req.body);
|
||||
});
|
||||
|
||||
speciesRouter.get('/:id', function(req, res) {
|
||||
var species = SPECIES.filter(function(s) {
|
||||
return s.id == req.params.id;
|
||||
});
|
||||
res.send({
|
||||
'species': species[0]
|
||||
});
|
||||
});
|
||||
|
||||
speciesRouter.put('/:id', function(req, res) {
|
||||
res.send(req.body);
|
||||
});
|
||||
|
||||
speciesRouter.delete('/:id', function(req, res) {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.use('/api/clostridium/species', speciesRouter);
|
||||
};
|
|
@ -1,130 +0,0 @@
|
|||
module.exports = function(app) {
|
||||
var express = require('express');
|
||||
var strainsRouter = express.Router();
|
||||
|
||||
var STRAINS = [
|
||||
{
|
||||
id: 1,
|
||||
species: 1,
|
||||
strainName: "ABC",
|
||||
typeStrain: true,
|
||||
accessionNumbers: "Test Accession",
|
||||
genbank: "Test Genbank",
|
||||
isolatedFrom: "Location 1",
|
||||
measurements: [1,2,3,4,5],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null,
|
||||
totalMeasurements: 5,
|
||||
notes: "Test notes",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
species: 1,
|
||||
strainName: "XYZ",
|
||||
typeStrain: false,
|
||||
accessionNumbers: "Test Accession",
|
||||
genbank: "Test Genbank",
|
||||
isolatedFrom: "Location 2",
|
||||
measurements: [6,7,8,9,10],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 3,
|
||||
updatedBy: 3,
|
||||
deletedBy: null,
|
||||
totalMeasurements: 5,
|
||||
notes: "Test notes",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
species: 2,
|
||||
strainName: "QRS",
|
||||
typeStrain: true,
|
||||
accessionNumbers: "Test Accession",
|
||||
genbank: "Test Genbank",
|
||||
isolatedFrom: "Location 1",
|
||||
measurements: [],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 1,
|
||||
updatedBy: 1,
|
||||
deletedBy: null,
|
||||
totalMeasurements: 0,
|
||||
notes: "Test notes",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
species: 3,
|
||||
strainName: "LMN",
|
||||
typeStrain: true,
|
||||
accessionNumbers: "Test Accession",
|
||||
genbank: "Test Genbank",
|
||||
isolatedFrom: "Location 2",
|
||||
measurements: [],
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null,
|
||||
createdBy: 3,
|
||||
updatedBy: 3,
|
||||
deletedBy: null,
|
||||
totalMeasurements: 0,
|
||||
notes: "Test notes",
|
||||
}
|
||||
];
|
||||
|
||||
strainsRouter.get('/', function(req, res) {
|
||||
var strains;
|
||||
if (req.query.ids) {
|
||||
strains = STRAINS.filter(function(s) {
|
||||
return req.query.ids.indexOf(s.id.toString()) > -1;
|
||||
});
|
||||
} else {
|
||||
strains = STRAINS;
|
||||
}
|
||||
res.send({
|
||||
'strains': strains
|
||||
});
|
||||
});
|
||||
|
||||
strainsRouter.post('/', function(req, res) {
|
||||
req.body.strain.id = Math.max.apply(Math, STRAINS.map(function(o){return o.id;})) + 1;
|
||||
res.status(201).send(req.body);
|
||||
});
|
||||
|
||||
strainsRouter.get('/:id', function(req, res) {
|
||||
var strains = STRAINS.filter(function(s) {
|
||||
return s.id == req.params.id;
|
||||
});
|
||||
res.send({
|
||||
'strain': strains[0]
|
||||
});
|
||||
});
|
||||
|
||||
strainsRouter.put('/:id', function(req, res) {
|
||||
var strains = STRAINS.filter(function(s) {
|
||||
return s.id == req.params.id;
|
||||
});
|
||||
if (strains.length === 0) {
|
||||
res.status(422).send({
|
||||
'errors':{
|
||||
"strainName": ["error1"],
|
||||
"typeStrain": ["error2", "error3"],
|
||||
"isolatedFrom": ["error4"]
|
||||
}
|
||||
}).end();
|
||||
} else {
|
||||
res.status(204).end();
|
||||
}
|
||||
});
|
||||
|
||||
strainsRouter.delete('/:id', function(req, res) {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.use('/api/clostridium/strains', strainsRouter);
|
||||
};
|
|
@ -1,75 +0,0 @@
|
|||
module.exports = function(app) {
|
||||
var express = require('express');
|
||||
var usersRouter = express.Router();
|
||||
|
||||
var USERS = [
|
||||
{
|
||||
id: 1,
|
||||
email: 'testA',
|
||||
name: 'Test Admin User',
|
||||
role: 'A',
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
email: 'testR',
|
||||
name: 'Test Read User',
|
||||
role: 'R',
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
email: 'testW',
|
||||
name: 'Test Write User',
|
||||
role: 'W',
|
||||
createdAt: "0001-01-01T00:00:00Z",
|
||||
updatedAt: "0001-01-01T00:00:00Z",
|
||||
deletedAt: null
|
||||
}
|
||||
]
|
||||
|
||||
usersRouter.get('/', function(req, res) {
|
||||
var users;
|
||||
if (req.query.ids) {
|
||||
users = USERS.filter(function(u) {
|
||||
return req.query.ids.indexOf(u.id.toString()) > -1;
|
||||
});
|
||||
} else {
|
||||
users = USERS;
|
||||
}
|
||||
res.send({
|
||||
'users': users
|
||||
});
|
||||
});
|
||||
|
||||
usersRouter.post('/', function(req, res) {
|
||||
res.status(201).end();
|
||||
});
|
||||
|
||||
usersRouter.get('/:id', function(req, res) {
|
||||
var user = USERS.filter(function(u) {
|
||||
return u.id == req.params.id;
|
||||
});
|
||||
res.send({
|
||||
'user': user
|
||||
});
|
||||
});
|
||||
|
||||
usersRouter.put('/:id', function(req, res) {
|
||||
res.send({
|
||||
'users': {
|
||||
id: req.params.id
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
usersRouter.delete('/:id', function(req, res) {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.use('/api/users', usersRouter);
|
||||
};
|
3
tests/helpers/flash-message.js
Normal file
3
tests/helpers/flash-message.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
import FlashObject from 'ember-cli-flash/flash/object';
|
||||
|
||||
FlashObject.reopen({ _destroyLater: null });
|
|
@ -1,4 +1,6 @@
|
|||
import resolver from './helpers/resolver';
|
||||
import flashMessageHelper from './helpers/flash-message';
|
||||
|
||||
import {
|
||||
setResolver
|
||||
} from 'ember-qunit';
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import {
|
||||
moduleFor,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:characteristics/index', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
var controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import {
|
||||
moduleFor,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:measurements/index', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
var controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import {
|
||||
moduleFor,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:sortable', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
var controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -1,15 +0,0 @@
|
|||
import {
|
||||
moduleFor,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:strains/index', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
var controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -1,12 +0,0 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('controller:compare', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
// Replace this with your real tests.
|
||||
test('it exists', function(assert) {
|
||||
var controller = this.subject();
|
||||
assert.ok(controller);
|
||||
});
|
|
@ -1,11 +0,0 @@
|
|||
import { moduleFor, test } from 'ember-qunit';
|
||||
|
||||
moduleFor('route:compare', 'Unit | Route | compare', {
|
||||
// Specify the other units that are required for this test.
|
||||
// needs: ['controller:foo']
|
||||
});
|
||||
|
||||
test('it exists', function(assert) {
|
||||
var route = this.subject();
|
||||
assert.ok(route);
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
import {
|
||||
moduleForComponent,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleForComponent('add-button', {
|
||||
// Specify the other units that are required for this test
|
||||
// needs: ['component:foo', 'helper:bar']
|
||||
});
|
||||
|
||||
test('it renders', function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// Creates the component instance
|
||||
var component = this.subject();
|
||||
assert.equal(component._state, 'preRender');
|
||||
|
||||
// Renders the component to the page
|
||||
this.render();
|
||||
assert.equal(component._state, 'inDOM');
|
||||
});
|
|
@ -1,21 +0,0 @@
|
|||
import {
|
||||
moduleForComponent,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleForComponent('characteristic-index-row', {
|
||||
// Specify the other units that are required for this test
|
||||
// needs: ['component:foo', 'helper:bar']
|
||||
});
|
||||
|
||||
test('it renders', function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// Creates the component instance
|
||||
var component = this.subject();
|
||||
assert.equal(component._state, 'preRender');
|
||||
|
||||
// Renders the component to the page
|
||||
this.render();
|
||||
assert.equal(component._state, 'inDOM');
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
import { moduleForComponent, test } from 'ember-qunit';
|
||||
|
||||
moduleForComponent('genbank-url', 'Unit | Component | genbank url', {
|
||||
// Specify the other units that are required for this test
|
||||
// needs: ['component:foo', 'helper:bar'],
|
||||
unit: true
|
||||
});
|
||||
|
||||
test('it renders', function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// Creates the component instance
|
||||
var component = this.subject();
|
||||
assert.equal(component._state, 'preRender');
|
||||
|
||||
// Renders the component to the page
|
||||
this.render();
|
||||
assert.equal(component._state, 'inDOM');
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
import { moduleForComponent, test } from 'ember-qunit';
|
||||
|
||||
moduleForComponent('loading-panel', 'Unit | Component | loading panel', {
|
||||
// Specify the other units that are required for this test
|
||||
// needs: ['component:foo', 'helper:bar'],
|
||||
unit: true
|
||||
});
|
||||
|
||||
test('it renders', function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// Creates the component instance
|
||||
var component = this.subject();
|
||||
assert.equal(component._state, 'preRender');
|
||||
|
||||
// Renders the component to the page
|
||||
this.render();
|
||||
assert.equal(component._state, 'inDOM');
|
||||
});
|
|
@ -1,19 +0,0 @@
|
|||
import { moduleForComponent, test } from 'ember-qunit';
|
||||
|
||||
moduleForComponent('measurement-search-panel', 'Unit | Component | measurement search panel', {
|
||||
// Specify the other units that are required for this test
|
||||
// needs: ['component:foo', 'helper:bar'],
|
||||
unit: true
|
||||
});
|
||||
|
||||
test('it renders', function(assert) {
|
||||
assert.expect(2);
|
||||
|
||||
// Creates the component instance
|
||||
var component = this.subject();
|
||||
assert.equal(component._state, 'preRender');
|
||||
|
||||
// Renders the component to the page
|
||||
this.render();
|
||||
assert.equal(component._state, 'inDOM');
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue