289 lines
8.3 KiB
JavaScript
289 lines
8.3 KiB
JavaScript
import Ember from 'ember';
|
|
import DS from 'ember-data';
|
|
import moment from 'moment';
|
|
import TrackablesFromType from 'flaredown/mixins/trackables-from-type';
|
|
|
|
const {
|
|
get,
|
|
set,
|
|
computed,
|
|
computed: { alias },
|
|
inject: { service },
|
|
Component,
|
|
isNone,
|
|
run: { next },
|
|
RSVP: { all }
|
|
} = Ember;
|
|
|
|
export default Component.extend(TrackablesFromType, {
|
|
classNames: ['trackables-step'],
|
|
|
|
model: alias('parentView.model'),
|
|
|
|
tracking: service(),
|
|
|
|
setupTracking() {
|
|
this.get('tracking').setup({
|
|
at: new Date(this.get('checkin.date')),
|
|
trackableType: this.get('trackableType').capitalize()
|
|
});
|
|
},
|
|
|
|
checkin: alias('model.checkin'),
|
|
isTodaysCheckin: computed('checkin', function() {
|
|
return moment(this.get('checkin.date')).isSame(new Date(), 'day');
|
|
}),
|
|
|
|
sortedTrackeds: computed('trackeds.{[],@each.position}', function() {
|
|
return this.get('trackeds').toArray().sortBy('position');
|
|
}),
|
|
|
|
didReceiveAttrs() {
|
|
this._super(...arguments);
|
|
|
|
this.setupTracking();
|
|
},
|
|
|
|
removedTracked: null,
|
|
addedTracked: null,
|
|
|
|
actions: {
|
|
remove(tracked) {
|
|
tracked.prepareForDestroy();
|
|
this.set('removedTracked', tracked);
|
|
this.saveCheckin();
|
|
},
|
|
|
|
add(selectedTrackable) {
|
|
const trackeds = get(this, 'trackeds');
|
|
|
|
if (get(trackeds, 'isFulfilled')) {
|
|
this.addTrackable(get(trackeds, 'content'), selectedTrackable);
|
|
} else {
|
|
this.addTrackable(trackeds, selectedTrackable);
|
|
}
|
|
},
|
|
|
|
move(draggedId, droppedId) {
|
|
// Return if the item has been dropped onto its own dropzone
|
|
if (Ember.isEqual(draggedId, droppedId)) { return; }
|
|
let sortedTrackeds = this.get('sortedTrackeds');
|
|
let draggedItem = sortedTrackeds.findBy('id', draggedId);
|
|
// draggedItem might not be found if it's dragged from a different
|
|
// trackables' collection (this might happen in summary screen)
|
|
if (Ember.isNone(draggedItem)) { return; }
|
|
let draggedPosition = draggedItem.get('position');
|
|
let droppedPosition = 0;
|
|
if (Ember.isEqual(droppedId, 'after-last')) {
|
|
droppedPosition = sortedTrackeds.get('length');
|
|
} else {
|
|
let droppedItem = sortedTrackeds.findBy('id', droppedId);
|
|
droppedPosition = droppedItem.get('position');
|
|
}
|
|
// Return if the item has been dropped onto its next item's dropzone
|
|
if (Ember.isEqual(droppedPosition, draggedPosition + 1)) { return; }
|
|
if (draggedPosition > droppedPosition) {
|
|
// Case 1: Item Moved Up
|
|
// Shift down items between droppedPosition and draggedPosition
|
|
for (let i = droppedPosition; i < draggedPosition; i++) {
|
|
sortedTrackeds[i].set('position', i + 1);
|
|
}
|
|
draggedItem.set('position', droppedPosition);
|
|
} else {
|
|
// Case 2: Move Down
|
|
// Shift up items between draggedPosition and droppedPosition
|
|
for (let i = draggedPosition + 1; i < droppedPosition; i++) {
|
|
sortedTrackeds[i].set('position', i - 1);
|
|
}
|
|
draggedItem.set('position', droppedPosition - 1);
|
|
}
|
|
this.set('clickedTrackableId', null);
|
|
this.saveCheckin();
|
|
},
|
|
|
|
onTrackableClicked(trackableId) {
|
|
if (Ember.isPresent(this.get('clickedTrackableId'))) {
|
|
if (Ember.isEqual(trackableId, this.get('clickedTrackableId'))) {
|
|
this.set('clickedTrackableId', null);
|
|
} else {
|
|
this.set('clickedTrackableId', trackableId);
|
|
}
|
|
} else {
|
|
this.set('clickedTrackableId', trackableId);
|
|
}
|
|
},
|
|
|
|
saveChanges() {
|
|
this.saveCheckin();
|
|
},
|
|
|
|
completeStep() {
|
|
this.get('onStepCompleted')();
|
|
},
|
|
goBack() {
|
|
this.get('onGoBack')();
|
|
}
|
|
},
|
|
|
|
addTrackable(trackeds, trackable) {
|
|
const trackableType = get(this, 'trackableType');
|
|
const foundTrackable = trackeds.findBy(`${trackableType}.id`, trackable.get('id'));
|
|
|
|
if (isNone(foundTrackable)) {
|
|
const recordAttrs = {
|
|
colorId: this.computeNewColorId(),
|
|
position: trackeds.get('length'),
|
|
[trackableType]: trackable
|
|
};
|
|
const recordType = `checkin_${trackableType}`.camelize();
|
|
const tracked = this.store.createRecord(recordType, recordAttrs);
|
|
|
|
trackeds.pushObject(tracked);
|
|
|
|
set(this, 'addedTracked', tracked);
|
|
this.saveCheckin();
|
|
}
|
|
|
|
set(this, 'selectedTrackable', null);
|
|
},
|
|
|
|
computeNewColorId() {
|
|
var nColors = 32;
|
|
let usedColorIds = this.get('checkin.allColorIds');
|
|
// if all colors have been used
|
|
if (usedColorIds.length >= nColors) {
|
|
// re-use them again in the same order
|
|
return usedColorIds[(usedColorIds.length % nColors)];
|
|
} else {
|
|
// return a random unused color
|
|
let randomColor = '';
|
|
do {
|
|
randomColor = Math.floor(Math.random()*nColors)+'';
|
|
} while (usedColorIds.includes(randomColor));
|
|
return randomColor;
|
|
}
|
|
},
|
|
|
|
saveCheckin() {
|
|
set(this, 'checkin.hasDirtyAttributes', true);
|
|
next(this, () => {
|
|
set(this, 'isSaving', true);
|
|
|
|
all([
|
|
this.untrackRemovedTracked(),
|
|
this.trackAddedTracked()
|
|
])
|
|
.then(() => this.get('checkin').save())
|
|
.then(
|
|
() => {
|
|
Ember.Logger.info('Checkin successfully saved');
|
|
|
|
return this.onCheckinSaved();
|
|
},
|
|
(error) => {
|
|
this.get('checkin').handleSaveError(error);
|
|
}
|
|
)
|
|
.then(() => this.get('chartsVisibilityService').refresh())
|
|
.finally(() => {
|
|
set(this, 'isSaving', false);
|
|
});
|
|
});
|
|
},
|
|
|
|
onCheckinSaved() {
|
|
const checkin = this.get('checkin');
|
|
|
|
this.deleteAddedTracked();
|
|
checkin.set('hasDirtyAttributes', false);
|
|
checkin.deleteTrackablesPreparedForDestroy();
|
|
this.set('addedTracked', null);
|
|
this.set('removedTracked', null);
|
|
},
|
|
|
|
untrackRemovedTracked() {
|
|
// untrack() removedTracked if isTodaysCheckin
|
|
return new Ember.RSVP.Promise(resolve => {
|
|
let removedTracked = this.get('removedTracked');
|
|
if (this.get('isTodaysCheckin') && Ember.isPresent(removedTracked)) {
|
|
let rawTrackable = removedTracked.get(this.get('trackableType'));
|
|
if (DS.PromiseObject.detectInstance(rawTrackable)) {
|
|
let trackablePromise = rawTrackable;
|
|
if (trackablePromise.get('isFulfilled')) {
|
|
this.untrack(trackablePromise.get('content'), resolve);
|
|
} else {
|
|
trackablePromise.then(trackable => {
|
|
this.untrack(trackable, resolve);
|
|
});
|
|
}
|
|
} else {
|
|
Ember.Logger.debug('rawTrackable is not a DS.PromiseObject');
|
|
if (Ember.isPresent(rawTrackable)) {
|
|
this.untrack(rawTrackable, resolve);
|
|
}
|
|
}
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
},
|
|
|
|
untrack(trackable, resolve) {
|
|
this.get('tracking').untrack(
|
|
{
|
|
trackable: trackable,
|
|
trackableType: this.get('trackableType').capitalize()
|
|
},
|
|
resolve
|
|
);
|
|
},
|
|
|
|
trackAddedTracked() {
|
|
// track() addedTracked if isTodaysCheckin
|
|
return new Ember.RSVP.Promise(resolve => {
|
|
var addedTracked = this.get('addedTracked');
|
|
if (this.get('isTodaysCheckin') && Ember.isPresent(addedTracked)) {
|
|
let rawTrackable = addedTracked.get(this.get('trackableType'));
|
|
if (DS.PromiseObject.detectInstance(rawTrackable)) {
|
|
let trackablePromise = rawTrackable;
|
|
if (trackablePromise.get('isFulfilled')) {
|
|
this.get('tracking').track(
|
|
trackablePromise.get('content'),
|
|
addedTracked.get('colorId'),
|
|
resolve
|
|
);
|
|
} else {
|
|
trackablePromise.then(trackable => {
|
|
this.get('tracking').track(
|
|
trackable,
|
|
addedTracked.get('colorId'),
|
|
resolve
|
|
);
|
|
});
|
|
}
|
|
} else {
|
|
Ember.Logger.debug('rawTrackable is not a DS.PromiseObject');
|
|
if (Ember.isPresent(rawTrackable)) {
|
|
this.get('tracking').track(
|
|
rawTrackable,
|
|
addedTracked.get('colorId'),
|
|
resolve
|
|
);
|
|
}
|
|
}
|
|
} else {
|
|
resolve();
|
|
}
|
|
});
|
|
},
|
|
|
|
deleteAddedTracked() {
|
|
// remove addedTracked to avoid 'ghost' record
|
|
let addedTracked = this.get('addedTracked');
|
|
if (Ember.isPresent(addedTracked)) {
|
|
addedTracked.deleteRecord();
|
|
}
|
|
}
|
|
|
|
});
|