Compare commits
	
		
			5 commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | f0167c858d | ||
|   | 6f15bc940d | ||
|   | a4fc06d22d | ||
|   | dbcc51c80d | ||
|   | 9ed63ca5ba | 
					 66 changed files with 299 additions and 13666 deletions
				
			
		
							
								
								
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -15,4 +15,4 @@ | ||||||
| /libpeerconnection.log | /libpeerconnection.log | ||||||
| npm-debug.log | npm-debug.log | ||||||
| testem.log | testem.log | ||||||
| .firebase | .divshot-cache | ||||||
|  | @ -1,7 +0,0 @@ | ||||||
| import ApplicationAdapter from '../adapters/application'; |  | ||||||
| 
 |  | ||||||
| export default ApplicationAdapter.extend({ |  | ||||||
|   // If coalesceFindRequests is on, and we 403 on any requests, ESA logs
 |  | ||||||
|   // the current user out. Better to split the requests up at the adapter level.
 |  | ||||||
|   coalesceFindRequests: false, |  | ||||||
| }); |  | ||||||
|  | @ -1,70 +1,6 @@ | ||||||
| import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant'; | import OAuth2PasswordGrant from 'ember-simple-auth/authenticators/oauth2-password-grant'; | ||||||
| import config from '../config/environment'; | import config from '../config/environment'; | ||||||
| import parseBase64 from '../utils/parse-base64'; |  | ||||||
| import Ember from 'ember'; |  | ||||||
| const { RSVP: { Promise }, isEmpty, run, Logger: { warn } } = Ember; |  | ||||||
| 
 | 
 | ||||||
