fix(courses): course cover image not saved on create/edit

The admin course form sent a stray upload id (imageId/uploadId) that
didn't match any field the API accepts; the API expects cover_media_id
with the media id returned from the upload endpoint. Also switch course
displays to the coverUrl field the API actually returns.
This commit is contained in:
Saghar Mohammadi
2026-09-02 15:02:02 +04:30
parent c0fe9e9918
commit fdc52381db
5 changed files with 18 additions and 22 deletions
@@ -1,8 +1,8 @@
<template>
<div class="course-item">
<div class="course-item__main">
<div v-if="course.image" class="course-item__image">
<img :src="course.image" :alt="course.title" />
<div v-if="course.coverUrl" class="course-item__image">
<img :src="course.coverUrl" :alt="course.title" />
</div>
<div v-else class="course-item__image course-item__image--placeholder">
<SvgIcon name="book" :size="22" color="#bcbcbc" />
@@ -30,14 +30,13 @@
/>
</div>
<div class="course-item__actions">
<div v-if="!isActive" class="course-item__status">
<span class="course-item__status-badge">
<span class="course-item__status-dot" />
غیرفعال
</span>
</div>
<div class="course-item__actions">
<ToggleSwitch
:model-value="!!isActive"
@update:model-value="emit('change-status', { id: course.id, isActive: !isActive })"
@@ -192,10 +191,6 @@ const isActive = computed(() => props.course.isActive ?? false)
justify-content: flex-start;
}
&__status {
flex: 0 0 auto;
}
&__status-badge {
display: inline-flex;
align-items: center;
@@ -217,6 +212,7 @@ const isActive = computed(() => props.course.isActive ?? false)
&__actions {
display: flex;
flex-wrap: wrap;
justify-content: flex-end;
align-items: center;
gap: 0.375rem;
@@ -154,7 +154,7 @@ const form = ref({
templateId: '',
title: '',
capacity: '',
imageId: null,
coverMediaId: null,
isActive: false,
})
@@ -212,10 +212,10 @@ watch(existingCourse, (course) => {
templateId: '',
title: course.title || '',
capacity: course.capacity ?? '',
imageId: course.imageId || null,
coverMediaId: course.coverMediaId || null,
isActive: course.isActive ?? false,
}
if (course.image) image.value = { url: course.image }
if (course.coverUrl) image.value = { url: course.coverUrl }
})
const uploadMutation = useUploadMediaMutation()
@@ -225,8 +225,8 @@ const onImageCropped = async (file) => {
const formData = objectToFormData({ file, purpose: 'cover', context: 'course' })
const response = await uploadMutation.mutateAsync(formData)
const payload = response?.data ?? response
image.value = { url: payload?.url, uploadId: payload?.uploadId, ...payload }
form.value.imageId = payload?.uploadId || payload?.id
image.value = { url: payload?.url, ...payload }
form.value.coverMediaId = payload?.id
} catch {
/* handled globally */
}
@@ -14,7 +14,7 @@
<template v-else-if="course">
<div class="course-details__hero">
<div class="course-details__image">
<img v-if="course.image" :src="course.image" :alt="course.title" />
<img v-if="course.coverUrl" :src="course.coverUrl" :alt="course.title" />
<SvgIcon v-else name="book" :size="32" color="#bcbcbc" />
</div>
<div class="course-details__grid">
+1 -1
View File
@@ -24,6 +24,6 @@ export const offeredCourseSchema = object().shape({
.required()
.integer('ظرفیت باید عدد صحیح باشد')
.min(1, 'ظرفیت باید حداقل ۱ نفر باشد'),
imageId: string().nullable().notRequired(),
coverMediaId: number().typeError('شناسه تصویر دوره باید عدد باشد').nullable().notRequired(),
isActive: boolean().nullable().notRequired(),
})
@@ -1,8 +1,8 @@
<template>
<div class="course-item">
<div class="course-item__main">
<div v-if="course.image" class="course-item__image">
<img :src="course.image" :alt="course.title" />
<div v-if="course.coverUrl" class="course-item__image">
<img :src="course.coverUrl" :alt="course.title" />
</div>
<div v-else class="course-item__image course-item__image--placeholder">
<SvgIcon name="book" :size="22" color="#bcbcbc" />