Finished answered request layout (#9)

Also:
- rearranged API calls to be in alphabetical order (except the bearer
stuff at the top)
- modified the API base URL and Auth0 renewal URLs to be derived from
the browser's location (localhost / prod both work now with no change)
This commit is contained in:
Daniel J. Summers 2017-10-22 22:50:26 -05:00
parent a1ce40ee83
commit 3c3f0a7981
8 changed files with 171 additions and 52 deletions

View File

@ -56,6 +56,19 @@ export default function (checkJwt) {
}
await next()
})
// Get a complete request; equivalent to full + notes
.get('/:id/complete', checkJwt, async (ctx, next) => {
const req = await db.request.fullById(ctx.state.user.sub, ctx.params.id)
if ('Not Found' === req.text) {
ctx.response.status = 404
} else {
ctx.response.status = 200
req.notes = await db.request.notesById(ctx.state.user.sub, ctx.params.id)
ctx.body = req
}
await next()
})
// Get all answered requests
.get('/answered', checkJwt, async (ctx, next) => {
ctx.body = await db.request.answered(ctx.state.user.sub)
ctx.response.status = 200

View File

@ -1,7 +1,7 @@
import axios from 'axios'
const http = axios.create({
baseURL: 'http://localhost:3000/api/'
baseURL: `${location.protocol}//${location.host}/api/`
})
/**
@ -20,11 +20,6 @@ export default {
*/
removeBearer: () => delete http.defaults.headers.common['authorization'],
/**
* Get all prayer requests and their most recent updates
*/
journal: () => http.get('journal/'),
/**
* Add a note for a prayer request
* @param {string} requestId The Id of the request to which the note applies
@ -39,19 +34,9 @@ export default {
addRequest: requestText => http.post('request/', { requestText }),
/**
* Update a prayer request
* @param request The request (should have requestId, status, and updateText properties)
* Get all answered requests, along with the text they had when it was answered
*/
updateRequest: request => http.post(`request/${request.requestId}/history`, {
status: request.status,
updateText: request.updateText
}),
/**
* Get a prayer request (journal-style; only latest update)
* @param {string} requestId The Id of the request to retrieve
*/
getRequest: requestId => http.get(`request/${requestId}`),
getAnsweredRequests: () => http.get('request/answered'),
/**
* Get a prayer request (full; includes all history)
@ -59,15 +44,35 @@ export default {
*/
getFullRequest: requestId => http.get(`request/${requestId}/full`),
/**
* Get all answered requests, along with the text they had when it was answered
*/
getAnsweredRequests: () => http.get('request/answered'),
/**
* Get past notes for a prayer request
* @param {string} requestId The Id of the request for which notes should be retrieved
*/
getNotes: requestId => http.get(`request/${requestId}/notes`)
getNotes: requestId => http.get(`request/${requestId}/notes`),
/**
* Get a prayer request (journal-style; only latest update)
* @param {string} requestId The Id of the request to retrieve
*/
getRequest: requestId => http.get(`request/${requestId}`),
/**
* Get a complete request; equivalent of "full" and "notes" combined
*/
getRequestComplete: requestId => http.get(`request/${requestId}/complete`),
/**
* Get all prayer requests and their most recent updates
*/
journal: () => http.get('journal/'),
/**
* Update a prayer request
* @param request The request (should have requestId, status, and updateText properties)
*/
updateRequest: request => http.post(`request/${request.requestId}/history`, {
status: request.status,
updateText: request.updateText
})
}

View File

@ -2,36 +2,29 @@
article
page-title(title='Answered Requests')
p(v-if='!loaded') Loading answered requests...
div(v-if='loaded')
div(v-if='loaded').mpj-answered-list
p.mpj-request-text(v-for='req in requests' :key='req.requestId')
b-btn(@click='showFull(req.requestId)'
| {{ req.text }}
br
br
b-btn(:to='{ name: "AnsweredDetail", params: { id: req.requestId }}'
size='sm'
variant='outline-secondary')
icon(name='search')
|  View Full Request
|     {{ req.text }}  
= ' View Full Request'
small.text-muted: em.
(Answered #[date-from-now(:value='req.asOf')])
full-request(:events='eventBus')
  Answered #[date-from-now(:value='req.asOf')]
</template>
<script>
'use static'
import Vue from 'vue'
import FullRequest from './request/FullRequest'
import api from '@/api'
export default {
name: 'answered',
components: {
FullRequest
},
data () {
return {
eventBus: new Vue(),
requests: [],
loaded: false
}
@ -54,11 +47,15 @@ export default {
} finally {
this.loaded = true
}
},
methods: {
showFull (requestId) {
this.eventBus.$emit('full', requestId)
}
}
}
</script>
<style>
.mpj-answered-list p {
border-top: solid 1px lightgray;
}
.mpj-answered-list p:first-child {
border-top: none;
}
</style>

View File

@ -0,0 +1,81 @@
<template lang="pug">
article
page-title(title='Answered Request')
p(v-if='!request') Loading request...
template(v-if='request')
p.
Answered {{ formatDate(answered) }} (#[date-from-now(:value='answered')]) &nbsp;
#[small: em.text-muted prayed {{ prayedCount }} times, open {{ openDays }} days]
p.mpj-request-text {{ lastText }}
b-table(small hover :fields='fields' :items='log')
template(slot='action' scope='data') {{ data.item.status }} on {{ formatDate(data.item.asOf) }}
</template>
<script>
'use strict'
import moment from 'moment'
import api from '@/api'
const asOfDesc = (a, b) => b.asOf - a.asOf
export default {
name: 'answer-detail',
props: {
id: {
type: String,
required: true
}
},
data () {
return {
request: null,
fields: [
{ key: 'action', label: 'Action' },
{ key: 'text', label: 'Update / Notes' }
]
}
},
computed: {
answered () {
return this.request.history.find(hist => hist.status === 'Answered').asOf
},
lastText () {
return this.request.history
.filter(hist => hist.text > '')
.sort(asOfDesc)[0].text
},
log () {
return this.request.notes
.map(note => ({ asOf: note.asOf, text: note.notes, status: 'Notes' }))
.concat(this.request.history)
.sort(asOfDesc)
.slice(1)
},
openDays () {
return Math.floor(
(this.answered - this.request.history.find(hist => hist.status === 'Created').asOf) / 1000 / 60 / 60 / 24)
},
prayedCount () {
return this.request.history.filter(hist => hist.status === 'Prayed').length
}
},
async mounted () {
this.$Progress.start()
try {
const req = await api.getRequestComplete(this.id)
this.request = req.data
this.$Progress.finish()
} catch (e) {
console.log(e)
this.$Progress.fail()
}
},
methods: {
formatDate (asOf) {
return moment(asOf).format('LL')
}
}
}
</script>

View File

@ -21,8 +21,8 @@ b-modal(v-model='notesVisible'
small.text-muted: date-from-now(:value='note.asOf')
br
div.mpj-request-text {{ note.notes }}
div(v-if='noPriorNotes').text-center.text-muted There are no prior notes for this request
div(v-if='!priorNotesLoaded').text-center
div(v-else-if='noPriorNotes').text-center.text-muted There are no prior notes for this request
div(v-else).text-center
b-btn(variant='outline-secondary'
@click='loadNotes()') Load Prior Notes
div.w-100.text-right(slot='modal-footer')

View File

@ -17,7 +17,7 @@ div
= '(last activity '
date-from-now(:value='request.asOf')
| )
b-card(v-for='it in 3 - row.length')
b-card(v-for='it in 3 - row.length' key='-1')
br
</template>

View File

@ -2,6 +2,7 @@ import Vue from 'vue'
import Router from 'vue-router'
import Answered from '@/components/Answered'
import AnsweredDetail from '@/components/AnsweredDetail'
import Home from '@/components/Home'
import Journal from '@/components/Journal'
import LogOn from '@/components/user/LogOn'
@ -11,9 +12,31 @@ Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{ path: '/', name: 'Home', component: Home },
{ path: '/answered', name: 'Answered', component: Answered },
{ path: '/journal', name: 'Journal', component: Journal },
{ path: '/user/log-on', name: 'LogOn', component: LogOn }
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/answered/:id',
name: 'AnsweredDetail',
component: AnsweredDetail,
props: true
},
{
path: '/answered',
name: 'Answered',
component: Answered
},
{
path: '/journal',
name: 'Journal',
component: Journal
},
{
path: '/user/log-on',
name: 'LogOn',
component: LogOn
}
]
})

View File

@ -9,12 +9,12 @@
clientID: 'Of2s0RQCQ3mt3dwIkOBY5h85J9sXbF2n',
scope: 'openid profile email',
responseType: 'token id_token',
redirectUri: 'http://localhost:3000/static/silent.html'
redirectUri: location.protocol + '//' + location.host + '/static/silent.html'
})
</script>
<script>
webAuth.parseHash(window.location.hash, function (err, response) {
parent.postMessage(err || response, 'http://localhost:3000');
parent.postMessage(err || response, location.protocol + '//' + location.host);
})
</script>
</head>