From 0bcc0de172c4cdf444fe640b2c40d10c059825c6 Mon Sep 17 00:00:00 2001 From: je-lopez Date: Tue, 26 Sep 2017 15:58:43 -0700 Subject: [PATCH 1/4] creates phase_4_status table migration --- database/migrations/20170926155324_phase_4_status.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 database/migrations/20170926155324_phase_4_status.js diff --git a/database/migrations/20170926155324_phase_4_status.js b/database/migrations/20170926155324_phase_4_status.js new file mode 100644 index 00000000..c953ef23 --- /dev/null +++ b/database/migrations/20170926155324_phase_4_status.js @@ -0,0 +1,11 @@ + +exports.up = (knex, Promise) => + knex.schema.createTable('phase_4_status', table => { + table.string('user_id').notNullable().unique() + table.string('status').notNullable() + table.timestamp('updated_at') + }) + + +exports.down = (knex, Promise) => + knex.schema.dropTable('phase_4_status') From 6ccefc018cb524827362dd1565ed8a8768841c06 Mon Sep 17 00:00:00 2001 From: je-lopez Date: Tue, 26 Sep 2017 16:11:37 -0700 Subject: [PATCH 2/4] phase 4 user status route created --- web-server/index.js | 3 +++ web-server/routes/phases.js | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/web-server/index.js b/web-server/index.js index a9902c43..c6e7faea 100644 --- a/web-server/index.js +++ b/web-server/index.js @@ -1,6 +1,7 @@ require('../environment') const https = require('express-sslify').HTTPS const express = require('express') +const bodyParser = require('body-parser') const compression = require('compression') @@ -19,6 +20,8 @@ app.get('/_status', (request, response, next) => { response.send('status ok') }) +app.use(bodyParser({ urlencoded: true })) + app.use(compression()) require('./routes/assets')(app) app.use(require('cookie-parser')()) diff --git a/web-server/routes/phases.js b/web-server/routes/phases.js index 71899420..f3c9a130 100644 --- a/web-server/routes/phases.js +++ b/web-server/routes/phases.js @@ -1,4 +1,5 @@ const queries = require('../../database/queries') +const commands = require('../../database/commands') module.exports = app => { @@ -48,6 +49,24 @@ module.exports = app => { response.render('phases/goals', {title: 'Phase 3 Goals'}) }) + app.get('/phases/4/status', (request, response, next) => { + const userId = request.user.id + request.backOffice.getPhase4Statuses() + .then(users => { + response.render('users/status', {title: 'Phase 4 Status', users, userId}) + }) + .catch(next) + }) + + app.post('/phases/4/status', (request, response, next) => { + const user_id = request.user.id + const {status} = request.body + commands.setStatus({user_id, status}) + .then(() => { + response.redirect('/phases/4/status') + }) + }) + app.use('/phases/:phaseNumber/dashboard', app.ensureAdmin) app.get('/phases/:phaseNumber/dashboard', (request, response, next) => { From c62afe8f809be3fdbbdd4f7e31a67cb8e1903a59 Mon Sep 17 00:00:00 2001 From: je-lopez Date: Tue, 3 Oct 2017 11:06:41 -0700 Subject: [PATCH 3/4] creates routes and view for phase 4 status page --- backoffice/index.js | 24 ++++++++++++++++++++ database/commands.js | 31 ++++++++++++++++++++++++++ web-server/assets/src/style/users.sass | 12 ++++++++++ web-server/views/users/status.jade | 27 ++++++++++++++++++++++ 4 files changed, 94 insertions(+) create mode 100644 web-server/views/users/status.jade diff --git a/backoffice/index.js b/backoffice/index.js index 1f3f0169..6011a94b 100644 --- a/backoffice/index.js +++ b/backoffice/index.js @@ -2,6 +2,7 @@ require('../environment') const { IDMClient } = require('./idm') const { EchoClient } = require('./echo') const hubspot = require('./hubspot') +const knex = require('../database/knex') const { PHASES, isValidPhase, isUserALearner, isUserActive, isUserInactive } = require('./util') module.exports = class BackOffice { @@ -78,6 +79,29 @@ module.exports = class BackOffice { }) } + getPhase4Statuses(){ + return this.getAllUsers({ + includePhases: true, + phase: 4, + }) + .then(users => { + const userIds = users.map(user => user.id) + return knex + .select('*') + .from('phase_4_status') + .whereIn('user_id', userIds) + .then(statuses => { + users.map(user => { + statuses.forEach(status => { + user.status = status.user_id === user.id + ? status.status + : 'Status Not Updated' + }) + }) + return users + }) + }) + } getPhasesForUsers(users){ return this.echo.getPhasesForUsers(users) diff --git a/database/commands.js b/database/commands.js index 9a794415..b22b0d0d 100644 --- a/database/commands.js +++ b/database/commands.js @@ -30,6 +30,37 @@ const setSkillCheck = ({user_id, label, checked, referrer}) => } }) +const logStatusUpdate = ({user_id, status}) => + knex + .insert({ + occurred_at: knex.fn.now(), + type: 'phase_4_status_update', + user_id, + metadata: {status}, + }) + .into('event_logs') + + +const setStatus = ({user_id, status}) => + logStatusUpdate({user_id, status}) + .then(() => { + const insert = knex('phase_4_status').insert({ + user_id, status, updated_at: knex.fn.now() + }).toString() + + const update = knex('phase_4_status') + .update({status}) + .where('phase_4_status.user_id', user_id) + + const query = util.format( + '%s ON CONFLICT (user_id) DO UPDATE SET %s', + insert.toString(), + update.toString().replace(/^update\s.*\sset\s/i, '') + ) + return knex.raw(query) + }) + module.exports = { setSkillCheck, + setStatus } diff --git a/web-server/assets/src/style/users.sass b/web-server/assets/src/style/users.sass index 7545037c..27ebe128 100644 --- a/web-server/assets/src/style/users.sass +++ b/web-server/assets/src/style/users.sass @@ -1,4 +1,14 @@ .users + &-status > * + display: inline-block + + &-status + margin-bottom: 15px + + &-status-content + font-size: 15px + margin: 0 20px + &-grid-controls padding-top: 0.25em text-align: right @@ -29,3 +39,5 @@ &-grid-member-avatar > img height: 10vh min-width: 10vh + &-display-inline + diplay: inline diff --git a/web-server/views/users/status.jade b/web-server/views/users/status.jade new file mode 100644 index 00000000..3c7f9cec --- /dev/null +++ b/web-server/views/users/status.jade @@ -0,0 +1,27 @@ +extends ../layout + +block content + mixin usersList(_users) + for user in _users + .users-status + .users-grid-member.well(data-user=JSON.stringify(user)) + a(href='/users/'+user.handle) + .users-grid-member-name= user.name + .users-grid-member-handle= user.handle + .users-grid-member-avatar + img(src=user.avatarUrl) + p.users-status-content= user.status + if userId === user.id + button.btn.btn-primary(type='button' data-toggle='modal' data-target='#statusModal') Update Your Status + .modal.fade#statusModal(role='dialog') + .modal-dialog + .modal-content + .modal-header + button.close(type='button' data-dismiss='modal') × + h4.modal-title Update Your Status + .modal-body + form(action='/phases/4/status' method='post') + textarea(name='status' rows='6' cols='70') + button.btn.btn-default(type='submit') Update + h3 Phase #{phase.number} Status Reports + +usersList(users.filter(u => u.phase === 4)) \ No newline at end of file From 2871c2c396ff98a26ee0ecdcdd6e03cee6d178ad Mon Sep 17 00:00:00 2001 From: je-lopez Date: Tue, 3 Oct 2017 14:02:46 -0700 Subject: [PATCH 4/4] adds requested changes --- backoffice/index.js | 24 ------------------------ database/queries.js | 7 +++++++ web-server/helpers.js | 21 +++++++++++++++++++++ web-server/index.js | 3 --- web-server/routes/phases.js | 7 ++++--- web-server/views/markdown.jade | 3 +++ 6 files changed, 35 insertions(+), 30 deletions(-) diff --git a/backoffice/index.js b/backoffice/index.js index 6011a94b..b0dc5009 100644 --- a/backoffice/index.js +++ b/backoffice/index.js @@ -79,30 +79,6 @@ module.exports = class BackOffice { }) } - getPhase4Statuses(){ - return this.getAllUsers({ - includePhases: true, - phase: 4, - }) - .then(users => { - const userIds = users.map(user => user.id) - return knex - .select('*') - .from('phase_4_status') - .whereIn('user_id', userIds) - .then(statuses => { - users.map(user => { - statuses.forEach(status => { - user.status = status.user_id === user.id - ? status.status - : 'Status Not Updated' - }) - }) - return users - }) - }) - } - getPhasesForUsers(users){ return this.echo.getPhasesForUsers(users) } diff --git a/database/queries.js b/database/queries.js index 2118404b..f04f2752 100644 --- a/database/queries.js +++ b/database/queries.js @@ -38,9 +38,16 @@ const hashChecksByLabel = checks => { return checkedMap } +const getPhase4Status = userIds => + knex + .select('*') + .from('phase_4_status') + .whereIn('user_id', userIds) + module.exports = { getChecksForUserAndLabels, getCheckLogsForUsers, + getPhase4Status, } diff --git a/web-server/helpers.js b/web-server/helpers.js index 3178ce1c..deaf846f 100644 --- a/web-server/helpers.js +++ b/web-server/helpers.js @@ -186,6 +186,27 @@ module.exports = app => { }) } + request.getPhase4Users = function(){ + return this.backOffice.getAllUsers({ + includePhases: true, + phase: 4, + }) + } + + request.getPhase4UsersWithStatus = function(){ + return this.getPhase4Users() + .then(users => { + const userIds = users.map(user => user.id) + return queries.getPhase4Status(userIds).then(statuses => { + users.forEach(user => { + const status = statuses.find(status => status.user_id === user.id) + user.status = status ? status.status : '[no status found]' + }) + return users + }) + }) + } + next() }) } diff --git a/web-server/index.js b/web-server/index.js index c6e7faea..a9902c43 100644 --- a/web-server/index.js +++ b/web-server/index.js @@ -1,7 +1,6 @@ require('../environment') const https = require('express-sslify').HTTPS const express = require('express') -const bodyParser = require('body-parser') const compression = require('compression') @@ -20,8 +19,6 @@ app.get('/_status', (request, response, next) => { response.send('status ok') }) -app.use(bodyParser({ urlencoded: true })) - app.use(compression()) require('./routes/assets')(app) app.use(require('cookie-parser')()) diff --git a/web-server/routes/phases.js b/web-server/routes/phases.js index f3c9a130..c1e460f0 100644 --- a/web-server/routes/phases.js +++ b/web-server/routes/phases.js @@ -1,5 +1,7 @@ +const bodyParser = require('body-parser') const queries = require('../../database/queries') const commands = require('../../database/commands') +const urlEncodedBodyParser = bodyParser({ urlencoded: true }) module.exports = app => { @@ -7,7 +9,6 @@ module.exports = app => { response.renderMarkdownFile(`/phases/README.md`) }) - app.get('/phases/:phaseNumber', app.ensureTrailingSlash) app.use('/phases/:phaseNumber', (request, response, next) => { @@ -51,14 +52,14 @@ module.exports = app => { app.get('/phases/4/status', (request, response, next) => { const userId = request.user.id - request.backOffice.getPhase4Statuses() + request.getPhase4UsersWithStatus() .then(users => { response.render('users/status', {title: 'Phase 4 Status', users, userId}) }) .catch(next) }) - app.post('/phases/4/status', (request, response, next) => { + app.post('/phases/4/status', urlEncodedBodyParser, (request, response, next) => { const user_id = request.user.id const {status} = request.body commands.setStatus({user_id, status}) diff --git a/web-server/views/markdown.jade b/web-server/views/markdown.jade index 2ad2891a..b54783d0 100644 --- a/web-server/views/markdown.jade +++ b/web-server/views/markdown.jade @@ -13,6 +13,9 @@ block content a(href="/phases/#{phase.number}/skills") Skills li a(href="/phases/#{phase.number}/schedule") Schedule + if phase.number === 4 + li + a(href="/phases/4/status") Status if currentUser.isAdmin li a(href="/phases/#{phase.number}/dashboard") Dashboard