Rough in species
This commit is contained in:
parent
dfc62cd1ac
commit
afcf24a8d8
22 changed files with 399 additions and 2 deletions
17
app/abilities/species.js
Normal file
17
app/abilities/species.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { Ability } from 'ember-can';
|
||||
|
||||
export default Ability.extend({
|
||||
// Only admins and writers can create a new species
|
||||
canAdd: function() {
|
||||
let role = this.get('session.currentUser.role');
|
||||
return (role === 'W') || (role === 'A');
|
||||
}.property('session.currentUser.role'),
|
||||
|
||||
// Only admins and the person who created can edit
|
||||
canEdit: function() {
|
||||
let role = this.get('session.currentUser.role');
|
||||
let id = this.get('session.currentUser.id');
|
||||
let author = this.get('model.createdBy');
|
||||
return (role === 'W' && (+id === author)) || (role === 'A');
|
||||
}.property('session.currentUser.role', 'session.currentUser.id', 'model.createdBy')
|
||||
});
|
20
app/components/species/species-details.js
Normal file
20
app/components/species/species-details.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['grid-1'],
|
||||
isEditing: false,
|
||||
isNew: false,
|
||||
actions: {
|
||||
editSpecies: function() {
|
||||
this.get('species').get('errors').clear();
|
||||
if (this.get('isNew')) {
|
||||
this.get('species').destroyRecord().then(this.sendAction());
|
||||
}
|
||||
this.toggleProperty('isEditing');
|
||||
this.get('species').rollback();
|
||||
},
|
||||
saveSpecies: function() {
|
||||
this.get('species').save().then(this.toggleProperty('isEditing'));
|
||||
}
|
||||
}
|
||||
});
|
16
app/models/species.js
Normal file
16
app/models/species.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
import DS from 'ember-data';
|
||||
|
||||
export default DS.Model.extend({
|
||||
speciesName: DS.attr('string'),
|
||||
typeSpecies: DS.attr('boolean'),
|
||||
etymology: DS.attr('string'),
|
||||
genusName: DS.attr('string'),
|
||||
strains: DS.hasMany('strain'),
|
||||
totalStrains: DS.attr('number'),
|
||||
createdAt: DS.attr('date'),
|
||||
updatedAt: DS.attr('date'),
|
||||
deletedAt: DS.attr('date'),
|
||||
createdBy: DS.attr('number'),
|
||||
updatedBy: DS.attr('number'),
|
||||
deletedBy: DS.attr('number'),
|
||||
});
|
|
@ -8,6 +8,10 @@ var Router = Ember.Router.extend({
|
|||
Router.map(function() {
|
||||
this.route('login');
|
||||
this.route('about');
|
||||
this.resource('species', function() {
|
||||
this.route('show', { path: ':species_id' });
|
||||
this.route('new');
|
||||
});
|
||||
this.resource('strains', function() {
|
||||
this.route('new');
|
||||
this.route('show', { path: ':strain_id' }, function() {
|
||||
|
|
4
app/routes/species.js
Normal file
4
app/routes/species.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);
|
7
app/routes/species/index.js
Normal file
7
app/routes/species/index.js
Normal file
|
@ -0,0 +1,7 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model: function() {
|
||||
return this.store.findAll('species');
|
||||
}
|
||||
});
|
12
app/routes/species/new.js
Normal file
12
app/routes/species/new.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model: function() {
|
||||
return this.store.createRecord('species');
|
||||
},
|
||||
actions: {
|
||||
cancelSpecies: function() {
|
||||
this.transitionTo('species.index');
|
||||
}
|
||||
}
|
||||
});
|
4
app/routes/species/show.js
Normal file
4
app/routes/species/show.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
import Ember from 'ember';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
});
|
|
@ -4,6 +4,9 @@
|
|||
{{/link-to}}
|
||||
{{#if session.isAuthenticated}}
|
||||
<ul>
|
||||
{{#link-to 'species' tagName='li' href=false}}
|
||||
{{#link-to 'species'}}Species{{/link-to}}
|
||||
{{/link-to}}
|
||||
{{#link-to 'strains' tagName='li' href=false}}
|
||||
{{#link-to 'strains'}}Strains{{/link-to}}
|
||||
{{/link-to}}
|
||||
|
|
76
app/templates/components/species/species-details.hbs
Normal file
76
app/templates/components/species/species-details.hbs
Normal file
|
@ -0,0 +1,76 @@
|
|||
<div class="span-1">
|
||||
<fieldset {{bind-attr class=":flakes-information-box isEditing"}}>
|
||||
<legend>
|
||||
Species
|
||||
{{#if isEditing}}
|
||||
{{input value=species.speciesName}}
|
||||
{{else}}
|
||||
{{species.speciesName}}
|
||||
{{/if}}
|
||||
{{display-errors a=species.errors.speciesName}}
|
||||
</legend>
|
||||
|
||||
{{! ROW 1 }}
|
||||
<div class="grid-4">
|
||||
<dl class="span-4">
|
||||
<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 (can "edit species" strain)}}
|
||||
<div class="grid-4">
|
||||
<div class="span-1">
|
||||
{{! Does nothing ATM }}
|
||||
<a {{bind-attr class=":smaller isEditing:button-red:button-gray"}} {{action 'editSpecies'}}>
|
||||
{{#if isEditing}}Cancel{{else}}Edit{{/if}}
|
||||
</a>
|
||||
{{#if isEditing}}
|
||||
<a class="button-green smaller" {{action 'saveSpecies'}}>
|
||||
Save
|
||||
</a>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{/if}}
|
||||
</fieldset>
|
||||
</div>
|
1
app/templates/species.hbs
Normal file
1
app/templates/species.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{outlet}}
|
30
app/templates/species/index.hbs
Normal file
30
app/templates/species/index.hbs
Normal file
|
@ -0,0 +1,30 @@
|
|||
<h2>{{genus-name}} Species</h2>
|
||||
<h3>Total species: {{controller.length}}</h3>
|
||||
|
||||
{{#if (can "add species")}}
|
||||
{{! Does nothing ATM }}
|
||||
{{#link-to 'species.new' class="button-gray smaller"}}
|
||||
Add Species
|
||||
{{/link-to}}
|
||||
{{/if}}
|
||||
|
||||
<table class="flakes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th {{action "setSortBy" "speciesName"}}>Name</th>
|
||||
<th {{action "setSortBy" "totalStrains"}}>Strains</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each species in controller}}
|
||||
<tr>
|
||||
<td>
|
||||
{{#link-to 'species.show' species}}
|
||||
{{species.speciesName}}
|
||||
{{/link-to}}
|
||||
</td>
|
||||
<td>{{species.totalStrains}}</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
1
app/templates/species/new.hbs
Normal file
1
app/templates/species/new.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{species/species-details species=model isEditing=true isNew=true action="cancelSpecies"}}
|
1
app/templates/species/show.hbs
Normal file
1
app/templates/species/show.hbs
Normal file
|
@ -0,0 +1 @@
|
|||
{{species/species-details species=model}}
|
|
@ -21,6 +21,7 @@
|
|||
"devDependencies": {
|
||||
"body-parser": "^1.12.2",
|
||||
"broccoli-asset-rev": "^2.0.2",
|
||||
"connect-restreamer": "^1.0.2",
|
||||
"ember-can": "^0.4.0",
|
||||
"ember-cli": "0.2.3",
|
||||
"ember-cli-app-version": "0.3.3",
|
||||
|
@ -37,9 +38,9 @@
|
|||
"ember-cli-uglify": "1.0.1",
|
||||
"ember-data": "1.0.0-beta.16.1",
|
||||
"ember-export-application-global": "^1.0.2",
|
||||
"express": "^4.12.3",
|
||||
"express": "^4.12.4",
|
||||
"glob": "^4.5.3",
|
||||
"jsonwebtoken": "^5.0.0",
|
||||
"morgan": "^1.5.2"
|
||||
"morgan": "^1.5.3"
|
||||
}
|
||||
}
|
||||
|
|
108
server/mocks/species.js
Normal file
108
server/mocks/species.js
Normal file
|
@ -0,0 +1,108 @@
|
|||
module.exports = function(app) {
|
||||
var express = require('express');
|
||||
var speciesRouter = express.Router();
|
||||
|
||||
var SPECIES = [
|
||||
{
|
||||
id: 1,
|
||||
genusName: "Hymenobacter",
|
||||
speciesName: "One",
|
||||
typeSpecies: true,
|
||||
etymology: "Test Etymology",
|
||||
strains: [1],
|
||||
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: "Hymenobacter",
|
||||
speciesName: "Two",
|
||||
typeSpecies: true,
|
||||
etymology: "Test Etymology",
|
||||
strains: [2],
|
||||
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: "Hymenobacter",
|
||||
speciesName: "Three",
|
||||
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: 4,
|
||||
genusName: "Hymenobacter",
|
||||
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) {
|
||||
res.status(201).end();
|
||||
});
|
||||
|
||||
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({
|
||||
'species': {
|
||||
id: req.params.id
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
speciesRouter.delete('/:id', function(req, res) {
|
||||
res.status(204).end();
|
||||
});
|
||||
|
||||
app.use('/api/hymenobacter/species', speciesRouter);
|
||||
};
|
21
tests/unit/components/species/species-details-test.js
Normal file
21
tests/unit/components/species/species-details-test.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
import {
|
||||
moduleForComponent,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleForComponent('species/species-details', {
|
||||
// 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');
|
||||
});
|
15
tests/unit/models/species-test.js
Normal file
15
tests/unit/models/species-test.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
import {
|
||||
moduleForModel,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleForModel('species', {
|
||||
// Specify the other units that are required for this test.
|
||||
needs: ['model:strain']
|
||||
});
|
||||
|
||||
test('it exists', function(assert) {
|
||||
var model = this.subject();
|
||||
// var store = this.store();
|
||||
assert.ok(!!model);
|
||||
});
|
14
tests/unit/routes/species-test.js
Normal file
14
tests/unit/routes/species-test.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {
|
||||
moduleFor,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleFor('route:species', {
|
||||
// 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);
|
||||
});
|
14
tests/unit/routes/species/index-test.js
Normal file
14
tests/unit/routes/species/index-test.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {
|
||||
moduleFor,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleFor('route:species/index', {
|
||||
// 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);
|
||||
});
|
14
tests/unit/routes/species/new-test.js
Normal file
14
tests/unit/routes/species/new-test.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {
|
||||
moduleFor,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleFor('route:species/new', {
|
||||
// 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);
|
||||
});
|
14
tests/unit/routes/species/show-test.js
Normal file
14
tests/unit/routes/species/show-test.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import {
|
||||
moduleFor,
|
||||
test
|
||||
} from 'ember-qunit';
|
||||
|
||||
moduleFor('route:species/show', {
|
||||
// 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);
|
||||
});
|
Reference in a new issue