interim commit with lots of stuff

- conversion from Element UI to Bootstrap 4 in progress (smaller, more
flexible)
- added Font Awesome for fonts, vue-toast for notifications
- added common components to main.js and out of other components
- some work on pulling answered requests (#3), added icon for notes (#8)
This commit is contained in:
Daniel J. Summers 2017-09-28 21:59:40 -05:00
parent 1e1afa9d89
commit ef88964cd0
19 changed files with 244 additions and 165 deletions

View File

@ -4,4 +4,4 @@ Journaling has a long history; it helps people remember what happened, and the a
This is borne of out of a personal desire I had to have something that would help me with my prayer life. When it's time to pray, it's not really time to use an app, so the design goal here is to keep it simple and unobtrusive. It will also help eliminate some of the downsides to a paper prayer journal, like not remembering whether you've prayed for a request, or running out of room to write another update on one.
It is still a work-in-progress (WIP). It will eventually be hosted at <https://prayerjournal.me>, and will be available for public use.
It is still a work-in-progress (WIP), but is available for public preview at <https://prayerjournal.me>.

View File

@ -68,6 +68,17 @@ export default function (pool) {
})
},
/**
* 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
* @return All requests
*/
answered: async (userId) =>
(await pool.query(`${currentRequestSql}
WHERE "userId" = $1
AND "lastStatus" = 'Answered'
ORDER BY "asOf" DESC`)).rows,
/**
* Get the "current" version of a request by its Id
* @param {string} requestId The Id of the request to retrieve

View File

@ -45,7 +45,10 @@ export default function (checkJwt) {
ctx.body = await db.request.oldest(ctx.state.user.sub)
await next()
})
.get('/answered', checkJwt, async (ctx, next) => {
ctx.body = await db.request.answered(ctx.state.user.sub)
await next()
})
return router
}

View File

@ -16,12 +16,14 @@
"dependencies": {
"auth0-js": "^8.10.1",
"axios": "^0.16.2",
"element-ui": "^1.4.4",
"bootstrap-vue": "^1.0.0-beta.9",
"moment": "^2.18.1",
"pug": "^2.0.0-rc.4",
"vue": "^2.4.4",
"vue-awesome": "^2.3.3",
"vue-progressbar": "^0.7.3",
"vue-router": "^2.6.0",
"vue-toast": "^3.1.0",
"vuex": "^2.4.0"
},
"devDependencies": {

View File

@ -22,42 +22,17 @@ export default {
</script>
<style>
@import url('../node_modules/element-ui/lib/theme-default/index.css');
body {
font-family: -apple-system,system-ui,BlinkMacSystemFont,"Segoe UI","Roboto","Helvetica Neue", Arial, sans-serif;
padding-top: 60px;
margin: 0;
}
#content {
padding: 0 10px;
}
footer {
border-top: solid 1px lightgray;
margin-top: 1rem;
padding: 0 1rem;
}
footer p {
margin: 0;
}
.text-right {
text-align: right;
}
.material-icons.md-18 {
font-size: 18px;
}
.material-icons.md-24 {
font-size: 24px;
}
.material-icons.md-36 {
font-size: 36px;
}
.material-icons.md-48 {
font-size: 48px;
}
.material-icons {
vertical-align: middle;
}
.mpj-page-title {
border-bottom: solid 1px lightgray;
margin-bottom: 20px;

View File

@ -50,6 +50,11 @@ export default {
* Get a prayer request (full; includes all history)
* @param {string} requestId The Id of the request to retrieve
*/
getFullRequest: requestId => http.get(`request/${requestId}/full`)
getFullRequest: requestId => http.get(`request/${requestId}/full`),
/**
* Get all answered requests, along with the text they had when it was answered
*/
getAnsweredRequests: () => http.get('request/answered')
}

View File

@ -0,0 +1,38 @@
<template lang="pug">
article
p(v-if='!loaded') Loading answered requests...
div(v-if='loaded')
</template>
<script>
'use static'
import api from '@/api'
export default {
name: 'answered',
data () {
return {
requests: [],
loaded: false
}
},
async mounted () {
this.$Progress.start()
try {
const reqs = await api.getAnsweredRequests()
this.requests = reqs.data
this.$Progress.finish()
} catch (err) {
console.error(err)
this.$message({
message: 'Error loading requests; check console for details',
type: 'error'
})
this.$Progress.fail()
} finally {
this.loaded = true
}
}
}
</script>

View File

