added 260907 version of worker toolkit

This commit is contained in:
2026-09-08 20:30:51 -04:00
parent cbd3f0f8ca
commit 97aca37663
1283 changed files with 142951 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
import Ember from 'ember';
import moment from 'moment';
import CheckinByDate from 'flaredown/mixins/checkin-by-date';
const {
get,
computed,
Component,
} = Ember;
export default Component.extend(CheckinByDate, {
classNames: ['checkin-head-nav'],
classNameBindings: ['big'],
pickadateOptions: {
container: 'body > .ember-view',
max: moment().toDate(),
},
currentDate: Ember.computed.alias('date'),
checkinDate: computed('currentDate', function() {
return moment(get(this, 'currentDate')).toDate();
}),
monthAndDay: Ember.computed('currentDate', function() {
return moment(this.get('currentDate')).format("MMMM Do");
}),
isToday: Ember.computed('currentDate', function() {
return moment(this.get('currentDate')).isSame(new Date(), 'day');
}),
isntToday: Ember.computed.not('isToday'),
checkinDateLabel: computed('checkinDate', function() {
return moment(get(this, 'checkinDate')).format('MMM D');
}),
actions: {
goToYesterday() {
const yesterday = moment(this.get('currentDate')).subtract(1, 'days');
this.routeToCheckinsForDate(yesterday.format("YYYY-MM-DD"));
},
goToTomorrow() {
if (this.get('isntToday')) {
const tomorrow = moment(this.get('currentDate')).add(1, 'days');
this.routeToCheckinsForDate(tomorrow.format("YYYY-MM-DD"));
}
},
openPicker() {
this.$('.calendar-opener .picker__input').first().pickadate('picker').open();
},
checkinDateChanged(date) {
const checkinDate = moment(date);
this.routeToCheckinsForDate(checkinDate.format("YYYY-MM-DD"));
},
}
});

View File

@@ -0,0 +1,44 @@
import Ember from 'ember';
const {
get,
set,
Component,
computed: { alias },
} = Ember;
export default Component.extend({
classNames: ['hbi-step'],
checkin: alias('parentView.model.checkin'),
didReceiveAttrs() {
this._super(...arguments);
const checkin = get(this, 'checkin');
const hbi = get(checkin, 'harveyBradshawIndex');
set(this, 'hbi', hbi || get(this, 'store').createRecord('harveyBradshawIndex', { checkin }));
},
actions: {
save() {
get(this, 'hbi')
.save()
.then(() => this.get('chartsVisibilityService').refresh());
},
goBack() {
this.get('onGoBack')();
},
selectOption(field, option) {
set(this, `hbi.${field}`, option.value);
},
completeStep() {
this.get('onStepCompleted')();
},
},
});

View File

@@ -0,0 +1,40 @@
import Ember from 'ember';
const {
Component,
computed: { alias },
inject: { service },
} = Ember;
export default Component.extend({
store: service(),
model: alias('parentView.model'),
checkin: alias('model.checkin'),
findFactors(factorClassName, name) {
const factors = this.get('store').peekAll(factorClassName);
const existingFactor = factors.findBy('name', name);
if (existingFactor) {
this.get('store').unloadRecord(existingFactor);
}
},
actions: {
completeStep() {
this.get('onStepCompleted')();
},
goBack() {
this.get('onGoBack')();
},
onBeforeAddTag(name) {
this.findFactors('tag', name);
},
onBeforeAddFood(name) {
this.findFactors('food', name);
},
},
});

View File

@@ -0,0 +1,53 @@
import Ember from 'ember';
import SearchableDropdown from 'flaredown/mixins/searchable-dropdown';
export default Ember.Component.extend(SearchableDropdown, {
isSelectFocused: false,
selectPlaceholder: Ember.computed('isSelectFocused', function() {
if (this.get('isSelectFocused')) {
return 'Start typing a ' + this.get('modelName');
} else {
return 'Add a ' + this.get('modelName');
}
}),
actions: {
toggleSelectFocused() {
this.toggleProperty('isSelectFocused');
},
handleFocus() {
// TODO remove debug log when this is fixed:
// https://github.com/cibernox/ember-power-select/issues/739
Ember.Logger.debug('focus');
this.set('isSelectFocused', true);
},
handleBlur() {
// TODO remove debug log when this is fixed:
// https://github.com/cibernox/ember-power-select/issues/739
Ember.Logger.debug('blur');
this.set('isSelectFocused', false);
},
createObject(name) {
this.sendAction('onBeforeAdd', name);
this.createAndSave(this.get('modelName'), name).then(obj => this.get('onSelected')(obj));
},
handleChange(obj) {
this.get('onSelected')(obj);
},
clicked(obj) {
this.get('onClicked')(obj);
},
},
performSearch(term, resolve, reject) {
this
.searchByTerm(this.get('modelName'), term)
.then(function() { resolve(...arguments); }, reject);
},
});

View File

