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"
|
||||
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">
|
||||
<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"
|
||||
@close="removeToast(toast.key)" style="pointer-events: auto"/>
|
||||
</div>
|
||||
|
|
|
@ -11,25 +11,25 @@
|
|||
</div>
|
||||
</div>
|
||||
<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}}"
|
||||
:class="['nav-link', { active: getActiveView === 'items' || getActiveView === 'item' }]">
|
||||
Items
|
||||
</router-link>
|
||||
</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}}"
|
||||
:class="['nav-link', { active: getActiveView === 'tickets' || getActiveView === 'ticket' }]">
|
||||
Tickets
|
||||
</router-link>
|
||||
</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' }]">
|
||||
Admin
|
||||
</router-link>
|
||||
</li>
|
||||
</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
|
||||
class="form-control w-100"
|
||||
type="search"
|
||||
|
@ -39,7 +39,7 @@
|
|||
disabled
|
||||
>
|
||||
</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">
|
||||
<button :class="['btn', 'btn-info', { active: layout === 'cards' }]" @click="setLayout('cards')">
|
||||
<font-awesome-icon icon="th"/>
|
||||
|
@ -99,14 +99,15 @@ export default {
|
|||
]
|
||||
}),
|
||||
computed: {
|
||||
...mapState(['events', 'activeEvent', 'layout']),
|
||||
...mapGetters(['getEventSlug', 'getActiveView', "checkRole"]),
|
||||
...mapState(['events', 'layout']),
|
||||
...mapGetters(['getEventSlug', 'getActiveView', "checkPermission", "hasPermissions"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['changeEvent', 'changeView', 'searchEventItems']),
|
||||
...mapMutations(['setLayout']),
|
||||
navigateTo(link) {
|
||||
this.$router.push(link);
|
||||
if (this.$router.currentRoute.path !== link)
|
||||
this.$router.push(link);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,6 +13,7 @@ import Tickets from "@/views/Tickets.vue";
|
|||
import Ticket from "@/views/Ticket.vue";
|
||||
import Admin from "@/views/admin/Admin.vue";
|
||||
import store from "@/store";
|
||||
import Empty from "@/views/Empty.vue";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
|
@ -21,17 +22,29 @@ const routes = [
|
|||
{path: '/login', name: 'login', component: Login, meta: {requiresAuth: false}},
|
||||
{path: '/register', name: 'register', component: Register, meta: {requiresAuth: false}},
|
||||
{path: '/howto', name: 'howto', component: HowTo, meta: {requiresAuth: true}},
|
||||
{path: '/:event/boxes', name: 'boxes', component: Boxes, meta: {requiresAuth: true}},
|
||||
{path: '/:event/items', name: 'items', component: Items, meta: {requiresAuth: true}},
|
||||
{path: '/:event/box/:uid', name: 'box', component: Boxes, meta: {requiresAuth: true}},
|
||||
{path: '/:event/item/:uid', name: 'item', component: Items, meta: {requiresAuth: true}},
|
||||
{path: '/:event/tickets', name: 'tickets', component: Tickets, meta: {requiresAuth: true}},
|
||||
{path: '/:event/ticket/:id', name: 'ticket', component: Ticket, meta: {requiresAuth: true}},
|
||||
{path: '/admin', name: 'admin', component: Admin, meta: {requiresAuth: true}},
|
||||
{path: '/admin/files', name: 'files', component: Files, meta: {requiresAuth: true}},
|
||||
{path: '/admin/events', name: 'events', component: Events, meta: {requiresAuth: true}},
|
||||
{path: '/admin/debug', name: 'debug', component: Debug, meta: {requiresAuth: true}},
|
||||
{path: '/admin/users', name: 'users', component: Events, meta: {requiresAuth: true}},
|
||||
{path: '/:event/boxes', name: 'boxes', component: Boxes, meta:
|
||||
{requiresAuth: true, requiresPermission: 'inventory.view_container'}},
|
||||
{path: '/:event/items', name: 'items', component: Items, meta:
|
||||
{requiresAuth: true, requiresPermission: 'inventory.view_item'}},
|
||||
{path: '/:event/box/:uid', name: 'box', component: Boxes, meta:
|
||||
{requiresAuth: true, requiresPermission: 'inventory.view_container'}},
|
||||
{path: '/:event/item/:uid', name: 'item', component: Items, meta:
|
||||
{requiresAuth: true, requiresPermission: 'inventory.view_item'}},
|
||||
{path: '/:event/tickets', name: 'tickets', component: Tickets, meta:
|
||||
{requiresAuth: true, requiresPermission: 'inventory.view_issuethread'}},
|
||||
{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},
|
||||
];
|
||||
|
||||
|
@ -57,11 +70,16 @@ const router = new VueRouter({
|
|||
|
||||
router.beforeEach((to, from, next) => {
|
||||
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({
|
||||
name: 'login',
|
||||
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 {
|
||||
next()
|
||||
}
|
||||
|
|
|
@ -67,11 +67,11 @@ const store = new Vuex.Store({
|
|||
loadedBoxes: [],
|
||||
toasts: [],
|
||||
tickets: [],
|
||||
userRoles: ['admin', 'team', 'orga', 'user'],
|
||||
lastEvent: localStorage.getItem('lf_lastEvent') || '36C3',
|
||||
lastUsed: JSON.parse(localStorage.getItem('lf_lastUsed') || '{}'),
|
||||
remember: false,
|
||||
user: null,
|
||||
userPermissions: [],
|
||||
token: null,
|
||||
token_expiry: null,
|
||||
local_loaded: false,
|
||||
|
@ -81,11 +81,13 @@ const store = new Vuex.Store({
|
|||
getActiveView: state => state.route.name || 'items',
|
||||
getFilters: state => state.route.query,
|
||||
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) {
|
||||
if (!state.local_loaded) {
|
||||
state.remember = localStorage.getItem('remember') === 'true'
|
||||
state.user = localStorage.getItem('user')
|
||||
state.userPermissions = JSON.parse(localStorage.getItem('permissions') || '[]')
|
||||
state.token = localStorage.getItem('token')
|
||||
state.token_expiry = localStorage.getItem('token_expiry')
|
||||
state.local_loaded = true
|
||||
|
@ -156,6 +158,11 @@ const store = new Vuex.Store({
|
|||
if (user)
|
||||
localStorage.setItem('user', user);
|
||||
},
|
||||
setPermissions(state, permissions) {
|
||||
state.userPermissions = permissions;
|
||||
if (permissions)
|
||||
localStorage.setItem('permissions', JSON.stringify(permissions));
|
||||
},
|
||||
setToken(state, {token, expiry}) {
|
||||
state.token = token;
|
||||
state.token_expiry = expiry;
|
||||
|
@ -167,6 +174,7 @@ const store = new Vuex.Store({
|
|||
state.user = null;
|
||||
state.token = null;
|
||||
localStorage.removeItem('user');
|
||||
localStorage.removeItem('permissions');
|
||||
localStorage.removeItem('token');
|
||||
localStorage.removeItem('token_expiry');
|
||||
router.push('/login');
|
||||
|
@ -219,7 +227,7 @@ const store = new Vuex.Store({
|
|||
async afterLogin({dispatch}) {
|
||||
const boxes = dispatch('loadBoxes');
|
||||
const items = dispatch('loadEventItems');
|
||||
const tickets = dispatch('loadTickets');
|
||||
const tickets = dispatch('loadTickets');
|
||||
const user = dispatch('loadUserInfo');
|
||||
},
|
||||
async fetchImage({state}, url) {
|
||||
|
@ -228,6 +236,7 @@ const store = new Vuex.Store({
|
|||
async loadUserInfo({commit}) {
|
||||
const {data} = await axios.get('/2/self/');
|
||||
commit('setUser', data.username);
|
||||
commit('setPermissions', data.permissions);
|
||||
},
|
||||
async loadEvents({commit}) {
|
||||
const {data} = await axios.get('/2/events/');
|
||||
|
@ -247,7 +256,7 @@ const store = new Vuex.Store({
|
|||
try {
|
||||
commit('replaceLoadedItems', []);
|
||||
const slug = getters.getEventSlug;
|
||||
if( slug in state.itemCache ) {
|
||||
if (slug in state.itemCache) {
|
||||
commit('replaceLoadedItems', state.itemCache[slug]);
|
||||
}
|
||||
const {data} = await axios.get(`/2/${slug}/items/`);
|
||||
|
|
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>
|
||||
<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>
|
||||
|
||||
<script>
|
||||
|
|
Loading…
Reference in a new issue