Pending course, rest ready for launch
This commit is contained in:
113
app/(protected)/practice/[slug]/actions.ts
Normal file
113
app/(protected)/practice/[slug]/actions.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
"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 [];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user