Add homework system (admin, student, curator)
Admin: - HomeworkEditor in lesson page: create/update/delete assignment description Student: - HomeworkSection in lesson page: view assignment, submit text + files - Resubmission allowed until curator gives feedback - Shows feedback from curator with date and name Curator: - New layout with Second Brain dark sidebar (replaces green theme) - /curator/dashboard: stats cards (pending, total, reviewed this week) - /curator/homework: list of all submissions, pending highlighted - /curator/homework/[id]: review submission, write feedback, redirect after send Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useTransition } from "react";
|
||||
import { saveHomework, deleteHomework } from "@/app/admin/courses/[courseId]/modules/[moduleId]/lessons/[lessonId]/homework-actions";
|
||||
|
||||
interface Props {
|
||||
lessonId: string;
|
||||
initial: { id: string; description: string } | null;
|
||||
}
|
||||
|
||||
export function HomeworkEditor({ lessonId, initial }: Props) {
|
||||
const [editing, setEditing] = useState(!initial);
|
||||
const [text, setText] = useState(initial?.description ?? "");
|
||||
const [saved, setSaved] = useState(false);
|
||||
const [pending, startTransition] = useTransition();
|
||||
|
||||
const inputStyle = {
|
||||
border: "2px solid var(--border)",
|
||||
background: "var(--background)",
|
||||
outline: "none",
|
||||
width: "100%",
|
||||
padding: "0.5rem 0.75rem",
|
||||
fontSize: "0.875rem",
|
||||
fontFamily: "inherit",
|
||||
resize: "vertical" as const,
|
||||
minHeight: "120px",
|
||||
};
|
||||
|
||||
function handleSave() {
|
||||
if (!text.trim()) return;
|
||||
startTransition(async () => {
|
||||
await saveHomework(lessonId, text.trim());
|
||||
setSaved(true);
|
||||
setEditing(false);
|
||||
setTimeout(() => setSaved(false), 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
if (!confirm("Удалить домашнее задание? Все сданные работы будут удалены.")) return;
|
||||
startTransition(async () => {
|
||||
await deleteHomework(lessonId);
|
||||
setText("");
|
||||
setEditing(true);
|
||||
});
|
||||
}
|
||||
|
||||
if (!editing && initial) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="px-4 py-3 text-sm whitespace-pre-wrap" style={{ border: "2px solid var(--border)", background: "var(--color-surface)" }}>
|
||||
{text || initial.description}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<button onClick={() => setEditing(true)} className="btn-aubade text-xs px-3 py-1.5">
|
||||
Редактировать
|
||||
</button>
|
||||
<button onClick={handleDelete} disabled={pending} className="text-xs px-3 py-1.5" style={{ color: "oklch(0.577 0.245 27.325)" }}>
|
||||
Удалить ДЗ
|
||||
</button>
|
||||
{saved && <span className="text-xs self-center" style={{ color: "var(--muted-foreground)" }}>✓ Сохранено</span>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<textarea
|
||||
value={text}
|
||||
onChange={(e) => setText(e.target.value)}
|
||||
style={inputStyle}
|
||||
placeholder="Опишите задание для студентов..."
|
||||
onFocus={(e) => (e.currentTarget.style.borderColor = "var(--foreground)")}
|
||||
onBlur={(e) => (e.currentTarget.style.borderColor = "var(--border)")}
|
||||
/>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={pending || !text.trim()}
|
||||
className="btn-aubade btn-aubade-accent text-xs px-4 py-1.5"
|
||||
style={{ opacity: pending || !text.trim() ? 0.6 : 1 }}
|
||||
>
|
||||
{pending ? "Сохранение..." : "Сохранить задание"}
|
||||
</button>
|
||||
{initial && (
|
||||
<button onClick={() => { setEditing(false); setText(initial.description); }} className="text-xs" style={{ color: "var(--muted-foreground)" }}>
|
||||
Отмена
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user