@@ -0,0 +1,54 @@
import Ember from 'ember';
const {
get,
set,
computed,
Component,
Logger: { debug },
computed: { alias },
} = Ember;
export default Component.extend({
checkin: alias('parentView.checkin'),
relatedObjects: computed('checkin', function() {
return get(this, 'checkin.' + get(this, 'relationName'));
}),
actions: {
add(obj) {
get(this, 'checkin').addObj(obj, get(this, 'idsKey'), get(this, 'relationName'));
this.saveCheckin();
},
remove(obj) {
get(this, 'checkin').removeObj(obj, get(this, 'idsKey'), get(this, 'relationName'));
this.saveCheckin();
},
onBeforeAdd(name) {
this.sendAction('onBeforeAdd', name);
},
},
saveCheckin() {
if (get(this, 'checkin.hasDirtyAttributes')) {
Ember.run.next(this, function() {
get(this, 'checkin')
.save()
.then(
() => {
debug('Checkin successfully saved');
set(this, 'checkin.hasDirtyAttributes', false);
},
(error) => {
get(this, 'checkin').handleSaveError(error);
}
)
.then(() => get(this, 'chartsVisibilityService').refresh());
});
}
},
});

View File

@@ -0,0 +1,23 @@
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['flaredown-white-box'],
saveScheduled: false,
keyUp() {
this.set('saveScheduled', true);
this.get('checkin').set('hasDirtyAttributes', true);
Ember.run.debounce(this, this.save, 2000);
},
save() {
this.get('checkin').save().then(() => {
this.set('saveScheduled', false);
this.get('checkin').set('hasDirtyAttributes', false);
}, (error) => {
this.get('checkin').handleSaveError(error);
});
}
});

View File

@@ -0,0 +1,69 @@
import Ember from 'ember';
import { translationMacro as t } from "ember-i18n";
const {
get,
set,
computed,
computed: {
alias,
},
inject: {
service,
},
Component
} = Ember;
export default Component.extend({
i18n: service(),
ajax: service(),
classNames: ['checkinPrStep'],
checkin: alias('parentView.model.checkin'),
didReceiveAttrs() {
this._super(...arguments);
const checkin = get(this, 'checkin');
const pr = get(this, 'checkin.promotionRate');
const store = get(this, 'store');
if(store) {
set(this, 'pr', pr || store.createRecord('promotionRate', { checkin }));
}
},
feedBackLabel: t("step.checkin.promotion_rate.feedBackLabel"),
prBody: t("step.checkin.promotion_rate.rateBody"),
prRates: [1,2,3,4,5,6,7,8,9,10],
showRatePage: computed('pr.score', function() {
const score = get(this, 'pr.score');
return typeof score == 'undefined';
}),
actions: {
sendFeedback() {
const pr = get(this, 'pr');
set(pr, 'score', get(this, 'selectedRate'));
pr.save();
set(this, 'showRatePage', false);
},
skipStep() {
let checkin = get(this, 'checkin');
set(checkin, 'promotionSkippedAt', get(checkin, 'date'));
checkin.save();
get(this, 'onStepCompleted')();
},
goBack() {
get(this, 'onGoBack')();
},
}
});

View File

@@ -0,0 +1,20 @@
import Ember from 'ember';
const {
get,
computed,
inject: {
service,
},
Component,
} = Ember;
export default Component.extend({
store: service(),
session: service(),
classNames: ['summary-posts'],
model: computed(function(){
return get(this, 'store').query('post', { summary: true });
}),
});

View File

@@ -0,0 +1,17 @@
import Ember from 'ember';
export default Ember.Component.extend({
model: Ember.computed.alias('parentView.model'),
checkin: Ember.computed.alias('model.checkin'),
actions: {
completeStep() {
this.get('onStepCompleted')();
},
goBack() {
this.get('onGoBack')();
}
}
});

View File

@@ -0,0 +1,288 @@
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();
}
}
});

View File

@@ -0,0 +1,53 @@
import Ember from 'ember';
export default Ember.Component.extend({
classNames: ['tracked-level'],
label: Ember.computed.alias('model.label'),
pips: [
{ value: 0 },
{ value: 1 },
{ value: 2 },
{ value: 3 },
{ value: 4 },
],
onDidInsertElement: Ember.on('didInsertElement', function() {
Ember.run.scheduleOnce('afterRender', this, () => {
Ember.RSVP.resolve(this.get('model')).then(model => {
var value = model.get(this.get('valueKey'));
if (Ember.isPresent(value)) {
this.set('selectedValue', value);
}
});
});
}),
actions: {
titleClicked() {
this.get('onTitleClicked')(this.get('model.id'));
},
setSelectedValue(pipValue) {
this.set('selectedValue', pipValue);
this.get('model').set(this.get('valueKey'), pipValue);
this.get('onValueChanged')();
},
setCurrentValue(pipValue) {
this.set('currentValue', pipValue);
},
unsetCurrentValue() {
this.set('currentValue', null);
},
xClick() {
var model = this.get('model');
this.get('onRemove')(model);
}
}
});

