stash
This commit is contained in:
parent
6c69948c44
commit
b6ed492382
6 changed files with 96 additions and 26 deletions
|
@ -6,7 +6,7 @@
|
||||||
<div aria-live="polite" aria-atomic="true" v-if="isLoggedIn"
|
<div aria-live="polite" aria-atomic="true" v-if="isLoggedIn"
|
||||||
class="d-flex justify-content-end align-items-start fixed-top mx-1 my-5 py-3"
|
class="d-flex justify-content-end align-items-start fixed-top mx-1 my-5 py-3"
|
||||||
style="min-height: 200px; z-index: 100000; pointer-events: none">
|
style="min-height: 200px; z-index: 100000; pointer-events: none">
|
||||||
<Toast v-for="toast in toasts" :key="toast" :title="toast.title" :message="toast.message"
|
<Toast v-for="(toast , index) in toasts" :key="index" :title="toast.title" :message="toast.message"
|
||||||
:color="toast.color"
|
:color="toast.color"
|
||||||
@close="removeToast(toast.key)" style="pointer-events: auto"/>
|
@close="removeToast(toast.key)" style="pointer-events: auto"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -11,25 +11,25 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="nav nav-tabs flex-nowrap">
|
<ul class="nav nav-tabs flex-nowrap">
|
||||||
<li class="nav-item">
|
<li class="nav-item" v-if="checkPermission(getEventSlug, 'inventory.view_item')">
|
||||||
<router-link :to="{name: 'items', params: {event: getEventSlug}}"
|
<router-link :to="{name: 'items', params: {event: getEventSlug}}"
|
||||||
:class="['nav-link', { active: getActiveView === 'items' || getActiveView === 'item' }]">
|
:class="['nav-link', { active: getActiveView === 'items' || getActiveView === 'item' }]">
|
||||||
Items
|
Items
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" v-if="checkRole('team')">
|
<li class="nav-item" v-if="checkPermission(getEventSlug, 'tickets.view_issuethread')">
|
||||||
<router-link :to="{name: 'tickets', params: {event: getEventSlug}}"
|
<router-link :to="{name: 'tickets', params: {event: getEventSlug}}"
|
||||||
:class="['nav-link', { active: getActiveView === 'tickets' || getActiveView === 'ticket' }]">
|
:class="['nav-link', { active: getActiveView === 'tickets' || getActiveView === 'ticket' }]">
|
||||||
Tickets
|
Tickets
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
<li class="nav-item" v-if="checkRole('admin')">
|
<li class="nav-item" v-if="checkPermission(getEventSlug, 'inventory.delete_event')">
|
||||||
<router-link :to="{name: 'admin'}" :class="['nav-link', { active: getActiveView === 'admin' }]">
|
<router-link :to="{name: 'admin'}" :class="['nav-link', { active: getActiveView === 'admin' }]">
|
||||||
Admin
|
Admin
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<form class="form-inline mt-1 my-lg-auto my-xl-auto w-100 d-inline">
|
<form class="form-inline mt-1 my-lg-auto my-xl-auto w-100 d-inline" v-if="hasPermissions">
|
||||||
<input
|
<input
|
||||||
class="form-control w-100"
|
class="form-control w-100"
|
||||||
type="search"
|
type="search"
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
disabled
|
disabled
|
||||||
>
|
>
|
||||||
</form>
|
</form>
|
||||||
<div class="custom-control-inline mr-1">
|
<div class="custom-control-inline mr-1" v-if="hasPermissions">
|
||||||
<div class="btn-group btn-group-toggle mx-1">
|
<div class="btn-group btn-group-toggle mx-1">
|
||||||
<button :class="['btn', 'btn-info', { active: layout === 'cards' }]" @click="setLayout('cards')">
|
<button :class="['btn', 'btn-info', { active: layout === 'cards' }]" @click="setLayout('cards')">
|
||||||
<font-awesome-icon icon="th"/>
|
<font-awesome-icon icon="th"/>
|
||||||
|
@ -99,13 +99,14 @@ export default {
|
||||||
]
|
]
|
||||||
}),
|
}),
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['events', 'activeEvent', 'layout']),
|
...mapState(['events', 'layout']),
|
||||||
...mapGetters(['getEventSlug', 'getActiveView', "checkRole"]),
|
...mapGetters(['getEventSlug', 'getActiveView', "checkPermission", "hasPermissions"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['changeEvent', 'changeView', 'searchEventItems']),
|
...mapActions(['changeEvent', 'changeView', 'searchEventItems']),
|
||||||
...mapMutations(['setLayout']),
|
...mapMutations(['setLayout']),
|
||||||
navigateTo(link) {
|
navigateTo(link) {
|
||||||
|
if (this.$router.currentRoute.path !== link)
|
||||||
this.$router.push(link);
|
this.$router.push(link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import Tickets from "@/views/Tickets.vue";
|
||||||
import Ticket from "@/views/Ticket.vue";
|
import Ticket from "@/views/Ticket.vue";
|
||||||
import Admin from "@/views/admin/Admin.vue";
|
import Admin from "@/views/admin/Admin.vue";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import Empty from "@/views/Empty.vue";
|
||||||
|
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
|
@ -21,17 +22,29 @@ const routes = [
|
||||||
{path: '/login', name: 'login', component: Login, meta: {requiresAuth: false}},
|
{path: '/login', name: 'login', component: Login, meta: {requiresAuth: false}},
|
||||||
{path: '/register', name: 'register', component: Register, meta: {requiresAuth: false}},
|
{path: '/register', name: 'register', component: Register, meta: {requiresAuth: false}},
|
||||||
{path: '/howto', name: 'howto', component: HowTo, meta: {requiresAuth: true}},
|
{path: '/howto', name: 'howto', component: HowTo, meta: {requiresAuth: true}},
|
||||||
{path: '/:event/boxes', name: 'boxes', component: Boxes, meta: {requiresAuth: true}},
|
{path: '/:event/boxes', name: 'boxes', component: Boxes, meta:
|
||||||
{path: '/:event/items', name: 'items', component: Items, meta: {requiresAuth: true}},
|
{requiresAuth: true, requiresPermission: 'inventory.view_container'}},
|
||||||
{path: '/:event/box/:uid', name: 'box', component: Boxes, meta: {requiresAuth: true}},
|
{path: '/:event/items', name: 'items', component: Items, meta:
|
||||||
{path: '/:event/item/:uid', name: 'item', component: Items, meta: {requiresAuth: true}},
|
{requiresAuth: true, requiresPermission: 'inventory.view_item'}},
|
||||||
{path: '/:event/tickets', name: 'tickets', component: Tickets, meta: {requiresAuth: true}},
|
{path: '/:event/box/:uid', name: 'box', component: Boxes, meta:
|
||||||
{path: '/:event/ticket/:id', name: 'ticket', component: Ticket, meta: {requiresAuth: true}},
|
{requiresAuth: true, requiresPermission: 'inventory.view_container'}},
|
||||||
{path: '/admin', name: 'admin', component: Admin, meta: {requiresAuth: true}},
|
{path: '/:event/item/:uid', name: 'item', component: Items, meta:
|
||||||
{path: '/admin/files', name: 'files', component: Files, meta: {requiresAuth: true}},
|
{requiresAuth: true, requiresPermission: 'inventory.view_item'}},
|
||||||
{path: '/admin/events', name: 'events', component: Events, meta: {requiresAuth: true}},
|
{path: '/:event/tickets', name: 'tickets', component: Tickets, meta:
|
||||||
{path: '/admin/debug', name: 'debug', component: Debug, meta: {requiresAuth: true}},
|
{requiresAuth: true, requiresPermission: 'inventory.view_issuethread'}},
|
||||||
{path: '/admin/users', name: 'users', component: Events, meta: {requiresAuth: true}},
|
{path: '/:event/ticket/:id', name: 'ticket', component: Ticket, meta:
|
||||||
|
{requiresAuth: true, requiresPermission: 'inventory.view_issuethread'}},
|
||||||
|
{path: '/admin', name: 'admin', component: Admin, meta:
|
||||||
|
{requiresAuth: true, requiresPermission: 'inventory.delete_event'}},
|
||||||
|
{path: '/admin/files', name: 'files', component: Files, meta:
|
||||||
|
{requiresAuth: true, requiresPermission: 'inventory.delete_event'}},
|
||||||
|
{path: '/admin/events', name: 'events', component: Events, meta:
|
||||||
|
{requiresAuth: true, requiresPermission: 'inventory.delete_event'}},
|
||||||
|
{path: '/admin/debug', name: 'debug', component: Debug, meta:
|
||||||
|
{requiresAuth: true, requiresPermission: 'inventory.delete_event'}},
|
||||||
|
{path: '/admin/users', name: 'users', component: Events, meta:
|
||||||
|
{requiresAuth: true, requiresPermission: 'inventory.delete_event'}},
|
||||||
|
{path: '/user', name: 'user', component: Empty, meta: {requiresAuth: true}},
|
||||||
{path: '*', component: Error},
|
{path: '*', component: Error},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -57,11 +70,16 @@ const router = new VueRouter({
|
||||||
|
|
||||||
router.beforeEach((to, from, next) => {
|
router.beforeEach((to, from, next) => {
|
||||||
if (to.meta.requiresAuth && !store.getters.isLoggedIn) {
|
if (to.meta.requiresAuth && !store.getters.isLoggedIn) {
|
||||||
//console.log("Not logged in, redirecting to login page")
|
console.log("Not logged in, redirecting to login page")
|
||||||
next({
|
next({
|
||||||
name: 'login',
|
name: 'login',
|
||||||
query: {redirect: to.fullPath},
|
query: {redirect: to.fullPath},
|
||||||
})
|
})
|
||||||
|
} else if (to.meta.requiresPermission && !store.getters.checkPermission(to.params.event || "*", to.meta.requiresPermission)) {
|
||||||
|
console.log("Not enough permissions, redirecting to empty page")
|
||||||
|
next({
|
||||||
|
path: '/user',
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,11 +67,11 @@ const store = new Vuex.Store({
|
||||||
loadedBoxes: [],
|
loadedBoxes: [],
|
||||||
toasts: [],
|
toasts: [],
|
||||||
tickets: [],
|
tickets: [],
|
||||||
userRoles: ['admin', 'team', 'orga', 'user'],
|
|
||||||
lastEvent: localStorage.getItem('lf_lastEvent') || '36C3',
|
lastEvent: localStorage.getItem('lf_lastEvent') || '36C3',
|
||||||
lastUsed: JSON.parse(localStorage.getItem('lf_lastUsed') || '{}'),
|
lastUsed: JSON.parse(localStorage.getItem('lf_lastUsed') || '{}'),
|
||||||
remember: false,
|
remember: false,
|
||||||
user: null,
|
user: null,
|
||||||
|
userPermissions: [],
|
||||||
token: null,
|
token: null,
|
||||||
token_expiry: null,
|
token_expiry: null,
|
||||||
local_loaded: false,
|
local_loaded: false,
|
||||||
|
@ -81,11 +81,13 @@ const store = new Vuex.Store({
|
||||||
getActiveView: state => state.route.name || 'items',
|
getActiveView: state => state.route.name || 'items',
|
||||||
getFilters: state => state.route.query,
|
getFilters: state => state.route.query,
|
||||||
getBoxes: state => state.loadedBoxes,
|
getBoxes: state => state.loadedBoxes,
|
||||||
checkRole: state => role => state.userRoles.includes(role),
|
checkPermission: state => (event, perm) => state.userPermissions.includes(`${event}:${perm}`) || state.userPermissions.includes(`*:${perm}`),
|
||||||
|
hasPermissions: state => state.userPermissions.length > 0,
|
||||||
isLoggedIn(state) {
|
isLoggedIn(state) {
|
||||||
if (!state.local_loaded) {
|
if (!state.local_loaded) {
|
||||||
state.remember = localStorage.getItem('remember') === 'true'
|
state.remember = localStorage.getItem('remember') === 'true'
|
||||||
state.user = localStorage.getItem('user')
|
state.user = localStorage.getItem('user')
|
||||||
|
state.userPermissions = JSON.parse(localStorage.getItem('permissions') || '[]')
|
||||||
state.token = localStorage.getItem('token')
|
state.token = localStorage.getItem('token')
|
||||||
state.token_expiry = localStorage.getItem('token_expiry')
|
state.token_expiry = localStorage.getItem('token_expiry')
|
||||||
state.local_loaded = true
|
state.local_loaded = true
|
||||||
|
@ -156,6 +158,11 @@ const store = new Vuex.Store({
|
||||||
if (user)
|
if (user)
|
||||||
localStorage.setItem('user', user);
|
localStorage.setItem('user', user);
|
||||||
},
|
},
|
||||||
|
setPermissions(state, permissions) {
|
||||||
|
state.userPermissions = permissions;
|
||||||
|
if (permissions)
|
||||||
|
localStorage.setItem('permissions', JSON.stringify(permissions));
|
||||||
|
},
|
||||||
setToken(state, {token, expiry}) {
|
setToken(state, {token, expiry}) {
|
||||||
state.token = token;
|
state.token = token;
|
||||||
state.token_expiry = expiry;
|
state.token_expiry = expiry;
|
||||||
|
@ -167,6 +174,7 @@ const store = new Vuex.Store({
|
||||||
state.user = null;
|
state.user = null;
|
||||||
state.token = null;
|
state.token = null;
|
||||||
localStorage.removeItem('user');
|
localStorage.removeItem('user');
|
||||||
|
localStorage.removeItem('permissions');
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
localStorage.removeItem('token_expiry');
|
localStorage.removeItem('token_expiry');
|
||||||
router.push('/login');
|
router.push('/login');
|
||||||
|
@ -228,6 +236,7 @@ const store = new Vuex.Store({
|
||||||
async loadUserInfo({commit}) {
|
async loadUserInfo({commit}) {
|
||||||
const {data} = await axios.get('/2/self/');
|
const {data} = await axios.get('/2/self/');
|
||||||
commit('setUser', data.username);
|
commit('setUser', data.username);
|
||||||
|
commit('setPermissions', data.permissions);
|
||||||
},
|
},
|
||||||
async loadEvents({commit}) {
|
async loadEvents({commit}) {
|
||||||
const {data} = await axios.get('/2/events/');
|
const {data} = await axios.get('/2/events/');
|
||||||
|
|
29
web/src/views/Empty.vue
Normal file
29
web/src/views/Empty.vue
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
<template>
|
||||||
|
<div class="container-fluid px-xl-5 mt-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-8 offset-xl-2">
|
||||||
|
<div class="card bg-dark text-light mb-2" id="filters">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="text-center">User: {{user}}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Your Acoount is not yet activated. Please contact an admin.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import {mapState} from "vuex";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Empty',
|
||||||
|
computed: mapState(['user']),
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
|
@ -1,5 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<p>Error</p>
|
<div class="container-fluid px-xl-5 mt-3">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-xl-8 offset-xl-2">
|
||||||
|
<div class="card bg-dark text-light mb-2" id="filters">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3 class="text-center">Error</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<p>Something went wrong. <a href="/">Go back to the start page </a>or contact an admin.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
Loading…
Reference in a new issue