Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions database/commands.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
10 changes: 10 additions & 0 deletions database/migrations/20170926155324_phase_4_status.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

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')
7 changes: 7 additions & 0 deletions database/queries.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}


12 changes: 12 additions & 0 deletions web-server/assets/src/style/users.sass
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
.users
&-status
margin-bottom: 15px
display: flex

&-status-content
display: flex
font-size: 15px
margin-left: 20px
align-items: center

&-grid-controls
padding-top: 0.25em
text-align: right
Expand Down Expand Up @@ -29,3 +39,5 @@
&-grid-member-avatar > img
height: 10vh
min-width: 10vh
&-display-inline
diplay: inline
21 changes: 21 additions & 0 deletions web-server/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
}
Expand Down
22 changes: 21 additions & 1 deletion web-server/routes/phases.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
const bodyParser = require('body-parser')
const queries = require('../../database/queries')
const commands = require('../../database/commands')
const urlEncodedBodyParser = bodyParser({ urlencoded: true })

module.exports = app => {

app.get('/phases', app.ensureTrailingSlash, (request, response, next) => {
response.renderMarkdownFile(`/phases/README.md`)
})


app.get('/phases/:phaseNumber', app.ensureTrailingSlash)

app.use('/phases/:phaseNumber', (request, response, next) => {
Expand Down Expand Up @@ -48,6 +50,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.getPhase4UsersWithStatus()
.then(users => {
response.render('phases/phase4status', {title: 'Phase 4 Status', users, userId})
})
.catch(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})
.then(() => {
response.redirect('/phases/4/status')
})
})

app.use('/phases/:phaseNumber/dashboard', app.ensureAdmin)

app.get('/phases/:phaseNumber/dashboard', (request, response, next) => {
Expand Down
3 changes: 3 additions & 0 deletions web-server/views/markdown.jade
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
27 changes: 27 additions & 0 deletions web-server/views/phases/phase4status.jade
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
extends ../layout

block content
button.btn.btn-primary.pull-right(type='button' data-toggle='modal' data-target='#statusModal') Update Status
h3 Phase #{phase.number} Status Reports
.container
.row
for user in users
.col-md-6.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)
.users-status-content
p= user.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