diff --git a/.ember-cli b/.ember-cli index fd97b187..d4e4e336 100644 --- a/.ember-cli +++ b/.ember-cli @@ -6,5 +6,12 @@ Setting `disableAnalytics` to true will prevent any data from being sent. */ "disableAnalytics": false, + + /** + Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript + rather than JavaScript by default, when a TypeScript version of a given blueprint is available. + */ + "isTypeScriptProject": false, + "port": 4300 } diff --git a/.eslintignore b/.eslintignore index 1c0249db..ae117de9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -19,7 +19,10 @@ # ember-try /.node_modules.ember-try/ /bower.json.ember-try +/npm-shrinkwrap.json.ember-try /package.json.ember-try +/package-lock.json.ember-try +/yarn.lock.ember-try - +# custom /mirage/mirage diff --git a/.eslintrc.js b/.eslintrc.js index 979db294..0147760c 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -62,7 +62,7 @@ module.exports = { }, }, { - // Test files: + // test files files: ['tests/**/*-test.{js,ts}'], extends: ['plugin:qunit/recommended'], }, diff --git a/.gitignore b/.gitignore index 82447690..f1e859b2 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,10 @@ # ember-try /.node_modules.ember-try/ /bower.json.ember-try +/npm-shrinkwrap.json.ember-try /package.json.ember-try +/package-lock.json.ember-try +/yarn.lock.ember-try -.travis/*.key +# broccoli-debug +/DEBUG/ diff --git a/.prettierignore b/.prettierignore index 92216555..4178fd57 100644 --- a/.prettierignore +++ b/.prettierignore @@ -14,8 +14,12 @@ /coverage/ !.* .eslintcache +.lint-todo/ # ember-try /.node_modules.ember-try/ /bower.json.ember-try +/npm-shrinkwrap.json.ember-try /package.json.ember-try +/package-lock.json.ember-try +/yarn.lock.ember-try diff --git a/README.md b/README.md index 8ebcab95..e2030dbc 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,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/) +* [Ember CLI](https://cli.emberjs.com/release/) * [Google Chrome](https://google.com/chrome/) ## Installation @@ -49,7 +49,7 @@ Specify what it takes to deploy your app. ## Further Reading / Useful Links * [ember.js](https://emberjs.com/) -* [ember-cli](https://ember-cli.com/) +* [ember-cli](https://cli.emberjs.com/release/) * Development Browser Extensions * [ember inspector for chrome](https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi) * [ember inspector for firefox](https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/) diff --git a/app/components/calendar-slot.gjs b/app/components/calendar-slot.gjs index 1bbc9bcf..9d35562b 100644 --- a/app/components/calendar-slot.gjs +++ b/app/components/calendar-slot.gjs @@ -90,11 +90,11 @@ export default class CalendarSlot extends Component { } get hidden() { - return !this.slot?.isNotFull && !this.isCommittedTo; + return !this.isNotFull && !this.isCommittedTo; } get disabled() { - const isNotFull = this.slot?.isNotFull; + const isNotFull = this.isNotFull; const start = this.slot?.start; const toggleIsRunning = this.toggle.isRunning; @@ -118,6 +118,13 @@ export default class CalendarSlot extends Component { return `${dividend}/${divisor}`; } + get isNotFull() { + const count = this.slot?.count ?? 0; + const commitmentCount = this.slot?.commitments?.length ?? 0; + + return count === 0 || commitmentCount < count; + } + toggle = task({ drop: true }, async (event) => { event.preventDefault(); @@ -142,7 +149,7 @@ export default class CalendarSlot extends Component { const errorDetail = error?.errors?.[0]?.detail; this.args.setError(errorDetail || 'Couldn’t save your change'); } - } else if (this.slot?.isNotFull) { + } else if (this.isNotFull) { const newRecord = this.store.createRecord('commitment', { slot: this.slot, person: this.person, diff --git a/app/components/ride-form.gjs b/app/components/ride-form.gjs index 3cc965e4..4b205938 100644 --- a/app/components/ride-form.gjs +++ b/app/components/ride-form.gjs @@ -710,12 +710,12 @@ export default class RideForm extends Component { @action handleSubmit(event) { event?.preventDefault?.(); - this.save?.(); + return this.save?.(); } @action handleCancel(event) { event?.preventDefault?.(); - this.cancel?.(); + return this.cancel?.(); } } diff --git a/app/controllers/admin-calendar.js b/app/controllers/admin-calendar.js index f9e21531..d7a30294 100644 --- a/app/controllers/admin-calendar.js +++ b/app/controllers/admin-calendar.js @@ -75,17 +75,18 @@ export default class AdminCalendarController extends CalendarController { @setDiff('activePeople', 'people') remainingPeople; - @mapBy('viewingSlot.commitments', 'person') - viewingSlotPeople; - - @mapBy('viewingSlotPeople', 'id') - viewingSlotPeopleIds; - - @computed('activePeople.[]', 'viewingSlotPeopleIds.[]') + @computed('activePeople.[]', 'viewingSlot.commitments.{[],@each.person}') get uncommittedPeople() { - const alreadyCommittedPeople = this.viewingSlotPeopleIds; - return this.activePeople.reject((person) => - alreadyCommittedPeople.includes(person.id), + const commitments = this.viewingSlot?.commitments || []; + const committedIds = new Set( + Array.from(commitments) + .map((commitment) => commitment.person?.id) + .filter(Boolean), + ); + const activePeople = this.activePeople || []; + + return Array.from(activePeople).filter( + (person) => !committedIds.has(person.id), ); } @@ -105,27 +106,26 @@ export default class AdminCalendarController extends CalendarController { } @action - createCommitment(person) { + async createCommitment(person) { const slot = this.viewingSlot; const commitment = this.store.createRecord('commitment', { slot: this.viewingSlot, person: person, }); - commitment - .save() - .then(() => { - this.set('errorMessage', undefined); - this.toasts.show( - `Committed ${person.get('name')} to drive on ${moment( - slot.get('start'), - ).format('MMMM D')}`, - ); - }) - .catch((error) => { - const errorDetail = get(error, 'errors.firstObject.detail'); - this.set('errorMessage', errorDetail || 'Couldn’t save your change'); - }); + try { + await commitment.save(); + this.set('errorMessage', undefined); + this.notifyPropertyChange('viewingSlot'); + this.toasts.show( + `Committed ${person.get('name')} to drive on ${moment( + slot.get('start'), + ).format('MMMM D')}`, + ); + } catch (error) { + const errorDetail = get(error, 'errors.firstObject.detail'); + this.set('errorMessage', errorDetail || 'Couldn’t save your change'); + } } @action @@ -137,6 +137,7 @@ export default class AdminCalendarController extends CalendarController { .destroyRecord() .then(() => { this.set('errorMessage', undefined); + this.notifyPropertyChange('viewingSlot'); this.toasts.show(`Deleted ${name}’s commitment on ${date}`); }) .catch((error) => { diff --git a/app/controllers/drivers.js b/app/controllers/drivers.js index b9660e41..3e19f2ea 100644 --- a/app/controllers/drivers.js +++ b/app/controllers/drivers.js @@ -7,6 +7,9 @@ import BufferedProxy from 'ember-buffered-proxy/proxy'; @classic export default class DriversController extends Controller { + @service('people') + peopleService; + @service store; @@ -18,9 +21,15 @@ export default class DriversController extends Controller { sortDir = 'asc'; errorMessage = undefined; - @computed('model.@each.{name,lastRide}', 'sortProp', 'sortDir') + @computed( + 'model', + 'model.[]', + 'model.@each.{name,lastRide}', + 'sortProp', + 'sortDir', + ) get sortedPeople() { - const people = this.model ? this.model.toArray() : []; + const people = Array.from(this.model || []); const sorted = people.slice().sort((a, b) => { let comparison; @@ -82,7 +91,7 @@ export default class DriversController extends Controller { this.set( 'editingPerson', BufferedProxy.create({ - content: this.store.createRecord('person'), + content: this.store.createRecord('person', { active: true }), }), ); } @@ -95,24 +104,24 @@ export default class DriversController extends Controller { } @action - savePerson(event) { + async savePerson(event) { event?.preventDefault(); const proxy = this.editingPerson; proxy.applyBufferedChanges(); - return proxy - .get('content') - .save() - .then(() => { - this.set('editingPerson', undefined); - this.set('errorMessage', undefined); - }) - .catch(() => { - this.set( - 'errorMessage', - 'There was an error saving this driver. Please try again.', - ); - }); + + try { + await proxy.get('content').save(); + await this.peopleService.load(); + + this.set('editingPerson', undefined); + this.set('errorMessage', undefined); + } catch { + this.set( + 'errorMessage', + 'There was an error saving this driver. Please try again.', + ); + } } @action diff --git a/app/controllers/reimbursements.js b/app/controllers/reimbursements.js index 42652d4d..1b47d965 100644 --- a/app/controllers/reimbursements.js +++ b/app/controllers/reimbursements.js @@ -10,9 +10,11 @@ import moment from 'moment-timezone'; @classic export default class ReimbursementsController extends Controller { - queryParams = { - showProcessed: 'processed', - }; + queryParams = [ + { + showProcessed: 'processed', + }, + ]; @alias('model') reimbursements; diff --git a/app/controllers/reports/new.js b/app/controllers/reports/new.js index dcbc1088..88ce4e4a 100644 --- a/app/controllers/reports/new.js +++ b/app/controllers/reports/new.js @@ -1,5 +1,5 @@ import classic from 'ember-classic-decorator'; -import { action } from '@ember/object'; +import { action, computed } from '@ember/object'; import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; @@ -19,6 +19,12 @@ export default class NewController extends Controller { editingRide; errorMessage = undefined; + @computed('model', 'model.{[],@each.complete}') + get reportableRides() { + const rides = Array.from(this.model || []); + return rides.filter((ride) => !ride.complete); + } + _setNumberProperty(property, event) { const ride = this.editingRide; @@ -83,28 +89,27 @@ export default class NewController extends Controller { } @action - submitReport(event) { + async submitReport(event) { event?.preventDefault?.(); let editingRide = this.editingRide; if (editingRide) { - return editingRide.save().then( - () => { - this.set('errorMessage', undefined); - this.toasts.show('Your report was saved'); - - // Remove the ride from the store before reloading from the server - this.store.unloadRecord(this.editingRide); - - this.set('editingRide', undefined); - this.router.transitionTo('application'); - window.scrollTo(0, 0); - }, - () => { - this.set('errorMessage', 'There was an error saving your report!'); - }, - ); + try { + await editingRide.save(); + editingRide.set('complete', true); + + this.set('errorMessage', undefined); + this.toasts.show('Your report was saved'); + + await this.store.findAll('ride', { reload: true }); + + this.set('editingRide', undefined); + this.router.transitionTo('application'); + window.scrollTo(0, 0); + } catch { + this.set('errorMessage', 'There was an error saving your report!'); + } } else { this.set('errorMessage', 'Please choose a ride'); } diff --git a/app/controllers/rides.js b/app/controllers/rides.js index 3c7ca8db..fed097b1 100644 --- a/app/controllers/rides.js +++ b/app/controllers/rides.js @@ -48,23 +48,26 @@ export default class RidesController extends Controller { showCancelled = this.showCancelled; const search = this.search; - let rides = this.model.rejectBy('isCombined').rejectBy('isNew'); + const rides = Array.from(this.model || []); + let filtered = rides.filter((ride) => !ride.isCombined && !ride.isNew); if (!showCompleted) { - rides = rides.filterBy('complete', false); + filtered = filtered.filter((ride) => !ride.complete); } if (!showCancelled) { - rides = rides.filterBy('enabled'); + filtered = filtered.filter((ride) => ride.enabled); } if (search) { - rides = rides.filter((ride) => ride.matches(search)); + filtered = filtered.filter((ride) => ride.matches(search)); } - rides.setEach('isDivider', false); + filtered.forEach((ride) => { + ride.isDivider = false; + }); - let sorted = rides.sortBy('start'); + let sorted = filtered.slice().sort((a, b) => a.start - b.start); const sortDir = this.sortDir; const now = new Date(); @@ -76,13 +79,13 @@ export default class RidesController extends Controller { const firstAfterNow = sorted.find((ride) => ride.start > now); if (firstAfterNow) { - firstAfterNow.set('isDivider', true); + firstAfterNow.isDivider = true; } } else { const firstBeforeNow = sorted.find((ride) => ride.start < now); if (firstBeforeNow) { - firstBeforeNow.set('isDivider', true); + firstBeforeNow.isDivider = true; } } @@ -104,21 +107,21 @@ export default class RidesController extends Controller { } @action - submitRide(proxy) { + async submitRide(proxy) { let buffer = proxy.buffer; proxy.applyBufferedChanges(); - return proxy.content - .save() - .then(() => { - this.editingRide = undefined; - this.rideErrorMessage = undefined; - return this.overlapsService.fetch(); - }) - .catch(() => { - this.rideErrorMessage = 'There was an error saving this ride'; - proxy.setProperties(buffer); - }); + try { + await proxy.content.save(); + + this.editingRide = undefined; + this.rideErrorMessage = undefined; + + await this.overlapsService.fetch(); + } catch { + this.rideErrorMessage = 'There was an error saving this ride'; + proxy.setProperties(buffer); + } } @action diff --git a/app/index.html b/app/index.html index 154cb409..a43b08da 100644 --- a/app/index.html +++ b/app/index.html @@ -2,7 +2,7 @@
- +