diff --git a/package-lock.json b/package-lock.json index 0d447cb..c1e99f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2767,6 +2767,11 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "bulma": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/bulma/-/bulma-0.7.4.tgz", + "integrity": "sha512-krG2rP6eAX1WE0sf6O0SC/FUVSOBX4m1PBC2+GKLpb2pX0qanaDqcv9U2nu75egFrsHkI0zdWYuk/oGwoszVWg==" + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -5499,8 +5504,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -5521,14 +5525,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -5543,20 +5545,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -5673,8 +5672,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -5686,7 +5684,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -5701,7 +5698,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -5709,14 +5705,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -5735,7 +5729,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -5816,8 +5809,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -5829,7 +5821,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -5915,8 +5906,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -5952,7 +5942,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -5972,7 +5961,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6016,14 +6004,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -9730,8 +9716,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-4.0.8.tgz", "integrity": "sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ=", - "dev": true, - "optional": true + "dev": true }, "rx-lite-aggregates": { "version": "4.0.8", diff --git a/package.json b/package.json index 3d0739d..36c5629 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dependencies": { "ajv": "^5.5.2", "axios": "^0.18.0", + "bulma": "^0.7.4", "vue": "^2.6.10", "vue-router": "^3.0.1", "vuex": "^3.0.1", diff --git a/src/api/invitation/index.js b/src/api/invitation/index.js new file mode 100644 index 0000000..121cd55 --- /dev/null +++ b/src/api/invitation/index.js @@ -0,0 +1,13 @@ +import api from '@/api'; +import getHeader from '@/api/utils/get-header'; + +export default { + async listInvitations(sessionID) { + const res = (await api.client.get('/admin/invitations', getHeader(sessionID))).data.invitations; + return res; + }, + async createInvitation(sessionID, email) { + const res = (await api.client.post('/admin/invitations', { email }, getHeader(sessionID))).data.invitations; + return res; + }, +}; diff --git a/src/router.js b/src/router.js index 3e24533..4c75095 100644 --- a/src/router.js +++ b/src/router.js @@ -79,6 +79,12 @@ const router = new Router({ name: 'profile', component: () => import(/* webpackChunkName: "profile" */ './views/memberIntroduction/Profile.vue'), }, + { + path: '/admin/invitations', + name: 'invitation', + component: () => import(/* webpackChunkName: "invitation" */ './views/admin/Invitation.vue'), + meta: { requiresAdmin: true }, + }, ], }); @@ -101,11 +107,24 @@ router.beforeEach(async (to, from, next) => { console.error(e); store.commit('session/clearSessionID'); } + + if (to.matched.some(record => record.meta.requiresAdmin) && !store.getters['user/isAdmin']) { + store.commit('criticalError/createError', { + response: { + status: 404, + data: { + message: 'Page not Found', + }, + }, + }); + return; + } if (to.matched.some(record => record.meta.requiresAuth) && !store.getters['session/loggedIn']) { next({ path: '/login', query: { redirect: to.fullPath } }); - } else { - next(); + return; } + + next(); }); export default router; diff --git a/src/store/index.js b/src/store/index.js index 557a996..e31c8cf 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -10,6 +10,7 @@ import achievement from './modules/achievement'; import emailConfirmations from './modules/emailConfirmations'; import memberIntroduction from './modules/memberIntroduction'; import editUser from './modules/editUser'; +import invitation from './modules/invitation'; Vue.use(Vuex); @@ -25,6 +26,7 @@ export default new Vuex.Store({ emailConfirmations, memberIntroduction, editUser, + invitation, }, plugins: [ createPersistedState({ diff --git a/src/store/modules/invitation/index.js b/src/store/modules/invitation/index.js new file mode 100644 index 0000000..a489281 --- /dev/null +++ b/src/store/modules/invitation/index.js @@ -0,0 +1,42 @@ +import invitationClient from '@/api/invitation'; + +export default { + namespaced: true, + state: { + invitations: null, + invitationError: null, + }, + /* eslint-disable no-param-reassign */ + mutations: { + setInvitations(state, invitations) { + state.invitations = invitations; + }, + clearInvitationError(state) { + state.invitationError = null; + }, + setInvitationError(state, error) { + state.invitationError = error; + }, + }, + /* eslint-enable no-param-reassign */ + actions: { + async listInvitations({ commit }, sessionID) { + try { + commit('setInvitations', await invitationClient.listInvitations(sessionID)); + } catch (e) { + commit('criticalError/createError', e, { root: true }); + } + }, + async invite({ commit, dispatch }, { sessionID, rawEmails }) { + commit('clearInvitationError'); + const emails = rawEmails.split(/\r\n|\n/).map(email => email.trim()).filter(email => email); + try { + await Promise.all(emails.map(email => invitationClient.createInvitation(sessionID, email))); + } catch (e) { + commit('setInvitationError', e); + } + + dispatch('listInvitations', sessionID); + }, + }, +}; diff --git a/src/store/modules/user/index.js b/src/store/modules/user/index.js index 4503e53..3962e4b 100644 --- a/src/store/modules/user/index.js +++ b/src/store/modules/user/index.js @@ -50,4 +50,9 @@ export default { } }, }, + getters: { + isAdmin(state) { + return state.userData && state.userData.authority === 'ADMIN'; + }, + }, }; diff --git a/src/views/admin/Invitation.vue b/src/views/admin/Invitation.vue new file mode 100644 index 0000000..6990c63 --- /dev/null +++ b/src/views/admin/Invitation.vue @@ -0,0 +1,77 @@ + + + + +