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> <template>
<div class="course-item"> <div class="course-item">
<div class="course-item__main"> <div class="course-item__main">
<div v-if="course.image" class="course-item__image"> <div v-if="course.coverUrl" class="course-item__image">
<img :src="course.image" :alt="course.title" /> <img :src="course.coverUrl" :alt="course.title" />
</div> </div>
<div v-else class="course-item__image course-item__image--placeholder"> <div v-else class="course-item__image course-item__image--placeholder">
<SvgIcon name="book" :size="22" color="#bcbcbc" /> <SvgIcon name="book" :size="22" color="#bcbcbc" />
@@ -30,14 +30,13 @@
/> />
</div> </div>
<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"> <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>
<ToggleSwitch <ToggleSwitch
:model-value="!!isActive" :model-value="!!isActive"
@update:model-value="emit('change-status', { id: course.id, isActive: !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; justify-content: flex-start;
} }
&__status {
flex: 0 0 auto;
}
&__status-badge { &__status-badge {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -217,6 +212,7 @@ const isActive = computed(() => props.course.isActive ?? false)
&__actions { &__actions {
display: flex; display: flex;
flex-wrap: wrap;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
gap: 0.375rem; gap: 0.375rem;
@@ -154,7 +154,7 @@ const form = ref({
templateId: '', templateId: '',
title: '', title: '',
capacity: '', capacity: '',
imageId: null, coverMediaId: null,
isActive: false, isActive: false,
}) })
@@ -212,10 +212,10 @@ watch(existingCourse, (course) => {
templateId: '', templateId: '',
title: course.title || '', title: course.title || '',
capacity: course.capacity ?? '', capacity: course.capacity ?? '',
imageId: course.imageId || null, coverMediaId: course.coverMediaId || null,
isActive: course.isActive ?? false, isActive: course.isActive ?? false,
} }
if (course.image) image.value = { url: course.image } if (course.coverUrl) image.value = { url: course.coverUrl }
}) })
const uploadMutation = useUploadMediaMutation() const uploadMutation = useUploadMediaMutation()
@@ -225,8 +225,8 @@ const onImageCropped = async (file) => {
const formData = objectToFormData({ file, purpose: 'cover', context: 'course' }) const formData = objectToFormData({ file, purpose: 'cover', context: 'course' })
const response = await uploadMutation.mutateAsync(formData) const response = await uploadMutation.mutateAsync(formData)
const payload = response?.data ?? response const payload = response?.data ?? response
image.value = { url: payload?.url, uploadId: payload?.uploadId, ...payload } image.value = { url: payload?.url, ...payload }
form.value.imageId = payload?.uploadId || payload?.id form.value.coverMediaId = payload?.id
} catch { } catch {
/* handled globally */ /* handled globally */
} }
@@ -14,7 +14,7 @@
<template v-else-if="course"> <template v-else-if="course">
<div class="course-details__hero"> <div class="course-details__hero">
<div class="course-details__image"> <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" /> <SvgIcon v-else name="book" :size="32" color="#bcbcbc" />
</div> </div>
<div class="course-details__grid"> <div class="course-details__grid">
+1 -1
View File
@@ -24,6 +24,6 @@ export const offeredCourseSchema = object().shape({
.required() .required()
.integer('ظرفیت باید عدد صحیح باشد') .integer('ظرفیت باید عدد صحیح باشد')
.min(1, 'ظرفیت باید حداقل ۱ نفر باشد'), .min(1, 'ظرفیت باید حداقل ۱ نفر باشد'),
imageId: string().nullable().notRequired(), coverMediaId: number().typeError('شناسه تصویر دوره باید عدد باشد').nullable().notRequired(),
isActive: boolean().nullable().notRequired(), isActive: boolean().nullable().notRequired(),
}) })
@@ -1,8 +1,8 @@
<template> <template>
<div class="course-item"> <div class="course-item">
<div class="course-item__main"> <div class="course-item__main">
<div v-if="course.image" class="course-item__image"> <div v-if="course.coverUrl" class="course-item__image">
<img :src="course.image" :alt="course.title" /> <img :src="course.coverUrl" :alt="course.title" />
</div> </div>
<div v-else class="course-item__image course-item__image--placeholder"> <div v-else class="course-item__image course-item__image--placeholder">
<SvgIcon name="book" :size="22" color="#bcbcbc" /> <SvgIcon name="book" :size="22" color="#bcbcbc" />