Add course management improvements: tree view, module descriptions, lesson toggles
- SortableModules: add description textarea in edit form, show description in row - CourseDetailPage: fetch lessons per module, add CourseTree overview section - ModulePage: fetch sibling modules, pass as otherModules to SortableLessons Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ import { createModule, deleteModule, updateModule, reorderModules } from "@/app/
|
||||
interface Module {
|
||||
id: string;
|
||||
title: string;
|
||||
description: string | null;
|
||||
order: number;
|
||||
_count: { lessons: number };
|
||||
}
|
||||
@@ -29,6 +30,7 @@ interface Module {
|
||||
function SortableModule({ mod, courseId }: { mod: Module; courseId: string }) {
|
||||
const [editing, setEditing] = useState(false);
|
||||
const [editValue, setEditValue] = useState(mod.title);
|
||||
const [editDesc, setEditDesc] = useState(mod.description ?? "");
|
||||
const [pending, startTransition] = useTransition();
|
||||
const { attributes, listeners, setNodeRef, transform, transition, isDragging } =
|
||||
useSortable({ id: mod.id });
|
||||
@@ -69,31 +71,50 @@ function SortableModule({ mod, courseId }: { mod: Module; courseId: string }) {
|
||||
</button>
|
||||
|
||||
{editing ? (
|
||||
<form onSubmit={handleUpdate} className="flex items-center gap-2 flex-1">
|
||||
<input
|
||||
name="title"
|
||||
value={editValue}
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
autoFocus
|
||||
required
|
||||
className="flex-1 px-2 py-1 text-sm"
|
||||
style={{ border: "2px solid var(--foreground)", background: "var(--background)", outline: "none" }}
|
||||
<form onSubmit={handleUpdate} className="flex flex-col gap-2 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
name="title"
|
||||
value={editValue}
|
||||
onChange={(e) => setEditValue(e.target.value)}
|
||||
autoFocus
|
||||
required
|
||||
placeholder="Название модуля"
|
||||
className="flex-1 px-2 py-1 text-sm"
|
||||
style={{ border: "2px solid var(--foreground)", background: "var(--background)", outline: "none" }}
|
||||
/>
|
||||
<button type="submit" disabled={pending} className="btn-aubade text-xs px-3 py-1 shrink-0">
|
||||
Сохранить
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setEditing(false); setEditValue(mod.title); setEditDesc(mod.description ?? ""); }}
|
||||
className="text-xs px-3 py-1 shrink-0"
|
||||
style={{ color: "var(--muted-foreground)" }}
|
||||
>
|
||||
Отмена
|
||||
</button>
|
||||
</div>
|
||||
<textarea
|
||||
name="description"
|
||||
value={editDesc}
|
||||
onChange={(e) => setEditDesc(e.target.value)}
|
||||
placeholder="Описание модуля (опционально)"
|
||||
rows={2}
|
||||
className="w-full px-2 py-1 text-sm resize-none"
|
||||
style={{ border: "1px solid var(--border)", background: "var(--background)", outline: "none" }}
|
||||
/>
|
||||
<button type="submit" disabled={pending} className="btn-aubade text-xs px-3 py-1">
|
||||
Сохранить
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { setEditing(false); setEditValue(mod.title); }}
|
||||
className="text-xs px-3 py-1"
|
||||
style={{ color: "var(--muted-foreground)" }}
|
||||
>
|
||||
Отмена
|
||||
</button>
|
||||
</form>
|
||||
) : (
|
||||
<>
|
||||
<span className="flex-1 font-medium text-sm">{mod.title}</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<span className="font-medium text-sm">{mod.title}</span>
|
||||
{mod.description && (
|
||||
<p className="text-xs truncate mt-0.5" style={{ color: "var(--muted-foreground)" }}>
|
||||
{mod.description}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<span className="text-xs" style={{ color: "var(--muted-foreground)" }}>
|
||||
{mod._count.lessons} {mod._count.lessons === 1 ? "урок" : mod._count.lessons < 5 ? "урока" : "уроков"}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user