diff --git a/app/helpers/equal.js b/app/helpers/equal.js
index db3e867..a003400 100644
--- a/app/helpers/equal.js
+++ b/app/helpers/equal.js
@@ -1,7 +1,9 @@
import Ember from 'ember';
+const { Helper: { helper } } = Ember;
+
export function equalHelper(params) {
return params[0] === params[1];
}
-export default Ember.HTMLBars.makeBoundHelper(equalHelper);
+export default helper(equalHelper);
diff --git a/app/mirage/config.js b/app/mirage/config.js
index 929ea8a..3982c0c 100644
--- a/app/mirage/config.js
+++ b/app/mirage/config.js
@@ -2,7 +2,7 @@ export default function() {
// Don't use mirage for development (for now)
this.urlPrefix = 'http://127.0.0.1:8901';
this.namespace = '/api';
- this.passthrough();
+ this.passthrough('http://localhost:4200/**', 'http://127.0.0.1:8901/**');
}
export function testConfig() {
@@ -10,7 +10,10 @@ export function testConfig() {
this.namespace = '/api/hymenobacter';
this.timing = 0;
+ this.get('/users');
+ this.post('/users');
this.get('/users/:id');
+ this.put('/users/:id');
this.get('/species');
this.post('/species');
diff --git a/app/mixins/save-model.js b/app/mixins/save-model.js
index 53efda9..8d3b233 100644
--- a/app/mixins/save-model.js
+++ b/app/mixins/save-model.js
@@ -16,6 +16,7 @@ export default Mixin.create({
if (model.get('hasDirtyAttributes')) {
model.save().then((model) => {
+ this.get('flashMessages').clearMessages();
this.transitionToRoute(fallbackRoute, model);
}, () => {
ajaxError(model.get('errors'), this.get('flashMessages'));
diff --git a/app/mixins/setup-metadata.js b/app/mixins/setup-metadata.js
index 183f93f..bdfd886 100644
--- a/app/mixins/setup-metadata.js
+++ b/app/mixins/setup-metadata.js
@@ -5,10 +5,12 @@ const { Mixin, inject: { service }} = Ember;
export default Mixin.create({
currentUser: service('session-account'),
metaData: null,
+ isAdmin: null,
setupMetaDataOnInit: Ember.on('init', function() {
this.get('currentUser.account').then((user) => {
this.set('metaData', user.get('metaData'));
+ this.set('isAdmin', user.get('isAdmin'));
});
}),
});
diff --git a/app/pods/protected/characteristics/edit/controller.js b/app/pods/protected/characteristics/edit/controller.js
index 0b83924..aff45e1 100644
--- a/app/pods/protected/characteristics/edit/controller.js
+++ b/app/pods/protected/characteristics/edit/controller.js
@@ -6,5 +6,5 @@ const { Controller } = Ember;
export default Controller.extend(SaveModel, {
// Required for SaveModel mixin
fallbackRouteSave: 'protected.characteristics.show',
- fallbackRouteCancel: 'protected.characteristics.index',
+ fallbackRouteCancel: 'protected.characteristics.show',
});
diff --git a/app/pods/protected/users/changepassword/controller.js b/app/pods/protected/users/changepassword/controller.js
index b8d100e..3aed813 100644
--- a/app/pods/protected/users/changepassword/controller.js
+++ b/app/pods/protected/users/changepassword/controller.js
@@ -1,33 +1,30 @@
import Ember from 'ember';
import ajaxRequest from '../../../../utils/ajax-request';
-export default Ember.Controller.extend({
- session: Ember.inject.service('session'),
- currentUser: Ember.inject.service('session-account'),
+const { Controller, inject: { service } } = Ember;
- passwordConfirm: null,
+export default Controller.extend({
+ session: service(),
+ currentUser: service('session-account'),
actions: {
- save: function() {
- if (this.get('password') !== this.get('passwordConfirm')) {
- this.get('flashMessages').clearMessages();
- this.get('flashMessages').error("Password fields don't match");
- return;
- }
-
- let url = `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/users/password`;
- let options = {
+ save: function(password) {
+ const url = `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/users/password`;
+ const id = this.get('currentUser.account.id');
+ const options = {
method: 'POST',
data: {
- id: this.get('currentUser.account.id'),
- password: this.get('password'),
+ id: id,
+ password: password,
},
};
ajaxRequest(url, options, this.get('session'));
- this.transitionToRoute('protected.users.index');
+ this.transitionToRoute('protected.users.show', id);
this.get('flashMessages').information('Your password has been changed.');
},
+ cancel: function() {
+ this.transitionToRoute('protected.users.show', this.get('currentUser.account.id'));
+ },
},
-
});
diff --git a/app/pods/protected/users/changepassword/password-form/component.js b/app/pods/protected/users/changepassword/password-form/component.js
new file mode 100644
index 0000000..494885e
--- /dev/null
+++ b/app/pods/protected/users/changepassword/password-form/component.js
@@ -0,0 +1,52 @@
+import Ember from 'ember';
+
+const { Component } = Ember;
+
+export default Component.extend({
+ password: null,
+ passwordConfirm: null,
+ matches: false,
+
+ // Actions
+ "on-save": null,
+ "on-cancel": null,
+
+ updateField: function(property, value) {
+ this.set(property, value);
+ this.verifyPassword(this.get('password'), this.get('passwordConfirm'));
+ },
+
+ verifyPassword: function(password, passwordConfirm) {
+ if (password && passwordConfirm) {
+ if (password !== passwordConfirm) {
+ this.get('flashMessages').clearMessages();
+ this.get('flashMessages').error("Password fields don't match");
+ this.set('matches', false);
+ } else {
+ this.get('flashMessages').clearMessages();
+ this.set('matches', true);
+ }
+ }
+ },
+
+ actions: {
+ save: function() {
+ this.verifyPassword(this.get('password'), this.get('passwordConfirm'));
+ if (this.get('matches')) {
+ return this.attrs['on-save'](this.get('password'));
+ }
+ },
+
+ cancel: function() {
+ return this.attrs['on-cancel']();
+ },
+
+ passwordDidChange: function(value) {
+ this.updateField('password', value);
+ },
+
+ passwordConfirmDidChange: function(value) {
+ this.updateField('passwordConfirm', value);
+ },
+ },
+});
diff --git a/app/pods/protected/users/changepassword/password-form/template.hbs b/app/pods/protected/users/changepassword/password-form/template.hbs
new file mode 100644
index 0000000..5d30c35
--- /dev/null
+++ b/app/pods/protected/users/changepassword/password-form/template.hbs
@@ -0,0 +1,29 @@
+
+
+
+
+
diff --git a/app/pods/protected/users/changepassword/route.js b/app/pods/protected/users/changepassword/route.js
index ce869dd..f6da398 100644
--- a/app/pods/protected/users/changepassword/route.js
+++ b/app/pods/protected/users/changepassword/route.js
@@ -1,16 +1,19 @@
import Ember from 'ember';
-export default Ember.Route.extend({
- currentUser: Ember.inject.service('session-account'),
+const { Route, inject: { service } } = Ember;
+
+export default Route.extend({
+ currentUser: service('session-account'),
beforeModel: function(transition) {
this._super(transition);
- let user_id = transition.params['protected.users.changepassword'].user_id;
+ // Only the logged in user can change their password
+ const user_id = transition.params['protected.users.changepassword'].user_id;
this.get('currentUser.account').then((user) => {
if (user.get('id') !== user_id) {
- this.transitionTo('protected.users.index');
+ this.transitionTo('protected.users.show', user.get('id'));
}
});
}
diff --git a/app/pods/protected/users/changepassword/template.hbs b/app/pods/protected/users/changepassword/template.hbs
index b22db6c..b7509c1 100644
--- a/app/pods/protected/users/changepassword/template.hbs
+++ b/app/pods/protected/users/changepassword/template.hbs
@@ -1,24 +1,5 @@
-
-
-
-
-
+{{
+ protected/users/changepassword/password-form
+ on-save=(action "save")
+ on-cancel=(action "cancel")
+}}
diff --git a/app/pods/protected/users/edit/controller.js b/app/pods/protected/users/edit/controller.js
index 955c328..283631c 100644
--- a/app/pods/protected/users/edit/controller.js
+++ b/app/pods/protected/users/edit/controller.js
@@ -1,40 +1,10 @@
import Ember from 'ember';
-import ajaxError from '../../../../utils/ajax-error';
+import SaveModel from '../../../../mixins/save-model';
-export default Ember.Controller.extend({
- actions: {
- save: function() {
- let user = this.get('model');
+const { Controller } = Ember;
- if (user.get('hasDirtyAttributes')) {
- let attrs = user.changedAttributes(), roleChanged = false;
- if (attrs.role) {
- roleChanged = true;
- }
- user.save().then((user) => {
- this.get('flashMessages').clearMessages();
- if (roleChanged) {
- // Need to clear the store so that canEdit and canAdd
- // attributes reflect the new role.
- this.get('store').unloadAll();
- }
- this.transitionToRoute('protected.users.show', user);
- }, () => {
- ajaxError(user.get('errors'), this.get('flashMessages'));
- });
- } else {
- this.transitionToRoute('protected.users.show', user);
- }
- },
-
- cancel: function() {
- let user = this.get('model');
-
- user.get('errors').clear();
- user.rollbackAttributes();
-
- this.transitionToRoute('protected.users.show', user);
- },
-
- },
+export default Controller.extend(SaveModel, {
+ // Required for SaveModel mixin
+ fallbackRouteSave: 'protected.users.show',
+ fallbackRouteCancel: 'protected.users.show',
});
diff --git a/app/pods/protected/users/edit/route.js b/app/pods/protected/users/edit/route.js
index b95bdb2..c91288e 100644
--- a/app/pods/protected/users/edit/route.js
+++ b/app/pods/protected/users/edit/route.js
@@ -1,8 +1,12 @@
import Ember from 'ember';
-export default Ember.Route.extend({
- currentUser: Ember.inject.service('session-account'),
+const { Route, inject: { service } } = Ember;
+export default Route.extend({
+ currentUser: service('session-account'),
+
+ // Not using ElevatedAccess Mixin because the rules for viewing user accounts
+ // is slightly different.
beforeModel: function(transition) {
this._super(transition);
@@ -16,7 +20,7 @@ export default Ember.Route.extend({
},
model: function(params) {
- return this.store.findRecord('user', params.user_id, { reload: true });
+ return this.store.findRecord('user', params.user_id);
},
});
diff --git a/app/pods/protected/users/edit/template.hbs b/app/pods/protected/users/edit/template.hbs
index e53c6e8..60cbe0d 100644
--- a/app/pods/protected/users/edit/template.hbs
+++ b/app/pods/protected/users/edit/template.hbs
@@ -1,7 +1,6 @@
{{
protected/users/user-form
user=model
- currentUser=currentUser.account
- save="save"
- cancel="cancel"
+ on-save=(action "save")
+ on-cancel=(action "cancel")
}}
diff --git a/app/pods/protected/users/index/route.js b/app/pods/protected/users/index/route.js
index 8429726..2752983 100644
--- a/app/pods/protected/users/index/route.js
+++ b/app/pods/protected/users/index/route.js
@@ -1,7 +1,9 @@
import Ember from 'ember';
-export default Ember.Route.extend({
- currentUser: Ember.inject.service('session-account'),
+const { Route, inject: { service } } = Ember;
+
+export default Route.extend({
+ currentUser: service('session-account'),
beforeModel: function(transition) {
this._super(transition);
diff --git a/app/pods/protected/users/index/template.hbs b/app/pods/protected/users/index/template.hbs
index adb5b55..f75072a 100644
--- a/app/pods/protected/users/index/template.hbs
+++ b/app/pods/protected/users/index/template.hbs
@@ -1,33 +1,6 @@
{{genus-name}} Users
-Total users: {{model.length}}
-
-
-
- Name |
- Email |
- Role |
- Date Registered |
-
-
-
- {{#each model as |row|}}
-
-
- {{#link-to 'protected.users.show' row}}
- {{row.name}}
- {{/link-to}}
- |
-
- {{row.email}}
- |
-
- {{row.fullRole}}
- |
-
- {{null-time row.createdAt 'LL'}}
- |
-
- {{/each}}
-
-
+{{
+ protected/users/index/users-table
+ users=model
+}}
diff --git a/app/pods/protected/users/index/users-table/component.js b/app/pods/protected/users/index/users-table/component.js
new file mode 100644
index 0000000..2abad45
--- /dev/null
+++ b/app/pods/protected/users/index/users-table/component.js
@@ -0,0 +1,7 @@
+import Ember from 'ember';
+
+const { Component } = Ember;
+
+export default Component.extend({
+ users: null,
+});
diff --git a/app/pods/protected/users/index/users-table/template.hbs b/app/pods/protected/users/index/users-table/template.hbs
new file mode 100644
index 0000000..ad2a064
--- /dev/null
+++ b/app/pods/protected/users/index/users-table/template.hbs
@@ -0,0 +1,33 @@
+
+Total users: {{users.length}}
+
+
+
+
+ Name |
+ Email |
+ Role |
+ Date Registered |
+
+
+
+ {{#each users as |user|}}
+
+
+ {{#link-to 'protected.users.show' user}}
+ {{user.name}}
+ {{/link-to}}
+ |
+
+ {{user.email}}
+ |
+
+ {{user.fullRole}}
+ |
+
+ {{null-time user.createdAt 'LL'}}
+ |
+
+ {{/each}}
+
+
diff --git a/app/pods/protected/users/loading/template.hbs b/app/pods/protected/users/loading/template.hbs
new file mode 100644
index 0000000..e5a3e05
--- /dev/null
+++ b/app/pods/protected/users/loading/template.hbs
@@ -0,0 +1 @@
+{{loading-panel}}
diff --git a/app/pods/protected/users/show/controller.js b/app/pods/protected/users/show/controller.js
index 3c6c549..26c243d 100644
--- a/app/pods/protected/users/show/controller.js
+++ b/app/pods/protected/users/show/controller.js
@@ -1,9 +1,9 @@
import Ember from 'ember';
+import DeleteModel from '../../../../mixins/delete-model';
-export default Ember.Controller.extend({
- currentUser: Ember.inject.service('session-account'),
+const { Controller } = Ember;
- isUser: Ember.computed('model.id', 'currentUser.account.id', function() {
- return this.get('model.id') === this.get('currentUser.account.id');
- }),
+export default Controller.extend(DeleteModel, {
+ // Required for DeleteModel mixin
+ transitionRoute: 'protected.index',
});
diff --git a/app/pods/protected/users/show/route.js b/app/pods/protected/users/show/route.js
index a0c9fd9..798787d 100644
--- a/app/pods/protected/users/show/route.js
+++ b/app/pods/protected/users/show/route.js
@@ -1,21 +1,25 @@
import Ember from 'ember';
-export default Ember.Route.extend({
- currentUser: Ember.inject.service('session-account'),
+const { Route, inject: { service } } = Ember;
+export default Route.extend({
+ currentUser: service('session-account'),
+
+ // Not using ElevatedAccess Mixin because the rules for viewing user accounts
+ // is slightly different.
beforeModel: function(transition) {
this._super(transition);
- this.get('currentUser.account').then((currentUser) => {
- let user_id = transition.params['protected.users.show'].user_id;
- if (!currentUser.get('isAdmin') && currentUser.get('id') !== user_id) {
+ this.get('currentUser.account').then((user) => {
+ const user_id = transition.params['protected.users.show'].user_id;
+ if (!user.get('isAdmin') && user.get('id') !== user_id) {
this.transitionTo('protected.users.index');
}
});
},
model: function(params) {
- return this.store.findRecord('user', params.user_id, { reload: true });
+ return this.store.findRecord('user', params.user_id);
},
});
diff --git a/app/pods/protected/users/show/template.hbs b/app/pods/protected/users/show/template.hbs
index ca89688..a8648c4 100644
--- a/app/pods/protected/users/show/template.hbs
+++ b/app/pods/protected/users/show/template.hbs
@@ -1,53 +1,5 @@
-
-
-
-
-
- {{#if isUser}}
-
- {{#link-to 'protected.users.changepassword' model.id class="button-gray smaller"}}
- Change Password
- {{/link-to}}
-
- {{/if}}
-
-
- {{#if model.canEdit}}
- {{#link-to 'protected.users.edit' model.id class="button-gray smaller"}}
- Edit
- {{/link-to}}
- {{/if}}
-
-
+{{
+ protected/users/show/user-card
+ user=model
+ on-delete=(action 'delete')
+}}
diff --git a/app/pods/protected/users/show/user-card/component.js b/app/pods/protected/users/show/user-card/component.js
new file mode 100644
index 0000000..00e3164
--- /dev/null
+++ b/app/pods/protected/users/show/user-card/component.js
@@ -0,0 +1,13 @@
+import Ember from 'ember';
+
+const { Component, computed, inject: { service } } = Ember;
+
+export default Component.extend({
+ currentUser: service('session-account'),
+
+ user: null,
+
+ isUser: computed('user.id', 'currentUser.account.id', function() {
+ return this.get('user.id') === this.get('currentUser.account.id');
+ }),
+});
diff --git a/app/pods/protected/users/show/user-card/template.hbs b/app/pods/protected/users/show/user-card/template.hbs
new file mode 100644
index 0000000..5ced125
--- /dev/null
+++ b/app/pods/protected/users/show/user-card/template.hbs
@@ -0,0 +1,53 @@
+
+
+
+
+
+ {{#if isUser}}
+
+ {{#link-to 'protected.users.changepassword' user.id class="button-gray smaller"}}
+ Change Password
+ {{/link-to}}
+
+ {{/if}}
+
+
+ {{#if user.canEdit}}
+ {{#link-to 'protected.users.edit' user.id class="button-gray smaller"}}
+ Edit
+ {{/link-to}}
+ {{/if}}
+
+
diff --git a/app/pods/protected/users/user-form/component.js b/app/pods/protected/users/user-form/component.js
index 0a706f3..28fb03a 100644
--- a/app/pods/protected/users/user-form/component.js
+++ b/app/pods/protected/users/user-form/component.js
@@ -1,15 +1,61 @@
import Ember from 'ember';
+import SetupMetaData from '../../../../mixins/setup-metadata';
-export default Ember.Component.extend({
+const { Component } = Ember;
+
+export default Component.extend(SetupMetaData, {
+ // Read-only attributes
+ user: null,
+ isDirty: false,
roles: Ember.String.w('A R W'),
+ // Actions
+ "on-save": null,
+ "on-cancel": null,
+ "on-update": null,
+
+ // Property mapping
+ propertiesList: ['name', 'email', 'role'],
+ name: null,
+ email: null,
+ role: null,
+
+ resetOnInit: Ember.on('init', function() {
+ this.get('propertiesList').forEach((field) => {
+ const valueInUser = this.get('user').get(field);
+ this.set(field, valueInUser);
+ });
+ }),
+
+ updateField: function(property, value) {
+ this.set(property, value);
+ // Manually compare against passed in value
+ if (this.get('user').get(property) !== value) {
+ this.set('isDirty', true);
+ } else {
+ this.set('isDirty', false);
+ }
+ },
+
actions: {
save: function() {
- this.sendAction('save');
+ return this.attrs['on-save'](this.getProperties(this.get('propertiesList')));
},
cancel: function() {
- this.sendAction('cancel');
+ return this.attrs['on-cancel']();
},
- }
+
+ nameDidChange: function(value) {
+ this.updateField('name', value);
+ },
+
+ emailDidChange: function(value) {
+ this.updateField('email', value);
+ },
+
+ roleDidChange: function(value) {
+ this.updateField('role', value);
+ },
+ },
});
diff --git a/app/pods/protected/users/user-form/template.hbs b/app/pods/protected/users/user-form/template.hbs
index c6209a9..cb0e27f 100644
--- a/app/pods/protected/users/user-form/template.hbs
+++ b/app/pods/protected/users/user-form/template.hbs
@@ -1,29 +1,29 @@