c3lf-system-3/web/src/views/Ticket.vue

214 lines
11 KiB
Vue

<template>
<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>
<Timeline :timeline="ticket.timeline">
<template v-slot:timeline_action1>
<span class="timeline-item-icon | faded-icon">
<font-awesome-icon icon="comment"/>
</span>
<div class="new-comment card bg-dark">
<div>
<textarea placeholder="add comment..." v-model="newComment"
class="form-control">
</textarea>
<AsyncButton class="btn btn-primary float-right" :task="addCommentAndClear" :disabled="!newComment">
<font-awesome-icon icon="comment"/>
Save Comment
</AsyncButton>
</div>
</div>
</template>
<template v-slot:timeline_action2>
<span class="timeline-item-icon | faded-icon">
<font-awesome-icon icon="envelope"/>
</span>
<div class="new-mail card bg-dark">
<div class="card-header">
{{ newestMailSubject }}
</div>
<div>
<textarea placeholder="reply mail..." v-model="newMail" class="form-control">
</textarea>
<AsyncButton class="btn btn-primary float-right" :task="sendMailAndClear" :disabled="!newMail">
<font-awesome-icon icon="envelope"/>
Send Mail
</AsyncButton>
</div>
</div>
</template>
</Timeline>
<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="selected_assignee">
<option v-for="user in users" :value="user.username">{{
user.username
}}
</option>
</select>
<button class="form-control btn btn-success"
@click="assignTicket(ticket)"
:disabled="!selected_assignee || (selected_assignee === ticket.assigned_to)">
Assign&nbsp;Ticket
</button>
</div>
<div class="btn-group">
<select class="form-control" v-model="selected_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)"
:disabled="(selected_state === ticket.state)">
Change&nbsp;Status
</button>
</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>
</div>
</div>
</div>
<div class="col-lg-3 col-xl-2 d-lg-none d-xl-block"
v-if="ticket.related_items && ticket.related_items.length">
<div class="card bg-dark text-light mb-2" id="filters">
<div class="card-body">
<h5 class="card-title text-info">Related</h5>
<div class="card bg-dark" v-for="item in ticket.related_items" v-bind:key="item.id">
<AuthenticatedImage v-if="item.file" cached
:src="`/media/2/256/${item.file}/`"
class="d-block card-img"
@click="openLightboxModalWith(item.file)"
/>
<div class="card-body">
<!--h6 class="card-title text-info"><span class="badge badge-primary">{{ item.relation_status }}</span></--h6-->
<h6 class="card-subtitle text-secondary">id: {{ item.id }} box: {{
item.box
}}</h6>
<router-link :to="{name: 'item', params: {id: item.id}}">
<h6 class="card-title">{{ item.description }}</h6>
</router-link>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</AsyncLoader>
</template>
<script>
import {mapActions, mapGetters, mapMutations, mapState} from 'vuex';
import Timeline from "@/components/Timeline.vue";
import ClipboardButton from "@/components/inputs/ClipboardButton.vue";
import AsyncLoader from "@/components/AsyncLoader.vue";
import AuthenticatedImage from "@/components/AuthenticatedImage.vue";
import AsyncButton from "@/components/inputs/AsyncButton.vue";
export default {
name: 'Ticket',
components: {AsyncButton, AuthenticatedImage, AsyncLoader, ClipboardButton, Timeline},
data() {
return {
selected_state: null,
selected_assignee: null,
shipping_voucher_type: null,
newMail: "",
newComment: ""
}
},
computed: {
...mapState(['state_options', 'users']),
...mapGetters(['availableShippingVoucherTypes', 'getAllTickets', 'route']),
ticket() {
const id = parseInt(this.route.params.id)
const ret = this.getAllTickets.find(ticket => ticket.id === id);
return ret ? ret : {};
},
shippingEmail() {
const domain = document.location.hostname;
return `ticket+${this.ticket.uuid}@${domain}`;
},
newestMailSubject() {
const mail = this.ticket.timeline ? this.ticket.timeline.filter(item => item.type === 'mail').pop() : null;
return mail ? mail.subject : "";
},
},
methods: {
...mapActions(['deleteItem', 'markItemReturned', 'sendMail', 'updateTicketPartial', 'postComment']),
...mapActions(['loadTickets', 'fetchTicketStates', 'loadUsers', 'scheduleAfterInit']),
...mapActions(['claimShippingVoucher', 'fetchShippingVouchers']),
...mapMutations(['openLightboxModalWith']),
changeTicketStatus() {
this.ticket.state = this.selected_state;
this.updateTicketPartial({
id: this.ticket.id,
state: this.selected_state,
})
},
assignTicket() {
this.ticket.assigned_to = this.selected_assignee;
this.updateTicketPartial({
id: this.ticket.id,
assigned_to: this.selected_assignee
})
},
sendMailAndClear: async function () {
await this.sendMail({
id: this.ticket.id,
message: this.newMail,
})
this.newMail = "";
},
addCommentAndClear: async function () {
await this.postComment({
id: this.ticket.id,
message: this.newComment
})
this.newComment = "";
}
},
mounted() {
this.scheduleAfterInit(() => [Promise.all([this.fetchTicketStates(), this.loadTickets(), this.loadUsers(), this.fetchShippingVouchers()]).then(() => {
if (this.ticket.state === "pending_new") {
this.selected_state = "pending_open";
this.changeTicketStatus()
}
this.selected_state = this.ticket.state;
this.selected_assignee = this.ticket.assigned_to
})]);
}
};
</script>
<style scoped>
</style>