145 lines
3.7 KiB
Vue
145 lines
3.7 KiB
Vue
<template>
|
|
<BasicModal
|
|
title="جزئیات تیکت"
|
|
title-en="Ticket details"
|
|
width="95%"
|
|
max-width="58rem"
|
|
min-width="auto"
|
|
:show-close-button="true"
|
|
>
|
|
<template #default>
|
|
<div class="tdm">
|
|
<SkeletonLoaderBlock v-if="isLoading && !ticket" :rows="2" :cols-per-row="1" />
|
|
|
|
<template v-else-if="ticket">
|
|
<div class="tdm__top">
|
|
<Badge :variant="statusVariant" size="sm" dot :value="statusLabel" />
|
|
<span class="tdm__date">{{ requestDate }}</span>
|
|
</div>
|
|
|
|
<div v-if="messages.length > 0" class="tdm__messages">
|
|
<div v-for="message in messages" :key="message.id" class="tdm__bubble">
|
|
<p class="tdm__text">{{ message.message }}</p>
|
|
<span class="tdm__time">{{ messageTime(message) }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<NoItems v-else title="پیامی نیست" desc="هنوز پیامی برای این تیکت ثبت نشده است." />
|
|
</template>
|
|
</div>
|
|
</template>
|
|
</BasicModal>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
import { TICKET_STATUS } from '@/enums'
|
|
import Badge from '@/components/Badge.vue'
|
|
import useModal from '@/composables/useModal'
|
|
import BasicModal from '@/components/BasicModal.vue'
|
|
import NoItems from '@/components/blocks/NoItems.vue'
|
|
import { formatJalaaliDate } from '@/utils/date-utils'
|
|
import { useStudentTicketQuery } from '@/services/query/student-tickets'
|
|
import SkeletonLoaderBlock from '@/components/blocks/SkeletonLoaderBlock.vue'
|
|
|
|
defineOptions({ name: 'TicketDetailsModal' })
|
|
|
|
const STATUS_VARIANT = {
|
|
open: 'warning',
|
|
answered: 'success',
|
|
closed: 'info',
|
|
}
|
|
|
|
const { getModal } = useModal()
|
|
|
|
const modalData = computed(() => getModal('TicketDetailsModal')?.data ?? {})
|
|
const ticketId = computed(() => modalData.value.id ?? null)
|
|
|
|
const { data: ticket, isLoading } = useStudentTicketQuery(ticketId, {
|
|
enabled: () => !!ticketId.value,
|
|
})
|
|
|
|
const messages = computed(() => ticket.value?.messages ?? [])
|
|
|
|
const statusLabel = computed(() => TICKET_STATUS[ticket.value?.status] || '—')
|
|
const statusVariant = computed(() => STATUS_VARIANT[ticket.value?.status] || 'neutral')
|
|
|
|
const requestDate = computed(() => formatJalaaliDate(ticket.value?.createdAt) || '—')
|
|
|
|
const messageTime = (message) => {
|
|
if (!message.createdAt) return ''
|
|
const d = new Date(message.createdAt)
|
|
if (Number.isNaN(d.getTime())) return ''
|
|
return d.toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' })
|
|
}
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.tdm {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
padding-block: 0.25rem 0.5rem;
|
|
text-align: start;
|
|
|
|
&__header {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: flex-start;
|
|
}
|
|
|
|
&__top {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 0.75rem;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
&__date {
|
|
background: #f5f5f5;
|
|
color: #a3a3a3;
|
|
border-radius: 9999px;
|
|
padding: 0.3rem 0.875rem;
|
|
font-family: var(--font-family-fa);
|
|
font-size: 0.7rem;
|
|
line-height: 1.45;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
&__messages {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
&__bubble {
|
|
position: relative;
|
|
background: #f6f6f6;
|
|
padding: 1rem 1rem 1.5rem;
|
|
border-radius: 0.75rem 0.75rem 0;
|
|
color: #454545;
|
|
}
|
|
|
|
&__text {
|
|
font-family: var(--font-family-fa);
|
|
font-weight: 300;
|
|
font-size: 0.8rem;
|
|
line-height: 1.7;
|
|
margin: 0;
|
|
text-align: justify;
|
|
white-space: pre-wrap;
|
|
}
|
|
|
|
&__time {
|
|
position: absolute;
|
|
inset-inline-end: 1rem;
|
|
inset-block-end: 0.5rem;
|
|
font-family: var(--font-family-fa);
|
|
font-size: 0.65rem;
|
|
color: #a7a7a7;
|
|
line-height: 1.45;
|
|
}
|
|
}
|
|
</style>
|