@@ -21,10 +21,6 @@
|
||||
</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" />
|
||||
|
||||
@@ -198,7 +198,6 @@ const normalizeExistingQuestions = (raw = []) => {
|
||||
optionText: o.optionText || '',
|
||||
isCorrect: !!o.isCorrect,
|
||||
})),
|
||||
// No `__local` flag — these came from the server, so the builder will lock them.
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -280,7 +279,6 @@ const submitting = computed(
|
||||
|
||||
const postQuestionsSequentially = async (id, list) => {
|
||||
for (const payload of list) {
|
||||
// Sequential so question position ordering is preserved on the backend.
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
await addQuestionMutation.mutateAsync({ examId: id, payload })
|
||||
}
|
||||
@@ -302,7 +300,7 @@ const onSubmit = async () => {
|
||||
if (targetExamId && newQuestions.length > 0) {
|
||||
await postQuestionsSequentially(targetExamId, newQuestions)
|
||||
}
|
||||
await queryClient.invalidateQueries({ queryKey: adminExamsKeys.all })
|
||||
await queryClient.invalidateQueries({ queryKey: adminExamsKeys.all, refetchType: 'all' })
|
||||
router.push({ name: 'admin-exams' })
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ const { data, isLoading } = useAdminTicketsListQuery(filters, pagination, {
|
||||
keepPreviousData: true,
|
||||
})
|
||||
|
||||
const tickets = computed(() => data.value?.data ?? [])
|
||||
const tickets = computed(() => data.value?.items ?? [])
|
||||
|
||||
const paginationMeta = computed(() => ({
|
||||
page: pagination.value.page,
|
||||
perPage: pagination.value.perPage,
|
||||
|
||||
@@ -39,11 +39,9 @@
|
||||
:src="mediaUrl"
|
||||
:video-id="session.id"
|
||||
/>
|
||||
<VoiceRecorder
|
||||
v-else-if="contentType === 'voice'"
|
||||
:model-value="mediaUrl"
|
||||
:disabled="true"
|
||||
/>
|
||||
<div v-else-if="contentType === 'voice'" class="session-details__voice">
|
||||
<VoiceRecorder :model-value="mediaUrl" :disabled="true" />
|
||||
</div>
|
||||
<a
|
||||
v-else-if="mediaUrl"
|
||||
:href="mediaUrl"
|
||||
@@ -111,9 +109,9 @@ const courseTitle = computed(() => session.value?.course?.title || '—')
|
||||
const isOnline = computed(() => session.value?.type === 'online')
|
||||
|
||||
const collectionToContentType = (collectionName) => {
|
||||
if (collectionName === 'videos') return 'video'
|
||||
if (collectionName === 'video') return 'video'
|
||||
if (collectionName === 'voice') return 'voice'
|
||||
if (collectionName === 'pdfs') return 'text'
|
||||
if (collectionName === 'pdf') return 'text'
|
||||
return ''
|
||||
}
|
||||
|
||||
@@ -173,6 +171,22 @@ const mediaFileName = computed(() => contentMedia.value?.fileName || 'فایل
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
&__voice {
|
||||
width: 100%;
|
||||
|
||||
/* stylelint-disable-next-line selector-class-pattern */
|
||||
:deep(.voice-recorder) {
|
||||
min-height: auto;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-class-pattern */
|
||||
:deep(.voice-recorder__main-btn) {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__file {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -357,7 +357,10 @@ const onSubmit = async () => {
|
||||
} else {
|
||||
await addMutation.mutateAsync(payload)
|
||||
}
|
||||
await queryClient.invalidateQueries({ queryKey: adminSessionsKeys.all })
|
||||
// The list query is inactive while we're on the form page, and the global
|
||||
// `refetchOnMount: false` means navigating back won't refetch a merely-stale
|
||||
// query — so force a refetch of all matching queries, active or not.
|
||||
await queryClient.invalidateQueries({ queryKey: adminSessionsKeys.all, refetchType: 'all' })
|
||||
router.push({ name: 'admin-sessions' })
|
||||
}
|
||||
|
||||
|
||||
+50
-45
@@ -17,19 +17,19 @@
|
||||
<LineInfoBlock title="نام و نام خانوادگی" :numeric-desc="fullName" />
|
||||
<LineInfoBlock
|
||||
title="تاریخ تولد"
|
||||
:numeric-desc="profile.faBirthDate || formatJalaaliDate(profile.birthDate) || ''"
|
||||
:numeric-desc="formatJalaaliDate(personal.birthDate) || ''"
|
||||
/>
|
||||
<LineInfoBlock title="شماره تماس" :numeric-desc="user.phoneNumber" />
|
||||
<LineInfoBlock title="کد ملی" :numeric-desc="user.nationalCode" />
|
||||
<LineInfoBlock title="شماره تماس" :numeric-desc="personal.phone" />
|
||||
<LineInfoBlock title="کد ملی" :numeric-desc="personal.nationalCode" />
|
||||
<LineInfoBlock
|
||||
title="وضعیت تاهل"
|
||||
:desc="profile.faMaritalStatus || MARITAL_STATUS[profile.maritalStatus] || ''"
|
||||
:desc="MARITAL_STATUS[personal.maritalStatus] || ''"
|
||||
/>
|
||||
<LineInfoBlock title="جنسیت" :desc="profile.faGender || GENDER[profile.gender] || ''" />
|
||||
<LineInfoBlock title="استان" :desc="user?.province?.name || '-'" />
|
||||
<LineInfoBlock title="شهر" :desc="user?.city?.name || '-'" />
|
||||
<LineInfoBlock title="جنسیت" :desc="GENDER[personal.gender] || ''" />
|
||||
<LineInfoBlock title="استان" :desc="personal.province || '-'" />
|
||||
<LineInfoBlock title="شهر" :desc="personal.city || '-'" />
|
||||
<div class="user-verification-details__cell user-verification-details__cell--span-3">
|
||||
<LineInfoBlock title="آدرس" :desc="user?.address || '-'" />
|
||||
<LineInfoBlock title="آدرس" :desc="personal.address || '-'" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -39,15 +39,15 @@
|
||||
<div class="user-verification-details__grid user-verification-details__grid--4">
|
||||
<LineInfoBlock
|
||||
title="وضعیت تحصیلی"
|
||||
:desc="profile.faEducationStatus || EDUCATION_STATUS[profile.educationStatus] || ''"
|
||||
:desc="EDUCATION_STATUS[profile.educationStatus] || ''"
|
||||
/>
|
||||
<LineInfoBlock
|
||||
title="آخرین مدرک تحصیلی (طلبه)"
|
||||
:desc="profile.faSeminaryLevel || SEMINARY_LEVEL[profile.seminaryLevel] || ''"
|
||||
:desc="SEMINARY_LEVEL[profile.seminaryLevel] || ''"
|
||||
/>
|
||||
<LineInfoBlock
|
||||
title="آخرین مدرک تحصیلی"
|
||||
:desc="profile.faUniversityLevel || UNIVERSITY_LEVEL[profile.universityLevel] || ''"
|
||||
:desc="UNIVERSITY_LEVEL[profile.universityLevel] || ''"
|
||||
/>
|
||||
<LineInfoBlock title="نام حوزه علمیه/دانشگاه" :desc="profile.universityName || ''" />
|
||||
<div class="user-verification-details__cell user-verification-details__cell--full">
|
||||
@@ -56,30 +56,12 @@
|
||||
:desc="profile.fieldOfStudy || ''"
|
||||
/>
|
||||
</div>
|
||||
<div class="user-verification-details__cell user-verification-details__cell--full">
|
||||
<LineInfoBlock
|
||||
title="خلاصهای از سوابق شغلی"
|
||||
:desc="profile.workExperienceSummary || ''"
|
||||
/>
|
||||
</div>
|
||||
<div class="user-verification-details__cell user-verification-details__cell--full">
|
||||
<LineInfoBlock
|
||||
title="خلاصهای از فعالیتها در موقعیتهای شغلی"
|
||||
:desc="profile.activitySummary || ''"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="user-verification-details__section">
|
||||
<LineTitleBlock title="اطلاعات شغلی" title-en="Job information" />
|
||||
<div class="user-verification-details__grid user-verification-details__grid--4">
|
||||
<div class="user-verification-details__cell user-verification-details__cell--full">
|
||||
<LineInfoBlock
|
||||
title="گرایش تخصصی حوزوی / رشته تحصیلی دانشگاهی"
|
||||
:desc="profile.fieldOfStudy || ''"
|
||||
/>
|
||||
</div>
|
||||
<div class="user-verification-details__cell user-verification-details__cell--full">
|
||||
<LineInfoBlock
|
||||
title="خلاصهای از سوابق شغلی"
|
||||
@@ -190,7 +172,9 @@
|
||||
<section v-if="faithProduction?.url" class="user-verification-details__section">
|
||||
<LineTitleBlock title="بخش صوت" title-en="Audio section" />
|
||||
<LineInfoBlock title="نظر و تحلیل از ویدئو ارائه شده" />
|
||||
<audio :src="faithProduction.url" controls class="user-verification-details__audio" />
|
||||
<div class="user-verification-details__voice">
|
||||
<VoiceRecorder :model-value="faithProduction" disabled />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-if="leaderMessage?.url" class="user-verification-details__section">
|
||||
@@ -214,6 +198,7 @@ import useModal from '@/composables/useModal'
|
||||
import BasicModal from '@/components/BasicModal.vue'
|
||||
import { formatJalaaliDate } from '@/utils/date-utils'
|
||||
import LineTitleBlock from '@/components/LineTitleBlock.vue'
|
||||
import VoiceRecorder from '@/components/form/VoiceRecorder.vue'
|
||||
import LineInfoBlock from '@/components/blocks/LineInfoBlock.vue'
|
||||
import { useAdminUserQuery, useAdminUserRegisterDataQuery } from '@/services/query/admin-users'
|
||||
import {
|
||||
@@ -227,7 +212,6 @@ import {
|
||||
SEMINARY_LEVEL,
|
||||
SESSION_CANCELLATION_RESPONSE,
|
||||
UNIVERSITY_LEVEL,
|
||||
VERIFICATION_MEDIA_TYPE,
|
||||
} from '@/enums'
|
||||
|
||||
defineOptions({ name: 'UserVerificationDetailsModal' })
|
||||
@@ -248,15 +232,23 @@ const { data: registerData } = useAdminUserRegisterDataQuery(userIdRef, {
|
||||
|
||||
const user = computed(() => modalData.value.user || fetched.value || null)
|
||||
|
||||
// The register-data endpoint returns the answers the user gave during the
|
||||
// registration steps — the same fields the sections below render. Merge it over
|
||||
// the user's stored profile so reviewing admins see what was actually submitted.
|
||||
const profile = computed(() => ({ ...user.value?.profile, ...registerData.value }))
|
||||
|
||||
const fullName = computed(
|
||||
() =>
|
||||
user.value?.name || `${user.value?.firstName || ''} ${user.value?.lastName || ''}`.trim() || '—'
|
||||
)
|
||||
const fullName = computed(() => user.value?.name || '—')
|
||||
const personal = computed(() => {
|
||||
const u = user.value || {}
|
||||
const p = u.profile || {}
|
||||
return {
|
||||
birthDate: u.birthday || p.birthDate || '',
|
||||
phone: u.phone || u.phoneNumber || '',
|
||||
nationalCode: u.nationalCode || '',
|
||||
maritalStatus: u.marriageStatus || p.maritalStatus || '',
|
||||
gender: u.gender || p.gender || '',
|
||||
address: typeof u.address === 'string' ? u.address : u.address?.address || '',
|
||||
province: u.province?.name || u.address?.province?.name || '',
|
||||
city: u.city?.name || u.address?.city?.name || '',
|
||||
}
|
||||
})
|
||||
|
||||
const platforms = computed(() =>
|
||||
Array.isArray(profile.value.propagationPlatforms) ? profile.value.propagationPlatforms : []
|
||||
@@ -276,13 +268,10 @@ const onlinePlatformDetails = computed(
|
||||
() => platforms.value.find((p) => p.platform === 'online')?.platformDetails || ''
|
||||
)
|
||||
|
||||
const verificationMedia = computed(() => user.value?.verification?.media || [])
|
||||
const faithProduction = computed(() =>
|
||||
verificationMedia.value.find((m) => m.type === VERIFICATION_MEDIA_TYPE.FAITH_PRODUCTION)
|
||||
)
|
||||
const leaderMessage = computed(() =>
|
||||
verificationMedia.value.find((m) => m.type === VERIFICATION_MEDIA_TYPE.LEADER_MESSAGE)
|
||||
)
|
||||
// Skill parts 6 & 7 store the uploaded media objects in register-data under
|
||||
// `faithProductionAudio` / `leaderMessageVideo` (each carries a `url`).
|
||||
const faithProduction = computed(() => profile.value.faithProductionAudio || null)
|
||||
const leaderMessage = computed(() => profile.value.leaderMessageVideo || null)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -428,6 +417,22 @@ const leaderMessage = computed(() =>
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
|
||||
&__voice {
|
||||
margin-top: 0.5rem;
|
||||
|
||||
/* stylelint-disable-next-line selector-class-pattern */
|
||||
:deep(.voice-recorder) {
|
||||
min-height: auto;
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line selector-class-pattern */
|
||||
:deep(.voice-recorder__main-btn) {
|
||||
width: 4rem;
|
||||
height: 4rem;
|
||||
}
|
||||
}
|
||||
|
||||
&__divider {
|
||||
border-block-end: 1px solid var(--color-thd-gray);
|
||||
margin-block: 1.5rem;
|
||||
|
||||
Reference in New Issue
Block a user