543d5b2d5e
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>
95 lines
3.2 KiB
TypeScript
95 lines
3.2 KiB
TypeScript
"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>
|
||
);
|
||
}
|