View File

@@ -0,0 +1,86 @@
import Ember from 'ember';
import SearchableDropdown from 'flaredown/mixins/searchable-dropdown';
export default Ember.Component.extend(SearchableDropdown, {
classNames: ['treatment-taker'],
isEditMode: false,
treatment: Ember.computed.alias('model.treatment'),
dose: Ember.computed.alias('model.dose'),
isTaken: Ember.computed.alias('model.isTaken'),
isTakenObserver: Ember.observer('isTaken', function() {
this.get('onIsTakenChanged')();
}),
allTreatmentDoses: Ember.computed('treatment', function() {
return this.customSearch({
resource: 'dose',
query: {
treatment_id: this.get('treatment.id')
}
});
}),
changeDose(newDose) {
this.get('model').set('dose', newDose);
this.set('isEditMode', false);
},
actions: {
titleClicked() {
this.get('onTitleClicked')(this.get('model.id'));
},
removeTreatment() {
this.get('onRemove')(this.get('model'));
},
edit() {
this.set('isEditMode', true);
// TODO: auto-focus the select
},
clearDose() {
this.changeDose(null);
this.get('onDoseChanged')();
},
handleChange(dose) {
this.changeDose(dose);
this.get('onDoseChanged')(dose);
},
createDose(name) {
let dose = this.createRecord('dose', name);
this.changeDose(dose);
this.get('onDoseChanged')(dose);
},
handleFocus() {
// TODO remove debug log when this is fixed:
// https://github.com/cibernox/ember-power-select/issues/739
Ember.Logger.debug('focus');
this.set('isSelectFocused', true);
},
handleBlur() {
// TODO remove debug log when this is fixed:
// https://github.com/cibernox/ember-power-select/issues/739
Ember.Logger.debug('blur');
this.set('isSelectFocused', false);
},
},
performSearch(term, resolve, reject) {
this
.customSearch({
resource: 'dose',
query: {
treatment_id: this.get('treatment.id'),
name: term,
}
})
.then(function() { resolve(...arguments); }, reject);
},
});

View File

@@ -0,0 +1,107 @@
import Ember from 'ember';
let { Component, computed, computed: { alias, notEmpty }, get, set, setProperties } = Ember;
export default Component.extend({
classNames: ['centered'],
weatherTypes: [
'clear-day',
'clear-night',
'cloudy',
'fog',
'partly-cloudy-day',
'partly-cloudy-night',
'rain',
'sleet',
'snow',
'wind',
],
newPostalCode: '',
inputVisible: false,
validPostalCode: true,
checkin: alias('parentView.model.checkin'),
hasWeather: notEmpty('weather'),
pressureUnits: alias('session.currentUser.profile.pressureUnits'),
temperatureUnits: alias('session.currentUser.profile.temperatureUnits'),
weather: alias('checkin.weather'),
formattedPrecipitation: computed('weather.precipIntensity', function() {
return get(this, 'weather.precipIntensity').toString().split('.')[1];
}),
shownTemperatureMin: computed('weather.temperatureMin', 'temperatureUnits', function() {
return get(this, 'weather').temperatureMinByUnits(get(this, 'temperatureUnits'));
}),
shownTemperatureMax: computed('weather.temperatureMax', 'temperatureUnits', function() {
return get(this, 'weather').temperatureMaxByUnits(get(this, 'temperatureUnits'));
}),
shownPressure: computed('weather.pressure', 'pressureUnits', function() {
return get(this, 'weather').pressureByUnits(get(this, 'pressureUnits'));
}),
iconType: computed('weather.icon', function() {
let icon = get(this, 'weather.icon');
return get(this, 'weatherTypes').includes(icon) ? icon : 'default';
}),
willRender() {
this._super(...arguments);
if (!get(this, 'hasWeather')) {
set(this, 'inputVisible', true);
}
},
actions: {
updatePostalCode() {
const date = get(this, 'checkin.date');
const newPostalCode = get(this, 'newPostalCode');
if(get(newPostalCode, 'length') === 0) {
set(this, 'validPostalCode', false);
} else {
this
.store
.queryRecord('weather', { date: date, postal_code: newPostalCode })
.then(record => {
let checkin = get(this, 'checkin');
setProperties(checkin, { postalCode: newPostalCode, weather: record });
if(!record) {
set(this, 'validPostalCode', false);
}
return checkin.save();
})
.then(() => set(this, 'inputVisible', false));
}
},
showInput() {
setProperties(this, { inputVisible: true, newPostalCode: get(this, 'checkin.postalCode') });
},
toggleTemperatureUnits() {
this.updateProfileFieldFromMap('temperatureUnits', { c: 'f', f: 'c' });
},
togglePressureUnits() {
this.updateProfileFieldFromMap('pressureUnits', { mb: 'in', in: 'mb' });
},
},
updateProfileFieldFromMap(field, map) {
get(this, 'session.currentUser.profile').then(profile => {
set(profile, field, map[get(this, field)]);
return profile.save();
});
},
});