+Vuex +ElementUI +Pug

- Log on now works, storing user profile in vuex state
- Templates in Pug = awesome
This commit is contained in:
Daniel J. Summers
2017-08-05 22:48:09 -05:00
parent 338f11d1ab
commit 196db30cc5
13 changed files with 506 additions and 122 deletions

View File

@@ -1,37 +1,17 @@
<template>
<div id="app">
<navigation :auth="auth" />
<div id="content" class="container">
<router-view :auth="auth"></router-view>
</div>
<footer>
<p class="text-right"><i>myPrayerJournal v0.8.0</i></p>
</footer>
</div>
<template lang="pug">
#app
navigation
#content.container
router-view
footer
p.text-right: i myPrayerJournal v0.8.0
</template>
<script>
import AuthService from './auth/AuthService'
import Navigation from './components/Navigation.vue'
const auth = new AuthService()
const { login, logout, authenticated, authNotifier } = auth
export default {
name: 'app',
data: function () {
authNotifier.on('authChange', authState => {
this.authenticated = authState.authenticated
})
return {
auth,
authenticated
}
},
methods: {
login,
logout
},
components: {
Navigation
}

0
src/app/src/api/Api.js Normal file
View File

View File

@@ -1,11 +1,9 @@
import auth0 from 'auth0-js'
import { AUTH_CONFIG } from './auth0-variables'
import EventEmitter from 'EventEmitter'
import router from './../router'
import * as types from '@/store/mutation-types'
export default class AuthService {
authenticated = this.isAuthenticated()
authNotifier = new EventEmitter()
constructor () {
this.login = this.login.bind(this)
@@ -20,24 +18,62 @@ export default class AuthService {
redirectUri: AUTH_CONFIG.callbackUrl,
audience: `https://${AUTH_CONFIG.domain}/userinfo`,
responseType: 'token id_token',
scope: 'openid'
scope: 'openid profile email'
})
login () {
this.auth0.authorize()
}
handleAuthentication () {
this.auth0.parseHash((err, authResult) => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult)
router.replace('/dashboard')
} else if (err) {
/**
* Promisified parseHash function
*/
parseHash () {
return new Promise((resolve, reject) => {
this.auth0.parseHash((err, authResult) => {
if (err) {
reject(err)
} else {
resolve(authResult)
}
})
})
}
/**
* Promisified userInfo function
*
* @param token The auth token from the login result
*/
userInfo (token) {
return new Promise((resolve, reject) => {
this.auth0.client.userInfo(token, (err, user) => {
if (err) {
reject(err)
} else {
resolve(user)
}
})
})
}
handleAuthentication (store, router) {
this.parseHash()
.then(authResult => {
if (authResult && authResult.accessToken && authResult.idToken) {
this.setSession(authResult)
this.userInfo(authResult.accessToken)
.then(user => {
store.commit(types.USER_LOGGED_ON, user)
router.replace('/dashboard')
})
}
})
.catch(err => {
router.replace('/')
console.log(err)
alert(`Error: ${err.error}. Check the console for further details.`)
}
})
})
}
setSession (authResult) {
@@ -48,17 +84,16 @@ export default class AuthService {
localStorage.setItem('access_token', authResult.accessToken)
localStorage.setItem('id_token', authResult.idToken)
localStorage.setItem('expires_at', expiresAt)
this.authNotifier.emit('authChange', { authenticated: true })
}
logout () {
logout (store, router) {
// Clear access token and ID token from local storage
localStorage.removeItem('access_token')
localStorage.removeItem('id_token')
localStorage.removeItem('expires_at')
this.userProfile = null
this.authNotifier.emit('authChange', false)
localStorage.setItem('user_profile', JSON.stringify({}))
// navigate to the home route
store.commit(types.USER_LOGGED_OFF)
router.replace('/')
}

View File

@@ -1,21 +1,23 @@
<template>
<article>
<page-title title="Your Dashboard" />
<p>here you are! {{ JSON.stringify(this.user) }}</p>
</article>
<template lang="pug">
article
page-title(:title="title")
p here you are!
</template>
<script>
import { mapState } from 'vuex'
import PageTitle from './PageTitle'
export default {
name: 'dashboard',
props: ['user'],
data () {
return {}
},
components: {
PageTitle
},
computed: {
title () {
return `${this.user.given_name}'s Dashboard`
},
...mapState(['user'])
}
}
</script>

View File

@@ -1,22 +1,15 @@
<template>
<article>
<page-title title="Welcome!" hideOnPage="true" />
<div class="row">
<div class="col">
<p>&nbsp;</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.&nbsp; It
will also allow individuals to review their answered prayers.
</p>
<p>
This site is currently in very limited alpha, as it is being developed with a core group of test users.&nbsp;
If this is something in which you are interested, check back around mid-November 2017 for an update on the
development progress.
</p>
</div>
</div>
</article>
<template lang="pug">
article
page-title(title="Welcome!" hideOnPage="true")
p &nbsp;
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.&nbsp; It will also
allow individuals to review their answered prayers.
p.
This site is currently in very limited alpha, as it is being developed with a core group of test users.&nbsp; If
this is something in which you are interested, check back around mid-November 2017 for an update on the
development progress.
</template>
<script>
@@ -24,9 +17,6 @@ import PageTitle from './PageTitle.vue'
export default {
name: 'home',
data () {
return {}
},
components: {
PageTitle
}

View File

@@ -1,25 +1,41 @@
<template>
<b-navbar toggleable type="inverse" variant="inverse" fixed="top">
<b-nav-toggle target="navCollapse"></b-nav-toggle>
<b-link class="navbar-brand" :to="{ name: 'Home' }">
<span style="font-weight:100;">my</span><span style="font-weight:600;">Prayer</span><span style="font-weight:700;">Journal</span>
</b-link>
<b-collapse is-nav id="navCollapse">
<b-nav is-nav-bar>
<b-nav-item v-if="auth.authenticated" :to="{ name: 'Dashboard' }">Dashboard</b-nav-item>
<b-nav-item v-if="auth.authenticated" @click="auth.logout()">Log Off</b-nav-item>
<b-nav-item v-if="!auth.authenticated" @click="auth.login()">Log On</b-nav-item>
</b-nav>
</b-collapse>
</b-navbar>
<template lang="pug">
el-menu(theme="dark" mode="horizontal" class="mpj-top-nav" router=true)
el-menu-item(index="/")
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="/dashboard") Dashboard
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
</template>
<script>
import { mapState } from 'vuex'
import AuthService from '@/auth/AuthService'
export default {
name: 'navigation',
props: ['auth'],
data: function () {
return { }
}
data () {
return {
auth0: new AuthService()
}
},
methods: {
logOn () {
this.auth0.login()
},
logOff () {
this.auth0.logout(this.$store, this.$router)
}
},
computed: mapState(['isAuthenticated'])
}
</script>
<style scoped>
.mpj-top-nav {
position: fixed;
top: 0px;
width: 100%;
}
</style>

View File

@@ -1,14 +1,11 @@
<template>
<h2 v-if="!hideOnPage" class="mpj-page-title" v-html="title"></h2>
<template lang="pug">
h2.mpj-page-title(v-if="!hideOnPage" v-html="title")
</template>
<script>
export default {
name: 'page-title',
props: ['title', 'hideOnPage'],
data () {
return {}
},
created () {
document.title = `${this.title} « myPrayerJournal`
},

View File

@@ -3,12 +3,13 @@
</template>
<script>
export default {
name: 'log-on',
props: ['auth'],
data () {
this.auth.handleAuthentication()
return {}
}
import AuthService from '@/auth/AuthService'
export default {
name: 'log-on',
data () {
new AuthService().handleAuthentication(this.$store, this.$router)
return {}
}
}
</script>

View File

@@ -1,18 +1,22 @@
// 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 BootstrapVue from 'bootstrap-vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
Vue.use(BootstrapVue)
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
template: '<App/>',
components: { App }
})

View File

@@ -0,0 +1,31 @@
import Vue from 'vue'
import Vuex from 'vuex'
import AuthService from '@/auth/AuthService'
import * as types from './mutation-types'
Vue.use(Vuex)
this.auth0 = new AuthService()
export default new Vuex.Store({
state: {
user: JSON.parse(localStorage.getItem('user_profile') || '{}'),
isAuthenticated: this.auth0.isAuthenticated()
},
mutations: {
[types.USER_LOGGED_ON] (state, user) {
localStorage.setItem('user_profile', JSON.stringify(user))
state.user = user
state.isAuthenticated = true
},
[types.USER_LOGGED_OFF] (state) {
state.user = {}
state.isAuthenticated = false
}
},
actions: {},
getters: {},
modules: {}
})

View File

@@ -0,0 +1,2 @@
export const USER_LOGGED_OFF = 'user-logged-out'
export const USER_LOGGED_ON = 'user-logged-on'