Updated merge from master
This commit is contained in:
commit
8ab97c00e1
127 changed files with 578 additions and 1802 deletions
|
@ -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}}
|
Reference in a new issue