c64f393a7b
- Wire settings to actual platform behavior: maintenance mode, registration toggle, notification emails, curator feedback emails, email verification flag - Add logo (logoUrl, showLogo) and social network links (YouTube, VK, Telegram) settings - Show logo + school name dynamically in student layout header - Add footer to student layout with org requisites and social links - Register page: read settings server-side, validate terms checkbox with legal links - Login page: show notice when redirected from closed registration - Settings form: add Logo and Social Networks sections Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
116 lines
3.3 KiB
TypeScript
116 lines
3.3 KiB
TypeScript
"use server";
|
|
|
|
import { prisma } from "@/lib/prisma";
|
|
import { auth } from "@/lib/auth";
|
|
import { headers } from "next/headers";
|
|
import { revalidatePath } from "next/cache";
|
|
import { sendFeedbackReceivedEmail } from "@/lib/email";
|
|
import { getSetting, asBool } from "@/lib/settings";
|
|
|
|
async function requireCurator() {
|
|
const session = await auth.api.getSession({ headers: await headers() });
|
|
if (!session || (session.user.role !== "curator" && session.user.role !== "admin")) {
|
|
throw new Error("Forbidden");
|
|
}
|
|
return session;
|
|
}
|
|
|
|
export async function submitFeedback(
|
|
submissionId: string,
|
|
data: {
|
|
text: string;
|
|
files?: { name: string; url: string; size: number }[];
|
|
audioUrl?: string | null;
|
|
action: "approve" | "reject";
|
|
}
|
|
) {
|
|
const session = await requireCurator();
|
|
const status = data.action === "approve" ? "APPROVED" : "REJECTED";
|
|
|
|
const submission = await prisma.homeworkSubmission.findUnique({
|
|
where: { id: submissionId },
|
|
include: {
|
|
user: { select: { id: true, email: true, name: true } },
|
|
homework: {
|
|
include: {
|
|
lesson: {
|
|
select: {
|
|
title: true,
|
|
id: true,
|
|
module: { select: { course: { select: { slug: true } } } },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!submission) throw new Error("Submission not found");
|
|
|
|
await prisma.$transaction(async (tx) => {
|
|
await tx.homeworkFeedback.create({
|
|
data: {
|
|
submissionId,
|
|
curatorId: session.user.id,
|
|
text: data.text,
|
|
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 notifySetting = await getSetting("notifyStudentOnFeedback");
|
|
if (asBool(notifySetting)) {
|
|
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/${submissionId}`);
|
|
revalidatePath(`/courses/${lesson.module.course.slug}/lessons/${lesson.id}`);
|
|
revalidatePath(`/courses/${lesson.module.course.slug}`);
|
|
revalidatePath("/dashboard");
|
|
}
|
|
|
|
export async function setReviewing(submissionId: string) {
|
|
await requireCurator();
|
|
await prisma.homeworkSubmission.update({
|
|
where: { id: submissionId },
|
|
data: { status: "REVIEWING", statusAt: new Date() },
|
|
});
|
|
revalidatePath("/curator/homework");
|
|
revalidatePath(`/curator/homework/${submissionId}`);
|
|
}
|
|
|
|
export async function deleteSubmission(submissionId: string) {
|
|
await requireCurator();
|
|
await prisma.homeworkSubmission.delete({ where: { id: submissionId } });
|
|
revalidatePath("/curator/homework");
|
|
}
|