Wrote API calls for history updates
Also switched app pkg mgt to yarn
This commit is contained in:
parent
a6b5149b34
commit
c703d717d8
@ -1,6 +1,6 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
import { Pool } from 'pg'
|
import { Pool, types } from 'pg'
|
||||||
|
|
||||||
import appConfig from '../appsettings.json'
|
import appConfig from '../appsettings.json'
|
||||||
import ddl from './ddl'
|
import ddl from './ddl'
|
||||||
@ -9,6 +9,10 @@ import request from './request'
|
|||||||
/** Pooled PostgreSQL instance */
|
/** Pooled PostgreSQL instance */
|
||||||
const pool = new Pool(appConfig.pgPool)
|
const pool = new Pool(appConfig.pgPool)
|
||||||
|
|
||||||
|
// Return "bigint" (int8) instances as number instead of strings
|
||||||
|
// ref: https://github.com/brianc/node-pg-types
|
||||||
|
types.setTypeParser(20, val => parseInt(val))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run a SQL query
|
* Run a SQL query
|
||||||
* @param {string} text The SQL command
|
* @param {string} text The SQL command
|
||||||
|
@ -3,6 +3,32 @@
|
|||||||
import { Pool } from 'pg'
|
import { Pool } from 'pg'
|
||||||
import cuid from 'cuid'
|
import cuid from 'cuid'
|
||||||
|
|
||||||
|
const currentRequestSql = `
|
||||||
|
SELECT
|
||||||
|
request."requestId",
|
||||||
|
(SELECT "text"
|
||||||
|
FROM mpj.history
|
||||||
|
WHERE history."requestId" = request."requestId"
|
||||||
|
AND "text" IS NOT NULL
|
||||||
|
ORDER BY "asOf" DESC
|
||||||
|
LIMIT 1) AS "text",
|
||||||
|
(SELECT "asOf"
|
||||||
|
FROM mpj.history
|
||||||
|
WHERE history."requestId" = request."requestId"
|
||||||
|
ORDER BY "asOf" DESC
|
||||||
|
LIMIT 1) AS "asOf"
|
||||||
|
FROM mpj.request`
|
||||||
|
|
||||||
|
const journalSql = `${currentRequestSql}
|
||||||
|
WHERE "userId" = $1
|
||||||
|
GROUP BY request."requestId"`
|
||||||
|
|
||||||
|
const requestNotFound = {
|
||||||
|
requestId: '',
|
||||||
|
text: 'Not Found',
|
||||||
|
asOf: 0
|
||||||
|
}
|
||||||
|
|
||||||
export default function (pool) {
|
export default function (pool) {
|
||||||
return {
|
return {
|
||||||
/**
|
/**
|
||||||
@ -10,29 +36,57 @@ export default function (pool) {
|
|||||||
* @param {string} userId The Id of the user
|
* @param {string} userId The Id of the user
|
||||||
* @return The requests that make up the current journal
|
* @return The requests that make up the current journal
|
||||||
*/
|
*/
|
||||||
journal: async userId =>
|
journal: async userId => (await pool.query(`${journalSql} ORDER BY "asOf" DESC`, [ userId ])).rows,
|
||||||
(await pool.query({
|
|
||||||
name: 'journal',
|
/**
|
||||||
text: `
|
* Get the least-recently-updated prayer request for the given user
|
||||||
SELECT
|
* @param {string} userId The Id of the current user
|
||||||
request."requestId",
|
* @return The least-recently-updated request for the given user
|
||||||
(SELECT "text"
|
*/
|
||||||
FROM mpj.history
|
oldest: async userId => (await pool.query(`${journalSql} ORDER BY "asOf" LIMIT 1`, [ userId ])).rows[0],
|
||||||
WHERE history."requestId" = request."requestId"
|
|
||||||
AND "text" IS NOT NULL
|
|
||||||
ORDER BY "asOf" DESC
|
|
||||||
LIMIT 1) AS "text",
|
|
||||||
(SELECT "asOf"
|
|
||||||
FROM mpj.history
|
|
||||||
WHERE history."requestId" = request."requestId"
|
|
||||||
ORDER BY "asOf" DESC
|
|
||||||
LIMIT 1) AS "asOf"
|
|
||||||
FROM mpj.request
|
|
||||||
WHERE "userId" = $1
|
|
||||||
GROUP BY request."requestId"
|
|
||||||
ORDER BY "asOf" DESC`
|
|
||||||
}, [userId])).rows,
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the "current" version of a request by its Id
|
||||||
|
* @param {string} requestId The Id of the request to retrieve
|
||||||
|
* @param {string} userId The Id of the user to which the request belongs
|
||||||
|
* @return The request, or a request-like object indicating that the request was not found
|
||||||
|
*/
|
||||||
|
byId: async (userId, requestId) => {
|
||||||
|
const reqs = await pool.query(`${currentRequestSql}
|
||||||
|
WHERE "requestId" = $1
|
||||||
|
AND "userId" = $2
|
||||||
|
GROUP BY request."requestId"`,
|
||||||
|
[ requestId, userId ])
|
||||||
|
return (0 < req.rowCount) ? reqs.rows[0] : requestNotFound
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a prayer request, including its full history, by its Id
|
||||||
|
* @param {string} userId The Id of the user to which the request belongs
|
||||||
|
* @param {string} requestId The Id of the request to retrieve
|
||||||
|
* @return The request, or a request-like object indicating that the request was not found
|
||||||
|
*/
|
||||||
|
fullById: async (userId, requestId) => {
|
||||||
|
const reqResults = await pool.query(`
|
||||||
|
SELECT "requestId", "enteredOn"
|
||||||
|
FROM mpj.request
|
||||||
|
WHERE "requestId" = $1
|
||||||
|
AND "userId" = $2`,
|
||||||
|
[ requestId, userId ])
|
||||||
|
if (0 === reqResults.rowCount) {
|
||||||
|
return requestNotFound
|
||||||
|
}
|
||||||
|
const req = reqResults.rows[0]
|
||||||
|
const history = await pool.query(`
|
||||||
|
SELECT "asOf", "status", COALESCE("text", '') AS "text"
|
||||||
|
FROM mpj.history
|
||||||
|
WHERE "requestId" = $1
|
||||||
|
ORDER BY "asOf"`,
|
||||||
|
[ requestId ])
|
||||||
|
req.history = history.rows
|
||||||
|
return req
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new prayer request
|
* Add a new prayer request
|
||||||
* @param {string} userId The Id of the user
|
* @param {string} userId The Id of the user
|
||||||
@ -64,6 +118,20 @@ export default function (pool) {
|
|||||||
console.error(e.stack)
|
console.error(e.stack)
|
||||||
return { requestId: '', text: 'error', asOf: 0 }
|
return { requestId: '', text: 'error', asOf: 0 }
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a history entry for this request
|
||||||
|
* @param {string} requestId The Id of the request
|
||||||
|
* @param {string} status The status for this history entry
|
||||||
|
* @param {string} updateText The updated text for the request (pass blank if no update)
|
||||||
|
*/
|
||||||
|
addHistory: async (requestId, status, updateText) => {
|
||||||
|
const asOf = Date.now()
|
||||||
|
await pool.query(`
|
||||||
|
INSERT INTO mpj.history ("requestId", "asOf", "status", "text") VALUES ($1, $2, $3, NULLIF($4, ''))`,
|
||||||
|
[ requestId, asOf, status, updateText ])
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,45 @@ const router = new Router()
|
|||||||
|
|
||||||
export default function (checkJwt) {
|
export default function (checkJwt) {
|
||||||
|
|
||||||
router.post('/', checkJwt, async (ctx, next) => {
|
router
|
||||||
ctx.body = await db.request.addNew(ctx.state.user.sub, ctx.request.body.requestText)
|
// Add a new request
|
||||||
await next()
|
.post('/', checkJwt, async (ctx, next) => {
|
||||||
})
|
ctx.body = await db.request.addNew(ctx.state.user.sub, ctx.request.body.requestText)
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
// Add a request history entry (prayed, updated, answered, etc.)
|
||||||
|
.post('/:id/history', checkJwt, async (ctx, next) => {
|
||||||
|
const body = ctx.request.body
|
||||||
|
const result = await db.request.addHistory(ctx.params.id, body.status, body.updateText)
|
||||||
|
ctx.status(('Not Found' === result.text) ? 404 : 204)
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
// Get a journal-style request by its Id
|
||||||
|
.get('/:id', checkJwt, async (ctx, next) => {
|
||||||
|
const req = await db.request.byId(ctx.state.user.sub, ctx.params.id)
|
||||||
|
if ('Not Found' === req.text) {
|
||||||
|
ctx.status(404)
|
||||||
|
} else {
|
||||||
|
ctx.body = req
|
||||||
|
}
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
// Get a request, along with its full history
|
||||||
|
.get('/:id/full', checkJwt, async (ctx, next) => {
|
||||||
|
const req = await db.request.fullById(ctx.state.user.sub, ctx.params.id)
|
||||||
|
if ('Not Found' === req.text) {
|
||||||
|
ctx.status(404)
|
||||||
|
} else {
|
||||||
|
ctx.body = req
|
||||||
|
}
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
// Get the least-recently-updated request (used for the "pray through the journal" feature)
|
||||||
|
.get('/:id/oldest', checkJwt, async (ctx, next) => {
|
||||||
|
ctx.body = await db.request.oldest(ctx.state.user.sub)
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
return router
|
return router
|
||||||
}
|
}
|
||||||
|
|
||||||
|
11278
src/app/package-lock.json
generated
11278
src/app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,7 @@
|
|||||||
"auth0-js": "^8.10.1",
|
"auth0-js": "^8.10.1",
|
||||||
"axios": "^0.16.2",
|
"axios": "^0.16.2",
|
||||||
"element-ui": "^1.4.4",
|
"element-ui": "^1.4.4",
|
||||||
|
"moment": "^2.18.1",
|
||||||
"pug": "^2.0.0-rc.4",
|
"pug": "^2.0.0-rc.4",
|
||||||
"vue": "^2.4.4",
|
"vue": "^2.4.4",
|
||||||
"vue-progressbar": "^0.7.3",
|
"vue-progressbar": "^0.7.3",
|
||||||
|
@ -29,6 +29,12 @@ export default {
|
|||||||
* Add a new prayer request
|
* Add a new prayer request
|
||||||
* @param {string} requestText The text of the request to be added
|
* @param {string} requestText The text of the request to be added
|
||||||
*/
|
*/
|
||||||
addRequest: requestText => http.post('request/', { requestText })
|
addRequest: requestText => http.post('request/', { requestText }),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark a prayer request as having been prayed
|
||||||
|
* @param {string} requestId The Id of the request
|
||||||
|
*/
|
||||||
|
markPrayed: requestId => http.post('request/${requestId}/history', { status: 'Prayed', updateText: '' })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
template(v-if="!isLoadingJournal")
|
template(v-if="!isLoadingJournal")
|
||||||
new-request
|
new-request
|
||||||
p journal has {{ journal.length }} entries
|
p journal has {{ journal.length }} entries
|
||||||
|
el-row
|
||||||
|
el-col(:span='4'): strong Actions
|
||||||
|
el-col(:span='16'): strong Request
|
||||||
|
el-col(:span='4'): strong As Of
|
||||||
request-list-item(v-for="request in journal" v-bind:request="request" v-bind:key="request.requestId")
|
request-list-item(v-for="request in journal" v-bind:request="request" v-bind:key="request.requestId")
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -1,22 +1,34 @@
|
|||||||
<template lang="pug">
|
<template lang="pug">
|
||||||
el-row.journal-request(:id="request.requestId")
|
el-row.journal-request
|
||||||
el-col(:span='4') Action buttons
|
el-col(:span='4')
|
||||||
el-col(:span='20')
|
p
|
||||||
p Id {{ request.requestId }} as of {{ request.asOf }}
|
el-button(icon='check' @click='markPrayed()' title='Pray')
|
||||||
p {{ request.text }}
|
el-button(icon='edit' @click='editRequest()' title='Edit')
|
||||||
|
el-button(icon='document' @click='viewHistory()' title='Show History')
|
||||||
|
el-col(:span='16'): p {{ request.text }}
|
||||||
|
el-col(:span='4'): p {{ asOf }}
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import moment from 'moment'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'request-list-item',
|
name: 'request-list-item',
|
||||||
props: ['request'],
|
props: ['request'],
|
||||||
data: function () {
|
data () {
|
||||||
return {}
|
return {}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
markPrayed: function (requestId) {
|
markPrayed () {
|
||||||
|
alert(`Marking ${this.request.requestId} prayed`)
|
||||||
// TODO: write function
|
// TODO: write function
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
asOf () {
|
||||||
|
// FIXME: why isn't this already a number?
|
||||||
|
return moment(parseInt(this.request.asOf)).fromNow()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -14,9 +14,9 @@ Vue.config.productionTip = false
|
|||||||
Vue.use(ElementUI)
|
Vue.use(ElementUI)
|
||||||
|
|
||||||
Vue.use(VueProgressBar, {
|
Vue.use(VueProgressBar, {
|
||||||
color: 'rgb(143, 255, 199)',
|
color: 'rgb(32, 160, 255)',
|
||||||
failedColor: 'red',
|
failedColor: 'red',
|
||||||
height: '2px'
|
height: '3px'
|
||||||
})
|
})
|
||||||
|
|
||||||
/* eslint-disable no-new */
|
/* eslint-disable no-new */
|
||||||
|
6509
src/app/yarn.lock
Normal file
6509
src/app/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user