Version 3
This commit is contained in:
@@ -1,3 +0,0 @@
|
||||
> 1%
|
||||
last 2 versions
|
||||
not ie <= 8
|
||||
@@ -1,17 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true
|
||||
},
|
||||
'extends': [
|
||||
'plugin:vue/essential',
|
||||
'@vue/standard'
|
||||
],
|
||||
rules: {
|
||||
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||
},
|
||||
parserOptions: {
|
||||
parser: 'babel-eslint'
|
||||
}
|
||||
}
|
||||
24
src/app/.gitignore
vendored
24
src/app/.gitignore
vendored
@@ -1,24 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw*
|
||||
|
||||
# Auth0 settings
|
||||
src/auth/auth0-variables.*
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Daniel J. Summers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,5 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"name": "my-prayer-journal",
|
||||
"version": "2.2.0",
|
||||
"description": "myPrayerJournal - Front End",
|
||||
"author": "Daniel J. Summers <daniel@bitbadger.solutions>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve --port 8081",
|
||||
"build": "vue-cli-service build --modern",
|
||||
"lint": "vue-cli-service lint",
|
||||
"apistart": "cd ../MyPrayerJournal.Api && dotnet run",
|
||||
"vue": "vue-cli-service build --modern && cd ../MyPrayerJournal.Api && dotnet run",
|
||||
"publish": "vue-cli-service build --modern && cd ../MyPrayerJournal.Api && dotnet publish -c Release -r linux-x64 --self-contained false"
|
||||
},
|
||||
"dependencies": {
|
||||
"auth0-js": "^9.13.2",
|
||||
"axios": "^0.21.1",
|
||||
"moment": "^2.18.1",
|
||||
"vue": "^2.5.15",
|
||||
"vue-material": "^1.0.0-beta-13",
|
||||
"vue-router": "^3.0.0",
|
||||
"vuex": "^3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "^3.0.0",
|
||||
"@vue/cli-plugin-eslint": "^3.0.0",
|
||||
"@vue/cli-service": "^3.0.0",
|
||||
"@vue/eslint-config-standard": "^4.0.0",
|
||||
"node-sass": "^4.12.0",
|
||||
"pug": "^3.0.1",
|
||||
"pug-plain-loader": "^1.0.0",
|
||||
"sass-loader": "^7.3.1",
|
||||
"vue-template-compiler": "^2.5.17",
|
||||
"webpack-bundle-analyzer": "^3.4.1"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,19 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="preload" as="style">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<title>myPrayerJournal</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but newapp doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,165 +0,0 @@
|
||||
<template lang="pug">
|
||||
#app.page-container
|
||||
md-app(md-waterfall md-mode='fixed-last' role='application')
|
||||
md-app-toolbar.md-large.md-dense.md-primary
|
||||
md-progress-bar(v-if='progress.visible'
|
||||
:md-mode='progress.mode')
|
||||
.md-no-progress-bar(v-if='!progress.visible')
|
||||
.md-toolbar-row
|
||||
.md-toolbar-section-start
|
||||
router-link(to='/').md-title
|
||||
span(style='font-weight:100;') my
|
||||
span(style='font-weight:400;') Prayer
|
||||
span(style='font-weight:700;') Journal
|
||||
navigation
|
||||
md-app-content
|
||||
router-view
|
||||
md-snackbar(:md-active.sync='snackbar.visible'
|
||||
md-position='center'
|
||||
:md-duration='snackbar.interval'
|
||||
ref='snackbar') {{ snackbar.message }}
|
||||
footer
|
||||
p.mpj-muted-text.mpj-text-right
|
||||
| myPrayerJournal v{{ version }}
|
||||
br
|
||||
em: small.
|
||||
#[router-link(to='/legal/privacy-policy') Privacy Policy] •
|
||||
#[router-link(to='/legal/terms-of-service') Terms of Service] •
|
||||
#[a(href='https://github.com/bit-badger/myprayerjournal' target='_blank') Developed] and hosted by
|
||||
#[a(href='https://bitbadger.solutions' target='_blank') Bit Badger Solutions]
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import Vue from 'vue'
|
||||
|
||||
import Navigation from '@/components/common/Navigation'
|
||||
|
||||
import actions from '@/store/action-types'
|
||||
import { version } from '../package.json'
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
Navigation
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
progress: {
|
||||
events: new Vue(),
|
||||
visible: false,
|
||||
mode: 'query'
|
||||
},
|
||||
snackbar: {
|
||||
events: new Vue(),
|
||||
visible: false,
|
||||
message: '',
|
||||
interval: 4000
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
this.progress.events.$on('show', this.showProgress)
|
||||
this.progress.events.$on('done', this.hideProgress)
|
||||
this.snackbar.events.$on('info', this.showInfo)
|
||||
this.snackbar.events.$on('error', this.showError)
|
||||
await this.$store.dispatch(actions.CHECK_AUTHENTICATION)
|
||||
},
|
||||
computed: {
|
||||
version () {
|
||||
return version.endsWith('.0')
|
||||
? version.endsWith('.0.0')
|
||||
? version.substr(0, version.length - 4)
|
||||
: version.substr(0, version.length - 2)
|
||||
: version
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
showSnackbar (message) {
|
||||
this.snackbar.message = message
|
||||
this.snackbar.visible = true
|
||||
},
|
||||
showInfo (message) {
|
||||
this.snackbar.interval = 4000
|
||||
this.showSnackbar(message)
|
||||
},
|
||||
showError (message) {
|
||||
this.snackbar.interval = Infinity
|
||||
this.showSnackbar(message)
|
||||
},
|
||||
showProgress (mode) {
|
||||
this.progress.mode = mode
|
||||
this.progress.visible = true
|
||||
},
|
||||
hideProgress () {
|
||||
this.progress.visible = false
|
||||
},
|
||||
handleLoginEvent (data) {
|
||||
if (!data.loggedIn) {
|
||||
this.showInfo('Logged out successfully')
|
||||
}
|
||||
}
|
||||
},
|
||||
provide () {
|
||||
return {
|
||||
messages: this.snackbar.events,
|
||||
progress: this.progress.events
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
@import "~vue-material/dist/theme/engine"
|
||||
@include md-register-theme("default", (primary: md-get-palette-color(green, 800), accent: md-get-palette-color(gray, 700)))
|
||||
@import "~vue-material/dist/theme/all"
|
||||
|
||||
html, body
|
||||
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
|
||||
font-size: 1rem
|
||||
p
|
||||
margin-bottom: 0
|
||||
footer
|
||||
border-top: solid 1px lightgray
|
||||
margin: 1rem -1rem 0
|
||||
padding: 0 1rem
|
||||
footer p
|
||||
margin: 0
|
||||
.mpj-full-page-card
|
||||
font-size: 1rem
|
||||
line-height: 1.25rem
|
||||
.mpj-main-content
|
||||
max-width: 60rem
|
||||
margin: auto
|
||||
.mpj-request-text
|
||||
white-space: pre-line
|
||||
p.mpj-request-text
|
||||
margin-top: 0
|
||||
.mpj-text-center
|
||||
text-align: center
|
||||
.mpj-text-nowrap
|
||||
white-space: nowrap
|
||||
.mpj-text-right
|
||||
text-align: right
|
||||
.mpj-muted-text
|
||||
color: rgba(0, 0, 0, .6)
|
||||
.mpj-valign-top
|
||||
vertical-align: top
|
||||
.mpj-narrow
|
||||
max-width: 40rem
|
||||
margin: auto
|
||||
.mpj-skinny
|
||||
max-width: 20rem
|
||||
margin: auto
|
||||
.mpj-full-width
|
||||
width: 100%
|
||||
.md-toolbar > .md-progress-bar
|
||||
height: 2px
|
||||
width: 100%
|
||||
background-color: rgba(255, 255, 255, .8) !important
|
||||
margin: 0
|
||||
.md-toolbar > .md-no-progress-bar
|
||||
height: 2px
|
||||
width: 100%
|
||||
</style>
|
||||
@@ -1,98 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
import axios from 'axios'
|
||||
|
||||
const http = axios.create({
|
||||
baseURL: `${location.protocol}//${location.host}/api/`
|
||||
})
|
||||
|
||||
/**
|
||||
* API access for myPrayerJournal
|
||||
*/
|
||||
export default {
|
||||
|
||||
/**
|
||||
* Set the bearer token for all future requests
|
||||
* @param {string} token The token to use to identify the user to the server
|
||||
*/
|
||||
setBearer: token => { http.defaults.headers.common.Authorization = `Bearer ${token}` },
|
||||
|
||||
/**
|
||||
* Remove the bearer token
|
||||
*/
|
||||
removeBearer: () => delete http.defaults.headers.common.Authorization,
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {string} recurType The type of recurrence for this request
|
||||
* @param {number} recurCount The number of intervals of recurrence
|
||||
*/
|
||||
addRequest: (requestText, recurType, recurCount) => http.post('request', { requestText, recurType, recurCount }),
|
||||
|
||||
/**
|
||||
* Get all answered requests, along with the text they had when it was answered
|
||||
*/
|
||||
getAnsweredRequests: () => http.get('requests/answered'),
|
||||
|
||||
/**
|
||||
* Get a prayer request (full; includes all history and notes)
|
||||
* @param {string} requestId The Id of the request to retrieve
|
||||
*/
|
||||
getFullRequest: requestId => http.get(`request/${requestId}/full`),
|
||||
|
||||
/**
|
||||
* 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`),
|
||||
|
||||
/**
|
||||
* 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 all prayer requests and their most recent updates
|
||||
*/
|
||||
journal: () => http.get('journal'),
|
||||
|
||||
/**
|
||||
* Show a request after the given date (used for "show now")
|
||||
* @param {string} requestId The ID of the request which should be shown
|
||||
* @param {number} showAfter The ticks after which the request should be shown
|
||||
*/
|
||||
showRequest: (requestId, showAfter) => http.patch(`request/${requestId}/show`, { showAfter }),
|
||||
|
||||
/**
|
||||
* Snooze a request until the given time
|
||||
* @param {string} requestId The ID of the prayer request to be snoozed
|
||||
* @param {number} until The ticks until which the request should be snoozed
|
||||
*/
|
||||
snoozeRequest: (requestId, until) => http.patch(`request/${requestId}/snooze`, { until }),
|
||||
|
||||
/**
|
||||
* Update recurrence for a prayer request
|
||||
* @param {string} requestId The ID of the prayer request for which recurrence is being updated
|
||||
* @param {string} recurType The type of recurrence to set
|
||||
* @param {number} recurCount The number of recurrence intervals to set
|
||||
*/
|
||||
updateRecurrence: (requestId, recurType, recurCount) =>
|
||||
http.patch(`request/${requestId}/recurrence`, { recurType, recurCount }),
|
||||
|
||||
/**
|
||||
* Update a prayer request
|
||||
* @param {string} requestId The ID of the request to be updated
|
||||
* @param {string} status The status of the update
|
||||
* @param {string} updateText The text of the update (optional)
|
||||
*/
|
||||
updateRequest: (requestId, status, updateText) => http.post(`request/${requestId}/history`, { status, updateText })
|
||||
}
|
||||
@@ -1,194 +0,0 @@
|
||||
'use strict'
|
||||
/* eslint-disable */
|
||||
import auth0 from 'auth0-js'
|
||||
import EventEmitter from 'events'
|
||||
|
||||
import AUTH_CONFIG from './auth0-variables'
|
||||
import mutations from '@/store/mutation-types'
|
||||
/* es-lint-enable*/
|
||||
|
||||
// Auth0 web authentication instance to use for our calls
|
||||
const webAuth = new auth0.WebAuth({
|
||||
domain: AUTH_CONFIG.domain,
|
||||
clientID: AUTH_CONFIG.clientId,
|
||||
redirectUri: AUTH_CONFIG.appDomain + AUTH_CONFIG.callbackUrl,
|
||||
audience: `https://${AUTH_CONFIG.domain}/userinfo`,
|
||||
responseType: 'token id_token',
|
||||
scope: 'openid profile email'
|
||||
})
|
||||
|
||||
/**
|
||||
* A class to handle all authentication calls and determinations
|
||||
*/
|
||||
class AuthService extends EventEmitter {
|
||||
|
||||
// Local storage key for our session data
|
||||
AUTH_SESSION = 'auth-session'
|
||||
|
||||
// Received and calculated values for our ssesion (initially loaded from local storage if present)
|
||||
session = {}
|
||||
|
||||
constructor() {
|
||||
super()
|
||||
this.refreshSession()
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the user log in flow
|
||||
*/
|
||||
login (customState) {
|
||||
webAuth.authorize({
|
||||
appState: customState
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Promisified parseHash function
|
||||
*/
|
||||
parseHash () {
|
||||
return new Promise((resolve, reject) => {
|
||||
webAuth.parseHash((err, authResult) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
resolve(authResult)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle authentication replies from Auth0
|
||||
*
|
||||
* @param store The Vuex store
|
||||
*/
|
||||
async handleAuthentication (store) {
|
||||
try {
|
||||
const authResult = await this.parseHash()
|
||||
if (authResult && authResult.accessToken && authResult.idToken) {
|
||||
this.setSession(authResult)
|
||||
store.commit(mutations.USER_LOGGED_ON, this.session.profile)
|
||||
}
|
||||
} catch(err) {
|
||||
console.error(err)
|
||||
alert(`Error: ${err.error}. Check the console for further details.`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the session and commit it to local storage
|
||||
*
|
||||
* @param authResult The authorization result
|
||||
*/
|
||||
setSession (authResult) {
|
||||
this.session.profile = authResult.idTokenPayload
|
||||
this.session.id.token = authResult.idToken
|
||||
this.session.id.expiry = this.session.profile.exp * 1000
|
||||
this.session.access.token = authResult.accessToken
|
||||
this.session.access.expiry = authResult.expiresIn * 1000 + Date.now()
|
||||
|
||||
localStorage.setItem(this.AUTH_SESSION, JSON.stringify(this.session))
|
||||
|
||||
this.emit('loginEvent', {
|
||||
loggedIn: true,
|
||||
profile: authResult.idTokenPayload,
|
||||
state: authResult.appState || {}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh this instance's session from the one in local storage
|
||||
*/
|
||||
refreshSession () {
|
||||
this.session =
|
||||
localStorage.getItem(this.AUTH_SESSION)
|
||||
? JSON.parse(localStorage.getItem(this.AUTH_SESSION))
|
||||
: { profile: {},
|
||||
id: {
|
||||
token: null,
|
||||
expiry: null
|
||||
},
|
||||
access: {
|
||||
token: null,
|
||||
expiry: null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renew authorzation tokens with Auth0
|
||||
*/
|
||||
renewTokens () {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.refreshSession()
|
||||
if (this.session.id.token !== null) {
|
||||
webAuth.checkSession({}, (err, authResult) => {
|
||||
if (err) {
|
||||
reject(err)
|
||||
} else {
|
||||
this.setSession(authResult)
|
||||
resolve(authResult)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
reject('Not logged in')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Log out of myPrayerJournal
|
||||
*
|
||||
* @param store The Vuex store
|
||||
*/
|
||||
logout (store) {
|
||||
// Clear access token and ID token from local storage
|
||||
localStorage.removeItem(this.AUTH_SESSION)
|
||||
this.refreshSession()
|
||||
|
||||
store.commit(mutations.USER_LOGGED_OFF)
|
||||
|
||||
webAuth.logout({
|
||||
returnTo: `${AUTH_CONFIG.appDomain}/`,
|
||||
clientID: AUTH_CONFIG.clientId
|
||||
})
|
||||
this.emit('loginEvent', { loggedIn: false })
|
||||
}
|
||||
|
||||
/**
|
||||
* Check expiration for a token (the way it's stored in the session)
|
||||
*/
|
||||
checkExpiry = (it) => it.token && it.expiry && Date.now() < it.expiry
|
||||
|
||||
/**
|
||||
* Is there a user authenticated?
|
||||
*/
|
||||
isAuthenticated () {
|
||||
return this.checkExpiry(this.session.id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current access token valid?
|
||||
*/
|
||||
isAccessTokenValid () {
|
||||
return this.checkExpiry(this.session.access)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user's access token, renewing it if required
|
||||
*/
|
||||
async getAccessToken () {
|
||||
if (this.isAccessTokenValid()) {
|
||||
return this.session.access.token
|
||||
} else {
|
||||
try {
|
||||
const authResult = await this.renewTokens()
|
||||
return authResult.accessToken
|
||||
} catch (reject) {
|
||||
throw reject
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default new AuthService()
|
||||
@@ -1,22 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-content(role='main').mpj-main-content
|
||||
page-title(title='Welcome!'
|
||||
hideOnPage=true)
|
||||
p
|
||||
p.
|
||||
myPrayerJournal is a place where individuals can record their prayer requests, record that they prayed for them,
|
||||
update them as God moves in the situation, and record a final answer received on that request. It also allows
|
||||
individuals to review their answered prayers.
|
||||
p.
|
||||
This site is open and available to the general public. To get started, simply click the “Log On” link
|
||||
above, and log on with either a Microsoft or Google account. You can also learn more about the site at the
|
||||
“Docs” link, also above.
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
export default {
|
||||
name: 'home'
|
||||
}
|
||||
</script>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-content(role='main').mpj-main-content-wide
|
||||
page-title(:title='title')
|
||||
p(v-if='isLoadingJournal') Loading your prayer journal...
|
||||
template(v-else)
|
||||
md-empty-state(v-if='journal.length === 0'
|
||||
md-icon='done_all'
|
||||
md-label='No Requests to Show'
|
||||
md-description='You have no requests to be shown; see the “Active” link above for snoozed/deferred requests, and the “Answered” link for answered requests')
|
||||
md-button(:to="{ name: 'EditRequest', params: { id: 'new' } }").md-primary.md-raised Add a New Request
|
||||
template(v-else)
|
||||
.mpj-text-center
|
||||
md-button(:to="{ name: 'EditRequest', params: { id: 'new' } }"
|
||||
role='button').md-raised.md-accent #[md-icon add_box] Add a New Request
|
||||
br
|
||||
.mpj-journal
|
||||
request-card(v-for='request in journal'
|
||||
:key='request.requestId'
|
||||
:request='request')
|
||||
notes-edit
|
||||
snooze-request
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import Vue from 'vue'
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import NotesEdit from './request/NotesEdit'
|
||||
import RequestCard from './request/RequestCard'
|
||||
import SnoozeRequest from './request/SnoozeRequest'
|
||||
|
||||
import actions from '@/store/action-types'
|
||||
|
||||
export default {
|
||||
name: 'journal',
|
||||
inject: [
|
||||
'messages',
|
||||
'progress'
|
||||
],
|
||||
components: {
|
||||
NotesEdit,
|
||||
RequestCard,
|
||||
SnoozeRequest
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
eventBus: new Vue()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
title () {
|
||||
return `${this.user.given_name}’s Prayer Journal`
|
||||
},
|
||||
snackbar () {
|
||||
return this.$parent.$refs.snackbar
|
||||
},
|
||||
...mapState(['user', 'journal', 'isLoadingJournal'])
|
||||
},
|
||||
async created () {
|
||||
await this.$store.dispatch(actions.LOAD_JOURNAL, this.progress)
|
||||
this.messages.$emit('info', `Loaded ${this.journal.length} prayer requests`)
|
||||
},
|
||||
provide () {
|
||||
return {
|
||||
journalEvents: this.eventBus
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.mpj-journal
|
||||
display: flex
|
||||
flex-flow: row wrap
|
||||
justify-content: center
|
||||
align-items: flex-start
|
||||
.mpj-dialog-content
|
||||
padding: 0 1rem
|
||||
</style>
|
||||
@@ -1,55 +0,0 @@
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
name: 'date-from-now',
|
||||
props: {
|
||||
tag: {
|
||||
type: String,
|
||||
default: 'span'
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
interval: {
|
||||
type: Number,
|
||||
default: 10000
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
fromNow: moment(this.value).fromNow(),
|
||||
intervalId: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
actual () {
|
||||
return moment(this.value).format('LLLL')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.intervalId = setInterval(this.updateFromNow, this.interval)
|
||||
this.$watch('value', this.updateFromNow)
|
||||
},
|
||||
beforeDestroy () {
|
||||
clearInterval(this.intervalId)
|
||||
},
|
||||
methods: {
|
||||
updateFromNow () {
|
||||
let newFromNow = moment(this.value).fromNow()
|
||||
if (newFromNow !== this.fromNow) this.fromNow = newFromNow
|
||||
}
|
||||
},
|
||||
render (createElement) {
|
||||
return createElement(this.tag, {
|
||||
domProps: {
|
||||
title: this.actual,
|
||||
innerText: this.fromNow
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,59 +0,0 @@
|
||||
<template lang="pug">
|
||||
.md-toolbar-row
|
||||
md-tabs(md-sync-route).md-primary
|
||||
template(v-if='isAuthenticated')
|
||||
md-tab(md-label='Journal'
|
||||
to='/journal')
|
||||
md-tab(md-label='Active'
|
||||
to='/requests/active')
|
||||
md-tab(v-if='hasSnoozed'
|
||||
md-label='Snoozed'
|
||||
to='/requests/snoozed')
|
||||
md-tab(md-label='Answered'
|
||||
to='/requests/answered')
|
||||
md-tab(md-label='Log Off'
|
||||
href='/user/log-off'
|
||||
@click.prevent='logOff()')
|
||||
md-tab(md-label='Docs'
|
||||
href='https://docs.prayerjournal.me'
|
||||
@click.prevent='showHelp()')
|
||||
template(v-else)
|
||||
md-tab(md-label='Log On'
|
||||
href='/user/log-on'
|
||||
@click.prevent='logOn()')
|
||||
md-tab(md-label='Docs'
|
||||
href='https://docs.prayerjournal.me'
|
||||
@click.prevent='showHelp()')
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
export default {
|
||||
name: 'navigation',
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
hasSnoozed () {
|
||||
return this.isAuthenticated &&
|
||||
Array.isArray(this.journal) &&
|
||||
this.journal.filter(req => req.snoozedUntil > Date.now()).length > 0
|
||||
},
|
||||
...mapState([ 'isAuthenticated', 'journal' ])
|
||||
},
|
||||
methods: {
|
||||
logOn () {
|
||||
this.$auth.login()
|
||||
},
|
||||
logOff () {
|
||||
this.$auth.logout(this.$store, this.$router)
|
||||
},
|
||||
showHelp () {
|
||||
window.open('https://docs.prayerjournal.me', '_blank')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,28 +0,0 @@
|
||||
<template lang="pug">
|
||||
h1(v-if='!hideOnPage'
|
||||
v-html='title').md-title
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'page-title',
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
hideOnPage: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
title () {
|
||||
document.title = `${this.title.replace('’', "'")} « myPrayerJournal`
|
||||
}
|
||||
},
|
||||
created () {
|
||||
document.title = `${this.title.replace('’', "'")} « myPrayerJournal`
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,59 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-content(role='main').mpj-main-content
|
||||
page-title(title='Privacy Policy'
|
||||
hide-on-page=true)
|
||||
md-card
|
||||
md-card-header
|
||||
.md-title Privacy Policy
|
||||
.md-subhead as of May 21, 2018
|
||||
md-card-content.mpj-full-page-card
|
||||
p.
|
||||
The nature of the service is one where privacy is a must. The items below will help you understand the data we
|
||||
collect, access, and store on your behalf as you use this service.
|
||||
hr
|
||||
h3 Third Party Services
|
||||
p.
|
||||
myPrayerJournal utilizes a third-party authentication and identity provider. You should familiarize yourself
|
||||
with the privacy policy for #[a(href='https://auth0.com/privacy' target='_blank') Auth0], as well as your
|
||||
chosen provider (#[a(href='https://privacy.microsoft.com/en-us/privacystatement' target='_blank') Microsoft] or
|
||||
#[a(href='https://policies.google.com/privacy' target='_blank') Google]).
|
||||
hr
|
||||
h3 What We Collect
|
||||
h4 Identifying Data
|
||||
ul
|
||||
li.
|
||||
The only identifying data myPrayerJournal stores is the subscriber (“sub”) field from the token we
|
||||
receive from Auth0, once you have signed in through their hosted service. All information is associated with
|
||||
you via this field.
|
||||
li.
|
||||
While you are signed in, within your browser, the service has access to your first and last names, along with
|
||||
a URL to the profile picture (provided by your selected identity provider). This information is not
|
||||
transmitted to the server, and is removed when “Log Off” is clicked.
|
||||
h4 User Provided Data
|
||||
ul
|
||||
li.
|
||||
myPrayerJournal stores the information you provide, including the text of prayer requests, updates, and notes;
|
||||
and the date/time when certain actions are taken.
|
||||
hr
|
||||
h3 How Your Data Is Accessed / Secured
|
||||
ul
|
||||
li.
|
||||
Your provided data is returned to you, as required, to display your journal or your answered requests. On the
|
||||
server, it is stored in a controlled-access database.
|
||||
li.
|
||||
Your data is backed up, along with other Bit Badger Solutions hosted systems, in a rolling manner; backups are
|
||||
preserved for the prior 7 days, and backups from the 1st and 15th are preserved for 3 months. These backups
|
||||
are stored in a private cloud data repository.
|
||||
li.
|
||||
The data collected and stored is the absolute minimum necessary for the functionality of the service. There
|
||||
are no plans to “monetize” this service, and storing the minimum amount of information means that
|
||||
the data we have is not interesting to purchasers (or those who may have more nefarious purposes).
|
||||
li Access to servers and backups is strictly controlled and monitored for unauthorized access attempts.
|
||||
hr
|
||||
h3 Removing Your Data
|
||||
p.
|
||||
At any time, you may choose to discontinue using this service. Both Microsoft and Google provide ways to revoke
|
||||
access from this application. However, if you want your data removed from the database, please contact daniel at
|
||||
bitbadger.solutions (via e-mail, replacing at with @) prior to doing so, to ensure we can determine which
|
||||
subscriber ID belongs to you.
|
||||
</template>
|
||||
@@ -1,40 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-content(role='main').mpj-main-content
|
||||
page-title(title='Terms of Service'
|
||||
hide-on-page=true)
|
||||
md-card
|
||||
md-card-header
|
||||
.md-title Terms of Service
|
||||
.md-subhead as of May 21, 2018
|
||||
md-card-content.mpj-full-page-card
|
||||
h3 1. Acceptance of Terms
|
||||
p.
|
||||
By accessing this web site, you are agreeing to be bound by these Terms and Conditions, and that you are
|
||||
responsible to ensure that your use of this site complies with all applicable laws. Your continued use of this
|
||||
site implies your acceptance of these terms.
|
||||
h3 2. Description of Service and Registration
|
||||
p.
|
||||
myPrayerJournal is a service that allows individuals to enter and amend their prayer requests. It requires no
|
||||
registration by itself, but access is granted based on a successful login with an external identity provider.
|
||||
See #[router-link(:to="{ name: 'PrivacyPolicy' }") our privacy policy] for details on how that information is
|
||||
accessed and stored.
|
||||
h3 3. Third Party Services
|
||||
p.
|
||||
This service utilizes a third-party service provider for identity management. Review the terms of service for
|
||||
#[a(href='https://auth0.com/terms' target='_blank') Auth0], as well as those for the selected authorization
|
||||
provider (#[a(href='https://www.microsoft.com/en-us/servicesagreement' target='_blank') Microsoft] or
|
||||
#[a(href='https://policies.google.com/terms' target='_blank') Google]).
|
||||
h3 4. Liability
|
||||
p.
|
||||
This service is provided "as is", and no warranty (express or implied) exists. The service and its developers
|
||||
may not be held liable for any damages that may arise through the use of this service.
|
||||
h3 5. Updates to Terms
|
||||
p.
|
||||
These terms and conditions may be updated at any time, and this service does not have the capability to notify
|
||||
users when these change. The date at the top of the page will be updated when any of the text of these terms is
|
||||
updated.
|
||||
hr
|
||||
p.
|
||||
You may also wish to review our #[router-link(:to="{ name: 'PrivacyPolicy' }") privacy policy] to learn how we
|
||||
handle your data.
|
||||
</template>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-content(role='main').mpj-main-content
|
||||
page-title(title='Active Requests'
|
||||
hide-on-page=true)
|
||||
template(v-if='loaded')
|
||||
md-empty-state(v-if='requests.length === 0'
|
||||
md-icon='sentiment_dissatisfied'
|
||||
md-label='No Active Requests'
|
||||
md-description='Your prayer journal has no active requests')
|
||||
md-button(to='/journal').md-primary.md-raised Return to your journal
|
||||
request-list(v-if='requests.length !== 0'
|
||||
title='Active Requests'
|
||||
:requests='requests')
|
||||
p(v-else) Loading journal...
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import RequestList from '@/components/request/RequestList'
|
||||
|
||||
import actions from '@/store/action-types'
|
||||
|
||||
export default {
|
||||
name: 'active-requests',
|
||||
inject: ['progress'],
|
||||
components: {
|
||||
RequestList
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
requests: [],
|
||||
loaded: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['journal', 'isLoadingJournal'])
|
||||
},
|
||||
created () {
|
||||
this.$on('requestUnsnoozed', this.ensureJournal)
|
||||
this.$on('requestNowShown', this.ensureJournal)
|
||||
},
|
||||
methods: {
|
||||
async ensureJournal () {
|
||||
if (!Array.isArray(this.journal)) {
|
||||
this.loaded = false
|
||||
await this.$store.dispatch(actions.LOAD_JOURNAL, this.progress)
|
||||
}
|
||||
this.requests = this.journal
|
||||
.sort((a, b) => a.showAfter - b.showAfter)
|
||||
this.loaded = true
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
await this.ensureJournal()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,53 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-content(role='main').mpj-main-content
|
||||
page-title(title='Answered Requests'
|
||||
hide-on-page=true)
|
||||
template(v-if='loaded')
|
||||
md-empty-state(v-if='requests.length === 0'
|
||||
md-icon='sentiment_dissatisfied'
|
||||
md-label='No Answered Requests'
|
||||
md-description='Your prayer journal has no answered requests; once you have marked one as “Answered”, it will appear here')
|
||||
request-list(v-if='requests.length !== 0'
|
||||
title='Answered Requests'
|
||||
:requests='requests')
|
||||
p(v-else) Loading answered requests...
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import api from '@/api'
|
||||
|
||||
import RequestList from '@/components/request/RequestList'
|
||||
|
||||
export default {
|
||||
name: 'answered-requests',
|
||||
inject: [
|
||||
'messages',
|
||||
'progress'
|
||||
],
|
||||
components: {
|
||||
RequestList
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
requests: [],
|
||||
loaded: false
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
this.progress.$emit('show', 'query')
|
||||
try {
|
||||
const reqs = await api.getAnsweredRequests()
|
||||
this.requests = reqs.data
|
||||
this.progress.$emit('done')
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
this.messages.$emit('error', 'Error loading requests; check console for details')
|
||||
this.progress.$emit('done')
|
||||
} finally {
|
||||
this.loaded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,174 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-content(role='main').mpj-narrow
|
||||
page-title(:title='title')
|
||||
md-field
|
||||
label(for='request_text') Prayer Request
|
||||
md-textarea(v-model='form.requestText'
|
||||
@blur='trimText()'
|
||||
md-autogrow
|
||||
autofocus).mpj-full-width
|
||||
br
|
||||
template(v-if='!isNew')
|
||||
label Also Mark As
|
||||
br
|
||||
md-radio(v-model='form.status'
|
||||
value='Updated') Updated
|
||||
md-radio(v-model='form.status'
|
||||
value='Prayed') Prayed
|
||||
md-radio(v-model='form.status'
|
||||
value='Answered') Answered
|
||||
br
|
||||
label Recurrence
|
||||
|
|
||||
em.mpj-muted-text After prayer, request reappears...
|
||||
br
|
||||
.md-layout
|
||||
.md-layout-item.md-size-30
|
||||
md-radio(v-model='form.recur.typ'
|
||||
value='Immediate') Immediately
|
||||
.md-layout-item.md-size-20
|
||||
md-radio(v-model='form.recur.typ'
|
||||
value='other') Every...
|
||||
.md-layout-item.md-size-10
|
||||
md-field(md-inline)
|
||||
label Count
|
||||
md-input(v-model='form.recur.count'
|
||||
type='number'
|
||||
:disabled='!showRecurrence')
|
||||
.md-layout-item.md-size-20
|
||||
md-field
|
||||
label Interval
|
||||
md-select(v-model='form.recur.other'
|
||||
:disabled='!showRecurrence')
|
||||
md-option(value='Hours') hours
|
||||
md-option(value='Days') days
|
||||
md-option(value='Weeks') weeks
|
||||
.mpj-text-right
|
||||
md-button(:disabled='!isValidRecurrence'
|
||||
@click.stop='saveRequest()').md-primary.md-raised #[md-icon save] Save
|
||||
md-button(@click.stop='goBack()').md-raised #[md-icon arrow_back] Cancel
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import actions from '@/store/action-types'
|
||||
|
||||
export default {
|
||||
name: 'edit-request',
|
||||
inject: [
|
||||
'messages',
|
||||
'progress'
|
||||
],
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: 'Edit Prayer Request',
|
||||
isNew: false,
|
||||
form: {
|
||||
requestId: '',
|
||||
requestText: '',
|
||||
status: 'Updated',
|
||||
recur: {
|
||||
typ: 'Immediate',
|
||||
other: '',
|
||||
count: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
isValidRecurrence () {
|
||||
if (this.form.recur.typ === 'Immediate') return true
|
||||
const count = Number.parseInt(this.form.recur.count)
|
||||
if (isNaN(count) || this.form.recur.other === '') return false
|
||||
if (this.form.recur.other === 'Hours' && count > (365 * 24)) return false
|
||||
if (this.form.recur.other === 'Days' && count > 365) return false
|
||||
if (this.form.recur.other === 'Weeks' && count > 52) return false
|
||||
return true
|
||||
},
|
||||
showRecurrence () {
|
||||
return this.form.recur.typ !== 'Immediate'
|
||||
},
|
||||
...mapState(['journal'])
|
||||
},
|
||||
async mounted () {
|
||||
await this.ensureJournal()
|
||||
if (this.id === 'new') {
|
||||
this.title = 'Add Prayer Request'
|
||||
this.isNew = true
|
||||
this.form.requestId = ''
|
||||
this.form.requestText = ''
|
||||
this.form.status = 'Created'
|
||||
this.form.recur.typ = 'Immediate'
|
||||
this.form.recur.other = ''
|
||||
this.form.recur.count = ''
|
||||
} else {
|
||||
this.title = 'Edit Prayer Request'
|
||||
this.isNew = false
|
||||
if (this.journal.length === 0) {
|
||||
await this.$store.dispatch(actions.LOAD_JOURNAL, this.progress)
|
||||
}
|
||||
const req = this.journal.filter(r => r.requestId === this.id)[0]
|
||||
this.form.requestId = this.id
|
||||
this.form.requestText = req.text
|
||||
this.form.status = 'Updated'
|
||||
if (req.recurType === 'Immediate') {
|
||||
this.form.recur.typ = 'Immediate'
|
||||
this.form.recur.other = ''
|
||||
this.form.recur.count = ''
|
||||
} else {
|
||||
this.form.recur.typ = 'other'
|
||||
this.form.recur.other = req.recurType
|
||||
this.form.recur.count = req.recurCount
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goBack () {
|
||||
this.$router.go(-1)
|
||||
},
|
||||
trimText () {
|
||||
this.form.requestText = this.form.requestText.trim()
|
||||
},
|
||||
async ensureJournal () {
|
||||
if (!Array.isArray(this.journal)) {
|
||||
await this.$store.dispatch(actions.LOAD_JOURNAL, this.progress)
|
||||
}
|
||||
},
|
||||
async saveRequest () {
|
||||
if (this.isNew) {
|
||||
await this.$store.dispatch(actions.ADD_REQUEST, {
|
||||
progress: this.progress,
|
||||
requestText: this.form.requestText,
|
||||
recurType: this.form.recur.typ === 'Immediate' ? 'Immediate' : this.form.recur.other,
|
||||
recurCount: this.form.recur.typ === 'Immediate' ? 0 : Number.parseInt(this.form.recur.count)
|
||||
})
|
||||
this.messages.$emit('info', 'New prayer request added')
|
||||
} else {
|
||||
await this.$store.dispatch(actions.UPDATE_REQUEST, {
|
||||
progress: this.progress,
|
||||
requestId: this.form.requestId,
|
||||
updateText: this.form.requestText,
|
||||
status: this.form.status,
|
||||
recurType: this.form.recur.typ === 'Immediate' ? 'Immediate' : this.form.recur.other,
|
||||
recurCount: this.form.recur.typ === 'Immediate' ? 0 : Number.parseInt(this.form.recur.count)
|
||||
})
|
||||
if (this.form.status === 'Answered') {
|
||||
this.messages.$emit('info', 'Request updated and removed from active journal')
|
||||
} else {
|
||||
this.messages.$emit('info', 'Request updated')
|
||||
}
|
||||
}
|
||||
this.goBack()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,94 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-content(role='main').mpj-main-content
|
||||
page-title(title='Full Prayer Request'
|
||||
hide-on-page=true)
|
||||
md-card(v-if='request')
|
||||
md-card-header
|
||||
.md-title Full Prayer Request
|
||||
.md-subhead
|
||||
span(v-if='isAnswered') Answered {{ formatDate(answered) }} (#[date-from-now(:value='answered')]) !{' • '}
|
||||
| Prayed {{ prayedCount }} times • Open {{ openDays }} days
|
||||
md-card-content.mpj-full-page-card
|
||||
p.mpj-request-text {{ lastText }}
|
||||
md-table
|
||||
md-table-row
|
||||
md-table-head Action
|
||||
md-table-head Update / Notes
|
||||
md-table-row(v-for='item in log'
|
||||
:key='item.asOf')
|
||||
md-table-cell.mpj-valign-top {{ item.status }} on #[span.mpj-text-nowrap {{ formatDate(item.asOf) }}]
|
||||
md-table-cell(v-if='item.text').mpj-request-text.mpj-valign-top {{ item.text }}
|
||||
md-table-cell(v-else)
|
||||
p(v-else) Loading request...
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import moment from 'moment'
|
||||
|
||||
import api from '@/api'
|
||||
|
||||
const asOfDesc = (a, b) => b.asOf - a.asOf
|
||||
|
||||
export default {
|
||||
name: 'full-request',
|
||||
inject: ['progress'],
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
request: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
answered () {
|
||||
return this.request.history.find(hist => hist.status === 'Answered').asOf
|
||||
},
|
||||
isAnswered () {
|
||||
return this.request.history.filter(hist => hist.status === 'Answered').length > 0
|
||||
},
|
||||
lastText () {
|
||||
return this.request.history
|
||||
.filter(hist => hist.text)
|
||||
.sort(asOfDesc)[0].text
|
||||
},
|
||||
log () {
|
||||
const allHistory = (this.request.notes || [])
|
||||
.map(note => ({ asOf: note.asOf, text: note.notes, status: 'Notes' }))
|
||||
.concat(this.request.history)
|
||||
.sort(asOfDesc)
|
||||
// Skip the first entry for answered requests; that info is already displayed
|
||||
return this.isAnswered ? allHistory.slice(1) : allHistory
|
||||
},
|
||||
openDays () {
|
||||
const asOf = this.isAnswered ? this.answered : Date.now()
|
||||
return Math.floor(
|
||||
(asOf - 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.$emit('show', 'indeterminate')
|
||||
try {
|
||||
const req = await api.getFullRequest(this.id)
|
||||
this.request = req.data
|
||||
this.progress.$emit('done')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
this.progress.$emit('done')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatDate (asOf) {
|
||||
return moment(asOf).format('LL')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,118 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-dialog(:md-active.sync='notesVisible').mpj-note-dialog
|
||||
md-dialog-title Add Notes to Prayer Request
|
||||
md-content.mpj-dialog-content
|
||||
md-field
|
||||
label Notes
|
||||
md-textarea(v-model='form.notes'
|
||||
md-autogrow
|
||||
@blur='trimText()')
|
||||
md-dialog-actions
|
||||
md-button(@click='saveNotes()').md-primary #[md-icon save] Save
|
||||
md-button(@click='closeDialog()') #[md-icon undo] Cancel
|
||||
md-dialog-content(md-scrollbar='true').mpj-dialog-content
|
||||
div(v-if='hasPriorNotes')
|
||||
p.mpj-text-center: strong Prior Notes for This Request
|
||||
.mpj-note-list
|
||||
p(v-for='note in priorNotes'
|
||||
:key='note.asOf')
|
||||
small.mpj-muted-text: date-from-now(:value='note.asOf')
|
||||
br
|
||||
span.mpj-request-text {{ note.notes }}
|
||||
div(v-else-if='noPriorNotes').mpj-text-center.mpj-muted-text There are no prior notes for this request
|
||||
div(v-else).mpj-text-center
|
||||
hr
|
||||
md-button(@click='loadNotes()') #[md-icon cloud_download] Load Prior Notes
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import api from '@/api'
|
||||
|
||||
export default {
|
||||
name: 'notes-edit',
|
||||
inject: [
|
||||
'journalEvents',
|
||||
'messages',
|
||||
'progress'
|
||||
],
|
||||
data () {
|
||||
return {
|
||||
notesVisible: false,
|
||||
form: {
|
||||
requestId: '',
|
||||
notes: ''
|
||||
},
|
||||
priorNotes: [],
|
||||
priorNotesLoaded: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasPriorNotes () {
|
||||
return this.priorNotesLoaded && this.priorNotes.length > 0
|
||||
},
|
||||
noPriorNotes () {
|
||||
return this.priorNotesLoaded && this.priorNotes.length === 0
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.journalEvents.$on('notes', this.openDialog)
|
||||
},
|
||||
methods: {
|
||||
closeDialog () {
|
||||
this.form.requestId = ''
|
||||
this.form.notes = ''
|
||||
this.priorNotes = []
|
||||
this.priorNotesLoaded = false
|
||||
this.notesVisible = false
|
||||
},
|
||||
async loadNotes () {
|
||||
this.progress.$emit('show', 'indeterminate')
|
||||
try {
|
||||
const notes = await api.getNotes(this.form.requestId)
|
||||
this.priorNotes = notes.data.sort((a, b) => b.asOf - a.asOf)
|
||||
this.progress.$emit('done')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.progress.$emit('done')
|
||||
} finally {
|
||||
this.priorNotesLoaded = true
|
||||
}
|
||||
},
|
||||
openDialog (request) {
|
||||
this.form.requestId = request.requestId
|
||||
this.notesVisible = true
|
||||
},
|
||||
async saveNotes () {
|
||||
this.progress.$emit('show', 'indeterminate')
|
||||
try {
|
||||
await api.addNote(this.form.requestId, this.form.notes)
|
||||
this.progress.$emit('done')
|
||||
this.messages.$emit('info', 'Added notes')
|
||||
this.closeDialog()
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.progress.$emit('done')
|
||||
}
|
||||
},
|
||||
trimText () {
|
||||
this.form.notes = this.form.notes.trim()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.mpj-note-dialog
|
||||
width: 40rem
|
||||
padding-bottom: 1.5rem
|
||||
@media screen and (max-width: 40rem)
|
||||
@media screen and (max-width: 20rem)
|
||||
.mpj-note-dialog
|
||||
width: 100%
|
||||
.mpj-note-dialog
|
||||
width: 20rem
|
||||
.mpj-note-list p
|
||||
border-top: dotted 1px lightgray
|
||||
</style>
|
||||
@@ -1,78 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-card(v-if='shouldDisplay'
|
||||
md-with-hover).mpj-request-card
|
||||
md-card-actions(md-alignment='space-between')
|
||||
md-button(@click='markPrayed()').md-icon-button.md-raised.md-primary
|
||||
md-icon done
|
||||
md-tooltip(md-direction='top'
|
||||
md-delay=1000) Mark as Prayed
|
||||
span
|
||||
md-button(@click.stop='showEdit()').md-icon-button.md-raised
|
||||
md-icon edit
|
||||
md-tooltip(md-direction='top'
|
||||
md-delay=1000) Edit Request
|
||||
md-button(@click.stop='showNotes()').md-icon-button.md-raised
|
||||
md-icon comment
|
||||
md-tooltip(md-direction='top'
|
||||
md-delay=1000) Add Notes
|
||||
md-button(@click.stop='snooze()').md-icon-button.md-raised
|
||||
md-icon schedule
|
||||
md-tooltip(md-direction='top'
|
||||
md-delay=1000) Snooze Request
|
||||
md-card-content
|
||||
p.mpj-request-text {{ request.text }}
|
||||
p.mpj-text-right: small.mpj-muted-text: em (last activity #[date-from-now(:value='request.asOf')])
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import actions from '@/store/action-types'
|
||||
|
||||
export default {
|
||||
name: 'request-card',
|
||||
inject: [
|
||||
'journalEvents',
|
||||
'messages',
|
||||
'progress'
|
||||
],
|
||||
props: {
|
||||
request: { required: true }
|
||||
},
|
||||
computed: {
|
||||
shouldDisplay () {
|
||||
const now = Date.now()
|
||||
return Math.max(now, this.request.showAfter, this.request.snoozedUntil) === now
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async markPrayed () {
|
||||
await this.$store.dispatch(actions.UPDATE_REQUEST, {
|
||||
progress: this.progress,
|
||||
requestId: this.request.requestId,
|
||||
status: 'Prayed',
|
||||
updateText: ''
|
||||
})
|
||||
this.messages.$emit('info', 'Request marked as prayed')
|
||||
},
|
||||
showEdit () {
|
||||
this.$router.push({ name: 'EditRequest', params: { id: this.request.requestId } })
|
||||
},
|
||||
showNotes () {
|
||||
this.journalEvents.$emit('notes', this.request)
|
||||
},
|
||||
snooze () {
|
||||
this.journalEvents.$emit('snooze', this.request.requestId)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.mpj-request-card
|
||||
width: 20rem
|
||||
margin-bottom: 1rem
|
||||
@media screen and (max-width: 20rem)
|
||||
.mpj-request-card
|
||||
width: 100%
|
||||
</style>
|
||||
@@ -1,40 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-table(md-card)
|
||||
md-table-toolbar
|
||||
h1.md-title {{ title }}
|
||||
md-table-row
|
||||
md-table-head Actions
|
||||
md-table-head Request
|
||||
request-list-item(v-for='req in requests'
|
||||
:key='req.requestId'
|
||||
:request='req')
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import RequestListItem from '@/components/request/RequestListItem'
|
||||
|
||||
export default {
|
||||
name: 'request-list',
|
||||
components: { RequestListItem },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
requests: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return { }
|
||||
},
|
||||
created () {
|
||||
this.$on('requestUnsnoozed', this.$parent.$emit('requestUnsnoozed'))
|
||||
this.$on('requestNowShown', this.$parent.$emit('requestNowShown'))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,95 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-table-row
|
||||
md-table-cell.mpj-action-cell.mpj-valign-top
|
||||
md-button(@click='viewFull').md-icon-button.md-raised
|
||||
md-icon description
|
||||
md-tooltip(md-direction='top'
|
||||
md-delay=250) View Full Request
|
||||
template(v-if='!isAnswered')
|
||||
md-button(@click='editRequest').md-icon-button.md-raised
|
||||
md-icon edit
|
||||
md-tooltip(md-direction='top'
|
||||
md-delay=250) Edit Request
|
||||
template(v-if='isSnoozed')
|
||||
md-button(@click='cancelSnooze()').md-icon-button.md-raised
|
||||
md-icon restore
|
||||
md-tooltip(md-direction='top'
|
||||
md-delay=250) Cancel Snooze
|
||||
template(v-if='isPending')
|
||||
md-button(@click='showNow()').md-icon-button.md-raised
|
||||
md-icon restore
|
||||
md-tooltip(md-direction='top'
|
||||
md-delay=250) Show Now
|
||||
md-table-cell.mpj-valign-top
|
||||
p.mpj-request-text {{ request.text }}
|
||||
br(v-if='isSnoozed || isPending || isAnswered')
|
||||
small(v-if='isSnoozed').mpj-muted-text: em Snooze expires #[date-from-now(:value='request.snoozedUntil')]
|
||||
small(v-if='isPending').mpj-muted-text: em Request appears next #[date-from-now(:value='request.showAfter')]
|
||||
small(v-if='isAnswered').mpj-muted-text: em Answered #[date-from-now(:value='request.asOf')]
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import actions from '@/store/action-types'
|
||||
|
||||
export default {
|
||||
name: 'request-list-item',
|
||||
inject: [
|
||||
'messages',
|
||||
'progress'
|
||||
],
|
||||
props: {
|
||||
request: { required: true }
|
||||
},
|
||||
data () {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
answered () {
|
||||
return this.request.history.find(hist => hist.status === 'Answered').asOf
|
||||
},
|
||||
isAnswered () {
|
||||
return this.request.lastStatus === 'Answered'
|
||||
},
|
||||
isPending () {
|
||||
return !this.isSnoozed && this.request.showAfter > Date.now()
|
||||
},
|
||||
isSnoozed () {
|
||||
return this.request.snoozedUntil > Date.now()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async cancelSnooze () {
|
||||
await this.$store.dispatch(actions.SNOOZE_REQUEST, {
|
||||
progress: this.progress,
|
||||
requestId: this.request.requestId,
|
||||
until: 0
|
||||
})
|
||||
this.messages.$emit('info', 'Request un-snoozed')
|
||||
this.$parent.$emit('requestUnsnoozed')
|
||||
},
|
||||
editRequest () {
|
||||
this.$router.push({ name: 'EditRequest', params: { id: this.request.requestId } })
|
||||
},
|
||||
async showNow () {
|
||||
await this.$store.dispatch(actions.SHOW_REQUEST_NOW, {
|
||||
progress: this.progress,
|
||||
requestId: this.request.requestId,
|
||||
showAfter: 0
|
||||
})
|
||||
this.messages.$emit('info', 'Recurrence skipped; request now shows in journal')
|
||||
this.$parent.$emit('requestNowShown')
|
||||
},
|
||||
viewFull () {
|
||||
this.$router.push({ name: 'FullRequest', params: { id: this.request.requestId } })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="sass">
|
||||
.mpj-action-cell
|
||||
width: 1%
|
||||
white-space: nowrap
|
||||
</style>
|
||||
@@ -1,69 +0,0 @@
|
||||
<template lang="pug">
|
||||
md-dialog(:md-active.sync='snoozeVisible').mpj-skinny
|
||||
md-dialog-title Snooze Prayer Request
|
||||
md-content.mpj-dialog-content
|
||||
span.mpj-text-muted Until
|
||||
md-datepicker(v-model='form.snoozedUntil'
|
||||
:md-disabled-dates='datesInPast'
|
||||
md-immediately)
|
||||
md-dialog-actions
|
||||
md-button(:disabled='!isValid'
|
||||
@click='snoozeRequest()').md-primary #[md-icon snooze] Snooze
|
||||
md-button(@click='closeDialog()') #[md-icon undo] Cancel
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import actions from '@/store/action-types'
|
||||
|
||||
export default {
|
||||
name: 'snooze-request',
|
||||
inject: [
|
||||
'journalEvents',
|
||||
'messages',
|
||||
'progress'
|
||||
],
|
||||
props: {
|
||||
events: { required: true }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
snoozeVisible: false,
|
||||
datesInPast: date => date < new Date(),
|
||||
form: {
|
||||
requestId: '',
|
||||
snoozedUntil: ''
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.journalEvents.$on('snooze', this.openDialog)
|
||||
},
|
||||
computed: {
|
||||
isValid () {
|
||||
return !isNaN(Date.parse(this.form.snoozedUntil))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
closeDialog () {
|
||||
this.form.requestId = ''
|
||||
this.form.snoozedUntil = ''
|
||||
this.snoozeVisible = false
|
||||
},
|
||||
openDialog (requestId) {
|
||||
this.form.requestId = requestId
|
||||
this.snoozeVisible = true
|
||||
},
|
||||
async snoozeRequest () {
|
||||
await this.$store.dispatch(actions.SNOOZE_REQUEST, {
|
||||
progress: this.progress,
|
||||
requestId: this.form.requestId,
|
||||
until: Date.parse(this.form.snoozedUntil)
|
||||
})
|
||||
this.messages.$emit('info', `Request snoozed until ${this.form.snoozedUntil}`)
|
||||
this.closeDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template lang="pug">
|
||||
article.mpj-main-content(role='main')
|
||||
page-title(title='Snoozed Requests'
|
||||
hide-on-page=true)
|
||||
template(v-if='loaded')
|
||||
md-empty-state(v-if='requests.length === 0'
|
||||
md-icon='sentiment_dissatisfied'
|
||||
md-label='No Snoozed Requests'
|
||||
md-description='Your prayer journal has no snoozed requests')
|
||||
md-button(to='/journal').md-primary.md-raised Return to your journal
|
||||
request-list(v-if='requests.length !== 0'
|
||||
title='Snoozed Requests'
|
||||
:requests='requests')
|
||||
p(v-else) Loading journal...
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
import { mapState } from 'vuex'
|
||||
|
||||
import actions from '@/store/action-types'
|
||||
|
||||
import RequestList from '@/components/request/RequestList'
|
||||
|
||||
export default {
|
||||
name: 'snoozed-requests',
|
||||
inject: ['progress'],
|
||||
components: {
|
||||
RequestList
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
requests: [],
|
||||
loaded: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['journal', 'isLoadingJournal'])
|
||||
},
|
||||
created () {
|
||||
this.$on('requestUnsnoozed', this.ensureJournal)
|
||||
},
|
||||
methods: {
|
||||
async ensureJournal () {
|
||||
if (!Array.isArray(this.journal)) {
|
||||
this.loaded = false
|
||||
await this.$store.dispatch(actions.LOAD_JOURNAL, this.progress)
|
||||
}
|
||||
this.requests = this.journal
|
||||
.filter(req => req.snoozedUntil > Date.now())
|
||||
.sort((a, b) => a.snoozedUntil - b.snoozedUntil)
|
||||
this.loaded = true
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
await this.ensureJournal()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,23 +0,0 @@
|
||||
<template lang="pug">
|
||||
article.mpj-main-content(role='main')
|
||||
pageTitle(title='Logging On')
|
||||
p Logging you on...
|
||||
</template>
|
||||
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
export default {
|
||||
name: 'log-on',
|
||||
inject: ['progress'],
|
||||
async created () {
|
||||
this.progress.$emit('show', 'indeterminate')
|
||||
await this.$auth.handleAuthentication(this.$store)
|
||||
},
|
||||
methods: {
|
||||
handleLoginEvent (data) {
|
||||
this.$router.push(data.state.target || '/journal')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,64 +0,0 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// Vue packages and components
|
||||
import Vue from 'vue'
|
||||
import { MdApp,
|
||||
MdButton,
|
||||
MdCard,
|
||||
MdContent,
|
||||
MdDatepicker,
|
||||
MdDialog,
|
||||
MdEmptyState,
|
||||
MdField,
|
||||
MdIcon,
|
||||
MdLayout,
|
||||
MdProgress,
|
||||
MdRadio,
|
||||
MdSnackbar,
|
||||
MdTable,
|
||||
MdTabs,
|
||||
MdToolbar,
|
||||
MdTooltip } from 'vue-material/dist/components'
|
||||
|
||||
// myPrayerJournal components
|
||||
import App from './App'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import DateFromNow from './components/common/DateFromNow'
|
||||
import PageTitle from './components/common/PageTitle'
|
||||
import AuthPlugin from './plugins/auth'
|
||||
|
||||
/* eslint-enable */
|
||||
|
||||
// Styles
|
||||
import 'vue-material/dist/vue-material.min.css'
|
||||
import 'vue-material/dist/theme/default.css'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
Vue.use(MdApp)
|
||||
Vue.use(MdButton)
|
||||
Vue.use(MdCard)
|
||||
Vue.use(MdContent)
|
||||
Vue.use(MdDatepicker)
|
||||
Vue.use(MdDialog)
|
||||
Vue.use(MdEmptyState)
|
||||
Vue.use(MdField)
|
||||
Vue.use(MdIcon)
|
||||
Vue.use(MdLayout)
|
||||
Vue.use(MdProgress)
|
||||
Vue.use(MdRadio)
|
||||
Vue.use(MdSnackbar)
|
||||
Vue.use(MdTable)
|
||||
Vue.use(MdTabs)
|
||||
Vue.use(MdToolbar)
|
||||
Vue.use(MdTooltip)
|
||||
Vue.use(AuthPlugin)
|
||||
Vue.component('date-from-now', DateFromNow)
|
||||
Vue.component('page-title', PageTitle)
|
||||
|
||||
new Vue({
|
||||
router,
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount('#app')
|
||||
@@ -1,22 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
import authService from '../auth/AuthService'
|
||||
|
||||
export default {
|
||||
install (Vue) {
|
||||
Vue.prototype.$auth = authService
|
||||
|
||||
Vue.mixin({
|
||||
created () {
|
||||
if (this.handleLoginEvent) {
|
||||
authService.addListener('loginEvent', this.handleLoginEvent)
|
||||
}
|
||||
},
|
||||
destroyed () {
|
||||
if (this.handleLoginEvent) {
|
||||
authService.removeListener('loginEvent', this.handleLoginEvent)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/* eslint-disable */
|
||||
import Vue from 'vue'
|
||||
import Router from 'vue-router'
|
||||
|
||||
import auth from './auth/AuthService'
|
||||
import Home from '@/components/Home'
|
||||
/* eslint-enable */
|
||||
|
||||
Vue.use(Router)
|
||||
|
||||
export default new Router({
|
||||
mode: 'history',
|
||||
base: process.env.BASE_URL,
|
||||
scrollBehavior (to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return savedPosition
|
||||
} else {
|
||||
return { x: 0, y: 0 }
|
||||
}
|
||||
},
|
||||
beforeEach (to, from, next) {
|
||||
if (to.path === '/' || to.path === '/user/log-on' || auth.isAuthenticated()) {
|
||||
return next()
|
||||
}
|
||||
auth.login({ target: to.path })
|
||||
},
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Home',
|
||||
component: Home
|
||||
},
|
||||
{
|
||||
path: '/journal',
|
||||
name: 'Journal',
|
||||
component: () => import('@/components/Journal')
|
||||
},
|
||||
{
|
||||
path: '/legal/privacy-policy',
|
||||
name: 'PrivacyPolicy',
|
||||
component: () => import('@/components/legal/PrivacyPolicy')
|
||||
},
|
||||
{
|
||||
path: '/legal/terms-of-service',
|
||||
name: 'TermsOfService',
|
||||
component: () => import('@/components/legal/TermsOfService')
|
||||
},
|
||||
{
|
||||
path: '/request/:id/edit',
|
||||
name: 'EditRequest',
|
||||
component: () => import('@/components/request/EditRequest'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/request/:id/full',
|
||||
name: 'FullRequest',
|
||||
component: () => import('@/components/request/FullRequest'),
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '/requests/active',
|
||||
name: 'ActiveRequests',
|
||||
component: () => import('@/components/request/ActiveRequests')
|
||||
},
|
||||
{
|
||||
path: '/requests/answered',
|
||||
name: 'AnsweredRequests',
|
||||
component: () => import('@/components/request/AnsweredRequests')
|
||||
},
|
||||
{
|
||||
path: '/requests/snoozed',
|
||||
name: 'SnoozedRequests',
|
||||
component: () => import('@/components/request/SnoozedRequests')
|
||||
},
|
||||
{
|
||||
path: '/user/log-on',
|
||||
name: 'LogOn',
|
||||
component: () => import('@/components/user/LogOn')
|
||||
}
|
||||
]
|
||||
})
|
||||
@@ -1,16 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
export default {
|
||||
/** Action to add a prayer request (pass request text) */
|
||||
ADD_REQUEST: 'add-request',
|
||||
/** Action to check if a user is authenticated, refreshing the session first if it exists */
|
||||
CHECK_AUTHENTICATION: 'check-authentication',
|
||||
/** Action to load the user's prayer journal */
|
||||
LOAD_JOURNAL: 'load-journal',
|
||||
/** Action to update a request */
|
||||
UPDATE_REQUEST: 'update-request',
|
||||
/** Action to skip the remaining recurrence period */
|
||||
SHOW_REQUEST_NOW: 'show-request-now',
|
||||
/** Action to snooze a request */
|
||||
SNOOZE_REQUEST: 'snooze-request'
|
||||
}
|
||||
@@ -1,186 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/* eslint-disable no-multi-spaces */
|
||||
import Vue from 'vue'
|
||||
import Vuex from 'vuex'
|
||||
|
||||
import api from '@/api'
|
||||
import auth from '@/auth/AuthService'
|
||||
|
||||
import mutations from './mutation-types'
|
||||
import actions from './action-types'
|
||||
/* eslint-enable no-multi-spaces */
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
/* eslint-disable no-console */
|
||||
const logError = function (error) {
|
||||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
console.error(error.response.data)
|
||||
console.error(error.response.status)
|
||||
console.error(error.response.headers)
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
|
||||
// http.ClientRequest in node.js
|
||||
console.error(error.request)
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
console.error('Error', error.message)
|
||||
}
|
||||
console.error(`config: ${error.config}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the "Bearer" authorization header with the current access token
|
||||
*/
|
||||
const setBearer = async function () {
|
||||
try {
|
||||
await auth.getAccessToken()
|
||||
api.setBearer(auth.session.id.token)
|
||||
} catch (err) {
|
||||
if (err === 'Not logged in') {
|
||||
console.warn('API request attempted when user was not logged in')
|
||||
} else {
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
/* eslint-enable no-console */
|
||||
|
||||
/**
|
||||
* Get the sort value for a prayer request
|
||||
* @param x The prayer request
|
||||
*/
|
||||
const sortValue = x => x.showAfter === 0 ? x.asOf : x.showAfter
|
||||
|
||||
/**
|
||||
* Sort journal requests either by asOf or showAfter
|
||||
*/
|
||||
const journalSort = (a, b) => sortValue(a) - sortValue(b)
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
user: auth.session.profile,
|
||||
isAuthenticated: auth.isAuthenticated(),
|
||||
journal: [],
|
||||
isLoadingJournal: false
|
||||
},
|
||||
mutations: {
|
||||
[mutations.LOADING_JOURNAL] (state, flag) {
|
||||
state.isLoadingJournal = flag
|
||||
},
|
||||
[mutations.LOADED_JOURNAL] (state, journal) {
|
||||
state.journal = journal.sort(journalSort)
|
||||
},
|
||||
[mutations.REQUEST_ADDED] (state, newRequest) {
|
||||
state.journal.push(newRequest)
|
||||
},
|
||||
[mutations.REQUEST_UPDATED] (state, request) {
|
||||
const jrnl = state.journal.filter(it => it.requestId !== request.requestId)
|
||||
if (request.lastStatus !== 'Answered') jrnl.push(request)
|
||||
state.journal = jrnl
|
||||
},
|
||||
[mutations.SET_AUTHENTICATION] (state, value) {
|
||||
state.isAuthenticated = value
|
||||
},
|
||||
[mutations.USER_LOGGED_OFF] (state) {
|
||||
state.user = {}
|
||||
api.removeBearer()
|
||||
state.isAuthenticated = false
|
||||
},
|
||||
[mutations.USER_LOGGED_ON] (state, user) {
|
||||
state.user = user
|
||||
state.isAuthenticated = true
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async [actions.ADD_REQUEST] ({ commit }, { progress, requestText, recurType, recurCount }) {
|
||||
progress.$emit('show', 'indeterminate')
|
||||
try {
|
||||
await setBearer()
|
||||
const newRequest = await api.addRequest(requestText, recurType, recurCount)
|
||||
commit(mutations.REQUEST_ADDED, newRequest.data)
|
||||
progress.$emit('done')
|
||||
} catch (err) {
|
||||
logError(err)
|
||||
progress.$emit('done')
|
||||
}
|
||||
},
|
||||
async [actions.CHECK_AUTHENTICATION] ({ commit }) {
|
||||
try {
|
||||
await auth.getAccessToken()
|
||||
commit(mutations.SET_AUTHENTICATION, auth.isAuthenticated())
|
||||
} catch (_) {
|
||||
commit(mutations.SET_AUTHENTICATION, false)
|
||||
}
|
||||
},
|
||||
async [actions.LOAD_JOURNAL] ({ commit }, progress) {
|
||||
commit(mutations.LOADED_JOURNAL, [])
|
||||
progress.$emit('show', 'query')
|
||||
commit(mutations.LOADING_JOURNAL, true)
|
||||
await setBearer()
|
||||
try {
|
||||
const jrnl = await api.journal()
|
||||
commit(mutations.LOADED_JOURNAL, jrnl.data)
|
||||
progress.$emit('done')
|
||||
} catch (err) {
|
||||
logError(err)
|
||||
progress.$emit('done')
|
||||
} finally {
|
||||
commit(mutations.LOADING_JOURNAL, false)
|
||||
}
|
||||
},
|
||||
async [actions.UPDATE_REQUEST] ({ commit, state }, { progress, requestId, status, updateText, recurType, recurCount }) {
|
||||
progress.$emit('show', 'indeterminate')
|
||||
try {
|
||||
await setBearer()
|
||||
const oldReq = (state.journal.filter(req => req.requestId === requestId) || [])[0] || {}
|
||||
if (!(status === 'Prayed' && updateText === '')) {
|
||||
if (status !== 'Answered' && (oldReq.recurType !== recurType || oldReq.recurCount !== recurCount)) {
|
||||
await api.updateRecurrence(requestId, recurType, recurCount)
|
||||
}
|
||||
}
|
||||
if (status !== 'Updated' || oldReq.text !== updateText) {
|
||||
await api.updateRequest(requestId, status, oldReq.text !== updateText ? updateText : '')
|
||||
}
|
||||
const request = await api.getRequest(requestId)
|
||||
commit(mutations.REQUEST_UPDATED, request.data)
|
||||
progress.$emit('done')
|
||||
} catch (err) {
|
||||
logError(err)
|
||||
progress.$emit('done')
|
||||
}
|
||||
},
|
||||
async [actions.SHOW_REQUEST_NOW] ({ commit }, { progress, requestId, showAfter }) {
|
||||
progress.$emit('show', 'indeterminate')
|
||||
try {
|
||||
await setBearer()
|
||||
await api.showRequest(requestId, showAfter)
|
||||
const request = await api.getRequest(requestId)
|
||||
commit(mutations.REQUEST_UPDATED, request.data)
|
||||
progress.$emit('done')
|
||||
} catch (err) {
|
||||
logError(err)
|
||||
progress.$emit('done')
|
||||
}
|
||||
},
|
||||
async [actions.SNOOZE_REQUEST] ({ commit }, { progress, requestId, until }) {
|
||||
progress.$emit('show', 'indeterminate')
|
||||
try {
|
||||
await setBearer()
|
||||
await api.snoozeRequest(requestId, until)
|
||||
const request = await api.getRequest(requestId)
|
||||
commit(mutations.REQUEST_UPDATED, request.data)
|
||||
progress.$emit('done')
|
||||
} catch (err) {
|
||||
logError(err)
|
||||
progress.$emit('done')
|
||||
}
|
||||
}
|
||||
},
|
||||
getters: {},
|
||||
modules: {}
|
||||
})
|
||||
@@ -1,18 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
export default {
|
||||
/** Mutation for when the user's prayer journal is being loaded */
|
||||
LOADING_JOURNAL: 'loading-journal',
|
||||
/** Mutation for when the user's prayer journal has been loaded */
|
||||
LOADED_JOURNAL: 'journal-loaded',
|
||||
/** Mutation for adding a new prayer request (pass text) */
|
||||
REQUEST_ADDED: 'request-added',
|
||||
/** Mutation to replace a prayer request at the top of the current journal */
|
||||
REQUEST_UPDATED: 'request-updated',
|
||||
/** Mutation for setting the authentication state */
|
||||
SET_AUTHENTICATION: 'set-authentication',
|
||||
/** Mutation for logging a user off */
|
||||
USER_LOGGED_OFF: 'user-logged-off',
|
||||
/** Mutation for logging a user on (pass user) */
|
||||
USER_LOGGED_ON: 'user-logged-on'
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script src="https://cdn.auth0.com/js/auth0/8.9/auth0.min.js"></script>
|
||||
<script>
|
||||
var webAuth = new auth0.WebAuth({
|
||||
domain: 'djs-consulting.auth0.com',
|
||||
clientID: 'Of2s0RQCQ3mt3dwIkOBY5h85J9sXbF2n',
|
||||
scope: 'openid profile email',
|
||||
responseType: 'token id_token',
|
||||
redirectUri: location.protocol + '//' + location.host + '/static/silent.html'
|
||||
})
|
||||
</script>
|
||||
<script>
|
||||
webAuth.parseHash(window.location.hash, function (err, response) {
|
||||
parent.postMessage(err || response, location.protocol + '//' + location.host);
|
||||
})
|
||||
</script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
@@ -1,16 +0,0 @@
|
||||
const webpack = require('webpack')
|
||||
// const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
||||
module.exports = {
|
||||
outputDir: '../MyPrayerJournal.Api/wwwroot',
|
||||
configureWebpack: {
|
||||
plugins: [
|
||||
// new BundleAnalyzerPlugin(),
|
||||
new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)
|
||||
],
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
chunks: 'all'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9273
src/app/yarn.lock
9273
src/app/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user