fix(admin): restore description fields on Learn English course and module forms

Re-add description inputs to course create/edit and module create/edit dialogs so descriptions are sent to the API instead of empty strings.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Yared Yemane 2026-06-10 08:51:02 -07:00
parent a10d7684d5
commit c9959cce23
3 changed files with 73 additions and 6 deletions

View File

@ -21,6 +21,7 @@ import {
DialogTitle, DialogTitle,
} from "../../components/ui/dialog"; } from "../../components/ui/dialog";
import { Input } from "../../components/ui/input"; import { Input } from "../../components/ui/input";
import { Textarea } from "../../components/ui/textarea";
import { cn } from "../../lib/utils"; import { cn } from "../../lib/utils";
import spinnerSrc from "../../assets/Circular-indeterminate progress indicator.svg"; import spinnerSrc from "../../assets/Circular-indeterminate progress indicator.svg";
import alertSrc from "../../assets/Alert.svg"; import alertSrc from "../../assets/Alert.svg";
@ -162,6 +163,7 @@ export function CourseDetailPage() {
const [editingModule, setEditingModule] = const [editingModule, setEditingModule] =
useState<TopLevelCourseModuleItem | null>(null); useState<TopLevelCourseModuleItem | null>(null);
const [editModuleName, setEditModuleName] = useState(""); const [editModuleName, setEditModuleName] = useState("");
const [editModuleDescription, setEditModuleDescription] = useState("");
const [editModuleSortOrder, setEditModuleSortOrder] = useState(""); const [editModuleSortOrder, setEditModuleSortOrder] = useState("");
const [editModuleIcon, setEditModuleIcon] = useState(""); const [editModuleIcon, setEditModuleIcon] = useState("");
const [editModuleIconUploadBusy, setEditModuleIconUploadBusy] = const [editModuleIconUploadBusy, setEditModuleIconUploadBusy] =
@ -197,6 +199,7 @@ export function CourseDetailPage() {
const openEditModule = (module: TopLevelCourseModuleItem) => { const openEditModule = (module: TopLevelCourseModuleItem) => {
setEditingModule(module); setEditingModule(module);
setEditModuleName(module.name ?? ""); setEditModuleName(module.name ?? "");
setEditModuleDescription(module.description ?? "");
setEditModuleSortOrder(String(module.sort_order ?? 0)); setEditModuleSortOrder(String(module.sort_order ?? 0));
setEditModuleIcon(module.icon?.trim() ?? ""); setEditModuleIcon(module.icon?.trim() ?? "");
setEditModuleIconUploadBusy(false); setEditModuleIconUploadBusy(false);
@ -460,7 +463,7 @@ export function CourseDetailPage() {
try { try {
await updateTopLevelCourseModule(editingModule.id, { await updateTopLevelCourseModule(editingModule.id, {
name, name,
description: editingModule.description?.trim() ?? "", description: editModuleDescription.trim(),
icon: editModuleIcon.trim(), icon: editModuleIcon.trim(),
sort_order, sort_order,
}); });
@ -618,7 +621,7 @@ export function CourseDetailPage() {
<DialogHeader className="shrink-0 space-y-1.5 border-b border-grayScale-100 px-6 pb-4 pt-6 pr-12"> <DialogHeader className="shrink-0 space-y-1.5 border-b border-grayScale-100 px-6 pb-4 pt-6 pr-12">
<DialogTitle>Edit module</DialogTitle> <DialogTitle>Edit module</DialogTitle>
<DialogDescription> <DialogDescription>
Update name, sort order, and icon (upload or URL). Update name, description, sort order, and icon (upload or URL).
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 py-4"> <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 py-4">
@ -635,6 +638,19 @@ export function CourseDetailPage() {
disabled={savingModuleEdit} disabled={savingModuleEdit}
/> />
</div> </div>
<div className="space-y-2">
<label className="text-sm font-medium text-grayScale-700">
Description
</label>
<Textarea
value={editModuleDescription}
onChange={(e) => setEditModuleDescription(e.target.value)}
rows={4}
className="min-h-[100px] resize-y rounded-xl"
placeholder="Short summary of the module"
disabled={savingModuleEdit || editModuleIconUploadBusy}
/>
</div>
<div className="space-y-2"> <div className="space-y-2">
<label <label
htmlFor="edit-module-sort-order" htmlFor="edit-module-sort-order"

View File

@ -14,6 +14,7 @@ import {
DialogTrigger, DialogTrigger,
} from "../../components/ui/dialog"; } from "../../components/ui/dialog";
import { Input } from "../../components/ui/input"; import { Input } from "../../components/ui/input";
import { Textarea } from "../../components/ui/textarea";
import uploadIcon from "../../assets/icons/upload.png"; import uploadIcon from "../../assets/icons/upload.png";
import spinnerSrc from "../../assets/Circular-indeterminate progress indicator.svg"; import spinnerSrc from "../../assets/Circular-indeterminate progress indicator.svg";
import alertSrc from "../../assets/Alert.svg"; import alertSrc from "../../assets/Alert.svg";
@ -64,6 +65,7 @@ export function ProgramCoursesPage() {
null, null,
); );
const [editName, setEditName] = useState(""); const [editName, setEditName] = useState("");
const [editDescription, setEditDescription] = useState("");
const [editSortOrder, setEditSortOrder] = useState(""); const [editSortOrder, setEditSortOrder] = useState("");
const [editThumbnail, setEditThumbnail] = useState(""); const [editThumbnail, setEditThumbnail] = useState("");
const [savingEdit, setSavingEdit] = useState(false); const [savingEdit, setSavingEdit] = useState(false);
@ -72,6 +74,7 @@ export function ProgramCoursesPage() {
const [createCourseOpen, setCreateCourseOpen] = useState(false); const [createCourseOpen, setCreateCourseOpen] = useState(false);
const [createName, setCreateName] = useState(""); const [createName, setCreateName] = useState("");
const [createDescription, setCreateDescription] = useState("");
const [createSortOrder, setCreateSortOrder] = useState(""); const [createSortOrder, setCreateSortOrder] = useState("");
const [createThumbnail, setCreateThumbnail] = useState(""); const [createThumbnail, setCreateThumbnail] = useState("");
const [createSaving, setCreateSaving] = useState(false); const [createSaving, setCreateSaving] = useState(false);
@ -218,6 +221,7 @@ export function ProgramCoursesPage() {
const openEditCourse = (course: ProgramCourseListItem) => { const openEditCourse = (course: ProgramCourseListItem) => {
setEditingCourse(course); setEditingCourse(course);
setEditName(course.name ?? ""); setEditName(course.name ?? "");
setEditDescription(course.description?.trim() ?? "");
setEditThumbnail( setEditThumbnail(
course.thumbnail?.trim() || course.thumbnail_url?.trim() || "", course.thumbnail?.trim() || course.thumbnail_url?.trim() || "",
); );
@ -227,6 +231,7 @@ export function ProgramCoursesPage() {
const closeEditCourse = () => { const closeEditCourse = () => {
setEditingCourse(null); setEditingCourse(null);
setEditName(""); setEditName("");
setEditDescription("");
setEditSortOrder(""); setEditSortOrder("");
setEditThumbnail(""); setEditThumbnail("");
setUploadingEditThumbnail(false); setUploadingEditThumbnail(false);
@ -291,7 +296,7 @@ export function ProgramCoursesPage() {
try { try {
await updateTopLevelCourse(editingCourse.id, { await updateTopLevelCourse(editingCourse.id, {
name, name,
description: editingCourse.description?.trim() ?? "", description: editDescription.trim(),
thumbnail: editThumbnail.trim(), thumbnail: editThumbnail.trim(),
sort_order, sort_order,
}); });
@ -311,6 +316,7 @@ export function ProgramCoursesPage() {
const clearCreateCourseForm = () => { const clearCreateCourseForm = () => {
setCreateName(""); setCreateName("");
setCreateDescription("");
setCreateSortOrder(""); setCreateSortOrder("");
setCreateThumbnail(""); setCreateThumbnail("");
setCreateUploadingThumbnail(false); setCreateUploadingThumbnail(false);
@ -381,7 +387,7 @@ export function ProgramCoursesPage() {
try { try {
await createProgramCourse(programId, { await createProgramCourse(programId, {
name, name,
description: "", description: createDescription.trim(),
thumbnail: createThumbnail.trim(), thumbnail: createThumbnail.trim(),
sort_order, sort_order,
}); });
@ -509,6 +515,20 @@ export function ProgramCoursesPage() {
/> />
</div> </div>
<div className="space-y-2">
<label className="text-[15px] font-medium text-grayScale-700">
Description
</label>
<Textarea
value={createDescription}
onChange={(e) => setCreateDescription(e.target.value)}
placeholder="Short summary of the course"
rows={3}
className="min-h-[88px] resize-y rounded-xl"
disabled={createSaving || createUploadingThumbnail}
/>
</div>
<div className="space-y-2"> <div className="space-y-2">
<label <label
htmlFor="create-course-sort-order" htmlFor="create-course-sort-order"
@ -822,7 +842,7 @@ export function ProgramCoursesPage() {
<DialogHeader className="shrink-0 space-y-1.5 border-b border-grayScale-100 px-6 pb-4 pt-6 pr-12"> <DialogHeader className="shrink-0 space-y-1.5 border-b border-grayScale-100 px-6 pb-4 pt-6 pr-12">
<DialogTitle>Edit course</DialogTitle> <DialogTitle>Edit course</DialogTitle>
<DialogDescription> <DialogDescription>
Update name, sort order, and thumbnail. Update name, description, sort order, and thumbnail.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 py-4"> <div className="min-h-0 flex-1 overflow-y-auto overscroll-contain px-6 py-4">
@ -839,6 +859,19 @@ export function ProgramCoursesPage() {
disabled={savingEdit || uploadingEditThumbnail} disabled={savingEdit || uploadingEditThumbnail}
/> />
</div> </div>
<div className="space-y-2">
<label className="text-sm font-medium text-grayScale-700">
Description
</label>
<Textarea
value={editDescription}
onChange={(e) => setEditDescription(e.target.value)}
rows={4}
className="min-h-[100px] resize-y rounded-xl"
placeholder="Short summary of the course"
disabled={savingEdit || uploadingEditThumbnail}
/>
</div>
<div className="space-y-2"> <div className="space-y-2">
<label <label
htmlFor="edit-course-sort-order" htmlFor="edit-course-sort-order"

View File

@ -9,6 +9,7 @@ import {
DialogClose, DialogClose,
} from "../../../components/ui/dialog"; } from "../../../components/ui/dialog";
import { Input } from "../../../components/ui/input"; import { Input } from "../../../components/ui/input";
import { Textarea } from "../../../components/ui/textarea";
import { toast } from "sonner"; import { toast } from "sonner";
import { createTopLevelCourseModule } from "../../../api/courses.api"; import { createTopLevelCourseModule } from "../../../api/courses.api";
import { ModuleIconUploadField } from "./ModuleIconUploadField"; import { ModuleIconUploadField } from "./ModuleIconUploadField";
@ -27,6 +28,7 @@ export function AddModuleModal({
onCreated, onCreated,
}: AddModuleModalProps) { }: AddModuleModalProps) {
const [name, setName] = useState(""); const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [sortOrder, setSortOrder] = useState(""); const [sortOrder, setSortOrder] = useState("");
const [icon, setIcon] = useState(""); const [icon, setIcon] = useState("");
const [submitting, setSubmitting] = useState(false); const [submitting, setSubmitting] = useState(false);
@ -35,6 +37,7 @@ export function AddModuleModal({
useEffect(() => { useEffect(() => {
if (isOpen) { if (isOpen) {
setName(""); setName("");
setDescription("");
setSortOrder(""); setSortOrder("");
setIcon(""); setIcon("");
setSubmitting(false); setSubmitting(false);
@ -44,6 +47,7 @@ export function AddModuleModal({
const resetAndClose = () => { const resetAndClose = () => {
setName(""); setName("");
setDescription("");
setSortOrder(""); setSortOrder("");
setIcon(""); setIcon("");
setIconUploadBusy(false); setIconUploadBusy(false);
@ -82,7 +86,7 @@ export function AddModuleModal({
try { try {
await createTopLevelCourseModule(courseId, { await createTopLevelCourseModule(courseId, {
name: trimmedName, name: trimmedName,
description: "", description: description.trim(),
icon: icon.trim(), icon: icon.trim(),
sort_order, sort_order,
}); });
@ -149,6 +153,20 @@ export function AddModuleModal({
/> />
</div> </div>
<div className="space-y-2">
<label className="text-[15px] font-medium text-grayScale-700">
Description
</label>
<Textarea
value={description}
onChange={(e) => setDescription(e.target.value)}
placeholder="Short summary of the module"
rows={3}
className="min-h-[88px] resize-y rounded-xl"
disabled={submitting || iconUploadBusy}
/>
</div>
<div className="space-y-2"> <div className="space-y-2">
<label <label
htmlFor="create-module-sort-order" htmlFor="create-module-sort-order"