Skip to content

Commit

Permalink
Tagging entries with active contest
Browse files Browse the repository at this point in the history
  • Loading branch information
etousley committed Aug 28, 2017
1 parent 15c5115 commit 378c61d
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 44 deletions.
11 changes: 0 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,3 @@ flog
A webapp to track employees' fitness progress for a workplace wellness initiative

(Based on the [hackathon-starter](https://github.com/sahat/hackathon-starter) boilerplate)


<!--
Notes
======
When setting the app up for the first time, you'll have to make sure that you
have a working MongoDB instance with a database named 'flog'. You'll also need
to give the user in the environment's MONGODB_URI (see .env file) root
privileges (via the admin database).
-->
34 changes: 33 additions & 1 deletion controllers/contest.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,43 @@ exports.renderContest = (req, res) => {
};


/**
* Get first contest whose start/end dates bound today's date
*/
getActiveContestName = (logEntry) => {
const logEntryDate = moment(logEntry.date);
let contestName = '';
let contestInfo;
let contestStartDate;
let contestEndDate;
let userTeamInfo = userTeams[logEntry.user];

// Only assign to contest if user is a competitor
if ( !(userTeamInfo && userTeamInfo.isCompetitor) ) {
return '';
}

for ( contestName of Object.keys(contestDefinitions) ) {
contestInfo = contestDefinitions[contestName];
contestStartDate = moment(contestInfo.startDate);
contestEndDate = moment(contestInfo.endDate);
// console.log(logEntryDate, contestStartDate, contestEndDate);

// Return contest if date is between startDate (inclusive) and endDate (NOT inclusive)
// Credit: https://stackoverflow.com/a/29495647
if ( logEntryDate.isBetween(contestStartDate, contestEndDate, 'days', '[)') ) {
console.log(logEntryDate, contestName)
return contestName;
}
}
}


/**
* Aggregate user-level point totals to team-level point totals
* Note: Want to show points = 0 rather than omitting team entirely
* Note: Would have been a lot easier with a relational DB
* Refactor using Javascript map() ???
* Refactor using map() ???
*/
getContestResults = (callback) => {
const contestUserQuery = [
Expand Down
16 changes: 10 additions & 6 deletions controllers/logEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const request = bluebird.promisifyAll(require('request'), { multiArgs: true });
const LogEntry = require('../models/LogEntry');
const lookups = require('../public/js/lookups.js');
const userTeams = require('../controllers/user.js').userTeams;

const contest = require('../controllers/contest.js');

/**
* Return activityDefinitions object
Expand All @@ -18,7 +18,7 @@ exports.getActivityDefinitions = (req, res) => {
*/
calculateActivityPoints = (logEntry) => {
const activityDefinition = lookups.activitiesSortedAlpha[logEntry.activity];
console.log(JSON.stringify(logEntry));
// console.log(JSON.stringify(logEntry));

// Is it the right time unit?
if (logEntry.durationUnit !== activityDefinition.durationUnit) {
Expand All @@ -27,7 +27,7 @@ calculateActivityPoints = (logEntry) => {

const completedTimeChunks = Math.floor(logEntry.durationValue / activityDefinition.durationValue);
const points = completedTimeChunks * activityDefinition.points;
console.log("calculated points: " + points);
// console.log("calculated points: " + points);

return points;
};
Expand Down Expand Up @@ -75,8 +75,10 @@ exports.getLogEntries = (req, res) => {

LogEntry.find(filter, function(err, logEntries) {
if (err) {
console.log(err);
res.status(500).send({"error": err})
} else {
// console.log(logEntries;)
res.send({ "data": logEntries });
}
});
Expand All @@ -96,7 +98,7 @@ exports.getLogEntry = (req, res) => {
res.statusMessage = err.toString();
res.status(500).end();
} else {
console.log(logEntry);
// console.log(logEntry);
res.send({ "data": logEntry });
}
});
Expand All @@ -120,8 +122,9 @@ exports.createLogEntry = (req, res) => {
}

entry.points = calculateActivityPoints(entry);
entry.contest = getActiveContestName(entry);

// Note: Mongoose bug: Model.create() and instance.save() never execute callback
// Note: Mongoose bug: Model.create() and instance.save() don't execute callback?
// Workaround: Use $__save()
// https://github.com/Automattic/mongoose/issues/4064
entry.$__save({}, function(err, createdEntry) {
Expand All @@ -130,7 +133,7 @@ exports.createLogEntry = (req, res) => {
res.statusMessage = err.toString();
res.status(500).end();
} else {
console.log('created:' + JSON.stringify(createdEntry));
console.log('created: ' + JSON.stringify(createdEntry));
res.send({ "data": createdEntry });
}
});
Expand Down Expand Up @@ -158,6 +161,7 @@ exports.updateLogEntry = (req, res) => {
}

entryData.points = calculateActivityPoints(entryData);
entryData.contest = getActiveContestName(entryData);
if (teamInfo.isCompetitor) {
entryData.team = teamInfo.team;
}
Expand Down
2 changes: 1 addition & 1 deletion public/css/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,11 @@ textarea {
padding-top: 2em;
resize: both;
overflow: auto;
cursor: pointer;
}

// Hack to treat .fc-day:hover and .fc-day-top:hover the same way with JS
.fc-day.active {
cursor: pointer;
background-color: #d9edf7 !important;
}

Expand Down
48 changes: 33 additions & 15 deletions public/js/log.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@


// Global variables used by multiple functions
const CSRF_HEADER = 'X-CSRF-Token';

let modal = $('#log-entry-modal');
Expand All @@ -9,7 +6,8 @@ let entryTitleField = $('#entry-title-field');
let entryActivityField = $('#entry-activity-field');
let entryDescriptionField = $('#entry-description-field');
let entryDurationValueField = $('#entry-duration-value-field');
let entryDurationUnitField = $('#entry-duration-unit');
let entryDurationUnitField = $('#entry-duration-unit-field');
let entryContestField = $('#entry-contest-field');
let entryErrorField = $('#entry-error-field');
let entryInfoField = $('#entry-info-field');
let entryPointsField = $('#entry-points-field');
Expand Down Expand Up @@ -47,7 +45,7 @@ fillLogEntries = () => {
const startDate = dayElems[0].dataset.date;
const endDate = dayElems[dayElems.length - 1].dataset.date;
const getLogEntriesUrl = '/api/log?' + jQuery.param({
"email": userEmail,
"user": userEmail,
"from": startDate,
"to": endDate
});
Expand All @@ -63,26 +61,37 @@ fillLogEntries = () => {
* Render and display modal to reflect data (date, user, existing activities)
*/
drawLogEntryModal = (clickedDayElem) => {
// console.log('in drawLogEntryModal, activeEntryElem: ');
// console.log( activeEntryElem);

const activityName = activeEntryElem.dataset.activity;
let targetActivity = undefined;
let logOwner = activeEntryElem.dataset.user;

// If non-owner clicks an empty date, nothing should happen
// if (userEmail !== logOwner && !activityName) {
// return;
// }

entryDateField.html( activeEntryElem.dataset.date.slice(0, 10) );
entryTitleField.val(activeEntryElem.dataset.title);
entryDescriptionField.val(activeEntryElem.dataset.description);
entryDurationValueField.val(activeEntryElem.dataset.durationValue);
entryContestField.val(activeEntryElem.dataset.contest);
// console.log(activeEntryElem.dataset);

updateModalPoints(activeEntryElem.dataset);

if (activityName !== undefined) {
if (activeEntryElem.dataset.activity) {
// Activity has already been saved
entryActivityField.val(activeEntryElem.dataset.activity);
entryActivityField.text(activeEntryElem.dataset.activity);
} else if (logOwner && userEmail === logOwner) {
// Log owner is saving activity for the first time
targetActivity = $(".dropdown-item:contains('" + activityName + "')")[0];
entryDurationUnitField.val(targetActivity.dataset.durationUnit) + 's';
updateElemDataset(entryActivityField, targetActivity.dataset);
entryActivityField.val(targetActivity);
entryActivityField.text(activityName);
} else {
// Activity hasn't been chosen yet
entryActivityField.val(undefined);
entryActivityField.text("Choose an activity...");
}
Expand All @@ -109,9 +118,8 @@ drawLogEntryModal = (clickedDayElem) => {
"activity": entryActivityField.html(),
"description": entryDescriptionField.val(),
"durationValue": entryDurationValueField.val(),
"durationUnit": entryActivityField.dataset.durationUnit,
"category": entryActivityField.dataset.category,
"points": parseInt(activeEntryElem.dataset.points)
"durationUnit": entryDurationUnitField.text(),
// "category": entryActivityField.dataset.category
}

if (activeEntryElem.dataset._id !== undefined) {
Expand All @@ -122,9 +130,12 @@ drawLogEntryModal = (clickedDayElem) => {
data: {"data": entryData},
success: function(data) {
let updatedEntry = data.data;
console.log(updatedEntry);
updateElemDataset(activeEntryElem, updatedEntry);
updateEntryTitle(activeEntryElem.dataset);
updateModalPoints(updatedEntry);
entryContestField.val(updatedEntry.contest);

entryInfoField.text('Updated entry');
entryInfoField.show();
console.log('Updated entry: ' + JSON.stringify(updatedEntry));
Expand All @@ -146,6 +157,7 @@ drawLogEntryModal = (clickedDayElem) => {
entryDeleteButton.prop("disabled", false);
entryInfoField.text('Added new entry');
entryInfoField.show();
entryContestField.val(updatedEntry.contest);
console.log('Created entry: ' + JSON.stringify(createdEntry));
},
error: function(error) {
Expand All @@ -166,6 +178,7 @@ drawLogEntryModal = (clickedDayElem) => {
type: 'DELETE',
success: function(data) {
activeEntryElem.remove();
entrySaveButton.prop("disabled", true);
entryDeleteButton.prop("disabled", true);
entryInfoField.text('Deleted entry');
entryInfoField.show();
Expand Down Expand Up @@ -195,6 +208,8 @@ addEntryElem = (entryData) => {
updateEntryTitle(entryData);
updateModalPoints(entryData);
updateRowHeight(entryDate);

entryContestField.val(entryData.contest);
};


Expand All @@ -213,7 +228,6 @@ updateRowHeight = (entryDate) => {
totalHeight += elem.clientHeight;
}

// console.log(entryDate + ' | ' + thisRowHeightPx + ' | ' + minRowHeightPx + ' | ' + (totalHeight + 100));
if (thisRowHeightPx < (totalHeight + 100)) {
thisRow.style.height = Math.max( minRowHeightPx, (totalHeight + 100) ) + 'px';
} // else { shrink row -- probably unecessary }
Expand All @@ -225,8 +239,6 @@ updateRowHeight = (entryDate) => {
*/
updateEntryTitle = (entryData) => {
if (entryData.title && entryData.title.length > 0) {
// console.log('in updateEntryTitle, activeEntryElem: ');
// console.log( activeEntryElem);
activeEntryElem.textContent = entryData.title;
} else {
activeEntryElem.textContent = entryData.activity + " (" + entryData.points + " pts)";
Expand Down Expand Up @@ -343,8 +355,14 @@ $(document).ready(function() {
if (event.target.classList.contains('fc-day') || event.target.classList.contains('fc-day-top')) {
const entryDate = event.target.dataset.date;
const dayElem = document.querySelectorAll(`.fc-day[data-date='${entryDate}']`)[0];

// If user is also log owner, indicate that day elems are clickable
// Need to access current user
// const logOwner = event.target.dataset.user;
// if (logOwner === userEmail) {
$('.fc-day.active').removeClass('active');
dayElem.className += ' active';
// }
}
});
// Mouseout doesn't look great because of html inside cell (see: .fc-content-skeleton)
Expand Down
26 changes: 16 additions & 10 deletions views/log/index.pug
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ block content
span.input-group-addon#entry-duration-unit-field minutes
span.top-buffer-md
textarea.form-control#entry-description-field(placeholder="Description (optional)")
.input-group.top-buffer-md
span.input-group-addon Contest
input.form-control#entry-contest-field(type="text" disabled="True" placeholder="(none)")
.alert-container
.alert.alert-danger#entry-error-field(role="alert")
.alert.alert-info#entry-info-field(role="alert")
Expand All @@ -58,16 +61,19 @@ block content
//- Read-only modal for non-owners
else
.modal-body
.input-group.top-buffer-md-sm
span.input-group-addon.entry-date {{ date }}
.form-control.entry-title(type="text" placeholder="Title of log entry (optional)")
.input-group.top-buffer-md-sm
.entry-activity(placeholder="Activity type...")
.input-group.top-buffer-md-sm
.entry-duration-value(type="text" placeholder="Minutes (e.g. 20)")
.input-group-addon minutes
span.top-buffer-md-sm
.entry-description(placeholder="Description (optional)")
button.btn.btn-default#entry-activity-field(type="button" disabled="True")
.input-group.top-buffer-md
input.form-control#entry-duration-value-field(type="text" placeholder="Minutes (e.g. 20)" disabled="True")
span.input-group-addon#entry-duration-unit-field minutes
span.top-buffer-md
textarea.form-control#entry-description-field(placeholder="Description (optional)" disabled="True")
.input-group.top-buffer-md
span.input-group-addon Contest
input.form-control#entry-contest-field(type="text" disabled="True" placeholder="(none)")
.alert-container
.alert.alert-danger#entry-error-field(role="alert")
.alert.alert-info#entry-info-field(role="alert")
.alert.alert-success#entry-points-field(role="alert")
.modal-footer
button.btn.btn-secondary(type="button", data-dismiss="modal") Close

Expand Down

0 comments on commit 378c61d

Please sign in to comment.