313 lines
11 KiB
Vue
313 lines
11 KiB
Vue
<template>
|
||
<div class="student-register">
|
||
<div class="student-register__container">
|
||
<aside class="student-register__sidebar">
|
||
<SideBarSteps :tabs="tabs" :active-step="store.currentStep" @select-step="goToStep" />
|
||
</aside>
|
||
|
||
<main class="student-register__main">
|
||
<header class="student-register__heading">
|
||
<div class="student-register__step-meta">
|
||
<span>مرحله {{ currentStepNumber }} از {{ totalSteps }}</span>
|
||
<span>{{ progressPercent }}٪</span>
|
||
</div>
|
||
<div class="student-register__progress" aria-hidden="true">
|
||
<span :style="{ width: `${progressPercent}%` }" />
|
||
</div>
|
||
<h1 class="student-register__title">{{ stepLabel.title }}</h1>
|
||
<p class="student-register__desc">{{ stepLabel.desc }}</p>
|
||
</header>
|
||
|
||
<div v-if="isRegistrationDataPending" class="student-register__loading" aria-live="polite">
|
||
در حال بازیابی اطلاعات ثبتنام…
|
||
</div>
|
||
<component :is="currentComponent" v-else />
|
||
</main>
|
||
</div>
|
||
|
||
<SuccessModal v-if="isModal('StudentRegisterSuccessModal')" />
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import useModal from '@/composables/useModal'
|
||
import { computed, markRaw, watch } from 'vue'
|
||
import { STUDENT_REGISTRATION } from '@/enums'
|
||
import SideBarSteps from '@/features/auth/components/studentRegister/SideBarSteps.vue'
|
||
import SuccessModal from '@/features/auth/components/studentRegister/SuccessModal.vue'
|
||
import { useStudentRegistrationStore } from '@/features/auth/store/student-registration'
|
||
import { useGetRegisterDataQuery, useGetRegistrationVideoQuery } from '@/services/query/auth'
|
||
import PersonalInformation from '@/features/auth/components/studentRegister/PersonalInformation.vue'
|
||
import SkillAssessmentPart1 from '@/features/auth/components/studentRegister/SkillAssessmentPart1.vue'
|
||
import SkillAssessmentPart2 from '@/features/auth/components/studentRegister/SkillAssessmentPart2.vue'
|
||
import SkillAssessmentPart3 from '@/features/auth/components/studentRegister/SkillAssessmentPart3.vue'
|
||
import SkillAssessmentPart4 from '@/features/auth/components/studentRegister/SkillAssessmentPart4.vue'
|
||
import SkillAssessmentPart5 from '@/features/auth/components/studentRegister/SkillAssessmentPart5.vue'
|
||
import SkillAssessmentPart6 from '@/features/auth/components/studentRegister/SkillAssessmentPart6.vue'
|
||
import SkillAssessmentPart7 from '@/features/auth/components/studentRegister/SkillAssessmentPart7.vue'
|
||
import EducationalInformation from '@/features/auth/components/studentRegister/EducationalInformation.vue'
|
||
import ProfessionalInformation from '@/features/auth/components/studentRegister/ProfessionalInformation.vue'
|
||
|
||
const store = useStudentRegistrationStore()
|
||
const { isModal, openModal } = useModal()
|
||
|
||
const { data: registerData, isPending: isRegistrationDataPending } = useGetRegisterDataQuery()
|
||
watch(
|
||
registerData,
|
||
(value) => {
|
||
if (value && Object.keys(value).length > 0) store.hydrateFromRegisterData(value)
|
||
},
|
||
{ immediate: true, flush: 'sync' }
|
||
)
|
||
|
||
const { data: registrationMedia } = useGetRegistrationVideoQuery({ retry: false })
|
||
watch(registrationMedia, (value) => store.setRegistrationMedia(value), {
|
||
immediate: true,
|
||
flush: 'sync',
|
||
})
|
||
|
||
const STEP_TO_COMPONENT = {
|
||
[STUDENT_REGISTRATION.PERSONAL_INFORMATION]: markRaw(PersonalInformation),
|
||
[STUDENT_REGISTRATION.EDUCATIONAL_INFORMATION]: markRaw(EducationalInformation),
|
||
[STUDENT_REGISTRATION.PROFESSIONAL_INFORMATION]: markRaw(ProfessionalInformation),
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_1]: markRaw(SkillAssessmentPart1),
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_2]: markRaw(SkillAssessmentPart2),
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_3]: markRaw(SkillAssessmentPart3),
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_4]: markRaw(SkillAssessmentPart4),
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_5]: markRaw(SkillAssessmentPart5),
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_6]: markRaw(SkillAssessmentPart6),
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_7]: markRaw(SkillAssessmentPart7),
|
||
}
|
||
|
||
const STEP_LABELS = {
|
||
[STUDENT_REGISTRATION.PERSONAL_INFORMATION]: {
|
||
title: 'اطلاعـــــات شخصـــــی',
|
||
desc: 'لطفاً اطلاعات شخصی خود را بهصورت کامل و دقیق وارد کنید',
|
||
},
|
||
[STUDENT_REGISTRATION.EDUCATIONAL_INFORMATION]: {
|
||
title: 'اطلاعـــــات تحصیلــــی و شغلی',
|
||
desc: 'لطفاً اطلاعات تحصیلی و شغلی خود را بهصورت کامل و دقیق وارد کنید',
|
||
},
|
||
[STUDENT_REGISTRATION.PROFESSIONAL_INFORMATION]: {
|
||
title: 'اطلاعات فعالیتهای تبلیغی',
|
||
desc: 'لطفاً اطلاعات فعالیتهای تبلیغی خود را بهصورت کامل و دقیق وارد کنید',
|
||
},
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_1]: {
|
||
title: 'بخش مهارت (بخش اول)',
|
||
desc: 'مهارتها و قابلیتهای خود را معرفی کنید.',
|
||
},
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_2]: {
|
||
title: 'بخش مهارت (بخش دوم)',
|
||
desc: 'مهارتها و قابلیتهای خود را معرفی کنید.',
|
||
},
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_3]: {
|
||
title: 'بخش مهارت (بخش سوم)',
|
||
desc: 'مهارتها و قابلیتهای خود را معرفی کنید.',
|
||
},
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_4]: {
|
||
title: 'بخش مهارت (بخش چهارم)',
|
||
desc: 'مهارتها و قابلیتهای خود را معرفی کنید.',
|
||
},
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_5]: {
|
||
title: 'بخش مهارت (بخش پنجم)',
|
||
desc: 'مهارتها و قابلیتهای خود را معرفی کنید.',
|
||
},
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_6]: {
|
||
title: 'بخش مهارت (بخش ششم)',
|
||
desc: 'مهارتها و قابلیتهای خود را معرفی کنید.',
|
||
},
|
||
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_7]: {
|
||
title: 'بخش مهارت (بخش هفتم)',
|
||
desc: 'مهارتها و قابلیتهای خود را معرفی کنید.',
|
||
},
|
||
}
|
||
|
||
const STEP_NAVIGATION = [
|
||
{ step: STUDENT_REGISTRATION.PERSONAL_INFORMATION, title: 'مشخصات فردی', icon: 'user' },
|
||
{ step: STUDENT_REGISTRATION.EDUCATIONAL_INFORMATION, title: 'اطلاعات تحصیلی', icon: 'book' },
|
||
{ step: STUDENT_REGISTRATION.PROFESSIONAL_INFORMATION, title: 'فعالیتهای تبلیغی', icon: 'file' },
|
||
{ step: STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_1, title: 'مهارت ۱', icon: 'check-square' },
|
||
{ step: STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_2, title: 'مهارت ۲', icon: 'check-square' },
|
||
{ step: STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_3, title: 'مهارت ۳', icon: 'check-square' },
|
||
{ step: STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_4, title: 'مهارت ۴', icon: 'check-square' },
|
||
{ step: STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_5, title: 'مهارت ۵', icon: 'check-square' },
|
||
{ step: STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_6, title: 'مهارت ۶', icon: 'check-square' },
|
||
{ step: STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_7, title: 'مهارت ۷', icon: 'check-square' },
|
||
]
|
||
|
||
const tabs = computed(() =>
|
||
STEP_NAVIGATION.map((tab) => ({
|
||
...tab,
|
||
completed: store.completedSteps.includes(tab.step),
|
||
disabled: !store.canGoToStep(tab.step),
|
||
}))
|
||
)
|
||
|
||
const totalSteps = STEP_NAVIGATION.length
|
||
const currentStepNumber = computed(() => {
|
||
const index = STEP_NAVIGATION.findIndex((item) => item.step === store.currentStep)
|
||
return index >= 0 ? index + 1 : 1
|
||
})
|
||
const progressPercent = computed(() => Math.round((currentStepNumber.value / totalSteps) * 100))
|
||
|
||
const currentComponent = computed(
|
||
() =>
|
||
STEP_TO_COMPONENT[store.currentStep] ||
|
||
STEP_TO_COMPONENT[STUDENT_REGISTRATION.PERSONAL_INFORMATION]
|
||
)
|
||
|
||
const stepLabel = computed(
|
||
() => STEP_LABELS[store.currentStep] || STEP_LABELS[STUDENT_REGISTRATION.PERSONAL_INFORMATION]
|
||
)
|
||
|
||
const goToStep = (step) => {
|
||
store.updateCurrentStep(step)
|
||
}
|
||
|
||
watch(
|
||
() => store.isProfileCompleted,
|
||
(val) => {
|
||
if (val) openModal('StudentRegisterSuccessModal')
|
||
},
|
||
{ immediate: true }
|
||
)
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.student-register {
|
||
min-height: 100dvh;
|
||
width: 100%;
|
||
|
||
&__container {
|
||
height: 100dvh;
|
||
max-width: 95%;
|
||
margin: 0 auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
padding: 0.5rem;
|
||
|
||
@media (min-width: 1024px) {
|
||
flex-direction: row;
|
||
align-items: stretch;
|
||
padding: 1rem;
|
||
gap: 0.75rem;
|
||
}
|
||
}
|
||
|
||
&__sidebar {
|
||
width: 100%;
|
||
|
||
@media (min-width: 1024px) {
|
||
width: 13.5rem;
|
||
min-width: 13.5rem;
|
||
}
|
||
}
|
||
|
||
&__main {
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow-y: auto;
|
||
padding: 0.5rem;
|
||
|
||
@media (min-width: 1024px) {
|
||
padding: 1rem 1.5rem;
|
||
}
|
||
}
|
||
|
||
&__heading {
|
||
margin-bottom: 1.25rem;
|
||
}
|
||
|
||
&__step-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 0.375rem;
|
||
color: #6f6f6f;
|
||
font-family: var(--font-family-number-fa);
|
||
font-size: 0.875rem;
|
||
font-weight: 500;
|
||
}
|
||
|
||
&__progress {
|
||
width: 100%;
|
||
height: 0.35rem;
|
||
margin-bottom: 1rem;
|
||
overflow: hidden;
|
||
border-radius: 999px;
|
||
background: #f2f2f2;
|
||
|
||
span {
|
||
display: block;
|
||
height: 100%;
|
||
border-radius: inherit;
|
||
background: var(--color-primary);
|
||
transition: width 0.2s ease;
|
||
}
|
||
}
|
||
|
||
&__title {
|
||
font-family: var(--font-family-fa);
|
||
font-weight: 600;
|
||
font-size: 1.375rem;
|
||
color: #3f3f3f;
|
||
margin: 0 0 0.375rem;
|
||
}
|
||
|
||
&__desc {
|
||
font-family: var(--font-family-fa);
|
||
font-weight: 400;
|
||
font-size: 0.9375rem;
|
||
line-height: 1.8;
|
||
color: #747474;
|
||
margin: 0;
|
||
}
|
||
|
||
&__loading {
|
||
padding: 3rem 1rem;
|
||
color: #747474;
|
||
font-family: var(--font-family-fa);
|
||
font-size: 1rem;
|
||
font-weight: 500;
|
||
text-align: center;
|
||
}
|
||
|
||
&__main :deep(.text-field__label),
|
||
&__main :deep(.textarea-field__label),
|
||
&__main :deep(.select-field__label),
|
||
&__main :deep(.datepicker-field__label) {
|
||
color: #626262;
|
||
font-size: 0.9375rem;
|
||
font-weight: 400;
|
||
}
|
||
|
||
&__main :deep(.text-field__input),
|
||
&__main :deep(.textarea-field__input),
|
||
&__main :deep(.select-field__trigger),
|
||
&__main :deep(.datepicker-field__trigger) {
|
||
min-height: 2.6rem;
|
||
font-size: 1rem;
|
||
font-weight: 400;
|
||
}
|
||
|
||
&__main :deep(.line-title__fa) {
|
||
font-size: 1rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
&__main :deep(.line-title__en) {
|
||
font-size: 0.8125rem;
|
||
font-weight: 400;
|
||
}
|
||
|
||
&__main :deep(.text-field__error),
|
||
&__main :deep(.textarea-field__error),
|
||
&__main :deep(.select-field__error),
|
||
&__main :deep(.datepicker-field__error) {
|
||
font-size: 0.8125rem;
|
||
font-weight: 400;
|
||
}
|
||
}
|
||
</style>
|