Compare commits
No commits in common. "594b8c4da36ec25a790ff41954c0951f890df55e" and "e005d6fb6ff91c7369baa2e1921b50e03ef747d7" have entirely different histories.
594b8c4da3
...
e005d6fb6f
12 changed files with 185 additions and 441 deletions
|
@ -22,7 +22,7 @@ class ContainerViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
@permission_classes([IsAuthenticated])
|
@permission_classes([IsAuthenticated])
|
||||||
#@permission_required('view_item', raise_exception=True)
|
@permission_required('view_item', raise_exception=True)
|
||||||
def search_items(request, event_slug, query):
|
def search_items(request, event_slug, query):
|
||||||
try:
|
try:
|
||||||
event = Event.objects.get(slug=event_slug)
|
event = Event.objects.get(slug=event_slug)
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<div style="min-height: 100vh; display: flex; flex-direction: column;">
|
<div id="app">
|
||||||
<AddItemModal v-if="addItemModalOpen && isLoggedIn" @close="closeAddItemModal()" isModal="true"/>
|
<AddItemModal v-if="addItemModalOpen && isLoggedIn" @close="closeAddItemModal()" isModal="true"/>
|
||||||
<AddTicketModal v-if="addTicketModalOpen && isLoggedIn" @close="closeAddTicketModal()" isModal="true"/>
|
<AddTicketModal v-if="addTicketModalOpen && isLoggedIn" @close="closeAddTicketModal()" isModal="true"/>
|
||||||
<AddBoxModal v-if="showAddBoxModal && isLoggedIn" @close="closeAddBoxModal()" isModal="true"/>
|
<AddBoxModal v-if="showAddBoxModal && isLoggedIn" @close="closeAddBoxModal()" isModal="true"/>
|
||||||
<Navbar v-if="isLoggedIn" @addItemClicked="openAddItemModal()" @addTicketClicked="openAddTicketModal()"/>
|
<Navbar v-if="isLoggedIn" @addItemClicked="openAddItemModal()" @addTicketClicked="openAddTicketModal()"/>
|
||||||
<router-view style="flex: 1 1;"/>
|
<router-view/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,133 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="async-wrapper" :class="{ 'loaded': loaded }">
|
|
||||||
<div class="deferred">
|
|
||||||
<slot></slot>
|
|
||||||
</div>
|
|
||||||
<div class="loader-wrapper">
|
|
||||||
<div class="loader-ellipsis">
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
<div></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'AsyncLoader',
|
|
||||||
props: {
|
|
||||||
loaded: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.async-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .deferred {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper.loaded > .deferred {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis {
|
|
||||||
color: #17a2b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper.loaded > .loader-wrapper > .loader-ellipsis {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis,
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis div {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis {
|
|
||||||
display: inline-block;
|
|
||||||
position: relative;
|
|
||||||
width: 80px;
|
|
||||||
height: 80px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis div {
|
|
||||||
position: absolute;
|
|
||||||
top: 33.33333px;
|
|
||||||
width: 13.33333px;
|
|
||||||
height: 13.33333px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: currentColor;
|
|
||||||
animation-timing-function: cubic-bezier(0, 1, 1, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis div:nth-child(1) {
|
|
||||||
left: 8px;
|
|
||||||
animation: loader-ellipsis1 0.6s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis div:nth-child(2) {
|
|
||||||
left: 8px;
|
|
||||||
animation: loader-ellipsis2 0.6s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis div:nth-child(3) {
|
|
||||||
left: 32px;
|
|
||||||
animation: loader-ellipsis2 0.6s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
.async-wrapper > .loader-wrapper > .loader-ellipsis div:nth-child(4) {
|
|
||||||
left: 56px;
|
|
||||||
animation: loader-ellipsis3 0.6s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes loader-ellipsis1 {
|
|
||||||
0% {
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes loader-ellipsis3 {
|
|
||||||
0% {
|
|
||||||
transform: scale(1);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: scale(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes loader-ellipsis2 {
|
|
||||||
0% {
|
|
||||||
transform: translate(0, 0);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
transform: translate(24px, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -29,7 +29,16 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<SearchBox v-if="hasPermissions" class="mt-1 my-lg-auto my-xl-auto w-100 d-inline mr-1"/>
|
<form class="form-inline mt-1 my-lg-auto my-xl-auto w-100 d-inline mr-1" v-if="hasPermissions">
|
||||||
|
<input
|
||||||
|
class="form-control w-100"
|
||||||
|
type="search"
|
||||||
|
placeholder="Search"
|
||||||
|
aria-label="Search"
|
||||||
|
@input="searchEventItems($event.target.value)"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
</form>
|
||||||
<div class="custom-control-inline mr-1" v-if="hasPermissions">
|
<div class="custom-control-inline mr-1" v-if="hasPermissions">
|
||||||
<div class="btn-group btn-group-toggle mr-1" v-if="isItemView()">
|
<div class="btn-group btn-group-toggle mr-1" v-if="isItemView()">
|
||||||
<button :class="['btn', 'btn-info', { active: layout === 'cards' }]" @click="setLayout('cards')">
|
<button :class="['btn', 'btn-info', { active: layout === 'cards' }]" @click="setLayout('cards')">
|
||||||
|
@ -94,13 +103,9 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapState, mapActions, mapMutations, mapGetters} from 'vuex';
|
import {mapState, mapActions, mapMutations, mapGetters} from 'vuex';
|
||||||
import SearchBox from "@/components/inputs/SearchBox.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Navbar',
|
name: 'Navbar',
|
||||||
components: {
|
|
||||||
SearchBox
|
|
||||||
},
|
|
||||||
data: () => ({
|
data: () => ({
|
||||||
views: [
|
views: [
|
||||||
{'title': 'items', 'path': 'items'},
|
{'title': 'items', 'path': 'items'},
|
||||||
|
@ -117,7 +122,7 @@ export default {
|
||||||
...mapGetters(['getEventSlug', 'getActiveView', "checkPermission", "hasPermissions", "layout", "route"]),
|
...mapGetters(['getEventSlug', 'getActiveView', "checkPermission", "hasPermissions", "layout", "route"]),
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['changeEvent', 'changeView']),
|
...mapActions(['changeEvent', 'changeView', 'searchEventItems']),
|
||||||
...mapMutations(['logout']),
|
...mapMutations(['logout']),
|
||||||
navigateTo(link) {
|
navigateTo(link) {
|
||||||
if (this.route.path !== link)
|
if (this.route.path !== link)
|
||||||
|
|
|
@ -40,10 +40,10 @@
|
||||||
<div class="">
|
<div class="">
|
||||||
<textarea placeholder="add comment..." v-model="newComment" class="form-control">
|
<textarea placeholder="add comment..." v-model="newComment" class="form-control">
|
||||||
</textarea>
|
</textarea>
|
||||||
<AsyncButton class="btn btn-primary float-right" :task="addCommentAndClear">
|
<button class="btn btn-primary float-right" @click="addCommentAndClear">
|
||||||
<font-awesome-icon icon="comment"/>
|
<font-awesome-icon icon="comment"/>
|
||||||
Save Comment
|
Save Comment
|
||||||
</AsyncButton>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -58,10 +58,10 @@
|
||||||
<div>
|
<div>
|
||||||
<textarea placeholder="reply mail..." v-model="newMail" class="form-control">
|
<textarea placeholder="reply mail..." v-model="newMail" class="form-control">
|
||||||
</textarea>
|
</textarea>
|
||||||
<AsyncButton class="btn btn-primary float-right" :task="sendMailAndClear">
|
<button class="btn btn-primary float-right" @click="sendMailAndClear">
|
||||||
<font-awesome-icon icon="envelope"/>
|
<font-awesome-icon icon="envelope"/>
|
||||||
Send Mail
|
Send Mail
|
||||||
</AsyncButton>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
@ -73,16 +73,15 @@
|
||||||
import TimelineMail from "@/components/TimelineMail.vue";
|
import TimelineMail from "@/components/TimelineMail.vue";
|
||||||
import TimelineComment from "@/components/TimelineComment.vue";
|
import TimelineComment from "@/components/TimelineComment.vue";
|
||||||
import TimelineStateChange from "@/components/TimelineStateChange.vue";
|
import TimelineStateChange from "@/components/TimelineStateChange.vue";
|
||||||
import {mapActions, mapGetters} from "vuex";
|
import {mapGetters} from "vuex";
|
||||||
import TimelineAssignment from "@/components/TimelineAssignment.vue";
|
import TimelineAssignment from "@/components/TimelineAssignment.vue";
|
||||||
import TimelineRelatedItem from "@/components/TimelineRelatedItem.vue";
|
import TimelineRelatedItem from "@/components/TimelineRelatedItem.vue";
|
||||||
import TimelineShippingVoucher from "@/components/TimelineShippingVoucher.vue";
|
import TimelineShippingVoucher from "@/components/TimelineShippingVoucher.vue";
|
||||||
import AsyncButton from "@/components/inputs/AsyncButton.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Timeline',
|
name: 'Timeline',
|
||||||
components: {
|
components: {
|
||||||
TimelineShippingVoucher, AsyncButton,
|
TimelineShippingVoucher,
|
||||||
TimelineRelatedItem, TimelineAssignment, TimelineStateChange, TimelineComment, TimelineMail
|
TimelineRelatedItem, TimelineAssignment, TimelineStateChange, TimelineComment, TimelineMail
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -104,15 +103,12 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapActions(['sendMail', 'postComment']),
|
sendMailAndClear: function () {
|
||||||
sendMailAndClear: async function () {
|
this.$emit('sendMail', this.newMail);
|
||||||
//this.$emit('sendMail', this.newMail);
|
|
||||||
await this.sendMail(this.newMail);
|
|
||||||
this.newMail = "";
|
this.newMail = "";
|
||||||
},
|
},
|
||||||
addCommentAndClear: async function () {
|
addCommentAndClear: function () {
|
||||||
//this.$emit('addComment', this.newComment);
|
this.$emit('addComment', this.newComment);
|
||||||
await this.postComment(this.newComment);
|
|
||||||
this.newComment = "";
|
this.newComment = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
<template>
|
|
||||||
<button @click.stop="handleClick" :disabled="disabled">
|
|
||||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"
|
|
||||||
:class="{'d-none': !disabled}"></span>
|
|
||||||
<span class="ml-2" :class="{'d-none': !disabled}">In Progress...</span>
|
|
||||||
<span :class="{'d-none': disabled}"><slot></slot></span>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'AsyncButton',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
disabled: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
task: {
|
|
||||||
type: Function,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async handleClick() {
|
|
||||||
console.log("AsyncButton.handleClick() called");
|
|
||||||
if (this.task && typeof this.task === 'function') {
|
|
||||||
this.disabled = true;
|
|
||||||
try {
|
|
||||||
await this.task();
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
} finally {
|
|
||||||
this.disabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.spinner-border {
|
|
||||||
vertical-align: -0.125em;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -1,29 +0,0 @@
|
||||||
<template>
|
|
||||||
<form class="form-inline">
|
|
||||||
<input
|
|
||||||
class="form-control w-100"
|
|
||||||
type="search"
|
|
||||||
placeholder="Search"
|
|
||||||
aria-label="Search"
|
|
||||||
v-model="search_query"
|
|
||||||
@input="searchEventItems(search_query)"
|
|
||||||
>
|
|
||||||
</form>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import {mapActions} from "vuex";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
name: 'SearchBox',
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
search_query: ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
...mapActions(['searchEventItems']),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
|
@ -46,7 +46,6 @@ const store = createStore({
|
||||||
states: 0,
|
states: 0,
|
||||||
messageTemplates: 0,
|
messageTemplates: 0,
|
||||||
shippingVouchers: 0,
|
shippingVouchers: 0,
|
||||||
userNotificationChannels: 0,
|
|
||||||
},
|
},
|
||||||
persistent_loaded: false,
|
persistent_loaded: false,
|
||||||
shared_loaded: false,
|
shared_loaded: false,
|
||||||
|
@ -234,10 +233,6 @@ const store = createStore({
|
||||||
state.shippingVouchers = codes;
|
state.shippingVouchers = codes;
|
||||||
state.fetchedData = {...state.fetchedData, shippingVouchers: Date.now()};
|
state.fetchedData = {...state.fetchedData, shippingVouchers: Date.now()};
|
||||||
},
|
},
|
||||||
setUserNotificationChannels(state, channels) {
|
|
||||||
state.userNotificationChannels = channels;
|
|
||||||
state.fetchedData = {...state.fetchedData, userNotificationChannels: Date.now()};
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async login({commit}, {username, password, remember}) {
|
async login({commit}, {username, password, remember}) {
|
||||||
|
@ -515,10 +510,9 @@ const store = createStore({
|
||||||
},
|
},
|
||||||
async fetchUserNotificationChannels({commit, state}) {
|
async fetchUserNotificationChannels({commit, state}) {
|
||||||
if (!state.user.token) return;
|
if (!state.user.token) return;
|
||||||
if (state.fetchedData.userNotificationChannels > Date.now() - 1000 * 60 * 60 * 24) return;
|
|
||||||
const {data, success} = await http.get('/2/user_notification_channels/', state.user.token);
|
const {data, success} = await http.get('/2/user_notification_channels/', state.user.token);
|
||||||
if (data && success) {
|
if (data && success) {
|
||||||
commit('setUserNotificationChannels', data);
|
state.userNotificationChannels = data;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<template>
|
<template>
|
||||||
<AsyncLoader :loaded="loadedItems.length > 0">
|
|
||||||
<div class="container-fluid px-xl-5 mt-3">
|
<div class="container-fluid px-xl-5 mt-3">
|
||||||
<Modal title="Edit Item" v-if="editingItem" @close="closeEditingModal()">
|
<Modal title="Edit Item" v-if="editingItem" @close="closeEditingModal()">
|
||||||
<template #body>
|
<template #body>
|
||||||
|
@ -25,8 +24,7 @@
|
||||||
<template #actions="{ item }">
|
<template #actions="{ item }">
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button class="btn btn-success"
|
<button class="btn btn-success"
|
||||||
@click.stop="confirm('return Item?') && markItemReturned(item)"
|
@click.stop="confirm('return Item?') && markItemReturned(item)" title="returned">
|
||||||
title="returned">
|
|
||||||
<font-awesome-icon icon="check"/>
|
<font-awesome-icon icon="check"/>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-secondary" @click.stop="openEditingModalWith(item)" title="edit">
|
<button class="btn btn-secondary" @click.stop="openEditingModalWith(item)" title="edit">
|
||||||
|
@ -62,12 +60,10 @@
|
||||||
@click.stop="confirm('return Item?') && markItemReturned(item)" title="returned">
|
@click.stop="confirm('return Item?') && markItemReturned(item)" title="returned">
|
||||||
<font-awesome-icon icon="check"/>
|
<font-awesome-icon icon="check"/>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-secondary" @click.stop="openEditingModalWith(item)"
|
<button class="btn btn-outline-secondary" @click.stop="openEditingModalWith(item)" title="edit">
|
||||||
title="edit">
|
|
||||||
<font-awesome-icon icon="edit"/>
|
<font-awesome-icon icon="edit"/>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-outline-danger"
|
<button class="btn btn-outline-danger" @click.stop="confirm('delete Item?') && deleteItem(item)"
|
||||||
@click.stop="confirm('delete Item?') && deleteItem(item)"
|
|
||||||
title="delete">
|
title="delete">
|
||||||
<font-awesome-icon icon="trash"/>
|
<font-awesome-icon icon="trash"/>
|
||||||
</button>
|
</button>
|
||||||
|
@ -76,7 +72,6 @@
|
||||||
</div>
|
</div>
|
||||||
</Cards>
|
</Cards>
|
||||||
</div>
|
</div>
|
||||||
</AsyncLoader>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -87,7 +82,6 @@ import EditItem from '@/components/EditItem';
|
||||||
import {mapActions, mapGetters, mapState} from 'vuex';
|
import {mapActions, mapGetters, mapState} from 'vuex';
|
||||||
import Lightbox from '../components/Lightbox';
|
import Lightbox from '../components/Lightbox';
|
||||||
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
|
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
|
||||||
import AsyncLoader from "@/components/AsyncLoader.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Items',
|
name: 'Items',
|
||||||
|
@ -95,7 +89,7 @@ export default {
|
||||||
lightboxHash: null,
|
lightboxHash: null,
|
||||||
editingItem: null,
|
editingItem: null,
|
||||||
}),
|
}),
|
||||||
components: {AsyncLoader, AuthenticatedImage, Lightbox, Table, Cards, Modal, EditItem},
|
components: {AuthenticatedImage, Lightbox, Table, Cards, Modal, EditItem},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['loadedItems']),
|
...mapState(['loadedItems']),
|
||||||
...mapGetters(['layout']),
|
...mapGetters(['layout']),
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<template>
|
<template>
|
||||||
<AsyncLoader :loaded="ticket.id">
|
|
||||||
<div class="container-fluid px-xl-5 mt-3">
|
<div class="container-fluid px-xl-5 mt-3">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xl-8 offset-xl-2">
|
<div class="col-xl-8 offset-xl-2">
|
||||||
|
@ -18,16 +17,13 @@
|
||||||
<select class="form-control" v-model="ticket.assigned_to">
|
<select class="form-control" v-model="ticket.assigned_to">
|
||||||
<option v-for="user in users" :value="user.username">{{ user.username }}</option>
|
<option v-for="user in users" :value="user.username">{{ user.username }}</option>
|
||||||
</select>
|
</select>
|
||||||
<button class="form-control btn btn-success" @click="assignTicket(ticket)">
|
<button class="form-control btn btn-success" @click="assigTicket(ticket)">
|
||||||
Assign Ticket
|
Assign Ticket
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<select class="form-control" v-model="ticket.state">
|
<select class="form-control" v-model="ticket.state">
|
||||||
<option v-for="status in state_options" :value="status.value">{{
|
<option v-for="status in state_options" :value="status.value">{{ status.text }}</option>
|
||||||
status.text
|
|
||||||
}}
|
|
||||||
</option>
|
|
||||||
</select>
|
</select>
|
||||||
<button class="form-control btn btn-success" @click="changeTicketStatus(ticket)">
|
<button class="form-control btn btn-success" @click="changeTicketStatus(ticket)">
|
||||||
Change Status
|
Change Status
|
||||||
|
@ -56,18 +52,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</AsyncLoader>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import {mapActions, mapGetters, mapState} from 'vuex';
|
import {mapActions, mapGetters, mapState} from 'vuex';
|
||||||
import Timeline from "@/components/Timeline.vue";
|
import Timeline from "@/components/Timeline.vue";
|
||||||
import ClipboardButton from "@/components/inputs/ClipboardButton.vue";
|
import ClipboardButton from "@/components/inputs/ClipboardButton.vue";
|
||||||
import AsyncLoader from "@/components/AsyncLoader.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Ticket',
|
name: 'Ticket',
|
||||||
components: {AsyncLoader, ClipboardButton, Timeline},
|
components: {ClipboardButton, Timeline},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
shipping_voucher_type: null
|
shipping_voucher_type: null
|
||||||
|
@ -108,7 +102,7 @@ export default {
|
||||||
state: ticket.state
|
state: ticket.state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
assignTicket(ticket) {
|
assigTicket(ticket) {
|
||||||
this.updateTicketPartial({
|
this.updateTicketPartial({
|
||||||
id: ticket.id,
|
id: ticket.id,
|
||||||
assigned_to: ticket.assigned_to
|
assigned_to: ticket.assigned_to
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<template>
|
<template>
|
||||||
<AsyncLoader :loaded="tickets.length > 0">
|
|
||||||
<div class="container-fluid px-xl-5 mt-3">
|
<div class="container-fluid px-xl-5 mt-3">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xl-8 offset-xl-2">
|
<div class="col-xl-8 offset-xl-2">
|
||||||
|
@ -47,7 +46,6 @@
|
||||||
</template>
|
</template>
|
||||||
</CollapsableCards>
|
</CollapsableCards>
|
||||||
</div>
|
</div>
|
||||||
</AsyncLoader>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -58,11 +56,10 @@ import {mapActions, mapGetters, mapState} from 'vuex';
|
||||||
import Lightbox from '../components/Lightbox';
|
import Lightbox from '../components/Lightbox';
|
||||||
import SlotTable from "@/components/SlotTable.vue";
|
import SlotTable from "@/components/SlotTable.vue";
|
||||||
import CollapsableCards from "@/components/CollapsableCards.vue";
|
import CollapsableCards from "@/components/CollapsableCards.vue";
|
||||||
import AsyncLoader from "@/components/AsyncLoader.vue";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Tickets',
|
name: 'Tickets',
|
||||||
components: {AsyncLoader, Lightbox, SlotTable, Cards, Modal, EditItem, CollapsableCards},
|
components: {Lightbox, SlotTable, Cards, Modal, EditItem, CollapsableCards},
|
||||||
computed: {
|
computed: {
|
||||||
...mapState(['tickets']),
|
...mapState(['tickets']),
|
||||||
...mapGetters(['stateInfo', 'getEventSlug', 'layout']),
|
...mapGetters(['stateInfo', 'getEventSlug', 'layout']),
|
||||||
|
|
|
@ -1,36 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<ul>
|
||||||
<Table :items="userNotificationChannels.map(channel => ({...channel, username: channel.user.username || {}}))"
|
<li v-for="channel in userNotificationChannels" :key="channel.id">
|
||||||
:columns="['id', 'username', 'channel_type', 'channel_target', 'event_filter', /*'active', 'created'*/]">
|
{{ channel.id }} - {{ channel.channel_type }} - {{ channel.channel_target }} - {{ channel.event_filter }} - {{ channel.active }} - {{ channel.created }} - {{ channel.user }}
|
||||||
<template #actions="{ item }">
|
</li>
|
||||||
<div class="btn-group">
|
</ul>
|
||||||
<button class="btn btn-danger" @click.stop="">
|
|
||||||
<font-awesome-icon icon="trash"/>
|
|
||||||
delete
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</Table>
|
|
||||||
<div class="card bg-dark">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="input-group">
|
|
||||||
<select class="form-control">
|
|
||||||
<option value="1">user</option>
|
|
||||||
<option value="2">admin</option>
|
|
||||||
</select>
|
|
||||||
<select class="form-control">
|
|
||||||
<option value="email">Email</option>
|
|
||||||
<option value="telegram">Telegram</option>
|
|
||||||
</select>
|
|
||||||
<input type="text" class="form-control" placeholder="channel_target">
|
|
||||||
<input type="text" class="form-control" value="*">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<button class="btn btn-primary">Add</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
Loading…
Reference in a new issue