| export default OAuth2PasswordGrant.extend({ | export default OAuth2PasswordGrant.extend({ | ||||||
|   serverTokenEndpoint: `${config.apiURL}/api/authenticate`, |   serverTokenEndpoint: `${config.apiURL}/api/authenticate`, | ||||||
|   serverTokenRefreshEndpoint: `${config.apiURL}/api/refresh`, |  | ||||||
| 
 |  | ||||||
|   authenticate: function(identification, password) { |  | ||||||
|     return new Promise((resolve, reject) => { |  | ||||||
|       const data = { username: identification, password }; |  | ||||||
|       const serverTokenEndpoint = this.get('serverTokenEndpoint'); |  | ||||||
|       this.makeRequest(serverTokenEndpoint, data).then((response) => { |  | ||||||
|         run(() => { |  | ||||||
|           const token = parseBase64(response['access_token']); |  | ||||||
|           const expiresAt = this._absolutizeExpirationTime(token['exp']); |  | ||||||
|           this._scheduleAccessTokenRefresh(expiresAt, response['access_token']); |  | ||||||
|           if (!isEmpty(expiresAt)) { |  | ||||||
|             response = Ember.merge(response, { 'expires_at': expiresAt }); |  | ||||||
|           } |  | ||||||
|           resolve(response); |  | ||||||
|         }); |  | ||||||
|       }, (xhr) => { |  | ||||||
|         run(null, reject, xhr.responseJSON || xhr.responseText); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }, |  | ||||||
| 
 |  | ||||||
|   _scheduleAccessTokenRefresh: function(expiresAt, accessToken) { |  | ||||||
|     if (this.get('refreshAccessTokens')) { |  | ||||||
|       const now = (new Date()).getTime(); |  | ||||||
|       const offset = (Math.floor(Math.random() * 5) + 5) * 1000; |  | ||||||
|       if (!isEmpty(accessToken) && !isEmpty(expiresAt) && expiresAt > now - offset) { |  | ||||||
|         run.cancel(this._refreshTokenTimeout); |  | ||||||
|         delete this._refreshTokenTimeout; |  | ||||||
|         if (!Ember.testing) { |  | ||||||
|           this._refreshTokenTimeout = run.later(this, this._refreshAccessToken, expiresAt, accessToken, expiresAt - now - offset); |  | ||||||
|         } |  | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
| 
 |  | ||||||
|   _refreshAccessToken: function(expiresAt, accessToken) { |  | ||||||
|     const data = { 'token': accessToken }; |  | ||||||
|     const serverTokenRefreshEndpoint = this.get('serverTokenRefreshEndpoint'); |  | ||||||
|     return new Promise((resolve, reject) => { |  | ||||||
|       this.makeRequest(serverTokenRefreshEndpoint, data).then((response) => { |  | ||||||
|         run(() => { |  | ||||||
|           const token = parseBase64(response['access_token']); |  | ||||||
|           const expiresAt = this._absolutizeExpirationTime(token['exp']); |  | ||||||
|           const data = Ember.merge(response, { 'expires_at': expiresAt }); |  | ||||||
|           this._scheduleAccessTokenRefresh(expiresAt, response['access_token']); |  | ||||||
|           this.trigger('sessionDataUpdated', data); |  | ||||||
|           resolve(data); |  | ||||||
|         }); |  | ||||||
|       }, (xhr, status, error) => { |  | ||||||
|         warn(`Access token could not be refreshed - server responded with ${error}.`); |  | ||||||
|         reject(); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }, |  | ||||||
| 
 |  | ||||||
|   _absolutizeExpirationTime: function(expiresAt) { |  | ||||||
|     if (!isEmpty(expiresAt)) { |  | ||||||
|       return new Date(expiresAt * 1000).getTime(); |  | ||||||
|     } |  | ||||||
|   } |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -2,6 +2,7 @@ import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { get, Helper: { helper } } = Ember; | const { get, Helper: { helper } } = Ember; | ||||||
| 
 | 
 | ||||||
|  | // This will be unneccesary when ember 2.0 lands
 | ||||||
| export default helper(function(params) { | export default helper(function(params) { | ||||||
|   return get(params[0], params[1]); |   return get(params[0], params[1]); | ||||||
| }); | }); | ||||||
|  |  | ||||||
							
								
								
									
										20
									
								
								app/initializers/global-variables.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								app/initializers/global-variables.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | ||||||
|  | import Ember from 'ember'; | ||||||
|  | import config from '../config/environment'; | ||||||
|  | 
 | ||||||
|  | var globals = Ember.Object.extend({ | ||||||
|  |   genus: config.APP.genus, | ||||||
|  |   apiURL: config.apiURL, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export function initialize(container, application) { | ||||||
|  |   application.register('service:globals', globals, {singleton: true}); | ||||||
|  |   application.inject('route', 'globals', 'service:globals'); | ||||||
|  |   application.inject('controller', 'globals', 'service:globals'); | ||||||
|  |   application.inject('component', 'globals', 'service:globals'); | ||||||
|  |   application.inject('adapter', 'globals', 'service:globals'); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export default { | ||||||
|  |   name: 'global-variables', | ||||||
|  |   initialize: initialize | ||||||
|  | }; | ||||||
|  | @ -19,13 +19,11 @@ export function testConfig() { | ||||||
|   this.post('/species'); |   this.post('/species'); | ||||||
|   this.get('/species/:id'); |   this.get('/species/:id'); | ||||||
|   this.put('/species/:id'); |   this.put('/species/:id'); | ||||||
|   this.delete('/species/:id'); |  | ||||||
| 
 | 
 | ||||||
|   this.get('/characteristics'); |   this.get('/characteristics'); | ||||||
|   this.post('/characteristics'); |   this.post('/characteristics'); | ||||||
|   this.get('/characteristics/:id'); |   this.get('/characteristics/:id'); | ||||||
|   this.put('/characteristics/:id'); |   this.put('/characteristics/:id'); | ||||||
|   this.delete('/characteristics/:id'); |  | ||||||
| 
 | 
 | ||||||
|   this.get('/strains', function(db /*, request*/) { |   this.get('/strains', function(db /*, request*/) { | ||||||
|     return { |     return { | ||||||
|  | @ -41,5 +39,4 @@ export function testConfig() { | ||||||
|     }; |     }; | ||||||
|   }); |   }); | ||||||
|   this.put('/strains/:id'); |   this.put('/strains/:id'); | ||||||
|   this.delete('/strains/:id'); |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -8,10 +8,6 @@ export default Mixin.create({ | ||||||
|   actions: { |   actions: { | ||||||
|     delete: function() { |     delete: function() { | ||||||
|       this.get('model').destroyRecord().then(() => { |       this.get('model').destroyRecord().then(() => { | ||||||
|         // Instead of unloading the entire store, we keep the loaded user models
 |  | ||||||
|         ['species', 'strain', 'characteristic', 'measurement'].map((model) => { |  | ||||||
|           this.get('store').unloadAll(model); |  | ||||||
|         }); |  | ||||||
|         this.transitionToRoute(this.get('transitionRoute')); |         this.transitionToRoute(this.get('transitionRoute')); | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|  |  | ||||||
|  | @ -23,16 +23,11 @@ export default Mixin.create({ | ||||||
| 
 | 
 | ||||||
|     cancel: function() { |     cancel: function() { | ||||||
|       const model = this.get('model'); |       const model = this.get('model'); | ||||||
|       const isNew = model.get('isNew'); |  | ||||||
| 
 | 
 | ||||||
|       model.get('errors').clear(); |       model.get('errors').clear(); | ||||||
|       model.rollbackAttributes(); |       model.rollbackAttributes(); | ||||||
| 
 | 
 | ||||||
|       if (isNew) { |  | ||||||
|         this.transitionToRoute(this.get('fallbackRouteCancel')); |  | ||||||
|       } else { |  | ||||||
|       this.transitionToRoute(this.get('fallbackRouteCancel'), model); |       this.transitionToRoute(this.get('fallbackRouteCancel'), model); | ||||||
|       } |  | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,5 +1,6 @@ | ||||||
| import DS from 'ember-data'; | import DS from 'ember-data'; | ||||||
| import config from '../config/environment'; | import config from '../config/environment'; | ||||||
|  | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Model, attr, hasMany } = DS; | const { Model, attr, hasMany } = DS; | ||||||
| 
 | 
 | ||||||
|  | @ -16,4 +17,9 @@ export default Model.extend({ | ||||||
|   updatedBy   : attr('number'), |   updatedBy   : attr('number'), | ||||||
|   sortOrder   : attr('number'), |   sortOrder   : attr('number'), | ||||||
|   canEdit     : attr('boolean'), |   canEdit     : attr('boolean'), | ||||||
|  | 
 | ||||||
|  |   // TODO: move this to component/helper
 | ||||||
|  |   speciesNameMU: function() { | ||||||
|  |     return Ember.String.htmlSafe(`<em>${this.get('speciesName')}</em>`); | ||||||
|  |   }.property('speciesName').readOnly(), | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -2,7 +2,6 @@ import DS from 'ember-data'; | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Model, hasMany, belongsTo, attr } = DS; | const { Model, hasMany, belongsTo, attr } = DS; | ||||||
| const { computed, String: { htmlSafe } } = Ember; |  | ||||||
| 
 | 
 | ||||||
| export default Model.extend({ | export default Model.extend({ | ||||||
|   measurements       : hasMany('measurements', { async: false }), |   measurements       : hasMany('measurements', { async: false }), | ||||||
|  | @ -23,8 +22,19 @@ export default Model.extend({ | ||||||
|   sortOrder          : attr('number'), |   sortOrder          : attr('number'), | ||||||
|   canEdit            : attr('boolean'), |   canEdit            : attr('boolean'), | ||||||
| 
 | 
 | ||||||
|   fullNameMU: computed('species', 'strainName', function() { |   // TODO: move this to component/helper
 | ||||||
|     const type = this.get('typeStrain') ? '<sup>T</sup>' : ''; |   strainNameMU: function() { | ||||||
|     return htmlSafe(`<em>${this.get('species.speciesName')}</em> ${this.get('strainName')}${type}`); |     let type = this.get('typeStrain') ? '<sup>T</sup>' : ''; | ||||||
|  |     return Ember.String.htmlSafe(`${this.get('strainName')}${type}`); | ||||||
|  |   }.property('strainName', 'typeStrain').readOnly(), | ||||||
|  | 
 | ||||||
|  |   // TODO: move this to component/helper
 | ||||||
|  |   fullName: Ember.computed('species', 'strainName', function() { | ||||||
|  |     return `${this.get('species.speciesName')} ${this.get('strainNameMU')}`; | ||||||
|   }), |   }), | ||||||
|  | 
 | ||||||
|  |   // TODO: move this to component/helper
 | ||||||
|  |   fullNameMU: function() { | ||||||
|  |     return Ember.String.htmlSafe(`<em>${this.get('species.speciesName')}</em> ${this.get('strainNameMU')}`); | ||||||
|  |   }.property('species', 'strainNameMU').readOnly(), | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,13 +1,9 @@ | ||||||
| import DS from 'ember-data'; | import DS from 'ember-data'; | ||||||
| import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin'; | import DataAdapterMixin from 'ember-simple-auth/mixins/data-adapter-mixin'; | ||||||
| import Ember from 'ember'; |  | ||||||
| 
 | 
 | ||||||
| const { inject: { service } } = Ember; |  | ||||||
| const { RESTAdapter } = DS; | const { RESTAdapter } = DS; | ||||||
| 
 | 
 | ||||||
| export default RESTAdapter.extend(DataAdapterMixin, { | export default RESTAdapter.extend(DataAdapterMixin, { | ||||||
|   globals: service(), |  | ||||||
| 
 |  | ||||||
|   authorizer: 'authorizer:application', |   authorizer: 'authorizer:application', | ||||||
| 
 | 
 | ||||||
|   namespace: function() { |   namespace: function() { | ||||||
|  | @ -1,23 +1,13 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Component } = Ember; | export default Ember.Component.extend({ | ||||||
|  |   tagName: 'button', | ||||||
|  |   classNames: ["button-red", "smaller"], | ||||||
| 
 | 
 | ||||||
| export default Component.extend({ |   click: function() { | ||||||
|   tagName: 'span', |     if (window.confirm("Do you really want to delete this?")) { | ||||||
|   showConfirmDelete: false, |  | ||||||
| 
 |  | ||||||
|   actions: { |  | ||||||
|     initialClick: function() { |  | ||||||
|       this.set('showConfirmDelete', true); |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     cancelDelete: function() { |  | ||||||
|       this.set('showConfirmDelete', false); |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     confirmDelete: function() { |  | ||||||
|       this.attrs.delete(); |       this.attrs.delete(); | ||||||
|     }, |     } | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,12 +1 @@ | ||||||
| {{#unless showConfirmDelete}} |  | ||||||
|   <button class="button-red smaller delete" {{action "initialClick"}}> |  | ||||||
| Delete | Delete | ||||||
|   </button> |  | ||||||
| {{else}} |  | ||||||
|   <button class="button-red smaller delete-confirm" {{action "confirmDelete"}}> |  | ||||||
|     Confirm Delete |  | ||||||
|   </button> |  | ||||||
|   <button class="button-gray smaller delete-cancel" {{action "cancelDelete"}}> |  | ||||||
|     Cancel Delete |  | ||||||
|   </button> |  | ||||||
| {{/unless}} |  | ||||||
|  |  | ||||||
|  | @ -1,14 +1,8 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Component, computed, inject: { service } } = Ember; | export default Ember.Component.extend({ | ||||||
| 
 |  | ||||||
| export default Component.extend({ |  | ||||||
|   globals: service(), |  | ||||||
| 
 |  | ||||||
|   tagName: 'em', |   tagName: 'em', | ||||||
| 
 |   genus: function() { | ||||||
|   genus: computed('globals.genus', function() { |  | ||||||
|     return this.get('globals.genus').capitalize(); |     return this.get('globals.genus').capitalize(); | ||||||
|   }), |   }.property().readOnly(), | ||||||
| 
 |  | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,3 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Component, inject: { service } } = Ember; | export default Ember.Component.extend({}); | ||||||
| 
 |  | ||||||
| export default Component.extend({ |  | ||||||
|   globals: service(), |  | ||||||
| }); |  | ||||||
|  |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| {{strain.strainName}}{{{if strain.typeStrain '<sup>T</sup>' ''}}} |  | ||||||
|  | @ -1,25 +1,13 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| /* global Quill */ | /* global Quill */ | ||||||
| 
 | 
 | ||||||
| const { Component } = Ember; | export default Ember.Component.extend({ | ||||||
| 
 |  | ||||||
| export default Component.extend({ |  | ||||||
|   // Passed in
 |  | ||||||
|   value: null, |  | ||||||
| 
 |  | ||||||
|   // Internal
 |  | ||||||
|   quill: null, |   quill: null, | ||||||
| 
 |   value: null, | ||||||
|   didReceiveAttrs() { |   update: null, | ||||||
|     this._super(...arguments); |  | ||||||
| 
 |  | ||||||
|     if (!this.attrs.update) { |  | ||||||
|       throw new Error(`You must provide an \`update\` action.`); |  | ||||||
|     } |  | ||||||
|   }, |  | ||||||
| 
 | 
 | ||||||
|   didInsertElement: function() { |   didInsertElement: function() { | ||||||
|     const quill = new Quill(`#${this.get('elementId')} .editor`, { |     let quill = new Quill(`#${this.get('elementId')} .editor`, { | ||||||
|       formats: ['bold', 'italic', 'underline'], |       formats: ['bold', 'italic', 'underline'], | ||||||
|       modules: { |       modules: { | ||||||
|         'toolbar': { container: `#${this.get('elementId')} .toolbar` } |         'toolbar': { container: `#${this.get('elementId')} .toolbar` } | ||||||
|  |  | ||||||
|  | @ -1,12 +1,10 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Component, inject: { service } } = Ember; | export default Ember.Component.extend({ | ||||||
| 
 |  | ||||||
| export default Component.extend({ |  | ||||||
|   classNames: ["flakes-frame"], |   classNames: ["flakes-frame"], | ||||||
| 
 | 
 | ||||||
|   session: service(), |   session: Ember.inject.service('session'), | ||||||
|   currentUser: service('session-account'), |   currentUser: Ember.inject.service('session-account'), | ||||||
| 
 | 
 | ||||||
|   didInsertElement: function() { |   didInsertElement: function() { | ||||||
|     FlakesFrame.init(); |     FlakesFrame.init(); | ||||||
|  |  | ||||||
|  | @ -33,7 +33,7 @@ | ||||||
|       <br> |       <br> | ||||||
|       {{link-to 'Sign Up' 'users.new'}} |       {{link-to 'Sign Up' 'users.new'}} | ||||||
|       <br> |       <br> | ||||||
|       {{link-to 'Reset Password' 'users.requestlockouthelp'}} |       {{link-to 'Locked Out?' 'users.requestlockouthelp'}} | ||||||
|     </p> |     </p> | ||||||
|   {{/if}} |   {{/if}} | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | @ -1,54 +1,50 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Component, get, run: { schedule } } = Ember; | const { Component, isEmpty } = Ember; | ||||||
| 
 | 
 | ||||||
| export default Component.extend({ | export default Component.extend({ | ||||||
|   tagName: 'select', |   tagName: 'select', | ||||||
|  | 
 | ||||||
|  |   nameAttr: null, | ||||||
|  |   listItems: null, | ||||||
|  |   placeholder: null, | ||||||
|  |   selected: null, | ||||||
|  |   selectize: null, | ||||||
|  |   multiple: false, | ||||||
|  | 
 | ||||||
|   attributeBindings: [ |   attributeBindings: [ | ||||||
|     'multiple', |     'multiple', | ||||||
|   ], |   ], | ||||||
| 
 | 
 | ||||||
|   options: null, |  | ||||||
|   selected: null, |  | ||||||
|   nameAttr: null, |  | ||||||
|   placeholder: null, |  | ||||||
| 
 |  | ||||||
|   change: function() { |   change: function() { | ||||||
|     let selectedInComponent = this.get('selected'); |     this.attrs["update"](this.$()[0].selectize.getValue()); | ||||||
|     let selectedInWidget = this.$().val(); |   }, | ||||||
| 
 | 
 | ||||||
|     if (this.get('multiple')) { |   didReceiveAttrs: function() { | ||||||
|       if (selectedInWidget === null) { |     this._super(...arguments); | ||||||
|         selectedInWidget = []; |     console.log('didReceiveAttrs'); | ||||||
|       } | 
 | ||||||
|       selectedInComponent = selectedInComponent.toString(); |     if (!this.attrs.update) { | ||||||
|       selectedInWidget = selectedInWidget.toString(); |       throw new Error(`You must provide an \`update\` action.`); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // We need this to prevent an infinite loop of afterRender -> change.
 |     Ember.run.schedule('actions', this, () => { | ||||||
|     if (selectedInComponent !== selectedInWidget) { |       console.log('before adding'); | ||||||
|       this.attrs.update(this.$().val()); |       this.$()[0].selectize.setValue(this.get('selected'), true); | ||||||
|     } |       console.log('after adding') | ||||||
|  |     }); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   didInsertElement: function() { |   didInsertElement: function() { | ||||||
|     let options = {}; |     console.log('didInsertElement'); | ||||||
|     options.placeholder = this.get('placeholder'); |     const options = { | ||||||
|     options.templateResult = function(item) { |       closeAfterSelect: true, | ||||||
|       if (!item.disabled) { |       selectOnTab: true, | ||||||
|         const text = get(item, 'element.innerHTML'); |       plugins: ['drag_drop'], | ||||||
|         const $item = Ember.$(`<span>${text}</span>`); |       items: this.get('selected'), | ||||||
|         return $item; |  | ||||||
|     } |     } | ||||||
|     }; |  | ||||||
|     this.$().select2(options); |  | ||||||
|   }, |  | ||||||
| 
 | 
 | ||||||
|   didRender: function() { |     this.$().selectize(options); | ||||||
|     const selected = this.get('selected'); |  | ||||||
|     schedule('afterRender', this, function() { |  | ||||||
|       this.$().val(selected).trigger('change'); |  | ||||||
|     }); |  | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,3 +1,6 @@ | ||||||
| {{#each options as |option|}} | {{#if placeholder}} | ||||||
|   <option value={{option.id}}>{{get-property option nameAttr}}</option> |   <option value="">{{placeholder}}</option> | ||||||
|  | {{/if}} | ||||||
|  | {{#each listItems as |option|}} | ||||||
|  |   <option value={{option.id}} selected={{equal option.id value.id}}>{{get-property option nameAttr}}</option> | ||||||
| {{/each}} | {{/each}} | ||||||
|  |  | ||||||
|  | @ -10,7 +10,3 @@ | ||||||
| <div> | <div> | ||||||
|   {{link-to 'Forget your password?' 'users.requestlockouthelp'}} |   {{link-to 'Forget your password?' 'users.requestlockouthelp'}} | ||||||
| </div> | </div> | ||||||
| <br> |  | ||||||
| <div> |  | ||||||
|   Just checking things out? Log in with email <code>read-only</code> and password <code>bacteria</code>! |  | ||||||
| </div> |  | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ | ||||||
|         <dl class="span-2"> |         <dl class="span-2"> | ||||||
|           <dt>Measurements</dt> |           <dt>Measurements</dt> | ||||||
|           <dd> |           <dd> | ||||||
|  |             <p>To add/edit/remove a measurement, please visit the strain's page (links below)</p> | ||||||
|             {{protected/characteristics/show/measurements-table characteristic=characteristic}} |             {{protected/characteristics/show/measurements-table characteristic=characteristic}} | ||||||
|           </dd> |           </dd> | ||||||
|         </dl> |         </dl> | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Component, computed } = Ember; | const { Component, computed } = Ember; | ||||||
| const { alias, sort } = computed; | const { sort } = computed; | ||||||
| 
 | 
 | ||||||
| export default Component.extend({ | export default Component.extend({ | ||||||
|   characteristic: null, |   characteristic: null, | ||||||
|  | @ -10,9 +10,10 @@ export default Component.extend({ | ||||||
|     return this.get('characteristic.measurements.length') > 0; |     return this.get('characteristic.measurements.length') > 0; | ||||||
|   }), |   }), | ||||||
| 
 | 
 | ||||||
|   measurements: alias('characteristic.measurements'), |   sortParams: ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName'], | ||||||
|   sortParams: ['strain.sortOrder'], |   sortAsc: true, | ||||||
|   sortedMeasurements: sort('measurements', 'sortParams'), |   paramsChanged: false, | ||||||
|  |   sortedMeasurements: sort('characteristic.measurements', 'sortParams'), | ||||||
| 
 | 
 | ||||||
|   actions: { |   actions: { | ||||||
|     changeSortParam: function(col) { |     changeSortParam: function(col) { | ||||||
|  |  | ||||||
|  | @ -7,24 +7,24 @@ | ||||||
| <table class="flakes-table"> | <table class="flakes-table"> | ||||||
|   <thead> |   <thead> | ||||||
|     <tr> |     <tr> | ||||||
|       <th {{action "changeSortParam" "strain.sortOrder"}} class="click">Strain</th> |       <th {{action "changeSortParam" "strain.strainName"}} class="click">Strain</th> | ||||||
|       <th {{action "changeSortParam" "value"}} class="click">Value</th> |       <th {{action "changeSortParam" "value"}} class="click">Value</th> | ||||||
|       <th {{action "changeSortParam" "notes"}} class="click">Notes</th> |       <th {{action "changeSortParam" "notes"}} class="click">Notes</th> | ||||||
|     </tr> |     </tr> | ||||||
|   </thead> |   </thead> | ||||||
|   <tbody> |   <tbody> | ||||||
|     {{#each sortedMeasurements as |measurement|}} |     {{#each sortedMeasurements as |row|}} | ||||||
|       <tr> |       <tr> | ||||||
|         <td> |         <td> | ||||||
|           {{#link-to 'protected.strains.show' measurement.strain.id classBinding="measurement.strain.typeStrain:type-strain"}} |           {{#link-to 'protected.strains.show' row.strain.id}} | ||||||
|             {{measurement.strain.fullNameMU}} |             {{{row.strain.strainNameMU}}} | ||||||
|           {{/link-to}} |           {{/link-to}} | ||||||
|         </td> |         </td> | ||||||
|         <td> |         <td> | ||||||
|           {{measurement.value}} |           {{row.value}} | ||||||
|         </td> |         </td> | ||||||
|         <td> |         <td> | ||||||
|           {{measurement.notes}} |           {{row.notes}} | ||||||
|         </td> |         </td> | ||||||
|       </tr> |       </tr> | ||||||
|     {{/each}} |     {{/each}} | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ const { Route } = Ember; | ||||||
| 
 | 
 | ||||||
| export default Route.extend({ | export default Route.extend({ | ||||||
|   model: function(params) { |   model: function(params) { | ||||||
|     return this.store.findRecord('characteristic', params.characteristic_id, { reload: true }); |     return this.store.findRecord('characteristic', params.characteristic_id); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -3,8 +3,8 @@ import Ember from 'ember'; | ||||||
| const { Controller } = Ember; | const { Controller } = Ember; | ||||||
| 
 | 
 | ||||||
| export default Controller.extend({ | export default Controller.extend({ | ||||||
|   selectedStrains: [], |   selectedStrains: null, | ||||||
|   selectedCharacteristics: [], |   selectedCharacteristics: null, | ||||||
| 
 | 
 | ||||||
|   actions: { |   actions: { | ||||||
|     search: function(query) { |     search: function(query) { | ||||||
|  | @ -12,10 +12,12 @@ export default Controller.extend({ | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     updateStrainSelection: function(selection) { |     updateStrainSelection: function(selection) { | ||||||
|  |       console.log(selection); | ||||||
|       this.set('selectedStrains', selection); |       this.set('selectedStrains', selection); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     updateCharacteristicSelection: function(selection) { |     updateCharacteristicSelection: function(selection) { | ||||||
|  |       console.log(selection); | ||||||
|       this.set('selectedCharacteristics', selection); |       this.set('selectedCharacteristics', selection); | ||||||
|     }, |     }, | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ const { Component, computed, inject: { service } } = Ember; | ||||||
| 
 | 
 | ||||||
| export default Component.extend({ | export default Component.extend({ | ||||||
|   session: service(), |   session: service(), | ||||||
|   globals: service(), |  | ||||||
| 
 | 
 | ||||||
|   strains: null, |   strains: null, | ||||||
|   characteristics: null, |   characteristics: null, | ||||||
|  |  | ||||||
|  | @ -14,16 +14,8 @@ | ||||||
|   <tbody> |   <tbody> | ||||||
|     {{#each characteristics as |row|}} |     {{#each characteristics as |row|}} | ||||||
|       <tr> |       <tr> | ||||||
|         {{#each row key="@index" as |col index|}} |         {{#each row key="@index" as |col|}} | ||||||
|           <td> |           <td>{{col}}</td> | ||||||
|             {{#unless index}} |  | ||||||
|               {{#link-to 'protected.characteristics.show' col.id}} |  | ||||||
|                 {{col.characteristicName}} |  | ||||||
|               {{/link-to}} |  | ||||||
|             {{else}} |  | ||||||
|               {{col}} |  | ||||||
|             {{/unless}} |  | ||||||
|           </td> |  | ||||||
|         {{/each}} |         {{/each}} | ||||||
|       </tr> |       </tr> | ||||||
|     {{/each}} |     {{/each}} | ||||||
|  |  | ||||||
|  | @ -30,15 +30,16 @@ export default Route.extend({ | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const compare = this.controllerFor('protected.compare'); |     const compare = this.controllerFor('protected.compare'); | ||||||
|     compare.set('selectedStrains', params.strain_ids.split(",")); |     compare.set('selectedStrains', params.strain_ids.split(',')); | ||||||
|     compare.set('selectedCharacteristics', params.characteristic_ids.split(",")); |     compare.set('selectedCharacteristics', params.characteristic_ids.split(',')); | ||||||
| 
 | 
 | ||||||
|     return this.get('ajax').request('/compare', { data: params }); |     return this.get('ajax').request('/compare', { data: params }); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   setupController: function(controller, model) { |   setupController: function(controller, model) { | ||||||
|     model.forEach((m, i) => { |     model.forEach((m, i) => { | ||||||
|       model[i][0] = this.store.peekRecord('characteristic', m[0]); |       const c = this.store.peekRecord('characteristic', m[0]); | ||||||
|  |       model[i][0] = c.get('characteristicName'); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     const compare = this.controllerFor('protected.compare'); |     const compare = this.controllerFor('protected.compare'); | ||||||
|  |  | ||||||
|  | @ -1,6 +1,6 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| 
 | 
 | ||||||
| const { Component, computed: { sort } } = Ember; | const { Component } = Ember; | ||||||
| 
 | 
 | ||||||
| export default Component.extend({ | export default Component.extend({ | ||||||
|   characteristics: null, |   characteristics: null, | ||||||
|  | @ -10,23 +10,12 @@ export default Component.extend({ | ||||||
|   "update-strains": null, |   "update-strains": null, | ||||||
|   "update-characteristics": null, |   "update-characteristics": null, | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|   charSortParams: ['characteristicTypeName', 'sortOrder', 'characteristicName'], |  | ||||||
|   sortedCharacteristics: sort('characteristics', 'charSortParams'), |  | ||||||
|   strainSortParams: ['sortOrder'], |  | ||||||
|   sortedStrains: sort('strains', 'sortParams'), |  | ||||||
| 
 |  | ||||||
|   selectedStrains: [], |  | ||||||
|   selectedCharacteristics: [], |  | ||||||
| 
 |  | ||||||
|   updateStrains: function(selection) { |   updateStrains: function(selection) { | ||||||
|     this.set('selectedStrains', selection); |     this.attrs["update-strains"](selection); | ||||||
|     this.attrs['update-strains'](this.get('selectedStrains')); |  | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   updateCharacteristics: function(selection) { |   updateCharacteristics: function(selection) { | ||||||
|     this.set('selectedCharacteristics', selection); |     this.attrs['update-characteristics'](selection); | ||||||
|     this.attrs['update-characteristics'](this.get('selectedCharacteristics')); |  | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   actions: { |   actions: { | ||||||
|  | @ -68,7 +57,7 @@ export default Component.extend({ | ||||||
|       this.updateStrains(selection); |       this.updateStrains(selection); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     updateCharacteristicsSelection: function(selection) { |     updateCharacteristicSelection: function(selection) { | ||||||
|       this.updateCharacteristics(selection); |       this.updateCharacteristics(selection); | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  | @ -6,12 +6,12 @@ | ||||||
|           <label>Strains</label> |           <label>Strains</label> | ||||||
|           {{ |           {{ | ||||||
|             x-select |             x-select | ||||||
|             options=sortedStrains |             listItems=strains | ||||||
|             nameAttr='fullNameMU' |             nameAttr="fullNameMU" | ||||||
|  |             update=(action "updateStrainSelection") | ||||||
|  |             placeholder="Select one ore more strains" | ||||||
|             multiple=true |             multiple=true | ||||||
|             selected=selectedStrains |             selected=selectedStrains | ||||||
|             update=(action "updateStrainSelection") |  | ||||||
|             placeholder="Select one or more strains" |  | ||||||
|           }} |           }} | ||||||
|         </li> |         </li> | ||||||
|         <li> |         <li> | ||||||
|  | @ -26,12 +26,12 @@ | ||||||
|           <label>Characteristics</label> |           <label>Characteristics</label> | ||||||
|           {{ |           {{ | ||||||
|             x-select |             x-select | ||||||
|             options=sortedCharacteristics |             listItems=characteristics | ||||||
|             nameAttr='characteristicName' |             nameAttr="characteristicName" | ||||||
|  |             update=(action "updateCharacteristicSelection") | ||||||
|  |             placeholder="Select one ore more characteristics" | ||||||
|             multiple=true |             multiple=true | ||||||
|             selected=selectedCharacteristics |             selected=selectedCharacteristics | ||||||
|             update=(action "updateCharacteristicsSelection") |  | ||||||
|             placeholder="Select one or more characteristics" |  | ||||||
|           }} |           }} | ||||||
|         </li> |         </li> | ||||||
|         <li> |         <li> | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ const { Component, computed: { sort } } = Ember; | ||||||
| export default Component.extend(SetupMetaData, { | export default Component.extend(SetupMetaData, { | ||||||
|   species: null, |   species: null, | ||||||
| 
 | 
 | ||||||
|   sortParams: ['sortOrder'], |   sortParams: ['speciesName', 'strainCount'], | ||||||
|   sortedSpecies: sort('species', 'sortParams'), |   sortedSpecies: sort('species', 'sortParams'), | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -23,7 +23,7 @@ | ||||||
|           {{#each species.strains as |strain index|}} |           {{#each species.strains as |strain index|}} | ||||||
|             {{if index ","}} |             {{if index ","}} | ||||||
|             {{#link-to 'protected.strains.show' strain.id}} |             {{#link-to 'protected.strains.show' strain.id}} | ||||||
|               {{strain-name strain=strain}} |               {{{strain.strainNameMU}}} | ||||||
|             {{/link-to}} |             {{/link-to}} | ||||||
|           {{/each}} |           {{/each}} | ||||||
|         </td> |         </td> | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ const { Route } = Ember; | ||||||
| 
 | 
 | ||||||
| export default Route.extend({ | export default Route.extend({ | ||||||
|   model: function(params) { |   model: function(params) { | ||||||
|     return this.store.findRecord('species', params.species_id, { reload: true }); |     return this.store.findRecord('species', params.species_id); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ | ||||||
|               {{#each species.strains as |strain index|}} |               {{#each species.strains as |strain index|}} | ||||||
|                 <li> |                 <li> | ||||||
|                   {{#link-to 'protected.strains.show' strain.id}} |                   {{#link-to 'protected.strains.show' strain.id}} | ||||||
|                     {{strain-name strain=strain}} |                     {{{strain.strainNameMU}}} | ||||||
|                   {{/link-to}} |                   {{/link-to}} | ||||||
|                 </li> |                 </li> | ||||||
|               {{/each}} |               {{/each}} | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ | ||||||
|         {{#each strains as |strain index|}} |         {{#each strains as |strain index|}} | ||||||
|           {{if index ","}} |           {{if index ","}} | ||||||
|           {{#link-to 'protected.strains.show' strain.id}} |           {{#link-to 'protected.strains.show' strain.id}} | ||||||
|             {{strain-name strain=strain}} |             {{{strain.strainNameMU}}} | ||||||
|           {{/link-to}} |           {{/link-to}} | ||||||
|         {{/each}} |         {{/each}} | ||||||
|         {{add-button label="Add Strain" link="protected.strains.new" canAdd=metaData.canAdd}} |         {{add-button label="Add Strain" link="protected.strains.new" canAdd=metaData.canAdd}} | ||||||
|  |  | ||||||
|  | @ -1,61 +1,36 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
|  | import SaveModel from '../../../../mixins/save-model'; | ||||||
|  | import ajaxError from '../../../../utils/ajax-error'; | ||||||
| 
 | 
 | ||||||
| const { Controller, RSVP, inject: { service } } = Ember; | const { Controller } = Ember; | ||||||
| 
 |  | ||||||
| export default Controller.extend({ |  | ||||||
|   ajaxError: service('ajax-error'), |  | ||||||
| 
 | 
 | ||||||
|  | export default Controller.extend(SaveModel, { | ||||||
|  |   // Required for SaveModel mixin
 | ||||||
|   fallbackRouteSave: 'protected.strains.show', |   fallbackRouteSave: 'protected.strains.show', | ||||||
|   fallbackRouteCancel: 'protected.strains.show', |   fallbackRouteCancel: 'protected.strains.show', | ||||||
| 
 | 
 | ||||||
|   actions: { |   actions: { | ||||||
|     save: function(properties, deleteQueue, updateQueue) { |     addCharacteristic: function() { | ||||||
|       let promises = []; |       return this.store.createRecord('measurement', { | ||||||
|       properties.measurements.forEach((measurement) => { |         characteristic: this.store.createRecord('characteristic', { sortOrder: -999 }), | ||||||
|         if (measurement.get('isNew')) { |  | ||||||
|           promises.push(measurement.save()); |  | ||||||
|         } |  | ||||||
|       }); |       }); | ||||||
|  |     }, | ||||||
| 
 | 
 | ||||||
|       updateQueue.forEach((measurement) => { |     saveMeasurement: function(measurement, properties) { | ||||||
|         promises.push(measurement.save()); |       measurement.setProperties(properties); | ||||||
|       }); |       measurement.save().then(() => { | ||||||
| 
 |  | ||||||
|       deleteQueue.forEach((measurement) => { |  | ||||||
|         promises.push(measurement.destroyRecord()); |  | ||||||
|       }); |  | ||||||
| 
 |  | ||||||
|       const model = this.get('model'); |  | ||||||
|       const fallbackRoute = this.get('fallbackRouteSave'); |  | ||||||
| 
 |  | ||||||
|       RSVP.all(promises).then(() => { |  | ||||||
|         // Can't call _super inside promise, have to reproduce save-model
 |  | ||||||
|         // mixin here :-(
 |  | ||||||
|         model.setProperties(properties); |  | ||||||
|         model.save().then((model) => { |  | ||||||
|         this.get('flashMessages').clearMessages(); |         this.get('flashMessages').clearMessages(); | ||||||
|           this.transitionToRoute(fallbackRoute, model); |       }, () => { | ||||||
|         }); |         ajaxError(measurement.get('errors'), this.get('flashMessages')); | ||||||
|       }, (errors) => { |  | ||||||
|         this.get('ajaxError').alert(errors); |  | ||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     cancel: function() { |     deleteMeasurement: function(measurement) { | ||||||
|       const model = this.get('model'); |       const characteristic = measurement.get('characteristic'); | ||||||
| 
 |       if (characteristic.get('isNew')) { | ||||||
|       model.get('errors').clear(); |         characteristic.destroyRecord(); | ||||||
|       model.rollbackAttributes(); |  | ||||||
| 
 |  | ||||||
|       if (model.get('isNew')) { |  | ||||||
|         this.transitionToRoute(this.get('fallbackRouteCancel')); |  | ||||||
|       } else { |  | ||||||
|         this.transitionToRoute(this.get('fallbackRouteCancel'), model); |  | ||||||
|       } |       } | ||||||
|     }, |       measurement.destroyRecord(); | ||||||
| 
 |  | ||||||
|     addMeasurement: function() { |  | ||||||
|       return this.store.createRecord('measurement'); |  | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  | @ -2,8 +2,10 @@ | ||||||
|   protected/strains/strain-form |   protected/strains/strain-form | ||||||
|   strain=model |   strain=model | ||||||
|   speciesList=speciesList |   speciesList=speciesList | ||||||
|   add-measurement=(action "addMeasurement") |   add-characteristic=(action "addCharacteristic") | ||||||
|   allCharacteristics=allCharacteristics |   allCharacteristics=allCharacteristics | ||||||
|  |   save-measurement=(action "saveMeasurement") | ||||||
|  |   delete-measurement=(action "deleteMeasurement") | ||||||
|   on-save=(action "save") |   on-save=(action "save") | ||||||
|   on-cancel=(action "cancel") |   on-cancel=(action "cancel") | ||||||
| }} | }} | ||||||
|  |  | ||||||
|  | @ -6,7 +6,7 @@ const { Component, computed: { sort } } = Ember; | ||||||
| export default Component.extend(SetupMetaData, { | export default Component.extend(SetupMetaData, { | ||||||
|   strains: null, |   strains: null, | ||||||
| 
 | 
 | ||||||
|   sortParams: ['sortOrder'], |   sortParams: ['fullName'], | ||||||
|   sortedStrains: sort('strains', 'sortParams'), |   sortedStrains: sort('strains', 'sortParams'), | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -13,7 +13,7 @@ | ||||||
|     {{#each sortedStrains as |strain|}} |     {{#each sortedStrains as |strain|}} | ||||||
|       <tr> |       <tr> | ||||||
|         <td> |         <td> | ||||||
|           {{#link-to 'protected.strains.show' strain classBinding="strain.typeStrain:type-strain"}} |           {{#link-to 'protected.strains.show' strain classBinding="data.typeStrain:type-strain"}} | ||||||
|             {{strain.fullNameMU}} |             {{strain.fullNameMU}} | ||||||
|           {{/link-to}} |           {{/link-to}} | ||||||
|         </td> |         </td> | ||||||
|  |  | ||||||
|  | @ -10,8 +10,6 @@ export default Component.extend({ | ||||||
|   allCharacteristics: null, |   allCharacteristics: null, | ||||||
|   measurement: null, |   measurement: null, | ||||||
|   isDirty: null, |   isDirty: null, | ||||||
|   isNew: false, |  | ||||||
|   isQueued: false, |  | ||||||
| 
 | 
 | ||||||
|   // Actions
 |   // Actions
 | ||||||
|   "save-measurement": null, |   "save-measurement": null, | ||||||
|  | @ -24,23 +22,11 @@ export default Component.extend({ | ||||||
|   notes: null, |   notes: null, | ||||||
| 
 | 
 | ||||||
|   resetOnInit: Ember.on('init', function() { |   resetOnInit: Ember.on('init', function() { | ||||||
|     this._resetProperties(); |  | ||||||
|   }), |  | ||||||
| 
 |  | ||||||
|   _resetProperties: function() { |  | ||||||
|     this.get('propertiesList').forEach((field) => { |     this.get('propertiesList').forEach((field) => { | ||||||
|       const valueInMeasurement = this.get('measurement').get(field); |       const valueInMeasurement = this.get('measurement').get(field); | ||||||
|       this.set(field, valueInMeasurement); |       this.set(field, valueInMeasurement); | ||||||
|     }); |     }); | ||||||
|     // Read-only attributes
 |   }), | ||||||
|     this.set('isNew', this.get('measurement.isNew')); |  | ||||||
|     if (this.get('isNew') && !this.get('isQueued')) { |  | ||||||
|       this.set('isEditing', true); |  | ||||||
|     } else { |  | ||||||
|       this.set('isEditing', false); |  | ||||||
|     } |  | ||||||
|     this.set('isDirty', false); |  | ||||||
|   }, |  | ||||||
| 
 | 
 | ||||||
|   updateField: function(property, value) { |   updateField: function(property, value) { | ||||||
|     this.set(property, value); |     this.set(property, value); | ||||||
|  | @ -54,22 +40,12 @@ export default Component.extend({ | ||||||
| 
 | 
 | ||||||
|   actions: { |   actions: { | ||||||
|     edit: function() { |     edit: function() { | ||||||
|       this.set('isEditing', true); |       this.toggleProperty('isEditing'); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     save: function() { |     save: function() { | ||||||
|       this.attrs['save-measurement'](this.get('measurement'), this.getProperties(this.get('propertiesList'))); |       this.attrs['save-measurement'](this.get('measurement'), this.getProperties(this.get('propertiesList'))); | ||||||
|       this.set('isQueued', true); |       this.toggleProperty('isEditing'); | ||||||
|       this._resetProperties(); |  | ||||||
|     }, |  | ||||||
| 
 |  | ||||||
|     cancel: function() { |  | ||||||
|       if (this.get('isNew')) { |  | ||||||
|         this.attrs['delete-measurement'](this.get('measurement')); |  | ||||||
|       } else { |  | ||||||
|         this._resetProperties(); |  | ||||||
|         this.set('isEditing', false); |  | ||||||
|       } |  | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     delete: function() { |     delete: function() { | ||||||
|  |  | ||||||
|  | @ -1 +0,0 @@ | ||||||
| {{loading-panel}} |  | ||||||
|  | @ -1,7 +1,5 @@ | ||||||
| {{#if isEditing}} | {{#if isEditing}} | ||||||
|   <td> |   <td></td> | ||||||
|     {{{characteristic.characteristicTypeName}}} |  | ||||||
|   </td> |  | ||||||
|   <td> |   <td> | ||||||
|     <select onchange={{action "characteristicDidChange" value="target.value"}}> |     <select onchange={{action "characteristicDidChange" value="target.value"}}> | ||||||
|       {{#each allCharacteristics as |characteristicChoice|}} |       {{#each allCharacteristics as |characteristicChoice|}} | ||||||
|  | @ -17,13 +15,14 @@ | ||||||
|   </td> |   </td> | ||||||
|   {{#if canEdit}} |   {{#if canEdit}} | ||||||
|     <td> |     <td> | ||||||
|       <button class="button-gray smaller" {{action 'cancel'}}> |  | ||||||
|         Cancel |  | ||||||
|       </button> |  | ||||||
|       {{#if isDirty}} |       {{#if isDirty}} | ||||||
|       <button class="button-green smaller" {{action 'save'}}> |       <button class="button-green smaller" {{action 'save'}}> | ||||||
|         Save |         Save | ||||||
|       </button> |       </button> | ||||||
|  |       {{else}} | ||||||
|  |       <button class="button-gray smaller" {{action 'save'}}> | ||||||
|  |         Cancel | ||||||
|  |       </button> | ||||||
|       {{/if}} |       {{/if}} | ||||||
|     </td> |     </td> | ||||||
|   {{/if}} |   {{/if}} | ||||||
|  |  | ||||||
|  | @ -5,13 +5,13 @@ const { sort } = computed; | ||||||
| 
 | 
 | ||||||
| export default Component.extend({ | export default Component.extend({ | ||||||
|   // Passed in
 |   // Passed in
 | ||||||
|   measurements: null, |   strain: null, | ||||||
|   allCharacteristics: null, |   allCharacteristics: null, | ||||||
|   canEdit: false, |   canEdit: false, | ||||||
|   canAdd: false, |   canAdd: false, | ||||||
| 
 | 
 | ||||||
|   // Actions
 |   // Actions
 | ||||||
|   "add-measurement": null, |   "add-characteristic": null, | ||||||
|   "save-measurement": null, |   "save-measurement": null, | ||||||
|   "delete-measurement": null, |   "delete-measurement": null, | ||||||
| 
 | 
 | ||||||
|  | @ -19,11 +19,15 @@ export default Component.extend({ | ||||||
|   sortParams: ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName'], |   sortParams: ['characteristic.characteristicTypeName', 'characteristic.sortOrder', 'characteristic.characteristicName'], | ||||||
|   sortAsc: true, |   sortAsc: true, | ||||||
|   paramsChanged: false, |   paramsChanged: false, | ||||||
|   sortedMeasurements: sort('measurements', 'sortParams'), |   sortedMeasurements: sort('strain.measurements', 'sortParams'), | ||||||
|  |   measurementsPresent: computed('strain.measurements', function() { | ||||||
|  |     return this.get('strain.measurements.length') > 0; | ||||||
|  |   }), | ||||||
| 
 | 
 | ||||||
|   actions: { |   actions: { | ||||||
|     addMeasurement: function() { |     addCharacteristic: function() { | ||||||
|       return this.attrs['add-measurement'](); |       const newChar = this.attrs['add-characteristic'](); | ||||||
|  |       this.get('strain.measurements').addObject(newChar); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     changeSortParam: function(col) { |     changeSortParam: function(col) { | ||||||
|  |  | ||||||
|  | @ -1,11 +1,12 @@ | ||||||
| {{#if canAdd}} | {{#if canAdd}} | ||||||
|   <br> |   <br> | ||||||
|   <button class="button-green smaller" {{action "addMeasurement"}}> |   <button class="button-green smaller" {{action "addCharacteristic"}}> | ||||||
|     Add measurement |     Add characteristic | ||||||
|   </button> |   </button> | ||||||
|   <br><br> |   <br><br> | ||||||
| {{/if}} | {{/if}} | ||||||
| 
 | 
 | ||||||
|  | {{#if measurementsPresent}} | ||||||
|   {{#if paramsChanged}} |   {{#if paramsChanged}} | ||||||
|     <button class="button-gray smaller" {{action 'resetSortParam'}}> |     <button class="button-gray smaller" {{action 'resetSortParam'}}> | ||||||
|       Reset sort |       Reset sort | ||||||
|  | @ -47,10 +48,9 @@ | ||||||
|         allCharacteristics=allCharacteristics |         allCharacteristics=allCharacteristics | ||||||
|         canEdit=canEdit |         canEdit=canEdit | ||||||
|       }} |       }} | ||||||
|     {{else}} |  | ||||||
|       <tr> |  | ||||||
|         <td colspan="5">No Measurements on Record</td> |  | ||||||
|       </tr> |  | ||||||
|     {{/each}} |     {{/each}} | ||||||
|   </tbody> |   </tbody> | ||||||
| </table> | </table> | ||||||
|  | {{else}} | ||||||
|  | No measurements on record. | ||||||
|  | {{/if}} | ||||||
|  |  | ||||||
|  | @ -4,7 +4,7 @@ const { Route } = Ember; | ||||||
| 
 | 
 | ||||||
| export default Route.extend({ | export default Route.extend({ | ||||||
|   model: function(params) { |   model: function(params) { | ||||||
|     return this.store.findRecord('strain', params.strain_id, { reload: true }); |     return this.store.findRecord('strain', params.strain_id); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| <div class="span-1"> | <div class="span-1"> | ||||||
|   <fieldset class="flakes-information-box"> |   <fieldset class="flakes-information-box"> | ||||||
|     <legend> |     <legend> | ||||||
|       {{strain-name strain=strain}} |       {{strain.strainNameMU}} | ||||||
|     </legend> |     </legend> | ||||||
| 
 | 
 | ||||||
|     {{! ROW 1 }} |     {{! ROW 1 }} | ||||||
|  | @ -10,7 +10,7 @@ | ||||||
|         <dt>Species</dt> |         <dt>Species</dt> | ||||||
|         <dd> |         <dd> | ||||||
|           {{#link-to 'protected.species.show' strain.species.id}} |           {{#link-to 'protected.species.show' strain.species.id}} | ||||||
|             <em>{{strain.species.speciesName}}</em> |             <em>{{strain.species.speciesNameMU}}</em> | ||||||
|           {{/link-to}} |           {{/link-to}} | ||||||
|         </dd> |         </dd> | ||||||
|       </dl> |       </dl> | ||||||
|  | @ -71,11 +71,11 @@ | ||||||
|     {{! ROW 5 }} |     {{! ROW 5 }} | ||||||
|     <div class="grid-1 gutter-20"> |     <div class="grid-1 gutter-20"> | ||||||
|       <dl class="span-1"> |       <dl class="span-1"> | ||||||
|         <dt>Characteristic Measurements</dt> |         <dt>Characteristics</dt> | ||||||
|         <dd> |         <dd> | ||||||
|           {{ |           {{ | ||||||
|             protected/strains/measurements-table |             protected/strains/measurements-table | ||||||
|             measurements=strain.measurements |             strain=strain | ||||||
|             canEdit=false |             canEdit=false | ||||||
|             canAdd=false |             canAdd=false | ||||||
|           }} |           }} | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| import Ember from 'ember'; | import Ember from 'ember'; | ||||||
| import SetupMetaData from '../../../../mixins/setup-metadata'; | import SetupMetaData from '../../../../mixins/setup-metadata'; | ||||||
| 
 | 
 | ||||||
| const { Component, computed: { sort } } = Ember; | const { Component } = Ember; | ||||||
| 
 | 
 | ||||||
| export default Component.extend(SetupMetaData, { | export default Component.extend(SetupMetaData, { | ||||||
|   // Read-only attributes
 |   // Read-only attributes
 | ||||||
|  | @ -9,22 +9,18 @@ export default Component.extend(SetupMetaData, { | ||||||
|   isNew: null, |   isNew: null, | ||||||
|   isDirty: false, |   isDirty: false, | ||||||
|   speciesList: null, |   speciesList: null, | ||||||
|   allCharacteristics: [], |   allCharacteristics: null, | ||||||
|   updateQueue: [], |  | ||||||
|   deleteQueue: [], |  | ||||||
| 
 | 
 | ||||||
|   // Actions
 |   // Actions
 | ||||||
|   "on-save": null, |   "on-save": null, | ||||||
|   "on-cancel": null, |   "on-cancel": null, | ||||||
|   "on-update": null, |   "on-update": null, | ||||||
|   "add-measurements": null, |   "add-characteristic": null, | ||||||
| 
 |   "save-measurement": null, | ||||||
|   // CPs
 |   "delete-measurement": null, | ||||||
|   sortParams: ['sortOrder'], |  | ||||||
|   sortedSpeciesList: sort('speciesList', 'sortParams'), |  | ||||||
| 
 | 
 | ||||||
|   // Property mapping
 |   // Property mapping
 | ||||||
|   propertiesList: ['strainName', 'typeStrain', 'species', 'isolatedFrom', 'accessionNumbers', 'genbank', 'wholeGenomeSequence', 'notes', 'measurements'], |   propertiesList: ['strainName', 'typeStrain', 'species', 'isolatedFrom', 'accessionNumbers', 'genbank', 'wholeGenomeSequence', 'notes'], | ||||||
|   strainName: null, |   strainName: null, | ||||||
|   typeStrain: null, |   typeStrain: null, | ||||||
|   species: null, |   species: null, | ||||||
|  | @ -33,55 +29,15 @@ export default Component.extend(SetupMetaData, { | ||||||
|   genbank: null, |   genbank: null, | ||||||
|   wholeGenomeSequence: null, |   wholeGenomeSequence: null, | ||||||
|   notes: null, |   notes: null, | ||||||
|   measurements: [], |  | ||||||
| 
 |  | ||||||
|   // Dropdown menu
 |  | ||||||
|   characteristics: [], |  | ||||||
|   charSortParams: ['characteristicTypeName', 'sortOrder', 'characteristicName'], |  | ||||||
|   sortedCharacteristics: sort('characteristics', 'charSortParams'), |  | ||||||
|   setupCharacteristics: Ember.on('init', function() { |  | ||||||
|     const tempArray = this._resetArray(this.get('allCharacteristics')); |  | ||||||
|     this.set('characteristics', tempArray); |  | ||||||
|   }), |  | ||||||
| 
 | 
 | ||||||
|   resetOnInit: Ember.on('init', function() { |   resetOnInit: Ember.on('init', function() { | ||||||
|     this._resetProperties(); |  | ||||||
|   }), |  | ||||||
| 
 |  | ||||||
|   _resetArray: function(arr) { |  | ||||||
|     let tempArray = []; |  | ||||||
|     arr.forEach((val) => { |  | ||||||
|       if (!val.get('isNew')) { |  | ||||||
|         tempArray.push(val); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|     return tempArray; |  | ||||||
|   }, |  | ||||||
| 
 |  | ||||||
|   _resetProperties: function() { |  | ||||||
|     // Still some coupling going on here because of adding strain to measurement
 |  | ||||||
|     this.get('measurements').forEach((val) => { |  | ||||||
|       if (val.get('hasDirtyAttributes')) { |  | ||||||
|         val.rollbackAttributes(); |  | ||||||
|       } |  | ||||||
|       if (val.get('isNew')) { |  | ||||||
|         this.get('strain.measurements').removeObject(val); |  | ||||||
|       } |  | ||||||
|     }); |  | ||||||
|     this.get('propertiesList').forEach((field) => { |     this.get('propertiesList').forEach((field) => { | ||||||
|       const valueInStrain = this.get('strain').get(field); |       const valueInStrain = this.get('strain').get(field); | ||||||
|       if (field === 'measurements') { |  | ||||||
|         const tempArray = this._resetArray(valueInStrain); |  | ||||||
|         this.set(field, tempArray); |  | ||||||
|       } else { |  | ||||||
|       this.set(field, valueInStrain); |       this.set(field, valueInStrain); | ||||||
|       } |  | ||||||
|     }); |     }); | ||||||
|     this.set('updateQueue', []); |  | ||||||
|     this.set('deleteQueue', []); |  | ||||||
|     // Read-only attributes
 |     // Read-only attributes
 | ||||||
|     this.set('isNew', this.get('strain.isNew')); |     this.set('isNew', this.get('strain.isNew')); | ||||||
|   }, |   }), | ||||||
| 
 | 
 | ||||||
|   updateField: function(property, value) { |   updateField: function(property, value) { | ||||||
|     this.set(property, value); |     this.set(property, value); | ||||||
|  | @ -95,32 +51,23 @@ export default Component.extend(SetupMetaData, { | ||||||
| 
 | 
 | ||||||
|   actions: { |   actions: { | ||||||
|     save: function() { |     save: function() { | ||||||
|       return this.attrs['on-save'](this.getProperties(this.get('propertiesList')), this.get('deleteQueue'), this.get('updateQueue')); |       return this.attrs['on-save'](this.getProperties(this.get('propertiesList'))); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     cancel: function() { |     cancel: function() { | ||||||
|       this._resetProperties(); |  | ||||||
|       return this.attrs['on-cancel'](); |       return this.attrs['on-cancel'](); | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     addMeasurement: function() { |     addCharacteristic: function() { | ||||||
|       const measurement = this.attrs['add-measurement'](); |       return this.attrs['add-characteristic'](); | ||||||
|       this.get('measurements').pushObject(measurement); |  | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     saveMeasurement: function(measurement, properties) { |     saveMeasurement: function(measurement, properties) { | ||||||
|       measurement.setProperties(properties); |       return this.attrs['save-measurement'](measurement, properties); | ||||||
|       measurement.set('strain', this.get('strain')); |  | ||||||
|       if (!measurement.get('isNew')) { |  | ||||||
|         this.get('updateQueue').pushObject(measurement); |  | ||||||
|       } |  | ||||||
|       this.set('isDirty', true); |  | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     deleteMeasurement: function(measurement) { |     deleteMeasurement: function(measurement) { | ||||||
|       this.get('deleteQueue').pushObject(measurement); |       return this.attrs['delete-measurement'](measurement); | ||||||
|       this.get('measurements').removeObject(measurement); |  | ||||||
|       this.set('isDirty', true); |  | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     strainNameDidChange: function(value) { |     strainNameDidChange: function(value) { | ||||||
|  | @ -153,7 +100,7 @@ export default Component.extend(SetupMetaData, { | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     notesDidChange: function(value) { |     notesDidChange: function(value) { | ||||||
|       this.updateField('notes', value); |       this.updateField('strain.notes', value); | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -15,13 +15,11 @@ | ||||||
|     <div data-row-span="2"> |     <div data-row-span="2"> | ||||||
|       <div data-field-span="2"> |       <div data-field-span="2"> | ||||||
|         <label>Species</label> |         <label>Species</label> | ||||||
|         {{ |         <select onchange={{action "speciesDidChange" value="target.value"}}> | ||||||
|           x-select |           {{#each speciesList as |speciesChoice|}} | ||||||
|           options=sortedSpeciesList |             <option value={{speciesChoice.id}} selected={{equal species.id speciesChoice.id}}>{{speciesChoice.speciesName}}</option> | ||||||
|           nameAttr='speciesName' |           {{/each}} | ||||||
|           selected=species.id |         </select> | ||||||
|           update=(action "speciesDidChange") |  | ||||||
|         }} |  | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     <div data-row-span="2"> |     <div data-row-span="2"> | ||||||
|  | @ -30,7 +28,7 @@ | ||||||
|         {{text-editor value=isolatedFrom update=(action "isolatedFromDidChange")}} |         {{text-editor value=isolatedFrom update=(action "isolatedFromDidChange")}} | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|     <div data-row-span="2"> |     <div data-row-span="3"> | ||||||
|       <div data-field-span="1"> |       <div data-field-span="1"> | ||||||
|         <label>Accession Numbers</label> |         <label>Accession Numbers</label> | ||||||
|         {{one-way-input type="text" class="accession-numbers" value=accessionNumbers update=(action "accessionNumbersNameDidChange")}} |         {{one-way-input type="text" class="accession-numbers" value=accessionNumbers update=(action "accessionNumbersNameDidChange")}} | ||||||
|  | @ -39,8 +37,6 @@ | ||||||
|         <label>GenBank</label> |         <label>GenBank</label> | ||||||
|         {{one-way-input type="text" class="genbank" value=genbank update=(action "genbankDidChange")}} |         {{one-way-input type="text" class="genbank" value=genbank update=(action "genbankDidChange")}} | ||||||
|       </div> |       </div> | ||||||
|     </div> |  | ||||||
|     <div data-row-span="1"> |  | ||||||
|       <div data-field-span="1"> |       <div data-field-span="1"> | ||||||
|         <label>Whole Genome Sequence</label> |         <label>Whole Genome Sequence</label> | ||||||
|         {{one-way-input type="text" class="whole-genome-sequenc" value=wholeGenomeSequence update=(action "wholeGenomeSequenceDidChange")}} |         {{one-way-input type="text" class="whole-genome-sequenc" value=wholeGenomeSequence update=(action "wholeGenomeSequenceDidChange")}} | ||||||
|  | @ -53,24 +49,18 @@ | ||||||
|       </div> |       </div> | ||||||
|     </div> |     </div> | ||||||
|   </fieldset> |   </fieldset> | ||||||
|   {{#if isNew}} |  | ||||||
|     <div> |  | ||||||
|       Please save before adding measurements |  | ||||||
|     </div> |  | ||||||
|   {{else}} |  | ||||||
|   <div> |   <div> | ||||||
|     {{ |     {{ | ||||||
|       protected/strains/measurements-table |       protected/strains/measurements-table | ||||||
|         measurements=measurements |       strain=strain | ||||||
|         add-measurement=(action "addMeasurement") |       add-characteristic=(action "addCharacteristic") | ||||||
|         allCharacteristics=sortedCharacteristics |       allCharacteristics=allCharacteristics | ||||||
|       save-measurement=(action "saveMeasurement") |       save-measurement=(action "saveMeasurement") | ||||||
|       delete-measurement=(action "deleteMeasurement") |       delete-measurement=(action "deleteMeasurement") | ||||||
|       canEdit=strain.canEdit |       canEdit=strain.canEdit | ||||||
|       canAdd=metaData.canAdd |       canAdd=metaData.canAdd | ||||||
|     }} |     }} | ||||||
|   </div> |   </div> | ||||||
|   {{/if}} |  | ||||||
|   <br> |   <br> | ||||||
|   <a class="button-red smaller" {{action 'cancel'}}> |   <a class="button-red smaller" {{action 'cancel'}}> | ||||||
|     Cancel |     Cancel | ||||||
|  |  | ||||||
|  | @ -5,23 +5,18 @@ const { Controller, inject: { service } } = Ember; | ||||||
| export default Controller.extend({ | export default Controller.extend({ | ||||||
|   session: service(), |   session: service(), | ||||||
|   ajax: service(), |   ajax: service(), | ||||||
|   ajaxError: service('ajax-error'), |  | ||||||
|   currentUser: service('session-account'), |   currentUser: service('session-account'), | ||||||
| 
 | 
 | ||||||
|   actions: { |   actions: { | ||||||
|     save: function(password) { |     save: function(password) { | ||||||
|       const id = this.get('currentUser.account.id'); |       const id = this.get('currentUser.account.id'); | ||||||
|       const data = { id: id, password: password }; |       const data = { id: id, password: password }; | ||||||
|       this.get('ajax').post('/users/password', { data: data }).then(() => { |       this.get('ajax').post('/users/password', { data: data }); | ||||||
|       this.transitionToRoute('protected.users.show', id); |       this.transitionToRoute('protected.users.show', id); | ||||||
|       this.get('flashMessages').information('Your password has been changed.'); |       this.get('flashMessages').information('Your password has been changed.'); | ||||||
|       }, (errors) => { |  | ||||||
|         this.get('ajaxError').alert(errors); |  | ||||||
|       }); |  | ||||||
|     }, |     }, | ||||||
| 
 | 
 | ||||||
|     cancel: function() { |     cancel: function() { | ||||||
|       this.get('flashMessages').clearMessages(); |  | ||||||
|       this.transitionToRoute('protected.users.show', this.get('currentUser.account.id')); |       this.transitionToRoute('protected.users.show', this.get('currentUser.account.id')); | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|  | @ -11,8 +11,6 @@ export default Route.extend({ | ||||||
|       if (!user.get('isAdmin')) { |       if (!user.get('isAdmin')) { | ||||||
|         this.transitionTo('protected.index'); |         this.transitionTo('protected.index'); | ||||||
|       } |       } | ||||||
|     }, () => { |  | ||||||
|       this.transitionTo('protected.index'); |  | ||||||
|     }); |     }); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -15,13 +15,11 @@ export default Route.extend({ | ||||||
|       if (!user.get('isAdmin') && user.get('id') !== user_id) { |       if (!user.get('isAdmin') && user.get('id') !== user_id) { | ||||||
|         this.transitionTo('protected.users.index'); |         this.transitionTo('protected.users.index'); | ||||||
|       } |       } | ||||||
|     }, () => { |  | ||||||
|       this.transitionTo('protected.users.index'); |  | ||||||
|     }); |     }); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   model: function(params) { |   model: function(params) { | ||||||
|     return this.store.findRecord('user', params.user_id, { reload: true }); |     return this.store.findRecord('user', params.user_id); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -5,41 +5,24 @@ const { RESTSerializer } = DS; | ||||||
| const { isNone } = Ember; | const { isNone } = Ember; | ||||||
| 
 | 
 | ||||||
| export default RESTSerializer.extend({ | export default RESTSerializer.extend({ | ||||||
|   serializeBelongsTo: function(snapshot, json, relationship) { |   isNewSerializerAPI: true, | ||||||
|     const key = relationship.key; |  | ||||||
|     if (this._canSerialize(key)) { |  | ||||||
|       const belongsToId = snapshot.belongsTo(key, { id: true }); |  | ||||||
|       let payloadKey = this._getMappedKey(key, snapshot.type); |  | ||||||
|       if (payloadKey === key && this.keyForRelationship) { |  | ||||||
|         payloadKey = this.keyForRelationship(key, "belongsTo", "serialize"); |  | ||||||
|       } |  | ||||||
|       if (isNone(belongsToId)) { |  | ||||||
|         json[payloadKey] = null; |  | ||||||
|       } else { |  | ||||||
|         json[payloadKey] = +belongsToId; |  | ||||||
|       } |  | ||||||
| 
 | 
 | ||||||
|       if (relationship.options.polymorphic) { |   serializeBelongsTo: function(snapshot, json, relationship) { | ||||||
|         this.serializePolymorphicType(snapshot, json, relationship); |     let key = relationship.key; | ||||||
|       } |     const belongsTo = snapshot.belongsTo(key); | ||||||
|     } |     key = this.keyForRelationship ? this.keyForRelationship(key, "belongsTo", "serialize") : key; | ||||||
|  |     json[key] = isNone(belongsTo) ? belongsTo : +belongsTo.record.id; | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   serializeHasMany: function(snapshot, json, relationship) { |   serializeHasMany: function(snapshot, json, relationship) { | ||||||
|     const key = relationship.key; |     let key = relationship.key; | ||||||
|     if (this._shouldSerializeHasMany(snapshot, key, relationship)) { |     const hasMany = snapshot.hasMany(key); | ||||||
|       const hasMany = snapshot.hasMany(key, { ids: true }); |     key = this.keyForRelationship ? this.keyForRelationship(key, "hasMany", "serialize") : key; | ||||||
|       if (hasMany !== undefined) { | 
 | ||||||
|         let payloadKey = this._getMappedKey(key, snapshot.type); |     json[key] = []; | ||||||
|         if (payloadKey === key && this.keyForRelationship) { |  | ||||||
|           payloadKey = this.keyForRelationship(key, "hasMany", "serialize"); |  | ||||||
|         } |  | ||||||
|         json[payloadKey] = []; |  | ||||||
|     hasMany.forEach((item) => { |     hasMany.forEach((item) => { | ||||||
|           json[payloadKey].push(+item); |       json[key].push(+item.id); | ||||||
|     }); |     }); | ||||||
|       } |  | ||||||
|     } |  | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
| }); | }); | ||||||
|  |  | ||||||
|  | @ -1,19 +0,0 @@ | ||||||
| import Ember from 'ember'; |  | ||||||
| 
 |  | ||||||
| const { Service, inject: { service } } = Ember; |  | ||||||
| 
 |  | ||||||
| export default Service.extend({ |  | ||||||
|   flashMessages: service(), |  | ||||||
| 
 |  | ||||||
|   alert: function(error) { |  | ||||||
|     const flash = this.get('flashMessages'); |  | ||||||
| 
 |  | ||||||
|     flash.clearMessages(); |  | ||||||
|     window.scrollTo(0,0); |  | ||||||
|     error.errors.forEach((error) => { |  | ||||||
|       console.error(error); |  | ||||||
|       const source = error.source.pointer.split('/'); |  | ||||||
|       flash.error(`${source[source.length-1].replace(/([A-Z])/g, ' $1').capitalize()} - ${error.detail}`); |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
| }); |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| import Ember from 'ember'; |  | ||||||
| import config from '../config/environment'; |  | ||||||
| 
 |  | ||||||
| const { Service } = Ember; |  | ||||||
| 
 |  | ||||||
| export default Service.extend({ |  | ||||||
|   genus: config.APP.genus, |  | ||||||
|   apiURL: config.apiURL, |  | ||||||
| }); |  | ||||||
							
								
								
									
										12
									
								
								bower.json
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								bower.json
									
										
									
									
									
								
							|  | @ -2,25 +2,27 @@ | ||||||
|   "name": "hymenobacterdotinfo", |   "name": "hymenobacterdotinfo", | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "jquery": "~2.1.1", |     "jquery": "~2.1.1", | ||||||
|     "ember": "~2.2.0", |     "ember": "1.13.10", | ||||||
|     "ember-cli-shims": "0.0.6", |     "ember-cli-shims": "0.0.6", | ||||||
|     "ember-cli-test-loader": "0.2.1", |     "ember-cli-test-loader": "0.2.1", | ||||||
|     "ember-data": "~2.2.1", |     "ember-data": "1.13.15", | ||||||
|     "ember-load-initializers": "0.1.7", |     "ember-load-initializers": "0.1.7", | ||||||
|     "ember-qunit": "0.4.16", |     "ember-qunit": "0.4.16", | ||||||
|     "ember-qunit-notifications": "0.1.0", |     "ember-qunit-notifications": "0.1.0", | ||||||
|     "ember-resolver": "~0.1.20", |     "ember-resolver": "~0.1.20", | ||||||
|     "loader.js": "ember-cli/loader.js#3.4.0", |     "loader.js": "ember-cli/loader.js#3.2.1", | ||||||
|     "qunit": "~1.20.0", |     "qunit": "~1.20.0", | ||||||
|     "flakes": "~1.0.0", |     "flakes": "~1.0.0", | ||||||
|     "moment": "~2.10.6", |     "moment": "~2.10.6", | ||||||
|     "select2": "4.0.1-rc.1", |     "select2": "3.5.2", | ||||||
|     "antiscroll": "git://github.com/azirbel/antiscroll.git#90391fb371c7be769bc32e7287c5271981428356", |     "antiscroll": "git://github.com/azirbel/antiscroll.git#90391fb371c7be769bc32e7287c5271981428356", | ||||||
|     "jquery-mousewheel": "~3.1.4", |     "jquery-mousewheel": "~3.1.4", | ||||||
|     "jquery-ui": "~1.11.4", |     "jquery-ui": "~1.11.4", | ||||||
|     "quill": "~0.19.14", |     "quill": "~0.19.14", | ||||||
|     "pretender": "~0.10.1", |     "pretender": "~0.10.1", | ||||||
|     "lodash": "~3.7.0", |     "lodash": "~3.7.0", | ||||||
|     "Faker": "~3.0.0" |     "Faker": "~3.0.0", | ||||||
|  |     "selectize": "~0.12.1", | ||||||
|  |     "jQuery UI Sortable": "jquery-ui-sortable#*" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ module.exports = function(environment) { | ||||||
|       'script-src': "'self'", |       'script-src': "'self'", | ||||||
|       'font-src': "'self'", |       'font-src': "'self'", | ||||||
|       'connect-src': "'self'", |       'connect-src': "'self'", | ||||||
|       'img-src': "'self' data:", |       'img-src': "'self'", | ||||||
|       'style-src': "'self' 'unsafe-inline'", |       'style-src': "'self' 'unsafe-inline'", | ||||||
|       'media-src': "'self'" |       'media-src': "'self'" | ||||||
|     } |     } | ||||||
|  | @ -61,7 +61,7 @@ module.exports = function(environment) { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   ENV.apiURL = apiURL; |   ENV.apiURL = apiURL; | ||||||
|   ENV.contentSecurityPolicy['connect-src'] = "'self' " + apiURL; |   ENV.contentSecurityPolicy['connect-src'] = `'self' ${apiURL}`; | ||||||
| 
 | 
 | ||||||
|   return ENV; |   return ENV; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | @ -14,8 +14,8 @@ module.exports = function(defaults) { | ||||||
|   // quill
 |   // quill
 | ||||||
|   app.import('bower_components/quill/dist/quill.base.css'); |   app.import('bower_components/quill/dist/quill.base.css'); | ||||||
|   app.import('bower_components/quill/dist/quill.snow.css'); |   app.import('bower_components/quill/dist/quill.snow.css'); | ||||||
|   // select2
 |   // selectize
 | ||||||
|   app.import('bower_components/select2/dist/css/select2.min.css'); |   app.import('bower_components/selectize/dist/css/selectize.default.css'); | ||||||
| 
 | 
 | ||||||
|   // LIBS ////////////////////////////////////////////////////////////////////////
 |   // LIBS ////////////////////////////////////////////////////////////////////////
 | ||||||
|   // flakes (and deps)
 |   // flakes (and deps)
 | ||||||
|  | @ -27,8 +27,10 @@ module.exports = function(defaults) { | ||||||
|   app.import('bower_components/moment/moment.js'); |   app.import('bower_components/moment/moment.js'); | ||||||
|   // quill
 |   // quill
 | ||||||
|   app.import('bower_components/quill/dist/quill.min.js'); |   app.import('bower_components/quill/dist/quill.min.js'); | ||||||
|   // select2
 |   // jquery ui sortable
 | ||||||
|   app.import('bower_components/select2/dist/js/select2.full.min.js'); |   app.import('bower_components/jQuery UI Sortable/jquery-ui-sortable.min.js'); | ||||||
|  |   // selectize
 | ||||||
|  |   app.import('bower_components/selectize/dist/js/standalone/selectize.min.js'); | ||||||
| 
 | 
 | ||||||
|   return app.toTree(); |   return app.toTree(); | ||||||
| }; | }; | ||||||
|  |  | ||||||
							
								
								
									
										13051
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
							
						
						
									
										13051
									
								
								package-lock.json
									
										
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										19
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										19
									
								
								package.json
									
										
									
									
									
								
							|  | @ -10,11 +10,7 @@ | ||||||
|   "scripts": { |   "scripts": { | ||||||
|     "build": "ember build", |     "build": "ember build", | ||||||
|     "start": "ember server", |     "start": "ember server", | ||||||
|     "test": "ember test", |     "test": "ember test" | ||||||
|     "bower": "bower", |  | ||||||
|     "ember": "ember", |  | ||||||
|     "firebase": "firebase", |  | ||||||
|     "deployProd": "firebase deploy -e prod" |  | ||||||
|   }, |   }, | ||||||
|   "repository": "", |   "repository": "", | ||||||
|   "engines": { |   "engines": { | ||||||
|  | @ -24,7 +20,7 @@ | ||||||
|   "license": "MIT", |   "license": "MIT", | ||||||
|   "devDependencies": { |   "devDependencies": { | ||||||
|     "broccoli-asset-rev": "^2.2.0", |     "broccoli-asset-rev": "^2.2.0", | ||||||
|     "ember-cli": "1.13.13", |     "ember-cli": "1.13.11", | ||||||
|     "ember-cli-app-version": "^1.0.0", |     "ember-cli-app-version": "^1.0.0", | ||||||
|     "ember-cli-babel": "^5.1.5", |     "ember-cli-babel": "^5.1.5", | ||||||
|     "ember-cli-content-security-policy": "0.4.0", |     "ember-cli-content-security-policy": "0.4.0", | ||||||
|  | @ -37,16 +33,13 @@ | ||||||
|     "ember-cli-mirage": "0.1.11", |     "ember-cli-mirage": "0.1.11", | ||||||
|     "ember-cli-qunit": "^1.0.4", |     "ember-cli-qunit": "^1.0.4", | ||||||
|     "ember-cli-release": "0.2.8", |     "ember-cli-release": "0.2.8", | ||||||
|     "ember-cli-sri": "^1.2.0", |     "ember-cli-sri": "^1.1.0", | ||||||
|     "ember-cli-uglify": "^1.2.0", |     "ember-cli-uglify": "^1.2.0", | ||||||
|     "ember-data": "~2.2.1", |     "ember-data": "1.13.15", | ||||||
|     "ember-disable-proxy-controllers": "^1.0.1", |     "ember-disable-proxy-controllers": "^1.0.1", | ||||||
|     "ember-export-application-global": "^1.0.4", |     "ember-export-application-global": "^1.0.4", | ||||||
|     "ember-one-way-input": "0.1.3", |     "ember-one-way-input": "0.1.3", | ||||||
|     "ember-simple-auth": "1.0.1" |     "ember-select-2": "1.3.0", | ||||||
|   }, |     "ember-simple-auth": "1.0.0" | ||||||
|   "dependencies": { |  | ||||||
|     "bower": "^1.8.8", |  | ||||||
|     "firebase-tools": "^7.12.1" |  | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -6,10 +6,10 @@ import { authenticateSession } from '../helpers/ember-simple-auth'; | ||||||
| module('Acceptance | characteristics', { | module('Acceptance | characteristics', { | ||||||
|   beforeEach: function() { |   beforeEach: function() { | ||||||
|     this.application = startApp(); |     this.application = startApp(); | ||||||
|     server.create('users', { role: 'A', canEdit: true, sub: 1 }); |  | ||||||
|     authenticateSession(this.application, { |     authenticateSession(this.application, { | ||||||
|       access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" |       access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" | ||||||
|     }); |     }); | ||||||
|  |     server.create('users', { role: 'A', canEdit: true }); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   afterEach: function() { |   afterEach: function() { | ||||||
|  | @ -70,22 +70,3 @@ test('creating /characteristics/new', function(assert) { | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 |  | ||||||
| test('deleting /characteristics/:id', function(assert) { |  | ||||||
|   const characteristic = server.create('characteristics', { 'canEdit': true }); |  | ||||||
|   visit(`/characteristics/${characteristic.id}`); |  | ||||||
| 
 |  | ||||||
|   andThen(function() { |  | ||||||
|     assert.equal(currentURL(), `/characteristics/${characteristic.id}`); |  | ||||||
|     click('button.delete'); |  | ||||||
| 
 |  | ||||||
|     andThen(function() { |  | ||||||
|       click('button.delete-confirm'); |  | ||||||
| 
 |  | ||||||
|       andThen(function() { |  | ||||||
|         assert.equal(currentURL(), `/characteristics`); |  | ||||||
|         assert.equal(server.db.characteristics.length, 0); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
|  | @ -6,10 +6,10 @@ import { authenticateSession } from '../helpers/ember-simple-auth'; | ||||||
| module('Acceptance | species', { | module('Acceptance | species', { | ||||||
|   beforeEach: function() { |   beforeEach: function() { | ||||||
|     this.application = startApp(); |     this.application = startApp(); | ||||||
|     server.create('users', { role: 'A', canEdit: true, sub: 1 }); |  | ||||||
|     authenticateSession(this.application, { |     authenticateSession(this.application, { | ||||||
|       access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" |       access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" | ||||||
|     }); |     }); | ||||||
|  |     server.create('users', { role: 'A', canEdit: true }); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   afterEach: function() { |   afterEach: function() { | ||||||
|  | @ -69,22 +69,3 @@ test('creating /species/new', function(assert) { | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 |  | ||||||
| test('deleting /species/:id', function(assert) { |  | ||||||
|   const species = server.create('species', { 'canEdit': true }); |  | ||||||
|   visit(`/species/${species.id}`); |  | ||||||
| 
 |  | ||||||
|   andThen(function() { |  | ||||||
|     assert.equal(currentURL(), `/species/${species.id}`); |  | ||||||
|     click('button.delete'); |  | ||||||
| 
 |  | ||||||
|     andThen(function() { |  | ||||||
|       click('button.delete-confirm'); |  | ||||||
| 
 |  | ||||||
|       andThen(function() { |  | ||||||
|         assert.equal(currentURL(), `/species`); |  | ||||||
|         assert.equal(server.db.species.length, 0); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
|  | @ -6,10 +6,10 @@ import { authenticateSession } from '../helpers/ember-simple-auth'; | ||||||
| module('Acceptance | strains', { | module('Acceptance | strains', { | ||||||
|   beforeEach: function() { |   beforeEach: function() { | ||||||
|     this.application = startApp(); |     this.application = startApp(); | ||||||
|     server.create('users', { role: 'A', canEdit: true, sub: 1 }); |  | ||||||
|     authenticateSession(this.application, { |     authenticateSession(this.application, { | ||||||
|       access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" |       access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" | ||||||
|     }); |     }); | ||||||
|  |     server.create('users', { role: 'A', canEdit: true }); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   afterEach: function() { |   afterEach: function() { | ||||||
|  | @ -74,23 +74,3 @@ test('creating /strains/new', function(assert) { | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
| }); | }); | ||||||
| 
 |  | ||||||
| test('deleting /strains/:id', function(assert) { |  | ||||||
|   const species = server.create('species'); |  | ||||||
|   const strain = server.create('strains', { canEdit: true , species: species.id }); |  | ||||||
|   visit(`/strains/${strain.id}`); |  | ||||||
| 
 |  | ||||||
|   andThen(function() { |  | ||||||
|     assert.equal(currentURL(), `/strains/${strain.id}`); |  | ||||||
|     click('button.delete'); |  | ||||||
| 
 |  | ||||||
|     andThen(function() { |  | ||||||
|       click('button.delete-confirm'); |  | ||||||
| 
 |  | ||||||
|       andThen(function() { |  | ||||||
|         assert.equal(currentURL(), `/strains`); |  | ||||||
|         assert.equal(server.db.strains.length, 0); |  | ||||||
|       }); |  | ||||||
|     }); |  | ||||||
|   }); |  | ||||||
| }); |  | ||||||
|  |  | ||||||
|  | @ -6,10 +6,10 @@ import { invalidateSession, authenticateSession } from '../helpers/ember-simple- | ||||||
| module('Acceptance | users', { | module('Acceptance | users', { | ||||||
|   beforeEach: function() { |   beforeEach: function() { | ||||||
|     this.application = startApp(); |     this.application = startApp(); | ||||||
|     server.create('users', { role: 'A', canEdit: true, sub: 1 }); |  | ||||||
|     authenticateSession(this.application, { |     authenticateSession(this.application, { | ||||||
|       access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" |       access_token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJiYWN0ZGIiLCJzdWIiOiIxIiwiZXhwIjoxNDQ2NTAyMjI2LCJpYXQiOjE0NDY0OTg2MjZ9.vIjKHAsp2TkCV505EbtCo2xQT-2oQkB-Nv5y0b6E7Mg" | ||||||
|     }); |     }); | ||||||
|  |     server.create('users', { role: 'A', canEdit: true }); | ||||||
|   }, |   }, | ||||||
| 
 | 
 | ||||||
|   afterEach: function() { |   afterEach: function() { | ||||||
|  |  | ||||||
							
								
								
									
										23
									
								
								tests/unit/initializers/global-variables-test.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								tests/unit/initializers/global-variables-test.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,23 @@ | ||||||
|  | import Ember from 'ember'; | ||||||
|  | import { initialize } from '../../../initializers/global-variables'; | ||||||
|  | import { module, test } from 'qunit'; | ||||||
|  | 
 | ||||||
|  | var container, application; | ||||||
|  | 
 | ||||||
|  | module('Unit | Initializer | global variables', { | ||||||
|  |   beforeEach: function() { | ||||||
|  |     Ember.run(function() { | ||||||
|  |       application = Ember.Application.create(); | ||||||
|  |       container = application.__container__; | ||||||
|  |       application.deferReadiness(); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | // Replace this with your real tests.
 | ||||||
|  | test('it works', function(assert) { | ||||||
|  |   initialize(container, application); | ||||||
|  | 
 | ||||||
|  |   // you would normally confirm the results of the initializer here
 | ||||||
|  |   assert.ok(true); | ||||||
|  | }); | ||||||
		Reference in a new issue