diff --git a/package.json b/package.json index fe40c5f..f8e51f1 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "start": "webpack-dev-server --open" }, "dependencies": { + "qrcode": "^1.4.4", "safe-eval": "^0.4.1", "vee-validate": "^2.2.8", "vue": "^2.6.10", diff --git a/src/_helpers/api-util.js b/src/_helpers/api-util.js index 3609999..958de69 100644 --- a/src/_helpers/api-util.js +++ b/src/_helpers/api-util.js @@ -1,3 +1,8 @@ +let landingPage = null; +export function getLandingPage () { return landingPage; } +export function setLandingPage (page) { landingPage = page; } +export function resetLandingPage () { landingPage = null; } + export function currentUser() { let userJson = localStorage.getItem('user'); return userJson ? JSON.parse(userJson) : null; @@ -23,21 +28,25 @@ export function getWithAuth() { }; } -export function postWithAuth(obj) { - return { - method: 'POST', - headers: { ...authHeader(), 'Content-Type': 'application/json' }, - body: JSON.stringify(obj) - }; +function entityWithAuth(method, obj) { + console.log("entityWithAuth("+method+"): obj="+obj+" (type="+(typeof obj)+")"); + if (typeof obj === 'undefined' || obj === null || obj === 'undefined') { + return { + method: method, + headers: { ...authHeader(), 'Content-Type': 'application/json' } + }; + } else { + return { + method: method, + headers: { ...authHeader(), 'Content-Type': 'application/json' }, + body: JSON.stringify(obj) + }; + } } -export function putWithAuth(obj) { - return { - method: 'PUT', - headers: { ...authHeader(), 'Content-Type': 'application/json' }, - body: JSON.stringify(obj) - }; -} +export function postWithAuth(obj) { return entityWithAuth('POST', obj); } + +export function putWithAuth(obj) { return entityWithAuth('PUT', obj); } export function deleteWithAuth() { return { diff --git a/src/_helpers/router.js b/src/_helpers/router.js index 8460f38..6134459 100644 --- a/src/_helpers/router.js +++ b/src/_helpers/router.js @@ -5,6 +5,7 @@ import HomePage from '../account/HomePage' import RegisterPage from '../auth/RegisterPage' import LoginPage from '../auth/LoginPage' import ProfilePage from '../account/profile/ProfilePage' +import ActionPage from '../account/profile/ActionPage' import PolicyPage from '../account/profile/PolicyPage' import NotificationsPage from '../account/NotificationsPage' import ChangePasswordPage from '../account/profile/ChangePasswordPage' @@ -15,7 +16,7 @@ import AccountsPage from '../admin/AccountsPage' import StripePayment from "../account/payment/StripePayment"; import InviteCodePayment from "../account/payment/InviteCodePayment"; import UnknownPayment from "../account/payment/UnknownPayment"; -import { currentUser } from '../_helpers' +import { currentUser, setLandingPage } from '../_helpers' Vue.use(Router); @@ -43,6 +44,7 @@ export const router = new Router({ }, { path: '/me', component: ProfilePage }, { path: '/me/policy', component: PolicyPage }, + { path: '/me/action', component: ActionPage }, { path: '/me/changePassword', component: ChangePasswordPage }, { path: '/notifications', component: NotificationsPage }, { @@ -78,7 +80,10 @@ router.beforeEach((to, from, next) => { if (authRequired) { // redirect to login page if not logged in and trying to access a restricted page - if (!user) return next('/login'); + if (!user) { + setLandingPage(to); + return next('/login'); + } // redirect to home page if not admin and trying to access an admin page if (to.path.startsWith('/admin') && user.admin !== true) return next('/'); diff --git a/src/_services/user.service.js b/src/_services/user.service.js index a85fab2..fee6515 100644 --- a/src/_services/user.service.js +++ b/src/_services/user.service.js @@ -12,9 +12,20 @@ export const userService = { addPolicyContactById, removePolicyContactByUuid, update, - delete: _delete + delete: _delete, + approveAction, + denyAction }; +function setSessionUser (user) { + // login successful if there's a session token in the response + if (user.token) { + // store user details and session token in local storage to keep user logged in between page refreshes + localStorage.setItem('user', JSON.stringify(user)); + } + return user; +} + function login(name, password, messages, errors) { const requestOptions = { method: 'POST', @@ -23,14 +34,7 @@ function login(name, password, messages, errors) { }; return fetch(`${config.apiUrl}/auth/login`, requestOptions) .then(handleAuthResponse(messages, errors)) - .then(user => { - // login successful if there's a session token in the response - if (user.token) { - // store user details and session token in local storage to keep user logged in between page refreshes - localStorage.setItem('user', JSON.stringify(user)); - } - return user; - }); + .then(setSessionUser); } function logout() { @@ -46,13 +50,7 @@ function register(user, messages, errors) { }; return fetch(`${config.apiUrl}/auth/register`, requestOptions) .then(handleAuthResponse(messages, errors)) - .then(user => { - if (user.token) { - // store user details and session token in local storage to keep user logged in between page refreshes - localStorage.setItem('user', JSON.stringify(user)); - } - return user; - }); + .then(setSessionUser); } function getAll(messages, errors) { @@ -79,6 +77,16 @@ function removePolicyContactByUuid(id, uuid, messages, errors) { return fetch(`${config.apiUrl}/users/${id}/policy/contacts/${uuid}`, deleteWithAuth()).then(handleCrudResponse(messages, errors)); } +function approveAction(id, code, messages, errors) { + return fetch(`${config.apiUrl}/auth/approve/${code}`, postWithAuth()) + .then(handleCrudResponse(messages, errors)) + .then(setSessionUser); +} + +function denyAction(id, code, messages, errors) { + return fetch(`${config.apiUrl}/auth/deny/${code}`, postWithAuth()).then(handleCrudResponse(messages, errors)); +} + function update(user, messages, errors) { return fetch(`${config.apiUrl}/users/${user.uuid}`, postWithAuth(user)).then(handleCrudResponse(messages, errors)); } diff --git a/src/_store/account.module.js b/src/_store/account.module.js index 7ae2aa3..f194e6e 100644 --- a/src/_store/account.module.js +++ b/src/_store/account.module.js @@ -1,13 +1,13 @@ import { userService } from '../_services'; -import { router } from '../_helpers'; +import { router, getLandingPage, resetLandingPage } from '../_helpers'; // todo: why can't we import currentUser from api-util and use that here? // when I try to do that, webpack succeeds but then an error occurs loading any page, with the // error message "_helpers.currentUser is not defined" const user = JSON.parse(localStorage.getItem('user')); const state = user - ? { status: { loggedIn: true }, user } - : { status: {}, user: null }; + ? { status: { loggedIn: true }, user, actionStatus: {} } + : { status: {}, user: null, actionStatus: {} }; const actions = { login({ dispatch, commit }, { user, messages, errors }) { @@ -16,7 +16,14 @@ const actions = { .then( user => { commit('loginSuccess', user); - router.push('/'); + const landing = getLandingPage(); + console.log('getLandingPage returned: '+JSON.stringify(landing)); + if (landing === null) { + router.push('/'); + } else { + resetLandingPage(); + router.push(landing.fullPath); + } }, error => { commit('loginFailure', error); @@ -55,7 +62,7 @@ const actions = { router.push('/me'); setTimeout(() => { // display success message after route change completes - dispatch('alert/success', 'Profile update was successful', { root: true }); + dispatch('alert/success', messages.message_profile_update_success, { root: true }); }) }, error => { @@ -63,7 +70,25 @@ const actions = { dispatch('alert/error', error, { root: true }); } ); - } + }, + + approveAction({ commit }, {uuid, code, messages, errors}) { + commit('approveActionRequest'); + userService.approveAction(uuid, code, messages, errors) + .then( + policy => commit('approveActionSuccess', policy), + error => commit('approveActionFailure', error) + ); + }, + denyAction({ commit }, {uuid, code, messages, errors}) { + commit('denyActionRequest'); + userService.denyAction(uuid, code, messages, errors) + .then( + policy => commit('denyActionSuccess', policy), + error => commit('denyActionFailure', error) + ); + }, + }; const mutations = { @@ -79,10 +104,12 @@ const mutations = { state.status = {}; state.user = null; }, + logout(state) { state.status = {}; state.user = null; }, + registerRequest(state, user) { state.status = { registering: true }; state.user = user; @@ -93,7 +120,29 @@ const mutations = { }, registerFailure(state) { state.status = {}; + }, + + approveActionRequest(state) { + state.actionStatus = { requesting: true, type: 'approve' }; + }, + approveActionSuccess(state, user) { + state.actionStatus = { success: true, type: 'approve', result: user }; + if (user.token) state.user = user; + }, + approveActionFailure(state, error) { + state.actionStatus = { error: error, type: 'approve' }; + }, + denyActionRequest(state) { + state.actionStatus = { requesting: true, type: 'deny' }; + }, + denyActionSuccess(state, denial) { + state.actionStatus = { success: true, type: 'deny', result: denial }; + state.denial = denial; + }, + denyActionFailure(state, error) { + state.actionStatus = { error: error, type: 'deny' }; } + }; export const account = { diff --git a/src/_store/users.module.js b/src/_store/users.module.js index 96254a8..e7b777d 100644 --- a/src/_store/users.module.js +++ b/src/_store/users.module.js @@ -6,7 +6,8 @@ const state = { user: null, policy: {}, policyStatus: {}, - contact: null + contact: null, + authenticator: null }; const actions = { @@ -131,6 +132,9 @@ const mutations = { }, addPolicyContactByUuidSuccess(state, contact) { state.contact = contact; + if (contact.type === 'authenticator') { + state.authenticator = contact.info; + } }, addPolicyContactByUuidFailure(state, error) { state.contact = { error }; diff --git a/src/account/profile/ActionPage.vue b/src/account/profile/ActionPage.vue new file mode 100644 index 0000000..db79a66 --- /dev/null +++ b/src/account/profile/ActionPage.vue @@ -0,0 +1,65 @@ + + + \ No newline at end of file diff --git a/src/account/profile/PolicyPage.vue b/src/account/profile/PolicyPage.vue index 46bb0c5..2764075 100644 --- a/src/account/profile/PolicyPage.vue +++ b/src/account/profile/PolicyPage.vue @@ -1,5 +1,12 @@ \ No newline at end of file