@ -15,12 +15,7 @@ article
<script>
'use strict'
import PageTitle from './common/PageTitle.vue'
export default {
name: 'home',
components: {
PageTitle
}
name: 'home'
}
</script>

View File

@ -1,14 +1,13 @@
<template lang="pug">
article
page-title(:title='title')
toast(ref='toast')
p(v-if='isLoadingJournal') Loading your prayer journal...
template(v-if='!isLoadingJournal')
new-request
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-if='journal.length > 0' v-for='request in journal' :request='request' :key='request.requestId')
br
b-row
request-list-item(v-if='journal.length > 0' v-for='request in journal' :request='request' :key='request.requestId')
p.text-center(v-if='journal.length === 0'): em No requests found; click the "Add a New Request" button to add one
</template>
@ -17,7 +16,6 @@ article
import { mapState } from 'vuex'
import PageTitle from './common/PageTitle'
import NewRequest from './request/NewRequest'
import RequestListItem from './request/RequestListItem'
@ -26,7 +24,6 @@ import actions from '@/store/action-types'
export default {
name: 'journal',
components: {
PageTitle,
NewRequest,
RequestListItem
},
@ -38,10 +35,14 @@ export default {
},
async created () {
await this.$store.dispatch(actions.LOAD_JOURNAL, this.$Progress)
this.$message({
message: `Loaded ${this.journal.length} prayer requests`,
type: 'success'
})
this.$refs.toast.setOptions({ position: 'bottom right' })
this.$refs.toast.showToast(`Loaded ${this.journal.length} prayer requests`, { theme: 'success' })
}
}
/*
b-row
b-col(cols='2'): strong Actions
b-col(cols='8'): strong Request
b-col(cols='2'): strong As Of
*/
</script>

View File

@ -1,13 +1,17 @@
<template lang="pug">
el-menu(theme='dark' mode='horizontal' class='mpj-top-nav' router='true')
el-menu-item(index='/')
b-navbar(toggleable='sm' type='dark' variant='info' fixed='top')
b-nav-toggle(target='nav_collapse')
b-navbar-brand(to='/')
span(style='font-weight:100;') my
span(style='font-weight:600;') Prayer
span(style='font-weight:700;') Journal
el-menu-item(v-if='isAuthenticated' index='/journal') Journal
el-menu-item(v-if='isAuthenticated' index='3'): a(@click.stop='logOff()') Log Off
el-menu-item(v-if='!isAuthenticated' index='4'): a(@click.stop='logOn()') Log On
el-menu-item(index='5'): a(href='https://danieljsummers.github.io/myPrayerJournal/' target='_blank' @click.stop='') Docs
b-collapse#nav_collapse(is-nav)
b-nav(is-nav-bar)
b-nav-item(v-if='isAuthenticated' to='/journal') Journal
b-nav-item(v-if='isAuthenticated' to='/answered') Answered
b-nav-item(v-if='isAuthenticated'): a(@click.stop='logOff()') Log Off
b-nav-item(v-if='!isAuthenticated'): a(@click.stop='logOn()') Log On
b-nav-item(href='https://danieljsummers.github.io/myPrayerJournal/' target='_blank' @click.stop='') Docs
</template>
<script>
@ -36,15 +40,3 @@ export default {
}
}
</script>
<style>
.mpj-top-nav {
position: fixed;
top: 0px;
width: 100%;
}
.mpj-top-nav a:link,
.mpj-top-nav a:visited {
text-decoration: none;
}
</style>

View File

@ -20,8 +20,11 @@ export default {
}
},
data () {
const dt = moment(this.value)
return {
fromNow: moment(this.value).fromNow(),
dt,
fromNow: dt.fromNow(),
actual: dt.format('LLLL'),
intervalId: null
}
},
@ -34,12 +37,17 @@ export default {
},
methods: {
updateFromNow () {
let newFromNow = moment(this.value).fromNow()
let newFromNow = this.dt.fromNow()
if (newFromNow !== this.fromNow) this.fromNow = newFromNow
}
},
render (createElement) {
return createElement(this.tag, this.fromNow)
return createElement(this.tag, {
domProps: {
title: this.actual,
innerText: this.fromNow
}
})
}
}
</script>

View File

