From 38a396ba21fbdce09be81cc2bc3d65a1c9b45c2a Mon Sep 17 00:00:00 2001 From: sajjadtalkhabi <97734061+sajjadtalkhabi@users.noreply.github.com> Date: Tue, 7 Jul 2026 17:36:43 +0330 Subject: [PATCH] fix --- src/App.vue | 2 + src/assets/icons/clock.svg | 4 +- src/assets/icons/monitor.svg | 11 ++ src/assets/icons/newspaper.svg | 6 + src/assets/icons/power.svg | 4 + src/assets/images/assignment-icon.png | Bin 0 -> 2372 bytes src/assets/images/course-icon.png | Bin 0 -> 2089 bytes src/assets/images/session-icon.png | Bin 0 -> 2367 bytes src/components/Badge.vue | 7 +- src/components/DropdownMenu.vue | 2 +- src/components/HeaderBlock.vue | 28 ++- src/components/blocks/MessageAttachments.vue | 100 ++++++++++ src/components/blocks/TabsBlock.vue | 7 +- src/components/form/DatePickerField.vue | 3 +- src/components/form/ImageCropper.vue | 3 +- src/components/icons/icon-names.d.ts | 3 + src/constants/fields.js | 1 + src/enums/index.js | 17 ++ .../components/AssignmentsFilters.vue | 4 +- .../components/modals/AddAssignmentModal.vue | 2 +- .../assignments/pages/AssignmentsListPage.vue | 7 +- .../components/ConsultationsFilters.vue | 2 +- .../courses/components/CoursesFilters.vue | 2 +- .../admin/courses/pages/CourseFormPage.vue | 32 ++-- .../admin/courses/pages/CoursesListPage.vue | 6 +- .../admin/exams/components/ExamsFilters.vue | 2 +- .../admin/exams/pages/ExamFormPage.vue | 27 ++- .../admin/exams/pages/ExamsListPage.vue | 7 +- .../messages/components/TicketsFilters.vue | 4 +- .../components/modals/TicketDetailsModal.vue | 114 ++++++++++- src/features/admin/router.js | 161 +++++++++++++--- .../services/components/ServicesFilters.vue | 2 +- .../sessions/components/SessionsFilters.vue | 4 +- .../admin/sessions/pages/SessionFormPage.vue | 31 ++- .../admin/sessions/pages/SessionsListPage.vue | 4 +- .../admin/terms/components/TermsFilters.vue | 2 +- .../admin/terms/pages/TermFormPage.vue | 34 ++-- .../admin/users/components/UsersFilters.vue | 2 +- .../admin/users/components/UsersTable.vue | 67 +++++-- .../components/modals/UserDetailsModal.vue | 119 +++++++----- .../verifications/VerificationItem.vue | 80 +++----- .../verifications/VerificationsFilters.vue | 2 +- .../modals/RejectReasonDetailsModal.vue | 4 +- .../modals/RejectReasonModal.vue | 48 +---- .../modals/UserVerificationDetailsModal.vue | 5 +- .../admin/users/pages/UserFormPage.vue | 36 ++-- .../users/pages/UserVerificationPage.vue | 4 +- .../admin/users/pages/UsersListPage.vue | 2 +- src/features/admin/users/schema/index.js | 9 +- src/features/auth/components/LoginForm.vue | 38 ++-- .../components/LoginWithVerifyCodeForm.vue | 24 +-- src/features/auth/components/SignupForm.vue | 24 +-- .../auth/components/VerifyCodeForm.vue | 41 ++-- .../EducationalInformation.vue | 7 +- .../studentRegister/PersonalInformation.vue | 28 +-- .../ProfessionalInformation.vue | 7 +- .../studentRegister/SideBarSteps.vue | 2 + .../studentRegister/SkillAssessmentPart6.vue | 7 +- .../studentRegister/SkillAssessmentPart7.vue | 7 +- .../studentRegister/SkillSelectStep.vue | 7 +- .../auth/composables/useRedirectAfterLogin.js | 4 +- .../auth/pages/StudentRegisterPage.vue | 4 +- src/features/auth/schema/student-register.js | 2 +- .../auth/store/student-registration.js | 34 ++-- src/features/counselor/router.js | 7 +- .../components/MissionaryRequestItem.vue | 181 ++++++++++++++++++ .../modals/MissionaryRequestDetailsModal.vue | 123 ++++++++++++ src/features/missionary/index.js | 1 + .../pages/MissionaryRequestsPage.vue | 103 ++++++++++ src/features/missionary/router.js | 24 +++ src/features/student/router.js | 105 ++++++++-- .../components/modals/TicketDetailsModal.vue | 16 +- src/layouts/AdminLayout.vue | 7 +- src/layouts/CounselorLayout.vue | 7 +- src/layouts/MissionaryLayout.vue | 156 +++++++++++++++ src/layouts/StudentLayout.vue | 14 +- src/layouts/sidebars/AdminSidebar.vue | 4 +- src/layouts/sidebars/CounselorSidebar.vue | 2 + src/layouts/sidebars/SidebarLogout.vue | 90 +++++++++ src/layouts/sidebars/StudentSidebar.vue | 2 + src/router/guards.js | 19 +- src/router/index.js | 14 +- src/services/api/endpoints.js | 4 + src/services/api/missionary-requests.js | 11 ++ .../mock/fixtures/missionary-requests.js | 62 ++++++ .../mock/routes/missionary-requests.js | 22 +++ src/services/query/auth.js | 21 +- src/services/query/missionary-requests.js | 33 ++++ src/utils/gallery.js | 6 + 89 files changed, 1815 insertions(+), 478 deletions(-) create mode 100644 src/assets/icons/monitor.svg create mode 100644 src/assets/icons/newspaper.svg create mode 100644 src/assets/icons/power.svg create mode 100644 src/assets/images/assignment-icon.png create mode 100644 src/assets/images/course-icon.png create mode 100644 src/assets/images/session-icon.png create mode 100644 src/components/blocks/MessageAttachments.vue create mode 100644 src/features/missionary/components/MissionaryRequestItem.vue create mode 100644 src/features/missionary/components/modals/MissionaryRequestDetailsModal.vue create mode 100644 src/features/missionary/index.js create mode 100644 src/features/missionary/pages/MissionaryRequestsPage.vue create mode 100644 src/features/missionary/router.js create mode 100644 src/layouts/MissionaryLayout.vue create mode 100644 src/layouts/sidebars/SidebarLogout.vue create mode 100644 src/services/api/missionary-requests.js create mode 100644 src/services/mock/fixtures/missionary-requests.js create mode 100644 src/services/mock/routes/missionary-requests.js create mode 100644 src/services/query/missionary-requests.js diff --git a/src/App.vue b/src/App.vue index fba44b9..4104ff2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -17,6 +17,7 @@ import StudentLayout from '@/layouts/StudentLayout.vue' import ConfirmModal from '@/components/ConfirmModal.vue' import CounselorLayout from '@/layouts/CounselorLayout.vue' import { tokenService } from '@/services/api/token-service' +import MissionaryLayout from '@/layouts/MissionaryLayout.vue' import { VueQueryDevtools } from '@tanstack/vue-query-devtools' const route = useRoute() @@ -26,6 +27,7 @@ const layouts = { admin: AdminLayout, student: StudentLayout, counselor: CounselorLayout, + missionary: MissionaryLayout, } const currentLayout = computed(() => layouts[route.meta?.layout] || PublicLayout) diff --git a/src/assets/icons/clock.svg b/src/assets/icons/clock.svg index 8f50197..0350cd2 100644 --- a/src/assets/icons/clock.svg +++ b/src/assets/icons/clock.svg @@ -1,7 +1,7 @@ - - + + diff --git a/src/assets/icons/monitor.svg b/src/assets/icons/monitor.svg new file mode 100644 index 0000000..f91ab4a --- /dev/null +++ b/src/assets/icons/monitor.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/icons/newspaper.svg b/src/assets/icons/newspaper.svg new file mode 100644 index 0000000..2a0e973 --- /dev/null +++ b/src/assets/icons/newspaper.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/icons/power.svg b/src/assets/icons/power.svg new file mode 100644 index 0000000..491ad8e --- /dev/null +++ b/src/assets/icons/power.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/images/assignment-icon.png b/src/assets/images/assignment-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..b8dfeb0c56a7019cc8ebb0ccddba51e093b48658 GIT binary patch literal 2372 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 TAYbP!00000NkvXXu0mjf?0W+V literal 0 HcmV?d00001 diff --git a/src/assets/images/session-icon.png b/src/assets/images/session-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..dbe86f71610b8c60b96ab8123e11cff25558edab GIT binary patch literal 2367 zcmV-F3BdM=P)|h)516bjSSVZCnfW(3=l6%R5CBq6fUPW!8$TCJT!;E_D8F%;X`>Z;A zRkvq6Ml7hS+f}z-=k=XOGyNY;h}ie|5kKZIb+IP+W}2 ze?KsGxU<81hlliVx#V5iCAu?VdU7N)-_wkCHfLv>e&@+ZXm^($KKvc+4hBMddo-QR zWPfL;)xyDnv|K?wV9+zQ(GWhsDK>!k13sMphWD1=WV&<4k5?-;`8J#J2z#G}d)~&= zwr6xS8u8@pO#JLKJk7i(w>u!-zagHr+0G6Xe>{@qU?O{;eIV26lpal|azDrnK4ha_ zn$3a+9zGeVH{SeO-QE5kzMm3h8K1mN)%G?P2LqnZH+m`yrmZcKb( zvQ5~0wh-!ov70v?(fc$zej)Suq1zje1sW!L^hoIb{f%ut_qKz5XQUplp7Y)9C6nFK zRIK?Fgj8KJ_j|2T>x!>M@oT_3?{!(Sb(oV?B6Pi-cS%RKYUhqR3#q=U-0gAZXujiU zJd-Kby=elV=)e=7DVp~bjWeFpSnF7;uHs)h=!z(gEdnoI;jix~jn^yVw#V@OVErL&4Y ziP-)!*4@9Zo0`Qm+^vkPs~E^#dj!3bx{G&B;Ya+3F(M|CURlXHqVF45e`l1Kznqp0 z9i7HBu`m<8$b#)J-sS2t(e|`Jo*LOs56AvOT{@S#5RXy9s>no zL>;Y#>QWJGMQIm{6##;ZZNr^7V#ZtK1(jeVi8fHcJ*%{2VXc~_!R4tKn(^!ofvbGU3hs6e?uMO| zGf2-(6HMzA05%*jfv8ulxWrK)-f`n3mcXo?6*tDCGYU8X8Av7#V-Y&V(BuvcvuM-AuR z4zMek3U4HVfg+Z^K@I@oX}u1XA`tg%bJPmrAw%#app*G@&Qv0((n>&a8 zr&osY?LdPjMAxsk3;oumFKp*RvP!^2dpHk$mtJdi%B%CbB?RW$c+fgU8Ev$uUu|D{ zLDA;LU?U_Q(e*5s#bQBiD7iS^wnR6tA{$K81^r$FEdfmQ0)T2N?t}5(O9@#aXU=WDMR?WQyTc84#Yk zM6&{>p!%XLRpBzGjRq{9wU3e>H#tm}CGGv46ETzbdjL>Z&e!Ln8-PkXm&WLs9wD!M z8aM8rZP549IEpNQ&rXcfR=QDJV5A)4K$AH(x?w<#lf)1^g=i{6jl_hU01qjJhA_6^ zT%FL6es412Ad&lD5x4=iL$k>0}^dT_Qtix2OuGfhXytPO<$O zDE@(R2(1R7EIl02OpnW|q{J4^N5!Cl8X8D8Ci5`x!D_RH$^{%jVld-IQ7?4J$NKAg;3~4lGm+E2*;~a@L^M@g+8mLFPxa ztHu5+QA$cM9oxi`2QcpGc&#b`J;#o`rcpYSNp*v{RM>i^i>4&?%LLsMnykH=6QsuBS9nL({kkmgjXE^Fk%^{ z(6&LjmY{Czd?}_tC7XQQxVMkzbUJvKM_F#UuT}Q4WYtLsl^(2GBjBVVHvw8dCm$!g zS-e(ORhz6_XqAe0HiNuQ90|Md~2W*`GD8+ zHBaiAPK%t7v?!JeG&BLu0aQLO08s3Gnso>~QkAe0T$IEj?<>~;?2y?Z`!!NWz({UU z7vj}Tl4^K?vi+@&qvy{Z`4T?)ko1o}`iKW3990WipKF>#5#?Uip~nv-hbtqd!)M#@ z!VVJq6oNVHL9adVV}}UtAfnTig?sjpk`~rQNdtejLyZy)VU)J4qramz+>U(3?5H;< z{ovj`{_5d?zq@`@u6^j$n$zO6q8rJ83RJtncb)MXkWhbliSZ&wNCF`hq|%W_tZ@#J z2$H)dq-mV$*V3$!t~-JF#8LACM1cWc58_XYFKRU(9`a9r{TcoO91{KG9eI4q@;CmC zNO*0CP+Er)i+~?#Vq9QD79=i2BNRyj!c71xIEfmOwS~aGMzF4Inv{+0fI%uCdH05+ lV-&3K4MH*MiTi&_{{g7e{jJ0pwWR<6002ovPDHLkV1hlBX1V|X literal 0 HcmV?d00001 diff --git a/src/components/Badge.vue b/src/components/Badge.vue index 7a0aad1..fa4da1b 100644 --- a/src/components/Badge.vue +++ b/src/components/Badge.vue @@ -21,7 +21,7 @@ import { computed } from 'vue' import SvgIcon from '@/components/icons/SvgIcon.vue' const props = defineProps({ - /** @type {import('vue').PropType<'neutral' | 'success' | 'danger' | 'warning' | 'primary' | 'info' | 'cyan'>} */ + /** @type {import('vue').PropType<'neutral' | 'success' | 'danger' | 'warning' | 'primary' | 'info' | 'cyan' | 'blue'>} */ variant: { type: String, default: 'neutral' }, /** @type {import('vue').PropType<'sm' | 'md' | 'lg'>} */ size: { type: String, default: 'md' }, @@ -138,5 +138,10 @@ const onClick = (event) => { background: rgba(104, 104, 104, 10%); color: #686868; } + + &--blue { + background: rgba(0, 72, 255, 8%); + color: #0048ff; + } } diff --git a/src/components/DropdownMenu.vue b/src/components/DropdownMenu.vue index 8839041..747219a 100644 --- a/src/components/DropdownMenu.vue +++ b/src/components/DropdownMenu.vue @@ -29,7 +29,7 @@ v-if="item.icon" :name="item.icon" :size="16" - :color="item.iconColor ?? item.color" + :color="item.iconColor ?? item.color ?? 'currentColor'" class="dropdown-menu__icon" /> {{ item.label }} diff --git a/src/components/HeaderBlock.vue b/src/components/HeaderBlock.vue index 1ccde30..245e779 100644 --- a/src/components/HeaderBlock.vue +++ b/src/components/HeaderBlock.vue @@ -11,7 +11,10 @@
-
{{ pageTitle }}
+
+ {{ pageTitle }} + {{ pageSubtitle || pageTitle }} +
@@ -27,7 +30,10 @@
-
{{ pageTitle }}
+
+ {{ pageTitle }} + {{ pageSubtitle || pageTitle }} +
@@ -43,22 +49,20 @@ + + diff --git a/src/components/blocks/TabsBlock.vue b/src/components/blocks/TabsBlock.vue index 0282968..a334792 100644 --- a/src/components/blocks/TabsBlock.vue +++ b/src/components/blocks/TabsBlock.vue @@ -126,7 +126,7 @@ const onActionClick = (tab) => { gap: 0.375rem; background: rgba(255, 255, 255, 80%); box-shadow: 0 10px 15px -3px rgba(241, 241, 241, 100%); - padding: 0.875rem; + padding: 0.775rem; border-radius: 0.75rem; } @@ -144,7 +144,8 @@ const onActionClick = (tab) => { &--desktop { display: none; min-width: fit-content; - margin-block-end: 0.625rem; + + // margin-block-end: 0.625rem; @media (min-width: 768px) { display: flex; @@ -162,7 +163,7 @@ const onActionClick = (tab) => { } &__action-btn { - padding: 0 1.25rem; + height: 100%; min-width: fit-content; } diff --git a/src/components/form/DatePickerField.vue b/src/components/form/DatePickerField.vue index 8495404..d8897cd 100644 --- a/src/components/form/DatePickerField.vue +++ b/src/components/form/DatePickerField.vue @@ -14,7 +14,7 @@ @click="toggle" > {{ displayValue }} - +
@@ -377,6 +377,7 @@ export default { this.$emit('change', gregorian) this.close() } catch (error) { + // eslint-disable-next-line no-console console.error('Error in handleConfirm:', error) } }, diff --git a/src/components/form/ImageCropper.vue b/src/components/form/ImageCropper.vue index 5ed5280..049cf3d 100644 --- a/src/components/form/ImageCropper.vue +++ b/src/components/form/ImageCropper.vue @@ -325,8 +325,7 @@ onBeforeUnmount(cleanup) margin: 0; @media (min-width: 1024px) { - font-size: 0.75rem; - line-height: 1.85rem; + font-size: 0.7rem; } } } diff --git a/src/components/icons/icon-names.d.ts b/src/components/icons/icon-names.d.ts index 3fb7d0d..f8c4c83 100644 --- a/src/components/icons/icon-names.d.ts +++ b/src/components/icons/icon-names.d.ts @@ -36,13 +36,16 @@ export type IconName = | 'map-pin-simple-area' | 'menu' | 'microphone' + | 'monitor' | 'mood' + | 'newspaper' | 'paper-plane' | 'paper-plane-right' | 'pencil' | 'phone' | 'play' | 'plus' + | 'power' | 'scroll' | 'shopping-bag' | 'smiley-sad' diff --git a/src/constants/fields.js b/src/constants/fields.js index 913180c..5e1b860 100644 --- a/src/constants/fields.js +++ b/src/constants/fields.js @@ -17,6 +17,7 @@ export const fields = { postalCode: 'کد پستی', birthDate: 'تاریخ تولد', + birthday: 'تاریخ تولد', gender: 'جنسیت', maritalStatus: 'وضعیت تأهل', diff --git a/src/enums/index.js b/src/enums/index.js index bf16596..3453321 100644 --- a/src/enums/index.js +++ b/src/enums/index.js @@ -206,3 +206,20 @@ export const SERVICE_TYPE = Object.freeze({ educational: 'آموزشی', other: 'سایر', }) + +// Lifecycle of a dispatch request assigned to a missionary. `pending` is the +// initial backend state; the missionary may set seen, accepted, or rejected. +export const MISSIONARY_REQUEST_STATUS = Object.freeze({ + pending: 'در انتظار بررسی', + seen: 'دیده شده', + accepted: 'پذیرفته شده', + rejected: 'رد شده', +}) + +// Landing route per role after login / when a logged-in user hits a guest +// page. Roles not listed here fall back to the student dashboard. +export const ROLE_HOME_ROUTES = Object.freeze({ + admin: 'admin-dashboard', + counselor: 'counselor-consultations', + missionary: 'missionary-requests', +}) diff --git a/src/features/admin/assignments/components/AssignmentsFilters.vue b/src/features/admin/assignments/components/AssignmentsFilters.vue index 465f5a0..88e064c 100644 --- a/src/features/admin/assignments/components/AssignmentsFilters.vue +++ b/src/features/admin/assignments/components/AssignmentsFilters.vue @@ -3,7 +3,7 @@
@@ -20,7 +20,7 @@
@@ -50,6 +50,7 @@ diff --git a/src/features/missionary/components/modals/MissionaryRequestDetailsModal.vue b/src/features/missionary/components/modals/MissionaryRequestDetailsModal.vue new file mode 100644 index 0000000..aa31d3a --- /dev/null +++ b/src/features/missionary/components/modals/MissionaryRequestDetailsModal.vue @@ -0,0 +1,123 @@ + + + + + diff --git a/src/features/missionary/index.js b/src/features/missionary/index.js new file mode 100644 index 0000000..588598c --- /dev/null +++ b/src/features/missionary/index.js @@ -0,0 +1 @@ +export { default as MissionaryRequestDetailsModal } from './components/modals/MissionaryRequestDetailsModal.vue' diff --git a/src/features/missionary/pages/MissionaryRequestsPage.vue b/src/features/missionary/pages/MissionaryRequestsPage.vue new file mode 100644 index 0000000..88975b9 --- /dev/null +++ b/src/features/missionary/pages/MissionaryRequestsPage.vue @@ -0,0 +1,103 @@ + + + + + diff --git a/src/features/missionary/router.js b/src/features/missionary/router.js new file mode 100644 index 0000000..c9a7ed5 --- /dev/null +++ b/src/features/missionary/router.js @@ -0,0 +1,24 @@ +export default [ + { + path: '/missionary-panel/requests', + name: 'missionary-requests', + component: () => import('@/features/missionary/pages/MissionaryRequestsPage.vue'), + meta: { + layout: 'missionary', + role: 'missionary', + title: 'سیستم مدیریت اعزام', + subtitle: 'ساده‌ترین راه برای برنامه‌ریزی و پایش مأموریت‌ها', + }, + }, + { + path: '/missionary-panel/tickets', + name: 'missionary-tickets', + component: () => import('@/features/student/tickets/pages/StudentTicketsPage.vue'), + meta: { + layout: 'missionary', + role: 'missionary', + title: 'تیکت‌های شما', + subtitle: 'اینجا پل ارتباطی شما با پشتیبانی است.', + }, + }, +] diff --git a/src/features/student/router.js b/src/features/student/router.js index c2cab36..8150bfb 100644 --- a/src/features/student/router.js +++ b/src/features/student/router.js @@ -3,90 +3,165 @@ export default [ path: '/dashboard', name: 'student-dashboard', component: () => import('@/features/student/pages/StudentDashboardPage.vue'), - meta: { layout: 'student', role: 'student', title: 'پیشخوان' }, + meta: { + layout: 'student', + role: 'student', + title: 'سلام بر شما', + subtitle: 'به سامانه موسسه بانو خوش آمدید.', + }, }, { path: '/profile', name: 'student-profile', component: () => import('@/features/student/profile/pages/EditProfilePage.vue'), - meta: { layout: 'student', role: 'student', title: 'ویرایش پروفایل' }, + meta: { + layout: 'student', + role: 'student', + title: 'پروفایل شما', + subtitle: 'مشخصاتتان را ویرایش نمایید.', + }, }, { path: '/my-courses', name: 'student-courses', component: () => import('@/features/student/courses/pages/StudentCoursesPage.vue'), - meta: { layout: 'student', role: 'student', title: 'دوره‌های من' }, + meta: { + layout: 'student', + role: 'student', + title: 'یادگیری شما', + subtitle: 'مهارت‌ها و دوره‌های خود را دنبال کنید.', + }, }, { path: '/my-courses/:id', name: 'student-course-details', component: () => import('@/features/student/courses/pages/StudentCourseDetailsPage.vue'), - meta: { layout: 'student', role: 'student', title: 'جزئیات دوره' }, + meta: { + layout: 'student', + role: 'student', + title: 'یادگیری شما', + subtitle: 'مهارت‌ها و دوره‌های خود را دنبال کنید.', + }, }, { path: '/my-terms', name: 'student-terms', component: () => import('@/features/student/terms/pages/StudentTermsPage.vue'), - meta: { layout: 'student', role: 'student', title: 'ترم‌های آموزشی' }, + meta: { + layout: 'student', + role: 'student', + title: 'یادگیری شما', + subtitle: 'مهارت‌ها و دوره‌های خود را دنبال کنید.', + }, }, { path: '/my-terms/:id', name: 'student-term-details', component: () => import('@/features/student/terms/pages/StudentTermDetailsPage.vue'), - meta: { layout: 'student', role: 'student', title: 'جزئیات دوره' }, + meta: { + layout: 'student', + role: 'student', + title: 'یادگیری شما', + subtitle: 'مهارت‌ها و دوره‌های خود را دنبال کنید.', + }, }, { path: '/my-lessons/:id', name: 'student-lesson-details', component: () => import('@/features/student/lessons/pages/StudentLessonDetailsPage.vue'), - meta: { layout: 'student', role: 'student', title: 'جزئیات درس' }, + meta: { + layout: 'student', + role: 'student', + title: 'یادگیری شما', + subtitle: 'مهارت‌ها و دوره‌های خود را دنبال کنید.', + }, }, { path: '/my-sessions/:id', name: 'student-session-details', component: () => import('@/features/student/sessions/pages/StudentSessionDetailsPage.vue'), - meta: { layout: 'student', role: 'student', title: 'جزئیات جلسه' }, + meta: { + layout: 'student', + role: 'student', + title: 'یادگیری شما', + subtitle: 'مهارت‌ها و دوره‌های خود را دنبال کنید.', + }, }, { path: '/my-exams/:id', name: 'student-exam', component: () => import('@/features/student/exams/pages/StudentExamPage.vue'), - meta: { layout: 'student', role: 'student', title: 'آزمون' }, + meta: { + layout: 'student', + role: 'student', + title: 'یادگیری شما', + subtitle: 'مهارت‌ها و دوره‌های خود را دنبال کنید.', + }, }, { path: '/my-exams/:id/take', name: 'student-take-exam', component: () => import('@/features/student/exams/pages/StudentTakeExamPage.vue'), - meta: { layout: 'student', role: 'student', title: 'شروع آزمون' }, + meta: { + layout: 'student', + role: 'student', + title: 'یادگیری شما', + subtitle: 'مهارت‌ها و دوره‌های خود را دنبال کنید.', + }, }, { path: '/missionary', name: 'student-missionary', component: () => import('@/features/student/missionary/pages/StudentMissionaryPage.vue'), - meta: { layout: 'student', role: 'student', title: 'سامانه اعزام' }, + meta: { + layout: 'student', + role: 'student', + title: 'سیستم مدیریت اعزام', + subtitle: 'ساده‌ترین راه برای برنامه‌ریزی و پایش مأموریت‌ها', + }, }, { path: '/user/services', name: 'student-services', component: () => import('@/features/student/services/pages/StudentServicesPage.vue'), - meta: { layout: 'student', role: 'student', title: 'خدمات' }, + meta: { + layout: 'student', + role: 'student', + title: 'درخواست خدمت', + subtitle: 'فقط یک مرحله تا شروع کار شما.', + }, }, { path: '/user/consultants', name: 'student-consultants', component: () => import('@/features/student/consultants/pages/StudentConsultantsPage.vue'), - meta: { layout: 'student', role: 'student', title: 'مشاوره' }, + meta: { + layout: 'student', + role: 'student', + title: 'مشاوره سریع', + subtitle: 'چند ثانیه برای دریافت مشاوره کافیست.', + }, }, { path: '/user/certificates', name: 'student-certificates', component: () => import('@/features/student/certificates/pages/StudentCertificatesPage.vue'), - meta: { layout: 'student', role: 'student', title: 'گواهینامه ها' }, + meta: { + layout: 'student', + role: 'student', + title: 'گواهینامه معتبر', + subtitle: 'نشان اعتماد و تخصص ما در هر حوزه', + }, }, { path: '/my-tickets', name: 'student-tickets', component: () => import('@/features/student/tickets/pages/StudentTicketsPage.vue'), - meta: { layout: 'student', role: 'student', title: 'تیکت‌های من' }, + meta: { + layout: 'student', + role: 'student', + title: 'تیکت‌های شما', + subtitle: 'اینجا پل ارتباطی شما با پشتیبانی است.', + }, }, ] diff --git a/src/features/student/tickets/components/modals/TicketDetailsModal.vue b/src/features/student/tickets/components/modals/TicketDetailsModal.vue index c613ea6..0e132b4 100644 --- a/src/features/student/tickets/components/modals/TicketDetailsModal.vue +++ b/src/features/student/tickets/components/modals/TicketDetailsModal.vue @@ -14,7 +14,17 @@
-
+
+
+
+ + {{ messageTime(ticket) }} +
+
+

{{ message.message }}

{{ messageTime(message) }}
@@ -60,6 +71,7 @@ import BasicModal from '@/components/BasicModal.vue' import SvgIcon from '@/components/icons/SvgIcon.vue' import NoItems from '@/components/blocks/NoItems.vue' import { formatJalaaliDate } from '@/utils/date-utils' +import MessageAttachments from '@/components/blocks/MessageAttachments.vue' import SkeletonLoaderBlock from '@/components/blocks/SkeletonLoaderBlock.vue' import { studentTicketsKeys, @@ -81,6 +93,8 @@ const { data: ticket, isLoading } = useStudentTicketQuery(ticketId, { const messages = computed(() => ticket.value?.messages ?? []) +const ticketMedia = computed(() => ticket.value?.media ?? []) + const ticketDate = computed(() => formatJalaaliDate(ticket.value?.createdAt) || '—') // Student viewing their own ticket — their own messages get the highlighted diff --git a/src/layouts/AdminLayout.vue b/src/layouts/AdminLayout.vue index 897ea22..e5c6d6a 100644 --- a/src/layouts/AdminLayout.vue +++ b/src/layouts/AdminLayout.vue @@ -6,7 +6,11 @@
- +
@@ -53,6 +57,7 @@ onMounted(() => window.addEventListener('resize', onResize)) onBeforeUnmount(() => window.removeEventListener('resize', onResize)) const pageTitle = computed(() => route.meta?.title || 'پیشخوان') +const pageSubtitle = computed(() => route.meta?.subtitle || '') const educationRoutes = new Set([ 'admin-terms', diff --git a/src/layouts/CounselorLayout.vue b/src/layouts/CounselorLayout.vue index fd8ecd6..c9dfaa9 100644 --- a/src/layouts/CounselorLayout.vue +++ b/src/layouts/CounselorLayout.vue @@ -6,7 +6,11 @@
- +
@@ -53,6 +57,7 @@ onMounted(() => window.addEventListener('resize', onResize)) onBeforeUnmount(() => window.removeEventListener('resize', onResize)) const pageTitle = computed(() => route.meta?.title || 'مشاوره') +const pageSubtitle = computed(() => route.meta?.subtitle || '') const menuItems = computed(() => [ { diff --git a/src/layouts/MissionaryLayout.vue b/src/layouts/MissionaryLayout.vue new file mode 100644 index 0000000..f7d35b3 --- /dev/null +++ b/src/layouts/MissionaryLayout.vue @@ -0,0 +1,156 @@ + + + + + diff --git a/src/layouts/StudentLayout.vue b/src/layouts/StudentLayout.vue index b1121ab..bdf9f48 100644 --- a/src/layouts/StudentLayout.vue +++ b/src/layouts/StudentLayout.vue @@ -21,7 +21,11 @@
- +