Add lesson complete button with homework-aware logic
- Show "Отметить как пройденный" button only on lessons without homework - Show static "Пройдено" badge on homework lessons completed via approval - Auto-create LessonProgress when curator/admin approves homework submission - Revalidate student lesson, course, and dashboard pages on approval Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -179,9 +179,12 @@ export default async function LessonPage({ params }: Props) {
|
|||||||
<div />
|
<div />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!isAdmin && (
|
{!isAdmin && !lesson.homework && (
|
||||||
<LessonCompleteButton lessonId={lessonId} slug={slug} isCompleted={isCompleted} />
|
<LessonCompleteButton lessonId={lessonId} slug={slug} isCompleted={isCompleted} />
|
||||||
)}
|
)}
|
||||||
|
{!isAdmin && lesson.homework && isCompleted && (
|
||||||
|
<LessonCompleteButton lessonId={lessonId} slug={slug} isCompleted={true} readOnly={true} />
|
||||||
|
)}
|
||||||
|
|
||||||
{nextLesson ? (
|
{nextLesson ? (
|
||||||
<Link
|
<Link
|
||||||
|
|||||||
@@ -26,27 +26,10 @@ export async function submitFeedback(
|
|||||||
const session = await requireCurator();
|
const session = await requireCurator();
|
||||||
const status = data.action === "approve" ? "APPROVED" : "REJECTED";
|
const status = data.action === "approve" ? "APPROVED" : "REJECTED";
|
||||||
|
|
||||||
await prisma.$transaction([
|
|
||||||
prisma.homeworkFeedback.create({
|
|
||||||
data: {
|
|
||||||
submissionId,
|
|
||||||
curatorId: session.user.id,
|
|
||||||
text: data.text,
|
|
||||||
files: data.files ?? [],
|
|
||||||
audioUrl: data.audioUrl ?? null,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
prisma.homeworkSubmission.update({
|
|
||||||
where: { id: submissionId },
|
|
||||||
data: { status, statusAt: new Date() },
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Send email notification to student
|
|
||||||
const submission = await prisma.homeworkSubmission.findUnique({
|
const submission = await prisma.homeworkSubmission.findUnique({
|
||||||
where: { id: submissionId },
|
where: { id: submissionId },
|
||||||
include: {
|
include: {
|
||||||
user: { select: { email: true, name: true } },
|
user: { select: { id: true, email: true, name: true } },
|
||||||
homework: {
|
homework: {
|
||||||
include: {
|
include: {
|
||||||
lesson: {
|
lesson: {
|
||||||
@@ -61,20 +44,54 @@ export async function submitFeedback(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (submission) {
|
if (!submission) throw new Error("Submission not found");
|
||||||
const { lesson } = submission.homework;
|
|
||||||
const lessonUrl = `${process.env.BETTER_AUTH_URL ?? "https://school.second-brain.ru"}/courses/${lesson.module.course.slug}/lessons/${lesson.id}`;
|
await prisma.$transaction(async (tx) => {
|
||||||
await sendFeedbackReceivedEmail(
|
await tx.homeworkFeedback.create({
|
||||||
submission.user.email,
|
data: {
|
||||||
submission.user.name,
|
submissionId,
|
||||||
lesson.title,
|
curatorId: session.user.id,
|
||||||
data.text,
|
text: data.text,
|
||||||
lessonUrl
|
files: data.files ?? [],
|
||||||
);
|
audioUrl: data.audioUrl ?? null,
|
||||||
}
|
},
|
||||||
|
});
|
||||||
|
await tx.homeworkSubmission.update({
|
||||||
|
where: { id: submissionId },
|
||||||
|
data: { status, statusAt: new Date() },
|
||||||
|
});
|
||||||
|
if (status === "APPROVED") {
|
||||||
|
await tx.lessonProgress.upsert({
|
||||||
|
where: {
|
||||||
|
userId_lessonId: {
|
||||||
|
userId: submission.user.id,
|
||||||
|
lessonId: submission.homework.lesson.id,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
userId: submission.user.id,
|
||||||
|
lessonId: submission.homework.lesson.id,
|
||||||
|
},
|
||||||
|
update: {},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const { lesson } = submission.homework;
|
||||||
|
const lessonUrl = `${process.env.BETTER_AUTH_URL ?? "https://school.second-brain.ru"}/courses/${lesson.module.course.slug}/lessons/${lesson.id}`;
|
||||||
|
await sendFeedbackReceivedEmail(
|
||||||
|
submission.user.email,
|
||||||
|
submission.user.name,
|
||||||
|
lesson.title,
|
||||||
|
data.text,
|
||||||
|
lessonUrl
|
||||||
|
);
|
||||||
|
|
||||||
revalidatePath("/curator/homework");
|
revalidatePath("/curator/homework");
|
||||||
revalidatePath(`/curator/homework/${submissionId}`);
|
revalidatePath(`/curator/homework/${submissionId}`);
|
||||||
|
revalidatePath(`/courses/${lesson.module.course.slug}/lessons/${lesson.id}`);
|
||||||
|
revalidatePath(`/courses/${lesson.module.course.slug}`);
|
||||||
|
revalidatePath("/dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setReviewing(submissionId: string) {
|
export async function setReviewing(submissionId: string) {
|
||||||
|
|||||||
@@ -8,13 +8,24 @@ export function LessonCompleteButton({
|
|||||||
lessonId,
|
lessonId,
|
||||||
slug,
|
slug,
|
||||||
isCompleted,
|
isCompleted,
|
||||||
|
readOnly = false,
|
||||||
}: {
|
}: {
|
||||||
lessonId: string;
|
lessonId: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
isCompleted: boolean;
|
isCompleted: boolean;
|
||||||
|
readOnly?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const [pending, startTransition] = useTransition();
|
const [pending, startTransition] = useTransition();
|
||||||
|
|
||||||
|
if (readOnly) {
|
||||||
|
return (
|
||||||
|
<div className="btn-aubade btn-aubade-accent flex items-center gap-2 px-5 py-2.5 text-sm cursor-default select-none">
|
||||||
|
<Check size={15} strokeWidth={3} />
|
||||||
|
Пройдено
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={() => startTransition(() => toggleLessonProgress(lessonId, slug))}
|
onClick={() => startTransition(() => toggleLessonProgress(lessonId, slug))}
|
||||||
|
|||||||
Reference in New Issue
Block a user