fix
Deploy banu-front / deploy (push) Successful in 1m20s

This commit is contained in:
sajjadtalkhabi
2026-06-27 20:07:58 +03:30
parent 2c617258f4
commit e700609236
22 changed files with 236 additions and 177 deletions
@@ -5,12 +5,6 @@
<div class="ses__stats">
<div class="ses__stat">
<SvgIcon name="check" :size="12" color="#5d5d5d" />
<span class="ses__stat-label">تعداد آزمونها :</span>
<span class="ses__stat-value">{{ attemptsCountText }}</span>
</div>
<div class="ses__stat">
<SvgIcon name="scroll" :size="12" color="#5d5d5d" />
<span class="ses__stat-label">تعداد سوال :</span>
<span class="ses__stat-value">{{ questionsCountText }}</span>
</div>
@@ -65,11 +59,6 @@ const props = defineProps({
const emit = defineEmits(['start'])
const attemptsCountText = computed(() =>
props.exam.attemptsCount == null
? `${(props.exam.attempts || []).length} آزمون`
: `${props.exam.attemptsCount} آزمون`
)
const questionsCountText = computed(() =>
props.exam.questionsCount == null ? '—' : `${props.exam.questionsCount}`
)
@@ -14,12 +14,12 @@
<p class="student-lesson-item__title">{{ lesson.title || '—' }}</p>
<Badge
variant="primary"
label="زمان یادگیری :"
:value="hoursText"
label="ظرفیت :"
:value="capacityText"
class="student-lesson-item__hours"
>
<template #prepend>
<SvgIcon name="calendar" :size="11" color="#007074" />
<SvgIcon name="users-three" :size="11" color="#007074" />
</template>
</Badge>
</div>
@@ -75,8 +75,8 @@ const props = defineProps({
const emit = defineEmits(['show-details'])
const hoursText = computed(() =>
props.lesson.learningHours == null ? '—' : `${props.lesson.learningHours} ساعت`
const capacityText = computed(() =>
props.lesson.capacity == null ? '—' : `${props.lesson.capacity} نفر`
)
const sessionsText = computed(() =>
props.lesson.sessionsCount == null ? '—' : `${props.lesson.sessionsCount} جلسه`
@@ -41,11 +41,11 @@
<div class="edit-profile__cell edit-profile__cell--third">
<DatePickerField
v-model="form.birthDate"
name="birthDate"
v-model="form.birthday"
name="birthday"
label="تاریخ تولد"
:max="todayIso"
:error="errors.birthDate"
:error="errors.birthday"
/>
</div>
<div class="edit-profile__cell edit-profile__cell--third">
@@ -253,7 +253,7 @@ const initialPhoneNumber = ref('')
const form = ref({
name: '',
birthDate: '',
birthday: '',
nationalCode: '',
marriageStatus: '',
gender: '',
@@ -298,34 +298,38 @@ const onProvinceChange = () => {
const { data: profile } = useGetMeQuery()
watch(profile, (user) => {
if (!user) return
const fullName =
user.name || [user.firstName, user.lastName].filter(Boolean).join(' ').trim() || ''
const phone = user.phone || user.phoneNumber || ''
form.value = {
...form.value,
name: fullName,
birthDate: user.profile?.birthDate || '',
nationalCode: user.nationalCode || '',
marriageStatus: user.profile?.marriageStatus || '',
gender: user.profile?.gender || '',
phone,
provinceId: user?.province?.id || '',
cityId: user?.city?.id || '',
address: user?.address || '',
avatarMediaId: user.profile?.avatarMediaId || null,
}
initialNationalCode.value = user.nationalCode || ''
initialPhoneNumber.value = phone
if (user.avatarUrl) avatar.value = { url: user.avatarUrl }
})
watch(
profile,
(user) => {
if (!user) return
const fullName =
user.name || [user.firstName, user.lastName].filter(Boolean).join(' ').trim() || ''
const phone = user.phone || user.phoneNumber || ''
form.value = {
...form.value,
name: fullName,
birthday: user?.birthday || '',
nationalCode: user.nationalCode || '',
marriageStatus: user?.marriageStatus || '',
gender: user?.gender || '',
phone,
provinceId: user?.province?.id || '',
cityId: user?.city?.id || '',
address: user?.address || '',
avatarMediaId: user?.avatarMediaId || null,
}
initialNationalCode.value = user.nationalCode || ''
initialPhoneNumber.value = phone
if (user.avatarUrl) avatar.value = { url: user.avatarUrl }
},
{ immediate: true }
)
const uploadMutation = useUploadMediaMutation()
const onAvatarCropped = async (file) => {
try {
const formData = objectToFormData({ file, subType: 'avatar', context: 'user' })
const formData = objectToFormData({ file, purpose: 'avatar', context: 'user' })
const response = await uploadMutation.mutateAsync(formData)
const payload = response?.data ?? response
avatar.value = { url: payload?.url, uploadId: payload?.uploadId, ...payload }
@@ -344,7 +348,7 @@ const onSubmit = async () => {
const { isValid, payload } = await validate(form.value)
if (!isValid) return
// Backend PATCH /me accepts: name, email, phone, avatar_media_id.
// Other UI-only fields (province/city/address/birthDate/…) are not in
// Other UI-only fields (province/city/address/birthday/…) are not in
// the backend Postman doc and are dropped here. Password is handled
// separately via POST /reset-password.
const body = {
@@ -471,7 +475,7 @@ const onCancel = () => router.push({ name: 'student-dashboard' })
&__actions {
display: flex;
justify-content: flex-end;
gap: 0.625rem;
justify-content: space-between;
margin-top: 1rem;
}
+1 -1
View File
@@ -9,7 +9,7 @@ export const studentProfileSchema = object().shape({
name: string().required().min(2),
phone: phoneNumberRule,
nationalCode: nationalCodeRule,
birthDate: string().nullable().notRequired(),
birthday: string().nullable().notRequired(),
marriageStatus: string().nullable().notRequired(),
gender: string().nullable().notRequired(),
provinceId: string().nullable().notRequired(),
@@ -4,8 +4,8 @@
<p class="student-session-item__title">{{ session.title || '—' }}</p>
<div class="student-session-item__hours">
<SvgIcon name="calendar" :size="9" color="#007074" />
<span class="student-session-item__hours-label">زمان جلسه :</span>
<span class="student-session-item__hours-value">{{ durationText }}</span>
<span class="student-session-item__hours-label">مهلت :</span>
<span class="student-session-item__hours-value">{{ deadlineText }}</span>
</div>
</div>
@@ -13,8 +13,8 @@
<Badge v-if="isComplete" variant="success">تکمیل شده</Badge>
<Badge v-if="session.isSeen" variant="success">دیده شده</Badge>
<Badge v-if="session.grade != null" variant="primary" label="نمره" :value="session.grade" />
<Badge v-if="session.needsAssignment" icon="paper-plane">دارای تکلیف</Badge>
<Badge v-if="session.hasQuiz" icon="file">دارای آزمون</Badge>
<Badge v-if="session.homeworksCount > 0" icon="paper-plane">دارای تکلیف</Badge>
<Badge v-if="session.examsCount > 0" icon="file">دارای آزمون</Badge>
<Badge v-if="isOnline" icon="users-three">جلسه آنلاین</Badge>
</div>
@@ -37,6 +37,7 @@ import { computed } from 'vue'
import Badge from '@/components/Badge.vue'
import BaseButton from '@/components/BaseButton.vue'
import SvgIcon from '@/components/icons/SvgIcon.vue'
import { formatJalaaliDate } from '@/utils/date-utils'
const props = defineProps({
session: { type: Object, required: true },
@@ -44,8 +45,8 @@ const props = defineProps({
const emit = defineEmits(['start'])
const durationText = computed(() =>
props.session.durationHours == null ? '—' : `${props.session.durationHours} ساعت`
const deadlineText = computed(
() => formatJalaaliDate(props.session.deadline || props.session.endDate) || '—'
)
const isOnline = computed(() => props.session.type === 'online')
@@ -119,8 +119,6 @@ const { data: session, isLoading } = useStudentSessionQuery(sessionId, {
enabled: () => !!sessionId.value,
})
// The homework for this session comes from the dedicated endpoint (the session
// payload itself doesn't embed homeworks).
const homeworkFilters = computed(() => ({ sessionId: sessionId.value }))
const homeworkPagination = ref({ page: 1, perPage: 20 })
const { data: homeworksData } = useStudentHomeworksListQuery(homeworkFilters, homeworkPagination, {
@@ -152,7 +150,14 @@ const homeworkId = computed(
)
const uploadHomeworkFile = async (file) => {
const fd = objectToFormData({ file, purpose: 'homework_file', context: 'homework' })
// The backend validates allowed extensions per `purpose`: `homework_file`
// accepts documents/images only, so a recorded voice must go up as `voice`.
const isAudio = (file?.type || '').startsWith('audio/')
const fd = objectToFormData({
file,
purpose: isAudio ? 'voice' : 'homework_file',
context: 'homework',
})
const response = await uploadMediaMutation.mutateAsync(fd)
const payload = response?.data ?? response
return payload?.id ?? null