fix: bugs

This commit is contained in:
sajjadtalkhabi
2026-07-30 17:38:27 +03:30
parent e8b1e4add6
commit 36b747d267
34 changed files with 301 additions and 84 deletions
@@ -1,9 +1,9 @@
<template>
<div class="dashboard">
<div class="dashboard__main">
<!-- اطلاعات موسسه -->
<!-- اطلاعات مدرسه -->
<section class="dashboard__section">
<BoxedIconTitleBlock title="اطلاعات موسسه" desc="آخرین اطلاعات موسسه شما.">
<BoxedIconTitleBlock title="اطلاعات مدرسه" desc="آخرین اطلاعات مدرسه شما.">
<template #icon>
<SvgIcon name="heart" :size="24" color="var(--color-primary)" />
</template>
+1 -1
View File
@@ -7,7 +7,7 @@ export default [
layout: 'admin',
role: 'admin',
title: 'سلام بر شما',
subtitle: 'به سامانه موسسه بانو خوش آمدید.',
subtitle: 'به سامانه مدرسه بانو خوش آمدید.',
},
},
{
@@ -59,12 +59,6 @@
<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.workExperienceSummary || ''"
/>
</div>
<div class="user-verification-details__cell user-verification-details__cell--full">
<LineInfoBlock
title="خلاصه‌ای از فعالیت‌ها در موقعیت‌های شغلی"
+1 -1
View File
@@ -1,6 +1,6 @@
<template>
<div class="auth-heading">
<img :src="logo" alt="banu" class="auth-heading__logo" />
<img :src="logo" alt="مدرسه بانو" class="auth-heading__logo" />
<h2 class="auth-heading__title">{{ title }}</h2>
<p v-if="step === 'otp'" class="auth-heading__subtitle">
<span class="auth-heading__phone-label">ارسال کد فعال‌سازی به شماره</span>
@@ -2,7 +2,7 @@
<div class="pattern-section">
<div class="pattern-section__bg" :style="{ backgroundImage: `url(${bgImage})` }" />
<div class="pattern-section__front">
<img :src="frontImage" alt="banu" />
<img :src="frontImage" alt="مدرسه بانو" />
</div>
</div>
</template>
@@ -144,7 +144,7 @@ const onResend = async () => {
const body = { phone: props.phoneNumber }
if (props.mode === 'forgotPassword') await forgotMutation.mutateAsync(body)
else if (props.mode === 'loginWithCode') await requestLoginOtpMutation.mutateAsync(body)
else await resendCodeMutation.mutateAsync({ phoneNumber: props.phoneNumber })
else await resendCodeMutation.mutateAsync(body)
}
</script>
@@ -28,7 +28,7 @@
<SelectField
v-model="form.universityLevel"
name="universityLevel"
label="آخرین مدرک تحصیلی"
label="آخرین مدرک تحصیلی دانشگاهی"
:options="universityLevelOptions"
option-label="label"
option-value="value"
@@ -65,20 +65,6 @@
<LineTitleBlock title="مشخصات شغلی" title-en="Job Details" />
<div class="educational-info__row">
<div class="educational-info__cell educational-info__cell--full">
<TextareaField
v-model="form.workExperienceSummary"
name="workExperienceSummary"
label="خلاصه ای از سوابق شغلی خود را ذکر بفرمایید."
:row="isMobile ? 5 : 1"
:error="errors.workExperienceSummary"
@blur="validateAt('workExperienceSummary', form.workExperienceSummary)"
>
<template #appendIcon>
<SvgIcon name="file" :size="20" color="var(--color-thd-gray)" />
</template>
</TextareaField>
</div>
<div class="educational-info__cell educational-info__cell--full">
<TextareaField
v-model="form.activitySummary"
@@ -75,6 +75,51 @@
:error="errors.gender"
/>
</div>
<div class="personal-info__cell">
<TextField
v-model="form.childrenCount"
name="childrenCount"
label="تعداد فرزند"
inputmode="numeric"
:convert-digits="true"
:error="errors.childrenCount"
@blur="validateAt('childrenCount', form.childrenCount)"
>
<template #appendIcon>
<SvgIcon name="users-three" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
<div class="personal-info__cell">
<TextField
v-model="form.seminaryCode"
name="seminaryCode"
label="کد حوزوی"
inputmode="numeric"
:convert-digits="true"
:error="errors.seminaryCode"
@blur="validateAt('seminaryCode', form.seminaryCode)"
>
<template #appendIcon>
<SvgIcon name="graduation-cap" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
<div class="personal-info__cell">
<TextField
v-model="form.seminaryServiceCenterCode"
name="seminaryServiceCenterCode"
label="کد مرکز خدمات حوزه"
inputmode="numeric"
:convert-digits="true"
:error="errors.seminaryServiceCenterCode"
@blur="validateAt('seminaryServiceCenterCode', form.seminaryServiceCenterCode)"
>
<template #appendIcon>
<SvgIcon name="check-square" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
</div>
<LineTitleBlock title="اطلاعات تماس" title-en="Contact Details" />
@@ -95,6 +140,36 @@
</template>
</TextField>
</div>
<div class="personal-info__cell">
<TextField
v-model="form.virtualPhoneNumber"
name="virtualPhoneNumber"
label="شماره تلفن مجازی"
inputmode="numeric"
:convert-digits="true"
:error="errors.virtualPhoneNumber"
@blur="validateAt('virtualPhoneNumber', form.virtualPhoneNumber)"
>
<template #appendIcon>
<SvgIcon name="phone" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
<div class="personal-info__cell">
<TextField
v-model="form.postalCode"
name="postalCode"
label="کد پستی"
inputmode="numeric"
:convert-digits="true"
:error="errors.postalCode"
@blur="validateAt('postalCode', form.postalCode)"
>
<template #appendIcon>
<SvgIcon name="map-pin-simple-area" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
<div class="personal-info__cell">
<SelectField
v-model="form.provinceId"
@@ -141,7 +216,11 @@
<StepActions
:show-back="false"
:loading="uploadMutation.isPending.value || updateProfileMutation.isPending.value"
:loading="
uploadMutation.isPending.value ||
updateProfileMutation.isPending.value ||
saveRegisterMutation.isPending.value
"
/>
</form>
</template>
@@ -166,7 +245,12 @@ import { personalInformationSchema } from '@/features/auth/schema/student-regist
import StepActions from '@/features/auth/components/studentRegister/StepActions.vue'
import { useStudentRegistrationStore } from '@/features/auth/store/student-registration'
import { useGetCitiesOfProvinceQuery, useGetProvincesQuery } from '@/services/query/common'
import { authKeys, useUpdateProfileMutation, useUploadMediaMutation } from '@/services/query/auth'
import {
authKeys,
useSaveRegisterDataMutation,
useUpdateProfileMutation,
useUploadMediaMutation,
} from '@/services/query/auth'
const store = useStudentRegistrationStore()
const { user } = useAuth()
@@ -212,6 +296,11 @@ const form = ref({
cityId: user.value?.cityId || store.personal.cityId || null,
address:
(typeof user.value?.address === 'string' && user.value.address) || store.personal.address || '',
postalCode: store.personal.postalCode || '',
virtualPhoneNumber: store.personal.virtualPhoneNumber || '',
seminaryCode: store.personal.seminaryCode || '',
seminaryServiceCenterCode: store.personal.seminaryServiceCenterCode || '',
childrenCount: store.personal.childrenCount ?? '',
avatar: initialAvatar,
avatarId: store.personal.avatarId || '',
})
@@ -258,6 +347,7 @@ const onAvatarCropped = async (file) => {
const onAvatarError = (msg) => toast.error(msg)
const updateProfileMutation = useUpdateProfileMutation()
const saveRegisterMutation = useSaveRegisterDataMutation()
const onSubmit = async () => {
const { isValid, payload } = await validate(form.value)
@@ -280,6 +370,13 @@ const onSubmit = async () => {
: {}),
}
await updateProfileMutation.mutateAsync(body)
await saveRegisterMutation.mutateAsync({
postalCode: payload.postalCode,
virtualPhoneNumber: payload.virtualPhoneNumber,
seminaryCode: payload.seminaryCode,
seminaryServiceCenterCode: payload.seminaryServiceCenterCode,
childrenCount: payload.childrenCount,
})
await queryClient.invalidateQueries({ queryKey: authKeys.me() })
store.personal = { ...store.personal, ...next }
store.markStepAsCompleted(STUDENT_REGISTRATION.PERSONAL_INFORMATION)
@@ -1,6 +1,6 @@
<template>
<form class="professional-info" @submit.prevent="onSubmit">
<LineTitleBlock title="مشخصات شغلی" title-en="Job Details" />
<LineTitleBlock title="مشخصات تبلیغی" title-en="Propagation Details" />
<div class="professional-info__row">
<div class="professional-info__cell professional-info__cell--5">
<TextField
@@ -49,7 +49,7 @@
<SelectField
v-model="form.propagationPlatforms"
name="propagationPlatforms"
label="در چه بستر هایی فعالیت تبلیغی داشته اید؟"
label="در چه بستر هایی فعالیت تبلیغی داشته اید؟ (امکان انتخاب چند گزینه وجود دارد)"
:options="platformOptions"
option-label="label"
option-value="value"
@@ -1,11 +1,11 @@
<template>
<aside class="sidebar-steps">
<div class="sidebar-steps__mobile-logo">
<img :src="logoPinkishRed" alt="banu" />
<img :src="logoPinkishRed" alt="مدرسه بانو" />
</div>
<div class="sidebar-steps__panel">
<div class="sidebar-steps__logo">
<img :src="logoWhite" alt="banu" />
<img :src="logoWhite" alt="مدرسه بانو" />
</div>
<ul class="sidebar-steps__list">
<li
@@ -93,8 +93,8 @@ const STEP_LABELS = {
desc: 'لطفاً اطلاعات تحصیلی و شغلی خود را به‌صورت کامل و دقیق وارد کنید',
},
[STUDENT_REGISTRATION.PROFESSIONAL_INFORMATION]: {
title: 'اطلاعات فعالیت تبلیغاتی و حرفه‌ای',
desc: 'لطفاً اطلاعات تبلیغاتی و حرفه‌ای خود را به‌صورت کامل و دقیق وارد کنید',
title: 'اطلاعات فعالیت‌های تبلیغی',
desc: 'لطفاً اطلاعات فعالیت‌های تبلیغی خود را به‌صورت کامل و دقیق وارد کنید',
},
[STUDENT_REGISTRATION.SKILL_ASSESSMENT_PART_1]: {
title: 'بخش مهارت (بخش اول)',
+9 -1
View File
@@ -20,6 +20,15 @@ export const personalInformationSchema = object().shape({
provinceId: mixed().required(),
cityId: mixed().required(),
address: requiredString(10),
postalCode: string()
.required()
.matches(/^\d{10}$/, 'کد پستی باید ۱۰ رقم باشد'),
virtualPhoneNumber: string()
.required()
.matches(/^\d{10,15}$/, 'شماره تلفن مجازی نامعتبر است'),
seminaryCode: requiredString(),
seminaryServiceCenterCode: requiredString(),
childrenCount: number().typeError('تعداد فرزند باید عدد باشد').required().min(0).max(20),
})
export const educationalInformationSchema = object().shape({
@@ -28,7 +37,6 @@ export const educationalInformationSchema = object().shape({
universityLevel: string().required(),
universityName: requiredString(5),
fieldOfStudy: requiredString(5),
workExperienceSummary: requiredString(10),
activitySummary: requiredString(10),
})
@@ -18,6 +18,11 @@ const emptyState = () => ({
provinceId: null,
cityId: null,
address: '',
postalCode: '',
virtualPhoneNumber: '',
seminaryCode: '',
seminaryServiceCenterCode: '',
childrenCount: '',
},
educational: {
educationStatus: '',
@@ -25,7 +30,6 @@ const emptyState = () => ({
universityLevel: '',
universityName: '',
fieldOfStudy: '',
workExperienceSummary: '',
activitySummary: '',
},
professional: {
@@ -83,6 +87,11 @@ const SECTION_FIELDS = {
'provinceId',
'cityId',
'address',
'postalCode',
'virtualPhoneNumber',
'seminaryCode',
'seminaryServiceCenterCode',
'childrenCount',
],
educational: [
'educationStatus',
@@ -90,7 +99,6 @@ const SECTION_FIELDS = {
'universityLevel',
'universityName',
'fieldOfStudy',
'workExperienceSummary',
'activitySummary',
],
professional: [
+1 -1
View File
@@ -7,7 +7,7 @@ export default [
layout: 'counselor',
role: 'counselor',
title: 'سلام بر شما',
subtitle: 'به پنل مشاوره بانو خوش آمدید.',
subtitle: 'به پنل مشاوره مدرسه بانو خوش آمدید.',
},
},
{
@@ -10,7 +10,7 @@
<template #default="{ data, close }">
<form class="sample-modal__form" @submit.prevent="onSubmit(close)">
<p class="sample-modal__intro">
این مودال الگوی استاندارد بنو است: عنوان با
این مودال الگوی استاندارد مدرسه بانو است: عنوان با
<code>LineTitleBlock</code>
در راست، دکمهٔ بستن داخلی در چپ.
</p>
@@ -82,6 +82,7 @@ const onSubmit = async (close) => {
close()
form.value = { subject: '', note: '' }
resetErrors()
// eslint-disable-next-line no-console
console.info('SampleModal submit', payload)
}
</script>
@@ -142,7 +142,7 @@
/>
<BaseButton
variant="secondary"
text="نمایش مودال نمونه (الگوی بنو)"
text="نمایش مودال نمونه (الگوی مدرسه بانو)"
customClass="global-components__btn-wide"
@click="openModal('SampleModal', { context: 'پیش‌نمایش' })"
/>
+1 -1
View File
@@ -7,7 +7,7 @@ export default [
layout: 'missionary',
role: 'missionary',
title: 'سلام بر شما',
subtitle: 'به پنل اعزام بانو خوش آمدید.',
subtitle: 'به پنل اعزام مدرسه بانو خوش آمدید.',
},
},
{
@@ -101,6 +101,66 @@
option-value="value"
/>
</div>
<div class="edit-profile__cell edit-profile__cell--third">
<TextField
v-model="form.childrenCount"
name="childrenCount"
label="تعداد فرزند"
inputmode="numeric"
:convert-digits="true"
:error="errors.childrenCount"
@blur="validateAt('childrenCount', form.childrenCount)"
>
<template #appendIcon>
<SvgIcon name="users-three" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
<div class="edit-profile__cell edit-profile__cell--third">
<TextField
v-model="form.virtualPhoneNumber"
name="virtualPhoneNumber"
label="شماره تلفن مجازی"
inputmode="numeric"
:convert-digits="true"
:error="errors.virtualPhoneNumber"
@blur="validateAt('virtualPhoneNumber', form.virtualPhoneNumber)"
>
<template #appendIcon>
<SvgIcon name="phone" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
<div class="edit-profile__cell edit-profile__cell--third">
<TextField
v-model="form.seminaryCode"
name="seminaryCode"
label="کد حوزوی"
inputmode="numeric"
:convert-digits="true"
:error="errors.seminaryCode"
@blur="validateAt('seminaryCode', form.seminaryCode)"
>
<template #appendIcon>
<SvgIcon name="graduation-cap" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
<div class="edit-profile__cell edit-profile__cell--third">
<TextField
v-model="form.seminaryServiceCenterCode"
name="seminaryServiceCenterCode"
label="کد مرکز خدمات حوزه"
inputmode="numeric"
:convert-digits="true"
:error="errors.seminaryServiceCenterCode"
@blur="validateAt('seminaryServiceCenterCode', form.seminaryServiceCenterCode)"
>
<template #appendIcon>
<SvgIcon name="check-square" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
</div>
<LineTitleBlock title="مشخصات محل سکونت" title-en="Address Details" />
@@ -127,6 +187,21 @@
:disabled="!form.provinceId"
/>
</div>
<div class="edit-profile__cell edit-profile__cell--third">
<TextField
v-model="form.postalCode"
name="postalCode"
label="کد پستی"
inputmode="numeric"
:convert-digits="true"
:error="errors.postalCode"
@blur="validateAt('postalCode', form.postalCode)"
>
<template #appendIcon>
<SvgIcon name="map-pin-simple-area" :size="20" color="var(--color-thd-gray)" />
</template>
</TextField>
</div>
<div class="edit-profile__cell edit-profile__cell--full">
<TextareaField
v-model="form.address"
@@ -227,6 +302,8 @@ import { studentProfileSchema, studentResetPasswordSchema } from '@/features/stu
import {
authKeys,
useGetMeQuery,
useGetRegisterDataQuery,
useSaveRegisterDataMutation,
useUpdateProfileMutation,
useUploadMediaMutation,
} from '@/services/query/auth'
@@ -262,6 +339,11 @@ const form = ref({
provinceId: '',
cityId: '',
address: '',
postalCode: '',
virtualPhoneNumber: '',
seminaryCode: '',
seminaryServiceCenterCode: '',
childrenCount: '',
avatarMediaId: null,
})
@@ -326,6 +408,24 @@ watch(
{ immediate: true }
)
const { data: registerData } = useGetRegisterDataQuery()
watch(
registerData,
(data) => {
if (!data) return
form.value = {
...form.value,
postalCode: data.postalCode || '',
virtualPhoneNumber: data.virtualPhoneNumber || '',
seminaryCode: data.seminaryCode || '',
seminaryServiceCenterCode: data.seminaryServiceCenterCode || '',
childrenCount: data.childrenCount ?? '',
}
},
{ immediate: true }
)
const uploadMutation = useUploadMediaMutation()
const onAvatarCropped = async (file) => {
@@ -343,6 +443,7 @@ const onAvatarCropped = async (file) => {
const onAvatarError = (msg) => toast.error(msg)
const updateMutation = useUpdateProfileMutation()
const saveRegisterDataMutation = useSaveRegisterDataMutation()
const resetPasswordMutation = useUpdateProfileMutation()
const onSubmit = async () => {
@@ -359,7 +460,18 @@ const onSubmit = async () => {
...(payload.avatarMediaId ? { avatarMediaId: payload.avatarMediaId } : {}),
}
await updateMutation.mutateAsync(body)
await saveRegisterDataMutation.mutateAsync({
postalCode: payload.postalCode || '',
virtualPhoneNumber: payload.virtualPhoneNumber || '',
seminaryCode: payload.seminaryCode || '',
seminaryServiceCenterCode: payload.seminaryServiceCenterCode || '',
childrenCount:
payload.childrenCount == null || payload.childrenCount === ''
? ''
: Number(payload.childrenCount),
})
await queryClient.invalidateQueries({ queryKey: authKeys.me() })
await queryClient.invalidateQueries({ queryKey: authKeys.registerData() })
toast.success('پروفایل با موفقیت به‌روز شد')
router.push({ name: 'student-dashboard' })
}
@@ -15,6 +15,20 @@ export const studentProfileSchema = object().shape({
provinceId: string().nullable().notRequired(),
cityId: string().nullable().notRequired(),
address: string().nullable().notRequired(),
postalCode: string()
.nullable()
.notRequired()
.matches(/^\d{10}$/, { message: 'کد پستی باید ۱۰ رقم باشد', excludeEmptyString: true }),
virtualPhoneNumber: string()
.nullable()
.notRequired()
.matches(/^\d{10,15}$/, { message: 'شماره تلفن مجازی نامعتبر است', excludeEmptyString: true }),
seminaryCode: string().nullable().notRequired(),
seminaryServiceCenterCode: string().nullable().notRequired(),
childrenCount: string()
.nullable()
.notRequired()
.matches(/^\d+$/, { message: 'تعداد فرزند باید عدد باشد', excludeEmptyString: true }),
})
export const studentResetPasswordSchema = object().shape({
+1 -1
View File
@@ -7,7 +7,7 @@ export default [
layout: 'student',
role: 'student',
title: 'سلام بر شما',
subtitle: 'به سامانه موسسه بانو خوش آمدید.',
subtitle: 'به سامانه مدرسه بانو خوش آمدید.',
},
},
{