wip
This commit is contained in:
parent
0f911589ca
commit
5bdfe313de
65 changed files with 2219 additions and 77 deletions
|
@ -47,7 +47,8 @@ export default {
|
|||
message: "Connecting to websocket...",
|
||||
color: "warning"
|
||||
});
|
||||
this.notify_socket = new WebSocket('wss://' + window.location.host + '/ws/2/notify/');
|
||||
const scheme = window.location.protocol === "https:" ? "wss" : "ws";
|
||||
this.notify_socket = new WebSocket(scheme + '://' + window.location.host + '/ws/2/notify/');
|
||||
this.notify_socket.onopen = (e) => {
|
||||
if (this.socket_toast) {
|
||||
this.removeToast(this.socket_toast.key);
|
||||
|
@ -96,7 +97,7 @@ export default {
|
|||
},
|
||||
},
|
||||
created: function () {
|
||||
this.tryConnect();
|
||||
//this.tryConnect();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -16,15 +16,11 @@
|
|||
|
||||
<script>
|
||||
import Modal from '@/components/Modal';
|
||||
import config from '../config';
|
||||
|
||||
export default {
|
||||
name: 'Lightbox',
|
||||
components: {Modal},
|
||||
props: ['file'],
|
||||
data: () => ({
|
||||
baseUrl: config.service.url,
|
||||
}),
|
||||
props: ['file']
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
@ -10,27 +10,25 @@
|
|||
:class="{ active: event.slug === getEventSlug }" @click="changeEvent(event)">{{ event.slug }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="custom-control-inline mr-1">
|
||||
<button type="button" class="btn mx-1 text-nowrap btn-success" @click="$emit('addClicked')">
|
||||
<font-awesome-icon icon="plus"/>
|
||||
<span class="d-none d-md-inline"> Add</span>
|
||||
</button>
|
||||
<div class="btn-group btn-group-toggle">
|
||||
<button :class="['btn', 'btn-info', { active: layout === 'cards' }]" @click="setLayout('cards')">
|
||||
<font-awesome-icon icon="th"/>
|
||||
</button>
|
||||
<button :class="['btn', 'btn-info', { active: layout === 'table' }]" @click="setLayout('table')">
|
||||
<font-awesome-icon icon="list"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
|
||||
<ul class="nav nav-tabs flex-nowrap">
|
||||
<li class="nav-item">
|
||||
<router-link :to="{name: 'items', params: {event: getEventSlug}}"
|
||||
:class="['nav-link', { active: getActiveView === 'items' }]">
|
||||
Items
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="nav-item" v-if="checkRole('postevent')">
|
||||
<router-link :to="{name: 'tickets', params: {event: getEventSlug}}"
|
||||
:class="['nav-link', { active: getActiveView === 'tickets' }]">
|
||||
Tickets
|
||||
</router-link>
|
||||
</li>
|
||||
<li class="nav-item" v-if="checkRole('admin')">
|
||||
<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">
|
||||
<input
|
||||
class="form-control w-100"
|
||||
|
@ -41,10 +39,27 @@
|
|||
disabled
|
||||
>
|
||||
</form>
|
||||
|
||||
<div class="custom-control-inline mr-1">
|
||||
<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"/>
|
||||
</button>
|
||||
<button :class="['btn', 'btn-info', { active: layout === 'table' }]" @click="setLayout('table')">
|
||||
<font-awesome-icon icon="list"/>
|
||||
</button>
|
||||
</div>
|
||||
<button type="button" class="btn text-nowrap btn-success" @click="$emit('addClicked')">
|
||||
<font-awesome-icon icon="plus"/>
|
||||
<span class="d-none d-md-inline"> Add</span>
|
||||
</button>
|
||||
</div>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent"
|
||||
aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav ml-auto">
|
||||
<li class="nav-item dropdown">
|
||||
<!--li class="nav-item dropdown">
|
||||
<button class="btn nav-link dropdown-toggle" type="button" id="dropdownMenuButton2"
|
||||
data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{{ getActiveView }}
|
||||
|
@ -55,7 +70,7 @@
|
|||
<a class="nav-link text-nowrap" href="#" @click="changeView(link)">{{ link.title }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</li-->
|
||||
|
||||
<li class="nav-item" v-for="(link, index) in links" v-bind:key="index">
|
||||
<a class="nav-link text-nowrap" :href="link.path" @click.prevent="navigateTo(link.path)">
|
||||
|
@ -80,12 +95,12 @@ export default {
|
|||
//{'title':'mass-edit','path':'massedit'},
|
||||
],
|
||||
links: [
|
||||
{'title': 'howto engel', 'path': '/howto/'}
|
||||
{'title': 'howto engel', 'path': '/howto/'},
|
||||
]
|
||||
}),
|
||||
computed: {
|
||||
...mapState(['events', 'activeEvent', 'layout']),
|
||||
...mapGetters(['getEventSlug', 'getActiveView']),
|
||||
...mapGetters(['getEventSlug', 'getActiveView', "checkRole"]),
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['changeEvent', 'changeView', 'searchEventItems']),
|
||||
|
@ -99,4 +114,29 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
@import "../scss/navbar.scss";
|
||||
|
||||
.nav-tabs {
|
||||
margin-bottom: -0.5rem !important;
|
||||
border-bottom: var(--gray) solid 1px !important;
|
||||
|
||||
& .nav-item {
|
||||
margin-right: 0.5em;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
& .nav-link {
|
||||
padding-bottom: 1rem !important;
|
||||
border: var(--gray) solid 1px !important;
|
||||
border-bottom: none !important;
|
||||
|
||||
&.active {
|
||||
background: black !important;
|
||||
border-bottom: none;
|
||||
color: white !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
336
web/src/components/Timeline.vue
Normal file
336
web/src/components/Timeline.vue
Normal file
|
@ -0,0 +1,336 @@
|
|||
<template>
|
||||
<ol class="timeline">
|
||||
<li class="timeline-item">
|
||||
<span class="timeline-item-icon | faded-icon">
|
||||
<font-awesome-icon icon="pen"/>
|
||||
</span>
|
||||
<div class="timeline-item-description">
|
||||
<i class="avatar | small">
|
||||
<font-awesome-icon icon="user"/>
|
||||
</i>
|
||||
<span><a href="#">Luna Bonifacio</a> has changed <a href="#">2 attributes</a> on <time
|
||||
datetime="21-01-2021">Jan 21, 2021</time></span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item">
|
||||
<span class="timeline-item-icon | faded-icon">
|
||||
<font-awesome-icon icon="check"/>
|
||||
</span>
|
||||
<div class="timeline-item-description">
|
||||
<i class="avatar | small">
|
||||
<font-awesome-icon icon="user"/>
|
||||
</i>
|
||||
<span><a href="#">Yoan Almedia</a> moved <a href="#">Eric Lubin</a> to <a href="#">📚 Technical Test</a> on <time
|
||||
datetime="20-01-2021">Jan 20, 2021</time></span>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item | extra-space">
|
||||
<span class="timeline-item-icon | filled-icon">
|
||||
<font-awesome-icon icon="envelope"/>
|
||||
</span>
|
||||
<div class="timeline-item-wrapper">
|
||||
<div class="timeline-item-description">
|
||||
<i class="avatar | small">
|
||||
<font-awesome-icon icon="user"/>
|
||||
</i>
|
||||
<span><a href="#">Yoan Almedia</a> commented on <time
|
||||
datetime="20-01-2021">Jan 20, 2021</time></span>
|
||||
</div>
|
||||
<div class="comment">
|
||||
<p>I've sent him the assignment we discussed recently, he is coming back to us this week. Regarding
|
||||
to our last call, I really enjoyed talking to him and so far he has the profile we are looking
|
||||
for. Can't wait to see his technical test, I'll keep you posted and we'll debrief it all
|
||||
together!😊</p>
|
||||
<button class="button">👏 2</button>
|
||||
<button class="button | square">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
|
||||
<path fill="none" d="M0 0h24v24H0z"/>
|
||||
<path fill="currentColor"
|
||||
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM7 12a5 5 0 0 0 10 0h-2a3 3 0 0 1-6 0H7z"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button class="show-replies">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-forward"
|
||||
width="44" height="44" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round">
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
|
||||
<path d="M15 11l4 4l-4 4m4 -4h-11a4 4 0 0 1 0 -8h1"/>
|
||||
</svg>
|
||||
Show 3 replies
|
||||
<span class="avatar-list">
|
||||
<i class="avatar | small">
|
||||
<font-awesome-icon icon="user"/>
|
||||
</i>
|
||||
<i class="avatar | small">
|
||||
<font-awesome-icon icon="user"/>
|
||||
</i>
|
||||
<i class="avatar | small">
|
||||
<font-awesome-icon icon="user"/>
|
||||
</i>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li class="timeline-item">
|
||||
<span class="timeline-item-icon | faded-icon">
|
||||
<font-awesome-icon icon="comment"/>
|
||||
</span>
|
||||
<div class="new-comment">
|
||||
<input type="text" placeholder="Add a comment..."/>
|
||||
</div>
|
||||
</li>
|
||||
</ol>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default {
|
||||
name: 'Timeline',
|
||||
props: {
|
||||
timeline: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
--c-grey-100: #f4f6f8;
|
||||
--c-grey-200: #e3e3e3;
|
||||
--c-grey-300: #b2b2b2;
|
||||
--c-grey-400: #7b7b7b;
|
||||
--c-grey-500: #3d3d3d;
|
||||
|
||||
--c-blue-500: #688afd;
|
||||
}
|
||||
|
||||
button,
|
||||
input,
|
||||
select,
|
||||
textarea {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* End basic CSS override */
|
||||
|
||||
.timeline {
|
||||
width: 85%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 32px 0 32px 32px;
|
||||
border-left: 2px solid var(--c-grey-200);
|
||||
font-size: 1.125rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
|
||||
& + * {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
& + .extra-space {
|
||||
margin-top: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.new-comment {
|
||||
width: 100%;
|
||||
|
||||
input {
|
||||
border: 1px solid var(--c-grey-200);
|
||||
border-radius: 6px;
|
||||
height: 48px;
|
||||
padding: 0 16px;
|
||||
width: 100%;
|
||||
|
||||
&::placeholder {
|
||||
color: var(--c-grey-300);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border-color: var(--c-grey-300);
|
||||
outline: 0; /* Don't actually do this */
|
||||
box-shadow: 0 0 0 4px var(--c-grey-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-item-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 50%;
|
||||
margin-left: -57px;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
|
||||
svg {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
&.faded-icon {
|
||||
background-color: var(--c-grey-100);
|
||||
color: var(--c-grey-400);
|
||||
}
|
||||
|
||||
&.filled-icon {
|
||||
background-color: var(--c-blue-500);
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-item-description {
|
||||
display: flex;
|
||||
padding-top: 6px;
|
||||
gap: 8px;
|
||||
color: var(--c-grey-400);
|
||||
|
||||
img {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
/*color: var(--c-grey-500);*/
|
||||
font-weight: 500;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
outline: 0; /* Don't actually do this */
|
||||
color: var(--c-blue-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
aspect-ratio: 1 / 1;
|
||||
flex-shrink: 0;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
|
||||
&.small {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
img {
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
|
||||
.comment {
|
||||
margin-top: 12px;
|
||||
/*color: var(--c-grey-500);*/
|
||||
border: 1px solid var(--c-grey-200);
|
||||
background: var(--dark);
|
||||
border-radius: 6px;
|
||||
padding: 16px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.button {
|
||||
border: 0;
|
||||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
margin-right: 4px;
|
||||
margin-top: 12px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1rem;
|
||||
height: 32px;
|
||||
padding: 0 8px;
|
||||
background-color: var(--c-grey-100);
|
||||
flex-shrink: 0;
|
||||
cursor: pointer;
|
||||
border-radius: 99em;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--c-grey-200);
|
||||
}
|
||||
|
||||
&.square {
|
||||
border-radius: 50%;
|
||||
color: var(--c-grey-400);
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
padding: 0;
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--c-grey-200);
|
||||
color: var(--c-grey-500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.show-replies {
|
||||
color: var(--c-grey-300);
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin-top: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
|
||||
svg {
|
||||
flex-shrink: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
color: var(--c-grey-200);
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& > * {
|
||||
position: relative;
|
||||
box-shadow: 0 0 0 2px #fff;
|
||||
background: var(--dark);
|
||||
margin-right: -8px;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
|
@ -29,14 +29,19 @@ import {
|
|||
faPen,
|
||||
faCheck,
|
||||
faTimes,
|
||||
faSave
|
||||
faSave,
|
||||
faEye,
|
||||
faComment,
|
||||
faEnvelope,
|
||||
faUser,
|
||||
faComments
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome';
|
||||
|
||||
import vueDebounce from 'vue-debounce';
|
||||
|
||||
library.add(faPlus, faCheckCircle, faEdit, faTrash, faCat, faSyncAlt, faSort, faSortUp, faSortDown, faTh, faList, faWindowClose, faCamera, faStop, faPen, faCheck, faTimes, faSave);
|
||||
|
||||
library.add(faPlus, faCheckCircle, faEdit, faTrash, faCat, faSyncAlt, faSort, faSortUp, faSortDown, faTh, faList,
|
||||
faWindowClose, faCamera, faStop, faPen, faCheck, faTimes, faSave, faEye, faComment, faUser, faComments, faEnvelope);
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon);
|
||||
|
||||
sync(store, router);
|
||||
|
|
|
@ -6,25 +6,41 @@ import Error from './views/Error';
|
|||
import HowTo from './views/HowTo';
|
||||
import VueRouter from 'vue-router';
|
||||
import Vue from 'vue';
|
||||
|
||||
import Debug from "@/views/admin/Debug.vue";
|
||||
import Tickets from "@/views/Tickets.vue";
|
||||
import Ticket from "@/views/Ticket.vue";
|
||||
import Admin from "@/views/admin/Admin.vue";
|
||||
import store from "@/store";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
const routes = [
|
||||
{path: '/', redirect: '/Camp23/items'},
|
||||
{path: '/howto', name: 'howto', component: HowTo},
|
||||
{path: '/admin/files', name: 'files', component: Files},
|
||||
{path: '/admin/events', name: 'events', component: Events},
|
||||
{path: '/:event/boxes', name: 'boxes', component: Boxes},
|
||||
{path: '/:event/items', name: 'items', component: Items},
|
||||
{path: '/:event/box/:uid', name: 'boxes', component: Boxes},
|
||||
{path: '/:event/item/:uid', name: 'items', component: Items},
|
||||
{path: '/:event/box/:uid', name: 'box', component: Boxes},
|
||||
{path: '/:event/item/:uid', name: 'item', component: Items},
|
||||
{path: '/:event/tickets', name: 'tickets', component: Tickets},
|
||||
{path: '/:event/ticket/:id', name: 'ticket', component: Ticket},
|
||||
{path: '/admin', name: 'admin', component: Admin},
|
||||
{path: '/admin/files', name: 'files', component: Files},
|
||||
{path: '/admin/events', name: 'events', component: Events},
|
||||
{path: '/admin/debug', name: 'debug', component: Debug},
|
||||
{path: '/admin/users', name: 'users', component: Events},
|
||||
{path: '*', component: Error},
|
||||
];
|
||||
|
||||
const router = new VueRouter({
|
||||
mode: 'history',
|
||||
routes
|
||||
routes,
|
||||
});
|
||||
|
||||
router.afterEach((to, from) => {
|
||||
if (to.params.event) {
|
||||
//console.log('update last event', to.params.event);
|
||||
store.commit('updateLastEvent', to.params.event);
|
||||
}
|
||||
});
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import AxiosBootstrap from 'axios';
|
||||
import config from '../config';
|
||||
import * as _ from 'lodash/fp';
|
||||
import router from '../router';
|
||||
|
||||
|
@ -10,8 +9,7 @@ import * as utf8 from 'utf8';
|
|||
|
||||
Vue.use(Vuex);
|
||||
const axios = AxiosBootstrap.create({
|
||||
baseURL: config.service.url,
|
||||
auth: config.service.auth
|
||||
baseURL: '/api',
|
||||
});
|
||||
|
||||
axios.interceptors.response.use(response => response, error => {
|
||||
|
@ -45,19 +43,61 @@ const store = new Vuex.Store({
|
|||
loadedItems: [],
|
||||
loadedBoxes: [],
|
||||
toasts: [],
|
||||
tickets: [
|
||||
{
|
||||
id: 1,
|
||||
name: "test1",
|
||||
state: "open",
|
||||
assigned_to: "test",
|
||||
last_activity: "2019-12-27T12:00:00+01:00",
|
||||
timeline: [{name: "test1", time: "2019-12-27T12:00:00+01:00"}, {
|
||||
name: "test2",
|
||||
time: "2019-12-27T12:00:00+01:00"
|
||||
}]
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "test2",
|
||||
state: "open",
|
||||
assigned_to: "test",
|
||||
last_activity: "2019-12-27T12:00:00+01:00",
|
||||
timeline: [{name: "test1", time: "2019-12-27T12:00:00+01:00"}, {
|
||||
name: "test2",
|
||||
time: "2019-12-27T12:00:00+01:00"
|
||||
}]
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "test3",
|
||||
state: "open",
|
||||
assigned_to: "test",
|
||||
last_activity: "2019-12-27T12:00:00+01:00",
|
||||
timeline: [{name: "test1", time: "2019-12-27T12:00:00+01:00"}, {
|
||||
name: "test2",
|
||||
time: "2019-12-27T12:00:00+01:00"
|
||||
}]
|
||||
},
|
||||
],
|
||||
userRoles: ['admin', 'postevent', 'orga', 'user'],
|
||||
lastEvent: localStorage.getItem('lf_lastEvent') || '36C3',
|
||||
lastUsed: localStorage.getItem('lf_lastUsed') || {},
|
||||
},
|
||||
getters: {
|
||||
getEventSlug: state => state.route && state.route.params.event ? state.route.params.event : state.events.length ? state.events[0].slug : '36C3',
|
||||
getEventSlug: state => state.route && state.route.params.event ? state.route.params.event : state.lastEvent,
|
||||
getActiveView: state => state.route.name || 'items',
|
||||
getFilters: state => state.route.query,
|
||||
getBoxes: state => state.loadedBoxes
|
||||
getBoxes: state => state.loadedBoxes,
|
||||
checkRole: state => role => state.userRoles.includes(role),
|
||||
},
|
||||
mutations: {
|
||||
updateLastUsed(state, diff) {
|
||||
state.lastUsed = _.extend(state.lastUsed, diff);
|
||||
localStorage.setItem('lf_lastUsed', state.lastUsed);
|
||||
},
|
||||
updateLastEvent(state, slug) {
|
||||
state.lastEvent = slug;
|
||||
localStorage.setItem('lf_lastEvent', slug);
|
||||
},
|
||||
replaceEvents(state, events) {
|
||||
state.events = events;
|
||||
},
|
||||
|
|
|
@ -134,5 +134,8 @@ export default {
|
|||
</script>
|
||||
|
||||
<style scoped>
|
||||
* {
|
||||
color: #c8c8c8 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -45,7 +45,7 @@
|
|||
@itemActivated="openLightboxModalWith($event)"
|
||||
>
|
||||
<img
|
||||
:src="`${baseUrl}/1/thumbs/${item.file}`"
|
||||
:src="`/api/1/thumbs/${item.file}`"
|
||||
class="card-img-top img-fluid"
|
||||
>
|
||||
<div class="card-body">
|
||||
|
@ -75,7 +75,6 @@ import Cards from '@/components/Cards';
|
|||
import Modal from '@/components/Modal';
|
||||
import EditItem from '@/components/EditItem';
|
||||
import {mapActions, mapState} from 'vuex';
|
||||
import config from '../config';
|
||||
import Lightbox from '../components/Lightbox';
|
||||
|
||||
export default {
|
||||
|
@ -83,7 +82,6 @@ export default {
|
|||
data: () => ({
|
||||
lightboxItem: null,
|
||||
editingItem: null,
|
||||
baseUrl: config.service.url,
|
||||
}),
|
||||
components: {Lightbox, Table, Cards, Modal, EditItem},
|
||||
computed: mapState(['loadedItems', 'layout']),
|
||||
|
|
49
web/src/views/Ticket.vue
Normal file
49
web/src/views/Ticket.vue
Normal file
|
@ -0,0 +1,49 @@
|
|||
<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>Ticket #{{ ticket.id }} - {{ ticket.name }}</h3>
|
||||
</div>
|
||||
<Timeline :timeline="ticket.timeline"/>
|
||||
<div class="card-footer d-flex justify-content-between">
|
||||
<router-link :to="{name: 'tickets'}" class="btn btn-secondary mr-2">Back</router-link>
|
||||
<button class="btn btn-danger" @click="deleteItem({type: 'tickets', id: ticket.id})">
|
||||
<font-awesome-icon icon="trash"/>
|
||||
Delete
|
||||
</button>
|
||||
<button class="btn btn-success" @click="markItemReturned({type: 'tickets', id: ticket.id})">Mark
|
||||
as returned
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions, mapState} from 'vuex';
|
||||
import Timeline from "@/components/Timeline.vue";
|
||||
|
||||
export default {
|
||||
name: 'Ticket',
|
||||
components: {Timeline},
|
||||
computed: {
|
||||
...mapState(['tickets']),
|
||||
ticket() {
|
||||
const id = parseInt(this.$route.params.id)
|
||||
const ret = this.tickets.find(ticket => ticket.id === id);
|
||||
return ret ? ret : {};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(['deleteItem', 'markItemReturned']),
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
46
web/src/views/Tickets.vue
Normal file
46
web/src/views/Tickets.vue
Normal file
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<div class="container-fluid px-xl-5 mt-3">
|
||||
<div class="row">
|
||||
<div class="col-xl-8 offset-xl-2">
|
||||
<Table
|
||||
:columns="['id', 'name', 'state', 'last_activity', 'assigned_to']"
|
||||
:items="tickets"
|
||||
:keyName="'id'"
|
||||
v-slot="{ item }"
|
||||
>
|
||||
<div class="btn-group">
|
||||
<a class="btn btn-primary" :href="'/ticket/' + item.id" title="view"
|
||||
@click.prevent="gotoDetail(item)">
|
||||
<font-awesome-icon icon="eye"/>
|
||||
View
|
||||
</a>
|
||||
</div>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Table from '@/components/Table';
|
||||
import Cards from '@/components/Cards';
|
||||
import Modal from '@/components/Modal';
|
||||
import EditItem from '@/components/EditItem';
|
||||
import {mapActions, mapState} from 'vuex';
|
||||
import Lightbox from '../components/Lightbox';
|
||||
|
||||
export default {
|
||||
name: 'Tickets',
|
||||
components: {Lightbox, Table, Cards, Modal, EditItem},
|
||||
computed: mapState(['tickets']),
|
||||
methods: {
|
||||
gotoDetail(ticket) {
|
||||
this.$router.push({name: 'ticket', params: {id: ticket.id}});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
40
web/src/views/admin/Admin.vue
Normal file
40
web/src/views/admin/Admin.vue
Normal file
|
@ -0,0 +1,40 @@
|
|||
<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">
|
||||
<h3 class="text-center">Admin</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<router-link :to="{name: 'debug'}">Debug</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{name: 'boxes', params: {event: getEventSlug}}">Boxes</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{name: 'events'}">Events</router-link>
|
||||
</li>
|
||||
<li>
|
||||
<router-link :to="{name: 'users'}">Users</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapGetters} from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'Admin',
|
||||
computed: {
|
||||
...mapGetters(['getEventSlug']),
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
39
web/src/views/admin/Boxes.vue
Normal file
39
web/src/views/admin/Boxes.vue
Normal file
|
@ -0,0 +1,39 @@
|
|||
<template>
|
||||
<div class="container-fluid px-xl-5 mt-3">
|
||||
<div class="row">
|
||||
<div class="col-xl-8 offset-xl-2">
|
||||
<Table
|
||||
:columns="['cid', 'name','itemCount']"
|
||||
:items="loadedBoxes"
|
||||
:keyName="'cid'"
|
||||
v-slot="{ item }"
|
||||
>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-secondary" @click.stop="showBoxContent(item.name)">
|
||||
<!--font-awesome-icon icon="archive"/--> content
|
||||
</button>
|
||||
<button class="btn btn-danger" @click.stop="" title="delete">
|
||||
<font-awesome-icon icon="trash"/>
|
||||
</button>
|
||||
</div>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions, mapState} from 'vuex';
|
||||
import Table from '@/components/Table';
|
||||
|
||||
export default {
|
||||
name: 'Boxes',
|
||||
components: {Table},
|
||||
computed: mapState(['loadedBoxes', 'layout']),
|
||||
methods: mapActions(['showBoxContent']),
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
75
web/src/views/admin/Debug.vue
Normal file
75
web/src/views/admin/Debug.vue
Normal file
|
@ -0,0 +1,75 @@
|
|||
<template>
|
||||
<div class="container-fluid px-xl-5 mt-3">
|
||||
<div class="row">
|
||||
<div class="col-xl-8 offset-xl-2">
|
||||
<h3 class="text-center">Events</h3>
|
||||
<!--p>{{ events }}</p-->
|
||||
<ul>
|
||||
<li v-for="event in events" :key="event.id">
|
||||
{{ event.slug }}
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="text-center">Items</h3>
|
||||
<!--p>{{ loadedItems }}</p-->
|
||||
<ul>
|
||||
<li v-for="item in loadedItems" :key="item.id">
|
||||
{{ item.description }}
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="text-center">Boxes</h3>
|
||||
<!--p>{{ loadedBoxes }}</p-->
|
||||
<ul>
|
||||
<li v-for="box in loadedBoxes" :key="box.id">
|
||||
{{ box.name }}
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="text-center">Mails</h3>
|
||||
<!--p>{{ mails }}</p-->
|
||||
<ul>
|
||||
<li v-for="mail in mails" :key="mail.id">
|
||||
{{ mail.id }}
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="text-center">Issues</h3>
|
||||
<!--p>{{ issues }}</p-->
|
||||
<ul>
|
||||
<li v-for="issue in issues" :key="issue.id">
|
||||
{{ issue.id }}
|
||||
</li>
|
||||
</ul>
|
||||
<h3 class="text-center">System Events</h3>
|
||||
<!--p>{{ systemEvents }}</p-->
|
||||
<ul>
|
||||
<li v-for="systemEvent in systemEvents" :key="systemEvent.id">
|
||||
{{ systemEvent.id }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions, mapState} from 'vuex';
|
||||
import Table from '@/components/Table';
|
||||
import Events from "@/views/Events.vue";
|
||||
|
||||
export default {
|
||||
name: 'Debug',
|
||||
components: {Events, Table},
|
||||
computed: mapState(['events', 'loadedItems', 'loadedBoxes', 'mails', 'issues', 'systemEvents']),
|
||||
methods: {
|
||||
...mapActions(['changeEvent', 'loadMails', 'loadIssues', 'loadSystemEvents']),
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.loadMails();
|
||||
this.loadIssues();
|
||||
this.loadSystemEvents();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
41
web/src/views/admin/Events.vue
Normal file
41
web/src/views/admin/Events.vue
Normal file
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<div class="container-fluid px-xl-5 mt-3">
|
||||
<div class="row">
|
||||
<div class="col-xl-8 offset-xl-2">
|
||||
<Table
|
||||
:columns="['slug', 'name']"
|
||||
:items="events"
|
||||
:keyName="'slug'"
|
||||
v-slot="{ item }"
|
||||
>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-secondary" @click.stop="changeEvent(item)">
|
||||
<font-awesome-icon icon="archive"/>
|
||||
use
|
||||
</button>
|
||||
<button class="btn btn-danger" @click.stop="">
|
||||
<font-awesome-icon icon="trash"/>
|
||||
delete
|
||||
</button>
|
||||
</div>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions, mapState} from 'vuex';
|
||||
import Table from '@/components/Table';
|
||||
|
||||
export default {
|
||||
name: 'Events',
|
||||
components: {Table},
|
||||
computed: mapState(['events']),
|
||||
methods: mapActions(['changeEvent']),
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
29
web/src/views/admin/Users.vue
Normal file
29
web/src/views/admin/Users.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">
|
||||
<h3 class="text-center">Users</h3>
|
||||
<p>{{ users }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {mapActions, mapState} from 'vuex';
|
||||
import Table from '@/components/Table';
|
||||
import Events from "@/views/Events.vue";
|
||||
|
||||
export default {
|
||||
name: 'Users',
|
||||
computed: mapState(['users']),
|
||||
methods: mapActions(['loadUsers']),
|
||||
mounted() {
|
||||
this.loadUsers();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
Loading…
Add table
Add a link
Reference in a new issue