@ -1,18 +1,25 @@
<template lang="pug">
span
el-button(icon='edit' @click='openDialog()' title='Edit')
el-dialog(title='Edit Prayer Request' :visible.sync='editVisible')
el-form(:model='form' :label-position='top')
el-form-item(label='Prayer Request')
el-input(type='textarea' v-model='form.requestText' :rows='10' @blur="trimText()")
el-form-item(label='Also Mark As')
el-radio-group(v-model='form.status')
el-radio-button(label='Updated') Updated
el-radio-button(label='Prayed') Prayed
el-radio-button(label='Answered') Answered
span.dialog-footer(slot='footer')
el-button(@click='closeDialog()') Cancel
el-button(type='primary' @click='saveRequest()') Save
b-btn(@click='openDialog()' title='Edit' size='sm' variant='outline-secondary'): icon(name='pencil')
b-modal(title='Edit Prayer Request'
v-model='editVisible'
size='lg'
header-bg-variant='dark'
header-text-variant='light'
@shows='focusRequestText')
b-form
b-form-group(label='Prayer Request' label-for='request_text')
b-textarea#request_text(v-model='form.requestText' :rows='10' @blur='trimText()' ref='toFocus')
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
| &nbsp; &nbsp;
b-btn(variant='outline-secondary' @click='closeDialog()') Cancel
toast(ref='toast')
</template>
<script>
@ -22,7 +29,9 @@ import actions from '@/store/action-types'
export default {
name: 'edit-request',
props: [ 'request' ],
props: {
request: { required: true }
},
data () {
return {
editVisible: false,
@ -33,12 +42,18 @@ export default {
formLabelWidth: '120px'
}
},
mounted () {
this.$refs.toast.setOptions({ position: 'bottom right' })
},
methods: {
closeDialog () {
this.form.requestText = ''
this.form.status = 'Updated'
this.editVisible = false
},
focusRequestText (e) {
this.$refs.toFocus.focus()
},
openDialog () {
this.editVisible = true
},
@ -53,15 +68,9 @@ export default {
status: this.form.status
})
if (this.form.status === 'Answered') {
this.$message({
message: 'Request updated and removed from active journal',
type: 'success'
})
this.$refs.toast.showToast('Request updated and removed from active journal', { theme: 'success' })
} else {
this.$message({
message: 'Request updated',
type: 'success'
})
this.$refs.toast.showToast('Request updated', { theme: 'success' })
}
this.editVisible = false
}

View File

@ -1,11 +1,16 @@
<template lang="pug">
span
el-button(icon='view' @click='openDialog()' title='Show History')
el-dialog(title='Prayer Request History' :visible.sync='historyVisible')
span(v-if='null !== full')
b-btn(@click='openDialog()' title='Show History' size='sm' variant='outline-secondary'): icon(name='search')
b-modal(title='Prayer Request History'
v-model='historyVisible'
size='lg'
header-bg-variant='dark'
header-text-variant='light'
@shows='focusRequestText')
b-list-group(v-if='null !== full' flush)
full-request-history(v-for='item in full.history' :history='item' :key='item.asOf')
span.dialog-footer(slot='footer')
el-button(type='primary' @click='closeDialog()') Close
div.w-100.text-right(slot='modal-footer')
b-btn(variant='primary' @click='closeDialog()') Close
</template>
<script>
@ -17,7 +22,9 @@ import api from '@/api'
export default {
name: 'full-request',
props: [ 'request' ],
props: {
request: { required: true }
},
data () {
return {
historyVisible: false,

View File

@ -1,5 +1,5 @@
<template lang="pug">
p.journal-request
b-list-group-item
| {{ history.status }} {{ asOf }}
span(v-if='0 < history.text.length') &nbsp;&raquo; {{ history.text }}
</template>
@ -11,7 +11,9 @@ import moment from 'moment'
export default {
name: 'full-request-history',
props: [ 'history' ],
props: {
history: { required: true }
},
computed: {
asOf () {
return moment(this.history.asOf).fromNow()

View File

@ -1,13 +1,22 @@
<template lang="pug">
div
el-button(icon='plus' @click='openDialog()') Add a New Request
el-dialog(title='Add a New Prayer Request' :visible.sync='showNewVisible')
el-form(:model='form' :label-position='top')
el-form-item(label='Prayer Request')
el-input(type='textarea' v-model='form.requestText' :rows='10' @blur='trimText()')
span.dialog-footer(slot='footer')
el-button(@click='closeDialog()') Cancel
el-button(type='primary' @click='saveRequest()') Save
b-btn(@click='openDialog()' size='sm' variant='primary')
icon(name='plus')
| &nbsp; Add a New Request
b-modal(title='Add a New Prayer Request'
v-model='showNewVisible'
size='lg'
header-bg-variant='dark'
header-text-variant='light'
@shown='focusRequestText')
b-form
b-form-group(label='Prayer Request' label-for='request_text')
b-textarea#request_text(v-model='form.requestText' :rows='10' @blur='trimText()' ref='toFocus')
div.w-100.text-right(slot='modal-footer')
b-btn(variant='primary' @click='saveRequest()') Save
| &nbsp; &nbsp;
b-btn(variant='outline-secondary' @click='closeDialog()') Cancel
toast(ref='toast')
</template>
<script>
@ -26,11 +35,17 @@ export default {
formLabelWidth: '120px'
}
},
mounted () {
this.$refs.toast.setOptions({ position: 'bottom right' })
},
methods: {
closeDialog () {
this.form.requestText = ''
this.showNewVisible = false
},
focusRequestText (e) {
this.$refs.toFocus.focus()
},
openDialog () {
this.showNewVisible = true
},
@ -42,10 +57,7 @@ export default {
progress: this.$Progress,
requestText: this.form.requestText
})
this.$message({
message: 'New prayer request added',
type: 'success'
})
this.$refs.toast.showToast('New prayer request added', { theme: 'success' })
this.closeDialog()
}
}

View File

@ -1,11 +1,23 @@
<template lang="pug">
el-row.journal-request
el-col(:span='4'): p
el-button(icon='check' @click='markPrayed()' title='Pray')
edit-request(:request='request')
full-request(:request='request')
el-col(:span='16'): p {{ text }}
el-col(:span='4'): p: date-from-now(:value='request.asOf')
b-col(xs='12' sm='6' md='4')
b-card(border-variant='dark' no-body)
div.card-body.p-0
p.card-text.mb-1.px-3.pt-3
| {{ text }}
p.card-text.p-0.pr-1.text-right: small.text-muted: em
= '(last activity '
date-from-now(:value='request.asOf')
| )
//-
edit-request(:request='request')
full-request(:request='request')
b-card-footer.text-center.py-1.
#[b-btn(@click='markPrayed()' variant='outline-primary' title='Pray' size='sm'): icon(name='check')]
#[b-btn(variant='outline-secondary' title='Edit' size='sm'): icon(name='pencil')]
#[b-btn(variant='outline-secondary' title='Add Notes' size='sm'): icon(name='file-text-o')]
#[b-btn(variant='outline-secondary' title='View Full Request' size='sm'): icon(name='search')]
br
toast(ref='toast')
</template>
<script>
@ -13,7 +25,6 @@ el-row.journal-request
import moment from 'moment'
import DateFromNow from '../common/DateFromNow'
import EditRequest from './EditRequest'
import FullRequest from './FullRequest'
@ -21,15 +32,16 @@ import actions from '@/store/action-types'
export default {
name: 'request-list-item',
props: [ 'request' ],
data () {
return { interval: null }
props: {
request: { required: true }
},
components: {
DateFromNow,
EditRequest,
FullRequest
},
mounted () {
this.$refs.toast.setOptions({ position: 'bottom right' })
},
methods: {
async markPrayed () {
await this.$store.dispatch(actions.UPDATE_REQUEST, {
@ -38,10 +50,7 @@ export default {
status: 'Prayed',
updateText: ''
})
this.$message({
message: 'Request marked as prayed',
type: 'success'
})
this.$refs.toast.showToast('Request marked as prayed', { theme: 'success' })
}
},
computed: {
@ -53,10 +62,13 @@ export default {
}
}
}
/*
b-row.journal-request
b-col(cols='2'): p
b-btn(@click='markPrayed()' size='sm' variant='outline-primary' title='Pray'): icon(name='check')
edit-request(:request='request')
full-request(:request='request')
b-col(cols='8'): p {{ text }}
b-col(cols='2'): p: date-from-now(:value='request.asOf')
*/
</script>
<style>
.journal-request {
border-bottom: dotted 1px lightgray;
}
</style>

View File

@ -7,14 +7,10 @@ article
<script>
'use strict'
import PageTitle from '../common/PageTitle'
import AuthService from '@/auth/AuthService'
export default {
name: 'log-on',
components: {
PageTitle
},
created () {
this.$Progress.start()
new AuthService().handleAuthentication(this.$store, this.$router)

View File

@ -1,20 +1,33 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import ElementUI from 'element-ui'
import BootstrapVue from 'bootstrap-vue'
import Icon from 'vue-awesome/components/Icon'
import VueProgressBar from 'vue-progressbar'
import 'element-ui/lib/theme-default/index.css'
import VueToast from 'vue-toast'
import 'bootstrap-vue/dist/bootstrap-vue.css'
import 'bootstrap/dist/css/bootstrap.css'
import 'vue-toast/dist/vue-toast.min.css'
// Only import the icons we need; the whole set is ~500K!
import 'vue-awesome/icons/check'
import 'vue-awesome/icons/file-text-o'
import 'vue-awesome/icons/pencil'
import 'vue-awesome/icons/plus'
import 'vue-awesome/icons/search'
import App from './App'
import router from './router'
import store from './store'
import DateFromNow from './components/common/DateFromNow'
import PageTitle from './components/common/PageTitle'
Vue.config.productionTip = false
Vue.use(ElementUI)
Vue.use(BootstrapVue)
Vue.use(VueProgressBar, {
color: 'rgb(32, 160, 255)',
color: 'yellow',
failedColor: 'red',
height: '5px',
transition: {
@ -24,6 +37,11 @@ Vue.use(VueProgressBar, {
}
})
Vue.component('icon', Icon)
Vue.component('date-from-now', DateFromNow)
Vue.component('page-title', PageTitle)
Vue.component('toast', VueToast)
/* eslint-disable no-new */
new Vue({
el: '#app',

View File

@ -241,10 +241,6 @@ async-each@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
async-validator@1.6.9:
version "1.6.9"
resolved "https://registry.yarnpkg.com/async-validator/-/async-validator-1.6.9.tgz#a8309daa8b83421cdbd4628e026d6abb25192d34"
async@1.x, async@^1.4.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
@ -480,10 +476,6 @@ babel-helper-replace-supers@^6.24.1:
babel-traverse "^6.24.1"
babel-types "^6.24.1"
babel-helper-vue-jsx-merge-props@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-2.0.2.tgz#aceb1c373588279e2755ea1cfd35c22394fd33f8"
babel-helpers@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
@ -1001,6 +993,16 @@ boom@5.x.x:
dependencies:
hoek "4.x.x"
bootstrap-vue@^1.0.0-beta.9:
version "1.0.0-beta.9"
resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-1.0.0-beta.9.tgz#4e0bc5bcb95a90dc3bec7124ea3ddf5cc4c6ffa6"
dependencies:
bootstrap "^4.0.0-beta"
bootstrap@^4.0.0-beta:
version "4.0.0-beta"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0-beta.tgz#dc5928175d2e71310bc668cf9e05a907211b72a6"
brace-expansion@^1.0.0, brace-expansion@^1.1.7:
version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
@ -1792,10 +1794,6 @@ deep-is@~0.1.3:
version "0.1.3"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
deepmerge@^1.2.0:
version "1.5.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753"
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
@ -1986,15 +1984,6 @@ electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.18:
version "1.3.21"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.21.tgz#a967ebdcfe8ed0083fc244d1894022a8e8113ea2"
element-ui@^1.4.4:
version "1.4.4"
resolved "https://registry.yarnpkg.com/element-ui/-/element-ui-1.4.4.tgz#cbcb0bf36d06b7e9c8cefdb4514d2d0a50a4a6db"
dependencies:
async-validator "1.6.9"
babel-helper-vue-jsx-merge-props "^2.0.0"
deepmerge "^1.2.0"
throttle-debounce "^1.0.1"
elliptic@^6.0.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
@ -5937,10 +5926,6 @@ text-table@^0.2.0, text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
throttle-debounce@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-1.0.1.tgz#dad0fe130f9daf3719fdea33dc36a8e6ba7f30b5"
throttleit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c"
@ -6208,6 +6193,10 @@ void-elements@^2.0.0, void-elements@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
vue-awesome@^2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/vue-awesome/-/vue-awesome-2.3.3.tgz#e83f976fe5c7f86d207c24ca33731bdc6e9906a9"
vue-hot-reload-api@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.1.0.tgz#9ca58a6e0df9078554ce1708688b6578754d86de"
@ -6256,6 +6245,10 @@ vue-template-es2015-compiler@^1.2.2:
version "1.5.3"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.5.3.tgz#22787de4e37ebd9339b74223bc467d1adee30545"
vue-toast@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/vue-toast/-/vue-toast-3.1.0.tgz#19eb4c8150faf5c31c12f8b897a955d1ac0b5e9e"
vue@^2.4.4:
version "2.4.4"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.4.4.tgz#ea9550b96a71465fd2b8b17b61673b3561861789"