diff --git a/docs/index.md b/docs/index.md
index c3122dc..c422d0d 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -28,6 +28,10 @@ The first button for each request has a checkmark icon; clicking this button wil
The second button for each request has a pencil icon. This allows you to edit the text of the request, pretty much the same way you entered it; it starts with the current text, and you can add to it, modify it, or completely replace it. By default, updates will go in with an "Updated" status; you have the option to also mark this update as "Prayed" or "Answered." Answered requests will drop off the journal list.
+## Adding Notes
+
+The third button for each request has an icon that looks like a piece of paper with writing; this lets you record notes about the request. If there is something you want to record that doesn't change the text of the request, this is the place to do it. For example, you may be praying for a long-term health issue, and that person tells you that their status is the same; or, you may want to record something God said to you while you were praying for that request.
+
## Viewing a Request and Its History
myPrayerJournal tracks all of the actions related to a request; the fourth button, with the magnifying glass icon, will show you the entire history, including the text as it changed, and all the times "Prayed" was recorded.
diff --git a/src/api/src/db/ddl.js b/src/api/src/db/ddl.js
index 63ecf16..003f965 100644
--- a/src/api/src/db/ddl.js
+++ b/src/api/src/db/ddl.js
@@ -46,6 +46,17 @@ const ddl = [
PRIMARY KEY ("requestId", "asOf"));
COMMENT ON TABLE mpj.history IS 'Request update history'`
},
+ {
+ name: 'note Table',
+ check: tableSql('note'),
+ fix: `
+ CREATE TABLE mpj.note (
+ "requestId" varchar(25) NOT NULL REFERENCES mpj.request,
+ "asOf" bigint NOT NULL,
+ "notes" text NOT NULL,
+ PRIMARY KEY ("requestId", "asOf"));
+ COMMENT ON TABLE mpj.note IS 'Notes regarding a request'`
+ },
{
name: 'request.userId Index',
check: indexSql('request', 'idx_request_userId'),
diff --git a/src/api/src/db/request.js b/src/api/src/db/request.js
index bec77b8..01c5ccc 100644
--- a/src/api/src/db/request.js
+++ b/src/api/src/db/request.js
@@ -18,21 +18,41 @@ const requestNotFound = {
}
export default function (pool) {
+
+ /**
+ * Retrieve basic information about a single request
+ * @param {string} requestId The Id of the request to retrieve
+ * @param {string} userId The Id of the user to whom the request belongs
+ */
+ let retrieveRequest = (requestId, userId) =>
+ pool.query(`
+ SELECT "requestId", "enteredOn"
+ FROM mpj.request
+ WHERE "requestId" = $1
+ AND "userId" = $2`,
+ [ requestId, userId ])
+
return {
/**
* Add a history entry for this request
- * @param {string} requestId The Id of the request
+ * @param {string} userId The Id of the user to whom this request belongs
+ * @param {string} requestId The Id of the request to which the update applies
* @param {string} status The status for this history entry
* @param {string} updateText The updated text for the request (pass blank if no update)
+ * @return {number} 404 if the request is not found or does not belong to the given user, 204 if successful
*/
- addHistory: async (requestId, status, updateText) => {
- const asOf = Date.now()
+ addHistory: async (userId, requestId, status, updateText) => {
+ const req = retrieveRequest(requestId, userId)
+ if (req.rowCount === 0) {
+ return 404
+ }
await pool.query(`
INSERT INTO mpj.history
("requestId", "asOf", "status", "text")
VALUES
($1, $2, $3, NULLIF($4, ''))`,
- [ requestId, asOf, status, updateText ])
+ [ requestId, Date.now(), status, updateText ])
+ return 204
},
/**
@@ -68,6 +88,27 @@ export default function (pool) {
})
},
+ /**
+ * Add a note about a prayer request
+ * @param {string} userId The Id of the user to whom the request belongs
+ * @param {string} requestId The Id of the request to which the note applies
+ * @param {string} note The notes to add
+ * @return {number} 404 if the request is not found or does not belong to the given user, 204 if successful
+ */
+ addNote: async (userId, requestId, note) => {
+ const req = retrieveRequest(requestId, userId)
+ if (req.rowCount === 0) {
+ return 404
+ }
+ await pool.query(`
+ INSERT INTO mpj.note
+ ("requestId", "asOf", "notes")
+ VALUES
+ ($1, $2, $3)`,
+ [ requestId, Date.now(), note ])
+ return 204
+ },
+
/**
* Get all answered requests with their text as of the "Answered" status
* @param {string} userId The Id of the user for whom requests should be retrieved
@@ -101,12 +142,7 @@ export default function (pool) {
* @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 ])
+ const reqResults = await retrieveRequest(requestId, userId)
if (0 === reqResults.rowCount) {
return requestNotFound
}
@@ -126,7 +162,27 @@ export default function (pool) {
* @param {string} userId The Id of the user
* @return The requests that make up the current journal
*/
- journal: async userId => (await pool.query(`${journalSql} ORDER BY "asOf"`, [ userId ])).rows
+ journal: async userId => (await pool.query(`${journalSql} ORDER BY "asOf"`, [ userId ])).rows,
+
+ /**
+ * Get the notes for a request, most recent first
+ * @param {string} userId The Id of the user to whom the request belongs
+ * @param {string} requestId The Id of the request whose notes should be retrieved
+ * @return The notes for the request
+ */
+ notesById: async (userId, requestId) => {
+ const reqResults = await retrieveRequest(requestId, userId)
+ if (0 === reqResults.rowCount) {
+ return requestNotFound
+ }
+ const notes = await pool.query(`
+ SELECT "asOf", "notes"
+ FROM mpj.note
+ WHERE "requestId" = $1
+ ORDER BY "asOf" DESC`,
+ [ requestId ])
+ return notes.rows
+ }
}
}
diff --git a/src/api/src/routes/request.js b/src/api/src/routes/request.js
index 31126c2..9cd5ab0 100644
--- a/src/api/src/routes/request.js
+++ b/src/api/src/routes/request.js
@@ -16,8 +16,13 @@ export default function (checkJwt) {
// Add a request history entry (prayed, updated, answered, etc.)
.post('/:id/history', checkJwt, async (ctx, next) => {
const body = ctx.request.body
- await db.request.addHistory(ctx.params.id, body.status, body.updateText)
- ctx.response.status = 204
+ ctx.response.status = await db.request.addHistory(ctx.state.user.sub, ctx.params.id, body.status, body.updateText)
+ await next()
+ })
+ // Add a note to a request
+ .post('/:id/note', checkJwt, async (ctx, next) => {
+ const body = ctx.request.body
+ ctx.response.status = await db.request.addNote(ctx.state.user.sub, ctx.params.id, body.notes)
await next()
})
// Get a journal-style request by its Id
@@ -40,6 +45,17 @@ export default function (checkJwt) {
}
await next()
})
+ // Get the notes for a request
+ .get('/:id/notes', checkJwt, async (ctx, next) => {
+ const notes = await db.request.notesById(ctx.state.user.sub, ctx.params.id)
+ if (notes.text && 'Not Found' === notes.text) {
+ ctx.response.status = 404
+ } else {
+ ctx.body = notes
+ ctx.response.status = 200
+ }
+ await next()
+ })
.get('/answered', checkJwt, async (ctx, next) => {
ctx.body = await db.request.answered(ctx.state.user.sub)
ctx.response.status = 200
diff --git a/src/app/src/api/index.js b/src/app/src/api/index.js
index 4a78ca8..c35b9aa 100644
--- a/src/app/src/api/index.js
+++ b/src/app/src/api/index.js
@@ -25,6 +25,13 @@ export default {
*/
journal: () => http.get('journal/'),
+ /**
+ * Add a note for a prayer request
+ * @param {string} requestId The Id of the request to which the note applies
+ * @param {string} notes The notes to be added
+ */
+ addNote: (requestId, notes) => http.post(`request/${requestId}/note`, { notes }),
+
/**
* Add a new prayer request
* @param {string} requestText The text of the request to be added
@@ -55,6 +62,12 @@ export default {
/**
* Get all answered requests, along with the text they had when it was answered
*/
- getAnsweredRequests: () => http.get('request/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`)
}
diff --git a/src/app/src/components/Journal.vue b/src/app/src/components/Journal.vue
index 8efaca5..382f9c5 100644
--- a/src/app/src/components/Journal.vue
+++ b/src/app/src/components/Journal.vue
@@ -14,6 +14,8 @@ article
p.text-center(v-if='journal.length === 0'): em No requests found; click the "Add a New Request" button to add one
edit-request(:events='eventBus'
:toast='toast')
+ notes-edit(:events='eventBus'
+ :toast='toast')
full-request(:events='eventBus')
@@ -27,6 +29,7 @@ import chunk from 'lodash/chunk'
import EditRequest from './request/EditRequest'
import FullRequest from './request/FullRequest'
import NewRequest from './request/NewRequest'
+import NotesEdit from './request/NotesEdit'
import RequestListItem from './request/RequestListItem'
import actions from '@/store/action-types'
@@ -37,6 +40,7 @@ export default {
EditRequest,
FullRequest,
NewRequest,
+ NotesEdit,
RequestListItem
},
data () {
diff --git a/src/app/src/components/request/EditRequest.vue b/src/app/src/components/request/EditRequest.vue
index 7482f12..77e9544 100644
--- a/src/app/src/components/request/EditRequest.vue
+++ b/src/app/src/components/request/EditRequest.vue
@@ -1,31 +1,30 @@
-span
- b-modal(v-model='editVisible'
- header-bg-variant='mpj'
- header-text-variant='light'
- size='lg'
- title='Edit Prayer Request'
- @edit='openDialog()'
- @shows='focusRequestText')
- b-form
- b-form-group(label='Prayer Request'
- label-for='request_text')
- b-textarea#request_text(ref='toFocus'
- v-model='form.requestText'
- :rows='10'
- @blur='trimText()')
- b-form-group(label='Also Mark As')
- b-radio-group(v-model='form.status'
- buttons)
- b-radio(value='Updated') Updated
- b-radio(value='Prayed') Prayed
- b-radio(value='Answered') Answered
- div.w-100.text-right(slot='modal-footer')
- b-btn(variant='primary'
- @click='saveRequest()') Save
- |
- b-btn(variant='outline-secondary'
- @click='closeDialog()') Cancel
+b-modal(v-model='editVisible'
+ header-bg-variant='mpj'
+ header-text-variant='light'
+ size='lg'
+ title='Edit Prayer Request'
+ @edit='openDialog()'
+ @shows='focusRequestText')
+ b-form
+ b-form-group(label='Prayer Request'
+ label-for='request_text')
+ b-textarea#request_text(ref='toFocus'
+ v-model='form.requestText'
+ :rows='10'
+ @blur='trimText()')
+ b-form-group(label='Also Mark As')
+ b-radio-group(v-model='form.status'
+ buttons)
+ b-radio(value='Updated') Updated
+ b-radio(value='Prayed') Prayed
+ b-radio(value='Answered') Answered
+ div.w-100.text-right(slot='modal-footer')
+ b-btn(variant='primary'
+ @click='saveRequest()') Save
+ |
+ b-btn(variant='outline-secondary'
+ @click='closeDialog()') Cancel
diff --git a/src/app/src/components/request/RequestListItem.vue b/src/app/src/components/request/RequestListItem.vue
index 648a2e6..c284423 100644
--- a/src/app/src/components/request/RequestListItem.vue
+++ b/src/app/src/components/request/RequestListItem.vue
@@ -5,6 +5,11 @@ div
:key='request.requestId'
border-variant='dark'
no-body)
+ b-card-header.text-center.py-1.
+ #[b-btn(@click='markPrayed(idx)' variant='outline-primary' title='Pray' size='sm'): icon(name='check')]
+ #[b-btn(@click.stop='showEdit(request)' variant='outline-secondary' title='Edit' size='sm'): icon(name='pencil')]
+ #[b-btn(@click.stop='showNotes(request)' variant='outline-secondary' title='Add Notes' size='sm'): icon(name='file-text-o')]
+ #[b-btn(@click.stop='showFull(idx)' variant='outline-secondary' title='View Full Request' size='sm'): icon(name='search')]
b-card-body.p-0
p.card-text.mpj-request-text.mb-1.px-3.pt-3
| {{ request.text }}
@@ -12,11 +17,6 @@ div
= '(last activity '
date-from-now(:value='request.asOf')
| )
- b-card-footer.text-center.py-1.
- #[b-btn(@click='markPrayed(idx)' variant='outline-primary' title='Pray' size='sm'): icon(name='check')]
- #[b-btn(@click.stop='showEdit(request)' variant='outline-secondary' title='Edit' size='sm'): icon(name='pencil')]
- #[b-btn(disabled variant='outline-secondary' title='Add Notes' size='sm'): icon(name='file-text-o')]
- #[b-btn(@click.stop='showFull(idx)' variant='outline-secondary' title='View Full Request' size='sm'): icon(name='search')]
b-card(v-for='it in 3 - row.length')
br
@@ -48,6 +48,9 @@ export default {
},
showFull (idx) {
this.events.$emit('full', this.row[idx].requestId)
+ },
+ showNotes (request) {
+ this.events.$emit('notes', request)
}
}
}