Compare commits

..

2 commits

Author SHA1 Message Date
4875f42152 WIP 2017-12-03 20:18:46 -07:00
7f73046773 WIP 2017-12-03 17:06:50 -07:00
61 changed files with 390 additions and 22388 deletions

View file

@ -3,17 +3,11 @@ branches:
only:
- master
node_js:
- "9"
sudo: required
dist: trusty
addons:
chrome: stable
- '6'
sudo: false
cache:
directories:
- "$HOME/.npm"
env:
global:
- JOBS=1
before_install:
- npm config set spin false
- npm install -g phantomjs-prebuilt

View file

@ -10,7 +10,7 @@ You will need the following things properly installed on your computer.
* [Git](https://git-scm.com/)
* [Node.js](https://nodejs.org/) (with NPM)
* [Ember CLI](https://ember-cli.com/)
* [Google Chrome](https://google.com/chrome/)
* [PhantomJS](http://phantomjs.org/)
## Installation

View file

@ -1,9 +1,11 @@
import Application from '@ember/application';
import Ember from 'ember';
import Resolver from './resolver';
import loadInitializers from 'ember-load-initializers';
import config from './config/environment';
const App = Application.extend({
let App;
App = Ember.Application.extend({
modulePrefix: config.modulePrefix,
podModulePrefix: config.podModulePrefix,
Resolver

View file

@ -1,11 +1,9 @@
import { Promise } from 'rsvp';
import $ from 'jquery';
import { get } from '@ember/object';
import { isEmpty } from '@ember/utils';
import { run } from '@ember/runloop';
import Ember from 'ember';
import BaseAuthenticator from 'ember-simple-auth/authenticators/base';
import config from '../config/environment';
const { RSVP: { Promise }, $, get, isEmpty, run } = Ember;
export default BaseAuthenticator.extend({
serverTokenEndpoint: `${config.APP.API_HOST}/api/auth/login/`,
tokenAttributeName: 'data.attributes.auth-token',

View file

@ -1,7 +1,8 @@
import { isEmpty } from '@ember/utils';
import { get } from '@ember/object';
import Ember from 'ember';
import BaseAuthorizer from 'ember-simple-auth/authorizers/base';
const { isEmpty, get } = Ember;
export default BaseAuthorizer.extend({
authorize(data, block) {
const accessToken = get(data, 'data.attributes.auth-token');

View file

@ -1,4 +1,6 @@
import Component from '@ember/component';
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({
tagName: 'a',

View file

@ -1,3 +1,5 @@
import Component from '@ember/component';
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({});

View file

@ -1,3 +1,5 @@
import Component from '@ember/component';
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({ });

View file

@ -1,6 +1,6 @@
import Component from '@ember/component';
import { alias } from '@ember/object/computed';
import { computed } from '@ember/object';
import Ember from 'ember';
const { Component, computed: { alias }, computed } = Ember;
export default Component.extend({
// ARGS
@ -8,7 +8,7 @@ export default Component.extend({
// COMPUTED
meta: alias('model.meta'),
links: alias('meta.links'),
links: alias('model.links'),
currentPage: alias('meta.pagination.page'),
totalRecords: alias('meta.pagination.count'),

View file

@ -1,6 +1,8 @@
import Component from '@ember/component';
import Ember from 'ember';
import Table from 'ember-light-table';
const { Component } = Ember;
export default Component.extend({
// ARGS
model: null,

View file

@ -1,11 +1,8 @@
import { getProperties, set } from '@ember/object';
import Component from '@ember/component';
import { inject as service } from '@ember/service';
import { debounce } from '@ember/runloop';
import RSVP from 'rsvp';
import Ember from 'ember';
import Changeset from 'ember-changeset';
import lookupValidator from 'ember-changeset-validations';
import config from 'ccdb-web/config/environment';
const { Component, inject: { service } } = Ember;
export default Component.extend({
store: service(),
@ -14,80 +11,81 @@ export default Component.extend({
this._super(...arguments);
const model = this.get('model');
const validations = this.get('validations');
const hasMany = this.get('hasMany');
let changesets = {};
changesets['new'] = [];
changesets['delete'] = [];
changesets['hasMany'] = {};
changesets['hasMany'] = [];
changesets['model'] = new Changeset(model,
lookupValidator(validations['collection']),
validations['collection']);
hasMany.forEach((hasMany) => {
let relatedChangesets = [];
let validation = validations[hasMany];
const related = model.get(hasMany);
related.forEach((r) => {
const changeset = new Changeset(r, lookupValidator(validation),
validation);
relatedChangesets.push({ model: r, changeset: changeset });
let collectionSpeciesChangesets = [];
const collectionSpecies = model.get('collectionSpecies');
collectionSpecies.forEach((cs) => {
const changeset = new Changeset(cs,
lookupValidator(validations['collectionSpecies']),
validations['collectionSpecies']);
collectionSpeciesChangesets.push({ model: cs, changeset: changeset });
});
changesets['hasMany'][hasMany] = relatedChangesets;
changesets['hasMany']['collectionSpecies'] = collectionSpeciesChangesets;
let datasheetsChangesets = [];
const datasheets = model.get('datasheets');
datasheets.forEach((d) => {
const changeset = new Changeset(d,
lookupValidator(validations['datasheet']),
validations['datasheet']);
datasheetsChangesets.push({ model: d, changeset: changeset });
});
changesets['hasMany']['datasheets'] = datasheetsChangesets;
this.set('changesets', changesets);
this.set('newStudyLocationAdmin', `${config.APP.API_HOST}/admin/locations/studylocation/add/`);
},
actions: {
addHasMany(modelName, relatedName) {
addCollectionSpecies() {
const store = this.get('store');
let changesets = this.get('changesets');
const validations = this.get('validations');
const validation = validations[relatedName];
const model = this.get('model');
const related = store.createRecord(modelName, { collection: model });
model.get(relatedName).pushObject(related);
changesets['new'].pushObject(related);
const changeset = new Changeset(related, lookupValidator(validation), validation);
changesets['hasMany'][relatedName].pushObject({ model: related, changeset: changeset });
const collection = this.get('model');
const cs = store.createRecord('collection-species', { collection: collection });
collection.get('collectionSpecies').pushObject(cs);
changesets['new'].pushObject(cs);
const changeset = new Changeset(cs,
lookupValidator(validations['collectionSpecies']),
validations['collectionSpecies']);
changesets['hasMany']['collectionSpecies'].pushObject({ model: cs, changeset: changeset });
},
deleteHasMany(changesetRecord, relatedName) {
deleteCollectionSpecies(changesetRecord) {
let changesets = this.get('changesets');
changesets['delete'].pushObject(changesetRecord.model);
changesets['hasMany'][relatedName].removeObject(changesetRecord);
},
// Gross, this side-effects by saving immediately. Someday I should clean
// this up, but for now, you have been warned.
addOption(relatedModelName, optionName, collectionAttrName, relatedAttrName, term) {
const props = getProperties(this, 'store', 'options', 'changesets');
const { store, options, changesets: { model } } = props;
let payload = {};
payload[relatedAttrName] = term;
const record = store.createRecord(relatedModelName, payload)
record.save().then((record) => {
set(options, optionName, store.peekAll(relatedModelName));
set(model, collectionAttrName, record);
});
changesets['hasMany']['collectionSpecies'].removeObject(changesetRecord);
},
updateDatasheet(changeset, event) {
changeset.set('datasheet', event.target.files[0]);
},
searchStudyLocation(term) {
return new RSVP.Promise((resolve, reject) => {
debounce(this, this._performSearch, 'study-location', { page_size: 500, code: term }, resolve, reject, 400);
});
},
addDatasheet() {
const store = this.get('store');
let changesets = this.get('changesets');
const validations = this.get('validations');
const collection = this.get('model');
const d = store.createRecord('datasheet-attachment', { collection: collection });
collection.get('datasheets').pushObject(d);
changesets['new'].pushObject(d);
const changeset = new Changeset(d,
lookupValidator(validations['datasheets']),
validations['datasheets']);
changesets['hasMany']['datasheets'].pushObject({ model: d, changeset: changeset });
},
_performSearch(model, payload, resolve, reject) {
this.get('store').query(model, payload).then((results) => {
resolve(results);
}, reject);
deleteDatasheet(changesetRecord) {
let changesets = this.get('changesets');
changesets['delete'].pushObject(changesetRecord.model);
changesets['hasMany']['datasheets'].removeObject(changesetRecord);
},
},
});

View file

@ -1,4 +1,6 @@
import Component from '@ember/component';
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({
// ARGS
@ -22,13 +24,6 @@ export default Component.extend({
{ label: 'Species', valuePath: 'species.commonName' },
{ label: 'Count', valuePath: 'count' },
{ label: 'Count Estimated?', valuePath: 'countEstimated' },
{ label: 'Sex', valuePath: 'sex.name' },
],
envMeasColumns: [
{ label: 'Date Measured', valuePath: 'dateMeasured', },
{ label: 'Time Measured', valuePath: 'timeMeasured', },
{ label: 'Water Temp (deg C)', valuePath: 'waterTempC', },
{ label: 'Air Temp (deg C)', valuePath: 'airTempC', },
{ label: 'Sex', valuePath: 'sex' },
],
});

View file

@ -1,4 +1,6 @@
import Component from '@ember/component';
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({
// ARGS

View file

@ -1,23 +0,0 @@
import Component from '@ember/component';
export default Component.extend({
tagName: 'span',
showConfirm: false,
initialLabel: 'LABEL',
confirmLabel: 'CONFIRM LABEL',
cancelLabel: 'Cancel',
actions: {
initial() {
this.set('showConfirm', true);
},
cancel() {
this.set('showConfirm', false);
},
confirm() {
this.get('onClick')();
},
},
});

View file

@ -1,4 +1,6 @@
import Component from '@ember/component';
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({
// ARGS

View file

@ -1,4 +1,6 @@
import Component from '@ember/component';
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({
tagName: 'form',

View file

@ -1,4 +1,6 @@
import Component from '@ember/component';
import Ember from 'ember';
const { Component } = Ember;
export default Component.extend({
classNames: ['spinner'],

View file

@ -1,6 +1,6 @@
import Component from '@ember/component';
import { get, computed } from '@ember/object';
import { isEmpty } from '@ember/utils';
import Ember from 'ember';
const { Component, computed, get, isEmpty } = Ember;
export default Component.extend({
classNames: ['form-group'],

View file

@ -1,5 +1,6 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import Ember from 'ember';
const { Controller, inject: { service } } = Ember;
export default Controller.extend({
session: service('session'),

View file

@ -1,23 +1,21 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import Ember from 'ember';
import CollectionValidations from 'ccdb-web/validations/collection';
import CollectionSpeciesValidations from 'ccdb-web/validations/collection-species';
import CollectionMeasurementValidations from 'ccdb-web/validations/collection-measurement';
import DatasheetValidations from 'ccdb-web/validations/datasheet';
import ValidationMixin from 'ccdb-web/mixins/validation';
const { Controller, computed } = Ember;
export default Controller.extend(ValidationMixin, {
CollectionValidations,
CollectionSpeciesValidations,
DatasheetValidations,
CollectionMeasurementValidations,
hasMany: ['collectionSpecies', 'datasheets', 'envMeasurements'],
hasMany: ['collectionSpecies', 'datasheets'],
options: computed('projectOptions', 'studyLocationOptions',
'collectionTypeOptions', 'collectionMethodOptions',
'speciesOptions', 'adfgPermitOptions', 'sexOptions',
function() {
'speciesOptions', 'adfgPermitOptions', function() {
return {
projects: this.get('projectOptions'),
studyLocations: this.get('studyLocationOptions'),
@ -25,22 +23,17 @@ export default Controller.extend(ValidationMixin, {
collectionMethods: this.get('collectionMethodOptions'),
species: this.get('speciesOptions'),
adfgPermits: this.get('adfgPermitOptions'),
sexes: this.get('sexOptions'),
};
}),
actions: {
onSave(changeset) {
const postSave = () => { this.transitionToRoute('collections.index'); };
return this.transitionToRoute('loading').then(() => {
return this.validationSave(changeset, postSave);
});
},
onCancel(changeset) {
const postCancel = () => { this.transitionToRoute('collections.index'); };
return this.transitionToRoute('loading').then(() => {
return this.validationCancel(changeset, postCancel);
});
},
},
});

View file

@ -1,23 +1,22 @@
import Controller from '@ember/controller';
import { computed } from '@ember/object';
import Ember from 'ember';
import CollectionValidations from 'ccdb-web/validations/collection';
import CollectionSpeciesValidations from 'ccdb-web/validations/collection-species';
import CollectionMeasurementValidations from 'ccdb-web/validations/collection-measurement';
import DatasheetValidations from 'ccdb-web/validations/datasheet';
import ValidationMixin from 'ccdb-web/mixins/validation';
const { Controller, computed } = Ember;
export default Controller.extend(ValidationMixin, {
CollectionValidations,
CollectionSpeciesValidations,
DatasheetValidations,
CollectionMeasurementValidations,
hasMany: ['collectionSpecies', 'datasheets', 'envMeasurements'],
hasMany: ['collectionSpecies', 'datasheets'],
safeNavigate: false,
options: computed('projectOptions', 'studyLocationOptions',
'collectionTypeOptions', 'collectionMethodOptions',
'speciesOptions', 'adfgPermitOptions', 'sexOptions',
function() {
'speciesOptions', 'adfgPermitOptions', function() {
return {
projects: this.get('projectOptions'),
studyLocations: this.get('studyLocationOptions'),
@ -25,28 +24,29 @@ export default Controller.extend(ValidationMixin, {
collectionMethods: this.get('collectionMethodOptions'),
species: this.get('speciesOptions'),
adfgPermits: this.get('adfgPermitOptions'),
sexes: this.get('sexOptions'),
};
}),
actions: {
onSave(changesets) {
const postSave = () => {
this.set('safeNavigate', true);
// Use the model's ID here because of the ArrayProxy in the route
this.transitionToRoute('collections.detail', this.get('model.id'));
this.set('safeNavigate', false);
console.log('save');
};
return this.transitionToRoute('loading').then(() => {
return this.validationSave(changesets, postSave);
});
},
onCancel(changesets) {
const postCancel = () => {
this.set('safeNavigate', true);
// Use the model's ID here because of the ArrayProxy in the route
return this.transitionToRoute('collections.detail', this.get('model.id'));
this.transitionToRoute('collections.detail', this.get('model.id'));
this.set('safeNavigate', false);
console.log('cancel');
};
return this.transitionToRoute('loading').then(() => {
return this.validationCancel(changesets, postCancel);
});
},
},
});

View file

@ -1,14 +1,11 @@
import Controller from '@ember/controller';
import Ember from 'ember';
const { Controller } = Ember;
export default Controller.extend({
actions: {
editCollection() {
this.transitionToRoute('collections.detail.edit', this.get('model'));
},
deleteCollection() {
this.get('model')[0].destroyRecord().then(() => {
this.transitionToRoute('collections');
});
},
},
});

View file

@ -1,5 +1,6 @@
import Controller from '@ember/controller';
import { set, get, computed } from '@ember/object';
import Ember from 'ember';
const { Controller, computed, get, set } = Ember;
export default Controller.extend({

View file

@ -1,5 +1,6 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import Ember from 'ember';
const { Controller, inject: { service } } = Ember;
export default Controller.extend({
session: service(),

View file

@ -17,8 +17,8 @@
<body>
{{content-for "body"}}
<script integrity="" src="{{rootURL}}assets/vendor.js"></script>
<script integrity="" src="{{rootURL}}assets/ccdb-web.js"></script>
<script src="{{rootURL}}assets/vendor.js"></script>
<script src="{{rootURL}}assets/ccdb-web.js"></script>
{{content-for "body-footer"}}
</body>

View file

@ -0,0 +1,36 @@
import Ember from 'ember';
const { Mixin, run: { once } } = Ember;
export default Mixin.create({
actions: {
willTransition(transition) {
if (!this.get('controller.safeNavigate')) {
if (confirm('Any unsaved changes will be discarded.')) {
let model = this.get('controller.model');
let hasMany = this.get('controller.hasMany');
hasMany.forEach((relationship) => {
model.get(relationship).forEach((r) => {
once(this, () => {
if (r.get('isNew')) {
r.deleteRecord();
} else {
r.rollbackAttributes();
}
}, this);
});
});
if (model.get('isNew')) {
model.deleteRecord();
}
} else {
transition.abort();
return false
}
}
return true;
},
},
});

View file

@ -1,5 +1,6 @@
import Mixin from '@ember/object/mixin';
import { isArray } from '@ember/array';
import Ember from 'ember';
const { Mixin, isArray } = Ember;
const { keys } = Object;
// Portions borrowed from https://github.com/funtusov/ember-cli-form-data

View file

@ -1,7 +1,8 @@
import Mixin from '@ember/object/mixin';
import { get } from '@ember/object';
import RSVP from 'rsvp';
import Ember from 'ember';
const { Mixin, get, RSVP } = Ember;
const { keys } = Object;
const { isArray } = Array;
export default Mixin.create({
validationSave(changesets, postSave) {
@ -54,14 +55,10 @@ export default Mixin.create({
for (const model of changesets[key]) {
model.destroyRecord();
}
} else if (key === 'hasMany') {
const hasMany = changesets[key];
for (const hasManyKey of keys(changesets[key])) {
const hasManyChangesets = hasMany[hasManyKey];
for (const changeset of hasManyChangesets) {
} else if (isArray(changesets[key])) { // hasMany
for (const { changeset } of changesets[key]) {
changeset.rollback();
}
}
} else { // single
const changeset = changesets[key];
changeset.rollback();

View file

@ -1,12 +0,0 @@
import DS from 'ember-data';
const { Model, attr, belongsTo } = DS;
export default Model.extend({
dateMeasured: attr('ccdb-date'),
timeMeasured: attr('string'),
waterTempC: attr('number'),
airTempC: attr('number'),
collection: belongsTo('collection'),
});

View file

@ -3,7 +3,7 @@ import DS from 'ember-data';
const { Model, attr, belongsTo } = DS;
export default Model.extend({
sex: belongsTo('sex'),
sex: attr('string'),
count: attr('number'),
countEstimated: attr('boolean', { defaultValue: false }),

View file

@ -1,17 +1,16 @@
import { mapBy } from '@ember/object/computed';
import { computed } from '@ember/object';
import Ember from 'ember';
import DS from 'ember-data';
const { computed } = Ember;
const { Model, attr, belongsTo, hasMany } = DS;
export default Model.extend({
displayName: attr('string'),
numberOfTraps: attr('number'),
collectionStartDate: attr('ccdb-date'),
collectionStartDate: attr('string-null-to-empty'),
collectionStartTime: attr('string-null-to-empty'),
collectionEndDate: attr('ccdb-date'),
collectionEndDate: attr('string-null-to-empty'),
collectionEndTime: attr('string-null-to-empty'),
notes: attr('string', { defaultValue: '' }),
project: belongsTo('project'),
studyLocation: belongsTo('study-location'),
@ -21,14 +20,13 @@ export default Model.extend({
collectionSpecies: hasMany('collection-species'),
datasheets: hasMany('datasheet-attachment'),
envMeasurements: hasMany('collection-measurement'),
// computed
species: mapBy('collectionSpecies', 'species'),
species: computed.mapBy('collectionSpecies', 'species'),
speciesNames: mapBy('species', 'commonName'),
speciesNames: computed.mapBy('species', 'commonName'),
counts: mapBy('collectionSpecies', 'count'),
counts: computed.mapBy('collectionSpecies', 'count'),
speciesAndCounts: computed('speciesNames', 'counts', function() {
const speciesNames = this.get('speciesNames');

View file

@ -1,8 +0,0 @@
import DS from 'ember-data';
const { Model, attr } = DS;
export default Model.extend({
name: attr('string'),
sortOrder: attr('number'),
});

View file

@ -1,7 +1,7 @@
import EmberRouter from '@ember/routing/router';
import Ember from 'ember';
import config from './config/environment';
const Router = EmberRouter.extend({
const Router = Ember.Router.extend({
location: config.locationType,
rootURL: config.rootURL
});

View file

@ -1,4 +1,6 @@
import Route from '@ember/routing/route';
import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
const { Route } = Ember;
export default Route.extend(ApplicationRouteMixin, {});

View file

@ -1,18 +1,19 @@
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
import Ember from 'ember';
import CleanupFormMixin from 'ccdb-web/mixins/cleanup-form';
export default Route.extend({
const { Route, RSVP } = Ember;
export default Route.extend(CleanupFormMixin, {
model() {
const store = this.get('store');
return RSVP.hash({
model: store.createRecord('collection'),
projectOptions: store.findAll('project'),
studyLocationOptions: store.query('study-location', { page_size: 500 }),
studyLocationOptions: store.findAll('study-location'),
collectionTypeOptions: store.findAll('collection-type'),
collectionMethodOptions: store.findAll('collection-method'),
speciesOptions: store.query('species', { page_size: 500 }),
speciesOptions: store.findAll('species'),
adfgPermitOptions: store.findAll('adfg-permit'),
sexOptions: store.findAll('sex'),
});
},

View file

@ -1,11 +1,12 @@
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
import Ember from 'ember';
const { Route, RSVP } = Ember;
export default Route.extend({
model(params) {
return RSVP.all([
this.get('store').findRecord('collection', params.collection_id, {
include: 'collection-species,datasheets,env-measurements',
include: 'collection-species,datasheets',
})
]);
},

View file

@ -1,19 +1,20 @@
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
import Ember from 'ember';
import CleanupFormMixin from 'ccdb-web/mixins/cleanup-form';
export default Route.extend({
const { Route, RSVP } = Ember;
export default Route.extend(CleanupFormMixin, {
model() {
const store = this.get('store');
const model = this.modelFor('collections.detail');
return RSVP.hash({
model: model,
projectOptions: store.findAll('project'),
studyLocationOptions: store.query('study-location', { page_size: 500 }),
studyLocationOptions: store.findAll('study-location'),
collectionTypeOptions: store.findAll('collection-type'),
collectionMethodOptions: store.findAll('collection-method'),
speciesOptions: store.query('species', { page_size: 500 }),
speciesOptions: store.findAll('species'),
adfgPermitOptions: store.findAll('adfg-permit'),
sexOptions: store.findAll('sex'),
});
},

View file

@ -1,5 +1,6 @@
import Route from '@ember/routing/route';
import RSVP from 'rsvp';
import Ember from 'ember';
const { Route, RSVP } = Ember;
export default Route.extend({
queryParams: {
@ -29,10 +30,10 @@ export default Route.extend({
projectOptions: store.findAll('project'),
regionOptions: store.findAll('region'),
siteOptions: store.findAll('site'),
studyLocationOptions: store.query('study-location', { page_size: 500 }),
studyLocationOptions: store.findAll('study-location'),
collectionMethodOptions: store.findAll('collection-method'),
adfgPermitOptions: store.findAll('adfg-permit'),
speciesOptions: store.query('species', { page_size: 500 }),
speciesOptions: store.findAll('species'),
model: store.query('collection', Object.assign(params, opts)),
});
},
@ -43,38 +44,27 @@ export default Route.extend({
const store = this.get('store');
/* eslint-disable no-console */
let project = controller.get('project');
console.log('project', project);
project = project.map(id => store.peekRecord('project', id));
let region = controller.get('region');
console.log('region', region);
region = region.map(id => store.peekRecord('region', id));
let site = controller.get('site');
console.log('site', site);
site = site.map(id => store.peekRecord('site', id));
let studyLocation = controller.get('study_location');
console.log('studyLocation', studyLocation);
studyLocation = studyLocation.map(id => store.peekRecord('study-location', id));
let collectionMethod = controller.get('collection_method');
console.log('collectionMethod', collectionMethod);
collectionMethod = collectionMethod.map(id => store.peekRecord('collection-method', id));
let adfgPermit = controller.get('adfg_permit');
console.log('adfgPermit', adfgPermit);
adfgPermit = adfgPermit.map(id => store.peekRecord('adfg-permit', id));
let species = controller.get('species');
console.log('species', species);
species = species.map(id => store.peekRecord('species', id));
/* eslint-enable no-console */
const numberOfTraps = controller.get('number_of_traps');
const collectionStartDate = controller.get('collection_start_date');
const collectionEndDate = controller.get('collection_end_date');

View file

@ -1,6 +1,8 @@
import Route from '@ember/routing/route';
import Ember from 'ember';
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
const { Route } = Ember;
export default Route.extend(AuthenticatedRouteMixin, {
afterModel() {
this.transitionTo('collections');

View file

@ -1,4 +1,6 @@
import Route from '@ember/routing/route';
import Ember from 'ember';
import UnauthenticatedRouteMixin from 'ember-simple-auth/mixins/unauthenticated-route-mixin';
const { Route } = Ember;
export default Route.extend(UnauthenticatedRouteMixin, {});

View file

@ -1,5 +1,6 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
import Ember from 'ember';
const { Route, inject: { service }} = Ember;
export default Route.extend({
session: service('session'),

View file

@ -1,16 +1,10 @@
import { capitalize } from '@ember/string';
import Ember from 'ember';
import DS from 'ember-data';
const { JSONAPISerializer } = DS;
export default JSONAPISerializer.extend({
payloadTypeFromModelName(modelName) {
return modelName.split('-').map(key => capitalize(key)).join('');
},
normalizeArrayResponse(store, primaryModelClass, payload, id, requestType) {
let normalizedDocument = this._super(store, primaryModelClass, payload, id, requestType);
normalizedDocument.meta.links = normalizedDocument.links;
return normalizedDocument;
},
return modelName.split('-').map(key => Ember.String.capitalize(key)).join('');
}
});

View file

@ -4,10 +4,8 @@
validations=(hash
collection=CollectionValidations
collectionSpecies=CollectionSpeciesValidations
envMeasurements=CollectionMeasurementValidations
datasheet=DatasheetValidations)
options=options
hasMany=hasMany
onSave=(action 'onSave')
onCancel=(action 'onCancel')
}}

View file

@ -4,10 +4,8 @@
validations=(hash
collection=CollectionValidations
collectionSpecies=CollectionSpeciesValidations
datasheet=DatasheetValidations
envMeasurements=CollectionMeasurementValidations)
datasheet=DatasheetValidations)
options=options
hasMany=hasMany
onSave=(action 'onSave')
onCancel=(action 'onCancel')
}}

View file

@ -2,5 +2,4 @@
collection/detail-container
model=model
editCollection=(action 'editCollection')
deleteCollection=(action 'deleteCollection')
}}

View file

@ -21,32 +21,26 @@
{{/validated-field}}
{{#validated-field property='adfgPermit' label='ADFG Permit' changeset=changeset}}
{{#power-select-with-create
{{#power-select
options=options.adfgPermits
selected=changeset.adfgPermit
onchange=(action (mut changeset.adfgPermit))
oncreate=(action 'addOption' 'adfg-permit' 'adfgPermits' 'adfgPermit' 'name')
searchField='name'
as |adfgPermit term|
as |adfgPermit|
}}
{{adfgPermit.name}}
{{/power-select-with-create}}
{{/power-select}}
{{/validated-field}}
{{#validated-field property='studyLocation' changeset=changeset}}
<label class="control-label">
Study location
<a href="{{newStudyLocationAdmin}}" target="_blank">+</a>
</label>
{{#validated-field property='studyLocation' label='Study location' changeset=changeset}}
{{#power-select
search=(action 'searchStudyLocation')
options=options.studyLocations
selected=changeset.studyLocation
onchange=(action (mut changeset.studyLocation))
searchField='code'
searchField='name'
as |studyLocation|
}}
{{studyLocation.code}}
{{studyLocation.name}}
{{/power-select}}
{{/validated-field}}
@ -79,54 +73,29 @@
{{/validated-field}}
{{#validated-field property='collectionStartDate' label='Collection start date' changeset=changeset}}
{{
pikaday-input
onSelection=(action (mut changeset.collectionStartDate))
value=changeset.collectionStartDate
useUTC=true
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
class='form-control'
}}
{{input value=changeset.collectionStartDate type='date' class='form-control'}}
{{/validated-field}}
{{#validated-field property='collectionStartTime' label='Collection start time' changeset=changeset}}
{{input value=changeset.collectionStartTime type='time' class='form-control' placeholder='HH:MM:SS (24 hour)'}}
{{input value=changeset.collectionStartTime type='time' class='form-control'}}
{{/validated-field}}
{{#validated-field property='collectionEndDate' label='Collection end date' changeset=changeset}}
{{
pikaday-input
onSelection=(action (mut changeset.collectionEndDate))
value=changeset.collectionEndDate
useUTC=true
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
class='form-control'
}}
{{input value=changeset.collectionEndDate type='date' class='form-control'}}
{{/validated-field}}
{{#validated-field property='collectionEndTime' label='Collection end time' changeset=changeset}}
{{input value=changeset.collectionEndTime type='time' class='form-control' placeholder='HH:MM:SS (24 hour)'}}
{{input value=changeset.collectionEndTime type='time' class='form-control'}}
{{/validated-field}}
{{/with}}
{{/f.content}}
</div>
</div>
<div class="col-md-8">
<div class="row">
<div class="col-md-12">
{{#validated-field property='notes' label='Notes' changeset=changesets.model}}
{{textarea value=changesets.model.notes class='form-control'}}
{{/validated-field}}
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table">
<caption>
Species / Count Info
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addHasMany' 'collection-species' 'collectionSpecies')}}
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addCollectionSpecies')}}
</caption>
<thead>
<tr>
@ -165,75 +134,11 @@
</td>
<td class="col-md-3">
{{#validated-field property='sex' changeset=cs.changeset}}
{{#power-select
options=options.sexes
selected=cs.changeset.sex
onchange=(action (mut cs.changeset.sex))
searchField='name'
as |sex|
}}
{{sex.name}}
{{/power-select}}
{{input value=cs.changeset.sex}}
{{/validated-field}}
</td>
<td class="col-md-2">
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteHasMany' cs 'collectionSpecies')}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table class="table">
<caption>
Environmental Measurements
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addHasMany' 'collection-measurement' 'envMeasurements')}}
</caption>
<thead>
<tr>
<th class="col-md-3">Date Measured</th>
<th class="col-md-3">Time Measured</th>
<th class="col-md-3">Water Temperature (deg C)</th>
<th class="col-md-3">Air Temperature (deg C)</th>
<th class="col-md-1">Delete</th>
</tr>
</thead>
<tbody>
{{#each changesets.hasMany.envMeasurements as |cm|}}
<tr class="form">
<td class="col-md-3">
{{#validated-field property='dateMeasured' changeset=cm.changeset}}
{{
pikaday-input
onSelection=(action (mut cm.changeset.dateMeasured))
value=cm.changeset.dateMeasured
useUTC=true
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
class='form-control'
}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='timeMeasured' changeset=cm.changeset}}
{{input value=cm.changeset.timeMeasured type='time' class='form-control'}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='waterTempC' changeset=cm.changeset}}
{{input value=cm.changeset.waterTempC class='form-control'}}
{{/validated-field}}
</td>
<td class="col-md-3">
{{#validated-field property='airTempC' changeset=cm.changeset}}
{{input value=cm.changeset.airTempC class='form-control'}}
{{/validated-field}}
</td>
<td class="col-md-2">
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteHasMany' cm 'envMeasurements')}}
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteCollectionSpecies' cs)}}
</td>
</tr>
{{/each}}
@ -247,7 +152,7 @@
<table class="table">
<caption>
Attachments
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addHasMany' 'datasheet-attachment' 'datasheets')}}
{{action-button isSuccess=true isXSmall=true label='+' onClick=(action 'addDatasheet')}}
</caption>
<thead>
<tr>
@ -268,15 +173,14 @@
{{/if}}
</td>
<td>
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteHasMany' d 'datasheets')}}
{{action-button isDanger=true isXSmall=true label='X' onClick=(action 'deleteDatasheet' d)}}
</td>
</tr>
{{/each}}
</tbody>
</table>
</form>
</div>
</div>
</div>
</div>
{{f.save}} {{f.cancel}}

View file

@ -5,13 +5,6 @@
onClick=(action editCollection)
}}
{{
confirm-button
initialLabel='Delete Collection'
confirmLabel='Yes, Delete Collection'
onClick=(action deleteCollection)
}}
<h3>Main Detail</h3>
<div class="row">
<div class="col-md-12">
@ -40,24 +33,14 @@
<hr>
<h3>Environmental Measurements</h3>
<div class="row">
<div class="col-md-12">
{{#ccdb-table model=model.[0].envMeasurements columns=envMeasColumns as |c|}}
{{#c.grid as |g|}}
{{g.head}}
{{g.body}}
{{/c.grid}}
{{/ccdb-table}}
</div>
</div>
<hr>
<h3>Notes</h3>
<div class="row">
<div class="col-md-12">
{{model.[0].notes}}
<ul>
<li>PLACEHOLDER</li>
<li>PLACEHOLDER</li>
<li>PLACEHOLDER</li>
</ul>
</div>
</div>

View file

@ -102,8 +102,7 @@
onSelection=(action (mut filters.collection_start_date))
value=filters.collection_start_date
useUTC=true
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
format='YYYY-MM-DD'
class='form-control'
}}
</div>
@ -114,8 +113,7 @@
onSelection=(action (mut filters.collection_end_date))
value=filters.collection_end_date
useUTC=true
placeholder='MM/DD/YYYY'
format='MM/DD/YYYY'
format='YYYY-MM-DD'
class='form-control'
}}
</div>

View file

@ -1,6 +0,0 @@
{{#if showConfirm}}
{{action-button isDanger=true label=cancelLabel onClick=(action 'cancel')}}
{{action-button isSuccess=true label=confirmLabel onClick=(action 'confirm')}}
{{else}}
{{action-button isDanger=true label=initialLabel onClick=(action 'initial')}}
{{/if}}

View file

@ -1,19 +0,0 @@
import DS from 'ember-data';
export default DS.Transform.extend({
deserialize(serialized) {
return serialized || '';
},
serialize(date) {
if (date !== '') {
date = new Date(date);
const day = date.getUTCDate();
const month = date.getUTCMonth() + 1;
const year = date.getUTCFullYear();
return `${year}-${month}-${day}`;
} else {
return null;
}
}
});

View file

@ -1,12 +0,0 @@
import {
validatePresence,
validateNumber,
} from 'ember-changeset-validations/validators';
export default {
dateMeasured: validatePresence(true),
timeMeasured: validatePresence(true),
waterTempC: validateNumber({ allowBlank: true, integer: false, positive: false }),
airTempC: validateNumber({ allowBlank: true, integer: false, positive: false }),
collection: validatePresence(true),
}

View file

@ -20,23 +20,13 @@ module.exports = function(environment) {
APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
sentry: {
dsn: 'https://fd3c695fa9394de48a7c69b7a322960b@sentry.io/1186914',
globalErrorCatching: false,
},
contentSecurityPolicy: {
'script-src': "'self' 'unsafe-inline' 'unsafe-eval'",
'img-src': 'data: app.getsentry.com',
}
};
if (environment === 'development') {
ENV.APP.API_HOST = 'http://localhost:8000';
ENV.APP.API_NAMESPACE = 'api/v1';
ENV.sentry.development = true;
ENV.APP.LOG_TRANSITIONS = true;
}
if (environment === 'test') {
@ -51,7 +41,7 @@ module.exports = function(environment) {
}
if (environment === 'production') {
ENV.APP.API_HOST = 'https://ccdb-api.thermokar.st';
ENV.APP.API_HOST = 'https://obscure-caverns-99102.herokuapp.com';
ENV.APP.API_NAMESPACE = 'api/v1';
}

21802
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -17,26 +17,25 @@
"devDependencies": {
"broccoli-asset-rev": "^2.4.5",
"ember-ajax": "^3.0.0",
"ember-changeset": "^1.3.0",
"ember-changeset-validations": "^1.2.8",
"ember-cli": "^3.24.0",
"ember-changeset": "1.3.0",
"ember-changeset-validations": "1.2.8",
"ember-cli": "~2.14.0",
"ember-cli-app-version": "^3.0.0",
"ember-cli-babel": "^6.6.0",
"ember-cli-babel": "^6.3.0",
"ember-cli-code-coverage": "^0.4.1",
"ember-cli-dependency-checker": "^2.0.0",
"ember-cli-eslint": "^4.0.0",
"ember-cli-flash": "^1.4.3",
"ember-cli-dependency-checker": "^1.3.0",
"ember-cli-eslint": "^3.0.0",
"ember-cli-flash": "1.4.3",
"ember-cli-htmlbars": "^2.0.1",
"ember-cli-htmlbars-inline-precompile": "^1.0.0",
"ember-cli-htmlbars-inline-precompile": "^0.4.3",
"ember-cli-inject-live-reload": "^1.4.1",
"ember-cli-moment-shim": "^3.5.0",
"ember-cli-qunit": "^4.0.0",
"ember-cli-sentry": "^3.0.0",
"ember-cli-shims": "^1.1.0",
"ember-cli-sri": "^2.1.0",
"ember-cli-uglify": "^2.0.0",
"ember-cli-uglify": "^1.2.0",
"ember-composable-helpers": "^2.0.3",
"ember-data": "~2.16.2",
"ember-data": "~2.14.3",
"ember-export-application-global": "^2.0.0",
"ember-inflector": "^2.0.1",
"ember-light-table": "^1.10.0",
@ -44,19 +43,16 @@
"ember-moment": "7.3.1",
"ember-pikaday": "^2.2.3",
"ember-power-select": "^1.8.5",
"ember-power-select-with-create": "^0.4.3",
"ember-power-select-with-create": "0.4.3",
"ember-resolver": "^4.0.0",
"ember-responsive": "^2.0.4",
"ember-simple-auth": "1.4.0",
"ember-sinon": "^1.0.0",
"ember-source": "~2.16.0",
"ember-source": "~2.14.0",
"loader.js": "^4.2.3"
},
"engines": {
"node": "^4.5 || 6.* || >= 7.*"
},
"private": true,
"dependencies": {
"npm": "^5.7.1"
}
"private": true
}

View file

@ -3,20 +3,10 @@ module.exports = {
test_page: 'tests/index.html?hidepassed',
disable_watching: true,
launch_in_ci: [
'Chrome'
'PhantomJS'
],
launch_in_dev: [
'PhantomJS',
'Chrome'
],
browser_args: {
Chrome: {
mode: 'ci',
args: [
'--disable-gpu',
'--headless',
'--remote-debugging-port=9222',
'--window-size=1440,900'
]
},
}
};

View file

@ -1,5 +1,5 @@
import { run } from '@ember/runloop';
import Ember from 'ember';
export default function destroyApp(application) {
run(application, 'destroy');
Ember.run(application, 'destroy');
}

View file

@ -1,3 +1,6 @@
import Ember from 'ember';
import FlashObject from 'ember-cli-flash/flash/object';
FlashObject.reopen({ init() {} });
const { K } = Ember;
FlashObject.reopen({ init: K });

View file

@ -1,8 +1,10 @@
import { module } from 'qunit';
import { resolve } from 'rsvp';
import Ember from 'ember';
import startApp from '../helpers/start-app';
import destroyApp from '../helpers/destroy-app';
const { RSVP: { resolve } } = Ember;
export default function(name, options = {}) {
module(name, {
beforeEach() {

View file

@ -1,16 +1,17 @@
import { registerAsyncHelper } from '@ember/test';
import { A } from '@ember/array';
import { computed } from '@ember/object';
import { classify } from '@ember/string';
import { getOwner } from '@ember/application';
import Ember from 'ember';
import MediaService from 'ember-responsive/media';
const {
getOwner
} = Ember;
const { classify } = Ember.String;
MediaService.reopen({
// Change this if you want a different default breakpoint in tests.
_defaultBreakpoint: 'desktop',
_breakpointArr: computed('breakpoints', function() {
return Object.keys(this.get('breakpoints')) || A([]);
_breakpointArr: Ember.computed('breakpoints', function() {
return Object.keys(this.get('breakpoints')) || Ember.A([]);
}),
_forceSetBreakpoint(breakpoint) {
@ -44,7 +45,7 @@ MediaService.reopen({
}
});
export default registerAsyncHelper('setBreakpoint', function(app, breakpoint) {
export default Ember.Test.registerAsyncHelper('setBreakpoint', function(app, breakpoint) {
// this should use getOwner once that's supported
const mediaService = app.__deprecatedInstance__.lookup('service:media');
mediaService._forceSetBreakpoint(breakpoint);

View file

@ -1,13 +1,12 @@
import Ember from 'ember';
import Application from '../../app';
import config from '../../config/environment';
import { merge } from '@ember/polyfills';
import { run } from '@ember/runloop';
export default function startApp(attrs) {
let attributes = merge({}, config.APP);
attributes = merge(attributes, attrs); // use defaults, but you can override;
let attributes = Ember.merge({}, config.APP);
attributes = Ember.merge(attributes, attrs); // use defaults, but you can override;
return run(() => {
return Ember.run(() => {
let application = Application.create(attributes);
application.setupForTesting();
application.injectTestHelpers();

View file

@ -1,6 +1,6 @@
import $ from 'jquery';
import { moduleFor, test } from 'ember-qunit';
import sinon from 'sinon';
import Ember from 'ember';
moduleFor('authenticator:application', 'Unit | application', {
unit: true,
@ -35,28 +35,28 @@ test('`invalidate` should invalidate the session', function(assert) {
test('`makeRequest` should make a request', function(assert) {
assert.expect(2);
const stub = sinon.stub($, 'ajax');
const stub = sinon.stub(Ember.$, 'ajax');
stub.resolves(42);
const authenticator = this.subject();
authenticator.set('serverTokenEndpoint', 'foo')
const promise = authenticator.makeRequest({bar: 'baz'}).then((d) => {
assert.equal(d, 42);
});
assert.ok($.ajax.calledWithMatch({url: 'foo', data: {bar: 'baz'}}));
$.ajax.restore();
assert.ok(Ember.$.ajax.calledWithMatch({url: 'foo', data: {bar: 'baz'}}));
Ember.$.ajax.restore();
return promise;
});
test('authenticate should craft a nice payload', function(assert) {
assert.expect(2);
const stub = sinon.stub($, 'ajax');
const stub = sinon.stub(Ember.$, 'ajax');
stub.resolves(42);
const authenticator = this.subject();
authenticator.set('serverTokenEndpoint', 'foo')
const promise = authenticator.authenticate('myusername', 'mypassword').then((d) => {
assert.equal(d, 42);
});
assert.ok($.ajax.calledWithMatch({url: 'foo', data: {username: 'myusername', password: 'mypassword'}}));
$.ajax.restore();
assert.ok(Ember.$.ajax.calledWithMatch({url: 'foo', data: {username: 'myusername', password: 'mypassword'}}));
Ember.$.ajax.restore();
return promise;
});