Skip to content

Commit

Permalink
Can populate (most of) a log entry
Browse files Browse the repository at this point in the history
  • Loading branch information
etousley committed Aug 7, 2017
1 parent 59475d3 commit 7dd6a16
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 125 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
flog
=======================

A simple webapp to track employees' fitness progress for a workplace wellness initiative
A webapp to track employees' fitness progress for a workplace wellness initiative

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


Notes
Expand Down
48 changes: 27 additions & 21 deletions config/passport.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,30 +46,36 @@ passport.use(new GoogleStrategy({
}
});
} else {
User.findOne({ google: profile.id }, (err, existingUser) => {
if (err) { return done(err); }
if (existingUser) {
return done(null, existingUser);
}
User.findOne({ email: profile.emails[0].value }, (err, existingEmailUser) => {
if ( profile.emails[0].value.endsWith('readingplus.com') ) {
User.findOne({ google: profile.id }, (err, existingUser) => {
if (err) { return done(err); }
if (existingEmailUser) {
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' });
done(err);
} else {
const user = new User();
user.email = profile.emails[0].value;
user.google = profile.id;
user.tokens.push({ kind: 'google', accessToken });
user.profile.name = profile.displayName;
user.profile.gender = profile._json.gender;
user.profile.picture = profile._json.image.url;
user.save((err) => {
done(err, user);
});
if (existingUser) {
return done(null, existingUser);
}
User.findOne({ email: profile.emails[0].value }, (err, existingEmailUser) => {
if (err) { return done(err); }
if (existingEmailUser) {
req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with Google manually from Account Settings.' });
done(err);
} else {
const user = new User();
user.email = profile.emails[0].value;
user.google = profile.id;
user.tokens.push({ kind: 'google', accessToken });
user.profile.name = profile.displayName;
user.profile.gender = profile._json.gender;
user.profile.picture = profile._json.image.url;
user.save((err) => {
done(err, user);
});
}
});
});
});
} else {
const msg = 'Access Denied. Please log in using a readingplus.com email address.';
req.flash('errors', { msg: msg });
done(new Error(msg));
}
}
}));

Expand Down
32 changes: 22 additions & 10 deletions controllers/logEntry.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ const moment = require('moment');
const LogEntry = require('../models/LogEntry');
const lookups = require('../public/js/lookups.js');


/**
* Return activityDefinitions object
*/
exports.getActivityDefinitions = (req, res) => {
res.send(lookups.activityDefinitions);
};


