206 lines
6.1 KiB
Vue
206 lines
6.1 KiB
Vue
<template>
|
|
<BasicModal
|
|
title="جزئیات جلسه"
|
|
title-en="details"
|
|
width="95%"
|
|
max-width="60rem"
|
|
min-width="auto"
|
|
:show-close-button="true"
|
|
>
|
|
<template #default="{ close }">
|
|
<div class="session-details">
|
|
<SkeletonLoaderBlock v-if="isLoading" :rows="3" :cols-per-row="1" />
|
|
<template v-else-if="session">
|
|
<div class="session-details__grid">
|
|
<LineInfoBlock title="عنوان جلسه" :desc="session.title || '—'" />
|
|
<LineInfoBlock
|
|
title="تاریخ شروع"
|
|
:numeric-desc="formatJalaaliDate(session.startsAt) || '—'"
|
|
/>
|
|
|
|
<LineInfoBlock title="مدت زمان جلسه" :numeric-desc="durationLabel" />
|
|
|
|
<LineInfoBlock title="نوع جلسه" :desc="sessionTypeLabel" />
|
|
<LineInfoBlock title="دوره مرتبط" :desc="courseTitle" />
|
|
<LineInfoBlock title="محتوای جلسه" :desc="contentTypeLabel" />
|
|
</div>
|
|
|
|
<div v-if="session.description" class="session-details__desc">
|
|
<LineInfoBlock title="توضیحات جلسه" :desc="session.description" />
|
|
</div>
|
|
|
|
<div v-if="isOnline && session.link" class="session-details__link-row">
|
|
<LineInfoBlock title="لینک جلسه" :desc="session.link" />
|
|
</div>
|
|
|
|
<div v-if="mediaUrl || contentType" class="session-details__media">
|
|
<VideoPlayerBlock
|
|
v-if="contentType === 'video'"
|
|
:src="mediaUrl"
|
|
:video-id="session.id"
|
|
/>
|
|
<VoiceRecorder
|
|
v-else-if="contentType === 'voice'"
|
|
:model-value="mediaUrl"
|
|
:disabled="true"
|
|
/>
|
|
<a
|
|
v-else-if="mediaUrl"
|
|
:href="mediaUrl"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="session-details__file"
|
|
>
|
|
<SvgIcon name="file" :size="18" color="var(--color-primary)" />
|
|
<span>{{ mediaFileName }}</span>
|
|
</a>
|
|
</div>
|
|
</template>
|
|
<NoItems v-else title="یافت نشد" desc="اطلاعات این جلسه در دسترس نیست." />
|
|
|
|
<div class="session-details__divider" />
|
|
|
|
<div class="session-details__footer">
|
|
<BaseButton text="تایید" custom-class="session-details__close-btn" @click="close">
|
|
<template #appendIcon>
|
|
<SvgIcon name="arrow-left" :size="18" color="#fff" />
|
|
</template>
|
|
</BaseButton>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
</BasicModal>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed } from 'vue'
|
|
import useModal from '@/composables/useModal'
|
|
import BasicModal from '@/components/BasicModal.vue'
|
|
import BaseButton from '@/components/BaseButton.vue'
|
|
import SvgIcon from '@/components/icons/SvgIcon.vue'
|
|
import NoItems from '@/components/blocks/NoItems.vue'
|
|
import { formatJalaaliDate } from '@/utils/date-utils'
|
|
import { COURSE_CONTENT_TYPE, SESSION_TYPE } from '@/enums'
|
|
import VoiceRecorder from '@/components/form/VoiceRecorder.vue'
|
|
import LineInfoBlock from '@/components/blocks/LineInfoBlock.vue'
|
|
import { useAdminSessionQuery } from '@/services/query/admin-sessions'
|
|
import VideoPlayerBlock from '@/components/blocks/VideoPlayerBlock.vue'
|
|
import SkeletonLoaderBlock from '@/components/blocks/SkeletonLoaderBlock.vue'
|
|
|
|
defineOptions({ name: 'SessionDetailsModal' })
|
|
|
|
const { getModal } = useModal()
|
|
|
|
const modalData = computed(() => getModal('SessionDetailsModal')?.data ?? {})
|
|
const sessionId = computed(() => modalData.value.id ?? null)
|
|
|
|
const { data: session, isLoading } = useAdminSessionQuery(sessionId, {
|
|
enabled: () => !!sessionId.value,
|
|
})
|
|
|
|
const sessionTypeLabel = computed(
|
|
() => SESSION_TYPE[session.value?.type] || session.value?.typeFa || '—'
|
|
)
|
|
|
|
const durationLabel = computed(() =>
|
|
session.value?.durationMinutes == null ? '—' : `${session.value.durationMinutes} دقیقه`
|
|
)
|
|
|
|
const courseTitle = computed(() => session.value?.course?.title || '—')
|
|
|
|
const isOnline = computed(() => session.value?.type === 'online')
|
|
|
|
const collectionToContentType = (collectionName) => {
|
|
if (collectionName === 'videos') return 'video'
|
|
if (collectionName === 'voices') return 'voice'
|
|
if (collectionName === 'pdfs') return 'text'
|
|
return ''
|
|
}
|
|
|
|
const media = computed(() => (Array.isArray(session.value?.media) ? session.value.media : []))
|
|
|
|
const contentMedia = computed(() => media.value.find((m) => m.collectionName !== 'cover'))
|
|
|
|
const contentType = computed(() => collectionToContentType(contentMedia.value?.collectionName))
|
|
|
|
const contentTypeLabel = computed(() => COURSE_CONTENT_TYPE[contentType.value] || '—')
|
|
|
|
const mediaUrl = computed(() => contentMedia.value?.url || contentMedia.value?.downloadUrl || '')
|
|
|
|
const mediaFileName = computed(() => contentMedia.value?.fileName || 'فایل پیوست')
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.session-details {
|
|
width: 100%;
|
|
text-align: start;
|
|
|
|
&__grid {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
gap: 0.25rem;
|
|
|
|
@media (min-width: 640px) {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
@media (min-width: 1024px) {
|
|
grid-template-columns: repeat(4, 1fr);
|
|
}
|
|
}
|
|
|
|
&__desc,
|
|
&__link-row {
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
&__link-row {
|
|
display: grid;
|
|
grid-template-columns: 1fr;
|
|
|
|
@media (min-width: 640px) {
|
|
grid-template-columns: repeat(2, 1fr);
|
|
}
|
|
|
|
@media (min-width: 1024px) {
|
|
grid-template-columns: repeat(4, 1fr);
|
|
}
|
|
}
|
|
|
|
&__media {
|
|
margin-top: 1rem;
|
|
display: flex;
|
|
justify-content: center;
|
|
}
|
|
|
|
&__file {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.5rem;
|
|
color: var(--color-primary);
|
|
text-decoration: none;
|
|
font-family: var(--font-family-fa);
|
|
font-size: 0.875rem;
|
|
padding: 0.5rem 0;
|
|
|
|
&:hover {
|
|
text-decoration: underline;
|
|
}
|
|
}
|
|
|
|
&__divider {
|
|
border-block-end: 1px solid var(--color-thd-gray);
|
|
margin-block: 1.25rem;
|
|
}
|
|
|
|
&__footer {
|
|
display: flex;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
&__close-btn {
|
|
min-width: 10rem;
|
|
}
|
|
}
|
|
</style>
|