Files
ACVE/app/(protected)/practice/[slug]/actions.ts
2026-03-15 13:52:11 +00:00

114 lines
3.3 KiB
TypeScript

"use server";
import { revalidatePath } from "next/cache";
import { requireUser } from "@/lib/auth/requireUser";
import { mockPracticeModules } from "@/lib/data/mockPractice";
import { db } from "@/lib/prisma";
import { refreshStudyRecommendations } from "@/lib/recommendations";
type SubmitAttemptInput = {
slug: string;
selectedAnswers: number[];
};
type PracticePrismaClient = {
miniGame: {
upsert: (args: object) => Promise<{ id: string }>;
};
miniGameAttempt: {
create: (args: object) => Promise<unknown>;
findMany: (args: object) => Promise<
{ id: string; scorePercent: number; correctCount: number; totalQuestions: number; completedAt: Date }[]
>;
};
};
function toDifficulty(level?: string): "BEGINNER" | "INTERMEDIATE" | "ADVANCED" {
if (level === "Beginner") return "BEGINNER";
if (level === "Advanced") return "ADVANCED";
return "INTERMEDIATE";
}
export async function submitPracticeAttempt({ slug, selectedAnswers }: SubmitAttemptInput) {
const user = await requireUser();
if (!user?.id) return { success: false as const, error: "Unauthorized" };
const practiceModule = mockPracticeModules.find((item) => item.slug === slug && item.isInteractive && item.questions?.length);
if (!practiceModule || !practiceModule.questions) return { success: false as const, error: "Practice module not found" };
const correctCount = practiceModule.questions.reduce((acc, question, index) => {
return acc + (selectedAnswers[index] === question.answerIndex ? 1 : 0);
}, 0);
const total = practiceModule.questions.length;
const scorePercent = Math.round((correctCount / total) * 100);
const prismaMini = db as unknown as PracticePrismaClient;
try {
const miniGame = await prismaMini.miniGame.upsert({
where: { slug: practiceModule.slug },
update: {
title: practiceModule.title,
description: practiceModule.description,
isActive: true,
difficulty: toDifficulty(practiceModule.difficulty),
},
create: {
slug: practiceModule.slug,
title: practiceModule.title,
description: practiceModule.description,
isActive: true,
difficulty: toDifficulty(practiceModule.difficulty),
},
select: { id: true },
});
await prismaMini.miniGameAttempt.create({
data: {
userId: user.id,
miniGameId: miniGame.id,
scorePercent,
correctCount,
totalQuestions: total,
},
});
await refreshStudyRecommendations(user.id);
} catch {
return { success: false as const, error: "Mini-game tables are not migrated yet" };
}
revalidatePath("/profile");
return { success: true as const, scorePercent, correctCount, total };
}
export async function getPracticeAttempts(slug: string) {
const user = await requireUser();
if (!user?.id) return [];
try {
const attempts = await (db as unknown as PracticePrismaClient).miniGameAttempt.findMany({
where: {
userId: user.id,
miniGame: {
slug,
},
},
orderBy: {
completedAt: "desc",
},
take: 5,
select: {
id: true,
scorePercent: true,
correctCount: true,
totalQuestions: true,
completedAt: true,
},
});
return attempts;
} catch {
return [];
}
}