/**
* GET /log
* Render logEntry log template
Expand All @@ -21,35 +23,41 @@ exports.getLog = (req, res) => {
});
};


/**
* REST API endpoint
* GET all log entries. Can filter with query string, e.g.:
* /api/[email protected]&from=2017-05-01&to=2017-06-01
*/
exports.getLogEntries = (req, res) => {
const user = req.query.user; // user's email address
let user = req.query.user; // user's email address
let from = req.query.from;
let to = req.query.to;
let filter = {};

if (user !== undefined) {
filter.user = user;
user = user.replace('%40', '@'); // '@' symbol gets escaped sometimes
filter["user"] = user;
}
if (from !== undefined) {
from = moment(from, 'YYYY-MM-DD').toArray();
filter.from = { $gte: new Date(from[0], from[1], from[2]) };
}
if (to !== undefined) {
to = moment(to, 'YYYY-MM-DD').toArray();
filter.to = { $lte: new Date(to[0], to[1], to[2]) };

if (from !== undefined || to !== undefined) {
filter["date"] = {};
if (from !== undefined) {
filter["date"]["$gte"] = from;
}
if (to !== undefined) {
filter["date"]["$lte"] = to;
}
}
console.log(JSON.stringify(filter));

console.log('filter: ' + JSON.stringify(filter));

LogEntry.find(filter, function(err, activities) {
res.send({ data: activities });
});
};


/**
* REST API endpoint
* GET one log entry by ID
Expand All @@ -62,6 +70,7 @@ exports.getLogEntry = (req, res) => {
});
};


/**
* REST API endpoint
* Update log entry by ID
Expand All @@ -76,6 +85,7 @@ exports.updateLogEntry = (req, res) => {
});
};


/**
* REST API endpoint
* Create new log entry
Expand All @@ -89,6 +99,7 @@ exports.createLogEntry = (req, res) => {
});
};


/**
* REST API endpoint
* Delete log entry by id
Expand All @@ -102,6 +113,7 @@ exports.deleteLogEntry = (req, res) => {
});
};


/**
* Calculate points based on activity and duration
*/
Expand Down
11 changes: 4 additions & 7 deletions models/LogEntry.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
const mongoose = require('mongoose');
const logEntry = require('../controllers/logEntry');

// Embedded child object of logEntrySchema
const logEntryDurationSchema = new mongoose.Schema({
unit: { type: String, required: true }, // e.g. "minutes"
value: { type: Number, required: true } // e.g. 20
})

const logEntrySchema = new mongoose.Schema({
user: { type: String, required: true }, // User's email address
date: { type: Date, required: true },
activity: { type: String, required: true },
category: { type: String, required: true },
duration: { type: logEntryDurationSchema, required: true },
durationUnit: { type: String, required: true },
durationValue: { type: Number, required: true },
points: Number,

title: String,
Expand All @@ -37,6 +33,7 @@ logEntrySchema.pre('update', function(next) {
this.points = logEntry.calculateActivityPoints(this);
})

const LogEntry = mongoose.model('LogEntry', logEntrySchema);
// Model is named 'LogEntry', but collection name is 'logEntries'
const LogEntry = mongoose.model('LogEntry', logEntrySchema, 'logEntries');

module.exports = LogEntry;
1 change: 1 addition & 0 deletions models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ userSchema.methods.gravatar = function gravatar(size) {
return `https://gravatar.com/avatar/${md5}?s=${size}&d=retro`;
};

// Model is named 'User', but collection name is 'users'
const User = mongoose.model('User', userSchema);

module.exports = User;
17 changes: 17 additions & 0 deletions public/css/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ footer {
}
}

.vert-spaced-sm {
margin-top: 3px;
margin-bottom: 3px;
}

// Input Fields
// -------------------------

Expand Down Expand Up @@ -78,3 +83,15 @@ textarea {
.fc-day.active {
background-color: #e6fff7;
}

// Log entry modal styles
// -------------------------
.modal-title {
max-width: 90%;
float: left;
}

.modal-header > .close {
max-width: 10%;
float: rightt;
}
94 changes: 66 additions & 28 deletions public/js/log.js
Original file line number Diff line number Diff line change
@@ -1,65 +1,103 @@

// TODO: get log data
// TODO: map log data to DOM calendar
// TODO: view existing log entry
// TODO: create/edit log entry (in modal?)
// TODO: save log entry


/**
* Get log data from REST API
* E.g. queryParams = {'user': 'me@domain.com', 'from': '2017-08-01', 'to': '2017-09-01'};
* Get log data from REST API, then draw them to calendar
*/
getLogEntries = (queryParams) => {
let url = '/api/log?' + jQuery.param(queryParams);
console.log(url);
fillLogEntries = () => {
const dayElems = $('.fc-day');
const email = window.location.href.split("/").slice(-1)[0];
const startDate = dayElems[0].dataset.date;
const endDate = dayElems[dayElems.length - 1].dataset.date;
const ignoreDataKeys = {'_id': 1, 'user': 1}
let entryTitle = undefined;
let entryElem = undefined;

// Build query parameters for GET request
const url = '/api/log?' + jQuery.param({
"email": email,
"from": startDate,
"to": endDate
});

// Map data to calendar days
// Could probably do this more efficiently...
$.get(url, function(data) {
return data;
for (entry of data.data) {
for (dayElem of dayElems) {
if (entry.date.slice(0, 10) === dayElem.dataset.date) {
entryTitle = entry.title || (entry.activity + " (" + entry.durationValue + " " + entry.durationUnit + "s)");
entryElem = document.createElement("div");
entryElem.className = "log-entry-title";
entryElem.textContent = entryTitle;

// Add data as data-foo attributes to the event element (probably a better way...)
for (let [key, value] of Object.entries(entry)) {
if ( !(key in ignoreDataKeys) ) {
entryElem.dataset[key] = value;
}
}

dayElem.appendChild(entryElem);
break;
}
}
}
})
}


/**
* Render and display modal to reflect data (date, user, existing activities)
*/
drawLogEntryModal = (email, date) => {
const modal = $('#log-entry-modal');
const modalTitle = modal.find('.modal-title');
const modalBody = modal.find('.modal-body');

const testQueryParams = {
'user': '[email protected]',
'from': '2017-08-01',
'to': '2017-09-01'
};
const testData = getLogEntries(testQueryParams);
console.log(testData);

modalTitle.html('Log: ' + data.date);
drawLogEntryModal = (clickedDayElem) => {
const entryElem = clickedDayElem.find('.log-entry-title')[0];
let modal = $('#log-entry-modal');
let modalDate = modal.find('.entry-date');
let modalTitle = modal.find('.entry-title-input');
let modalActivity = modal.find('.entry-activity-input');
let modalDurationValue = modal.find('.entry-duration-value-input');
let modalDescription = modal.find('.entry-description-input');

modalDate.html( entryElem.dataset.date.slice(0, 10) );
modalTitle.val(entryElem.dataset.title);
modalActivity.html(entryElem.dataset.activity); // TODO: this is wrong
modalDurationValue.val(entryElem.dataset.durationValue);
modalDescription.val(entryElem.dataset.description);

modal.modal('show');
};




/**
* Do stuff when page loads
*/
$(document).ready(function() {
let visibleDates = undefined;

// Hide modal
$('#log-entry-modal').modal('hide');

// Draw calendar
// Draw calendar
$('#calendar').fullCalendar({
// put options and callbacks here
});

// Retrieve and insert log entries for visible date range
// TODO: this should also get called when month arrows are clicked
fillLogEntries();

$('.fc-day').on('click touch', function () {
const clickedDate = $(this).attr('data-date');
const clickedDayElem = $(this);

// Mark active day
$('.fc-day').removeClass('active');
$(this).addClass('active');
clickedDayElem.addClass('active');

drawLogEntryModal(clickedDate);
drawLogEntryModal(clickedDayElem);
// $('#log-entry-modal').modal('show');
});
});
Loading

0 comments on commit 7dd6a16

Please sign in to comment.