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:
parent
a1ce40ee83
commit
3c3f0a7981
@ -56,6 +56,19 @@ export default function (checkJwt) {
|
|||||||
}
|
}
|
||||||
await next()
|
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) => {
|
.get('/answered', checkJwt, async (ctx, next) => {
|
||||||
ctx.body = await db.request.answered(ctx.state.user.sub)
|
ctx.body = await db.request.answered(ctx.state.user.sub)
|
||||||
ctx.response.status = 200
|
ctx.response.status = 200
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
const http = axios.create({
|
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'],
|
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
|
* Add a note for a prayer request
|
||||||
* @param {string} requestId The Id of the request to which the note applies
|
* @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 }),
|
addRequest: requestText => http.post('request/', { requestText }),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a prayer request
|
* Get all answered requests, along with the text they had when it was answered
|
||||||
* @param request The request (should have requestId, status, and updateText properties)
|
|
||||||
*/
|
*/
|
||||||
updateRequest: request => http.post(`request/${request.requestId}/history`, {
|
getAnsweredRequests: () => http.get('request/answered'),
|
||||||
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}`),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a prayer request (full; includes all history)
|
* Get a prayer request (full; includes all history)
|
||||||
@ -59,15 +44,35 @@ export default {
|
|||||||
*/
|
*/
|
||||||
getFullRequest: requestId => http.get(`request/${requestId}/full`),
|
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
|
* Get past notes for a prayer request
|
||||||
* @param {string} requestId The Id of the request for which notes should be retrieved
|
* @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
|
||||||
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,36 +2,29 @@
|
|||||||
article
|
article
|
||||||
page-title(title='Answered Requests')
|
page-title(title='Answered Requests')
|
||||||
p(v-if='!loaded') Loading 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')
|
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'
|
size='sm'
|
||||||
variant='outline-secondary')
|
variant='outline-secondary')
|
||||||
icon(name='search')
|
icon(name='search')
|
||||||
| View Full Request
|
= ' View Full Request'
|
||||||
| {{ req.text }}
|
|
||||||
small.text-muted: em.
|
small.text-muted: em.
|
||||||
(Answered #[date-from-now(:value='req.asOf')])
|
Answered #[date-from-now(:value='req.asOf')]
|
||||||
full-request(:events='eventBus')
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
'use static'
|
'use static'
|
||||||
|
|
||||||
import Vue from 'vue'
|
|
||||||
|
|
||||||
import FullRequest from './request/FullRequest'
|
|
||||||
|
|
||||||
import api from '@/api'
|
import api from '@/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'answered',
|
name: 'answered',
|
||||||
components: {
|
|
||||||
FullRequest
|
|
||||||
},
|
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
eventBus: new Vue(),
|
|
||||||
requests: [],
|
requests: [],
|
||||||
loaded: false
|
loaded: false
|
||||||
}
|
}
|
||||||
@ -54,11 +47,15 @@ export default {
|
|||||||
} finally {
|
} finally {
|
||||||
this.loaded = true
|
this.loaded = true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
showFull (requestId) {
|
|
||||||
this.eventBus.$emit('full', requestId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.mpj-answered-list p {
|
||||||
|
border-top: solid 1px lightgray;
|
||||||
|
}
|
||||||
|
.mpj-answered-list p:first-child {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
81
src/app/src/components/AnsweredDetail.vue
Normal file
81
src/app/src/components/AnsweredDetail.vue
Normal 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')])
|
||||||
|
#[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>
|
@ -21,8 +21,8 @@ b-modal(v-model='notesVisible'
|
|||||||
small.text-muted: date-from-now(:value='note.asOf')
|
small.text-muted: date-from-now(:value='note.asOf')
|
||||||
br
|
br
|
||||||
div.mpj-request-text {{ note.notes }}
|
div.mpj-request-text {{ note.notes }}
|
||||||
div(v-if='noPriorNotes').text-center.text-muted There are no prior notes for this request
|
div(v-else-if='noPriorNotes').text-center.text-muted There are no prior notes for this request
|
||||||
div(v-if='!priorNotesLoaded').text-center
|
div(v-else).text-center
|
||||||
b-btn(variant='outline-secondary'
|
b-btn(variant='outline-secondary'
|
||||||
@click='loadNotes()') Load Prior Notes
|
@click='loadNotes()') Load Prior Notes
|
||||||
div.w-100.text-right(slot='modal-footer')
|
div.w-100.text-right(slot='modal-footer')
|
||||||
|
@ -17,7 +17,7 @@ div
|
|||||||
= '(last activity '
|
= '(last activity '
|
||||||
date-from-now(:value='request.asOf')
|
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
|
br
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import Vue from 'vue'
|
|||||||
import Router from 'vue-router'
|
import Router from 'vue-router'
|
||||||
|
|
||||||
import Answered from '@/components/Answered'
|
import Answered from '@/components/Answered'
|
||||||
|
import AnsweredDetail from '@/components/AnsweredDetail'
|
||||||
import Home from '@/components/Home'
|
import Home from '@/components/Home'
|
||||||
import Journal from '@/components/Journal'
|
import Journal from '@/components/Journal'
|
||||||
import LogOn from '@/components/user/LogOn'
|
import LogOn from '@/components/user/LogOn'
|
||||||
@ -11,9 +12,31 @@ Vue.use(Router)
|
|||||||
export default new Router({
|
export default new Router({
|
||||||
mode: 'history',
|
mode: 'history',
|
||||||
routes: [
|
routes: [
|
||||||
{ path: '/', name: 'Home', component: Home },
|
{
|
||||||
{ path: '/answered', name: 'Answered', component: Answered },
|
path: '/',
|
||||||
{ path: '/journal', name: 'Journal', component: Journal },
|
name: 'Home',
|
||||||
{ path: '/user/log-on', name: 'LogOn', component: LogOn }
|
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
|
||||||
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
@ -9,12 +9,12 @@
|
|||||||
clientID: 'Of2s0RQCQ3mt3dwIkOBY5h85J9sXbF2n',
|
clientID: 'Of2s0RQCQ3mt3dwIkOBY5h85J9sXbF2n',
|
||||||
scope: 'openid profile email',
|
scope: 'openid profile email',
|
||||||
responseType: 'token id_token',
|
responseType: 'token id_token',
|
||||||
redirectUri: 'http://localhost:3000/static/silent.html'
|
redirectUri: location.protocol + '//' + location.host + '/static/silent.html'
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
webAuth.parseHash(window.location.hash, function (err, response) {
|
webAuth.parseHash(window.location.hash, function (err, response) {
|
||||||
parent.postMessage(err || response, 'http://localhost:3000');
|
parent.postMessage(err || response, location.protocol + '//' + location.host);
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
|
Loading…
Reference in New Issue
Block a user