This commit is contained in:
j3d1 2024-07-13 17:28:38 +02:00
parent 1cfeb34a4c
commit 4d8406bd7c
10 changed files with 438 additions and 182 deletions

View file

@ -1,77 +1,82 @@
<template>
<div class="container-fluid px-xl-5 mt-3">
<Modal title="Edit Item" v-if="editingItem" @close="closeEditingModal()">
<template #body>
<EditItem
:item="editingItem"
badge="uid"
<AsyncLoader :loaded="loadedItems.length > 0">
<div class="container-fluid px-xl-5 mt-3">
<Modal title="Edit Item" v-if="editingItem" @close="closeEditingModal()">
<template #body>
<EditItem
:item="editingItem"
badge="uid"
/>
</template>
<template #buttons>
<button type="button" class="btn btn-secondary" @click="closeEditingModal()">Cancel</button>
<button type="button" class="btn btn-success" @click="saveEditingItem()">Save Changes</button>
</template>
</Modal>
<Lightbox v-if="lightboxHash" :hash="lightboxHash" @close="closeLightboxModal()"/>
<div class="row" v-if="layout === 'table'">
<div class="col-xl-8 offset-xl-2">
<Table
:columns="['uid', 'description', 'box']"
:items="loadedItems"
:keyName="'uid'"
@itemActivated="openLightboxModalWith($event)"
>
<template #actions="{ item }">
<div class="btn-group">
<button class="btn btn-success"
@click.stop="confirm('return Item?') && markItemReturned(item)"
title="returned">
<font-awesome-icon icon="check"/>
</button>
<button class="btn btn-secondary" @click.stop="openEditingModalWith(item)" title="edit">
<font-awesome-icon icon="edit"/>
</button>
<button class="btn btn-danger" @click.stop="confirm('delete Item?') && deleteItem(item)"
title="delete">
<font-awesome-icon icon="trash"/>
</button>
</div>
</template>
</Table>
</div>
</div>
<Cards
v-if="layout === 'cards'"
:columns="['uid', 'description', 'box']"
:items="loadedItems"
:keyName="'uid'"
v-slot="{ item }"
@itemActivated="openLightboxModalWith($event)"
>
<AuthenticatedImage v-if="item.file" cached
:src="`/media/2/256/${item.file}/`"
class="card-img-top img-fluid"
/>
</template>
<template #buttons>
<button type="button" class="btn btn-secondary" @click="closeEditingModal()">Cancel</button>
<button type="button" class="btn btn-success" @click="saveEditingItem()">Save Changes</button>
</template>
</Modal>
<Lightbox v-if="lightboxHash" :hash="lightboxHash" @close="closeLightboxModal()"/>
<div class="row" v-if="layout === 'table'">
<div class="col-xl-8 offset-xl-2">
<Table
:columns="['uid', 'description', 'box']"
:items="loadedItems"
:keyName="'uid'"
@itemActivated="openLightboxModalWith($event)"
>
<template #actions="{ item }">
<div class="card-body">
<h6 class="card-title">{{ item.description }}</h6>
<h6 class="card-subtitle text-secondary">uid: {{ item.uid }} box: {{ item.box }}</h6>
<div class="row mx-auto mt-2">
<div class="btn-group">
<button class="btn btn-success"
<button class="btn btn-outline-success"
@click.stop="confirm('return Item?') && markItemReturned(item)" title="returned">
<font-awesome-icon icon="check"/>
</button>
<button class="btn btn-secondary" @click.stop="openEditingModalWith(item)" title="edit">
<button class="btn btn-outline-secondary" @click.stop="openEditingModalWith(item)"
title="edit">
<font-awesome-icon icon="edit"/>
</button>
<button class="btn btn-danger" @click.stop="confirm('delete Item?') && deleteItem(item)"
<button class="btn btn-outline-danger"
@click.stop="confirm('delete Item?') && deleteItem(item)"
title="delete">
<font-awesome-icon icon="trash"/>
</button>
</div>
</template>
</Table>
</div>
</div>
<Cards
v-if="layout === 'cards'"
:columns="['uid', 'description', 'box']"
:items="loadedItems"
:keyName="'uid'"
v-slot="{ item }"
@itemActivated="openLightboxModalWith($event)"
>
<AuthenticatedImage v-if="item.file" cached
:src="`/media/2/256/${item.file}/`"
class="card-img-top img-fluid"
/>
<div class="card-body">
<h6 class="card-title">{{ item.description }}</h6>
<h6 class="card-subtitle text-secondary">uid: {{ item.uid }} box: {{ item.box }}</h6>
<div class="row mx-auto mt-2">
<div class="btn-group">
<button class="btn btn-outline-success"
@click.stop="confirm('return Item?') && markItemReturned(item)" title="returned">
<font-awesome-icon icon="check"/>
</button>
<button class="btn btn-outline-secondary" @click.stop="openEditingModalWith(item)" title="edit">
<font-awesome-icon icon="edit"/>
</button>
<button class="btn btn-outline-danger" @click.stop="confirm('delete Item?') && deleteItem(item)"
title="delete">
<font-awesome-icon icon="trash"/>
</button>
</div>
</div>
</div>
</Cards>
</div>
</Cards>
</div>
</AsyncLoader>
</template>
<script>
@ -82,6 +87,7 @@ import EditItem from '@/components/EditItem';
import {mapActions, mapGetters, mapState} from 'vuex';
import Lightbox from '../components/Lightbox';
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
import AsyncLoader from "@/components/AsyncLoader.vue";
export default {
name: 'Items',
@ -89,7 +95,7 @@ export default {
lightboxHash: null,
editingItem: null,
}),
components: {AuthenticatedImage, Lightbox, Table, Cards, Modal, EditItem},
components: {AsyncLoader, AuthenticatedImage, Lightbox, Table, Cards, Modal, EditItem},
computed: {
...mapState(['loadedItems']),
...mapGetters(['layout']),

View file

@ -1,67 +1,73 @@
<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" @sendMail="handleMail" @addComment="handleComment"/>
<div class="card-footer d-flex justify-content-between">
<button class="btn btn-secondary mr-2" @click="$router.go(-1)">Back</button>
<!--button class="btn btn-danger" @click="deleteItem({type: 'tickets', id: ticket.id})">
<font-awesome-icon icon="trash"/>
Delete
</button-->
<div class="btn-group">
<select class="form-control" v-model="ticket.assigned_to">
<option v-for="user in users" :value="user.username">{{ user.username }}</option>
</select>
<button class="form-control btn btn-success" @click="assigTicket(ticket)">
Assign&nbsp;Ticket
</button>
<AsyncLoader :loaded="ticket.id">
<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>
<div class="btn-group">
<select class="form-control" v-model="ticket.state">
<option v-for="status in state_options" :value="status.value">{{ status.text }}</option>
</select>
<button class="form-control btn btn-success" @click="changeTicketStatus(ticket)">
Change&nbsp;Status
</button>
<Timeline :timeline="ticket.timeline" @sendMail="handleMail" @addComment="handleComment"/>
<div class="card-footer d-flex justify-content-between">
<button class="btn btn-secondary mr-2" @click="$router.go(-1)">Back</button>
<!--button class="btn btn-danger" @click="deleteItem({type: 'tickets', id: ticket.id})">
<font-awesome-icon icon="trash"/>
Delete
</button-->
<div class="btn-group">
<select class="form-control" v-model="ticket.assigned_to">
<option v-for="user in users" :value="user.username">{{ user.username }}</option>
</select>
<button class="form-control btn btn-success" @click="assignTicket(ticket)">
Assign&nbsp;Ticket
</button>
</div>
<div class="btn-group">
<select class="form-control" v-model="ticket.state">
<option v-for="status in state_options" :value="status.value">{{
status.text
}}
</option>
</select>
<button class="form-control btn btn-success" @click="changeTicketStatus(ticket)">
Change&nbsp;Status
</button>
</div>
</div>
</div>
<div class="card-footer d-flex justify-content-between">
<ClipboardButton :payload="shippingEmail" class="btn btn-primary">
<font-awesome-icon icon="clipboard"/>
Copy&nbsp;DHL&nbsp;contact&nbsp;to&nbsp;clipboard
</ClipboardButton>
<div class="btn-group">
<select class="form-control" v-model="shipping_voucher_type">
<option v-for="type in availableShippingVoucherTypes.filter(t=>t.count>0)"
:value="type.id">{{ type.name }}
</option>
</select>
<button class="form-control btn btn-success"
@click="claimShippingVoucher({ticket: ticket.id, shipping_voucher_type}).then(()=>shipping_voucher_type=null)"
:disabled="!shipping_voucher_type">
Claim&nbsp;Shipping&nbsp;Voucher
</button>
<div class="card-footer d-flex justify-content-between">
<ClipboardButton :payload="shippingEmail" class="btn btn-primary">
<font-awesome-icon icon="clipboard"/>
Copy&nbsp;DHL&nbsp;contact&nbsp;to&nbsp;clipboard
</ClipboardButton>
<div class="btn-group">
<select class="form-control" v-model="shipping_voucher_type">
<option v-for="type in availableShippingVoucherTypes.filter(t=>t.count>0)"
:value="type.id">{{ type.name }}
</option>
</select>
<button class="form-control btn btn-success"
@click="claimShippingVoucher({ticket: ticket.id, shipping_voucher_type}).then(()=>shipping_voucher_type=null)"
:disabled="!shipping_voucher_type">
Claim&nbsp;Shipping&nbsp;Voucher
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</AsyncLoader>
</template>
<script>
import {mapActions, mapGetters, mapState} from 'vuex';
import Timeline from "@/components/Timeline.vue";
import ClipboardButton from "@/components/inputs/ClipboardButton.vue";
import AsyncLoader from "@/components/AsyncLoader.vue";
export default {
name: 'Ticket',
components: {ClipboardButton, Timeline},
components: {AsyncLoader, ClipboardButton, Timeline},
data() {
return {
shipping_voucher_type: null
@ -102,7 +108,7 @@ export default {
state: ticket.state
})
},
assigTicket(ticket) {
assignTicket(ticket) {
this.updateTicketPartial({
id: ticket.id,
assigned_to: ticket.assigned_to

View file

@ -1,51 +1,53 @@
<template>
<div class="container-fluid px-xl-5 mt-3">
<div class="row">
<div class="col-xl-8 offset-xl-2">
<SlotTable
:columns="['id', 'name', 'state', 'last_activity', 'assigned_to', 'actions', 'actions2']"
:items="tickets.map(formatTicket)"
:keyName="'id'"
v-if="layout === 'table'"
>
<template v-slot:actions="{item}">
<div class="btn-group">
<a class="btn btn-primary" :href="'/'+ getEventSlug + '/ticket/' + item.id" title="view"
@click.prevent="gotoDetail(item)">
<font-awesome-icon icon="eye"/>
View
</a>
</div>
</template>
</SlotTable>
<AsyncLoader :loaded="tickets.length > 0">
<div class="container-fluid px-xl-5 mt-3">
<div class="row">
<div class="col-xl-8 offset-xl-2">
<SlotTable
:columns="['id', 'name', 'state', 'last_activity', 'assigned_to', 'actions', 'actions2']"
:items="tickets.map(formatTicket)"
:keyName="'id'"
v-if="layout === 'table'"
>
<template v-slot:actions="{item}">
<div class="btn-group">
<a class="btn btn-primary" :href="'/'+ getEventSlug + '/ticket/' + item.id" title="view"
@click.prevent="gotoDetail(item)">
<font-awesome-icon icon="eye"/>
View
</a>
</div>
</template>
</SlotTable>
</div>
</div>
</div>
<CollapsableCards v-if="layout === 'tasks'" :items="tickets"
:columns="['id', 'name', 'last_activity', 'assigned_to']"
:keyName="'state'" :sections="['pending_new', 'pending_open','pending_shipping',
<CollapsableCards v-if="layout === 'tasks'" :items="tickets"
:columns="['id', 'name', 'last_activity', 'assigned_to']"
:keyName="'state'" :sections="['pending_new', 'pending_open','pending_shipping',
'pending_physical_confirmation','pending_return','pending_postponed'].map(stateInfo)">
<template #section_header="{index, section, count}">
{{ section.text }} <span class="badge badge-light ml-1">{{ count }}</span>
</template>
<template #section_body="{item}">
<tr>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.last_activity }}</td>
<td>{{ item.assigned_to }}</td>
<td>
<div class="btn-group">
<a class="btn btn-primary" :href="'/'+ getEventSlug + '/ticket/' + item.id" title="view"
@click.prevent="gotoDetail(item)">
<font-awesome-icon icon="eye"/>
View
</a>
</div>
</td>
</tr>
</template>
</CollapsableCards>
</div>
<template #section_header="{index, section, count}">
{{ section.text }} <span class="badge badge-light ml-1">{{ count }}</span>
</template>
<template #section_body="{item}">
<tr>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.last_activity }}</td>
<td>{{ item.assigned_to }}</td>
<td>
<div class="btn-group">
<a class="btn btn-primary" :href="'/'+ getEventSlug + '/ticket/' + item.id" title="view"
@click.prevent="gotoDetail(item)">
<font-awesome-icon icon="eye"/>
View
</a>
</div>
</td>
</tr>
</template>
</CollapsableCards>
</div>
</AsyncLoader>
</template>
<script>
@ -56,10 +58,11 @@ import {mapActions, mapGetters, mapState} from 'vuex';
import Lightbox from '../components/Lightbox';
import SlotTable from "@/components/SlotTable.vue";
import CollapsableCards from "@/components/CollapsableCards.vue";
import AsyncLoader from "@/components/AsyncLoader.vue";
export default {
name: 'Tickets',
components: {Lightbox, SlotTable, Cards, Modal, EditItem, CollapsableCards},
components: {AsyncLoader, Lightbox, SlotTable, Cards, Modal, EditItem, CollapsableCards},
computed: {
...mapState(['tickets']),
...mapGetters(['stateInfo', 'getEventSlug', 'layout']),

View file

@ -1,9 +1,36 @@
<template>
<ul>
<li v-for="channel in userNotificationChannels" :key="channel.id">
{{ channel.id }} - {{ channel.channel_type }} - {{ channel.channel_target }} - {{ channel.event_filter }} - {{ channel.active }} - {{ channel.created }} - {{ channel.user }}
</li>
</ul>
<div>
<Table :items="userNotificationChannels.map(channel => ({...channel, username: channel.user.username || {}}))"
:columns="['id', 'username', 'channel_type', 'channel_target', 'event_filter', /*'active', 'created'*/]">
<template #actions="{ item }">
<div class="btn-group">
<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>
<script>