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 @@ +
+
+
+ Change password +
+
    +
  • + + {{one-way-input type="password" class="password" value=password update=(action "passwordDidChange")}} +
  • +
  • + + {{one-way-input type="password" class="password-confirm" value=passwordConfirm update=(action "passwordConfirmDidChange")}} +
  • +
  • + + Cancel + + {{#if matches}} + + {{/if}} +
  • +
+
+
+
+
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 @@ -
-
-
- Change password -
-
    -
  • - - {{input type="password" value=password}} -
  • -
  • - - {{input type="password" value=passwordConfirm}} -
  • -
  • - -
  • -
-
-
-
-
+{{ + 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}}

- - - - - - - - - - - {{#each model as |row|}} - - - - - - - {{/each}} - -
NameEmailRoleDate Registered
- {{#link-to 'protected.users.show' row}} - {{row.name}} - {{/link-to}} - - {{row.email}} - - {{row.fullRole}} - - {{null-time row.createdAt 'LL'}} -
+{{ + 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}}

+ + + + + + + + + + + + {{#each users as |user|}} + + + + + + + {{/each}} + +
NameEmailRoleDate Registered
+ {{#link-to 'protected.users.show' user}} + {{user.name}} + {{/link-to}} + + {{user.email}} + + {{user.fullRole}} + + {{null-time user.createdAt 'LL'}} +
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 @@ -
-
- - {{model.name}} - - - {{! ROW 1 }} -
-
-
Email
-
- {{model.email}} -
-
-
-
Role
-
- {{model.fullRole}} -
-
-
- - {{! ROW 2 }} -
-
-
Record Created
-
{{null-time model.createdAt 'LL'}}
-
-
-
Record Updated
-
{{null-time model.updatedAt 'LL'}}
-
-
-
-
-
-
- {{#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 @@ +
+
+ + {{user.name}} + + + {{! ROW 1 }} +
+
+
Email
+
+ {{user.email}} +
+
+
+
Role
+
+ {{user.fullRole}} +
+
+
+ + {{! ROW 2 }} +
+
+
Record Created
+
{{null-time user.createdAt 'LL'}}
+
+
+
Record Updated
+
{{null-time user.updatedAt 'LL'}}
+
+
+
+
+
+
+ {{#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 @@
- {{user.name}} + {{name}}
- {{input value=user.name}} + {{one-way-input type="text" class="user-name" value=name update=(action "nameDidChange")}}
- {{input value=user.email}} + {{one-way-input type="text" class="email" value=email update=(action "emailDidChange")}}
- {{#if session.currentUser.isAdmin}} - {{#each roles as |roleChoice|}} - + {{/each}} {{else}} - {{user.role}} + {{role}} {{!-- Not editable --}} {{/if}}
@@ -32,8 +32,8 @@ Cancel - {{#if user.hasDirtyAttributes}} - {{/if}} diff --git a/app/pods/users/lockoutauthenticate/controller.js b/app/pods/users/lockoutauthenticate/controller.js index 79734b5..4adee77 100644 --- a/app/pods/users/lockoutauthenticate/controller.js +++ b/app/pods/users/lockoutauthenticate/controller.js @@ -1,6 +1,8 @@ import Ember from 'ember'; -export default Ember.Controller.extend({ - queryParams: ['token'], +const { Controller } = Ember; +export default Controller.extend({ + queryParams: ['token'], + token: null, }); diff --git a/app/pods/users/lockoutauthenticate/route.js b/app/pods/users/lockoutauthenticate/route.js index 8ace7ef..21f807d 100644 --- a/app/pods/users/lockoutauthenticate/route.js +++ b/app/pods/users/lockoutauthenticate/route.js @@ -1,14 +1,16 @@ import Ember from 'ember'; import UnauthenticatedRouteMixin from 'ember-simple-auth/mixins/unauthenticated-route-mixin'; -export default Ember.Route.extend(UnauthenticatedRouteMixin, { - session: Ember.inject.service('session'), - currentUser: Ember.inject.service('session-account'), +const { Route, get, inject: { service } } = Ember; + +export default Route.extend(UnauthenticatedRouteMixin, { + session: service(), + currentUser: service('session-account'), beforeModel: function(transition) { this._super(transition); - let token = Ember.get(transition, 'queryParams.token'); + const token = get(transition, 'queryParams.token'); this.get('session').authenticate('authenticator:jwt-resolved', token).then(() => { this.get('currentUser.account').then((account) => { this.transitionTo('protected.users.changepassword', account.get('id')); diff --git a/app/pods/users/new/controller.js b/app/pods/users/new/controller.js index 3285b1a..e2bb0d8 100644 --- a/app/pods/users/new/controller.js +++ b/app/pods/users/new/controller.js @@ -1,30 +1,29 @@ import Ember from 'ember'; import ajaxError from '../../../utils/ajax-error'; -export default Ember.Controller.extend({ - passwordConfirm: null, +const { Controller } = Ember; + +export default Controller.extend({ + isLoading: false, 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; - } + save: function(properties) { + const user = this.get('model'); + user.setProperties(properties); if (user.get('hasDirtyAttributes')) { + this.set('isLoading', true); user.save().then(() => { - this.transitionTo('login').then(() => { + this.transitionToRoute('login').then(() => { this.get('flashMessages').information(`You have successfully signed up. Please check your email for further instructions.`); }); }, () => { + this.set('isLoading', false); ajaxError(user.get('errors'), this.get('flashMessages')); }); } }, + }, }); diff --git a/app/pods/users/new/new-user-form/component.js b/app/pods/users/new/new-user-form/component.js new file mode 100644 index 0000000..947080c --- /dev/null +++ b/app/pods/users/new/new-user-form/component.js @@ -0,0 +1,66 @@ +import Ember from 'ember'; + +const { Component } = Ember; + +export default Component.extend({ + // Read-only attributes + user: null, + isLoading: null, + + // Actions + "on-save": null, + "on-cancel": null, + + // Property mapping + propertiesList: ['name', 'email', 'password', 'passwordConfirm'], + name: null, + email: null, + password: null, + passwordConfirm: 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() { + // All validation is server-side, except for password verification matching + if (this.get('password') !== this.get('passwordConfirm')) { + this.get('flashMessages').clearMessages(); + this.get('flashMessages').error("Password fields don't match"); + return; + } + + return this.attrs['on-save'](this.getProperties(this.get('propertiesList'))); + }, + + nameDidChange: function(value) { + this.updateField('name', value); + }, + + emailDidChange: function(value) { + this.updateField('email', value); + }, + + passwordDidChange: function(value) { + this.updateField('password', value); + }, + + passwordConfirmDidChange: function(value) { + this.updateField('passwordConfirm', value); + } + } +}); diff --git a/app/pods/users/new/new-user-form/template.hbs b/app/pods/users/new/new-user-form/template.hbs new file mode 100644 index 0000000..980eec9 --- /dev/null +++ b/app/pods/users/new/new-user-form/template.hbs @@ -0,0 +1,36 @@ +{{#if isLoading}} + {{loading-panel}} +{{else}} +
+
+
+ New User Signup + +
    +
  • + + {{one-way-input type="text" class="user-name" value=name update=(action "nameDidChange")}} +
  • +
  • + + {{one-way-input type="text" class="email" value=email update=(action "emailDidChange")}} +
  • +
  • + + {{one-way-input type="password" class="password" value=password update=(action "passwordDidChange")}} +
  • +
  • + + {{one-way-input type="password" class="password-verify" value=passwordConfirm update=(action "passwordConfirmDidChange")}} +
  • +
  • + +
  • +
+ +
+
+
+{{/if}} diff --git a/app/pods/users/new/route.js b/app/pods/users/new/route.js index c3e757a..afdcdac 100644 --- a/app/pods/users/new/route.js +++ b/app/pods/users/new/route.js @@ -1,15 +1,10 @@ import Ember from 'ember'; import UnauthenticatedRouteMixin from 'ember-simple-auth/mixins/unauthenticated-route-mixin'; -export default Ember.Route.extend(UnauthenticatedRouteMixin, { +const { Route } = Ember; + +export default Route.extend(UnauthenticatedRouteMixin, { model: function() { - return Ember.RSVP.hash({ - user: this.store.createRecord('user'), - }); + return this.store.createRecord('user'); }, - - setupController: function(controller, model) { - controller.setProperties(model); - }, - }); diff --git a/app/pods/users/new/template.hbs b/app/pods/users/new/template.hbs index d6e81c5..ac728bf 100644 --- a/app/pods/users/new/template.hbs +++ b/app/pods/users/new/template.hbs @@ -1,32 +1,6 @@ -
-
-
- New User Signup -
-
    -
  • - - {{input value=user.name}} -
  • -
  • - - {{input value=user.email}} -
  • -
  • - - {{input type="password" value=user.password}} -
  • -
  • - - {{input type="password" value=passwordConfirm}} -
  • -
  • - -
  • -
-
-
-
-
+{{ + users/new/new-user-form + user=model + isLoading=isLoading + on-save=(action "save") +}} diff --git a/app/pods/users/new/verify/route.js b/app/pods/users/new/verify/route.js index 7c50823..06755b3 100644 --- a/app/pods/users/new/verify/route.js +++ b/app/pods/users/new/verify/route.js @@ -1,31 +1,26 @@ import Ember from 'ember'; import ajaxRequest from '../../../../utils/ajax-request'; -export default Ember.Route.extend({ - session: Ember.inject.service('session'), +const { Route, inject: { service } } = Ember; - apiURL: function() { - return this.get('globals.apiURL'); - }.property(), - - genus: function() { - return this.get('globals.genus'); - }.property(), +export default Route.extend({ + session: service(), + globals: service(), model: function(params) { - let url = `${this.get('apiURL')}/api/${this.get('genus')}/users/verify/${params.nonce}`; + const url = `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/users/verify/${params.nonce}`; return ajaxRequest(url, {}, this.get('session')); }, afterModel: function(model/*, transition*/) { - this.get('flashMessages').success(model.msg); + this.get('flashMessages').success(model.get('msg')); this.transitionTo('login'); }, actions: { error: function(error/*, transition*/) { - let err = Ember.$.parseJSON(error.responseText); + const err = Ember.$.parseJSON(error.responseText); this.get('flashMessages').error(err.error); this.transitionTo('login'); } diff --git a/app/pods/users/new/verify/template.hbs b/app/pods/users/new/verify/template.hbs deleted file mode 100644 index c24cd68..0000000 --- a/app/pods/users/new/verify/template.hbs +++ /dev/null @@ -1 +0,0 @@ -{{outlet}} diff --git a/app/pods/users/requestlockouthelp/controller.js b/app/pods/users/requestlockouthelp/controller.js index ccda273..2930b31 100644 --- a/app/pods/users/requestlockouthelp/controller.js +++ b/app/pods/users/requestlockouthelp/controller.js @@ -1,15 +1,18 @@ import Ember from 'ember'; import ajaxRequest from '../../../utils/ajax-request'; -export default Ember.Controller.extend({ - session: Ember.inject.service('session'), +const { Controller, inject: { service } } = Ember; + +export default Controller.extend({ + session: service(), + globals: service(), actions: { - save: function() { - let url = `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/users/lockout`; - let options = { + submit: function(email) { + const url = `${this.get('globals.apiURL')}/api/${this.get('globals.genus')}/users/lockout`; + const options = { method: 'POST', - data: { email: this.get('email') }, + data: { email: email }, }; ajaxRequest(url, options, this.get('session')); this.transitionToRoute('login'); diff --git a/app/pods/users/requestlockouthelp/lockout-form/component.js b/app/pods/users/requestlockouthelp/lockout-form/component.js new file mode 100644 index 0000000..2b7426f --- /dev/null +++ b/app/pods/users/requestlockouthelp/lockout-form/component.js @@ -0,0 +1,18 @@ +import Ember from 'ember'; + +const { Component } = Ember; + +export default Component.extend({ + email: null, + "on-submit": null, + + actions: { + save: function() { + return this.attrs["on-submit"](this.get('email')); + }, + + emailDidChange: function(value) { + this.set('email', value); + }, + } +}); diff --git a/app/pods/users/requestlockouthelp/lockout-form/template.hbs b/app/pods/users/requestlockouthelp/lockout-form/template.hbs new file mode 100644 index 0000000..5f18a09 --- /dev/null +++ b/app/pods/users/requestlockouthelp/lockout-form/template.hbs @@ -0,0 +1,18 @@ +
+
+
+ Account Lockout/Password Reset +
+
    +
  • + + {{one-way-input type="text" class="email" value=email update=(action "emailDidChange")}} +
  • +
  • + +
  • +
+
+
+
+
diff --git a/app/pods/users/requestlockouthelp/route.js b/app/pods/users/requestlockouthelp/route.js deleted file mode 100644 index 19831b4..0000000 --- a/app/pods/users/requestlockouthelp/route.js +++ /dev/null @@ -1,8 +0,0 @@ -import Ember from 'ember'; - -export default Ember.Route.extend({ - deactivate: function() { - this.controller.set('email', null); - }, - -}); diff --git a/app/pods/users/requestlockouthelp/template.hbs b/app/pods/users/requestlockouthelp/template.hbs index 7cdd25f..c482b70 100644 --- a/app/pods/users/requestlockouthelp/template.hbs +++ b/app/pods/users/requestlockouthelp/template.hbs @@ -1,18 +1,4 @@ -
-
-
- Account Lockout/Password Reset -
-
    -
  • - - {{input value=email}} -
  • -
  • - -
  • -
-
-
-
-
+{{ + users/requestlockouthelp/lockout-form + on-submit=(action "submit") +}} diff --git a/app/services/session-account.js b/app/services/session-account.js index 1b34554..ee09f3a 100644 --- a/app/services/session-account.js +++ b/app/services/session-account.js @@ -2,19 +2,20 @@ import Ember from 'ember'; import DS from 'ember-data'; import parseBase64 from '../utils/parse-base64'; -const { service } = Ember.inject; +const { Service, computed, isEmpty, inject: { service } } = Ember; +const { PromiseObject } = DS; -export default Ember.Service.extend({ +export default Service.extend({ session: service('session'), store: service(), - account: Ember.computed('session.data.authenticated.access_token', function() { + account: computed('session.data.authenticated.access_token', function() { const token = this.get('session.data.authenticated.access_token'); const claims = parseBase64(token); const id = claims['sub']; - if (!Ember.isEmpty(id)) { - return DS.PromiseObject.create({ + if (!isEmpty(id)) { + return PromiseObject.create({ promise: this.get('store').findRecord('user', id), }); } diff --git a/tests/acceptance/users-test.js b/tests/acceptance/users-test.js new file mode 100644 index 0000000..ed08d72 --- /dev/null +++ b/tests/acceptance/users-test.js @@ -0,0 +1,77 @@ +import Ember from 'ember'; +import { module, test } from 'qunit'; +import startApp from '../helpers/start-app'; +import { invalidateSession, authenticateSession } from '../helpers/ember-simple-auth'; + +module('Acceptance | users', { + beforeEach: function() { + this.application = startApp(); + authenticateSession(this.application, { + access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" + }); + server.create('users', { role: 'A', canEdit: true }); + }, + + afterEach: function() { + Ember.run(this.application, 'destroy'); + } +}); + +test('visiting /users', function(assert) { + const users = server.createList('users', 19); // We already created one user in beforeEach + visit('/users'); + + andThen(function() { + assert.equal(currentURL(), '/users'); + assert.equal(find(".flakes-table > tbody > tr").length, users.length + 1); + assert.equal(find("#total-users").text(), "Total users: 20"); + }); +}); + + +test('visiting /users/:id', function(assert) { + const user = server.create('users'); + visit(`/users/${user.id}`); + + andThen(function() { + assert.equal(currentURL(), `/users/${user.id}`); + assert.equal(find(".flakes-information-box > legend").text().trim(), user.name); + }); +}); + +test('editing /users/:id/edit', function(assert) { + const user = server.create('users', { 'canEdit': true }); + visit(`/users/${user.id}/edit`); + + andThen(function() { + assert.equal(currentURL(), `/users/${user.id}/edit`); + + fillIn('.user-name', 'Revised User Name'); + click('.save-user'); + + andThen(function() { + assert.equal(currentURL(), `/users/${user.id}`); + assert.equal(find(".flakes-information-box > legend").text().trim(), 'Revised User Name'); + }); + }); +}); + +test('creating /users/new', function(assert) { + invalidateSession(this.application); + visit(`/users/new`); + + andThen(function() { + assert.equal(currentURL(), `/users/new`); + fillIn('.user-name', 'New User Name'); + fillIn('.email', 'example@example.com'); + fillIn('.password', 'Password1'); + fillIn('.password-verify', 'Password1'); + click('.save-user'); + + andThen(function() { + assert.equal(currentURL(), `/login`); + assert.equal(find(".flakes-message").text().trim(), `✖ You have successfully signed up. + Please check your email for further instructions.`); + }); + }); +});