From 2436f77de57022d181bf2b930e053ac95afe7e05 Mon Sep 17 00:00:00 2001 From: dmitriylaukhin Date: Fri, 19 Jun 2026 16:10:29 +0500 Subject: [PATCH] Add 'approve without feedback' button for homework review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Curators/admins can mark a submission APPROVED (and complete the lesson) without writing feedback. The student sees 'ДЗ принято ✓' and can no longer resubmit. No email is sent on silent approval. Co-Authored-By: Claude Opus 4.8 --- .../homework/[submissionId]/actions.ts | 45 +++++++++++++++++++ .../homework/[submissionId]/feedback-form.tsx | 20 ++++++++- src/components/student/homework-section.tsx | 45 ++++++++++++++++++- src/lib/actions/student-actions.ts | 2 +- 4 files changed, 108 insertions(+), 4 deletions(-) diff --git a/src/app/curator/homework/[submissionId]/actions.ts b/src/app/curator/homework/[submissionId]/actions.ts index cb2dfe2..372b359 100644 --- a/src/app/curator/homework/[submissionId]/actions.ts +++ b/src/app/curator/homework/[submissionId]/actions.ts @@ -108,6 +108,51 @@ export async function setReviewing(submissionId: string) { revalidatePath(`/curator/homework/${submissionId}`); } +/** + * Принять ДЗ без письменного ответа: ставим APPROVED и засчитываем урок, + * фидбек не создаём и студенту письмо не шлём (тихое принятие). + */ +export async function approveWithoutFeedback(submissionId: string) { + await requireCurator(); + + const submission = await prisma.homeworkSubmission.findUnique({ + where: { id: submissionId }, + include: { + homework: { + include: { + lesson: { + select: { + id: true, + module: { select: { course: { select: { slug: true } } } }, + }, + }, + }, + }, + }, + }); + if (!submission) throw new Error("Submission not found"); + + const lesson = submission.homework.lesson; + + await prisma.$transaction(async (tx) => { + await tx.homeworkSubmission.update({ + where: { id: submissionId }, + data: { status: "APPROVED", statusAt: new Date() }, + }); + await tx.lessonProgress.upsert({ + where: { userId_lessonId: { userId: submission.userId, lessonId: lesson.id } }, + create: { userId: submission.userId, lessonId: lesson.id }, + update: {}, + }); + }); + + revalidatePath("/curator/homework"); + revalidatePath(`/curator/homework/${submissionId}`); + revalidatePath(`/courses/${lesson.module.course.slug}/lessons/${lesson.id}`); + revalidatePath(`/courses/${lesson.module.course.slug}`); + revalidatePath("/dashboard"); +} + export async function deleteSubmission(submissionId: string) { await requireCurator(); await prisma.homeworkSubmission.delete({ where: { id: submissionId } }); diff --git a/src/app/curator/homework/[submissionId]/feedback-form.tsx b/src/app/curator/homework/[submissionId]/feedback-form.tsx index 5db9c8e..d86080f 100644 --- a/src/app/curator/homework/[submissionId]/feedback-form.tsx +++ b/src/app/curator/homework/[submissionId]/feedback-form.tsx @@ -3,7 +3,7 @@ import { useState, useTransition, useRef } from "react"; import { useRouter } from "next/navigation"; import Link from "next/link"; -import { submitFeedback, setReviewing } from "./actions"; +import { submitFeedback, setReviewing, approveWithoutFeedback } from "./actions"; import { AudioRecorder } from "@/components/curator/audio-recorder"; interface FileItem { @@ -69,6 +69,13 @@ export function FeedbackForm({ }); } + function handleApproveNoFeedback() { + startTransition(async () => { + await approveWithoutFeedback(submissionId); + router.push("/curator/homework"); + }); + } + const isWorking = pending || uploading; return ( @@ -164,6 +171,17 @@ export function FeedbackForm({ {pending ? "Отправка..." : "Отправить ответ"} + + {currentStatus !== "REVIEWING" && (