250 lines
7.2 KiB
Vue
250 lines
7.2 KiB
Vue
<template>
|
|
<BasicModal
|
|
title="جزئیات تکلیف"
|
|
title-en="Assignment Details"
|
|
width="95%"
|
|
max-width="60rem"
|
|
min-width="auto"
|
|
:show-close-button="true"
|
|
>
|
|
<template #default="{ close }">
|
|
<div class="assignment-details">
|
|
<SkeletonLoaderBlock v-if="isLoading" :rows="3" :cols-per-row="1" />
|
|
<template v-else-if="assignment">
|
|
<div class="assignment-details__head">
|
|
<p class="assignment-details__title">{{ assignment.title || '—' }}</p>
|
|
<p class="assignment-details__sub">
|
|
<span>دوره: {{ courseTitle }}</span>
|
|
<span class="assignment-details__sep">|</span>
|
|
<span>جلسه: {{ sessionTitle }}</span>
|
|
</p>
|
|
</div>
|
|
|
|
<div class="assignment-details__grid">
|
|
<LineInfoBlock
|
|
title="تاریخ شروع"
|
|
:numeric-desc="formatJalaaliDate(assignment.startDate) || '—'"
|
|
/>
|
|
<LineInfoBlock title="تاریخ پایان" :numeric-desc="deadlineLabel" />
|
|
<LineInfoBlock title="مدت زمان" :numeric-desc="durationLabel" />
|
|
<LineInfoBlock title="اولویت" :desc="priorityLabel" />
|
|
<LineInfoBlock title="تعداد فرستندگان" :numeric-desc="submittersLabel" />
|
|
<LineInfoBlock
|
|
title="تاریخ ثبت"
|
|
:numeric-desc="
|
|
assignment.faCreatedAt || formatJalaaliDate(assignment.createdAt) || '—'
|
|
"
|
|
/>
|
|
</div>
|
|
|
|
<div v-if="assignment.description" class="assignment-details__desc">
|
|
<LineInfoBlock title="توضیحات" :desc="assignment.description" />
|
|
</div>
|
|
|
|
<div v-if="attachmentItems.length" class="assignment-details__attachments">
|
|
<LineTitleBlock title="فایلهای تکلیف" title-en="Attachments" />
|
|
<ul class="assignment-details__attachments-list">
|
|
<li v-for="file in attachmentItems" :key="file.id">
|
|
<a
|
|
:href="file.url || '#'"
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
class="assignment-details__file"
|
|
>
|
|
<SvgIcon name="file" :size="16" color="var(--color-prim-gray)" />
|
|
<span>{{ file.title }}</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</template>
|
|
<NoItems v-else title="یافت نشد" desc="اطلاعات این تکلیف در دسترس نیست." />
|
|
|
|
<div class="assignment-details__divider" />
|
|
|
|
<div class="assignment-details__footer">
|
|
<BaseButton text="بستن" custom-class="assignment-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 { ASSIGNMENT_PRIORITY } from '@/enums'
|
|
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 LineTitleBlock from '@/components/LineTitleBlock.vue'
|
|
import LineInfoBlock from '@/components/blocks/LineInfoBlock.vue'
|
|
import { useAdminAssignmentQuery } from '@/services/query/admin-assignments'
|
|
import SkeletonLoaderBlock from '@/components/blocks/SkeletonLoaderBlock.vue'
|
|
|
|
defineOptions({ name: 'AssignmentDetailsModal' })
|
|
|
|
const { getModal } = useModal()
|
|
|
|
const modalData = computed(() => getModal('AssignmentDetailsModal')?.data ?? {})
|
|
const assignmentId = computed(() => modalData.value.id ?? null)
|
|
|
|
const { data: assignment, isLoading } = useAdminAssignmentQuery(assignmentId, {
|
|
enabled: () => !!assignmentId.value,
|
|
})
|
|
|
|
const courseTitle = computed(
|
|
() =>
|
|
assignment.value?.session?.course?.title ||
|
|
assignment.value?.course?.title ||
|
|
assignment.value?.courseTitle ||
|
|
'—'
|
|
)
|
|
|
|
const sessionTitle = computed(
|
|
() => assignment.value?.session?.title || assignment.value?.sessionTitle || '—'
|
|
)
|
|
|
|
const deadlineRaw = computed(() => assignment.value?.endDate || assignment.value?.deadline || '')
|
|
const deadlineLabel = computed(() => formatJalaaliDate(deadlineRaw.value) || '—')
|
|
|
|
const durationLabel = computed(() => {
|
|
if (assignment.value?.durationDays != null) return `${assignment.value.durationDays} روز`
|
|
const start = assignment.value?.startDate || assignment.value?.createdAt
|
|
const end = deadlineRaw.value
|
|
if (!start || !end) return '—'
|
|
const diff = Math.ceil((new Date(end).getTime() - new Date(start).getTime()) / 86_400_000)
|
|
return diff >= 0 ? `${diff} روز` : '—'
|
|
})
|
|
|
|
const priorityLabel = computed(() => {
|
|
const priority =
|
|
assignment.value?.priority ||
|
|
(typeof assignment.value?.isPriority === 'boolean'
|
|
? assignment.value.isPriority
|
|
? 'mandatory'
|
|
: 'optional'
|
|
: null)
|
|
return ASSIGNMENT_PRIORITY[priority] || '—'
|
|
})
|
|
|
|
const submittersLabel = computed(
|
|
() => assignment.value?.submittersCount ?? assignment.value?.submissionsCount ?? 0
|
|
)
|
|
|
|
const attachmentItems = computed(() => {
|
|
const media = assignment.value?.media
|
|
if (Array.isArray(media) && media.length > 0) {
|
|
return media.map((m) => ({
|
|
id: m.id,
|
|
title: m.fileName || `فایل ${m.id}`,
|
|
url: m.url || m.downloadUrl || '',
|
|
}))
|
|
}
|
|
const legacy = assignment.value?.attachments
|
|
if (Array.isArray(legacy)) {
|
|
return legacy.map((a) => ({
|
|
id: a.id,
|
|
title: a.title || `فایل ${a.id}`,
|
|
url: a.fileUrl || '',
|
|
}))
|
|
}
|
|
return []
|
|
})
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.assignment-details {
|
|
width: 100%;
|
|
text-align: start;
|
|
|
|
&__head {
|
|
margin: 1rem 0 0.5rem;
|
|
}
|
|
|
|
&__title {
|
|
font-family: var(--font-family-fa);
|
|
font-size: 1.1rem;
|
|
font-weight: 500;
|
|
color: #4b4b4b;
|
|
margin: 0 0 0.25rem;
|
|
}
|
|
|
|
&__sub {
|
|
font-family: var(--font-family-fa);
|
|
font-size: 0.75rem;
|
|
color: #535353;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
&__sep {
|
|
color: #c4c4c4;
|
|
}
|
|
|
|
&__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(3, 1fr);
|
|
}
|
|
}
|
|
|
|
&__desc,
|
|
&__attachments {
|
|
margin-top: 1rem;
|
|
}
|
|
|
|
&__attachments-list {
|
|
list-style: none;
|
|
padding: 0;
|
|
margin: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.25rem;
|
|
}
|
|
|
|
&__file {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 0.375rem;
|
|
color: var(--color-primary);
|
|
text-decoration: none;
|
|
font-family: var(--font-family-fa);
|
|
font-size: 0.85rem;
|
|
|
|
&: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>
|